Converted modules to Kotlin.
This commit is contained in:
parent
fa4457a6f6
commit
9c1ab833da
70 changed files with 3313 additions and 3446 deletions
356
.idea/codeStyles/Project.xml
generated
Normal file
356
.idea/codeStyles/Project.xml
generated
Normal file
|
@ -0,0 +1,356 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="ANNOTATION_PARAMETER_WRAP" value="2" />
|
||||
<option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TEXT_BLOCKS" value="true" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value>
|
||||
<package name="java.awt" withSubpackages="false" static="false" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="JD_INDENT_ON_CONTINUATION" value="true" />
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ASSIGNMENT" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_THROWS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="5" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
<option name="RESOURCE_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="1" />
|
||||
<option name="THROWS_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
|
||||
<option name="THROWS_KEYWORD_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="FOR_STATEMENT_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||
<option name="ASSERT_STATEMENT_WRAP" value="1" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
||||
<option name="FOR_BRACE_FORCE" value="1" />
|
||||
<option name="WRAP_LONG_LINES" value="true" />
|
||||
<option name="VARIABLE_ANNOTATION_WRAP" value="2" />
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="1" />
|
||||
<indentOptions>
|
||||
<option name="USE_RELATIVE_INDENTS" value="true" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<groups>
|
||||
<group>
|
||||
<type>OVERRIDDEN_METHODS</type>
|
||||
<order>BY_NAME</order>
|
||||
</group>
|
||||
</groups>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
<STATIC>true</STATIC>
|
||||
<visibility />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<FIELD>true</FIELD>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONSTRUCTOR>true</CONSTRUCTOR>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD>true</METHOD>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<METHOD>true</METHOD>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<ENUM>true</ENUM>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INTERFACE>true</INTERFACE>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<CLASS>true</CLASS>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CLASS>true</CLASS>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
1
.idea/codeStyles/codeStyleConfig.xml
generated
1
.idea/codeStyles/codeStyleConfig.xml
generated
|
@ -1,5 +1,6 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
|
||||
</state>
|
||||
</component>
|
2
.idea/mobibot.iml
generated
2
.idea/mobibot.iml
generated
|
@ -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+122" 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+147" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
|
|
36
.idea/modules/mobibot.main.iml
generated
36
.idea/modules/mobibot.main.iml
generated
|
@ -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+122" 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+147" 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.0/e3765b66f0610afc92053ff1a93a87a544fca2b/kotlin-stdlib-jdk8-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.3.9/4be434f5e86c1998a273e7f19a7286440894f0b0/kotlinx-coroutines-core-jvm-1.3.9.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.0/9cc187c3dfaf6e4001bdf962e3cdadff7690261b/kotlin-stdlib-jdk7-1.4.0.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.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/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.1.2/9b082a619fb7deeac71684462c2804c76e0df694/spotbugs-annotations-4.1.2.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/11481f2c9feeb0e9b1b828953f2d4ba2f9b2753c/commons-net-3.7.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/20200518/41a767de4bde8f01d53856b905c49b2db8862f13/json-20200518.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.13.3/4e857439fc4fe974d212adaaaa3b118b8b50e3ec/log4j-core-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.3/7cca27a921a18645139cf651c04b83b1a19cfd76/log4j-slf4j-impl-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.3/ec1508160b93d274b1add34419b897bae84c6ca9/log4j-api-2.13.3.jar:/home/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.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:/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="noStdlib" value="true" />
|
||||
<option name="noReflect" value="true" />
|
||||
<option name="moduleName" value="mobibot" />
|
||||
|
@ -20,11 +20,11 @@
|
|||
</option>
|
||||
<option name="pluginClasspaths">
|
||||
<array>
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.4.0/52ac54a56c9121f54fcca387c5a0f441d1af9be8/kotlin-script-runtime-1.4.0.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.4.0/9c5c5503c8f96b477661789168b3f4a3c61a6c6e/kotlin-scripting-common-1.4.0.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.4.0/d807bf2ab36765af04b29319989322efd5ad1401/kotlin-scripting-jvm-1.4.0.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0/1c752cce0ead8d504ccc88a4fed6471fd83ab0dd/kotlin-stdlib-common-1.4.0.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.0/63e75298e93d4ae0b299bb869cf0c627196f8843/kotlin-stdlib-1.4.0.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" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.20/c6761d7805b5312302f2bbd78cda68c976ce0c70/kotlin-stdlib-common-1.4.20.jar" />
|
||||
<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" />
|
||||
</array>
|
||||
|
@ -50,35 +50,35 @@
|
|||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<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.0" 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.1.2" 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" 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:20200518" 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.13.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.0" 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.3.9" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.4.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: 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.0" 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" />
|
||||
</component>
|
||||
</module>
|
38
.idea/modules/mobibot.test.iml
generated
38
.idea/modules/mobibot.test.iml
generated
File diff suppressed because one or more lines are too long
|
@ -2,18 +2,64 @@
|
|||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||
<ID>LongMethod:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
||||
<ID>LongMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, cmd: String, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
|
||||
<ID>LongParameterList:Comment.kt$Comment$( bot: Mobibot, sender: String, isOp: Boolean, entry: EntryLink, index: Int, commentIndex: Int )</ID>
|
||||
<ID>LongParameterList:Comment.kt$Comment$(bot: Mobibot, cmd: String, sender: String, entry: EntryLink, index: Int, commentIndex: Int)</ID>
|
||||
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
|
||||
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$33</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID>
|
||||
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
|
||||
<ID>MagicNumber:Dice.kt$Dice$7</ID>
|
||||
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
|
||||
<ID>MagicNumber:Modules.kt$Modules$7</ID>
|
||||
<ID>MagicNumber:Recap.kt$Recap.Companion$10</ID>
|
||||
<ID>MagicNumber:Twitter.kt$Twitter$1000L</ID>
|
||||
<ID>MagicNumber:Twitter.kt$Twitter$60L</ID>
|
||||
<ID>MagicNumber:Users.kt$Users$8</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils.Companion$30</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils.Companion$365</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils.Companion$7</ID>
|
||||
<ID>MagicNumber:View.kt$View$8</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime$17</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$3600</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$60</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime.Companion$86.4</ID>
|
||||
<ID>MemberNameEqualsClassName:Calc.kt$Calc.Companion$ @JvmStatic fun calc(query: String): String</ID>
|
||||
<ID>MemberNameEqualsClassName:Lookup.kt$Lookup.Companion$ @JvmStatic @Throws(UnknownHostException::class) fun lookup(query: String): String</ID>
|
||||
<ID>MemberNameEqualsClassName:WorldTime.kt$WorldTime.Companion$ @JvmStatic fun worldTime(query: String): Message</ID>
|
||||
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand, props: Properties)</ID>
|
||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse( sender: String, login: String, args: String, isOp: Boolean, isPrivate: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</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: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<Message></ID>
|
||||
<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<Message></ID>
|
||||
<ID>NestedBlockDepth:WorldTime.kt$WorldTime$override fun commandResponse( sender: String, cmd: String, args: String, isPrivate: Boolean )</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<Message></ID>
|
||||
<ID>ThrowsCount:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></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<Message></ID>
|
||||
<ID>TooGenericExceptionCaught:FeedReader.kt$FeedReader$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
|
||||
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
|
||||
<ID>TooManyFunctions:Utils.kt$Utils$Companion</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Constants.java
|
||||
* Constants.kt
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,81 +29,81 @@
|
|||
* 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
|
||||
|
||||
package net.thauvin.erik.mobibot;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The <code>Constants</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
* The `Constants` class.
|
||||
*/
|
||||
public final class Constants {
|
||||
object Constants {
|
||||
/**
|
||||
* The connect/read timeout in ms.
|
||||
*/
|
||||
public static final int CONNECT_TIMEOUT = 5000;
|
||||
const val CONNECT_TIMEOUT = 5000
|
||||
|
||||
/**
|
||||
* Debug command line argument.
|
||||
*/
|
||||
public static final String DEBUG_ARG = "debug";
|
||||
const val DEBUG_ARG = "debug"
|
||||
|
||||
/**
|
||||
* The debug command.
|
||||
*/
|
||||
public static final String DEBUG_CMD = "debug";
|
||||
const val DEBUG_CMD = "debug"
|
||||
|
||||
/**
|
||||
* Default IRC Port.
|
||||
*/
|
||||
public static final int DEFAULT_PORT = 6667;
|
||||
const val DEFAULT_PORT = 6667
|
||||
|
||||
/**
|
||||
* Default IRC Server.
|
||||
*/
|
||||
public static final String DEFAULT_SERVER = "irc.freenode.net";
|
||||
const val DEFAULT_SERVER = "irc.freenode.net"
|
||||
|
||||
/**
|
||||
* The die command.
|
||||
*/
|
||||
public static final String DIE_CMD = "die";
|
||||
const val DIE_CMD = "die"
|
||||
|
||||
/**
|
||||
* Help command line argument.
|
||||
*/
|
||||
public static final String HELP_ARG = "help";
|
||||
const val HELP_ARG = "help"
|
||||
|
||||
/**
|
||||
* The help command.
|
||||
*/
|
||||
public static final String HELP_CMD = "help";
|
||||
const val HELP_CMD = "help"
|
||||
|
||||
/**
|
||||
* The link command.
|
||||
*/
|
||||
public static final String LINK_CMD = "L";
|
||||
const val LINK_CMD = "L"
|
||||
|
||||
/**
|
||||
* Default locale.
|
||||
*/
|
||||
public static final Locale LOCALE = Locale.getDefault();
|
||||
val LOCALE: Locale = Locale.getDefault()
|
||||
|
||||
/**
|
||||
* The empty title string.
|
||||
*/
|
||||
public static final String NO_TITLE = "No Title";
|
||||
const val NO_TITLE = "No Title"
|
||||
|
||||
/**
|
||||
* Properties command line argument.
|
||||
*/
|
||||
public static final String PROPS_ARG = "properties";
|
||||
const val PROPS_ARG = "properties"
|
||||
|
||||
/**
|
||||
* The timer delay in minutes.
|
||||
*/
|
||||
public static final long TIMER_DELAY = 10L;
|
||||
const val TIMER_DELAY = 10L
|
||||
|
||||
/**
|
||||
* Properties version line argument.
|
||||
*/
|
||||
public static final String VERSION_ARG = "version";
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*/
|
||||
private Constants() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
const val VERSION_ARG = "version"
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* FeedReader.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 com.rometools.rome.feed.synd.SyndEntry;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
import com.rometools.rome.io.XmlReader;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads a RSS feed.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Feb 1, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
public class FeedReader implements Runnable {
|
||||
// Maximum number of feed items to display
|
||||
private static final int MAX_ITEMS = 5;
|
||||
|
||||
// Bot
|
||||
private final Mobibot bot;
|
||||
|
||||
// Nick of the person who sent the message
|
||||
private final String sender;
|
||||
|
||||
// URL to fetch
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FeedReader} instance.
|
||||
*
|
||||
* @param bot The bot's instance.
|
||||
* @param sender The nick of the person who sent the message.
|
||||
* @param url The URL to fetch.
|
||||
*/
|
||||
public FeedReader(final Mobibot bot, final String sender, final String url) {
|
||||
this.bot = bot;
|
||||
this.sender = sender;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the Feed's items.
|
||||
*/
|
||||
@Override
|
||||
public final void run() {
|
||||
try {
|
||||
final SyndFeedInput input = new SyndFeedInput();
|
||||
try (final XmlReader reader = new XmlReader(new URL(url))) {
|
||||
final SyndFeed feed = input.build(reader);
|
||||
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
if (items.isEmpty()) {
|
||||
bot.send(sender, "There is currently nothing to view.", false);
|
||||
} else {
|
||||
SyndEntry item;
|
||||
for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) {
|
||||
item = items.get(i);
|
||||
bot.send(sender, item.getTitle(), false);
|
||||
bot.send(sender, Utils.helpIndent(Utils.green(item.getLink()), false), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
bot.getLogger().debug("Invalid feed URL.", e);
|
||||
bot.send(sender, "The feed URL is invalid.", false);
|
||||
} catch (Exception e) {
|
||||
bot.getLogger().debug("Unable to fetch the feed.", e);
|
||||
bot.send(sender, "An error has occurred while fetching the feed: " + e.getMessage(), false);
|
||||
}
|
||||
}
|
||||
}
|
85
src/main/java/net/thauvin/erik/mobibot/FeedReader.kt
Normal file
85
src/main/java/net/thauvin/erik/mobibot/FeedReader.kt
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* FeedReader.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 com.rometools.rome.io.SyndFeedInput
|
||||
import com.rometools.rome.io.XmlReader
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Reads a RSS feed.
|
||||
*/
|
||||
class FeedReader(
|
||||
// Bot
|
||||
private val bot: Mobibot,
|
||||
// Nick of the person who sent the message
|
||||
private val sender: String,
|
||||
// URL to fetch
|
||||
private val url: String
|
||||
) : Runnable {
|
||||
/**
|
||||
* Fetches the Feed's items.
|
||||
*/
|
||||
override fun run() {
|
||||
with(bot) {
|
||||
try {
|
||||
val input = SyndFeedInput()
|
||||
XmlReader(URL(url)).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
val items = feed.entries
|
||||
if (items.isEmpty()) {
|
||||
send(sender, "There is currently nothing to view.", false)
|
||||
} else {
|
||||
var i = 0
|
||||
while (i < items.size && i < MAX_ITEMS) {
|
||||
send(sender, items[i].title, false)
|
||||
send(sender, Utils.helpIndent(Utils.green(items[i].link), false), false)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: MalformedURLException) {
|
||||
logger.debug("Invalid feed URL.", e)
|
||||
send(sender, "The feed URL is invalid.", false)
|
||||
} catch (e: Exception) {
|
||||
logger.debug("Unable to fetch the feed.", e)
|
||||
send(sender, "An error has occurred while fetching the feed: ${e.message}", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Maximum number of feed items to display
|
||||
private const val MAX_ITEMS = 5
|
||||
}
|
||||
}
|
|
@ -579,7 +579,7 @@ public class Mobibot extends PircBot {
|
|||
*/
|
||||
private boolean helpModules(final String sender, final String topic, final boolean isPrivate) {
|
||||
for (final AbstractModule module : addons.getModules()) {
|
||||
for (final String cmd : module.getCommands()) {
|
||||
for (final String cmd : module.commands) {
|
||||
if (topic.equals(cmd)) {
|
||||
module.helpResponse(sender, isPrivate);
|
||||
return true;
|
||||
|
@ -646,7 +646,7 @@ public class Mobibot extends PircBot {
|
|||
*/
|
||||
public final void joinChannel() {
|
||||
joinChannel(ircChannel);
|
||||
twitter.notification("%1$s %2$s has joined %3$s");
|
||||
twitter.notification(getName() + " " + ReleaseInfo.VERSION + " has joined " + getChannel());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -695,7 +695,7 @@ public class Mobibot extends PircBot {
|
|||
}
|
||||
// Modules
|
||||
for (final AbstractModule module : addons.getModules()) { // modules
|
||||
for (final String c : module.getCommands()) {
|
||||
for (final String c : module.commands) {
|
||||
if (cmd.startsWith(c)) {
|
||||
module.commandResponse(sender, cmd, args, false);
|
||||
return;
|
||||
|
@ -739,7 +739,7 @@ public class Mobibot extends PircBot {
|
|||
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
||||
helpResponse(sender, args, true);
|
||||
} else if (isOp && "kill".equals(cmd)) { // kill
|
||||
twitter.notification("%1$s killed by " + sender + " on %3$s");
|
||||
twitter.notification(getName() + " killed by " + sender + " on " + getChannel());
|
||||
sendRawLine("QUIT : Poof!");
|
||||
System.exit(0);
|
||||
} else if (isOp && Constants.DEBUG_CMD.equals(cmd)) { // debug
|
||||
|
@ -753,7 +753,7 @@ public class Mobibot extends PircBot {
|
|||
send(sender + " has just signed my death sentence.");
|
||||
TIMER.cancel();
|
||||
twitter.shutdown();
|
||||
twitter.notification("%1$s stopped by " + sender + " on %3$s");
|
||||
twitter.notification(getName() + " stopped by " + sender + " on " + getChannel());
|
||||
sleep(3);
|
||||
quitServer("The Bot Is Out There!");
|
||||
System.exit(0);
|
||||
|
@ -766,7 +766,7 @@ public class Mobibot extends PircBot {
|
|||
}
|
||||
for (final AbstractModule module : addons.getModules()) {
|
||||
if (module.isPrivateMsgEnabled()) {
|
||||
for (final String c : module.getCommands()) {
|
||||
for (final String c : module.commands) {
|
||||
if (cmd.equals(c)) {
|
||||
module.commandResponse(sender, cmd, args, true);
|
||||
return;
|
||||
|
|
|
@ -69,8 +69,8 @@ public final class TwitterOAuth {
|
|||
* @throws TwitterException If an error occurs.
|
||||
* @throws IOException If an IO error occurs.
|
||||
*/
|
||||
@SuppressFBWarnings({"DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE"})
|
||||
@SuppressWarnings({"PMD.AvoidPrintStackTrace", "PMD.SystemPrintln"})
|
||||
@SuppressFBWarnings({ "DM_DEFAULT_ENCODING", "IMC_IMMATURE_CLASS_PRINTSTACKTRACE" })
|
||||
@SuppressWarnings({ "PMD.AvoidPrintStackTrace", "PMD.SystemPrintln" })
|
||||
public static void main(final String[] args) throws TwitterException, IOException {
|
||||
if (args.length == 2) {
|
||||
final twitter4j.Twitter twitter = new TwitterFactory().getInstance();
|
||||
|
@ -91,9 +91,10 @@ public final class TwitterOAuth {
|
|||
}
|
||||
|
||||
System.out.println(
|
||||
"Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey="
|
||||
+ args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token="
|
||||
+ accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken.getTokenSecret());
|
||||
"Please add the following to the bot's property file:" + "\n\n" + "twitter-consumerKey="
|
||||
+ args[0] + '\n' + "twitter-consumerSecret=" + args[1] + '\n' + "twitter-token="
|
||||
+ accessToken.getToken() + '\n' + "twitter-tokenSecret=" + accessToken
|
||||
.getTokenSecret());
|
||||
} catch (TwitterException te) {
|
||||
if (401 == te.getStatusCode()) {
|
||||
System.out.println("Unable to get the access token.");
|
||||
|
|
|
@ -1,388 +0,0 @@
|
|||
/*
|
||||
* Utils.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 org.apache.commons.lang3.StringUtils;
|
||||
import org.jibble.pircbot.Colors;
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Miscellaneous utilities class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-26
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Utils {
|
||||
private static final String[] searchFlags = { "%c", "%n" };
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the constructor is called.
|
||||
*/
|
||||
private Utils() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given int bold.
|
||||
*
|
||||
* @param i The int.
|
||||
* @return The bold string.
|
||||
*/
|
||||
public static String bold(final int i) {
|
||||
return bold(Integer.toString(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string bold.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The bold string.
|
||||
*/
|
||||
public static String bold(final String s) {
|
||||
return colorize(s, Colors.BOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize a string.
|
||||
*
|
||||
* @param s The string.
|
||||
* @param color The color.
|
||||
* @return The colorized string.
|
||||
*/
|
||||
static String colorize(final String s, final String color) {
|
||||
if (s == null) {
|
||||
return Colors.NORMAL;
|
||||
} else if (Colors.BOLD.equals(color) || Colors.REVERSE.equals(color)) {
|
||||
return color + s + color;
|
||||
}
|
||||
|
||||
return color + s + Colors.NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string cyan.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The cyan string.
|
||||
*/
|
||||
public static String cyan(final String s) {
|
||||
return colorize(s, Colors.CYAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL encodes the given string.
|
||||
*
|
||||
* @param s The string to encode.
|
||||
* @return The encoded string.
|
||||
*/
|
||||
public static String encodeUrl(final String s) {
|
||||
return URLEncoder.encode(s, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given location (File/URL) has a trailing slash (<code>/</code>) to indicate a directory.
|
||||
*
|
||||
* @param location The File or URL location.
|
||||
* @param isUrl Set to true if the location is a URL
|
||||
* @return The location ending with a slash.
|
||||
*/
|
||||
static String ensureDir(final String location, final boolean isUrl) {
|
||||
if (isUrl) {
|
||||
if (location.charAt(location.length() - 1) == '/') {
|
||||
return location;
|
||||
} else {
|
||||
return location + '/';
|
||||
}
|
||||
} else {
|
||||
if (location.charAt(location.length() - 1) == File.separatorChar) {
|
||||
return location;
|
||||
} else {
|
||||
return location + File.separatorChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property as an int.
|
||||
*
|
||||
* @param property The property value.
|
||||
* @param def The default property value.
|
||||
* @return The port or default value if invalid.
|
||||
*/
|
||||
public static int getIntProperty(final String property, final int def) {
|
||||
int prop;
|
||||
|
||||
try {
|
||||
prop = Integer.parseInt(property);
|
||||
} catch (NumberFormatException ignore) {
|
||||
prop = def;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string green.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The green string.
|
||||
*/
|
||||
public static String green(final String s) {
|
||||
return colorize(s, Colors.DARK_GREEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a help command by replacing {@code %c} with the bot's pub/priv command, and {@code %n} with the bot's
|
||||
* nick.
|
||||
*
|
||||
* @param text The help command text.
|
||||
* @param botNick The bot's nick.
|
||||
* @param isPrivate The private flag.
|
||||
* @return The formatted help command.
|
||||
*/
|
||||
public static String helpFormat(final String text, final String botNick, final boolean isPrivate) {
|
||||
final String[] replace = { (isPrivate) ? "/msg " + botNick : botNick + ':', botNick };
|
||||
return StringUtils.replaceEach(text, searchFlags, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indented help string.
|
||||
*
|
||||
* @param help The help string.
|
||||
* @return The indented help string.
|
||||
* @see #helpIndent(String, boolean)
|
||||
*/
|
||||
public static String helpIndent(final String help) {
|
||||
return helpIndent(help, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indented help string.
|
||||
*
|
||||
* @param help The help string.
|
||||
* @param isBold The bold flag.
|
||||
* @return The indented help string.
|
||||
*/
|
||||
public static String helpIndent(final String help, final boolean isBold) {
|
||||
return " " + (isBold ? bold(help) : help);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
|
||||
*/
|
||||
public static String isoLocalDate(final Date date) {
|
||||
return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
|
||||
*/
|
||||
public static String isoLocalDate(final LocalDateTime date) {
|
||||
return date.format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obfuscates the given string.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The obfuscated string.
|
||||
*/
|
||||
public static String obfuscate(final String s) {
|
||||
if (StringUtils.isNotBlank(s)) {
|
||||
return StringUtils.repeat('x', s.length());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural form of a word, if count > 1.
|
||||
*
|
||||
* @param count The count.
|
||||
* @param word The word.
|
||||
* @param plural The plural word.
|
||||
* @return The plural string.
|
||||
*/
|
||||
public static String plural(final long count, final String word, final String plural) {
|
||||
if (count > 1) {
|
||||
return plural;
|
||||
} else {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string red.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The red string.
|
||||
*/
|
||||
public static String red(final String s) {
|
||||
return colorize(s, Colors.RED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string reverse color.
|
||||
*
|
||||
* @param s The string.
|
||||
* @return The reverse color string.
|
||||
*/
|
||||
public static String reverseColor(final String s) {
|
||||
return colorize(s, Colors.REVERSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns today's date.
|
||||
*
|
||||
* @return Today's date in {@link DateTimeFormatter#ISO_LOCAL_DATE} format.
|
||||
*/
|
||||
public static String today() {
|
||||
return isoLocalDate(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts XML/XHTML entities to plain text.
|
||||
*
|
||||
* @param str The string to unescape.
|
||||
* @return The unescaped string.
|
||||
*/
|
||||
public static String unescapeXml(final String str) {
|
||||
return Jsoup.parse(str).text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts milliseconds to year month week day hour and minutes.
|
||||
*
|
||||
* @param uptime The uptime in milliseconds.
|
||||
* @return The uptime in year month week day hours and minutes.
|
||||
*/
|
||||
public static String uptime(final long uptime) {
|
||||
final StringBuilder info = new StringBuilder();
|
||||
|
||||
long days = TimeUnit.MILLISECONDS.toDays(uptime);
|
||||
final long years = days / 365;
|
||||
days %= 365;
|
||||
final long months = days / 30;
|
||||
days %= 30;
|
||||
final long weeks = days / 7;
|
||||
days %= 7;
|
||||
final long hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(
|
||||
TimeUnit.MILLISECONDS.toDays(uptime));
|
||||
final long minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(
|
||||
TimeUnit.MILLISECONDS.toHours(uptime));
|
||||
|
||||
if (years > 0) {
|
||||
info.append(years).append(plural(years, " year ", " years "));
|
||||
}
|
||||
|
||||
if (months > 0) {
|
||||
info.append(weeks).append(plural(months, " month ", " months "));
|
||||
}
|
||||
|
||||
if (weeks > 0) {
|
||||
info.append(weeks).append(plural(weeks, " week ", " weeks "));
|
||||
}
|
||||
|
||||
|
||||
if (days > 0) {
|
||||
info.append(days).append(plural(days, " day ", " days "));
|
||||
}
|
||||
|
||||
if (hours > 0) {
|
||||
info.append(hours).append(plural(hours, " hour ", " hours "));
|
||||
}
|
||||
|
||||
info.append(minutes).append(plural(minutes, " minute", " minutes"));
|
||||
|
||||
return info.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads contents of a URL.
|
||||
*
|
||||
* @param url The URL to read.
|
||||
* @return The URL contents.
|
||||
* @throws IOException If an IO error occurs.
|
||||
*/
|
||||
@SuppressFBWarnings("SECSSSRFUC")
|
||||
public static String urlReader(final URL url) throws IOException {
|
||||
try (final BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
|
||||
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The formatted date.
|
||||
*/
|
||||
public static String utcDateTime(final Date date) {
|
||||
return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as <code>yyyy-MM-dd HH:mm</code>.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The formatted date.
|
||||
*/
|
||||
public static String utcDateTime(final LocalDateTime date) {
|
||||
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
|
||||
}
|
||||
|
||||
}
|
301
src/main/java/net/thauvin/erik/mobibot/Utils.kt
Normal file
301
src/main/java/net/thauvin/erik/mobibot/Utils.kt
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Utils.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 org.apache.commons.lang3.StringUtils
|
||||
import org.jibble.pircbot.Colors
|
||||
import org.jsoup.Jsoup
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.net.URL
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.stream.Collectors
|
||||
|
||||
/**
|
||||
* Miscellaneous utilities class.
|
||||
*/
|
||||
class Utils private constructor() {
|
||||
companion object {
|
||||
private val searchFlags = arrayOf("%c", "%n")
|
||||
|
||||
/**
|
||||
* Makes the given int bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bold(i: Int): String {
|
||||
return bold(i.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun bold(s: String?): String {
|
||||
return colorize(s, Colors.BOLD)
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize a string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun colorize(s: String?, color: String): String {
|
||||
if (s == null) {
|
||||
return Colors.NORMAL
|
||||
} else if (Colors.BOLD == color || Colors.REVERSE == color) {
|
||||
return color + s + color
|
||||
}
|
||||
return color + s + Colors.NORMAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string cyan.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun cyan(s: String?): String {
|
||||
return colorize(s, Colors.CYAN)
|
||||
}
|
||||
|
||||
/**
|
||||
* URL encodes the given string.
|
||||
*/
|
||||
fun encodeUrl(s: String?): String {
|
||||
return URLEncoder.encode(s, StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun ensureDir(location: String, isUrl: Boolean): String {
|
||||
return if (location.isNotEmpty()) {
|
||||
if (isUrl) {
|
||||
if (location[location.length - 1] == '/') {
|
||||
location
|
||||
} else {
|
||||
"$location/"
|
||||
}
|
||||
} else {
|
||||
if (location[location.length - 1] == File.separatorChar) {
|
||||
location
|
||||
} else {
|
||||
location + File.separatorChar
|
||||
}
|
||||
}
|
||||
} else {
|
||||
location
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property as an int.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getIntProperty(property: String, def: Int): Int {
|
||||
return try {
|
||||
property.toInt()
|
||||
} catch (ignore: NumberFormatException) {
|
||||
def
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string green.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun green(s: String?): String {
|
||||
return colorize(s, Colors.DARK_GREEN)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
|
||||
* nick.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun helpFormat(text: String?, botNick: String, isPrivate: Boolean): String {
|
||||
val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick)
|
||||
return StringUtils.replaceEach(text, searchFlags, replace)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indented help string.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun helpIndent(help: String?, isBold: Boolean = true): String {
|
||||
return " " + if (isBold) bold(help) else help
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isoLocalDate(date: Date): String {
|
||||
return isoLocalDate(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isoLocalDate(date: LocalDateTime): String {
|
||||
return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Obfuscates the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun obfuscate(s: String?): String {
|
||||
return if (s!!.isNotBlank()) {
|
||||
StringUtils.repeat('x', s.length)
|
||||
} else s
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural form of a word, if count > 1.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun plural(count: Long, word: String, plural: String): String {
|
||||
return if (count > 1) {
|
||||
plural
|
||||
} else {
|
||||
word
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string red.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun red(s: String?): String {
|
||||
return colorize(s, Colors.RED)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string reverse color.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun reverseColor(s: String?): String {
|
||||
return colorize(s, Colors.REVERSE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns today's date.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun today(): String {
|
||||
return isoLocalDate(LocalDateTime.now())
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts XML/XHTML entities to plain text.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun unescapeXml(str: String?): String {
|
||||
return Jsoup.parse(str).text()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts milliseconds to year month week day hour and minutes.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uptime(uptime: Long): String {
|
||||
val info = StringBuilder()
|
||||
var days = TimeUnit.MILLISECONDS.toDays(uptime)
|
||||
val years = days / 365
|
||||
days %= 365
|
||||
val months = days / 30
|
||||
days %= 30
|
||||
val weeks = days / 7
|
||||
days %= 7
|
||||
val hours = TimeUnit.MILLISECONDS.toHours(uptime) - TimeUnit.DAYS.toHours(
|
||||
TimeUnit.MILLISECONDS.toDays(uptime)
|
||||
)
|
||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(uptime) - TimeUnit.HOURS.toMinutes(
|
||||
TimeUnit.MILLISECONDS.toHours(uptime)
|
||||
)
|
||||
with(info) {
|
||||
if (years > 0) {
|
||||
append(years).append(plural(years, " year ", " years "))
|
||||
}
|
||||
if (months > 0) {
|
||||
append(weeks).append(plural(months, " month ", " months "))
|
||||
}
|
||||
if (weeks > 0) {
|
||||
append(weeks).append(plural(weeks, " week ", " weeks "))
|
||||
}
|
||||
if (days > 0) {
|
||||
append(days).append(plural(days, " day ", " days "))
|
||||
}
|
||||
if (hours > 0) {
|
||||
append(hours).append(plural(hours, " hour ", " hours "))
|
||||
}
|
||||
append(minutes).append(plural(minutes, " minute", " minutes"))
|
||||
return toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads contents of a URL.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun urlReader(url: URL): String {
|
||||
BufferedReader(InputStreamReader(url.openStream(), StandardCharsets.UTF_8))
|
||||
.use { reader -> return reader.lines().collect(Collectors.joining(System.lineSeparator())) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun utcDateTime(date: Date): String {
|
||||
return utcDateTime(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun utcDateTime(date: LocalDateTime): String {
|
||||
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,9 +63,10 @@ abstract class AbstractCommand(val bot: Mobibot) {
|
|||
return false
|
||||
}
|
||||
|
||||
open fun getProperty(key: String) : String? {
|
||||
open fun getProperty(key: String): String? {
|
||||
return properties[key]
|
||||
}
|
||||
|
||||
open fun getPropertyKeys(): Set<String> {
|
||||
return properties.keys
|
||||
}
|
||||
|
|
|
@ -38,10 +38,7 @@ import net.thauvin.erik.mobibot.Utils
|
|||
class Cycle(bot: Mobibot) : AbstractCommand(bot) {
|
||||
private val wait = 10
|
||||
override val name = "cycle"
|
||||
override val help = listOf(
|
||||
"To have the bot leave the channel and come back:",
|
||||
Utils.helpIndent("%c $name")
|
||||
)
|
||||
override val help = listOf("To have the bot leave the channel and come back:", Utils.helpIndent("%c $name"))
|
||||
override val isOp = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
|
|
@ -48,7 +48,7 @@ public class Info extends AbstractCommand {
|
|||
+ " (" + Utils.green(ReleaseInfo.WEBSITE) + ')',
|
||||
"Written by " + ReleaseInfo.AUTHOR + " (" + Utils.green(ReleaseInfo.AUTHOR_URL) + ')');
|
||||
|
||||
public Info(@NotNull final Mobibot bot) {
|
||||
public Info(final Mobibot bot) {
|
||||
super(bot);
|
||||
}
|
||||
|
||||
|
@ -97,18 +97,14 @@ public class Info extends AbstractCommand {
|
|||
|
||||
if (isOp) {
|
||||
if (getBot().getTell().isEnabled()) {
|
||||
info.append(", Messages: ")
|
||||
.append(getBot().getTell().size());
|
||||
info.append(", Messages: ").append(getBot().getTell().size());
|
||||
}
|
||||
if (getBot().getTwitter().isAutoPost()) {
|
||||
info.append(", Twitter: ")
|
||||
.append(getBot().getTwitter().entriesCount());
|
||||
info.append(", Twitter: ").append(getBot().getTwitter().entriesCount());
|
||||
}
|
||||
}
|
||||
|
||||
info.append(", Recap: ")
|
||||
.append(Recap.recapCount())
|
||||
.append(']');
|
||||
info.append(", Recap: ").append(Recap.recapCount()).append(']');
|
||||
|
||||
getBot().send(sender, info.toString(), isPrivate);
|
||||
}
|
||||
|
|
|
@ -173,7 +173,8 @@ public class Tell extends AbstractCommand {
|
|||
Utils.helpIndent("%c " + TELL_CMD + " <nick> <message>"),
|
||||
"To view queued and sent messages:",
|
||||
Utils.helpIndent("%c " + TELL_CMD + ' ' + View.VIEW_CMD),
|
||||
"Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."));
|
||||
"Messages are kept for " + Utils.bold(maxDays)
|
||||
+ Utils.plural(maxDays, " day.", " days."));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -383,8 +384,8 @@ public class Tell extends AbstractCommand {
|
|||
isPrivate)),
|
||||
isPrivate);
|
||||
getBot().send(sender,
|
||||
"Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."),
|
||||
isPrivate);
|
||||
"Messages are kept for " + Utils.bold(maxDays)
|
||||
+ Utils.plural(maxDays, " day.", " days."), isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,8 @@ public final class EntriesUtils {
|
|||
* @param isView Set to true to display the number of comments.
|
||||
* @return The entry's link.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "CE_CLASS_ENVY", justification = "Yes, it does.")
|
||||
@SuppressFBWarnings(value = "CE_CLASS_ENVY",
|
||||
justification = "Yes, it does.")
|
||||
public static String buildLink(final int index, final EntryLink entry, final boolean isView) {
|
||||
final StringBuilder buff = new StringBuilder().append(Constants.LINK_CMD).append(index + 1)
|
||||
.append(": ").append('[').append(entry.getNick()).append(']');
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* AbstractModule.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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* The <code>Module</code> abstract class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2016-07-01
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractModule {
|
||||
final Mobibot bot;
|
||||
final List<String> commands = new ArrayList<>();
|
||||
final List<String> help = new ArrayList<>();
|
||||
final Map<String, String> properties = new ConcurrentHashMap<>();
|
||||
|
||||
AbstractModule(final Mobibot bot) {
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to a command.
|
||||
*
|
||||
* @param sender The sender.
|
||||
* @param cmd The command.
|
||||
* @param args The command arguments.
|
||||
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
|
||||
*/
|
||||
public abstract void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate);
|
||||
|
||||
/**
|
||||
* Returns the module's commands, if any.
|
||||
*
|
||||
* @return The commands.
|
||||
*/
|
||||
public List<String> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module's property keys.
|
||||
*
|
||||
* @return The keys.
|
||||
*/
|
||||
public Set<String> getPropertyKeys() {
|
||||
return properties.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the module has properties.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code> .
|
||||
*/
|
||||
public boolean hasProperties() {
|
||||
return !properties.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the module's help.
|
||||
*
|
||||
* @param sender The sender.
|
||||
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
|
||||
*/
|
||||
public void helpResponse(final String sender, final boolean isPrivate) {
|
||||
for (final String h : help) {
|
||||
bot.send(sender, Utils.helpFormat(h, bot.getNick(), isPrivateMsgEnabled() && isPrivate), isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the properties.
|
||||
*
|
||||
* @param keys The properties keys to initialize.
|
||||
*/
|
||||
public void initProperties(final String... keys) {
|
||||
for (final String key : keys) {
|
||||
properties.put(key, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the module is enabled.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
if (hasProperties()) {
|
||||
return isValidProperties();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the module responds to private messages.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
public boolean isPrivateMsgEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all properties have values.
|
||||
*
|
||||
* @return <code>true</code> if the properties are valid, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isValidProperties() {
|
||||
for (final String s : getPropertyKeys()) {
|
||||
if (StringUtils.isBlank(properties.get(s))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property key and value.
|
||||
*
|
||||
* @param key The key.
|
||||
* @param value The value.
|
||||
*/
|
||||
public void setProperty(final String key, final String value) {
|
||||
if (StringUtils.isNotBlank(key)) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
131
src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt
Normal file
131
src/main/java/net/thauvin/erik/mobibot/modules/AbstractModule.kt
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* AbstractModule.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.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
|
||||
|
||||
/**
|
||||
* The `Module` abstract class.
|
||||
*/
|
||||
abstract class AbstractModule(val bot: Mobibot) {
|
||||
/**
|
||||
* The module's commands, if any.
|
||||
*/
|
||||
@JvmField
|
||||
val commands: MutableList<String> = ArrayList()
|
||||
|
||||
@JvmField
|
||||
val help: MutableList<String> = ArrayList()
|
||||
val properties: MutableMap<String, String> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* Responds to a command.
|
||||
*/
|
||||
abstract fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the module's property keys.
|
||||
*/
|
||||
val propertyKeys: Set<String>
|
||||
get() = properties.keys
|
||||
|
||||
/**
|
||||
* Returns `true` if the module has properties.
|
||||
*/
|
||||
fun hasProperties(): Boolean {
|
||||
return properties.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the module's help.
|
||||
*/
|
||||
open fun helpResponse(sender: String, isPrivate: Boolean) {
|
||||
for (h in help) {
|
||||
bot.send(sender, Utils.helpFormat(h, bot.nick, isPrivateMsgEnabled && isPrivate), isPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the properties.
|
||||
*/
|
||||
fun initProperties(vararg keys: String) {
|
||||
for (key in keys) {
|
||||
properties[key] = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the module is enabled.
|
||||
*/
|
||||
val isEnabled: Boolean
|
||||
get() = if (hasProperties()) {
|
||||
isValidProperties
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the module responds to private messages.
|
||||
*/
|
||||
open val isPrivateMsgEnabled: Boolean = false
|
||||
|
||||
/**
|
||||
* Ensures that all properties have values.
|
||||
*/
|
||||
open val isValidProperties: Boolean
|
||||
get() {
|
||||
for (s in propertyKeys) {
|
||||
if (StringUtils.isBlank(properties[s])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property key and value.
|
||||
*/
|
||||
fun setProperty(key: String, value: String) {
|
||||
if (StringUtils.isNotBlank(key)) {
|
||||
properties[key] = value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Calc.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.modules;
|
||||
|
||||
import net.objecthunter.exp4j.Expression;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The Calc module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2016-07-01
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Calc extends AbstractModule {
|
||||
// Calc command
|
||||
private static final String CALC_CMD = "calc";
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public Calc(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(CALC_CMD);
|
||||
|
||||
help.add("To solve a mathematical calculation:");
|
||||
help.add(Utils.helpIndent("%c " + CALC_CMD + " <calculation>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a calculation.
|
||||
*
|
||||
* <p>1 + 1 * 2</p>
|
||||
*
|
||||
* @param query The query.
|
||||
* @return The calculation result.
|
||||
*/
|
||||
static String calc(final String query) {
|
||||
final DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
||||
|
||||
try {
|
||||
final Expression calc = new ExpressionBuilder(query).build();
|
||||
return query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate()));
|
||||
} catch (Exception e) {
|
||||
return "No idea. This is the kind of math I don't get.";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
bot.send(calc(args));
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* AbstractModuleTest.java
|
||||
* Calc.kt
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,41 +29,53 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
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
|
||||
|
||||
/**
|
||||
* The <code>AbstractModuleTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
* The Calc module.
|
||||
*/
|
||||
|
||||
final class AbstractModuleTest {
|
||||
private AbstractModuleTest() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
class Calc(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
bot.send(calc(args))
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("CE_CLASS_ENVY")
|
||||
static void testAbstractModule(final AbstractModule module) {
|
||||
final String name = module.getClass().getName();
|
||||
companion object {
|
||||
// Calc command
|
||||
private const val CALC_CMD = "calc"
|
||||
|
||||
assertThat(module.isEnabled()).as(name + ": enabled").isNotEqualTo(module.hasProperties());
|
||||
assertThat(module.getCommands().size()).as(name + ": commands > 0").isGreaterThan(0);
|
||||
if (!module.hasProperties()) {
|
||||
assertThat(module.getPropertyKeys().size()).as(name + ": no properties").isEqualTo(0);
|
||||
module.setProperty("test", "test");
|
||||
module.setProperty("", "invalid");
|
||||
/**
|
||||
* Performs a calculation. e.g.: 1 + 1 * 2
|
||||
*/
|
||||
@JvmStatic
|
||||
fun calc(query: String): String {
|
||||
val decimalFormat = DecimalFormat("#.##")
|
||||
return try {
|
||||
val calc = ExpressionBuilder(query).build()
|
||||
query.replace(" ", "") + " = " + Utils.bold(decimalFormat.format(calc.evaluate()))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
"No idea. This is the kind of math I don't get."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(module.getPropertyKeys().size()).as(name + ": properties > 0").isGreaterThan(0);
|
||||
|
||||
module.setProperty("invalid", "");
|
||||
assertThat(module.isValidProperties()).as(name + ": invalid properties").isFalse();
|
||||
init {
|
||||
commands.add(CALC_CMD)
|
||||
help.add("To solve a mathematical calculation:")
|
||||
help.add(Utils.helpIndent("%c $CALC_CMD <calculation>"))
|
||||
}
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
* CurrencyConverter.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jdom2.Document;
|
||||
import org.jdom2.Element;
|
||||
import org.jdom2.JDOMException;
|
||||
import org.jdom2.Namespace;
|
||||
import org.jdom2.input.SAXBuilder;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.upperCase;
|
||||
|
||||
/**
|
||||
* The CurrentConverter module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Feb 11, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
public final class CurrencyConverter extends ThreadedModule {
|
||||
// Currency command
|
||||
private static final String CURRENCY_CMD = "currency";
|
||||
// Rates keyword
|
||||
private static final String CURRENCY_RATES_KEYWORD = "rates";
|
||||
// Empty rate table.
|
||||
private static final String EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty.";
|
||||
// Exchange rates
|
||||
private static final Map<String, String> EXCHANGE_RATES = new TreeMap<>();
|
||||
// Exchange rates table URL
|
||||
private static final String EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
|
||||
// Last exchange rates table publication date
|
||||
private static String pubDate = "";
|
||||
|
||||
/**
|
||||
* Creates a new {@link CurrencyConverter} instance.
|
||||
*/
|
||||
public CurrencyConverter(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(CURRENCY_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from a currency to another.
|
||||
*
|
||||
* <p>100 USD to EUR</p>
|
||||
*
|
||||
* @param query The query.
|
||||
* @return The {@link Message} contained the converted currency.
|
||||
*/
|
||||
static Message convertCurrency(final String query) {
|
||||
final String[] cmds = query.split(" ");
|
||||
|
||||
if (cmds.length == 4) {
|
||||
if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) {
|
||||
return new PublicMessage("You're kidding, right?");
|
||||
} else {
|
||||
final String to = upperCase(cmds[1]);
|
||||
final String from = upperCase(cmds[3]);
|
||||
|
||||
if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) {
|
||||
try {
|
||||
final double amt = Double.parseDouble(cmds[0].replace(",", ""));
|
||||
final double doubleFrom = Double.parseDouble(EXCHANGE_RATES.get(to));
|
||||
final double doubleTo = Double.parseDouble(EXCHANGE_RATES.get(from));
|
||||
|
||||
return new PublicMessage(
|
||||
NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1)
|
||||
+ ' '
|
||||
+ upperCase(cmds[1])
|
||||
+ " = "
|
||||
+ NumberFormat.getCurrencyInstance(Constants.LOCALE)
|
||||
.format((amt * doubleTo) / doubleFrom)
|
||||
.substring(1)
|
||||
+ ' '
|
||||
+ upperCase(cmds[3]));
|
||||
} catch (NumberFormatException e) {
|
||||
return new ErrorMessage("Let's try with some real numbers next time, okay?");
|
||||
}
|
||||
} else {
|
||||
return new ErrorMessage("Sounds like monopoly money to me!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ErrorMessage("Invalid query. Let's try again.");
|
||||
}
|
||||
|
||||
static List<String> currencyRates() {
|
||||
final List<String> rates = new ArrayList<>(33);
|
||||
for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) {
|
||||
rates.add(" " + rate.getKey() + ": " + StringUtils.leftPad(rate.getValue(), 8));
|
||||
}
|
||||
|
||||
return rates;
|
||||
}
|
||||
|
||||
static void loadRates() throws ModuleException {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
final SAXBuilder builder = new SAXBuilder();
|
||||
// See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755
|
||||
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
|
||||
builder.setIgnoringElementContentWhitespace(true);
|
||||
|
||||
final Document doc = builder.build(new URL(EXCHANGE_TABLE_URL));
|
||||
final Element root = doc.getRootElement();
|
||||
final Namespace ns = root.getNamespace("");
|
||||
final Element cubeRoot = root.getChild("Cube", ns);
|
||||
final Element cubeTime = cubeRoot.getChild("Cube", ns);
|
||||
|
||||
pubDate = cubeTime.getAttribute("time").getValue();
|
||||
|
||||
final List<Element> cubes = cubeTime.getChildren();
|
||||
|
||||
for (final Element cube : cubes) {
|
||||
EXCHANGE_RATES.put(
|
||||
cube.getAttribute("currency").getValue(),
|
||||
cube.getAttribute("rate").getValue());
|
||||
}
|
||||
|
||||
EXCHANGE_RATES.put("EUR", "1");
|
||||
} catch (JDOMException | IOException e) {
|
||||
throw new ModuleException(e.getMessage(),
|
||||
"An error has occurred while parsing the exchange rates table.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
synchronized (this) {
|
||||
if (!pubDate.equals(Utils.today())) {
|
||||
EXCHANGE_RATES.clear();
|
||||
}
|
||||
}
|
||||
|
||||
super.commandResponse(sender, cmd, args, isPrivate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified currencies.
|
||||
*/
|
||||
@SuppressFBWarnings("REDOS")
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String query, final boolean isPrivate) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates();
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
bot.send(sender, EMPTY_RATE_TABLE, true);
|
||||
} else if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) {
|
||||
final Message msg = convertCurrency(query);
|
||||
bot.send(sender, msg);
|
||||
if (msg.isError()) {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
} else if (query.contains(CURRENCY_RATES_KEYWORD)) {
|
||||
bot.send(sender, "The currency rates for " + Utils.bold(pubDate) + " are:", isPrivate);
|
||||
bot.sendList(sender, currencyRates(), 3, isPrivate, false);
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final String sender, final boolean isPrivate) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates();
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().debug(e.getDebugMessage(), e);
|
||||
}
|
||||
}
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
bot.send(sender, EMPTY_RATE_TABLE, isPrivate);
|
||||
} else {
|
||||
bot.send(sender, "To convert from one currency to another:", isPrivate);
|
||||
bot.send(sender,
|
||||
Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + " 100 USD to EUR",
|
||||
bot.getNick(), isPrivateMsgEnabled())), isPrivate);
|
||||
bot.send(sender, "For a listing of current rates:", isPrivate);
|
||||
bot.send(sender,
|
||||
Utils.helpIndent(Utils.helpFormat("%c " + CURRENCY_CMD + ' ' + CURRENCY_RATES_KEYWORD,
|
||||
bot.getNick(), isPrivateMsgEnabled())), isPrivate);
|
||||
bot.send(sender, "The supported currencies are: ", isPrivate);
|
||||
bot.sendList(sender, new ArrayList<>(EXCHANGE_RATES.keySet()), 11, isPrivate, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* CurrencyConverter.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.jdom2.JDOMException
|
||||
import org.jdom2.input.SAXBuilder
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
import javax.xml.XMLConstants
|
||||
|
||||
/**
|
||||
* The CurrentConverter module.
|
||||
*/
|
||||
class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
synchronized(this) {
|
||||
if (pubDate != Utils.today()) {
|
||||
EXCHANGE_RATES.clear()
|
||||
}
|
||||
}
|
||||
super.commandResponse(sender, cmd, args, isPrivate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified currencies.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
send(sender, EMPTY_RATE_TABLE, true)
|
||||
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
|
||||
val msg = convertCurrency(args)
|
||||
send(sender, msg)
|
||||
if (msg.isError) {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
} else if (args.contains(CURRENCY_RATES_KEYWORD)) {
|
||||
send(sender, "The currency rates for ${Utils.bold(pubDate)} are:", isPrivate)
|
||||
sendList(sender, currencyRates(), 3, isPrivate, false)
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(sender: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
loadRates()
|
||||
} catch (e: ModuleException) {
|
||||
logger.debug(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
send(sender, EMPTY_RATE_TABLE, isPrivate)
|
||||
} else {
|
||||
send(sender, "To convert from one currency to another:", isPrivate)
|
||||
send(
|
||||
sender,
|
||||
Utils.helpIndent(
|
||||
Utils.helpFormat("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)
|
||||
),
|
||||
isPrivate
|
||||
)
|
||||
send(sender, "For a listing of current rates:", isPrivate)
|
||||
send(
|
||||
sender,
|
||||
Utils.helpIndent(
|
||||
Utils.helpFormat("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled)
|
||||
),
|
||||
isPrivate
|
||||
)
|
||||
send(sender, "The supported currencies are: ", isPrivate)
|
||||
sendList(sender, ArrayList(EXCHANGE_RATES.keys), 11, isPrivate, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Currency command
|
||||
private const val CURRENCY_CMD = "currency"
|
||||
|
||||
// Rates keyword
|
||||
private const val CURRENCY_RATES_KEYWORD = "rates"
|
||||
|
||||
// Empty rate table.
|
||||
private const val EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty."
|
||||
|
||||
// Exchange rates
|
||||
private val EXCHANGE_RATES: MutableMap<String, String> = TreeMap()
|
||||
|
||||
// Exchange rates table URL
|
||||
private const val EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
|
||||
|
||||
// Last exchange rates table publication date
|
||||
private var pubDate = ""
|
||||
|
||||
/**
|
||||
* Converts from a currency to another.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun convertCurrency(query: String): Message {
|
||||
val cmds = query.split(" ").toTypedArray()
|
||||
return if (cmds.size == 4) {
|
||||
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])
|
||||
if (EXCHANGE_RATES.containsKey(to) && EXCHANGE_RATES.containsKey(from)) {
|
||||
try {
|
||||
val amt = cmds[0].replace(",", "").toDouble()
|
||||
val doubleFrom = EXCHANGE_RATES[to]!!.toDouble()
|
||||
val doubleTo = EXCHANGE_RATES[from]!!.toDouble()
|
||||
PublicMessage(
|
||||
NumberFormat.getCurrencyInstance(Constants.LOCALE).format(amt).substring(1)
|
||||
+ " ${StringUtils.upperCase(cmds[1])} = "
|
||||
+ NumberFormat.getCurrencyInstance(Constants.LOCALE)
|
||||
.format(amt * doubleTo / doubleFrom).substring(1)
|
||||
+ " ${StringUtils.upperCase(cmds[3])}"
|
||||
)
|
||||
} catch (e: NumberFormatException) {
|
||||
ErrorMessage("Let's try with some real numbers next time, okay?")
|
||||
}
|
||||
} else {
|
||||
ErrorMessage("Sounds like monopoly money to me!")
|
||||
}
|
||||
}
|
||||
} else ErrorMessage("Invalid query. Let's try again.")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun currencyRates(): List<String> {
|
||||
val rates = ArrayList<String>(33)
|
||||
for ((key, value) in EXCHANGE_RATES) {
|
||||
rates.add(" $key: ${StringUtils.leftPad(value, 8)}")
|
||||
}
|
||||
return rates
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun loadRates() {
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
try {
|
||||
val builder = SAXBuilder()
|
||||
// See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755
|
||||
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "")
|
||||
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "")
|
||||
builder.ignoringElementContentWhitespace = true
|
||||
val doc = builder.build(URL(EXCHANGE_TABLE_URL))
|
||||
val root = doc.rootElement
|
||||
val ns = root.getNamespace("")
|
||||
val cubeRoot = root.getChild("Cube", ns)
|
||||
val cubeTime = cubeRoot.getChild("Cube", ns)
|
||||
pubDate = cubeTime.getAttribute("time").value
|
||||
val cubes = cubeTime.children
|
||||
for (cube in cubes) {
|
||||
EXCHANGE_RATES[cube.getAttribute("currency").value] = cube.getAttribute("rate").value
|
||||
}
|
||||
EXCHANGE_RATES["EUR"] = "1"
|
||||
} catch (e: JDOMException) {
|
||||
throw ModuleException(
|
||||
e.message,
|
||||
"An JDOM parsing error has occurred while parsing the exchange rates table.",
|
||||
e
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException(
|
||||
e.message,
|
||||
"An IO error has occurred while parsing the exchange rates table.",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(CURRENCY_CMD)
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Dice.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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static net.thauvin.erik.mobibot.Utils.bold;
|
||||
|
||||
/**
|
||||
* The Dice module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-28
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Dice extends AbstractModule {
|
||||
// Dice command
|
||||
private static final String DICE_CMD = "dice";
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public Dice(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(DICE_CMD);
|
||||
|
||||
help.add("To roll the dice:");
|
||||
help.add(Utils.helpIndent("%c " + DICE_CMD));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
final SecureRandom r = new SecureRandom();
|
||||
|
||||
int i = r.nextInt(6) + 1;
|
||||
int y = r.nextInt(6) + 1;
|
||||
final int playerTotal = i + y;
|
||||
|
||||
bot.send(bot.getChannel(),
|
||||
sender + " rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of "
|
||||
+ bold(playerTotal), isPrivate);
|
||||
|
||||
i = r.nextInt(6) + 1;
|
||||
y = r.nextInt(6) + 1;
|
||||
final int total = i + y;
|
||||
|
||||
bot.action(
|
||||
"rolled two dice: " + bold(i) + " and " + bold(y) + " for a total of " + bold(total));
|
||||
|
||||
if (playerTotal < total) {
|
||||
bot.action("wins.");
|
||||
} else if (playerTotal > total) {
|
||||
bot.action("lost.");
|
||||
} else {
|
||||
bot.action("tied.");
|
||||
}
|
||||
}
|
||||
}
|
103
src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt
Normal file
103
src/main/java/net/thauvin/erik/mobibot/modules/Dice.kt
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Dice.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* The Dice module.
|
||||
*/
|
||||
class Dice(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
val roll = roll()
|
||||
val playerRoll = roll()
|
||||
val total = roll.first + roll.second
|
||||
val playerTotal = playerRoll.first + playerRoll.second
|
||||
with(bot) {
|
||||
send(
|
||||
channel,
|
||||
"$sender rolled two dice: ${Utils.bold(playerRoll.first)} and ${Utils.bold(playerRoll.second)}"
|
||||
+ " for a total of ${Utils.bold(playerTotal)}",
|
||||
isPrivate
|
||||
)
|
||||
action(
|
||||
"rolled two dice: ${Utils.bold(roll.first)} and ${Utils.bold(roll.second)}" +
|
||||
" for a total of ${Utils.bold(total)}"
|
||||
)
|
||||
when (winLoseOrTie(total, playerTotal)) {
|
||||
Result.WIN -> action("wins.")
|
||||
Result.LOSE -> action("lost.")
|
||||
else -> action("tied.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Result {
|
||||
WIN, LOSE, TIE
|
||||
}
|
||||
|
||||
private fun roll(): Pair<Int, Int> {
|
||||
return Pair(Random.nextInt(1, 7), Random.nextInt(1, 7))
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Dice command
|
||||
private const val DICE_CMD = "dice"
|
||||
|
||||
fun winLoseOrTie(bot: Int, player: Int): Result {
|
||||
return when {
|
||||
bot > player -> {
|
||||
Result.WIN
|
||||
}
|
||||
bot < player -> {
|
||||
Result.LOSE
|
||||
}
|
||||
else -> {
|
||||
Result.TIE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(DICE_CMD)
|
||||
help.add("To roll the dice:")
|
||||
help.add(Utils.helpIndent("%c $DICE_CMD"))
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* GoogleSearch.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jibble.pircbot.Colors;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The GoogleSearch module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Feb 7, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class GoogleSearch extends ThreadedModule {
|
||||
// Google API Key property
|
||||
static final String GOOGLE_API_KEY_PROP = "google-api-key";
|
||||
// Google Custom Search Engine ID property
|
||||
static final String GOOGLE_CSE_KEY_PROP = "google-cse-cx";
|
||||
// Google command
|
||||
private static final String GOOGLE_CMD = "google";
|
||||
|
||||
/**
|
||||
* Creates a new {@link GoogleSearch} instance.
|
||||
*/
|
||||
public GoogleSearch(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(GOOGLE_CMD);
|
||||
|
||||
help.add("To search Google:");
|
||||
help.add(Utils.helpIndent("%c " + GOOGLE_CMD + " <query>"));
|
||||
|
||||
initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search on Google.
|
||||
*
|
||||
* @param query The search query.
|
||||
* @param apiKey The Google API key.
|
||||
* @param cseKey The Google CSE key.
|
||||
* @return The {@link Message} array containing the search results.
|
||||
* @throws ModuleException If an error occurs while searching.
|
||||
*/
|
||||
@SuppressFBWarnings({ "URLCONNECTION_SSRF_FD", "REC_CATCH_EXCEPTION" })
|
||||
@SuppressWarnings(("PMD.AvoidInstantiatingObjectsInLoops"))
|
||||
static List<Message> searchGoogle(final String query, final String apiKey, final String cseKey)
|
||||
throws ModuleException {
|
||||
if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) {
|
||||
throw new ModuleException(StringUtils.capitalize(GOOGLE_CMD) + " is disabled. The API keys are missing.");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(query)) {
|
||||
final ArrayList<Message> results = new ArrayList<>();
|
||||
try {
|
||||
final URL url =
|
||||
new URL("https://www.googleapis.com/customsearch/v1?key="
|
||||
+ apiKey
|
||||
+ "&cx="
|
||||
+ cseKey
|
||||
+ "&q="
|
||||
+ Utils.encodeUrl(query)
|
||||
+ "&filter=1&num=5&alt=json");
|
||||
|
||||
final JSONObject json = new JSONObject(Utils.urlReader(url));
|
||||
final JSONArray ja = json.getJSONArray("items");
|
||||
|
||||
for (int i = 0; i < ja.length(); i++) {
|
||||
final JSONObject j = ja.getJSONObject(i);
|
||||
results.add(new NoticeMessage(Utils.unescapeXml(j.getString("title"))));
|
||||
results.add(
|
||||
new NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ModuleException("searchGoogle(" + query + ')', "An error has occurred searching Google.", e);
|
||||
}
|
||||
|
||||
return results;
|
||||
} else {
|
||||
throw new ModuleException("Invalid query. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches Google.
|
||||
*/
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String query, final boolean isPrivate) {
|
||||
if (StringUtils.isNotBlank(query)) {
|
||||
try {
|
||||
final List<Message> results = searchGoogle(query, properties.get(GOOGLE_API_KEY_PROP),
|
||||
properties.get(GOOGLE_CSE_KEY_PROP));
|
||||
for (final Message msg : results) {
|
||||
bot.send(sender, msg);
|
||||
}
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage(), isPrivate);
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
125
src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt
Normal file
125
src/main/java/net/thauvin/erik/mobibot/modules/GoogleSearch.kt
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* GoogleSearch.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.jibble.pircbot.Colors
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The GoogleSearch module.
|
||||
*/
|
||||
class GoogleSearch(bot: Mobibot) : ThreadedModule(bot) {
|
||||
/**
|
||||
* Searches Google.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
try {
|
||||
val results = searchGoogle(
|
||||
args, properties[GOOGLE_API_KEY_PROP],
|
||||
properties[GOOGLE_CSE_KEY_PROP]
|
||||
)
|
||||
for (msg in results) {
|
||||
send(sender, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn(e.debugMessage, e)
|
||||
send(sender, e.message, isPrivate)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Google API Key property
|
||||
const val GOOGLE_API_KEY_PROP = "google-api-key"
|
||||
|
||||
// Google Custom Search Engine ID property
|
||||
const val GOOGLE_CSE_KEY_PROP = "google-cse-cx"
|
||||
|
||||
// Google command
|
||||
private const val GOOGLE_CMD = "google"
|
||||
|
||||
/**
|
||||
* Performs a search on Google.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message> {
|
||||
if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(cseKey)) {
|
||||
throw ModuleException("${StringUtils.capitalize(GOOGLE_CMD)} is disabled. The API keys are missing.")
|
||||
}
|
||||
return if (StringUtils.isNotBlank(query)) {
|
||||
val results = ArrayList<Message>()
|
||||
try {
|
||||
val url = URL(
|
||||
"https://www.googleapis.com/customsearch/v1?key=$apiKey&cx=$cseKey" +
|
||||
"&q=${Utils.encodeUrl(query)}&filter=1&num=5&alt=json"
|
||||
)
|
||||
val json = JSONObject(Utils.urlReader(url))
|
||||
val ja = json.getJSONArray("items")
|
||||
for (i in 0 until ja.length()) {
|
||||
val j = ja.getJSONObject(i)
|
||||
results.add(NoticeMessage(Utils.unescapeXml(j.getString("title"))))
|
||||
results.add(NoticeMessage(Utils.helpIndent(j.getString("link"), false), Colors.DARK_GREEN))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("searchGoogle($query)", "An IO error has occurred searching Google.", e)
|
||||
} catch (e: JSONException) {
|
||||
throw ModuleException("searchGoogle($query)", "A JSON error has occurred searching Google.", e)
|
||||
}
|
||||
results
|
||||
} else {
|
||||
throw ModuleException("Invalid query. Please try again.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(GOOGLE_CMD)
|
||||
help.add("To search Google:")
|
||||
help.add(Utils.helpIndent("%c $GOOGLE_CMD <query>"))
|
||||
initProperties(GOOGLE_API_KEY_PROP, GOOGLE_CSE_KEY_PROP)
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Joke.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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* The Joke module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-20
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Joke extends ThreadedModule {
|
||||
// Joke command
|
||||
private static final String JOKE_CMD = "joke";
|
||||
// ICNDB URL
|
||||
private static final String JOKE_URL =
|
||||
"http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]";
|
||||
|
||||
/**
|
||||
* Creates a new {@link Joke} instance.
|
||||
*/
|
||||
public Joke(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(JOKE_CMD);
|
||||
|
||||
help.add("To retrieve a random joke:");
|
||||
help.add(Utils.helpIndent("%c " + JOKE_CMD));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a random joke.
|
||||
*
|
||||
* @return The {@link Message} containing the new joke.
|
||||
* @throws ModuleException If an error occurs while retrieving a new joke.
|
||||
*/
|
||||
static Message randomJoke() throws ModuleException {
|
||||
try {
|
||||
final URL url = new URL(JOKE_URL);
|
||||
final JSONObject json = new JSONObject(Utils.urlReader(url));
|
||||
return new PublicMessage(
|
||||
json.getJSONObject("value").get("joke").toString().replace("\\'", "'")
|
||||
.replace("\\\"", "\""));
|
||||
} catch (Exception e) {
|
||||
throw new ModuleException("randomJoke()", "An error has occurred retrieving a random joke.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
new Thread(() -> run(sender, cmd, args, isPrivate)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>.
|
||||
*/
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String arg, final boolean isPrivate) {
|
||||
try {
|
||||
bot.send(Utils.cyan(randomJoke().getMsg()));
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage(), isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
100
src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt
Normal file
100
src/main/java/net/thauvin/erik/mobibot/modules/Joke.kt
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Joke.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* The Joke module.
|
||||
*/
|
||||
class Joke(bot: Mobibot) : ThreadedModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
Thread { run(sender, cmd, args, isPrivate) }.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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
|
||||
private const val JOKE_CMD = "joke"
|
||||
|
||||
// ICNDB URL
|
||||
private const val JOKE_URL =
|
||||
"http://api.icndb.com/jokes/random?escape=javascript&exclude=[explicit]&limitTo=[nerdy]"
|
||||
|
||||
/**
|
||||
* Retrieves a random joke.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun randomJoke(): Message {
|
||||
return try {
|
||||
val url = URL(JOKE_URL)
|
||||
val json = JSONObject(Utils.urlReader(url))
|
||||
PublicMessage(
|
||||
json.getJSONObject("value")["joke"].toString().replace("\\'", "'")
|
||||
.replace("\\\"", "\"")
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("randomJoke()", "An IO error has occurred retrieving a random joke.", e)
|
||||
} catch (e: JSONException) {
|
||||
throw ModuleException("randomJoke()", "An JSON error has occurred retrieving a random joke.", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(JOKE_CMD)
|
||||
help.add("To retrieve a random joke:")
|
||||
help.add(Utils.helpIndent("%c $JOKE_CMD"))
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* Lookup.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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.net.whois.WhoisClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* The Lookup module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-26
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Lookup extends AbstractModule {
|
||||
/**
|
||||
* The whois default host.
|
||||
*/
|
||||
static final String WHOIS_HOST = "whois.arin.net";
|
||||
|
||||
// Lookup command
|
||||
private static final String LOOKUP_CMD = "lookup";
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public Lookup(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(LOOKUP_CMD);
|
||||
|
||||
help.add("To perform a DNS lookup query:");
|
||||
help.add(Utils.helpIndent("%c " + LOOKUP_CMD + " <ip address or hostname>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a DNS lookup on the specified query.
|
||||
*
|
||||
* @param query The IP address or hostname.
|
||||
* @return The lookup query result string.
|
||||
* @throws java.net.UnknownHostException If the host is unknown.
|
||||
*/
|
||||
public static String lookup(final String query)
|
||||
throws UnknownHostException {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
|
||||
final InetAddress[] results = InetAddress.getAllByName(query);
|
||||
String hostInfo;
|
||||
|
||||
for (final InetAddress result : results) {
|
||||
if (result.getHostAddress().equals(query)) {
|
||||
hostInfo = result.getHostName();
|
||||
|
||||
if (hostInfo.equals(query)) {
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
} else {
|
||||
hostInfo = result.getHostAddress();
|
||||
}
|
||||
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
|
||||
buffer.append(hostInfo);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*
|
||||
* @param query The IP address.
|
||||
* @return The IP whois data, if any.
|
||||
* @throws java.io.IOException If a connection error occurs.
|
||||
*/
|
||||
private static String[] whois(final String query)
|
||||
throws IOException {
|
||||
return whois(query, WHOIS_HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*
|
||||
* @param query The IP address.
|
||||
* @param host The whois host.
|
||||
* @return The IP whois data, if any.
|
||||
* @throws java.io.IOException If a connection error occurs.
|
||||
*/
|
||||
public static String[] whois(final String query, final String host)
|
||||
throws IOException {
|
||||
final WhoisClient whoisClient = new WhoisClient();
|
||||
final String[] lines;
|
||||
|
||||
try {
|
||||
whoisClient.setDefaultTimeout(Constants.CONNECT_TIMEOUT);
|
||||
whoisClient.connect(host);
|
||||
whoisClient.setSoTimeout(Constants.CONNECT_TIMEOUT);
|
||||
whoisClient.setSoLinger(false, 0);
|
||||
|
||||
if (WHOIS_HOST.equals(host)) {
|
||||
lines = whoisClient.query("n - " + query).split("\n");
|
||||
} else {
|
||||
lines = whoisClient.query(query).split("\n");
|
||||
}
|
||||
} finally {
|
||||
whoisClient.disconnect();
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (args.matches("(\\S.)+(\\S)+")) {
|
||||
try {
|
||||
bot.send(Lookup.lookup(args));
|
||||
} catch (UnknownHostException ignore) {
|
||||
if (args.matches(
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\."
|
||||
+ "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) {
|
||||
try {
|
||||
final String[] lines = Lookup.whois(args);
|
||||
|
||||
if ((lines != null) && (lines.length > 0)) {
|
||||
String line;
|
||||
|
||||
for (final String rawLine : lines) {
|
||||
line = rawLine.trim();
|
||||
|
||||
if ((line.length() > 0) && (line.charAt(0) != '#')) {
|
||||
bot.send(line);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot.send("Unknown host.");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
bot.getLogger().debug("Unable to perform whois IP lookup: {}", args, ioe);
|
||||
bot.send("Unable to perform whois IP lookup: " + ioe.getMessage());
|
||||
}
|
||||
} else {
|
||||
bot.send("Unknown host.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, true);
|
||||
}
|
||||
}
|
||||
}
|
169
src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt
Normal file
169
src/main/java/net/thauvin/erik/mobibot/modules/Lookup.kt
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Lookup.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import org.apache.commons.net.whois.WhoisClient
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
/**
|
||||
* The Lookup module.
|
||||
*/
|
||||
class Lookup(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (args.matches("(\\S.)+(\\S)+".toRegex())) {
|
||||
with(bot) {
|
||||
try {
|
||||
send(lookup(args))
|
||||
} catch (ignore: UnknownHostException) {
|
||||
if (args.matches(
|
||||
("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
|
||||
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)").toRegex()
|
||||
)
|
||||
) {
|
||||
try {
|
||||
val lines = whois(args)
|
||||
if (lines.isNotEmpty()) {
|
||||
var line: String
|
||||
for (rawLine in lines) {
|
||||
line = rawLine.trim()
|
||||
if (line.isNotEmpty() && line[0] != '#') {
|
||||
send(line)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send("Unknown host.")
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
logger.debug("Unable to perform whois IP lookup: {}", args, ioe)
|
||||
send("Unable to perform whois IP lookup: ${ioe.message}")
|
||||
}
|
||||
} else {
|
||||
send("Unknown host.")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The whois default host.
|
||||
*/
|
||||
const val WHOIS_HOST = "whois.arin.net"
|
||||
|
||||
// Lookup command
|
||||
private const val LOOKUP_CMD = "lookup"
|
||||
|
||||
/**
|
||||
* Performs a DNS lookup on the specified query.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(UnknownHostException::class)
|
||||
fun lookup(query: String): String {
|
||||
val buffer = StringBuilder()
|
||||
val results = InetAddress.getAllByName(query)
|
||||
var hostInfo: String
|
||||
for (result in results) {
|
||||
if (result.hostAddress == query) {
|
||||
hostInfo = result.hostName
|
||||
if (hostInfo == query) {
|
||||
throw UnknownHostException()
|
||||
}
|
||||
} else {
|
||||
hostInfo = result.hostAddress
|
||||
}
|
||||
if (buffer.isNotEmpty()) {
|
||||
buffer.append(", ")
|
||||
}
|
||||
buffer.append(hostInfo)
|
||||
}
|
||||
return buffer.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*
|
||||
* @param query The IP address.
|
||||
* @return The IP whois data, if any.
|
||||
* @throws java.io.IOException If a connection error occurs.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun whois(query: String): Array<String> {
|
||||
return whois(query, WHOIS_HOST)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a whois IP query.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun whois(query: String, host: String): Array<String> {
|
||||
val whoisClient = WhoisClient()
|
||||
val lines: Array<String>
|
||||
with(whoisClient) {
|
||||
try {
|
||||
defaultTimeout = Constants.CONNECT_TIMEOUT
|
||||
connect(host)
|
||||
soTimeout = Constants.CONNECT_TIMEOUT
|
||||
setSoLinger(false, 0)
|
||||
lines = if (WHOIS_HOST == host) {
|
||||
query("n - $query").split("\n").toTypedArray()
|
||||
} else {
|
||||
query(query).split("\n").toTypedArray()
|
||||
}
|
||||
} finally {
|
||||
disconnect()
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(LOOKUP_CMD)
|
||||
help.add("To perform a DNS lookup query:")
|
||||
help.add(Utils.helpIndent("%c $LOOKUP_CMD <ip address or hostname>"))
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* ModuleException.java
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* The <code>ModuleException</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ModuleException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String debugMessage;
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
ModuleException(final String message) {
|
||||
super(message);
|
||||
this.debugMessage = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* @param debugMessage The debug message.
|
||||
* @param message The exception message.
|
||||
* @param cause The cause.
|
||||
*/
|
||||
ModuleException(final String debugMessage, final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
this.debugMessage = debugMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*
|
||||
* @param debugMessage The debug message.
|
||||
* @param message The exception message.
|
||||
*/
|
||||
ModuleException(final String debugMessage, final String message) {
|
||||
super(message);
|
||||
this.debugMessage = debugMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the debug message.
|
||||
*
|
||||
* @return The debug message.
|
||||
*/
|
||||
String getDebugMessage() {
|
||||
return debugMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sanitized message (e.g. remove API keys, etc.)
|
||||
*
|
||||
* @param sanitize The words to sanitize.
|
||||
* @return The sanitized message.
|
||||
*/
|
||||
String getSanitizedMessage(final String... sanitize) {
|
||||
final String[] obfuscate = new String[sanitize.length];
|
||||
for (int i = 0; i < sanitize.length; i++) {
|
||||
obfuscate[i] = Utils.obfuscate(sanitize[i]);
|
||||
}
|
||||
return getCause().getClass().getName() + ": " + StringUtils.replaceEach(getCause().getMessage(),
|
||||
sanitize,
|
||||
obfuscate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the exception has a cause.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
boolean hasCause() {
|
||||
return getCause() != null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* ModuleException.kit
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
/**
|
||||
* The `ModuleException` class.
|
||||
*/
|
||||
class ModuleException : Exception {
|
||||
/**
|
||||
* Returns the debug message.
|
||||
*/
|
||||
val debugMessage: String?
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*/
|
||||
constructor(message: String?) : super(message) {
|
||||
debugMessage = message
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*/
|
||||
constructor(debugMessage: String?, message: String?, cause: Throwable?) : super(message, cause) {
|
||||
this.debugMessage = debugMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*/
|
||||
constructor(debugMessage: String?, message: String?) : super(message) {
|
||||
this.debugMessage = debugMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sanitized message (e.g. remove API keys, etc.)
|
||||
*/
|
||||
fun getSanitizedMessage(vararg sanitize: String?): String {
|
||||
val obfuscate = arrayOfNulls<String>(sanitize.size)
|
||||
for (i in sanitize.indices) {
|
||||
obfuscate[i] = Utils.obfuscate(sanitize[i])
|
||||
}
|
||||
return cause!!.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the exception has a cause.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
fun hasCause(): Boolean {
|
||||
return cause != null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 1L
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Ping.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.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Ping module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2016-07-02
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Ping extends AbstractModule {
|
||||
/**
|
||||
* The ping responses.
|
||||
*/
|
||||
static final List<String> PINGS =
|
||||
Arrays.asList(
|
||||
"is barely alive.",
|
||||
"is trying to stay awake.",
|
||||
"has gone fishing.",
|
||||
"is somewhere over the rainbow.",
|
||||
"has fallen and can't get up.",
|
||||
"is running. You better go chase it.",
|
||||
"has just spontaneously combusted.",
|
||||
"is talking to itself... don't interrupt. That's rude.",
|
||||
"is bartending at an AA meeting.",
|
||||
"is hibernating.",
|
||||
"is saving energy: apathetic mode activated.",
|
||||
"is busy. Go away!");
|
||||
/**
|
||||
* The ping command.
|
||||
*/
|
||||
private static final String PING_CMD = "ping";
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public Ping(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(PING_CMD);
|
||||
|
||||
help.add("To ping the bot:");
|
||||
help.add(Utils.helpIndent("%c " + PING_CMD));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
final SecureRandom r = new SecureRandom();
|
||||
bot.action(PINGS.get(r.nextInt(PINGS.size())));
|
||||
}
|
||||
}
|
90
src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt
Normal file
90
src/main/java/net/thauvin/erik/mobibot/modules/Ping.kt
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Ping.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* The Ping module.
|
||||
*/
|
||||
class Ping(bot: Mobibot) : AbstractModule(bot) {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
bot.action(randomPing())
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The ping responses.
|
||||
*/
|
||||
@JvmField
|
||||
val PINGS = listOf(
|
||||
"is barely alive.",
|
||||
"is trying to stay awake.",
|
||||
"has gone fishing.",
|
||||
"is somewhere over the rainbow.",
|
||||
"has fallen and can't get up.",
|
||||
"is running. You better go chase it.",
|
||||
"has just spontaneously combusted.",
|
||||
"is talking to itself... don't interrupt. That's rude.",
|
||||
"is bartending at an AA meeting.",
|
||||
"is hibernating.",
|
||||
"is saving energy: apathetic mode activated.",
|
||||
"is busy. Go away!"
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun randomPing(): String {
|
||||
return PINGS[Random.nextInt(PINGS.size)]
|
||||
}
|
||||
|
||||
/**
|
||||
* The ping command.
|
||||
*/
|
||||
private const val PING_CMD = "ping"
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(PING_CMD)
|
||||
help.add("To ping the bot:")
|
||||
help.add(Utils.helpIndent("%c $PING_CMD"))
|
||||
}
|
||||
}
|
|
@ -34,9 +34,6 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.red
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
|
@ -96,21 +93,23 @@ class RockPaperScissors(bot: Mobibot) : AbstractModule(bot) {
|
|||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(sender: String, cmd: String, args: String?, isPrivate: Boolean) {
|
||||
override fun commandResponse(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
val hand = Hands.valueOf(cmd.toUpperCase())
|
||||
val botHand = Hands.values()[Random.nextInt(0, Hands.values().size)]
|
||||
when {
|
||||
hand == botHand -> {
|
||||
bot.send("${green(hand.name)} vs. ${green(botHand.name)}")
|
||||
bot.action("tied.")
|
||||
}
|
||||
hand.beats(botHand) -> {
|
||||
bot.send("${green(hand.name)} ${bold(hand.action)} ${red(botHand.name)}")
|
||||
bot.action("lost.")
|
||||
}
|
||||
else -> {
|
||||
bot.send("${green(botHand.name)} ${bold(botHand.action)} ${red(hand.name)}")
|
||||
bot.action("wins.")
|
||||
with(bot) {
|
||||
when {
|
||||
hand == botHand -> {
|
||||
send("${Utils.green(hand.name)} vs. ${Utils.green(botHand.name)}")
|
||||
action("tied.")
|
||||
}
|
||||
hand.beats(botHand) -> {
|
||||
send("${Utils.green(hand.name)} ${Utils.bold(hand.action)} ${Utils.red(botHand.name)}")
|
||||
action("lost.")
|
||||
}
|
||||
else -> {
|
||||
send("${Utils.green(botHand.name)} ${Utils.bold(botHand.action)} ${Utils.red(hand.name)}")
|
||||
action("wins.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* StockQuote.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
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.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The StockQuote module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Feb 7, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class StockQuote extends ThreadedModule {
|
||||
/**
|
||||
* The Alpha Advantage property key.
|
||||
*/
|
||||
static final String ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key";
|
||||
/**
|
||||
* The Invalid Symbol error string.
|
||||
*/
|
||||
static final String INVALID_SYMBOL = "Invalid symbol.";
|
||||
// Alpha Advantage URL
|
||||
private static final String ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function=";
|
||||
// Quote command
|
||||
private static final String STOCK_CMD = "stock";
|
||||
|
||||
/**
|
||||
* Creates a new {@link StockQuote} instance.
|
||||
*/
|
||||
public StockQuote(final Mobibot bot) {
|
||||
super(bot);
|
||||
commands.add(STOCK_CMD);
|
||||
|
||||
help.add("To retrieve a stock quote:");
|
||||
help.add(Utils.helpIndent("%c " + STOCK_CMD + " <symbol|keywords>"));
|
||||
|
||||
initProperties(ALPHAVANTAGE_API_KEY_PROP);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
|
||||
justification = "false positive?")
|
||||
private static JSONObject getJsonResponse(final String response, final String debugMessage)
|
||||
throws ModuleException {
|
||||
if (StringUtils.isNotBlank(response)) {
|
||||
final JSONObject json = new JSONObject(response);
|
||||
|
||||
try {
|
||||
final String info = json.getString("Information");
|
||||
if (!info.isEmpty()) {
|
||||
throw new ModuleException(debugMessage, Utils.unescapeXml(info));
|
||||
}
|
||||
} catch (JSONException ignore) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
try {
|
||||
final String error = json.getString("Note");
|
||||
if (!error.isEmpty()) {
|
||||
throw new ModuleException(debugMessage, Utils.unescapeXml(error));
|
||||
}
|
||||
} catch (JSONException ignore) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
try {
|
||||
final String error = json.getString("Error Message");
|
||||
if (!error.isEmpty()) {
|
||||
throw new ModuleException(debugMessage, Utils.unescapeXml(error));
|
||||
}
|
||||
} catch (JSONException ignore) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return json;
|
||||
} else {
|
||||
throw new ModuleException(debugMessage, "Empty Response.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a stock quote.
|
||||
*
|
||||
* @param symbol The stock symbol.
|
||||
* @return The {@link Message} array containing the stock quote.
|
||||
* @throws ModuleException If an errors occurs.
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.CloseResource" })
|
||||
static List<Message> getQuote(final String symbol, final String apiKey) throws ModuleException {
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
throw new ModuleException(StringUtils.capitalize(STOCK_CMD) + " is disabled. The API key is missing.");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(symbol)) {
|
||||
final String debugMessage = "getQuote(" + symbol + ')';
|
||||
final ArrayList<Message> messages = new ArrayList<>();
|
||||
|
||||
String response;
|
||||
try {
|
||||
// Search for symbol/keywords
|
||||
response = Utils.urlReader(new URL(
|
||||
ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey="
|
||||
+ Utils.encodeUrl(apiKey)));
|
||||
|
||||
JSONObject json = getJsonResponse(response, debugMessage);
|
||||
|
||||
final JSONArray symbols = json.getJSONArray("bestMatches");
|
||||
if (symbols.isEmpty()) {
|
||||
messages.add(new ErrorMessage(INVALID_SYMBOL));
|
||||
return messages;
|
||||
}
|
||||
|
||||
final JSONObject symbolInfo = symbols.getJSONObject(0);
|
||||
|
||||
// Get quote for symbol
|
||||
response = Utils.urlReader(new URL(
|
||||
ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + Utils.encodeUrl(symbolInfo.getString("1. symbol"))
|
||||
+ "&apikey=" + Utils.encodeUrl(apiKey)));
|
||||
|
||||
json = getJsonResponse(response, debugMessage);
|
||||
|
||||
final JSONObject quote = json.getJSONObject("Global Quote");
|
||||
|
||||
if (quote.isEmpty()) {
|
||||
messages.add(new ErrorMessage(INVALID_SYMBOL));
|
||||
return messages;
|
||||
}
|
||||
|
||||
messages.add(new PublicMessage(
|
||||
"Symbol: " + Utils.unescapeXml(quote.getString("01. symbol")) + " [" + Utils.unescapeXml(
|
||||
symbolInfo.getString("2. name") + ']')));
|
||||
messages.add(new PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price"))));
|
||||
messages.add(new PublicMessage(
|
||||
" Previous: "
|
||||
+ Utils.unescapeXml(quote.getString("08. previous close"))));
|
||||
messages.add(new NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open"))));
|
||||
messages.add(new NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high"))));
|
||||
messages.add(new NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low"))));
|
||||
messages.add(new NoticeMessage(" Volume: " + Utils.unescapeXml(quote.getString("06. volume"))));
|
||||
messages.add(new NoticeMessage(
|
||||
" Latest: " + Utils.unescapeXml(quote.getString("07. latest trading day"))));
|
||||
messages.add(new NoticeMessage(
|
||||
" Change: " + Utils.unescapeXml(quote.getString("09. change")) + " [" + Utils.unescapeXml(
|
||||
quote.getString("10. change percent")) + ']'));
|
||||
} catch (IOException | NullPointerException e) {
|
||||
throw new ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e);
|
||||
}
|
||||
return messages;
|
||||
} else {
|
||||
throw new ModuleException(INVALID_SYMBOL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified stock quote from Alpha Avantage.
|
||||
*/
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String symbol, final boolean isPrivate) {
|
||||
if (StringUtils.isNotBlank(symbol)) {
|
||||
try {
|
||||
final List<Message> messages = getQuote(symbol, properties.get(ALPHAVANTAGE_API_KEY_PROP));
|
||||
for (final Message msg : messages) {
|
||||
bot.send(sender, msg);
|
||||
}
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
214
src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt
Normal file
214
src/main/java/net/thauvin/erik/mobibot/modules/StockQuote.kt
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* StockQuote.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
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
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The StockQuote module.
|
||||
*/
|
||||
class StockQuote(bot: Mobibot) : ThreadedModule(bot) {
|
||||
/**
|
||||
* Returns the specified stock quote from Alpha Avantage.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
try {
|
||||
val messages = getQuote(args, properties[ALPHAVANTAGE_API_KEY_PROP])
|
||||
for (msg in messages) {
|
||||
send(sender, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn(e.debugMessage, e)
|
||||
send(e.message)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The Alpha Advantage property key.
|
||||
*/
|
||||
const val ALPHAVANTAGE_API_KEY_PROP = "alphavantage-api-key"
|
||||
|
||||
/**
|
||||
* The Invalid Symbol error string.
|
||||
*/
|
||||
const val INVALID_SYMBOL = "Invalid symbol."
|
||||
|
||||
// Alpha Advantage URL
|
||||
private const val ALAPHAVANTAGE_URL = "https://www.alphavantage.co/query?function="
|
||||
|
||||
// Quote command
|
||||
private const val STOCK_CMD = "stock"
|
||||
|
||||
@Throws(ModuleException::class)
|
||||
private fun getJsonResponse(response: String, debugMessage: String): JSONObject {
|
||||
return if (StringUtils.isNotBlank(response)) {
|
||||
val json = JSONObject(response)
|
||||
try {
|
||||
val info = json.getString("Information")
|
||||
if (info.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, Utils.unescapeXml(info))
|
||||
}
|
||||
} catch (ignore: JSONException) {
|
||||
// Do nothing
|
||||
}
|
||||
try {
|
||||
var error = json.getString("Note")
|
||||
if (error.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, Utils.unescapeXml(error))
|
||||
}
|
||||
error = json.getString("Error Message")
|
||||
if (error.isNotEmpty()) {
|
||||
throw ModuleException(debugMessage, Utils.unescapeXml(error))
|
||||
}
|
||||
} catch (ignore: JSONException) {
|
||||
// Do nothing
|
||||
}
|
||||
json
|
||||
} else {
|
||||
throw ModuleException(debugMessage, "Empty Response.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a stock quote.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun getQuote(symbol: String, apiKey: String?): List<Message> {
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
throw ModuleException("${STOCK_CMD.capitalize()} is disabled. The API key is missing.")
|
||||
}
|
||||
return if (StringUtils.isNotBlank(symbol)) {
|
||||
val debugMessage = "getQuote($symbol)"
|
||||
val messages = ArrayList<Message>()
|
||||
var response: String
|
||||
try {
|
||||
with(messages) {
|
||||
// Search for symbol/keywords
|
||||
response = Utils.urlReader(
|
||||
URL(
|
||||
"${ALAPHAVANTAGE_URL}SYMBOL_SEARCH&keywords=" + Utils.encodeUrl(symbol) + "&apikey="
|
||||
+ Utils.encodeUrl(apiKey)
|
||||
)
|
||||
)
|
||||
var json = getJsonResponse(response, debugMessage)
|
||||
val symbols = json.getJSONArray("bestMatches")
|
||||
if (symbols.isEmpty) {
|
||||
messages.add(ErrorMessage(INVALID_SYMBOL))
|
||||
} else {
|
||||
val symbolInfo = symbols.getJSONObject(0)
|
||||
|
||||
// Get quote for symbol
|
||||
response = Utils.urlReader(
|
||||
URL(
|
||||
"${ALAPHAVANTAGE_URL}GLOBAL_QUOTE&symbol="
|
||||
+ Utils.encodeUrl(symbolInfo.getString("1. symbol"))
|
||||
+ "&apikey=" + Utils.encodeUrl(apiKey)
|
||||
)
|
||||
)
|
||||
json = getJsonResponse(response, debugMessage)
|
||||
val quote = json.getJSONObject("Global Quote")
|
||||
if (quote.isEmpty) {
|
||||
add(ErrorMessage(INVALID_SYMBOL))
|
||||
return messages
|
||||
}
|
||||
add(
|
||||
PublicMessage(
|
||||
"Symbol: " + Utils.unescapeXml(quote.getString("01. symbol"))
|
||||
+ " [" + Utils.unescapeXml(symbolInfo.getString("2. name")) + ']'
|
||||
)
|
||||
)
|
||||
add(PublicMessage(" Price: " + Utils.unescapeXml(quote.getString("05. price"))))
|
||||
add(
|
||||
PublicMessage(
|
||||
" Previous: " + Utils.unescapeXml(quote.getString("08. previous close"))
|
||||
)
|
||||
)
|
||||
add(NoticeMessage(" Open: " + Utils.unescapeXml(quote.getString("02. open"))))
|
||||
add(NoticeMessage(" High: " + Utils.unescapeXml(quote.getString("03. high"))))
|
||||
add(NoticeMessage(" Low: " + Utils.unescapeXml(quote.getString("04. low"))))
|
||||
add(
|
||||
NoticeMessage(
|
||||
" Volume: " + Utils.unescapeXml(quote.getString("06. volume"))
|
||||
)
|
||||
)
|
||||
add(
|
||||
NoticeMessage(
|
||||
" Latest: "
|
||||
+ Utils.unescapeXml(quote.getString("07. latest trading day"))
|
||||
)
|
||||
)
|
||||
add(
|
||||
NoticeMessage(
|
||||
" Change: " + Utils.unescapeXml(quote.getString("09. change"))
|
||||
+ " [" + Utils.unescapeXml(quote.getString("10. change percent")) + ']'
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException(debugMessage, "An IO error has occurred retrieving a stock quote.", e)
|
||||
} catch (e: NullPointerException) {
|
||||
throw ModuleException(debugMessage, "An error has occurred retrieving a stock quote.", e)
|
||||
}
|
||||
messages
|
||||
} else {
|
||||
throw ModuleException(INVALID_SYMBOL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(STOCK_CMD)
|
||||
help.add("To retrieve a stock quote:")
|
||||
help.add(Utils.helpIndent("%c $STOCK_CMD <symbol|keywords>"))
|
||||
initProperties(ALPHAVANTAGE_API_KEY_PROP)
|
||||
}
|
||||
}
|
|
@ -29,40 +29,34 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
|
||||
/**
|
||||
* The <code>ThreadedModule</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-03
|
||||
* @since 1.0
|
||||
* The `ThreadedModule` class.
|
||||
*/
|
||||
public abstract class ThreadedModule extends AbstractModule {
|
||||
ThreadedModule(final Mobibot bot) {
|
||||
super(bot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (isEnabled() && args.length() > 0) {
|
||||
new Thread(() -> run(sender, cmd, args, isPrivate)).start();
|
||||
abstract class ThreadedModule(bot: Mobibot) : AbstractModule(bot) {
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
if (isEnabled && args.isNotEmpty()) {
|
||||
Thread { run(sender, cmd, args, isPrivate) }.start()
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the thread.
|
||||
*/
|
||||
abstract void run(String sender,
|
||||
@SuppressWarnings("unused") String cmd,
|
||||
String args,
|
||||
boolean isPrivate);
|
||||
abstract fun run(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
)
|
||||
}
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
* Twitter.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo;
|
||||
import net.thauvin.erik.mobibot.TwitterTimer;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr;
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import twitter4j.DirectMessage;
|
||||
import twitter4j.Status;
|
||||
import twitter4j.TwitterFactory;
|
||||
import twitter4j.conf.ConfigurationBuilder;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* The Twitter module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Sept 10, 2008
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Twitter extends ThreadedModule {
|
||||
// Property keys
|
||||
static final String AUTOPOST_PROP = "twitter-auto-post";
|
||||
static final String CONSUMER_KEY_PROP = "twitter-consumerKey";
|
||||
static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret";
|
||||
static final String HANDLE_PROP = "twitter-handle";
|
||||
static final String TOKEN_PROP = "twitter-token";
|
||||
static final String TOKEN_SECRET_PROP = "twitter-tokenSecret";
|
||||
// Twitter command
|
||||
private static final String TWITTER_CMD = "twitter";
|
||||
|
||||
// Twitter auto-posts.
|
||||
private final Set<Integer> entries = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Twitter} instance.
|
||||
*/
|
||||
public Twitter(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(TWITTER_CMD);
|
||||
|
||||
help.add("To post to Twitter:");
|
||||
help.add(Utils.helpIndent("%c " + TWITTER_CMD + " <message>"));
|
||||
|
||||
properties.put(AUTOPOST_PROP, "false");
|
||||
initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts on Twitter.
|
||||
*
|
||||
* @param consumerKey The consumer key.
|
||||
* @param consumerSecret The consumer secret.
|
||||
* @param token The token.
|
||||
* @param tokenSecret The token secret.
|
||||
* @param handle The Twitter handle (dm) or nickname.
|
||||
* @param message The message to post.
|
||||
* @param isDm The direct message flag.
|
||||
* @return The confirmation {@link Message}.
|
||||
* @throws ModuleException If an error occurs while posting.
|
||||
*/
|
||||
static Message twitterPost(final String consumerKey,
|
||||
final String consumerSecret,
|
||||
final String token,
|
||||
final String tokenSecret,
|
||||
final String handle,
|
||||
final String message,
|
||||
final boolean isDm) throws ModuleException {
|
||||
try {
|
||||
final ConfigurationBuilder cb = new ConfigurationBuilder();
|
||||
cb.setDebugEnabled(true).setOAuthConsumerKey(consumerKey).setOAuthConsumerSecret(consumerSecret)
|
||||
.setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret);
|
||||
|
||||
final TwitterFactory tf = new TwitterFactory(cb.build());
|
||||
final twitter4j.Twitter twitter = tf.getInstance();
|
||||
|
||||
if (!isDm) {
|
||||
final Status status = twitter.updateStatus(message);
|
||||
return new NoticeMessage("You message was posted to https://twitter.com/" + twitter.getScreenName()
|
||||
+ "/statuses/" + status.getId());
|
||||
} else {
|
||||
final DirectMessage dm = twitter.sendDirectMessage(handle, message);
|
||||
return new NoticeMessage(dm.getText());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ModuleException("twitterPost(" + message + ")", "An error has occurred: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to be posted on Twitter.
|
||||
*
|
||||
* @param index The entry index.
|
||||
*/
|
||||
public final void addEntry(final int index) {
|
||||
entries.add(index);
|
||||
}
|
||||
|
||||
public final int entriesCount() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
public String getHandle() {
|
||||
return properties.get(HANDLE_PROP);
|
||||
}
|
||||
|
||||
public final boolean hasEntry(final int index) {
|
||||
return entries.contains(index);
|
||||
}
|
||||
|
||||
public boolean isAutoPost() {
|
||||
return isEnabled() && Boolean.parseBoolean(properties.get(AUTOPOST_PROP));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValidProperties() {
|
||||
for (final String s : getPropertyKeys()) {
|
||||
if (!AUTOPOST_PROP.equals(s) && !HANDLE_PROP.equals(s) && StringUtils.isBlank(properties.get(s))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the registered Twitter handle.
|
||||
*
|
||||
* @param msg The twitter message.
|
||||
*/
|
||||
public final void notification(final String msg) {
|
||||
if (isEnabled() && isNotBlank(getHandle())) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
post(String.format(msg, bot.getName(), ReleaseInfo.VERSION, bot.getChannel()), true);
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn("Failed to notify @{}: {}", getHandle(), msg, e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts on Twitter.
|
||||
*
|
||||
* @param message The message to post.
|
||||
* @param isDm The direct message flag.
|
||||
* @throws ModuleException If an error occurs while posting.
|
||||
*/
|
||||
public void post(final String message, final boolean isDm)
|
||||
throws ModuleException {
|
||||
post(properties.get(HANDLE_PROP), message, isDm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts on Twitter.
|
||||
*
|
||||
* @param handle The Twitter handle (dm) or nickname.
|
||||
* @param message The message to post.
|
||||
* @param isDm The direct message flag.
|
||||
* @return The {@link Message} to send back.
|
||||
* @throws ModuleException If an error occurs while posting.
|
||||
*/
|
||||
public Message post(final String handle, final String message, final boolean isDm)
|
||||
throws ModuleException {
|
||||
return twitterPost(properties.get(CONSUMER_KEY_PROP),
|
||||
properties.get(CONSUMER_SECRET_PROP),
|
||||
properties.get(TOKEN_PROP),
|
||||
properties.get(TOKEN_SECRET_PROP),
|
||||
handle,
|
||||
message,
|
||||
isDm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an entry to twitter.
|
||||
*
|
||||
* @param index The post entry index.
|
||||
*/
|
||||
@SuppressFBWarnings("SUI_CONTAINS_BEFORE_REMOVE")
|
||||
public final void postEntry(final int index) {
|
||||
if (isAutoPost() && hasEntry(index) && LinksMgr.getEntriesCount() >= index) {
|
||||
final EntryLink entry = LinksMgr.getEntry(index);
|
||||
final String msg =
|
||||
entry.getTitle() + ' ' + entry.getLink() + " via " + entry.getNick() + " on " + bot.getChannel();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
bot.getLogger().debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1);
|
||||
}
|
||||
post(msg, false);
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn("Failed to post entry on Twitter.", e);
|
||||
}
|
||||
}).start();
|
||||
removeEntry(index);
|
||||
}
|
||||
}
|
||||
|
||||
public void queueEntry(final int index) {
|
||||
if (isAutoPost()) {
|
||||
addEntry(index);
|
||||
bot.getLogger().debug("Scheduling ${Constants.LINK_CMD}${index + 1} for posting on Twitter.");
|
||||
bot.getTimer().schedule(new TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
public final void removeEntry(final int index) {
|
||||
entries.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts to twitter.
|
||||
*/
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String message, final boolean isPrivate) {
|
||||
try {
|
||||
bot.send(sender,
|
||||
post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMsg(),
|
||||
isPrivate);
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage(), isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post all the entries to Twitter on shutdown.
|
||||
*/
|
||||
public final void shutdown() {
|
||||
if (isAutoPost()) {
|
||||
for (final int index : entries) {
|
||||
postEntry(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
244
src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt
Normal file
244
src/main/java/net/thauvin/erik/mobibot/modules/Twitter.kt
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Twitter.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.TwitterTimer
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entriesCount
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.getEntry
|
||||
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
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The Twitter module.
|
||||
*/
|
||||
class Twitter(bot: Mobibot) : ThreadedModule(bot) {
|
||||
// Twitter auto-posts.
|
||||
private val entries: MutableSet<Int> = HashSet()
|
||||
|
||||
/**
|
||||
* Add an entry to be posted on Twitter.
|
||||
*/
|
||||
private fun addEntry(index: Int) {
|
||||
entries.add(index)
|
||||
}
|
||||
|
||||
fun entriesCount(): Int {
|
||||
return entries.size
|
||||
}
|
||||
|
||||
private val handle: String?
|
||||
get() = properties[HANDLE_PROP]
|
||||
|
||||
private fun hasEntry(index: Int): Boolean {
|
||||
return entries.contains(index)
|
||||
}
|
||||
|
||||
val isAutoPost: Boolean
|
||||
get() = isEnabled && properties[AUTOPOST_PROP].toBoolean()
|
||||
|
||||
override val isValidProperties: Boolean
|
||||
get() {
|
||||
for (s in propertyKeys) {
|
||||
if (AUTOPOST_PROP != s && HANDLE_PROP != s && properties[s]!!.isBlank()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the registered Twitter handle.
|
||||
*/
|
||||
fun notification(msg: String) {
|
||||
with(bot) {
|
||||
if (isEnabled && StringUtils.isNotBlank(handle)) {
|
||||
Thread {
|
||||
try {
|
||||
post(message = msg, isDm = true)
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Notified @{}: {}", handle, msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn("Failed to notify @{}: {}", handle, msg, e)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts on Twitter.
|
||||
*/
|
||||
@Throws(ModuleException::class)
|
||||
fun post(handle: String = "${properties[HANDLE_PROP]}", message: String, isDm: Boolean): Message {
|
||||
return twitterPost(
|
||||
properties[CONSUMER_KEY_PROP],
|
||||
properties[CONSUMER_SECRET_PROP],
|
||||
properties[TOKEN_PROP],
|
||||
properties[TOKEN_SECRET_PROP],
|
||||
handle,
|
||||
message,
|
||||
isDm
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an entry to twitter.
|
||||
*/
|
||||
fun postEntry(index: Int) {
|
||||
with(bot) {
|
||||
if (isAutoPost && hasEntry(index) && entriesCount >= index) {
|
||||
val entry = getEntry(index)
|
||||
val msg = "${entry.title} ${entry.link} via ${entry.nick} on $channel"
|
||||
Thread {
|
||||
try {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Posting {}{} to Twitter.", Constants.LINK_CMD, index + 1)
|
||||
}
|
||||
post(message = msg, isDm = false)
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn("Failed to post entry on Twitter.", e)
|
||||
}
|
||||
}.start()
|
||||
removeEntry(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun queueEntry(index: Int) {
|
||||
if (isAutoPost) {
|
||||
addEntry(index)
|
||||
if (bot.logger.isDebugEnabled) {
|
||||
bot.logger.debug("Scheduling {}{} for posting on Twitter.", Constants.LINK_CMD, index + 1)
|
||||
}
|
||||
bot.timer.schedule(TwitterTimer(bot, index), Constants.TIMER_DELAY * 60L * 1000L)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeEntry(index: Int) {
|
||||
entries.remove(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts to twitter.
|
||||
*/
|
||||
override fun run(sender: String, cmd: String, args: String, isPrivate: Boolean) {
|
||||
with(bot) {
|
||||
try {
|
||||
send(
|
||||
sender,
|
||||
post(sender, "$args (by $sender on $channel)", false).msg,
|
||||
isPrivate
|
||||
)
|
||||
} catch (e: ModuleException) {
|
||||
logger.warn(e.debugMessage, e)
|
||||
send(sender, e.message, isPrivate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post all the entries to Twitter on shutdown.
|
||||
*/
|
||||
fun shutdown() {
|
||||
if (isAutoPost) {
|
||||
for (index in entries) {
|
||||
postEntry(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Property keys
|
||||
const val AUTOPOST_PROP = "twitter-auto-post"
|
||||
const val CONSUMER_KEY_PROP = "twitter-consumerKey"
|
||||
const val CONSUMER_SECRET_PROP = "twitter-consumerSecret"
|
||||
const val HANDLE_PROP = "twitter-handle"
|
||||
const val TOKEN_PROP = "twitter-token"
|
||||
const val TOKEN_SECRET_PROP = "twitter-tokenSecret"
|
||||
|
||||
// Twitter command
|
||||
private const val TWITTER_CMD = "twitter"
|
||||
|
||||
/**
|
||||
* Posts on Twitter.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun twitterPost(
|
||||
consumerKey: String?,
|
||||
consumerSecret: String?,
|
||||
token: String?,
|
||||
tokenSecret: String?,
|
||||
handle: String?,
|
||||
message: String,
|
||||
isDm: Boolean
|
||||
): Message {
|
||||
return try {
|
||||
val cb = ConfigurationBuilder()
|
||||
cb.setDebugEnabled(true)
|
||||
.setOAuthConsumerKey(consumerKey)
|
||||
.setOAuthConsumerSecret(consumerSecret)
|
||||
.setOAuthAccessToken(token).setOAuthAccessTokenSecret(tokenSecret)
|
||||
val tf = TwitterFactory(cb.build())
|
||||
val twitter = tf.instance
|
||||
if (!isDm) {
|
||||
val status = twitter.updateStatus(message)
|
||||
NoticeMessage(
|
||||
"You message was posted to https://twitter.com/${twitter.screenName}/statuses/${status.id}"
|
||||
)
|
||||
} else {
|
||||
val dm = twitter.sendDirectMessage(handle, message)
|
||||
NoticeMessage(dm.text)
|
||||
}
|
||||
} catch (e: TwitterException) {
|
||||
throw ModuleException("twitterPost($message)", "An error has occurred: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(TWITTER_CMD)
|
||||
help.add("To post to Twitter:")
|
||||
help.add(Utils.helpIndent("%c $TWITTER_CMD <message>"))
|
||||
properties[AUTOPOST_PROP] = "false"
|
||||
initProperties(CONSUMER_KEY_PROP, CONSUMER_SECRET_PROP, HANDLE_PROP, TOKEN_PROP, TOKEN_SECRET_PROP)
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ package net.thauvin.erik.mobibot.modules;
|
|||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
|
@ -71,9 +72,9 @@ public final class War extends AbstractModule {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
public void commandResponse(@NotNull final String sender,
|
||||
@NotNull final String cmd,
|
||||
@NotNull final String args,
|
||||
final boolean isPrivate) {
|
||||
final SecureRandom r = new SecureRandom();
|
||||
|
||||
|
@ -84,20 +85,21 @@ public final class War extends AbstractModule {
|
|||
i = r.nextInt(WAR_DECK.length);
|
||||
y = r.nextInt(WAR_DECK.length);
|
||||
|
||||
bot.send(sender + " drew the " + bold(WAR_DECK[i]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
|
||||
bot.action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
|
||||
getBot().send(sender + " drew the " + bold(WAR_DECK[i]) + " of "
|
||||
+ bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
|
||||
getBot().action("drew the " + bold(WAR_DECK[y]) + " of " + bold(WAR_SUITS[r.nextInt(WAR_SUITS.length)]));
|
||||
|
||||
if (i != y) {
|
||||
break;
|
||||
}
|
||||
|
||||
bot.send("This means " + bold("WAR") + '!');
|
||||
getBot().send("This means " + bold("WAR") + '!');
|
||||
}
|
||||
|
||||
if (i < y) {
|
||||
bot.action("lost.");
|
||||
getBot().action("lost.");
|
||||
} else {
|
||||
bot.action("wins.");
|
||||
getBot().action("wins.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
/*
|
||||
* Weather2.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.modules;
|
||||
|
||||
import net.aksingh.owmjapis.api.APIException;
|
||||
import net.aksingh.owmjapis.core.OWM;
|
||||
import net.aksingh.owmjapis.model.CurrentWeather;
|
||||
import net.aksingh.owmjapis.model.param.Main;
|
||||
import net.aksingh.owmjapis.model.param.Weather;
|
||||
import net.aksingh.owmjapis.model.param.Wind;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.thauvin.erik.mobibot.Utils.bold;
|
||||
|
||||
/**
|
||||
* The <code>Weather2</code> module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2017-04-02
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Weather2 extends ThreadedModule {
|
||||
/**
|
||||
* The OpenWeatherMap API Key property.
|
||||
*/
|
||||
static final String OWM_API_KEY_PROP = "owm-api-key";
|
||||
|
||||
// Weather command
|
||||
private static final String WEATHER_CMD = "weather";
|
||||
|
||||
/**
|
||||
* Creates a new {@link Weather2} instance.
|
||||
*/
|
||||
public Weather2(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
commands.add(WEATHER_CMD);
|
||||
|
||||
help.add("To display weather information:");
|
||||
help.add(Utils.helpIndent("%c " + WEATHER_CMD + " <city> [, <country code>]"));
|
||||
help.add("For example:");
|
||||
help.add(Utils.helpIndent("%c " + WEATHER_CMD + " paris, fr"));
|
||||
help.add("The default ISO 3166 country code is " + bold("US") + ". Zip codes supported in most countries.");
|
||||
|
||||
initProperties(OWM_API_KEY_PROP);
|
||||
}
|
||||
|
||||
private static OWM.Country getCountry(final String countryCode) {
|
||||
for (final OWM.Country c : OWM.Country.values()) {
|
||||
if (c.name().equalsIgnoreCase(countryCode)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return OWM.Country.UNITED_STATES;
|
||||
}
|
||||
|
||||
@SuppressWarnings("AvoidEscapedUnicodeCharacters")
|
||||
private static String getTemps(final Double d) {
|
||||
final double c = (d - 32) * 5 / 9;
|
||||
return Math.round(d) + " °F, " + Math.round(c) + " °C";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the weather data.
|
||||
*
|
||||
* <ul>
|
||||
* <li>98204</li>
|
||||
* <li>London, UK</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param query The query.
|
||||
* @param apiKey The API key.
|
||||
* @return The {@link Message} array containing the weather data.
|
||||
* @throws ModuleException If an error occurs while retrieving the weather data.
|
||||
*/
|
||||
static List<Message> getWeather(final String query, final String apiKey) throws ModuleException {
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
throw new ModuleException(StringUtils.capitalize(WEATHER_CMD) + " is disabled. The API key is missing.");
|
||||
}
|
||||
|
||||
final OWM owm = new OWM(apiKey);
|
||||
final ArrayList<Message> messages = new ArrayList<>();
|
||||
|
||||
owm.setUnit(OWM.Unit.IMPERIAL);
|
||||
|
||||
if (StringUtils.isNotBlank(query)) {
|
||||
final String[] argv = query.split(",");
|
||||
|
||||
if (argv.length >= 1 && argv.length <= 2) {
|
||||
final String country;
|
||||
final String city = argv[0].trim();
|
||||
if (argv.length > 1 && StringUtils.isNotBlank(argv[1])) {
|
||||
country = argv[1].trim();
|
||||
} else {
|
||||
country = "US";
|
||||
}
|
||||
|
||||
try {
|
||||
final CurrentWeather cwd;
|
||||
if (city.matches("\\d+")) {
|
||||
cwd = owm.currentWeatherByZipCode(Integer.parseInt(city), getCountry(country));
|
||||
} else {
|
||||
cwd = owm.currentWeatherByCityName(city, getCountry(country));
|
||||
}
|
||||
if (cwd.hasCityName()) {
|
||||
messages.add(new PublicMessage(
|
||||
"City: " + cwd.getCityName() + " [" + StringUtils.upperCase(country) + "]"));
|
||||
|
||||
final Main main = cwd.getMainData();
|
||||
if (main != null) {
|
||||
if (main.hasTemp()) {
|
||||
messages.add(new PublicMessage("Temperature: " + getTemps(main.getTemp())));
|
||||
}
|
||||
|
||||
if (main.hasHumidity() && (main.getHumidity() != null)) {
|
||||
messages.add(new NoticeMessage("Humidity: " + Math.round(main.getHumidity()) + "%"));
|
||||
}
|
||||
}
|
||||
|
||||
if (cwd.hasWindData()) {
|
||||
final Wind w = cwd.getWindData();
|
||||
if (w != null && w.hasSpeed()) {
|
||||
messages.add(new NoticeMessage("Wind: " + wind(w.getSpeed())));
|
||||
}
|
||||
}
|
||||
|
||||
if (cwd.hasWeatherList()) {
|
||||
final StringBuilder condition = new StringBuilder("Condition:");
|
||||
final List<Weather> list = cwd.getWeatherList();
|
||||
if (list != null) {
|
||||
for (final Weather w : list) {
|
||||
condition.append(' ')
|
||||
.append(StringUtils.capitalize(w.getDescription()))
|
||||
.append('.');
|
||||
}
|
||||
messages.add(new NoticeMessage(condition.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (cwd.hasCityId() && cwd.getCityId() != null) {
|
||||
if (cwd.getCityId() > 0) {
|
||||
messages.add(new NoticeMessage("https://openweathermap.org/city/" + cwd.getCityId(),
|
||||
Colors.GREEN));
|
||||
} else {
|
||||
messages.add(new NoticeMessage("https://openweathermap.org/find?q=" + Utils.encodeUrl(
|
||||
city + ',' + StringUtils.upperCase(country)), Colors.GREEN));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (APIException | NullPointerException e) {
|
||||
throw new ModuleException("getWeather(" + query + ')', "Unable to perform weather lookup.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
messages.add(new ErrorMessage("Invalid syntax."));
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private static String wind(final Double w) {
|
||||
final double kmh = w * 1.60934;
|
||||
return Math.round(w) + " mph, " + Math.round(kmh) + " km/h";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the weather data from a specific city.
|
||||
*/
|
||||
@Override
|
||||
void run(final String sender, final String cmd, final String args, final boolean isPrivate) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
try {
|
||||
final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP));
|
||||
if (messages.get(0).isError()) {
|
||||
helpResponse(sender, isPrivate);
|
||||
} else {
|
||||
for (final Message msg : messages) {
|
||||
bot.send(sender, msg);
|
||||
}
|
||||
}
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().debug(e.getDebugMessage(), e);
|
||||
bot.send(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate);
|
||||
}
|
||||
}
|
||||
}
|
200
src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt
Normal file
200
src/main/java/net/thauvin/erik/mobibot/modules/Weather2.kt
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Weather2.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.modules
|
||||
|
||||
import net.aksingh.owmjapis.api.APIException
|
||||
import net.aksingh.owmjapis.core.OWM
|
||||
import net.aksingh.owmjapis.core.OWM.Country
|
||||
import net.aksingh.owmjapis.model.CurrentWeather
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
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
|
||||
|
||||
/**
|
||||
* The `Weather2` module.
|
||||
*/
|
||||
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)) {
|
||||
try {
|
||||
val messages = getWeather(args, properties[OWM_API_KEY_PROP])
|
||||
if (messages[0].isError) {
|
||||
helpResponse(sender, isPrivate)
|
||||
} else {
|
||||
for (msg in messages) {
|
||||
bot.send(sender, msg)
|
||||
}
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
bot.logger.debug(e.debugMessage, e)
|
||||
bot.send(e.message)
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender, isPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The OpenWeatherMap API Key property.
|
||||
*/
|
||||
const val OWM_API_KEY_PROP = "owm-api-key"
|
||||
|
||||
// Weather command
|
||||
private const val WEATHER_CMD = "weather"
|
||||
private fun getCountry(countryCode: String): Country {
|
||||
for (c in Country.values()) {
|
||||
if (c.name.equals(countryCode, ignoreCase = true)) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return Country.UNITED_STATES
|
||||
}
|
||||
|
||||
private fun getTemps(d: Double?): String {
|
||||
val c = (d!! - 32) * 5 / 9
|
||||
return "${d.roundToInt()} °F, ${c.roundToInt()} °C"
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the weather data.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun getWeather(query: String, apiKey: String?): List<Message> {
|
||||
if (StringUtils.isBlank(apiKey)) {
|
||||
throw ModuleException("${WEATHER_CMD.capitalize()} is disabled. The API key is missing.")
|
||||
}
|
||||
val owm = OWM(apiKey!!)
|
||||
val messages = ArrayList<Message>()
|
||||
owm.unit = OWM.Unit.IMPERIAL
|
||||
if (StringUtils.isNotBlank(query)) {
|
||||
val argv = query.split(",").toTypedArray()
|
||||
if (argv.size in 1..2) {
|
||||
val city = argv[0].trim()
|
||||
val country: String = if (argv.size > 1 && StringUtils.isNotBlank(argv[1])) {
|
||||
argv[1].trim()
|
||||
} else {
|
||||
"US"
|
||||
}
|
||||
try {
|
||||
val cwd: CurrentWeather = if (city.matches("\\d+".toRegex())) {
|
||||
owm.currentWeatherByZipCode(city.toInt(), getCountry(country))
|
||||
} else {
|
||||
owm.currentWeatherByCityName(city, getCountry(country))
|
||||
}
|
||||
if (cwd.hasCityName()) {
|
||||
messages.add(
|
||||
PublicMessage("City: ${cwd.cityName} [${country.toUpperCase()}]")
|
||||
)
|
||||
with(cwd.mainData) {
|
||||
if (this != null) {
|
||||
if (hasTemp()) {
|
||||
messages.add(PublicMessage("Temperature: ${getTemps(temp)}"))
|
||||
}
|
||||
if (hasHumidity() && humidity != null) {
|
||||
messages.add(NoticeMessage("Humidity: ${(humidity!!).roundToInt()}%"))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cwd.hasWindData()) {
|
||||
with(cwd.windData) {
|
||||
if (this != null && hasSpeed()) {
|
||||
messages.add(NoticeMessage("Wind: ${wind(speed)}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cwd.hasWeatherList()) {
|
||||
val condition = StringBuilder("Condition:")
|
||||
val list = cwd.weatherList
|
||||
if (list != null) {
|
||||
for (w in list) {
|
||||
condition.append(' ').append(w!!.getDescription().capitalize()).append('.')
|
||||
}
|
||||
messages.add(NoticeMessage(condition.toString()))
|
||||
}
|
||||
}
|
||||
if (cwd.hasCityId() && cwd.cityId != null) {
|
||||
if (cwd.cityId!! > 0) {
|
||||
messages.add(
|
||||
NoticeMessage("https://openweathermap.org/city/${cwd.cityId}", Colors.GREEN)
|
||||
)
|
||||
} else {
|
||||
messages.add(
|
||||
NoticeMessage(
|
||||
"https://openweathermap.org/find?q="
|
||||
+ Utils.encodeUrl("$city,${country.toUpperCase()}"),
|
||||
Colors.GREEN
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: APIException) {
|
||||
throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e)
|
||||
} catch (e: NullPointerException) {
|
||||
throw ModuleException("getWeather($query)", "Unable to perform weather lookup.", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (messages.isEmpty()) {
|
||||
messages.add(ErrorMessage("Invalid syntax."))
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
private fun wind(w: Double?): String {
|
||||
val kmh = w!! * 1.60934
|
||||
return "${Math.round(w)} mph, ${kmh.roundToInt()} km/h"
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(WEATHER_CMD)
|
||||
help.add("To display weather information:")
|
||||
help.add(Utils.helpIndent("%c $WEATHER_CMD <city> [, <country code>]"))
|
||||
help.add("For example:")
|
||||
help.add(Utils.helpIndent("%c $WEATHER_CMD paris, fr"))
|
||||
help.add("The default ISO 3166 country code is ${Utils.bold("US")}. Zip codes supported in most countries.")
|
||||
initProperties(OWM_API_KEY_PROP)
|
||||
}
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* WorldTime.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* The WorldTime module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-27
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
public final class WorldTime extends AbstractModule {
|
||||
// Beats (Internet Time) keyword
|
||||
private static final String BEATS_KEYWORD = ".beats";
|
||||
// Supported countries
|
||||
private static final Map<String, String> COUNTRIES_MAP;
|
||||
|
||||
// The Time command
|
||||
private static final String TIME_CMD = "time";
|
||||
|
||||
static {
|
||||
// Initialize the countries map
|
||||
final Map<String, String> countries = new TreeMap<>();
|
||||
countries.put("AE", "Asia/Dubai");
|
||||
countries.put("AF", "Asia/Kabul");
|
||||
countries.put("AQ", "Antarctica/South_Pole");
|
||||
countries.put("AT", "Europe/Vienna");
|
||||
countries.put("AU", "Australia/Sydney");
|
||||
countries.put("AKST", "America/Anchorage");
|
||||
countries.put("AKDT", "America/Anchorage");
|
||||
countries.put("BE", "Europe/Brussels");
|
||||
countries.put("BR", "America/Sao_Paulo");
|
||||
countries.put("CA", "America/Montreal");
|
||||
countries.put("CDT", "America/Chicago");
|
||||
countries.put("CET", "CET");
|
||||
countries.put("CH", "Europe/Zurich");
|
||||
countries.put("CN", "Asia/Shanghai");
|
||||
countries.put("CST", "America/Chicago");
|
||||
countries.put("CU", "Cuba");
|
||||
countries.put("DE", "Europe/Berlin");
|
||||
countries.put("DK", "Europe/Copenhagen");
|
||||
countries.put("EDT", "America/New_York");
|
||||
countries.put("EG", "Africa/Cairo");
|
||||
countries.put("ER", "Africa/Asmara");
|
||||
countries.put("ES", "Europe/Madrid");
|
||||
countries.put("EST", "America/New_York");
|
||||
countries.put("FI", "Europe/Helsinki");
|
||||
countries.put("FR", "Europe/Paris");
|
||||
countries.put("GB", "Europe/London");
|
||||
countries.put("GMT", "GMT");
|
||||
countries.put("GR", "Europe/Athens");
|
||||
countries.put("HK", "Asia/Hong_Kong");
|
||||
countries.put("HST", "Pacific/Honolulu");
|
||||
countries.put("IE", "Europe/Dublin");
|
||||
countries.put("IL", "Asia/Tel_Aviv");
|
||||
countries.put("IN", "Asia/Kolkata");
|
||||
countries.put("IQ", "Asia/Baghdad");
|
||||
countries.put("IR", "Asia/Tehran");
|
||||
countries.put("IS", "Atlantic/Reykjavik");
|
||||
countries.put("IT", "Europe/Rome");
|
||||
countries.put("JM", "Jamaica");
|
||||
countries.put("JP", "Asia/Tokyo");
|
||||
countries.put("LY", "Africa/Tripoli");
|
||||
countries.put("MA", "Africa/Casablanca");
|
||||
countries.put("MDT", "America/Denver");
|
||||
countries.put("MH", "Kwajalein");
|
||||
countries.put("MQ", "America/Martinique");
|
||||
countries.put("MST", "America/Denver");
|
||||
countries.put("MX", "America/Mexico_City");
|
||||
countries.put("NL", "Europe/Amsterdam");
|
||||
countries.put("NO", "Europe/Oslo");
|
||||
countries.put("NP", "Asia/Katmandu");
|
||||
countries.put("NZ", "Pacific/Auckland");
|
||||
countries.put("PDT", "America/Los_Angeles");
|
||||
countries.put("PH", "Asia/Manila");
|
||||
countries.put("PK", "Asia/Karachi");
|
||||
countries.put("PL", "Europe/Warsaw");
|
||||
countries.put("PST", "America/Los_Angeles");
|
||||
countries.put("PT", "Europe/Lisbon");
|
||||
countries.put("PR", "America/Puerto_Rico");
|
||||
countries.put("RU", "Europe/Moscow");
|
||||
countries.put("SE", "Europe/Stockholm");
|
||||
countries.put("SG", "Asia/Singapore");
|
||||
countries.put("TH", "Asia/Bangkok");
|
||||
countries.put("TM", "Asia/Ashgabat");
|
||||
countries.put("TN", "Africa/Tunis");
|
||||
countries.put("TR", "Europe/Istanbul");
|
||||
countries.put("TW", "Asia/Taipei");
|
||||
countries.put("UK", "Europe/London");
|
||||
countries.put("US", "America/New_York");
|
||||
countries.put("UTC", "UTC");
|
||||
countries.put("VA", "Europe/Vatican");
|
||||
countries.put("VE", "America/Caracas");
|
||||
countries.put("VN", "Asia/Ho_Chi_Minh");
|
||||
countries.put("ZA", "Africa/Johannesburg");
|
||||
countries.put("ZULU", "Zulu");
|
||||
countries.put("INTERNET", BEATS_KEYWORD);
|
||||
countries.put("BEATS", BEATS_KEYWORD);
|
||||
|
||||
ZoneId.getAvailableZoneIds().stream()
|
||||
.filter(tz -> !tz.contains("/") && tz.length() == 3 && !countries.containsKey(tz))
|
||||
.forEach(tz -> countries.put(tz, tz));
|
||||
|
||||
COUNTRIES_MAP = Collections.unmodifiableMap(countries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link WorldTime} instance.
|
||||
*/
|
||||
public WorldTime(final Mobibot bot) {
|
||||
super(bot);
|
||||
|
||||
help.add("To display a country's current date/time:");
|
||||
help.add(Utils.helpIndent("%c " + TIME_CMD) + " [<country code>]");
|
||||
help.add("For a listing of the supported countries:");
|
||||
help.add(Utils.helpIndent("%c " + TIME_CMD));
|
||||
|
||||
commands.add(TIME_CMD);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current Internet (beat) Time.
|
||||
*
|
||||
* @return The Internet Time string.
|
||||
*/
|
||||
private static String internetTime() {
|
||||
final ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"));
|
||||
final int beats = (int) ((zdt.get(ChronoField.SECOND_OF_MINUTE) + (zdt.get(ChronoField.MINUTE_OF_HOUR) * 60)
|
||||
+ (zdt.get(ChronoField.HOUR_OF_DAY) * 3600)) / 86.4);
|
||||
return String.format("%c%03d", '@', beats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world time.
|
||||
*
|
||||
* <ul>
|
||||
* <li>PST</li>
|
||||
* <li>BEATS</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param query The query.
|
||||
* @return The {@link Message} containing the world time.
|
||||
*/
|
||||
@SuppressFBWarnings("STT_STRING_PARSING_A_FIELD")
|
||||
static Message worldTime(final String query) {
|
||||
final String tz = (COUNTRIES_MAP.get((StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim()))));
|
||||
final String response;
|
||||
|
||||
if (tz != null) {
|
||||
if (BEATS_KEYWORD.equals(tz)) {
|
||||
response = ("The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD));
|
||||
} else {
|
||||
response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(
|
||||
DateTimeFormatter
|
||||
.ofPattern("'The time is " + Utils.bold("'HH:mm'") + " on " + Utils.bold(
|
||||
"'EEEE, d MMMM yyyy'") + " in '"))
|
||||
+ Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '));
|
||||
}
|
||||
} else {
|
||||
return new ErrorMessage("Unsupported country/zone. Please try again.");
|
||||
}
|
||||
|
||||
return new PublicMessage(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (args.length() == 0) {
|
||||
bot.send(sender, "The supported countries/zones are: ", isPrivate);
|
||||
bot.sendList(sender, new ArrayList<>(COUNTRIES_MAP.keySet()), 17, false, false);
|
||||
} else {
|
||||
final Message msg = worldTime(args);
|
||||
if (isPrivate) {
|
||||
bot.send(sender, msg.getMsg(), true);
|
||||
} else {
|
||||
if (msg.isError()) {
|
||||
bot.send(sender, msg.getMsg(), false);
|
||||
} else {
|
||||
bot.send(msg.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPrivateMsgEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
219
src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt
Normal file
219
src/main/java/net/thauvin/erik/mobibot/modules/WorldTime.kt
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* WorldTime.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoField
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The WorldTime module.
|
||||
*/
|
||||
class WorldTime(bot: Mobibot) : AbstractModule(bot) {
|
||||
companion object {
|
||||
// Beats (Internet Time) keyword
|
||||
private const val BEATS_KEYWORD = ".beats"
|
||||
|
||||
// Supported countries
|
||||
private var COUNTRIES_MAP: Map<String, String>
|
||||
|
||||
// The Time command
|
||||
private const val TIME_CMD = "time"
|
||||
|
||||
/**
|
||||
* Returns the current Internet (beat) Time.
|
||||
*/
|
||||
private fun internetTime(): String {
|
||||
val zdt = ZonedDateTime.now(ZoneId.of("UTC+01:00"))
|
||||
val beats = ((zdt[ChronoField.SECOND_OF_MINUTE] + zdt[ChronoField.MINUTE_OF_HOUR] * 60
|
||||
+ zdt[ChronoField.HOUR_OF_DAY] * 3600) / 86.4).toInt()
|
||||
return String.format("%c%03d", '@', beats)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world time.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun worldTime(query: String): Message {
|
||||
val tz = COUNTRIES_MAP[StringUtils.upperCase(query.substring(query.indexOf(' ') + 1).trim())]
|
||||
val response: String = if (tz != null) {
|
||||
if (BEATS_KEYWORD == tz) {
|
||||
"The current Internet Time is: " + Utils.bold(internetTime() + ' ' + BEATS_KEYWORD)
|
||||
} else {
|
||||
(ZonedDateTime.now()
|
||||
.withZoneSameInstant(ZoneId.of(tz))
|
||||
.format(
|
||||
DateTimeFormatter.ofPattern(
|
||||
"'The time is ${Utils.bold("'HH:mm'")} on ${Utils.bold("'EEEE, d MMMM yyyy'")} in '"
|
||||
)
|
||||
)
|
||||
+ Utils.bold(tz.substring(tz.indexOf('/') + 1).replace('_', ' '))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return ErrorMessage("Unsupported country/zone. Please try again.")
|
||||
}
|
||||
return PublicMessage(response)
|
||||
}
|
||||
|
||||
init {
|
||||
// Initialize the countries map
|
||||
val countries = TreeMap<String, String>()
|
||||
countries["AE"] = "Asia/Dubai"
|
||||
countries["AF"] = "Asia/Kabul"
|
||||
countries["AQ"] = "Antarctica/South_Pole"
|
||||
countries["AT"] = "Europe/Vienna"
|
||||
countries["AU"] = "Australia/Sydney"
|
||||
countries["AKST"] = "America/Anchorage"
|
||||
countries["AKDT"] = "America/Anchorage"
|
||||
countries["BE"] = "Europe/Brussels"
|
||||
countries["BR"] = "America/Sao_Paulo"
|
||||
countries["CA"] = "America/Montreal"
|
||||
countries["CDT"] = "America/Chicago"
|
||||
countries["CET"] = "CET"
|
||||
countries["CH"] = "Europe/Zurich"
|
||||
countries["CN"] = "Asia/Shanghai"
|
||||
countries["CST"] = "America/Chicago"
|
||||
countries["CU"] = "Cuba"
|
||||
countries["DE"] = "Europe/Berlin"
|
||||
countries["DK"] = "Europe/Copenhagen"
|
||||
countries["EDT"] = "America/New_York"
|
||||
countries["EG"] = "Africa/Cairo"
|
||||
countries["ER"] = "Africa/Asmara"
|
||||
countries["ES"] = "Europe/Madrid"
|
||||
countries["EST"] = "America/New_York"
|
||||
countries["FI"] = "Europe/Helsinki"
|
||||
countries["FR"] = "Europe/Paris"
|
||||
countries["GB"] = "Europe/London"
|
||||
countries["GMT"] = "GMT"
|
||||
countries["GR"] = "Europe/Athens"
|
||||
countries["HK"] = "Asia/Hong_Kong"
|
||||
countries["HST"] = "Pacific/Honolulu"
|
||||
countries["IE"] = "Europe/Dublin"
|
||||
countries["IL"] = "Asia/Tel_Aviv"
|
||||
countries["IN"] = "Asia/Kolkata"
|
||||
countries["IQ"] = "Asia/Baghdad"
|
||||
countries["IR"] = "Asia/Tehran"
|
||||
countries["IS"] = "Atlantic/Reykjavik"
|
||||
countries["IT"] = "Europe/Rome"
|
||||
countries["JM"] = "Jamaica"
|
||||
countries["JP"] = "Asia/Tokyo"
|
||||
countries["LY"] = "Africa/Tripoli"
|
||||
countries["MA"] = "Africa/Casablanca"
|
||||
countries["MDT"] = "America/Denver"
|
||||
countries["MH"] = "Kwajalein"
|
||||
countries["MQ"] = "America/Martinique"
|
||||
countries["MST"] = "America/Denver"
|
||||
countries["MX"] = "America/Mexico_City"
|
||||
countries["NL"] = "Europe/Amsterdam"
|
||||
countries["NO"] = "Europe/Oslo"
|
||||
countries["NP"] = "Asia/Katmandu"
|
||||
countries["NZ"] = "Pacific/Auckland"
|
||||
countries["PDT"] = "America/Los_Angeles"
|
||||
countries["PH"] = "Asia/Manila"
|
||||
countries["PK"] = "Asia/Karachi"
|
||||
countries["PL"] = "Europe/Warsaw"
|
||||
countries["PST"] = "America/Los_Angeles"
|
||||
countries["PT"] = "Europe/Lisbon"
|
||||
countries["PR"] = "America/Puerto_Rico"
|
||||
countries["RU"] = "Europe/Moscow"
|
||||
countries["SE"] = "Europe/Stockholm"
|
||||
countries["SG"] = "Asia/Singapore"
|
||||
countries["TH"] = "Asia/Bangkok"
|
||||
countries["TM"] = "Asia/Ashgabat"
|
||||
countries["TN"] = "Africa/Tunis"
|
||||
countries["TR"] = "Europe/Istanbul"
|
||||
countries["TW"] = "Asia/Taipei"
|
||||
countries["UK"] = "Europe/London"
|
||||
countries["US"] = "America/New_York"
|
||||
countries["UTC"] = "UTC"
|
||||
countries["VA"] = "Europe/Vatican"
|
||||
countries["VE"] = "America/Caracas"
|
||||
countries["VN"] = "Asia/Ho_Chi_Minh"
|
||||
countries["ZA"] = "Africa/Johannesburg"
|
||||
countries["ZULU"] = "Zulu"
|
||||
countries["INTERNET"] = BEATS_KEYWORD
|
||||
countries["BEATS"] = BEATS_KEYWORD
|
||||
ZoneId.getAvailableZoneIds().stream()
|
||||
.filter { tz: String ->
|
||||
!tz.contains("/") && tz.length == 3 && !countries.containsKey(tz)
|
||||
}
|
||||
.forEach { tz: String ->
|
||||
countries[tz] = tz
|
||||
}
|
||||
COUNTRIES_MAP = Collections.unmodifiableMap(countries)
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(
|
||||
sender: String,
|
||||
cmd: String,
|
||||
args: String,
|
||||
isPrivate: Boolean
|
||||
) {
|
||||
with(bot) {
|
||||
if (args.isEmpty()) {
|
||||
send(sender, "The supported countries/zones are: ", isPrivate)
|
||||
sendList(sender, ArrayList(COUNTRIES_MAP.keys), 17, false, false)
|
||||
} else {
|
||||
val msg = worldTime(args)
|
||||
if (isPrivate) {
|
||||
send(sender, msg.msg, true)
|
||||
} else {
|
||||
if (msg.isError) {
|
||||
send(sender, msg.msg, false)
|
||||
} else {
|
||||
send(msg.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val isPrivateMsgEnabled = true
|
||||
|
||||
init {
|
||||
help.add("To display a country's current date/time:")
|
||||
help.add(Utils.helpIndent("%c $TIME_CMD") + " [<country code>]")
|
||||
help.add("For a listing of the supported countries:")
|
||||
help.add(Utils.helpIndent("%c $TIME_CMD"))
|
||||
commands.add(TIME_CMD)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ErrorMessage.java
|
||||
* ErrorMessage.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
|
|||
|
||||
/**
|
||||
* The `ErrorMessage` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
class ErrorMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
|
||||
init {
|
||||
|
|
|
@ -35,10 +35,6 @@ import org.jibble.pircbot.Colors
|
|||
|
||||
/**
|
||||
* The `Message` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
open class Message {
|
||||
companion object {
|
||||
|
@ -67,12 +63,6 @@ open class Message {
|
|||
|
||||
/**
|
||||
* Creates a new message.
|
||||
*
|
||||
* @param msg The message.
|
||||
* @param color The color.
|
||||
* @param isNotice The notice flag.
|
||||
* @param isError The error flag.
|
||||
* @param isPrivate The private flag.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(msg: String, color: String = DEFAULT_COLOR, isNotice: Boolean, isError: Boolean, isPrivate: Boolean) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* NoticeMessage.java
|
||||
* NoticeMessage.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
|
|||
|
||||
/**
|
||||
* The `NoticeMessage` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
class NoticeMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
|
||||
init {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* PrivateMessage.java
|
||||
* PrivateMessage.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
|
|||
|
||||
/**
|
||||
* The `PrivateMessage` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-09
|
||||
* @since 1.0
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class PrivateMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* PublicMessage.java
|
||||
* PublicMessage.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -33,10 +33,6 @@ package net.thauvin.erik.mobibot.msg
|
|||
|
||||
/**
|
||||
* The `PublicMessage` class.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net/)
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
class PublicMessage @JvmOverloads constructor(msg: String, color: String = DEFAULT_COLOR) : Message() {
|
||||
init {
|
||||
|
|
|
@ -66,7 +66,7 @@ public class UtilsTest {
|
|||
|
||||
@Test
|
||||
public void testBold() {
|
||||
assertThat(Utils.bold(1)).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD);
|
||||
assertThat(Utils.bold(Integer.toString(1))).as("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD);
|
||||
assertThat(Utils.bold(ASCII)).as("bold(ascii").isEqualTo(Colors.BOLD + ASCII + Colors.BOLD);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* CalcTest.java
|
||||
* CalcTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,32 +29,26 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.modules.Calc.Companion.calc
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The <code>CalcTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
* The `CalcTest` class.
|
||||
*/
|
||||
public class CalcTest {
|
||||
class CalcTest {
|
||||
@Test
|
||||
public void testCalc() {
|
||||
assertThat(Calc.calc("1 + 1")).as("calc(1+1)").isEqualTo("1+1 = %s", Utils.bold(2));
|
||||
assertThat(Calc.calc("1 -3")).as("calc(1 -3)").isEqualTo("1-3 = %s", Utils.bold(-2));
|
||||
assertThat(Calc.calc("pi+π+e+φ")).as("calc(pi+π+e+φ)").isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62"));
|
||||
assertThat(Calc.calc("one + one")).as("calc(one + one)").startsWith("No idea.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalcImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Calc(null));
|
||||
fun testCalc() {
|
||||
Assertions.assertThat(calc("1 + 1")).`as`("calc(1+1)")
|
||||
.isEqualTo("1+1 = %s", Utils.bold(2))
|
||||
Assertions.assertThat(calc("1 -3")).`as`("calc(1 -3)")
|
||||
.isEqualTo("1-3 = %s", Utils.bold(-2))
|
||||
Assertions.assertThat(calc("pi+π+e+φ")).`as`("calc(pi+π+e+φ)")
|
||||
.isEqualTo("pi+π+e+φ = %s", Utils.bold("10.62"))
|
||||
Assertions.assertThat(calc("one + one")).`as`("calc(one + one)")
|
||||
.startsWith("No idea.")
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* CurrencyConverterTest.java
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The <code>CurrencyConvertTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
public class CurrencyConverterTest {
|
||||
@BeforeClass
|
||||
public void before() throws ModuleException {
|
||||
CurrencyConverter.loadRates();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS")
|
||||
public void testConvertCurrency() {
|
||||
assertThat(CurrencyConverter.convertCurrency("100 USD to EUR").getMsg())
|
||||
.as("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR");
|
||||
assertThat(CurrencyConverter.convertCurrency("100 USD to USD").getMsg())
|
||||
.as("100 USD to USD").contains("You're kidding, right?");
|
||||
assertThat(CurrencyConverter.convertCurrency("100 USD").getMsg())
|
||||
.as("100 USD").contains("Invalid query.");
|
||||
assertThat(CurrencyConverter.currencyRates().size())
|
||||
.as("currencyRates().size() == 33").isEqualTo(33);
|
||||
assertThat(CurrencyConverter.currencyRates())
|
||||
.as("currencyRates().get(EUR)").contains(" EUR: 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrencyConvertererImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new CurrencyConverter(null));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* LookupTest.java
|
||||
* CurrencyConverterTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,40 +29,36 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.BeforeClass
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The <code>Lookup Test</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2017-05-30
|
||||
* @since 1.0
|
||||
* The `CurrencyConvertTest` class.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
|
||||
public class LookupTest {
|
||||
@Test
|
||||
public void testLookupImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Lookup(null));
|
||||
class CurrencyConverterTest {
|
||||
@BeforeClass
|
||||
@Throws(ModuleException::class)
|
||||
fun before() {
|
||||
loadRates()
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookup() throws Exception {
|
||||
final String result = Lookup.lookup("erik.thauvin.net");
|
||||
assertThat(result).as("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhois() throws Exception {
|
||||
final String[] result = Lookup.whois("17.178.96.59", Lookup.WHOIS_HOST);
|
||||
|
||||
assertThat(Arrays.stream(result).anyMatch(m -> m.contains("Apple Inc.")))
|
||||
.as("whois(17.178.96.59/Apple Inc.").isTrue();
|
||||
fun testConvertCurrency() {
|
||||
Assertions.assertThat(convertCurrency("100 USD to EUR").msg)
|
||||
.`as`("100 USD to EUR").matches("100\\.00 USD = \\d{2,3}\\.\\d{2} EUR")
|
||||
Assertions.assertThat(convertCurrency("100 USD to USD").msg)
|
||||
.`as`("100 USD to USD").contains("You're kidding, right?")
|
||||
Assertions.assertThat(convertCurrency("100 USD").msg)
|
||||
.`as`("100 USD").contains("Invalid query.")
|
||||
Assertions.assertThat(currencyRates().size)
|
||||
.`as`("currencyRates().size() == 33").isEqualTo(33)
|
||||
Assertions.assertThat(currencyRates())
|
||||
.`as`("currencyRates().get(EUR)").contains(" EUR: 1")
|
||||
}
|
||||
}
|
52
src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt
Normal file
52
src/test/java/net/thauvin/erik/mobibot/modules/DiceTest.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* DiceTest.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.modules
|
||||
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
class DiceTest {
|
||||
@Test
|
||||
fun testWinLoseOrTie() {
|
||||
assertThat(
|
||||
Dice.winLoseOrTie(6, 6)
|
||||
).`as`("6 vs. 6").isEqualTo(Dice.Result.TIE)
|
||||
assertThat(
|
||||
Dice.winLoseOrTie(6, 5)
|
||||
).`as`("6 vs. 5").isEqualTo(Dice.Result.WIN)
|
||||
assertThat(
|
||||
Dice.winLoseOrTie(5, 6)
|
||||
).`as`("5 vs. 6").isEqualTo(Dice.Result.LOSE)
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* GoogleSearchTest.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.LocalProperties;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* The <code>GoogleSearchTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-08
|
||||
* @since 1.0
|
||||
*/
|
||||
public class GoogleSearchTest extends LocalProperties {
|
||||
@Test
|
||||
public void testGoogleSearchImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new GoogleSearch(null));
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE")
|
||||
@SuppressWarnings("PMD.PreserveStackTrace")
|
||||
@Test
|
||||
public void testSearchGoogle() throws ModuleException {
|
||||
final String apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP);
|
||||
final String cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP);
|
||||
try {
|
||||
List<Message> messages = GoogleSearch.searchGoogle("mobibot site:github.com", apiKey, cseKey);
|
||||
assertThat(messages).as("mobibot results not empty").isNotEmpty();
|
||||
assertThat(messages.get(0).getMsg()).as("found mobitopia").contains("mobibot");
|
||||
|
||||
messages = GoogleSearch.searchGoogle("aapl", apiKey, cseKey);
|
||||
assertThat(messages).as("aapl results not empty").isNotEmpty();
|
||||
assertThat(messages.get(0).getMsg()).as("found apple").containsIgnoringCase("apple");
|
||||
|
||||
assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "", "apiKey")).as("no API key").isInstanceOf(
|
||||
ModuleException.class).hasNoCause();
|
||||
|
||||
assertThatThrownBy(() -> GoogleSearch.searchGoogle("test", "apiKey", "")).as("no CSE API key").isInstanceOf(
|
||||
ModuleException.class).hasNoCause();
|
||||
|
||||
assertThatThrownBy(() -> GoogleSearch.searchGoogle("", "apikey", "apiKey")).as("no query").isInstanceOf(
|
||||
ModuleException.class).hasNoCause();
|
||||
} catch (ModuleException e) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true".equals(System.getenv("CI"))) {
|
||||
throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey, cseKey));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* GoogleSearchTest.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.GoogleSearch.Companion.searchGoogle
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The `GoogleSearchTest` class.
|
||||
*/
|
||||
class GoogleSearchTest : LocalProperties() {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testSearchGoogle() {
|
||||
val apiKey = getProperty(GoogleSearch.GOOGLE_API_KEY_PROP)
|
||||
val cseKey = getProperty(GoogleSearch.GOOGLE_CSE_KEY_PROP)
|
||||
try {
|
||||
var messages = searchGoogle("mobibot site:github.com", apiKey, cseKey)
|
||||
Assertions.assertThat(messages).`as`("mobibot results not empty").isNotEmpty
|
||||
Assertions.assertThat(messages[0].msg).`as`("found mobitopia").contains("mobibot")
|
||||
messages = searchGoogle("aapl", apiKey, cseKey)
|
||||
Assertions.assertThat(messages).`as`("aapl results not empty").isNotEmpty
|
||||
Assertions.assertThat(messages[0].msg).`as`("found apple").containsIgnoringCase("apple")
|
||||
Assertions.assertThatThrownBy { searchGoogle("test", "", "apiKey") }
|
||||
.`as`("no API key")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
Assertions.assertThatThrownBy { searchGoogle("test", "apiKey", "") }
|
||||
.`as`("no CSE API key")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
Assertions.assertThatThrownBy { searchGoogle("", "apikey", "apiKey") }
|
||||
.`as`("no query").isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey, cseKey))
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* JokeTest.java
|
||||
* JokeTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,29 +29,20 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.modules.Joke.Companion.randomJoke
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The <code>JokeTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
* The `JokeTest` class.
|
||||
*/
|
||||
public class JokeTest {
|
||||
class JokeTest {
|
||||
@Test
|
||||
public void testJokeImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Joke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRamdomJoke() throws ModuleException {
|
||||
assertThat(Joke.randomJoke().getMsg().length() > 0).as("randomJoke() > 0").isTrue();
|
||||
assertThat(Joke.randomJoke().getMsg()).as("randomJoke()").containsIgnoringCase("chuck");
|
||||
@Throws(ModuleException::class)
|
||||
fun testRamdomJoke() {
|
||||
Assertions.assertThat(randomJoke().msg.isNotEmpty()).`as`("randomJoke() > 0").isTrue
|
||||
Assertions.assertThat(randomJoke().msg).`as`("randomJoke()").containsIgnoringCase("chuck")
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* WordTimeTest.java
|
||||
* LookupTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,31 +29,30 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.lookup
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The <code>WordTimeTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
* The `Lookup Test` class.
|
||||
*/
|
||||
public class WordTimeTest {
|
||||
class LookupTest {
|
||||
@Test
|
||||
public void testWorldTime() {
|
||||
assertThat(WorldTime.worldTime("PST").getMsg()).as("PST").endsWith(Utils.bold("Los Angeles"));
|
||||
assertThat(WorldTime.worldTime("BLAH").isError()).as("BLAH").isTrue();
|
||||
assertThat(WorldTime.worldTime("BEATS").getMsg()).as("BEATS").contains("@");
|
||||
@Throws(Exception::class)
|
||||
fun testLookup() {
|
||||
val result = lookup("erik.thauvin.net")
|
||||
Assertions.assertThat(result).`as`("lookup(erik.thauvin.net/104.31.77.12)").contains("104.31.77.12")
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorldTimeImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Lookup(null));
|
||||
@Throws(Exception::class)
|
||||
fun testWhois() {
|
||||
val result = whois("17.178.96.59", Lookup.WHOIS_HOST)
|
||||
Assertions.assertThat(Arrays.stream(result).anyMatch { m: String -> m.contains("Apple Inc.") })
|
||||
.`as`("whois(17.178.96.59/Apple Inc.").isTrue
|
||||
}
|
||||
}
|
|
@ -42,10 +42,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
|
||||
/**
|
||||
* The <code>ModuleExceptionTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-09
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ModuleExceptionTest {
|
||||
static final String debugMessage = "debugMessage";
|
||||
|
@ -53,13 +49,13 @@ public class ModuleExceptionTest {
|
|||
|
||||
@DataProvider(name = "dp")
|
||||
Object[][] createData(final Method m) {
|
||||
return new Object[][]{new Object[]{new ModuleException(debugMessage,
|
||||
message,
|
||||
new IOException("URL http://foobar.com"))},
|
||||
new Object[]{new ModuleException(debugMessage,
|
||||
message,
|
||||
new IOException("URL http://foobar.com?"))},
|
||||
new Object[]{new ModuleException(debugMessage, message)}};
|
||||
return new Object[][]{ new Object[]{ new ModuleException(debugMessage,
|
||||
message,
|
||||
new IOException("URL http://foobar.com")) },
|
||||
new Object[]{ new ModuleException(debugMessage,
|
||||
message,
|
||||
new IOException("URL http://foobar.com?")) },
|
||||
new Object[]{ new ModuleException(debugMessage, message) } };
|
||||
}
|
||||
|
||||
@Test(dataProvider = "dp")
|
||||
|
@ -78,7 +74,7 @@ public class ModuleExceptionTest {
|
|||
final ModuleException e = new ModuleException(debugMessage,
|
||||
message,
|
||||
new IOException(
|
||||
"URL http://foo.com?apiKey=" + apiKey + "&userID=me"));
|
||||
"URL http://foo.com?apiKey=" + apiKey + "&userID=me"));
|
||||
assertThat(e.getSanitizedMessage(apiKey)).as("sanitized url").contains("xxxxxxxxxx").doesNotContain(apiKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* PingTest.java
|
||||
* PingTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,28 +29,25 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.modules.Ping.Companion.randomPing
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The <code>PingTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
* The `PingTest` class.
|
||||
*/
|
||||
public class PingTest {
|
||||
class PingTest {
|
||||
@Test
|
||||
public void testPingImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Ping(null));
|
||||
fun testPingsArray() {
|
||||
Assertions.assertThat(Ping.PINGS).`as`("Pings array is not empty.").isNotEmpty
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPingsArray() {
|
||||
assertThat(Ping.PINGS).as("Pings array is not empty.").isNotEmpty();
|
||||
fun testRandomPing() {
|
||||
for (i in 0..9) {
|
||||
Assertions.assertThat(randomPing()).`as`("Random ping $i").isIn(Ping.PINGS)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.testng.annotations.Test
|
||||
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* StockQuoteTest.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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.LocalProperties;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* The <code>StockQuoteTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
|
||||
public class StockQuoteTest extends LocalProperties {
|
||||
@SuppressFBWarnings("LEST_LOST_EXCEPTION_STACK_TRACE")
|
||||
@SuppressWarnings("PMD.PreserveStackTrace")
|
||||
@Test
|
||||
public void testGetQuote() throws ModuleException {
|
||||
final String apiKey = LocalProperties.getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP);
|
||||
try {
|
||||
final List<Message> messages = StockQuote.getQuote("apple inc", apiKey);
|
||||
assertThat(messages).as("response not empty").isNotEmpty();
|
||||
assertThat(messages.get(0).getMsg()).as("same stock symbol").contains("AAPL").contains("Apple Inc.");
|
||||
|
||||
try {
|
||||
StockQuote.getQuote("blahfoo", apiKey);
|
||||
} catch (ModuleException e) {
|
||||
assertThat(e.getMessage()).as("invalid symbol").containsIgnoringCase(StockQuote.INVALID_SYMBOL);
|
||||
}
|
||||
|
||||
assertThatThrownBy(() -> StockQuote.getQuote("test", "")).as("no API key").isInstanceOf(
|
||||
ModuleException.class).hasNoCause();
|
||||
|
||||
assertThatThrownBy(() -> StockQuote.getQuote("", "apikey")).as("no symbol").isInstanceOf(
|
||||
ModuleException.class).hasNoCause();
|
||||
|
||||
} catch (ModuleException e) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true".equals(System.getenv("CI"))) {
|
||||
throw new ModuleException(e.getDebugMessage(), e.getSanitizedMessage(apiKey));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStockQuoteImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new StockQuote(null));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* StockQuoteTest.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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The `StockQuoteTest` class.
|
||||
*/
|
||||
class StockQuoteTest : LocalProperties() {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testGetQuote() {
|
||||
val apiKey = getProperty(StockQuote.ALPHAVANTAGE_API_KEY_PROP)
|
||||
try {
|
||||
val messages = getQuote("apple inc", apiKey)
|
||||
Assertions.assertThat(messages).`as`("response not empty").isNotEmpty
|
||||
Assertions.assertThat(messages[0].msg).`as`("same stock symbol")
|
||||
.isEqualTo("Symbol: AAPL [Apple Inc.]")
|
||||
Assertions.assertThat(messages[1].msg).`as`("price label")
|
||||
.startsWith(" Price: ")
|
||||
try {
|
||||
getQuote("blahfoo", apiKey)
|
||||
} catch (e: ModuleException) {
|
||||
Assertions.assertThat(e.message).`as`("invalid symbol")
|
||||
.containsIgnoringCase(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
Assertions.assertThatThrownBy { getQuote("test", "") }.`as`("no API key")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
Assertions.assertThatThrownBy { getQuote("", "apikey") }.`as`("no symbol")
|
||||
.isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
} catch (e: ModuleException) {
|
||||
// Avoid displaying api keys in CI logs
|
||||
if ("true" == System.getenv("CI")) {
|
||||
throw ModuleException(e.debugMessage, e.getSanitizedMessage(apiKey))
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TwitterTest.java
|
||||
* TwitterTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
|
@ -29,50 +29,43 @@
|
|||
* 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.modules
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.LocalProperties;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.Twitter.Companion.twitterPost
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
/**
|
||||
* The <code>TwitterTest</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
* The `TwitterTest` class.
|
||||
*/
|
||||
public class TwitterTest extends LocalProperties {
|
||||
@SuppressFBWarnings("MDM")
|
||||
private String getCi() {
|
||||
final String ciName = System.getenv("CI_NAME");
|
||||
if (ciName != null) {
|
||||
return ciName;
|
||||
} else {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Unknown Host";
|
||||
class TwitterTest : LocalProperties() {
|
||||
private val ci: String
|
||||
get() {
|
||||
val ciName = System.getenv("CI_NAME")
|
||||
return ciName ?: try {
|
||||
InetAddress.getLocalHost().hostName
|
||||
} catch (e: UnknownHostException) {
|
||||
"Unknown Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostTwitter() throws ModuleException {
|
||||
final String msg = "Testing Twitter API from " + getCi();
|
||||
assertThat(Twitter.twitterPost(
|
||||
@Throws(ModuleException::class)
|
||||
fun testPostTwitter() {
|
||||
val msg = "Testing Twitter API from $ci"
|
||||
Assertions.assertThat(
|
||||
twitterPost(
|
||||
getProperty(Twitter.CONSUMER_KEY_PROP),
|
||||
getProperty(Twitter.CONSUMER_SECRET_PROP),
|
||||
getProperty(Twitter.TOKEN_PROP),
|
||||
getProperty(Twitter.TOKEN_SECRET_PROP),
|
||||
getProperty(Twitter.HANDLE_PROP),
|
||||
msg,
|
||||
true).getMsg()).as("twitterPost(" + msg + ')').isEqualTo(msg);
|
||||
true
|
||||
).msg
|
||||
).`as`("twitterPost($msg)").isEqualTo(msg)
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Weather2Test.java
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.aksingh.owmjapis.api.APIException;
|
||||
import net.thauvin.erik.mobibot.LocalProperties;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* The <code>Weather2Test</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-09
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Weather2Test extends LocalProperties {
|
||||
@SuppressFBWarnings("PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS")
|
||||
@Test
|
||||
public void testWeather() throws ModuleException {
|
||||
List<Message> messages = Weather2.getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP));
|
||||
assertThat(messages.get(0).getMsg()).as("is Everett").contains("Everett").contains("US");
|
||||
assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Search").endsWith("98204%2CUS");
|
||||
|
||||
messages = Weather2.getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP));
|
||||
assertThat(messages.get(0).getMsg()).as("is UK").contains("London").contains("UK");
|
||||
assertThat(messages.get(messages.size() - 1).getMsg()).as("is City Code").endsWith("4517009");
|
||||
|
||||
assertThatThrownBy(() -> Weather2.getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)))
|
||||
.as("Montpellier not found").hasCauseInstanceOf(APIException.class);
|
||||
|
||||
assertThatThrownBy(() -> Weather2.getWeather("test", ""))
|
||||
.as("no API key").isInstanceOf(ModuleException.class).hasNoCause();
|
||||
|
||||
messages = Weather2.getWeather("", "apikey");
|
||||
assertThat(messages.get(0).isError()).as("no query").isTrue();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWeather2Impl() {
|
||||
AbstractModuleTest.testAbstractModule(new Weather2(null));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Weather2Test.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules
|
||||
|
||||
import net.aksingh.owmjapis.api.APIException
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.Weather2.Companion.getWeather
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The `Weather2Test` class.
|
||||
*/
|
||||
class Weather2Test : LocalProperties() {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun testWeather() {
|
||||
var messages = getWeather("98204", getProperty(Weather2.OWM_API_KEY_PROP))
|
||||
Assertions.assertThat(messages[0].msg).`as`("is Everett").contains("Everett").contains("US")
|
||||
Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Search").endsWith("98204%2CUS")
|
||||
messages = getWeather("London, UK", getProperty(Weather2.OWM_API_KEY_PROP))
|
||||
Assertions.assertThat(messages[0].msg).`as`("is UK").contains("London").contains("UK")
|
||||
Assertions.assertThat(messages[messages.size - 1].msg).`as`("is City Code").endsWith("4517009")
|
||||
Assertions.assertThatThrownBy { getWeather("Montpellier, FR", getProperty(Weather2.OWM_API_KEY_PROP)) }
|
||||
.`as`("Montpellier not found").hasCauseInstanceOf(APIException::class.java)
|
||||
Assertions.assertThatThrownBy { getWeather("test", "") }
|
||||
.`as`("no API key").isInstanceOf(ModuleException::class.java).hasNoCause()
|
||||
messages = getWeather("", "apikey")
|
||||
Assertions.assertThat(messages[0].isError).`as`("no query").isTrue
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* WordTimeTest.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, 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.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.worldTime
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.testng.annotations.Test
|
||||
|
||||
/**
|
||||
* The `WordTimeTest` class.
|
||||
*/
|
||||
class WordTimeTest {
|
||||
@Test
|
||||
fun testWorldTime() {
|
||||
Assertions.assertThat(worldTime("PST").msg).`as`("PST").endsWith(Utils.bold("Los Angeles"))
|
||||
Assertions.assertThat(worldTime("BLAH").isError).`as`("BLAH").isTrue
|
||||
Assertions.assertThat(worldTime("BEATS").msg).`as`("BEATS").contains("@")
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue