Converted Mobibot class to Kotlin.

This commit is contained in:
Erik C. Thauvin 2020-12-04 23:11:39 -08:00
parent a44b0c1f49
commit 69441f7006
30 changed files with 956 additions and 1134 deletions

11
.idea/compiler.xml generated
View file

@ -1,17 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Gradle Imported" enabled="true">
<outputRelativeToContentRoot value="true" />
<option name="semver.project.dir" value="$PROJECT_DIR$" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" />
</processorPath>
<module name="mobibot.main" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel target="11" />
</component>
</project>

2
.idea/mobibot.iml generated
View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4">
<module external.linked.project.id="mobibot" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+287" type="JAVA_MODULE" version="4">
<module external.linked.project.id="mobibot:main" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.type="sourceSet" external.system.module.version="0.8.0-beta+327" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false">
@ -8,7 +8,7 @@
</compilerSettings>
<compilerArguments>
<option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" />
<option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" />
<option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.20/756521338269950c2a276f1abe6fef8e1a5e5528/kotlin-stdlib-jdk8-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.4.2/4b9c6b2de7cabfb2c9ad7a5c709b1ddb7bbfd2ad/kotlinx-coroutines-core-jvm-1.4.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.20/9de2c79e95d4b4699a455e88ba285a95352e0bea/kotlin-stdlib-jdk7-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar:/home/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.2.0/39d2a464e63fd44bcdbc2332b12a80df274dac83/spotbugs-annotations-4.2.0.jar:/media/erik/Projects/maven/repository/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.15.0/d3614542b857eccc0555d1ee8dfc36d2043d9c1f/rome-1.15.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar:/home/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.7.2/fc22868c06d0b59dc97f23dc93ca77efd9381ab2/commons-net-3.7.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.11/68e9a6adf7cf8eb7e9d31bbc554c7c75eeaac568/commons-lang3-3.11.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20201115/f8e7a9953822c90e0701c3cd50764b5e9063f85c/json-20201115.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.14.0/e257b0562453f73eabac1bc3181ba33e79d193ed/log4j-core-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.14.0/d6003a3b3f24fdb476848f4ecabdb2a43354e410/log4j-slf4j-impl-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.14.0/23cdb2c6babad9b2b0dcf47c6a2c29d504e4c7a8/log4j-api-2.14.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar:/media/erik/Projects/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.15.0/ab1cb95382bf9a8dec81165d328bcbbf1acfb3ae/rome-utils-1.15.0.jar:/media/erik/Projects/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.14.2/eaed79ed6bc1e14fad462172b6a09524545b165c/okhttp-3.14.2.jar:/home/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.17.2/78c7820b205002da4d2d137f6f312bd64b3d6049/okio-1.17.2.jar" />
<option name="noStdlib" value="true" />
<option name="noReflect" value="true" />
<option name="moduleName" value="mobibot" />
@ -20,6 +20,9 @@
</option>
<option name="pluginClasspaths">
<array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-compiler-embeddable/1.4.20/fc1d26586910b32d676480c75acd3e922e5e81fa/kotlin-compiler-embeddable-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-daemon-embeddable/1.4.20/a051291fb01bf2397759625626fec670cd57b3f0/kotlin-daemon-embeddable-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.20/411fc46e908bfa9c034f52b0d31b2e1f61f06127/kotlin-reflect-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.20/9793d2f6b262847a2d8127951c5786cf907cc7b1/kotlin-script-runtime-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.20/2e2bf29688a76cec111df56bc5e358c5bbc5057/kotlin-scripting-common-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.20/83704fbbe39946cc2ac6d0f07f41947abeb8dc20/kotlin-scripting-jvm-1.4.20.jar" />
@ -27,6 +30,7 @@
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.20/9be77b243a362b745e365f286627b8724337009c/kotlin-stdlib-1.4.20.jar" />
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.3.7/8e2eb78158638b33793d204ffef0b65c4a578e1c/kotlinx-coroutines-core-1.3.7.jar" />
<option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<option value="$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar" />
</array>
</option>
<option name="errors">
@ -39,46 +43,59 @@
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/../../build/classes/java/main" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../src/generated/java">
<sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" />
<content url="file://$MODULE_DIR$/../../build/generated/source/kapt/main">
<sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kapt/main" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main">
<sourceFolder url="file://$MODULE_DIR$/../../build/generated/source/kaptKotlin/main" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/../../src/main">
<sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" />
<orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" />
<orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" />
<orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" />
<orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" />
<orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" />
<orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" />
<orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" />
<orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="Gradle: kaptGeneratedClasses">
<CLASSES>
<root url="file://$MODULE_DIR$/../../build/tmp/kapt3/classes/main" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" />
<orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.4.20" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" />
<orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:sources:1.5.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.2.0" level="project" />
<orderEntry type="library" name="Gradle: com.rometools:rome:1.15.0" level="project" />
<orderEntry type="library" name="Gradle: commons-cli:commons-cli:1.4" level="project" />
<orderEntry type="library" name="Gradle: commons-net:commons-net:3.7.2" level="project" />
<orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" />
<orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.11" level="project" />
<orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" />
<orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" />
<orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.14.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit:2.5.0" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.14.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" />
<orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.15.0" level="project" />
<orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.20" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.17.2" level="project" />
<orderEntry type="library" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" />
</component>
</module>

File diff suppressed because one or more lines are too long

32
ReleaseInfo.mustache Normal file
View file

@ -0,0 +1,32 @@
/*
* This file is automatically generated.
* Do not modify! -- ALL CHANGES WILL BE ERASED!
*/
package {{packageName}}
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.Instant
/**
* Provides semantic version information.
*
* @author [Semantic Version Annotation Processor](https://github.com/ethauvin/semver)
*/
object ReleaseInfo{
const val PROJECT = "{{project}}"
const val VERSION = "{{version}}"
@JvmField
val BUILDDATE = LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault())
const val MAJOR = {{major}}
const val MINOR = {{minor}}
const val PATCH = {{patch}}
const val BUILDMETA = "{{buildMeta}}"
const val PRERELEASE = "{{preRelease}}"
const val WEBSITE = "https://www.mobitopia.org/mobibot/"
const val AUTHOR = "Erik C. Thauvin"
const val AUTHOR_URL = "https://erik.thauvin.net/"
}

View file

@ -9,6 +9,7 @@ plugins {
id 'java'
id 'net.thauvin.erik.gradle.semver' version '1.0.4'
id 'org.jetbrains.kotlin.jvm' version '1.4.20'
id 'org.jetbrains.kotlin.kapt' version '1.4.20'
id 'org.sonarqube' version '3.0'
id 'pmd'
}
@ -33,8 +34,8 @@ repositories {
}
dependencies {
annotationProcessor semverProcessor
compileOnly semverProcessor
kapt(semverProcessor)
implementation(semverProcessor)
implementation 'pircbot:pircbot:1.5.0'
compileOnly 'pircbot:pircbot:1.5.0:sources'
@ -77,14 +78,18 @@ java {
targetCompatibility = JavaVersion.VERSION_11
}
kapt {
arguments {
arg("semver.project.dir", projectDir)
}
}
application {
mainClassName = packageName + '.Mobibot'
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java")
options.compilerArgs += ["-Asemver.project.dir=$projectDir"]
}
compileJava {

View file

@ -3,8 +3,10 @@
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<CurrentIssues>
<ID>ComplexMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List&lt;EntryLink&gt;, history: MutableList&lt;String&gt;, isDayBackup: Boolean )</ID>
<ID>ComplexMethod:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID>
<ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>LongMethod:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List&lt;EntryLink&gt;, history: MutableList&lt;String&gt;, isDayBackup: Boolean )</ID>
<ID>LongMethod:Mobibot.kt$Mobibot.Companion$ @JvmStatic fun main(args: Array&lt;String&gt;)</ID>
<ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
@ -22,6 +24,11 @@
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
<ID>MagicNumber:Dice.kt$Dice$7</ID>
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
<ID>MagicNumber:Mobibot.kt$Mobibot$10</ID>
<ID>MagicNumber:Mobibot.kt$Mobibot$1000L</ID>
<ID>MagicNumber:Mobibot.kt$Mobibot$3</ID>
<ID>MagicNumber:Mobibot.kt$Mobibot$5</ID>
<ID>MagicNumber:Mobibot.kt$Mobibot$8</ID>
<ID>MagicNumber:Modules.kt$Modules$7</ID>
<ID>MagicNumber:Recap.kt$Recap.Companion$10</ID>
<ID>MagicNumber:Tell.kt$Tell$50</ID>
@ -50,11 +57,14 @@
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID>
<ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ @Throws(IOException::class, FeedException::class) fun loadEntries(file: String, channel: String, entries: ArrayList&lt;EntryLink&gt;): String</ID>
<ID>NestedBlockDepth:EntriesMgr.kt$EntriesMgr.Companion$ fun saveEntries( bot: Mobibot, entries: List&lt;EntryLink&gt;, history: MutableList&lt;String&gt;, isDayBackup: Boolean )</ID>
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ fun setTags(tags: List&lt;String?&gt;)</ID>
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List&lt;String?&gt;)</ID>
<ID>NestedBlockDepth:FeedReader.kt$FeedReader$ override fun run()</ID>
<ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
<ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
<ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
<ID>NestedBlockDepth:Mobibot.kt$Mobibot$ fun connect()</ID>
<ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID>
<ID>NestedBlockDepth:Mobibot.kt$Mobibot$override fun onPrivateMessage( sender: String, login: String, hostname: String, message: String )</ID>
<ID>NestedBlockDepth:StockQuote.kt$StockQuote$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
<ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>NestedBlockDepth:Tell.kt$Tell$ @JvmOverloads fun send(nickname: String, isMessage: Boolean = false)</ID>
@ -63,14 +73,18 @@
<ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean)</ID>
<ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</ID>
<ID>ReturnCount:Mobibot.kt$Mobibot$override fun onMessage( channel: String, sender: String, login: String, hostname: String, message: String )</ID>
<ID>ReturnCount:Utils.kt$Utils.Companion$ @JvmStatic fun colorize(s: String?, color: String): String</ID>
<ID>ThrowsCount:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List&lt;Message&gt;</ID>
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$@Throws(ModuleException::class) private fun getJsonResponse(response: String, debugMessage: String): JSONObject</ID>
<ID>ThrowsCount:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List&lt;Message&gt;</ID>
<ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID>
<ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$e: Exception</ID>
<ID>TooGenericExceptionCaught:Mobibot.kt$Mobibot$ex: Exception</ID>
<ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
<ID>TooManyFunctions:Mobibot.kt$Mobibot : PircBot</ID>
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
<ID>TooManyFunctions:Utils.kt$Utils$Companion</ID>
</CurrentIssues>

View file

@ -1,36 +0,0 @@
/*
* This file is automatically generated.
* Do not modify! -- ALL CHANGES WILL BE ERASED!
*/
package {{packageName}};
import java.time.*;
/**
* Provides semantic version information.
*
* @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a>
*/
public final class {{className}} {
public static final String PROJECT = "{{project}}";
public static final String VERSION = "{{version}}";
public static final LocalDateTime BUILDDATE =
LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault());
public static final int MAJOR = {{major}};
public static final int MINOR = {{minor}};
public static final int PATCH = {{patch}};
public static final String BUILDMETA = "{{buildMeta}}";
public static final String PRERELEASE = "{{preRelease}}";
public static final String WEBSITE = "https://www.mobitopia.org/mobibot/";
public static final String AUTHOR = "Erik C. Thauvin";
public static final String AUTHOR_URL = "https://erik.thauvin.net/";
/**
* Disables the default constructor.
*/
private {{className}}() {
throw new UnsupportedOperationException("Illegal constructor call.");
}
}

View file

@ -1,36 +0,0 @@
/*
* This file is automatically generated.
* Do not modify! -- ALL CHANGES WILL BE ERASED!
*/
package net.thauvin.erik.mobibot;
import java.time.*;
/**
* Provides semantic version information.
*
* @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a>
*/
public final class ReleaseInfo {
public static final String PROJECT = "mobibot";
public static final String VERSION = "0.8.0-beta+305";
public static final LocalDateTime BUILDDATE =
LocalDateTime.ofInstant(Instant.ofEpochMilli(1607122730368L), ZoneId.systemDefault());
public static final int MAJOR = 0;
public static final int MINOR = 8;
public static final int PATCH = 0;
public static final String BUILDMETA = "305";
public static final String PRERELEASE = "beta";
public static final String WEBSITE = "https://www.mobitopia.org/mobibot/";
public static final String AUTHOR = "Erik C. Thauvin";
public static final String AUTHOR_URL = "https://erik.thauvin.net/";
/**
* Disables the default constructor.
*/
private ReleaseInfo() {
throw new UnsupportedOperationException("Illegal constructor call.");
}
}

View file

@ -1,956 +0,0 @@
/*
* Mobibot.java
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.thauvin.erik.mobibot.commands.AbstractCommand;
import net.thauvin.erik.mobibot.commands.AddLog;
import net.thauvin.erik.mobibot.commands.ChannelFeed;
import net.thauvin.erik.mobibot.commands.Cycle;
import net.thauvin.erik.mobibot.commands.Ignore;
import net.thauvin.erik.mobibot.commands.Info;
import net.thauvin.erik.mobibot.commands.Me;
import net.thauvin.erik.mobibot.commands.Modules;
import net.thauvin.erik.mobibot.commands.Msg;
import net.thauvin.erik.mobibot.commands.Nick;
import net.thauvin.erik.mobibot.commands.Recap;
import net.thauvin.erik.mobibot.commands.Say;
import net.thauvin.erik.mobibot.commands.Users;
import net.thauvin.erik.mobibot.commands.Versions;
import net.thauvin.erik.mobibot.commands.links.Comment;
import net.thauvin.erik.mobibot.commands.links.LinksMgr;
import net.thauvin.erik.mobibot.commands.links.Posting;
import net.thauvin.erik.mobibot.commands.links.Tags;
import net.thauvin.erik.mobibot.commands.links.View;
import net.thauvin.erik.mobibot.commands.tell.Tell;
import net.thauvin.erik.mobibot.entries.EntriesMgr;
import net.thauvin.erik.mobibot.entries.EntryLink;
import net.thauvin.erik.mobibot.modules.AbstractModule;
import net.thauvin.erik.mobibot.modules.Calc;
import net.thauvin.erik.mobibot.modules.CurrencyConverter;
import net.thauvin.erik.mobibot.modules.Dice;
import net.thauvin.erik.mobibot.modules.GoogleSearch;
import net.thauvin.erik.mobibot.modules.Joke;
import net.thauvin.erik.mobibot.modules.Lookup;
import net.thauvin.erik.mobibot.modules.Ping;
import net.thauvin.erik.mobibot.modules.RockPaperScissors;
import net.thauvin.erik.mobibot.modules.StockQuote;
import net.thauvin.erik.mobibot.modules.Twitter;
import net.thauvin.erik.mobibot.modules.War;
import net.thauvin.erik.mobibot.modules.Weather2;
import net.thauvin.erik.mobibot.modules.WorldTime;
import net.thauvin.erik.mobibot.msg.Message;
import net.thauvin.erik.pinboard.PinboardPoster;
import net.thauvin.erik.semver.Version;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.jibble.pircbot.PircBot;
import org.jibble.pircbot.User;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Properties;
import java.util.Timer;
import java.util.logging.ConsoleHandler;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.lowerCase;
/**
* Implements the #mobitopia bot.
*
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
* @created Jan 31, 2004
* @since 1.0
*/
@SuppressWarnings("WeakerAccess")
@Version(properties = "version.properties",
className = "ReleaseInfo")
public class Mobibot extends PircBot {
// Logger
private static final Logger LOGGER = LogManager.getLogger(Mobibot.class);
// Maximum number of times the bot will try to reconnect, if disconnected
private static final int MAX_RECONNECT = 10;
// Timer
private static final Timer TIMER = new Timer(true);
// Commands and Modules
private final Addons addons = new Addons();
// Main channel
private final String ircChannel;
// IRC port
private final int ircPort;
// IRC server
private final String ircServer;
// Logger default level
private final Level loggerLevel;
// Log directory
private final String logsDir;
// Tell command
private final Tell tell;
// Today's date
private final String today = Utils.today();
// Twitter module
private final Twitter twitter;
// Backlogs URL
private String backLogsUrl = "";
// Ident message
private String identMsg = "";
// Ident nick
private String identNick = "";
// NickServ ident password
private String identPwd = "";
// Pinboard posts handler
private PinboardPoster pinboard;
// Weblog URL
private String weblogUrl = "";
/**
* Creates a new {@link Mobibot} instance.
*
* @param channel The irc channel.
* @param nickname The bot's nickname.
* @param logsDirPath The path to the logs directory.
* @param p The bot's properties.
*/
public Mobibot(final String nickname, final String channel, final String logsDirPath, final Properties p) {
super();
System.getProperties().setProperty("sun.net.client.defaultConnectTimeout",
String.valueOf(Constants.CONNECT_TIMEOUT));
System.getProperties().setProperty("sun.net.client.defaultReadTimeout",
String.valueOf(Constants.CONNECT_TIMEOUT));
setName(nickname);
ircServer = p.getProperty("server", Constants.DEFAULT_SERVER);
ircPort = Utils.getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT);
ircChannel = channel;
logsDir = logsDirPath;
// Set the logger level
loggerLevel = LOGGER.getLevel();
// Load the current entries and backlogs, if any
try {
LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, ircChannel);
LOGGER.debug("Last feed: {}", LinksMgr.getStartDate());
} catch (Exception e) {
LOGGER.error("An error occurred while loading the logs.", e);
}
// Initialize the bot
setVerbose(true);
setAutoNickChange(true);
setLogin(p.getProperty("login", getName()));
setVersion(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION);
// setMessageDelay(1000);
setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", ""));
// Set the URLs
setWeblogUrl(p.getProperty("weblog", ""));
setBacklogsUrl(Utils.ensureDir(p.getProperty("backlogs", weblogUrl), true));
// Set the pinboard authentication
setPinboardAuth(p.getProperty("pinboard-api-token"));
// Load the commands
addons.add(new AddLog(this), p);
addons.add(new ChannelFeed(this, getChannelName()), p);
addons.add(new Cycle(this), p);
addons.add(new Ignore(this), p);
addons.add(new Info(this), p);
addons.add(new Me(this), p);
addons.add(new Modules(this), p);
addons.add(new Msg(this), p);
addons.add(new Nick(this), p);
addons.add(new Recap(this), p);
addons.add(new Say(this), p);
addons.add(new Users(this), p);
addons.add(new Versions(this), p);
// Tell command
tell = new Tell(this);
addons.add(tell, p);
// Load the links commands
addons.add(new Comment(this), p);
addons.add(new Posting(this), p);
addons.add(new Tags(this), p);
addons.add(new LinksMgr(this), p);
addons.add(new View(this), p);
// Load the modules
addons.add(new Calc(this), p);
addons.add(new CurrencyConverter(this), p);
addons.add(new Dice(this), p);
addons.add(new GoogleSearch(this), p);
addons.add(new Joke(this), p);
addons.add(new Lookup(this), p);
addons.add(new Ping(this), p);
addons.add(new RockPaperScissors(this), p);
addons.add(new StockQuote(this), p);
addons.add(new War(this), p);
addons.add(new Weather2(this), p);
addons.add(new WorldTime(this), p);
// Twitter module
twitter = new Twitter(this);
addons.add(twitter, p);
// Sort the addons
addons.sort();
// Save the entries
LinksMgr.saveEntries(this, true);
}
/**
* The Truth Is Out There...
*
* @param args The command line arguments.
*/
@SuppressFBWarnings(
{ "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE",
"DM_DEFAULT_ENCODING",
"IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" })
@SuppressWarnings({ "PMD.SystemPrintln", "PMD.AvoidFileStream", "PMD.CloseResource" })
public static void main(final String[] args) {
// Setup the command line options
final Options options = new Options()
.addOption(Constants.HELP_ARG.substring(0, 1),
Constants.HELP_ARG,
false,
"print this help message")
.addOption(Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false,
"print debug & logging data directly to the console")
.addOption(Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg()
.argName("file")
.desc("use " + "alternate properties file")
.longOpt(Constants.PROPS_ARG).build())
.addOption(Constants.VERSION_ARG.substring(0, 1),
Constants.VERSION_ARG,
false,
"print version info");
// Parse the command line
final CommandLineParser parser = new DefaultParser();
CommandLine commandLine = null;
try {
commandLine = parser.parse(options, args);
} catch (ParseException e) {
System.err.println("CLI Parsing failed. Reason: " + e.getMessage());
e.printStackTrace(System.err);
System.exit(1);
}
if (commandLine.hasOption(Constants.HELP_ARG.charAt(0))) {
// Output the usage
new HelpFormatter().printHelp(Mobibot.class.getName(), options);
} else if (commandLine.hasOption(Constants.VERSION_ARG.charAt(0))) {
System.out.println(ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION
+ " (" + Utils.isoLocalDate(ReleaseInfo.BUILDDATE) + ')');
System.out.println(ReleaseInfo.WEBSITE);
} else {
final Properties p = new Properties();
try (final InputStream fis = Files.newInputStream(
Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG.charAt(0), "./mobibot.properties")))) {
// Load the properties files
p.load(fis);
} catch (FileNotFoundException e) {
System.err.println("Unable to find properties file.");
e.printStackTrace(System.err);
System.exit(1);
} catch (IOException e) {
System.err.println("Unable to open properties file.");
e.printStackTrace(System.err);
System.exit(1);
}
final String nickname = p.getProperty("nick", lowerCase(Mobibot.class.getName()));
final String channel = p.getProperty("channel");
final String logsDir = Utils.ensureDir(p.getProperty("logs", "."), false);
// Redirect the stdout and stderr
if (!commandLine.hasOption(Constants.DEBUG_ARG.charAt(0))) {
try {
final PrintStream stdout = new PrintStream(new BufferedOutputStream(new FileOutputStream(
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true)), true);
System.setOut(stdout);
} catch (IOException e) {
System.err.println("Unable to open output (stdout) log file.");
e.printStackTrace(System.err);
System.exit(1);
}
try {
final PrintStream stderr =
new PrintStream(new BufferedOutputStream(
new FileOutputStream(logsDir + nickname + ".err", true)), true);
System.setErr(stderr);
} catch (IOException e) {
System.err.println("Unable to open error (stderr) log file.");
e.printStackTrace(System.err);
System.exit(1);
}
}
// Create the bot
final Mobibot bot = new Mobibot(nickname, channel, logsDir, p);
// Connect
bot.connect();
}
}
/**
* Sends an action to the current channel.
*
* @param action The action.
*/
public final void action(final String action) {
action(ircChannel, action);
}
/**
* Sends an action to the channel.
*
* @param channel The channel.
* @param action The action.
*/
private void action(final String channel, final String action) {
if (isNotBlank(channel) && isNotBlank(action)) {
sendAction(channel, action);
}
}
/**
* Adds pin on pinboard.
*
* @param entry The entry to add.
*/
public final void addPin(final EntryLink entry) {
if (pinboard != null) {
PinboardUtils.addPin(pinboard, ircServer, entry);
}
}
/**
* Connects to the server and joins the channel.
*/
@SuppressFBWarnings({ "DM_EXIT", "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE" })
public final void connect() {
try {
connect(ircServer, ircPort);
} catch (Exception e) {
int retries = 0;
while ((retries++ < MAX_RECONNECT) && !isConnected()) {
sleep(10);
try {
connect(ircServer, ircPort);
} catch (Exception ex) {
if (retries == MAX_RECONNECT) {
LOGGER.debug("Unable to reconnect to {} after {} retries.", ircServer, MAX_RECONNECT, ex);
e.printStackTrace(System.err);
System.exit(1);
}
}
}
}
identify();
joinChannel();
}
/**
* Deletes pin on pinboard.
*
* @param entry The entry to delete.
*/
public final void deletePin(final int index, final EntryLink entry) {
if (pinboard != null) {
PinboardUtils.deletePin(pinboard, entry);
}
if (twitter.isAutoPost()) {
twitter.removeEntry(index);
}
}
/**
* Returns the backlogs URL.
*
* @return The backlogs URL.
*/
public final String getBacklogsUrl() {
return backLogsUrl;
}
/**
* Returns the current channel.
*
* @return The current channel.
*/
public final String getChannel() {
return ircChannel;
}
/**
* Returns the current channel name.
*
* @return The current channel name.
*/
@SuppressFBWarnings("STT_STRING_PARSING_A_FIELD")
public final String getChannelName() {
return ircChannel.substring(1);
}
/**
* Returns the irc server.
*
* @return The irc server.
*/
public final String getIrcServer() {
return ircServer;
}
/**
* Returns the bot's logger.
*
* @return The bot's logger.
*/
public final Logger getLogger() {
return LOGGER;
}
/**
* Returns the log directory.
*
* @return the log directory.
*/
public final String getLogsDir() {
return logsDir;
}
/**
* Returns the enabled modules names.
*
* @return The modules names.
*/
public final List<String> getModulesNames() {
return addons.getModulesNames();
}
/**
* Returns the Tell command.
*
* @return The tell command.
*/
public final Tell getTell() {
return tell;
}
/**
* Returns the bot's timer.
*
* @return The timer.
*/
public final Timer getTimer() {
return TIMER;
}
/**
* Get today's date for the feed.
*
* @return Today's date.
*/
public String getToday() {
return today;
}
/**
* Returns the Twitter command.
*
* @return The Twitter command.
*/
public final Twitter getTwitter() {
return twitter;
}
/**
* Returns the weblog URL.
*
* @return The weblog URL.
*/
public final String getWeblogUrl() {
return weblogUrl;
}
/**
* Responds with the commands help, if any.
*
* @param sender The nick of the person requesting Constants.
* @param topic The help topic.
* @param isPrivate The private flag.
* @return {@code true} if the topic was found, {@code false} otherwise.
*/
private boolean helpCommands(final String sender, final String topic, final boolean isPrivate) {
for (final AbstractCommand command : addons.getCommands()) {
if (command.isVisible() && command.getName().startsWith(topic)) {
return command.helpResponse(topic, sender, isOp(sender), isPrivate);
}
}
return false;
}
/**
* Responds with the default Constants.
*
* @param sender The nick of the person requesting Constants.
* @param isOp The channel operator flag.
* @param isPrivate The private flag.
*/
public void helpDefault(final String sender, final boolean isOp, final boolean isPrivate) {
send(sender, "Type a URL on " + ircChannel + " to post it.", isPrivate);
send(sender, "For more information on a specific command, type:", isPrivate);
send(sender,
Utils.helpIndent(Utils.helpFormat("%c " + Constants.HELP_CMD + " <command>", getNick(), isPrivate)),
isPrivate);
send(sender, "The commands are:", isPrivate);
sendList(sender, addons.getNames(), 8, isPrivate, true);
if (isOp) {
send(sender, "The op commands are:", isPrivate);
sendList(sender, addons.getOps(), 8, isPrivate, true);
}
}
/**
* Responds with the modules help, if any.
*
* @param sender The nick of the person requesting Constants.
* @param topic The help topic.
* @param isPrivate The private flag.
* @return {@code true} if the topic was found, {@code false} otherwise.
*/
private boolean helpModules(final String sender, final String topic, final boolean isPrivate) {
for (final AbstractModule module : addons.getModules()) {
for (final String cmd : module.commands) {
if (topic.equals(cmd)) {
module.helpResponse(sender, isPrivate);
return true;
}
}
}
return false;
}
/**
* Responds with the bot's Constants.
*
* @param sender The nick of the person who sent the private message.
* @param topic The help topic, if any.
* @param isPrivate The private flag.
*/
private void helpResponse(final String sender, final String topic, final boolean isPrivate) {
final boolean isOp = isOp(sender);
if (StringUtils.isBlank(topic)) {
helpDefault(sender, isOp, isPrivate);
} else {
// Command, Modules or Default
if (!helpCommands(sender, topic, isPrivate) && !helpModules(sender, lowerCase(topic).trim(), isPrivate)) {
helpDefault(sender, isOp, isPrivate);
}
}
}
/**
* Identifies the bot.
*/
private void identify() {
// Identify with NickServ
if (isNotBlank(identPwd)) {
identify(identPwd);
}
// Identify with a specified nick
if (isNotBlank(identNick) && isNotBlank(identMsg)) {
sendMessage(identNick, identMsg);
}
}
/**
* Returns <code>true</code> if the specified sender is an Op on the {@link #ircChannel channel}.
*
* @param sender The sender.
* @return true, if the sender is an Op.
*/
public boolean isOp(final String sender) {
final User[] users = getUsers(ircChannel);
for (final User user : users) {
if (user.getNick().equals(sender)) {
return user.isOp();
}
}
return false;
}
/**
* Joins the bot's channel.
*/
public final void joinChannel() {
joinChannel(ircChannel);
twitter.notification(getName() + " " + ReleaseInfo.VERSION + " has joined " + getChannel());
}
/**
* {@inheritDoc}
*/
@Override
protected final void onDisconnect() {
if (isNotBlank(weblogUrl)) {
setVersion(weblogUrl);
}
sleep(5);
connect();
}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY",
justification = "Working on it.")
@Override
protected final void onMessage(final String channel, final String sender, final String login, final String hostname,
final String message) {
LOGGER.debug(">>> {} : {}", sender, message);
tell.send(sender, true);
if (message.matches("(?i)" + Pattern.quote(getNick()) + ":.*")) { // mobibot: <command>
final String[] cmds = message.substring(message.indexOf(':') + 1).trim().split(" ", 2);
final String cmd = lowerCase(cmds[0]);
String args = "";
if (cmds.length > 1) {
args = cmds[1].trim();
}
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
helpResponse(sender, args, false);
} else {
// Commands
for (final AbstractCommand command : addons.getCommands()) {
if (command.isPublic() && command.getName().startsWith(cmd)) {
command.commandResponse(sender, login, args, isOp(sender), false);
return;
}
}
// Modules
for (final AbstractModule module : addons.getModules()) { // modules
for (final String c : module.commands) {
if (cmd.startsWith(c)) {
module.commandResponse(sender, cmd, args, false);
return;
}
}
}
}
} else {
// Commands, e.g.: https://www.example.com/
for (final AbstractCommand command : addons.getCommands()) {
if (command.matches(message)) {
command.commandResponse(sender, login, message, isOp(sender), false);
return;
}
}
}
Recap.storeRecap(sender, message, false);
}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = { "DM_EXIT", "CC_CYCLOMATIC_COMPLEXITY" },
justification = "Yes, we want to bail out.")
@Override
protected final void onPrivateMessage(final String sender, final String login, final String hostname,
final String message) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(">>> {} : {}", sender, message);
}
final String[] cmds = message.split(" ", 2);
final String cmd = lowerCase(cmds[0]);
String args = "";
if (cmds.length > 1) {
args = cmds[1].trim();
}
final boolean isOp = isOp(sender);
if (cmd.startsWith(Constants.HELP_CMD)) { // help
helpResponse(sender, args, true);
} else if (isOp && "kill".equals(cmd)) { // kill
twitter.notification(getName() + " killed by " + sender + " on " + getChannel());
sendRawLine("QUIT : Poof!");
System.exit(0);
} else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug
if (LOGGER.isDebugEnabled()) {
Configurator.setLevel(LOGGER.getName(), loggerLevel);
} else {
Configurator.setLevel(LOGGER.getName(), Level.DEBUG);
}
send(sender, "Debug logging is " + (LOGGER.isDebugEnabled() ? "enabled." : "disabled."), true);
} else if (isOp && Constants.DIE_CMD.equals(cmd)) { // die
send(sender + " has just signed my death sentence.");
TIMER.cancel();
twitter.shutdown();
twitter.notification(getName() + " stopped by " + sender + " on " + getChannel());
sleep(3);
quitServer("The Bot Is Out There!");
System.exit(0);
} else {
for (final AbstractCommand command : addons.getCommands()) {
if (command.getName().startsWith(cmd)) {
command.commandResponse(sender, login, args, isOp, true);
return;
}
}
for (final AbstractModule module : addons.getModules()) {
if (module.isPrivateMsgEnabled()) {
for (final String c : module.commands) {
if (cmd.equals(c)) {
module.commandResponse(sender, cmd, args, true);
return;
}
}
}
}
helpDefault(sender, isOp, true);
}
}
/**
* {@inheritDoc}
*/
@Override
protected final void onAction(final String sender, final String login, final String hostname, final String target,
final String action) {
if (ircChannel.equals(target)) {
Recap.storeRecap(sender, action, true);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onJoin(final String channel, final String sender, final String login, final String hostname) {
tell.send(sender);
}
/**
* {@inheritDoc}
*/
@Override
protected void onNickChange(final String oldNick, final String login, final String hostname, final String newNick) {
tell.send(newNick);
}
/**
* Sends a private message or notice.
*
* @param sender The channel or nick of the person who sent the message.
* @param message The actual message.
* @param isPrivate Set to <code>true</code> if the response should be a private message, otherwise a notice is
* sent.
*/
public final void send(final String sender, final String message, final boolean isPrivate) {
if (isNotBlank(message) && isNotBlank(sender)) {
if (isPrivate) {
LOGGER.debug("Sending message to {} : {}", sender, message);
sendMessage(sender, message);
} else {
LOGGER.debug("Sending notice to {} : {}", sender, message);
sendNotice(sender, message);
}
}
}
/**
* Sends a notice to the channel.
*
* @param notice The notice message.
*/
public final void send(final String notice) {
send(getChannel(), notice, false);
}
/**
* Sends a message.
*
* @param who The channel or nick of the person who sent the command.
* @param message The message.
*/
public final void send(final String who, final Message message) {
send(message.isNotice() ? who : getChannel(), message.getMsg(), message.getColor(), message.isPrivate());
}
/**
* Sends a message.
*
* @param who The channel or nick of the person who sent the command.
* @param message The actual message.
* @param color The message's color.
* @param isPrivate The private flag.
*/
public final void send(final String who, final String message, final String color, final boolean isPrivate) {
send(who, Utils.colorize(message, color), isPrivate);
}
/**
* Send a formatted commands/modules, etc. list.
*
* @param nick The nick to send the list to.
* @param list The list to format.
* @param size The number of items per line.
* @param isPrivate The private flag.
* @param isBold The bold flag
*/
public final void sendList(final String nick,
final List<String> list,
final int size,
final boolean isPrivate,
final boolean isBold) {
for (int i = 0; i < list.size(); i += size) {
send(nick, Utils.helpIndent(
String.join(" ", list.subList(i, Math.min(list.size(), i + size))), isBold), isPrivate);
}
}
/**
* Sets the backlogs URL.
*
* @param url The backlogs URL.
*/
final void setBacklogsUrl(final String url) {
backLogsUrl = url;
}
/**
* Sets the bot's identification.
*
* @param pwd The password for NickServ, if any.
* @param nick The ident nick name.
* @param msg The ident message.
*/
final void setIdentity(final String pwd, final String nick, final String msg) {
identPwd = pwd;
identNick = nick;
identMsg = msg;
}
/**
* Sets the pinboard authentication.
*
* @param apiToken The API token
*/
final void setPinboardAuth(final String apiToken) {
if (isNotBlank(apiToken)) {
pinboard = new PinboardPoster(apiToken);
if (LOGGER.isDebugEnabled()) {
final ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(java.util.logging.Level.FINE);
pinboard.getLogger().addHandler(consoleHandler);
pinboard.getLogger().setLevel(java.util.logging.Level.FINE);
}
}
}
/**
* Sets the weblog URL.
*
* @param url The weblog URL.
*/
final void setWeblogUrl(final String url) {
weblogUrl = url;
}
/**
* Sleeps for the specified number of seconds.
*
* @param secs The number of seconds to sleep for.
*/
public final void sleep(final int secs) {
try {
Thread.sleep(secs * 1000L);
} catch (InterruptedException ignore) {
// Do nothing
}
}
/**
* Updates pin on pinboard.
*
* @param oldUrl The old pin url.
* @param entry The entry to update.
*/
public final void updatePin(final String oldUrl, final EntryLink entry) {
if (pinboard != null) {
PinboardUtils.updatePin(pinboard, ircServer, oldUrl, entry);
}
}
}

View file

@ -0,0 +1,776 @@
/*
* Mobibot.kt
*
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.mobibot
import net.thauvin.erik.mobibot.PinboardUtils.addPin
import net.thauvin.erik.mobibot.PinboardUtils.deletePin
import net.thauvin.erik.mobibot.PinboardUtils.updatePin
import net.thauvin.erik.mobibot.Utils.Companion.colorize
import net.thauvin.erik.mobibot.Utils.Companion.ensureDir
import net.thauvin.erik.mobibot.Utils.Companion.getIntProperty
import net.thauvin.erik.mobibot.Utils.Companion.helpFormat
import net.thauvin.erik.mobibot.Utils.Companion.helpIndent
import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate
import net.thauvin.erik.mobibot.Utils.Companion.today
import net.thauvin.erik.mobibot.commands.AddLog
import net.thauvin.erik.mobibot.commands.ChannelFeed
import net.thauvin.erik.mobibot.commands.Cycle
import net.thauvin.erik.mobibot.commands.Ignore
import net.thauvin.erik.mobibot.commands.Info
import net.thauvin.erik.mobibot.commands.Me
import net.thauvin.erik.mobibot.commands.Modules
import net.thauvin.erik.mobibot.commands.Msg
import net.thauvin.erik.mobibot.commands.Nick
import net.thauvin.erik.mobibot.commands.Recap
import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap
import net.thauvin.erik.mobibot.commands.Say
import net.thauvin.erik.mobibot.commands.Users
import net.thauvin.erik.mobibot.commands.Versions
import net.thauvin.erik.mobibot.commands.links.Comment
import net.thauvin.erik.mobibot.commands.links.LinksMgr
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.saveEntries
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startDate
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.startup
import net.thauvin.erik.mobibot.commands.links.Posting
import net.thauvin.erik.mobibot.commands.links.Tags
import net.thauvin.erik.mobibot.commands.links.View
import net.thauvin.erik.mobibot.commands.tell.Tell
import net.thauvin.erik.mobibot.entries.EntriesMgr
import net.thauvin.erik.mobibot.entries.EntryLink
import net.thauvin.erik.mobibot.modules.Calc
import net.thauvin.erik.mobibot.modules.CurrencyConverter
import net.thauvin.erik.mobibot.modules.Dice
import net.thauvin.erik.mobibot.modules.GoogleSearch
import net.thauvin.erik.mobibot.modules.Joke
import net.thauvin.erik.mobibot.modules.Lookup
import net.thauvin.erik.mobibot.modules.Ping
import net.thauvin.erik.mobibot.modules.RockPaperScissors
import net.thauvin.erik.mobibot.modules.StockQuote
import net.thauvin.erik.mobibot.modules.Twitter
import net.thauvin.erik.mobibot.modules.War
import net.thauvin.erik.mobibot.modules.Weather2
import net.thauvin.erik.mobibot.modules.WorldTime
import net.thauvin.erik.mobibot.msg.Message
import net.thauvin.erik.pinboard.PinboardPoster
import net.thauvin.erik.semver.Version
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.CommandLineParser
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.apache.logging.log4j.core.config.Configurator
import org.jibble.pircbot.PircBot
import java.io.BufferedOutputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.PrintStream
import java.lang.String.join
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import java.util.logging.ConsoleHandler
import java.util.regex.Pattern
import kotlin.system.exitProcess
/**
* Implements the #mobitopia bot.
*/
@Version(properties = "version.properties", className = "ReleaseInfo", template = "ReleaseInfo.mustache", type = "kt")
class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Properties) : PircBot() {
// Commands and Modules
private val addons = Addons()
/** Main channel. */
val channel: String
// IRC port
private val ircPort: Int
/** IRC server. */
val ircServer: String
/** Logger. */
val logger: Logger = LogManager.getLogger(Mobibot::class.java)
// Logger default level
private val loggerLevel: Level
/** Log directory. */
val logsDir: String
// Pinboard posts handler
private val pinboard: PinboardPoster = PinboardPoster()
/** Tell command. */
val tell: Tell
/** Today's date. */
val today = today()
/** Twitter module. */
val twitter: Twitter
/** The backlogs URL. */
var backlogsUrl = ""
// Ident message
private var identMsg = ""
// Ident nick
private var identNick = ""
// NickServ ident password
private var identPwd = ""
// Is pinboard enabled?
private var isPinboardEnabled = false
/** Timer. */
val timer = Timer(true)
/** Weblog URL */
var weblogUrl = ""
/** The current channel name. */
private val channelName: String
get() = channel.substring(1)
/** The enabled modules names. */
val modulesNames: List<String>
get() = addons.modulesNames
/**
* Sends an action to the current channel.
*/
fun action(action: String) {
action(channel, action)
}
/**
* Sends an action to the channel.
*/
private fun action(channel: String, action: String) {
if (channel.isNotBlank() && action.isNotBlank()) {
sendAction(channel, action)
}
}
/**
* Adds pin on pinboard.
*/
fun addPin(entry: EntryLink) {
if (isPinboardEnabled) {
addPin(pinboard, ircServer, entry)
}
}
/**
* Connects to the server and joins the channel.
*/
fun connect() {
try {
connect(ircServer, ircPort)
} catch (e: Exception) {
var retries = 0
while (retries++ < MAX_RECONNECT && !isConnected) {
sleep(10)
try {
connect(ircServer, ircPort)
} catch (ex: Exception) {
if (retries == MAX_RECONNECT) {
logger.debug("Unable to reconnect to $ircServer, after $MAX_RECONNECT retries.", ex)
e.printStackTrace(System.err)
exitProcess(1)
}
}
}
}
identify()
joinChannel()
}
/**
* Deletes pin on pinboard.
*/
fun deletePin(index: Int, entry: EntryLink) {
if (isPinboardEnabled) {
deletePin(pinboard, entry)
}
if (twitter.isAutoPost) {
twitter.removeEntry(index)
}
}
/**
* Responds with the commands help, if any.
*/
private fun helpCommands(sender: String, topic: String, isPrivate: Boolean): Boolean {
for (command in addons.commands) {
if (command.isVisible && command.name.startsWith(topic)) {
return command.helpResponse(topic, sender, isOp(sender), isPrivate)
}
}
return false
}
/**
* Responds with the default help.
*/
fun helpDefault(sender: String, isOp: Boolean, isPrivate: Boolean) {
send(sender, "Type a URL on $channel to post it.", isPrivate)
send(sender, "For more information on a specific command, type:", isPrivate)
send(
sender,
helpIndent(helpFormat("""%c ${Constants.HELP_CMD} <command>""", nick, isPrivate)),
isPrivate
)
send(sender, "The commands are:", isPrivate)
sendList(sender, addons.names, 8, isPrivate, true)
if (isOp) {
send(sender, "The op commands are:", isPrivate)
sendList(sender, addons.ops, 8, isPrivate, true)
}
}
/**
* Responds with the modules help, if any.
*/
private fun helpModules(sender: String, topic: String, isPrivate: Boolean): Boolean {
for (module in addons.modules) {
for (cmd in module.commands) {
if (topic == cmd) {
module.helpResponse(sender, isPrivate)
return true
}
}
}
return false
}
/**
* Responds with the default, commands or modules help.
*/
private fun helpResponse(sender: String, topic: String, isPrivate: Boolean) {
val isOp = isOp(sender)
if (topic.isBlank()) {
helpDefault(sender, isOp, isPrivate)
} else {
// Command, Modules or Default
if (!helpCommands(sender, topic, isPrivate) && !helpModules(
sender,
topic.toLowerCase().trim(),
isPrivate
)
) {
helpDefault(sender, isOp, isPrivate)
}
}
}
/**
* Identifies the bot.
*/
private fun identify() {
// Identify with NickServ
if (identPwd.isNotBlank()) {
identify(identPwd)
}
// Identify with a specified nick
if (identNick.isNotBlank() && identMsg.isNotBlank()) {
sendMessage(identNick, identMsg)
}
}
/**
* Returns {@code true} if the specified sender is an Op on the [channel][.ircChannel].
*/
fun isOp(sender: String): Boolean {
for (user in getUsers(channel)) {
if (user.nick == sender) {
return user.isOp
}
}
return false
}
/**
* Joins the bot's channel.
*/
private fun joinChannel() {
joinChannel(channel)
twitter.notification("$name ${ReleaseInfo.VERSION} has joined $channel")
}
override fun onDisconnect() {
if (weblogUrl.isNotBlank()) {
version = weblogUrl
}
sleep(5)
connect()
}
override fun onMessage(
channel: String,
sender: String,
login: String,
hostname: String,
message: String
) {
logger.debug(">>> $sender: $message")
tell.send(sender, true)
if (message.matches("(?i)${Pattern.quote(nick)}:.*".toRegex())) { // mobibot: <command>
val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2).toTypedArray()
val cmd = cmds[0].toLowerCase()
val args = if (cmds.size > 1) {
cmds[1].trim()
} else ""
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
helpResponse(sender, args, false)
} else {
// Commands
for (command in addons.commands) {
if (command.isPublic && command.name.startsWith(cmd)) {
command.commandResponse(sender, login, args, isOp(sender), false)
return
}
}
// Modules
for (module in addons.modules) { // modules
for (c in module.commands) {
if (cmd.startsWith(c)) {
module.commandResponse(sender, cmd, args, false)
return
}
}
}
}
} else {
// Commands, e.g.: https://www.example.com/
for (command in addons.commands) {
if (command.matches(message)) {
command.commandResponse(sender, login, message, isOp(sender), false)
return
}
}
}
storeRecap(sender, message, false)
}
override fun onPrivateMessage(
sender: String,
login: String,
hostname: String,
message: String
) {
if (logger.isDebugEnabled) {
logger.debug(">>> $sender : $message")
}
val cmds = message.split(" ".toRegex(), 2).toTypedArray()
val cmd = cmds[0].toLowerCase()
val args = if (cmds.size > 1) {
cmds[1].trim()
} else ""
val isOp = isOp(sender)
if (cmd.startsWith(Constants.HELP_CMD)) { // help
helpResponse(sender, args, true)
} else if (isOp && "kill" == cmd) { // kill
twitter.notification("$name killed by $sender on $channel")
sendRawLine("QUIT : Poof!")
exitProcess(0)
} else if (isOp && Constants.DEBUG_CMD == cmd) { // debug
if (logger.isDebugEnabled) {
Configurator.setLevel(logger.name, loggerLevel)
} else {
Configurator.setLevel(logger.name, Level.DEBUG)
}
send(sender, "Debug logging is " + if (logger.isDebugEnabled) "enabled." else "disabled.", true)
} else if (isOp && Constants.DIE_CMD == cmd) { // die
send("$sender has just signed my death sentence.")
timer.cancel()
twitter.shutdown()
twitter.notification("$name stopped by $sender on $channel")
sleep(3)
quitServer("The Bot Is Out There!")
exitProcess(0)
} else {
for (command in addons.commands) {
if (command.name.startsWith(cmd)) {
command.commandResponse(sender, login, args, isOp, true)
return
}
}
for (module in addons.modules) {
if (module.isPrivateMsgEnabled) {
for (c in module.commands) {
if (cmd == c) {
module.commandResponse(sender, cmd, args, true)
return
}
}
}
}
helpDefault(sender, isOp, true)
}
}
override fun onAction(sender: String, login: String, hostname: String, target: String, action: String) {
if (channel == target) {
storeRecap(sender, action, true)
}
}
override fun onJoin(channel: String, sender: String, login: String, hostname: String) {
tell.send(sender)
}
override fun onNickChange(oldNick: String, login: String, hostname: String, newNick: String) {
tell.send(newNick)
}
/**
* Sends a private message or notice.
*/
fun send(sender: String, message: String?, isPrivate: Boolean) {
if (message != null && sender.isNotBlank()) {
if (isPrivate) {
logger.debug("Sending message to $sender : $message")
sendMessage(sender, message)
} else {
logger.debug("Sending notice to $sender: $message")
sendNotice(sender, message)
}
}
}
/**
* Sends a notice to the channel.
*/
fun send(notice: String?) {
if (notice != null) send(channel, notice, false)
}
/**
* Sends a message.
*/
fun send(who: String, message: Message) {
send(if (message.isNotice) who else channel, message.msg, message.color, message.isPrivate)
}
/**
* Sends a message.
*/
fun send(who: String, message: String, color: String, isPrivate: Boolean) {
send(who, colorize(message, color), isPrivate)
}
/**
* Send a formatted commands/modules, etc. list.
*/
fun sendList(
nick: String,
list: List<String>,
size: Int,
isPrivate: Boolean,
isBold: Boolean
) {
var i = 0
while (i < list.size) {
send(
nick,
helpIndent(join(" ", list.subList(i, list.size.coerceAtMost(i + size))), isBold),
isPrivate
)
i += size
}
}
/**
* Sets the bot's identification.
*/
private fun setIdentity(pwd: String, nick: String, msg: String) {
identPwd = pwd
identNick = nick
identMsg = msg
}
/**
* Sets the pinboard authentication.
*/
private fun setPinboardAuth(apiToken: String) {
if (apiToken.isNotBlank()) {
pinboard.apiToken = apiToken
isPinboardEnabled = true
if (logger.isDebugEnabled) {
val consoleHandler = ConsoleHandler()
consoleHandler.level = java.util.logging.Level.FINE
pinboard.logger.addHandler(consoleHandler)
pinboard.logger.level = java.util.logging.Level.FINE
}
}
}
/**
* Sleeps for the specified number of seconds.
*/
fun sleep(secs: Int) {
try {
Thread.sleep(secs * 1000L)
} catch (ignore: InterruptedException) {
// Do nothing
}
}
/**
* Updates pin on pinboard.
*/
fun updatePin(oldUrl: String, entry: EntryLink) {
if (isPinboardEnabled) {
updatePin(pinboard, ircServer, oldUrl, entry)
}
}
companion object {
// Maximum number of times the bot will try to reconnect, if disconnected
private const val MAX_RECONNECT = 10
/**
* The Truth is Out There!
*/
@JvmStatic
fun main(args: Array<String>) {
// Setup the command line options
val options = Options()
.addOption(
Constants.HELP_ARG.substring(0, 1),
Constants.HELP_ARG,
false,
"print this help message"
)
.addOption(
Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false,
"print debug & logging data directly to the console"
)
.addOption(
Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg()
.argName("file")
.desc("use " + "alternate properties file")
.longOpt(Constants.PROPS_ARG).build()
)
.addOption(
Constants.VERSION_ARG.substring(0, 1),
Constants.VERSION_ARG,
false,
"print version info"
)
// Parse the command line
val parser: CommandLineParser = DefaultParser()
val commandLine: CommandLine
try {
commandLine = parser.parse(options, args)
} catch (e: ParseException) {
System.err.println("CLI Parsing failed. Reason: ${e.message}")
e.printStackTrace(System.err)
exitProcess(1)
}
when {
commandLine.hasOption(Constants.HELP_ARG[0]) -> {
// Output the usage
HelpFormatter().printHelp(Mobibot::class.java.name, options)
}
commandLine.hasOption(Constants.VERSION_ARG[0]) -> {
println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${isoLocalDate(ReleaseInfo.BUILDDATE)})")
println(ReleaseInfo.WEBSITE)
}
else -> {
val p = Properties()
try {
Files.newInputStream(
Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties"))
).use { fis ->
// Load the properties files
p.load(fis)
}
} catch (e: FileNotFoundException) {
System.err.println("Unable to find properties file.")
e.printStackTrace(System.err)
exitProcess(1)
} catch (e: IOException) {
System.err.println("Unable to open properties file.")
e.printStackTrace(System.err)
exitProcess(1)
}
val nickname = p.getProperty("nick", Mobibot::class.java.name.toLowerCase())
val channel = p.getProperty("channel")
val logsDir = ensureDir(p.getProperty("logs", "."), false)
// Redirect the stdout and stderr
if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) {
try {
val stdout = PrintStream(
BufferedOutputStream(
FileOutputStream(
logsDir + channel.substring(1) + '.' + today() + ".log", true
)
), true
)
System.setOut(stdout)
} catch (e: IOException) {
System.err.println("Unable to open output (stdout) log file.")
e.printStackTrace(System.err)
exitProcess(1)
}
try {
val stderr = PrintStream(
BufferedOutputStream(
FileOutputStream("$logsDir$nickname.err", true)
), true
)
System.setErr(stderr)
} catch (e: IOException) {
System.err.println("Unable to open error (stderr) log file.")
e.printStackTrace(System.err)
exitProcess(1)
}
}
// Create the bot
val bot = Mobibot(nickname, channel, logsDir, p)
// Connect
bot.connect()
}
}
}
}
/**
* Initialize the bot.
*/
init {
System.getProperties().setProperty(
"sun.net.client.defaultConnectTimeout",
java.lang.String.valueOf(Constants.CONNECT_TIMEOUT)
)
System.getProperties().setProperty(
"sun.net.client.defaultReadTimeout",
java.lang.String.valueOf(Constants.CONNECT_TIMEOUT)
)
name = nickname
ircServer = p.getProperty("server", Constants.DEFAULT_SERVER)
ircPort = getIntProperty(p.getProperty("port"), Constants.DEFAULT_PORT)
this.channel = channel
logsDir = logsDirPath
// Set the logger level
loggerLevel = logger.level
// Load the current entries and backlogs, if any
try {
startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel)
logger.debug("Last feed: $startDate")
} catch (e: Exception) {
logger.error("An error occurred while loading the logs.", e)
}
// Initialize the bot
setVerbose(true)
setAutoNickChange(true)
login = p.getProperty("login", name)
version = ReleaseInfo.PROJECT + ' ' + ReleaseInfo.VERSION
// setMessageDelay(1000);
setIdentity(p.getProperty("ident", ""), p.getProperty("ident-nick", ""), p.getProperty("ident-msg", ""))
// Set the URLs
weblogUrl = p.getProperty("weblog", "")
backlogsUrl = ensureDir(p.getProperty("backlogs", weblogUrl), true)
// Set the pinboard authentication
setPinboardAuth(p.getProperty("pinboard-api-token"))
// Load the commands
addons.add(AddLog(this), p)
addons.add(ChannelFeed(this, channelName), p)
addons.add(Cycle(this), p)
addons.add(Ignore(this), p)
addons.add(Info(this), p)
addons.add(Me(this), p)
addons.add(Modules(this), p)
addons.add(Msg(this), p)
addons.add(Nick(this), p)
addons.add(Recap(this), p)
addons.add(Say(this), p)
addons.add(Users(this), p)
addons.add(Versions(this), p)
// Tell command
tell = Tell(this)
addons.add(tell, p)
// Load the links commands
addons.add(Comment(this), p)
addons.add(Posting(this), p)
addons.add(Tags(this), p)
addons.add(LinksMgr(this), p)
addons.add(View(this), p)
// Load the modules
addons.add(Calc(this), p)
addons.add(CurrencyConverter(this), p)
addons.add(Dice(this), p)
addons.add(GoogleSearch(this), p)
addons.add(Joke(this), p)
addons.add(Lookup(this), p)
addons.add(Ping(this), p)
addons.add(RockPaperScissors(this), p)
addons.add(StockQuote(this), p)
addons.add(War(this), p)
addons.add(Weather2(this), p)
addons.add(WorldTime(this), p)
// Twitter module
twitter = Twitter(this)
addons.add(twitter, p)
// Sort the addons
addons.sort()
// Save the entries
saveEntries(this, true)
}
}

View file

@ -134,7 +134,7 @@ class Posting(bot: Mobibot) : AbstractCommand(bot) {
}
private fun removeEntry(sender: String, login: String, isOp: Boolean, index: Int) {
val entry: EntryLink = LinksMgr.entries[index]
val entry: EntryLink = entries[index]
if (entry.login == login || isOp) {
bot.deletePin(index, entry)
entries.removeAt(index)

View file

@ -41,7 +41,6 @@ import net.thauvin.erik.mobibot.Utils.Companion.reverseColor
import net.thauvin.erik.mobibot.Utils.Companion.utcDateTime
import net.thauvin.erik.mobibot.commands.AbstractCommand
import net.thauvin.erik.mobibot.commands.links.View
import org.apache.commons.lang3.StringUtils
import java.util.concurrent.CopyOnWriteArrayList
/**
@ -143,7 +142,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
isPrivate: Boolean
) {
if (isEnabled()) {
if (StringUtils.isBlank(args)) {
if (args.isBlank()) {
helpResponse(args, sender, isOp, isPrivate)
} else if (args.startsWith(View.VIEW_CMD)) {
if (bot.isOp(sender) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
@ -178,12 +177,13 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) {
// New message.
private fun newMessage(sender: String, args: String, isOp: Boolean, isPrivate: Boolean) {
val split = args.split(" ".toRegex(), 2).toTypedArray()
if (split.size == 2 && StringUtils.isNotBlank(split[1]) && split[1].contains(" ")) {
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
if (messages.size < maxSize) {
val message = TellMessage(sender, split[0], split[1].trim())
messages.add(message)
save()
bot.send(sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate
bot.send(
sender, "Message [ID ${message.id}] was queued for ${bold(message.recipient)}", isPrivate
)
} else {
bot.send(sender, "Sorry, the messages queue is currently full.", isPrivate)

View file

@ -32,18 +32,17 @@
package net.thauvin.erik.mobibot.commands.tell
import org.apache.logging.log4j.Logger
import java.time.LocalDateTime
import java.io.ObjectInputStream
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileNotFoundException
import java.io.IOException
import java.lang.ClassNotFoundException
import java.io.BufferedOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.nio.file.Files
import java.nio.file.Paths
import java.time.Clock
import java.util.ArrayList
import java.time.LocalDateTime
import java.util.*
/**
* The Tell Messages Manager.

View file

@ -41,7 +41,6 @@ import com.rometools.rome.io.SyndFeedInput
import com.rometools.rome.io.SyndFeedOutput
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils.Companion.isoLocalDate
import org.apache.commons.lang3.StringUtils
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStreamWriter
@ -143,7 +142,7 @@ class EntriesMgr private constructor() {
if (bot.logger.isDebugEnabled) {
bot.logger.debug("Saving the feeds...")
}
if (StringUtils.isNotBlank(bot.logsDir) && StringUtils.isNotBlank(bot.weblogUrl)) {
if (bot.logsDir.isNotBlank() && bot.weblogUrl.isNotBlank()) {
try {
val output = SyndFeedOutput()
var rss: SyndFeed = SyndFeedImpl()
@ -208,7 +207,7 @@ class EntriesMgr private constructor() {
), StandardCharsets.UTF_8
).use { fw -> output.output(rss, fw) }
if (isDayBackup) {
if (StringUtils.isNotBlank(bot.backlogsUrl)) {
if (bot.backlogsUrl.isNotBlank()) {
if (!history.contains(bot.today)) {
history.add(bot.today)
while (history.size > MAX_BACKLOGS) {

View file

@ -53,7 +53,7 @@ class EntryLink : Serializable {
var channel: String
// Creation date
var date = Calendar.getInstance().time
var date: Date = Calendar.getInstance().time
// Link's URL
var link: String
@ -178,12 +178,12 @@ class EntryLink : Serializable {
/**
* Sets the tags.
*/
fun setTags(tags: List<String?>) {
if (!tags.isEmpty()) {
private fun setTags(tags: List<String?>) {
if (tags.isNotEmpty()) {
var category: SyndCategoryImpl
for (tag in tags) {
if (StringUtils.isNoneBlank(tag)) {
val t = StringUtils.lowerCase(tag)
if (!tag.isNullOrBlank()) {
val t = tag.toLowerCase()
val mod = t[0]
if (mod == '-') {
// Don't remove the channel tag

View file

@ -33,7 +33,6 @@ package net.thauvin.erik.mobibot.modules
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import org.apache.commons.lang3.StringUtils
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@ -113,7 +112,7 @@ abstract class AbstractModule(val bot: Mobibot) {
open val isValidProperties: Boolean
get() {
for (s in propertyKeys) {
if (StringUtils.isBlank(properties[s])) {
if (properties[s].isNullOrBlank()) {
return false
}
}
@ -124,7 +123,7 @@ abstract class AbstractModule(val bot: Mobibot) {
* Sets a property key and value.
*/
fun setProperty(key: String, value: String) {
if (StringUtils.isNotBlank(key)) {
if (key.isNotBlank()) {
properties[key] = value
}
}

View file

@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.modules
import net.objecthunter.exp4j.ExpressionBuilder
import net.thauvin.erik.mobibot.Mobibot
import net.thauvin.erik.mobibot.Utils
import org.apache.commons.lang3.StringUtils
import java.text.DecimalFormat
/**
@ -47,7 +46,7 @@ class Calc(bot: Mobibot) : AbstractModule(bot) {
args: String,
isPrivate: Boolean
) {
if (StringUtils.isNotBlank(args)) {
if (args.isNotBlank()) {
bot.send(calc(args))
} else {
helpResponse(sender, isPrivate)

View file

@ -157,8 +157,8 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
if (cmds[3] == cmds[1] || "0" == cmds[0]) {
PublicMessage("You're kidding, right?")
} else {
val to = StringUtils.upperCase(cmds[1])
val from = StringUtils.upperCase(cmds[3])
val to = cmds[1].toUpperCase()
val from = cmds[3].toUpperCase()
if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) {
try {
val amt = cmds[0].replace(",", "").toDouble()
@ -166,10 +166,10 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
val doubleTo = EXCHANGE_RATES[from]!!.toDouble()
PublicMessage(
NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1)
+ " ${StringUtils.upperCase(cmds[1])} = "
+ " ${cmds[1].toUpperCase()} = "
+ NumberFormat.getCurrencyInstance(Constants.LOCALE)
.format(amt * doubleTo / doubleFrom).substring(1)
+ " ${StringUtils.upperCase(cmds[3])}"
+ " ${cmds[3].toUpperCase()}"
)
} catch (e: NumberFormatException) {
ErrorMessage("Let's try with some real numbers next time, okay?")

View file

@ -52,7 +52,7 @@ class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
*/
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
with(bot) {
if (StringUtils.isNotBlank(args)) {
if (args.isNotBlank()) {
try {
val results = searchGoogle(
args, properties[GOOGLE_API_KEY_PROP],

View file

@ -56,12 +56,14 @@ class Joke(bot: Mobibot) : ThreadedModule(bot) {
/**
* Returns a random joke from [The Internet Chuck Norris Database](http://www.icndb.com/).
*/
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) = try {
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
try {
bot.send(Utils.cyan(randomJoke().msg))
} catch (e: ModuleException) {
bot.logger.warn(e.debugMessage, e)
bot.send(sender, e.message, isPrivate)
}
}
companion object {
// Joke command

View file

@ -37,7 +37,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage
import net.thauvin.erik.mobibot.msg.Message
import net.thauvin.erik.mobibot.msg.NoticeMessage
import net.thauvin.erik.mobibot.msg.PublicMessage
import org.apache.commons.lang3.StringUtils
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
@ -53,7 +52,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
*/
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
with(bot) {
if (StringUtils.isNotBlank(args)) {
if (args.isNotBlank()) {
try {
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
for (msg in messages) {
@ -88,7 +87,7 @@ class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
@Throws(ModuleException::class)
private fun getJsonResponse(response: String, debugMessage: String): JSONObject {
return if (StringUtils.isNotBlank(response)) {
return if (response.isNotBlank()) {
val json = JSONObject(response)
try {
val info = json.getString("Information")

View file

@ -39,7 +39,6 @@ import net.thauvin.erik.mobibot.commands.links.LinksMgr
import net.thauvin.erik.mobibot.entries.EntriesUtils
import net.thauvin.erik.mobibot.msg.Message
import net.thauvin.erik.mobibot.msg.NoticeMessage
import org.apache.commons.lang3.StringUtils
import twitter4j.TwitterException
import twitter4j.TwitterFactory
import twitter4j.conf.ConfigurationBuilder
@ -88,7 +87,7 @@ class Twitter(bot: Mobibot) : ThreadedModule(bot) {
*/
fun notification(msg: String) {
with(bot) {
if (isEnabled && StringUtils.isNotBlank(handle)) {
if (isEnabled && !handle.isNullOrBlank()) {
Thread {
try {
post(message = msg, isDm = true)

View file

@ -41,7 +41,6 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage
import net.thauvin.erik.mobibot.msg.Message
import net.thauvin.erik.mobibot.msg.NoticeMessage
import net.thauvin.erik.mobibot.msg.PublicMessage
import org.apache.commons.lang3.StringUtils
import org.jibble.pircbot.Colors
import java.util.*
import kotlin.math.roundToInt
@ -54,7 +53,7 @@ class Weather2(bot: Mobibot) : ThreadedModule(bot) {
* Fetches the weather data from a specific city.
*/
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
if (StringUtils.isNotBlank(args)) {
if (args.isNotBlank()) {
try {
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
if (messages[0].isError) {

View file

@ -190,7 +190,7 @@ class WorldTime(bot: Mobibot) : AbstractModule(bot) {
with(bot) {
if (args.isEmpty()) {
send(sender, "The supported countries/zones are: ", isPrivate)
sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, false, false)
sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, isPrivate = false, isBold = false)
} else {
val msg = worldTime(args)
if (isPrivate) {

View file

@ -31,11 +31,11 @@
*/
package net.thauvin.erik.mobibot.commands.tell
import java.time.temporal.Temporal
import java.time.LocalDateTime
import org.assertj.core.api.Assertions
import org.testng.annotations.Test
import java.time.Duration
import java.time.LocalDateTime
import java.time.temporal.Temporal
/**
* The `TellMessageTest` class.

View file

@ -62,7 +62,7 @@ class GoogleSearchTest : LocalProperties() {
.`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
} catch (e: ModuleException) {
// Avoid displaying api keys in CI logs
if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank() && !cseKey.isNullOrBlank()) {
if ("true" == System.getenv("CI") && !apiKey.isBlank() && !cseKey.isBlank()) {
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey))
} else {
throw e

View file

@ -63,7 +63,7 @@ class StockQuoteTest : LocalProperties() {
.isInstanceOf(ModuleException::class.java).hasNoCause()
} catch (e: ModuleException) {
// Avoid displaying api keys in CI logs
if ("true" == System.getenv("CI") && !apiKey.isNullOrBlank()) {
if ("true" == System.getenv("CI") && !apiKey.isBlank()) {
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey))
} else {
throw e

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle
#Fri Dec 04 15:43:01 PST 2020
version.buildmeta=312
#Fri Dec 04 22:59:43 PST 2020
version.buildmeta=337
version.major=0
version.minor=8
version.patch=0
version.prerelease=beta
version.project=mobibot
version.semver=0.8.0-beta+312
version.semver=0.8.0-beta+337