Merge branch '0.8'
This commit is contained in:
commit
8c13c44731
170 changed files with 9770 additions and 9289 deletions
|
@ -4,7 +4,7 @@ defaults: &defaults
|
|||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
TERM: dumb
|
||||
CI: true
|
||||
CI_NAME: "CircleCI"
|
||||
|
||||
defaults_gradle: &defaults_gradle
|
||||
steps:
|
||||
|
@ -18,7 +18,8 @@ defaults_gradle: &defaults_gradle
|
|||
name: Gradle Dependencies
|
||||
command: ./gradlew dependencies
|
||||
- save_cache:
|
||||
paths: ~/.m2
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: gradle-dependencies-{{ checksum "build.gradle" }}
|
||||
- run:
|
||||
name: Run All Checks
|
||||
|
@ -30,26 +31,25 @@ defaults_gradle: &defaults_gradle
|
|||
path: build/reports/
|
||||
|
||||
jobs:
|
||||
build_gradle_jdk12:
|
||||
build_gradle_jdk17:
|
||||
<<: *defaults
|
||||
|
||||
docker:
|
||||
- image: openjdk:12-jdk
|
||||
- image: cimg/openjdk:17.0
|
||||
|
||||
<<: *defaults_gradle
|
||||
|
||||
build_gradle_jdk8:
|
||||
build_gradle_jdk11:
|
||||
<<: *defaults
|
||||
|
||||
docker:
|
||||
- image: circleci/openjdk:8-jdk
|
||||
- image: cimg/openjdk:11.0
|
||||
|
||||
<<: *defaults_gradle
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
gradle:
|
||||
jobs:
|
||||
- build_gradle_jdk8
|
||||
- build_gradle_jdk12
|
||||
|
||||
jobs:
|
||||
- build_gradle_jdk11
|
||||
- build_gradle_jdk17
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[*]
|
||||
insert_final_newline=true
|
||||
insert_final_newline = true
|
||||
|
|
73
.github/workflows/gradle.yml
vendored
Normal file
73
.github/workflows/gradle.yml
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
name: gradle-ci
|
||||
|
||||
on: [ push, pull_request, workflow_dispatch ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m"
|
||||
SONAR_JDK: "11"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [ 11, 17 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Cache SonarCloud packages
|
||||
if: matrix.java-version == env.SONAR_JDK
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
- name: Cache Gradle packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-${{ matrix.java-version }}-
|
||||
|
||||
- name: Test with Gradle
|
||||
env:
|
||||
CI_NAME: "GitHub CI"
|
||||
ALPHAVANTAGE_API_KEY: ${{ secrets.ALPHAVANTAGE_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
GOOGLE_CSE_CX: ${{ secrets.GOOGLE_CSE_CX }}
|
||||
OWM_API_KEY: ${{ secrets.OWM_API_KEY }}
|
||||
PINBOARD_API_TOKEN: ${{ secrets.PINBOARD_API_TOKEN }}
|
||||
TWITTER_CONSUMERKEY: ${{ secrets.TWITTER_CONSUMERKEY }}
|
||||
TWITTER_CONSUMERSECRET: ${{ secrets.TWITTER_CONSUMERSECRET }}
|
||||
TWITTER_HANDLE: ${{ secrets.TWITTER_HANDLE }}
|
||||
TWITTER_TOKEN: ${{ secrets.TWITTER_TOKEN }}
|
||||
TWITTER_TOKENSECRET: ${{ secrets.TWITTER_TOKENSECRET }}
|
||||
run: ./gradlew build check --stacktrace
|
||||
|
||||
- name: SonarCloud
|
||||
if: success() && matrix.java-version == env.SONAR_JDK
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: ./gradlew sonarqube
|
||||
|
||||
- name: Cleanup Gradle Cache
|
||||
run: |
|
||||
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f ~/.gradle/caches/modules-2/gc.properties
|
73
.gitignore
vendored
73
.gitignore
vendored
|
@ -1,44 +1,9 @@
|
|||
__pycache__
|
||||
!.vscode/extensions.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!gradle-wrapper.jar
|
||||
!properties/*
|
||||
.classpath
|
||||
.DS_Store
|
||||
.gradle
|
||||
.history
|
||||
.idea_modules/
|
||||
.idea/**/contentModel.xml
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dbnavigator.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
.idea/**/mongoSettings.xml
|
||||
.idea/**/shelf
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/caches/build_file_checksums.ser
|
||||
.idea/**/httpRequests
|
||||
.idea/**/replstate.xml
|
||||
.idea/**/shelf/
|
||||
.kobalt
|
||||
.mtj.tmp/
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
.nb-gradle
|
||||
.project
|
||||
.scannerwork
|
||||
.settings
|
||||
.vscode/*
|
||||
*.class
|
||||
*.code-workspace
|
||||
*.ctxt
|
||||
|
@ -52,6 +17,43 @@ __pycache__
|
|||
*.tar.gz
|
||||
*.war
|
||||
*.zip
|
||||
.DS_Store
|
||||
.classpath
|
||||
.gradle
|
||||
.history
|
||||
.idea/**/caches/build_file_checksums.ser
|
||||
.idea/**/contentModel.xml
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dbnavigator.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/httpRequests
|
||||
.idea/**/libraries
|
||||
.idea/**/mongoSettings.xml
|
||||
.idea/**/replstate.xml
|
||||
.idea/**/shelf
|
||||
.idea/**/shelf/
|
||||
.idea/**/sonarlint*
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/workspace.xml
|
||||
.idea_modules/
|
||||
.kobalt
|
||||
.mtj.tmp/
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
.nb-gradle
|
||||
.project
|
||||
.scannerwork
|
||||
.settings
|
||||
.vscode/*
|
||||
Thumbs.db
|
||||
__pycache__
|
||||
atlassian-ide-plugin.xml
|
||||
bin/
|
||||
build/
|
||||
|
@ -85,5 +87,4 @@ project.properties
|
|||
release.properties
|
||||
target/
|
||||
test-output
|
||||
Thumbs.db
|
||||
venv
|
||||
|
|
32
.gitlab-ci.yml
Normal file
32
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
image: gradle:7-jdk17
|
||||
|
||||
variables:
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
CI_NAME: "GitLab CI"
|
||||
|
||||
before_script:
|
||||
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: gradle --build-cache assemble
|
||||
cache:
|
||||
key: "$CI_COMMIT_REF_NAME"
|
||||
policy: push
|
||||
paths:
|
||||
- build
|
||||
- .gradle
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: gradle check
|
||||
cache:
|
||||
key: "$CI_COMMIT_REF_NAME"
|
||||
policy: pull
|
||||
paths:
|
||||
- build
|
||||
- .gradle
|
16
.idea/checkstyle-idea.xml
generated
16
.idea/checkstyle-idea.xml
generated
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CheckStyle-IDEA">
|
||||
<option name="configuration">
|
||||
<map>
|
||||
<entry key="checkstyle-version" value="8.29" />
|
||||
<entry key="copy-libs" value="true" />
|
||||
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
|
||||
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
|
||||
<entry key="scan-before-checkin" value="false" />
|
||||
<entry key="scanscope" value="JavaOnly" />
|
||||
<entry key="suppress-errors" value="false" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
15
.idea/codeStyles/Project.xml
generated
Normal file
15
.idea/codeStyles/Project.xml
generated
Normal file
|
@ -0,0 +1,15 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value />
|
||||
</option>
|
||||
<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="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
2
.idea/codeStyles/codeStyleConfig.xml
generated
2
.idea/codeStyles/codeStyleConfig.xml
generated
|
@ -1,5 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
26
.idea/compiler.xml
generated
26
.idea/compiler.xml
generated
|
@ -1,30 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Gradle Imported" enabled="true">
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<option name="semver.project.dir" value="K:\java\mobibot" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" />
|
||||
</processorPath>
|
||||
<module name="mobibot.main" />
|
||||
</profile>
|
||||
<profile name="Gradle Imported" enabled="true">
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<option name="semver.project.dir" value="K:\java\mobibot" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/github/spullara/mustache/java/compiler/0.9.6/compiler-0.9.6.jar" />
|
||||
</processorPath>
|
||||
<module name="mobibot.main" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="mobibot" target="14" />
|
||||
<module name="mobibot.main" target="14" />
|
||||
<module name="mobibot.test" target="14" />
|
||||
</bytecodeTargetLevel>
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -30,7 +30,7 @@
|
|||
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
|
||||
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
|
||||
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
|
||||
<option name="myAdditionalJavadocTags" value="created" />
|
||||
<option name="myAdditionalJavadocTags" value="created,created,created" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
|
@ -1,7 +0,0 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Erik's Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
21
.idea/jarRepositories.xml
generated
21
.idea/jarRepositories.xml
generated
|
@ -11,20 +11,25 @@
|
|||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenLocal" />
|
||||
<option name="name" value="MavenLocal" />
|
||||
<option name="url" value="file:$MAVEN_REPOSITORY$/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenLocal" />
|
||||
<option name="name" value="MavenLocal" />
|
||||
<option name="url" value="file:/$MAVEN_REPOSITORY$/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
11
.idea/misc.xml
generated
11
.idea/misc.xml
generated
|
@ -1,10 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
<component name="EntryPointsManager">
|
||||
<pattern value="net.thauvin.erik.mobibot.modules.War" />
|
||||
<pattern value="net.thauvin.erik.mobibot.modules.War" method="War" />
|
||||
</component>
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="14" project-jdk-type="JavaSDK" />
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
12
.idea/mobibot.iml
generated
12
.idea/mobibot.iml
generated
|
@ -1,12 +0,0 @@
|
|||
<?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.7.3-beta+739" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
10
.idea/modules.xml
generated
10
.idea/modules.xml
generated
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/mobibot.iml" filepath="$PROJECT_DIR$/.idea/mobibot.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.main.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/mobibot.test.iml" filepath="$PROJECT_DIR$/.idea/modules/mobibot.test.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
56
.idea/modules/mobibot.iml
generated
56
.idea/modules/mobibot.iml
generated
|
@ -1,56 +0,0 @@
|
|||
<?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.7.3-beta+547" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<output url="file://$MODULE_DIR$/../../out/production/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/../../out/test/classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../..">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../kobaltBuild" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../out" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Gradle: pircbot:pircbot:1.5.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: pircbot:pircbot:1.5.0:sources" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.12.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.12.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" 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.6" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" 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:4.2.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.12.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: net.thauvin.erik:semver:1.2.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spotbugs:spotbugs-annotations:4.0.0-beta4" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:2.2.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.50" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.google.code.gson:gson:2.8.5" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.github.spullara.mustache.java:compiler:0.9.6" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:7.0.0" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.assertj:assertj-core:3.13.2" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.beust:jcommander:1.72" level="project" />
|
||||
</component>
|
||||
</module>
|
82
.idea/modules/mobibot.main.iml
generated
82
.idea/modules/mobibot.main.iml
generated
|
@ -1,82 +0,0 @@
|
|||
<?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.7.3-beta+739" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false">
|
||||
<compilerSettings>
|
||||
<option name="additionalArguments" value="-Xallow-no-source-files" />
|
||||
</compilerSettings>
|
||||
<compilerArguments>
|
||||
<option name="destination" value="$MODULE_DIR$/../../build/classes/kotlin/main" />
|
||||
<option name="classpath" value="$MAVEN_REPOSITORY$/net/thauvin/erik/semver/1.2.0/semver-1.2.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/cc27715d1c9c8246beb6a33ea099a9ca5d4e5da1/pircbot-1.5.0-sources.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/pircbot/pircbot/1.5.0/7a9dd235e6e81db733212202cc4067b5625650cf/pircbot-1.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.github.spotbugs/spotbugs-annotations/4.0.1/192f5794e6aa74968a883ecdf5bf7d961843579f/spotbugs-annotations-4.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.13.1/533f6ae0bb0ce091493f2eeab0c1df4327e46ef1/log4j-core-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.13.1/79e92fe5b6c30cc4c8a52893378d4d130e298c65/log4j-slf4j-impl-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.13.1/cc670f92dc77bbf4540904c3fa211b997cba00d8/log4j-api-2.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.4/c51c00206bb913cd8612b24abd9fa98ae89719b1/commons-cli-1.4.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/commons-net/commons-net/3.6/b71de00508dcb078d2b24b5fa7e538636de9b3da/commons-net-3.6.jar;K:/maven/repository/net/thauvin/erik/pinboard-poster/1.0.1/pinboard-poster-1.0.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.aksingh/owm-japis/2.5.3.0/c3aca5d34ba937e0c8e9776cec906003b0703044/owm-japis-2.5.3.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/4.4.1/d2e71a032b1927539c07adc3a9f36336d6fbf4b9/okhttp-4.4.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome/1.12.2/eaa7a2025cd38a6678d96b2b78b4f7e68e3f8e36/rome-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.json/json/20190722/7bce7bacf0ab5e9f894d307a3de8b7f540064d5/json-20190722.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jsoup/jsoup/1.13.1/f9577f3732bb7caa4fee8aba5053158f4010c118/jsoup-1.13.1.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/net.objecthunter/exp4j/0.4.8/cf1cfc0f958077d86ac7452c7e36d944689b2ec4/exp4j-0.4.8.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.twitter4j/twitter4j-core/4.0.7/5fdb375ccfb3eda7354efb262cbe9b53abccff2/twitter4j-core-4.0.7.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.3.71/4defc79915cf4f78b49bbc4a8f1e80e87767a5b/kotlin-stdlib-jdk8-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.3.71/9180d3aec3f0b2ea6ef0dcf01b464a6e2219e381/kotlin-stdlib-jdk7-1.3.71.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.rometools/rome-utils/1.12.2/240dc40fb9333ac872319e7d31178bffc63f7900/rome-utils-1.12.2.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/2.4.3/d946f785445d73c6bc99bbd778576d2576e37ea9/okio-jvm-2.4.3.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar;K:/maven/repository/org/jdom/jdom2/2.0.6/jdom2-2.0.6.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar;C:/Users/erik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar;K:/maven/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
<option name="noStdlib" value="true" />
|
||||
<option name="noReflect" value="true" />
|
||||
<option name="moduleName" value="mobibot" />
|
||||
<option name="languageVersion" value="1.3" />
|
||||
<option name="apiVersion" value="1.3" />
|
||||
<option name="pluginOptions">
|
||||
<array />
|
||||
</option>
|
||||
<option name="pluginClasspaths">
|
||||
<array>
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-script-runtime/1.3.71/7f5522e7a9d1736fabfdb4335630f64504ce8f20/kotlin-script-runtime-1.3.71.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-common/1.3.71/80b8eb0986d1391e6a1dcf9aba968d59165dc4f/kotlin-scripting-common-1.3.71.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-scripting-jvm/1.3.71/ee1ba4f199415255ea6ea55cc5dc41856b2729fd/kotlin-scripting-jvm-1.3.71.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.3.71/e71c3fef58e26affeb03d675e91fd8abdd44aa7b/kotlin-stdlib-common-1.3.71.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.3.71/898273189ad22779da6bed88ded39b14cb5fd432/kotlin-stdlib-1.3.71.jar" />
|
||||
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.2.1/3839faf625f4197acaeceeb6da000f011a2acb49/kotlinx-coroutines-core-1.2.1.jar" />
|
||||
<option value="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
</array>
|
||||
</option>
|
||||
<option name="errors">
|
||||
<ArgumentParseErrors />
|
||||
</option>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_14">
|
||||
<output url="file://$MODULE_DIR$/../../build/classes/java/main" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../../src/generated/java">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/generated/java" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
<content url="file://$MODULE_DIR$/../../src/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<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.0.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-core:2.13.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-slf4j-impl:2.13.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.logging.log4j:log4j-api:2.13.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.apache.commons:commons-lang3:3.9" 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.6" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.thauvin.erik:pinboard-poster:1.0.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.aksingh:owm-japis:2.5.3.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:4.4.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.rometools:rome:1.12.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.json:json:20190722" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.13.1" level="project" />
|
||||
<orderEntry type="library" name="Gradle: net.objecthunter:exp4j:0.4.8" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.twitter4j:twitter4j-core:4.0.7" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.71" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.25" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.3.71" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.squareup.retrofit2:retrofit: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.okio:okio:2.4.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.rometools:rome-utils:1.12.2" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jdom:jdom2:2.0.6" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0" level="project" />
|
||||
</component>
|
||||
</module>
|
101
.idea/modules/mobibot.test.iml
generated
101
.idea/modules/mobibot.test.iml
generated
File diff suppressed because one or more lines are too long
10
.idea/runConfigurations.xml
generated
Normal file
10
.idea/runConfigurations.xml
generated
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
26
.travis.yml
26
.travis.yml
|
@ -1,26 +0,0 @@
|
|||
language: java
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
- CI=true
|
||||
|
||||
install:
|
||||
- git fetch --unshallow --tags
|
||||
|
||||
addons:
|
||||
sonarcloud:
|
||||
organization: "ethauvin-github"
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- openjdk12
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
|
||||
after_success:
|
||||
- |
|
||||
if [ "${TRAVIS_TEST_RESULT}" == 0 ] && [ "$TRAVIS_JDK_VERSION" == oraclejdk8 ]; then
|
||||
./gradlew sonarqube
|
||||
fi
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[](http://opensource.org/licenses/BSD-3-Clause) [](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [](https://travis-ci.com/ethauvin/mobibot) [](https://circleci.com/gh/ethauvin/mobibot/tree/master)
|
||||
[](http://opensource.org/licenses/BSD-3-Clause) [](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [](https://circleci.com/gh/ethauvin/mobibot/tree/master)
|
||||
|
||||
Some very basic instructions:
|
||||
|
||||
|
|
9
bitbucket-pipelines.yml
Normal file
9
bitbucket-pipelines.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
image: openjdk:17
|
||||
|
||||
pipelines:
|
||||
default:
|
||||
- step:
|
||||
caches:
|
||||
- gradle
|
||||
script:
|
||||
- bash ./gradlew check
|
202
build.gradle
202
build.gradle
|
@ -1,15 +1,14 @@
|
|||
plugins {
|
||||
id 'application'
|
||||
id 'checkstyle'
|
||||
id 'com.github.ben-manes.versions' version '0.28.0'
|
||||
id 'com.github.spotbugs' version '4.0.4'
|
||||
id 'com.github.ben-manes.versions' version '0.41.0'
|
||||
id 'idea'
|
||||
id 'io.gitlab.arturbosch.detekt' version '1.7.0'
|
||||
id 'jacoco'
|
||||
id 'io.gitlab.arturbosch.detekt' version '1.19.0'
|
||||
id 'java'
|
||||
id 'net.thauvin.erik.gradle.semver' version '1.0.4'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.71'
|
||||
id 'org.sonarqube' version '2.8'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.6.10'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.6.10'
|
||||
id 'org.jetbrains.kotlinx.kover' version '0.4.4'
|
||||
id 'org.sonarqube' version '3.3'
|
||||
id 'pmd'
|
||||
}
|
||||
|
||||
|
@ -19,83 +18,112 @@ final def packageName = 'net.thauvin.erik.mobibot'
|
|||
final def deployDir = 'deploy'
|
||||
final def semverProcessor = "net.thauvin.erik:semver:1.2.0"
|
||||
|
||||
def isNonStable = { String version ->
|
||||
def stableKeyword = ['RELEASE', 'FINAL', 'GA', 'JRE'].any { it -> version.toUpperCase().contains(it) }
|
||||
def regex = /^[0-9,.v-]+(-r)?$/
|
||||
return !stableKeyword && !(version ==~ regex)
|
||||
}
|
||||
|
||||
mainClassName = packageName + '.Mobibot'
|
||||
|
||||
ext {
|
||||
versions = [
|
||||
kotlin : '1.3.71',
|
||||
log4j : '2.13.1',
|
||||
spotbugs : '4.0.1'
|
||||
]
|
||||
}
|
||||
ext.versions = [
|
||||
log4j: '2.17.1',
|
||||
pmd : '6.41.0',
|
||||
]
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor semverProcessor
|
||||
compileOnly semverProcessor
|
||||
kapt(semverProcessor)
|
||||
compileOnly(semverProcessor)
|
||||
|
||||
implementation 'pircbot:pircbot:1.5.0'
|
||||
compileOnly 'pircbot:pircbot:1.5.0:sources'
|
||||
implementation 'com.github.pircbotx:pircbotx:-SNAPSHOT'
|
||||
|
||||
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
implementation 'org.apache.commons:commons-text:1.9'
|
||||
|
||||
implementation 'commons-cli:commons-cli:1.5.0'
|
||||
implementation 'commons-codec:commons-codec:1.15'
|
||||
implementation 'commons-net:commons-net:3.8.0'
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
implementation 'com.google.guava:guava:31.0.1-jre'
|
||||
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
|
||||
|
||||
implementation 'org.slf4j:slf4j-api:1.7.33'
|
||||
implementation "org.apache.logging.log4j:log4j-api:$versions.log4j"
|
||||
implementation "org.apache.logging.log4j:log4j-core:$versions.log4j"
|
||||
implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j"
|
||||
|
||||
implementation 'org.apache.commons:commons-lang3:3.9'
|
||||
implementation 'commons-cli:commons-cli:1.4'
|
||||
|
||||
implementation 'commons-net:commons-net:3.6'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||
|
||||
implementation 'com.rometools:rome:1.12.2'
|
||||
|
||||
implementation 'org.json:json:20190722'
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
implementation 'net.objecthunter:exp4j:0.4.8'
|
||||
|
||||
implementation 'org.twitter4j:twitter4j-core:4.0.7'
|
||||
implementation 'net.thauvin.erik:pinboard-poster:1.0.1'
|
||||
|
||||
implementation 'com.rometools:rome:1.18.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||
implementation 'net.aksingh:owm-japis:2.5.3.0'
|
||||
implementation 'net.objecthunter:exp4j:0.4.8'
|
||||
implementation 'org.json:json:20211205'
|
||||
implementation 'org.jsoup:jsoup:1.14.3'
|
||||
implementation 'org.twitter4j:twitter4j-core:4.0.7'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
|
||||
implementation 'net.thauvin.erik:cryptoprice:0.9.0'
|
||||
implementation 'net.thauvin.erik:pinboard-poster:1.0.3'
|
||||
|
||||
testImplementation 'org.testng:testng:7.2.0'
|
||||
testImplementation 'org.assertj:assertj-core:3.15.0'
|
||||
|
||||
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1'
|
||||
spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.4.7'
|
||||
|
||||
compileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs"
|
||||
testCompileOnly "com.github.spotbugs:spotbugs-annotations:$versions.spotbugs"
|
||||
testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
|
||||
// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
|
||||
// testImplementation "org.mockito:mockito-core:4.0.0"
|
||||
testImplementation 'org.testng:testng:7.5'
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
events('skipped', 'failed')
|
||||
}
|
||||
useTestNG() {
|
||||
options.suites('src/test/resources/testng.xml')
|
||||
}
|
||||
}
|
||||
|
||||
spotbugs {
|
||||
toolVersion.set("$versions.spotbugs")
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
tasks.withType(com.github.spotbugs.snom.SpotBugsTask) {
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
kapt {
|
||||
includeCompileClasspath = false
|
||||
arguments {
|
||||
arg('semver.project.dir', projectDir)
|
||||
}
|
||||
excludeFilter.set(file("$projectDir/config/spotbugs/excludeFilter.xml"))
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
compileJava {
|
||||
dependsOn 'incrementBuildMeta'
|
||||
options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation']
|
||||
}
|
||||
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = java.targetCompatibility.toString()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("dependencyUpdates").configure {
|
||||
rejectVersionIf {
|
||||
isNonStable(it.candidate.version)
|
||||
}
|
||||
}
|
||||
|
||||
pmd {
|
||||
toolVersion = '6.22.0'
|
||||
toolVersion = versions.pmd
|
||||
ignoreFailures = true
|
||||
ruleSets = []
|
||||
ruleSetFiles = files("${projectDir}/config/pmd.xml")
|
||||
|
@ -103,34 +131,15 @@ pmd {
|
|||
}
|
||||
|
||||
detekt {
|
||||
baseline = file("detekt-baseline.xml")
|
||||
}
|
||||
|
||||
tasks.withType(io.gitlab.arturbosch.detekt.Detekt) {
|
||||
exclude("**/**.java")
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/src/generated/java")
|
||||
options.compilerArgs += [ "-Asemver.project.dir=$projectDir" ]
|
||||
}
|
||||
|
||||
compileJava {
|
||||
dependsOn('incrementBuildMeta')
|
||||
options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation']
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.tags = ['created']
|
||||
options.author = true
|
||||
options.links('http://www.jibble.org/javadocs/pircbot/', 'http://docs.oracle.com/javase/8/docs/api/')
|
||||
//toolVersion = "main-SNAPSHOT"
|
||||
baseline = file("${projectDir}/config/detekt/baseline.xml")
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest.attributes('Main-Class': mainClassName,
|
||||
'Class-Path': '. ./lib/' + configurations.runtimeClasspath.collect { it.getName() }.join(' ./lib/'))
|
||||
archiveVersion.set("")
|
||||
exclude('log4j2.xml')
|
||||
}
|
||||
|
||||
clean {
|
||||
|
@ -140,7 +149,7 @@ clean {
|
|||
}
|
||||
|
||||
run {
|
||||
args '--v'
|
||||
args('-h')
|
||||
}
|
||||
|
||||
incrementBuildMeta {
|
||||
|
@ -148,42 +157,26 @@ incrementBuildMeta {
|
|||
if (!System.getenv('CI')) {
|
||||
buildMeta = sprintf("%03d", (buildMeta as Integer) + 1)
|
||||
} else {
|
||||
println "No increment on CIs."
|
||||
println 'No increment on CIs.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.projectKey", "ethauvin_mobibot"
|
||||
}
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = '0.8.5'
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
html.enabled = true
|
||||
xml.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Checkstyle) {
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
property('sonar.organization', 'ethauvin-github')
|
||||
property('sonar.projectKey', 'ethauvin_mobibot')
|
||||
property('sonar.host.url', 'https://sonarcloud.io')
|
||||
property('sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/kover/report.xml")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.sonarqube {
|
||||
dependsOn("jacocoTestReport")
|
||||
dependsOn 'koverReport'
|
||||
}
|
||||
|
||||
task copyToDeploy(type: Copy) {
|
||||
from('properties')
|
||||
from jar
|
||||
from('properties', jar)
|
||||
into deployDir
|
||||
}
|
||||
|
||||
|
@ -191,22 +184,23 @@ task copyToDeployLib(type: Copy) {
|
|||
from(configurations.runtimeClasspath) {
|
||||
exclude 'annotations-*.jar'
|
||||
}
|
||||
into deployDir + '/lib'
|
||||
into(deployDir + '/lib')
|
||||
}
|
||||
|
||||
task deploy(dependsOn: ['clean', 'build', 'jar']) {
|
||||
description = 'Copies all needed files to the ${deployDir} directory.'
|
||||
task deploy {
|
||||
description = "Copies all needed files to the ${deployDir} directory."
|
||||
group = 'Publishing'
|
||||
dependsOn(clean, build, jar)
|
||||
outputs.dir deployDir
|
||||
inputs.files copyToDeploy
|
||||
inputs.files copyToDeployLib
|
||||
inputs.files(copyToDeploy, copyToDeployLib)
|
||||
doLast {
|
||||
file(deployDir + '/logs').mkdir()
|
||||
}
|
||||
mustRunAfter clean
|
||||
}
|
||||
|
||||
task release(dependsOn: ['wrapper', 'deploy']) {
|
||||
task release {
|
||||
group = 'Publishing'
|
||||
description = 'Releases new version.'
|
||||
dependsOn(wrapper, 'deploy')
|
||||
}
|
||||
|
|
|
@ -1,277 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||
that can be found at https://google.github.io/styleguide/javaguide.html.
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
|
||||
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
|
||||
<module name="SuppressWithPlainTextCommentFilter">
|
||||
<property name="offCommentFormat" value="CHECKSTYLE_OFF"/>
|
||||
<property name="onCommentFormat" value="CHECKSTYLE_ON"/>
|
||||
<property name="checkFormat"
|
||||
value="^((?!(FileTabCharacterCheck)).)*$"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder"/>
|
||||
<module name="SuppressionCommentFilter">
|
||||
<property name="offCommentFormat" value="CHECKSTYLE_OFF: ALMOST_ALL"/>
|
||||
<property name="onCommentFormat" value="CHECKSTYLE_ON: ALMOST_ALL"/>
|
||||
<property name="checkFormat" value="^((?!(EqualsHashCode)).)*$"/>
|
||||
</module>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format"
|
||||
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message"
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlySame"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||
LITERAL_DO"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||
INSTANCE_INIT"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyLambdas" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
<message key="ws.notPreceded"
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapDot"/>
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapComma"/>
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||
<property name="tokens" value="ELLIPSIS"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||
<property name="tokens" value="METHOD_REF"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LambdaParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="CatchParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LocalVariableName">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="InterfaceTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="4"/>
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="caseIndent" value="4"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="4"/>
|
||||
<property name="arrayInitIndent" value="4"/>
|
||||
</module>
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
</module>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="customImportOrderRules"
|
||||
value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/>
|
||||
<property name="specialImportsRegExp" value="^javax\."/>
|
||||
<property name="standardPackageRegExp" value="^java\."/>
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="separateLineBetweenGroups" value="false"/>
|
||||
</module>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="ParenPad"/>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="SummaryJavadoc">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph"/>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
</module>
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoreInlineTags" value="false"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="expected"/>
|
||||
</module>
|
||||
<!-- <module name="CommentsIndentation"/> -->
|
||||
</module>
|
||||
</module>
|
76
config/detekt/baseline.xml
Normal file
76
config/detekt/baseline.xml
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexMethod:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||
<ID>ComplexMethod:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||
<ID>LongMethod:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||
<ID>LongMethod:Mobibot.kt$Mobibot.Companion$@JvmStatic @Throws(Exception::class) fun main(args: Array<String>)</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$( channel: String, cmd: String, entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent )</ID>
|
||||
<ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</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$4</ID>
|
||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</ID>
|
||||
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
|
||||
<ID>MagicNumber:Cycle.kt$Cycle$1000L</ID>
|
||||
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
|
||||
<ID>MagicNumber:Mobibot.kt$Mobibot$8</ID>
|
||||
<ID>MagicNumber:Modules.kt$Modules$7</ID>
|
||||
<ID>MagicNumber:StockQuote.kt$StockQuote.Companion$10</ID>
|
||||
<ID>MagicNumber:Tell.kt$Tell$50</ID>
|
||||
<ID>MagicNumber:Tell.kt$Tell$7</ID>
|
||||
<ID>MagicNumber:Twitter.kt$Twitter$1000L</ID>
|
||||
<ID>MagicNumber:Twitter.kt$Twitter$60L</ID>
|
||||
<ID>MagicNumber:TwitterOAuth.kt$TwitterOAuth$401</ID>
|
||||
<ID>MagicNumber:Users.kt$Users$8</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils$30</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils$365</ID>
|
||||
<ID>MagicNumber:Utils.kt$Utils$7</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$1.60934</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$32</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$404</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$5</ID>
|
||||
<ID>MagicNumber:Weather2.kt$Weather2.Companion$9</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime$14</ID>
|
||||
<ID>MagicNumber:WorldTime.kt$WorldTime$4</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>NestedBlockDepth:Addons.kt$Addons$ fun add(command: AbstractCommand)</ID>
|
||||
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule)</ID>
|
||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID>
|
||||
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List<String?>)</ID>
|
||||
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID>
|
||||
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
|
||||
<ID>NestedBlockDepth:GoogleSearch.kt$GoogleSearch.Companion$ @JvmStatic @Throws(ModuleException::class) fun searchGoogle(query: String, apiKey: String?, cseKey: String?): List<Message></ID>
|
||||
<ID>NestedBlockDepth:LinksMgr.kt$LinksMgr$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:Lookup.kt$Lookup$override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:Posting.kt$Posting$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:StockQuote.kt$StockQuote.Companion$ @JvmStatic @Throws(ModuleException::class) fun getQuote(symbol: String, apiKey: String?): List<Message></ID>
|
||||
<ID>NestedBlockDepth:Tell.kt$Tell$ fun send(event: GenericUserEvent)</ID>
|
||||
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ @JvmStatic fun load(file: String): List<TellMessage></ID>
|
||||
<ID>NestedBlockDepth:TellMessagesMgr.kt$TellMessagesMgr$ @JvmStatic fun save(file: String, messages: List<TellMessage?>?)</ID>
|
||||
<ID>NestedBlockDepth:TwitterOAuth.kt$TwitterOAuth$ @JvmStatic @Throws(TwitterException::class, IOException::class) fun main(args: Array<String>)</ID>
|
||||
<ID>NestedBlockDepth:Weather2.kt$Weather2$ override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent)</ID>
|
||||
<ID>NestedBlockDepth:Weather2.kt$Weather2.Companion$ @JvmStatic @Throws(ModuleException::class) fun getWeather(query: String, apiKey: String?): List<Message></ID>
|
||||
<ID>ReturnCount:Addons.kt$Addons$ fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean</ID>
|
||||
<ID>ReturnCount:Addons.kt$Addons$ fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean</ID>
|
||||
<ID>ReturnCount:ExceptionSanitizer.kt$ExceptionSanitizer$ fun ModuleException.sanitize(vararg sanitize: String): ModuleException</ID>
|
||||
<ID>SwallowedException:GoogleSearchTest.kt$GoogleSearchTest$e: ModuleException</ID>
|
||||
<ID>SwallowedException:StockQuoteTest.kt$StockQuoteTest$e: ModuleException</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:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
|
||||
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
|
||||
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
283
config/pmd.xml
283
config/pmd.xml
|
@ -5,111 +5,78 @@
|
|||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||
<description>Erik's Ruleset</description>
|
||||
<!-- BEST PRACTICES -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>-->
|
||||
<rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/>
|
||||
<rule ref="category/java/bestpractices.xml/CheckResultSet"/>
|
||||
<rule ref="category/java/bestpractices.xml/ConstantsInInterface"/>
|
||||
<rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/>
|
||||
<rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/>
|
||||
<rule ref="category/java/bestpractices.xml/GuardLogStatement"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/LooseCoupling"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray"/>
|
||||
<rule ref="category/java/bestpractices.xml">
|
||||
<exclude name="AvoidPrintStackTrace"/>
|
||||
<exclude name="JUnit4TestShouldUseTestAnnotation"/>
|
||||
<exclude name="JUnitTestContainsTooManyAsserts"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/MissingOverride">
|
||||
<properties>
|
||||
<property name="violationSuppressXPath"
|
||||
value="./MethodDeclarator[@Image='hashCode' or @Image='equals' or @Image='toString']"/>
|
||||
value="//MethodDeclaration[@Name='hashCode' or @Name='equals' or @Name='toString']"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInCaseInsensitiveComparisons"/>
|
||||
<rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInComparisons"/>
|
||||
<rule ref="category/java/bestpractices.xml/PreserveStackTrace"/>
|
||||
<rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap"/>
|
||||
<rule ref="category/java/bestpractices.xml/ReplaceVectorWithList"/> -->
|
||||
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/>
|
||||
<rule ref="category/java/bestpractices.xml/SystemPrintln"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedImports"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertTrueInsteadOfAssertEquals"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseVarargs"/>
|
||||
|
||||
|
||||
<!-- NAMING CONVENTIONS -->
|
||||
<rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/GenericsNaming"/>
|
||||
<rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/MethodNamingConventions"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/PackageCase"/>-->
|
||||
|
||||
|
||||
<!-- OTHER -->
|
||||
<rule ref="category/java/codestyle.xml/AvoidDollarSigns"/>
|
||||
<rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"/>
|
||||
<rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"/>
|
||||
<rule ref="category/java/codestyle.xml/AvoidUsingNativeCode"/>
|
||||
<rule ref="category/java/codestyle.xml/BooleanGetMethodName"/>
|
||||
<rule ref="category/java/codestyle.xml/CallSuperInConstructor"/>
|
||||
<rule ref="category/java/codestyle.xml/ControlStatementBraces"/>
|
||||
<rule ref="category/java/codestyle.xml/DontImportJavaLang"/>
|
||||
<rule ref="category/java/codestyle.xml/DuplicateImports"/>
|
||||
<rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract"/>
|
||||
<rule ref="category/java/codestyle.xml/ExtendsObject"/>
|
||||
<rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass"/>
|
||||
<rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/>
|
||||
<rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/>
|
||||
<rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal"/>
|
||||
<rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal"/>
|
||||
<rule ref="category/java/codestyle.xml/NoPackage"/>
|
||||
<rule ref="category/java/codestyle.xml/PrematureDeclaration"/>
|
||||
<rule ref="category/java/codestyle.xml/TooManyStaticImports"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryReturn"/>
|
||||
<rule ref="category/java/codestyle.xml/UselessQualifiedThis"/>
|
||||
|
||||
<!-- CODE STYLE -->
|
||||
<rule ref="category/java/codestyle.xml">
|
||||
<exclude name="AtLeastOneConstructor"/>
|
||||
<exclude name="ClassNamingConventions"/>
|
||||
<exclude name="ConfusingTernary"/>
|
||||
<exclude name="CommentDefaultAccessModifier"/>
|
||||
<exclude name="DefaultPackage"/>
|
||||
<exclude name="FieldNamingConventions"/>
|
||||
<exclude name="LongVariable"/>
|
||||
<exclude name="OnlyOneReturn"/>
|
||||
<exclude name="PackageCase"/>
|
||||
<exclude name="ShortClassName"/>
|
||||
<exclude name="ShortMethodName"/>
|
||||
<exclude name="ShortVariable"/>
|
||||
<exclude name="UselessParentheses"/>
|
||||
<exclude name="UseUnderscoresInNumericLiterals"/>
|
||||
</rule>
|
||||
|
||||
<!-- DESIGN -->
|
||||
<rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod"/>
|
||||
<rule ref="category/java/design.xml/AvoidRethrowingException"/>
|
||||
<rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException"/>
|
||||
<rule ref="category/java/design.xml/AvoidThrowingNullPointerException"/>
|
||||
<rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes"/>
|
||||
<rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/>
|
||||
<rule ref="category/java/design.xml/CollapsibleIfStatements"/>
|
||||
<rule ref="category/java/design.xml/CouplingBetweenObjects"/>
|
||||
<rule ref="category/java/design.xml/DataClass"/>
|
||||
<rule ref="category/java/design.xml/DoNotExtendJavaLangError"/>
|
||||
<rule ref="category/java/design.xml/ExceptionAsFlowControl"/>
|
||||
<!-- <rule ref="category/java/design.xml/ExcessivePublicCount"/>-->
|
||||
<rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/>
|
||||
<rule ref="category/java/design.xml/ImmutableField"/>
|
||||
<rule ref="category/java/design.xml/LogicInversion"/>
|
||||
<rule ref="category/java/design.xml/SignatureDeclareThrowsException"/>
|
||||
<rule ref="category/java/design.xml/SimplifiedTernary"/>
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanAssertion"/>
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanExpressions"/>
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanReturns"/>
|
||||
<rule ref="category/java/design.xml/SimplifyConditional"/>
|
||||
<rule ref="category/java/design.xml/SingularField"/>
|
||||
<rule ref="category/java/design.xml/SwitchDensity"/>
|
||||
<rule ref="category/java/design.xml/UselessOverridingMethod"/>
|
||||
<rule ref="category/java/design.xml/UseUtilityClass"/>
|
||||
<rule ref="category/java/design.xml">
|
||||
<exclude name="AvoidCatchingGenericException"/>
|
||||
<exclude name="AvoidDeeplyNestedIfStmts"/>
|
||||
<exclude name="AvoidUncheckedExceptionsInSignatures"/>
|
||||
<exclude name="CognitiveComplexity"/>
|
||||
<exclude name="CyclomaticComplexity"/>
|
||||
<exclude name="ExcessiveClassLength"/>
|
||||
<exclude name="ExcessiveMethodLength"/>
|
||||
<exclude name="ExcessiveParameterList"/>
|
||||
<exclude name="ExcessivePublicCount"/>
|
||||
<exclude name="GodClass"/>
|
||||
<exclude name="LawOfDemeter"/>
|
||||
<exclude name="LoosePackageCoupling"/>
|
||||
<exclude name="NPathComplexity"/>
|
||||
<exclude name="NcssCount"/>
|
||||
<exclude name="TooManyFields"/>
|
||||
<exclude name="TooManyMethods"/>
|
||||
<exclude name="UseObjectForClearerAPI"/>
|
||||
</rule>
|
||||
|
||||
<!-- DOCUMENTATION -->
|
||||
<rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"/>
|
||||
<rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"/>
|
||||
|
||||
<rule ref="category/java/documentation.xml">
|
||||
<exclude name="CommentRequired"/>
|
||||
<exclude name="CommentSize"/>
|
||||
</rule>
|
||||
|
||||
<!-- ERROR PRONE -->
|
||||
<rule ref="category/java/errorprone.xml">
|
||||
<exclude name="AssignmentInOperand"/>
|
||||
<exclude name="AvoidCatchingNPE"/>
|
||||
<exclude name="AvoidDuplicateLiterals"/>
|
||||
<exclude name="AvoidFieldNameMatchingMethodName"/>
|
||||
<exclude name="AvoidFieldNameMatchingTypeName"/>
|
||||
<exclude name="AvoidLiteralsInIfCondition"/>
|
||||
<exclude name="BeanMembersShouldSerialize"/>
|
||||
<exclude name="EmptyCatchBlock"/>
|
||||
<exclude name="NullAssignment"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/AssignmentInOperand">
|
||||
<properties>
|
||||
<property name="allowWhile" value="true"/>
|
||||
|
@ -117,86 +84,11 @@
|
|||
<property name="allowIf" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidCallingFinalize"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals">
|
||||
<properties>
|
||||
<property name="skipAnnotations" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"/>
|
||||
<rule ref="category/java/errorprone.xml/BadComparison"/>
|
||||
<rule ref="category/java/errorprone.xml/BrokenNullCheck"/>
|
||||
<rule ref="category/java/errorprone.xml/CallSuperFirst"/>
|
||||
<rule ref="category/java/errorprone.xml/CallSuperLast"/>
|
||||
<rule ref="category/java/errorprone.xml/CheckSkipResult"/>
|
||||
<rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneThrowsCloneNotSupportedException"/>
|
||||
<rule ref="category/java/errorprone.xml/CloseResource"/>
|
||||
<rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"/>
|
||||
<rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod"/>>
|
||||
<rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"/>
|
||||
<rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable"/>
|
||||
<rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard"/>
|
||||
<rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally"/>
|
||||
<rule ref="category/java/errorprone.xml/DontImportSun"/>
|
||||
<rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"/>
|
||||
<rule ref="category/java/errorprone.xml/EqualsNull"/>
|
||||
<rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize"/>
|
||||
<rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize"/>
|
||||
<rule ref="category/java/errorprone.xml/FinalizeOverloaded"/>
|
||||
<rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected"/>
|
||||
<rule ref="category/java/errorprone.xml/IdempotentOperations"/>
|
||||
<rule ref="category/java/errorprone.xml/ImportFromSamePackage"/>
|
||||
<rule ref="category/java/errorprone.xml/InstantiationToGetClass"/>
|
||||
<rule ref="category/java/errorprone.xml/InvalidLogMessageFormat"/>
|
||||
<rule ref="category/java/errorprone.xml/JumbledIncrementer"/>
|
||||
<rule ref="category/java/errorprone.xml/JUnitSpelling"/>
|
||||
<rule ref="category/java/errorprone.xml/JUnitStaticSuite"/>
|
||||
<rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass"/>
|
||||
<rule ref="category/java/errorprone.xml/MisplacedNullCheck"/>
|
||||
<rule ref="category/java/errorprone.xml/MissingBreakInSwitch"/>
|
||||
<rule ref="category/java/errorprone.xml/MissingSerialVersionUID"/>
|
||||
<rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"/>
|
||||
<rule ref="category/java/errorprone.xml/MoreThanOneLogger"/>
|
||||
<rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/>
|
||||
<rule ref="category/java/errorprone.xml/NonStaticInitializer"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/NullAssignment"/>-->
|
||||
<rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/>
|
||||
<rule ref="category/java/errorprone.xml/ProperCloneImplementation"/>
|
||||
<rule ref="category/java/errorprone.xml/ProperLogger"/>
|
||||
<rule ref="category/java/errorprone.xml/ReturnEmptyArrayRatherThanNull"/>
|
||||
<rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale"/>
|
||||
<rule ref="category/java/errorprone.xml/SingleMethodSingleton"/>
|
||||
<rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"/>
|
||||
<rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal"/>
|
||||
<rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar"/>
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName"/>
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName"/>
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"/>
|
||||
<rule ref="category/java/errorprone.xml/TestClassWithoutTestCases"/>
|
||||
<rule ref="category/java/errorprone.xml/UnconditionalIfStatement"/>
|
||||
<rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion"/>
|
||||
<rule ref="category/java/errorprone.xml/UnnecessaryCaseChange"/>
|
||||
<rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"/>
|
||||
<rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"/>
|
||||
<rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging"/>
|
||||
<rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings"/>
|
||||
<rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/>
|
||||
<rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyCatchBlock">
|
||||
<properties>
|
||||
<property name="allowExceptionNameRegex">
|
||||
|
@ -204,61 +96,16 @@
|
|||
</property>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/EmptyFinalizer"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyIfStmt"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyInitializer"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyStatementBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptySwitchStatements"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyTryBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyWhileStmt"/>
|
||||
|
||||
|
||||
<!-- MULTITHREADING -->
|
||||
<rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel"/>
|
||||
<rule ref="category/java/multithreading.xml/AvoidThreadGroup"/>
|
||||
<rule ref="category/java/multithreading.xml/AvoidUsingVolatile"/>
|
||||
<rule ref="category/java/multithreading.xml/DontCallThreadRun"/>
|
||||
<rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/>
|
||||
<rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/>
|
||||
<rule ref="category/java/multithreading.xml/UseConcurrentHashMap"/>
|
||||
<rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/>
|
||||
<rule ref="category/java/multithreading.xml">
|
||||
</rule>
|
||||
|
||||
<!-- PERFORMANCE -->
|
||||
<rule ref="category/java/performance.xml/AddEmptyString"/>
|
||||
<rule ref="category/java/performance.xml/AppendCharacterWithChar"/>
|
||||
<rule ref="category/java/performance.xml/AvoidArrayLoops"/>
|
||||
<rule ref="category/java/performance.xml/AvoidFileStream"/>
|
||||
<rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops"/>
|
||||
<rule ref="category/java/performance.xml/AvoidUsingShortType"/>
|
||||
<rule ref="category/java/performance.xml/BigIntegerInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/BooleanInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/ByteInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse"/>
|
||||
<rule ref="category/java/performance.xml/ConsecutiveLiteralAppends"/>
|
||||
<rule ref="category/java/performance.xml/InefficientEmptyStringCheck"/>
|
||||
<rule ref="category/java/performance.xml/InefficientStringBuffering"/>
|
||||
<rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration"/>
|
||||
<rule ref="category/java/performance.xml/IntegerInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/LongInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/OptimizableToArrayCall"/>
|
||||
<rule ref="category/java/performance.xml/RedundantFieldInitializer"/>
|
||||
<rule ref="category/java/performance.xml/SimplifyStartsWith"/>
|
||||
<rule ref="category/java/performance.xml/ShortInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/StringInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/StringToString"/>
|
||||
<rule ref="category/java/performance.xml/TooFewBranchesForASwitchStatement"/>
|
||||
<rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/>
|
||||
<!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector"/> -->
|
||||
<rule ref="category/java/performance.xml/UseArraysAsList"/>
|
||||
<rule ref="category/java/performance.xml/UseIndexOfChar"/>
|
||||
<rule ref="category/java/performance.xml/UselessStringValueOf"/>
|
||||
<rule ref="category/java/performance.xml/UseStringBufferForStringAppends"/>
|
||||
<rule ref="category/java/performance.xml/UseStringBufferLength"/>
|
||||
<rule ref="category/java/performance.xml">
|
||||
</rule>
|
||||
|
||||
<!-- SECURITY -->
|
||||
<rule ref="category/java/security.xml/HardCodedCryptoKey"/>
|
||||
<rule ref="category/java/security.xml/InsecureCryptoIv"/>
|
||||
<rule ref="category/java/security.xml">
|
||||
</rule>
|
||||
</ruleset>
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter
|
||||
xmlns="https://github.com/spotbugs/filter/3.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">
|
||||
<Match>
|
||||
<Or>
|
||||
<Package name="net.thauvin.erik.mobibot.*"/>
|
||||
<Package name="net.thauvin.erik.mobibot.tell.*"/>
|
||||
<Package name="net.thauvin.erik.mobibot.entries.*"/>
|
||||
</Or>
|
||||
<Or>
|
||||
<Bug pattern="PATH_TRAVERSAL_IN"/>
|
||||
<Bug pattern="PATH_TRAVERSAL_OUT"/>
|
||||
|
||||
</Or>
|
||||
<Confidence value="2"/>
|
||||
</Match>
|
||||
<Match>
|
||||
<Or>
|
||||
<Class name="net.thauvin.erik.mobibot.Mobibot"/>
|
||||
<Class name="net.thauvin.erik.mobibot.Pinboard"/>
|
||||
<Class name="net.thauvin.erik.mobibot.FeedReader"/>
|
||||
<Class name="net.thauvin.erik.mobibot.tell.Tell"/>
|
||||
<Package name="net.thauvin.erik.mobibot.modules.*"/>
|
||||
<Package name="net.thauvin.erik.mobibot.entries.*"/>
|
||||
</Or>
|
||||
<Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY"/>
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="net.thauvin.erik.mobibot.Mobibot"/>
|
||||
<Method name="main"/>
|
||||
<Bug pattern="PATH_TRAVERSAL_OUT"/>
|
||||
<Confidence value="1"/>
|
||||
</Match>
|
||||
</FindBugsFilter>
|
12
deploy.sh
Executable file
12
deploy.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
DEPLOYDIR=/home/erik/mobitopia/mobibot
|
||||
|
||||
if [ -f "deploy/mobibot.jar" ]; then
|
||||
/bin/cp deploy/mobibot.jar $DEPLOYDIR
|
||||
rm -rf $DEPLOYDIR/lib/*.jar
|
||||
cp deploy/lib/*.jar $DEPLOYDIR/lib
|
||||
chmod 755 $DEPLOYDIR/*.jar $DEPLOYDIR/lib/*.jar
|
||||
else
|
||||
echo "mobibot.jar not found."
|
||||
fi
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<!--
|
||||
~ detekt-baseline.xml
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<SmellBaseline>
|
||||
<Blacklist></Blacklist>
|
||||
<Whitelist></Whitelist>
|
||||
</SmellBaseline>
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-rc-3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
269
gradlew
vendored
269
gradlew
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,78 +17,113 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
|
@ -105,79 +140,95 @@ location of your Java installation."
|
|||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
|
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -54,7 +54,7 @@ goto fail
|
|||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
|
@ -64,28 +64,14 @@ echo location of your Java installation.
|
|||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
License (http://ostermiller.org/utils/)
|
||||
|
||||
Copyright (C) 2004-2010 Stephen Ostermiller
|
||||
http://ostermiller.org/contact.pl?regarding=Java+Utilities
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
See the GNU General Public License (https://ostermiller.org/utils/license.html) for more details.
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2009-2018 Jonathan Hedley <jonathan@hedley.net>
|
||||
Copyright (c) 2009-2022 Jonathan Hedley <jonathan@hedley.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* This file is automatically generated.
|
||||
* Do not modify! -- ALL CHANGES WILL BE ERASED!
|
||||
*/
|
||||
package {{packageName}};
|
||||
|
||||
import java.time.*;
|
||||
|
||||
/**
|
||||
* Provides semantic version information.
|
||||
*
|
||||
* @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a>
|
||||
*/
|
||||
public final class {{className}} {
|
||||
public static final String PROJECT = "{{project}}";
|
||||
public static final LocalDateTime BUILDDATE =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli({{epoch}}L), ZoneId.systemDefault());
|
||||
public static final int MAJOR = {{major}};
|
||||
public static final int MINOR = {{minor}};
|
||||
public static final int PATCH = {{patch}};
|
||||
public static final String PRERELEASE = "{{preRelease}}";
|
||||
public static final String BUILDMETA = "{{buildMeta}}";
|
||||
public static final String VERSION = "{{version}}";
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*/
|
||||
private {{className}}() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
}
|
|
@ -1,16 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="stderr" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="net.thauvin.erik.mobibot" level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Logger>
|
||||
<Root level="error">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
<Configuration status="warn">
|
||||
<Appenders>
|
||||
<Console name="stderr" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
<Console name="input" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} %msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
<Console name="output" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{UNIX_MILLIS} >>>%msg%n"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="warn" additivity="false">
|
||||
<AppenderRef ref="stderr"/>
|
||||
</Root>
|
||||
<logger level="debug" name="org.pircbotx.InputParser" additivity="false">
|
||||
<appender-ref ref="input"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="debug" name="org.pircbotx.output.OutputRaw" additivity="false">
|
||||
<appender-ref ref="output"/>
|
||||
<appender-ref ref="stderr" level="warn"/>
|
||||
</logger>
|
||||
<logger level="warn" name="net.thauvin.erik.mobibot" additivity="false">
|
||||
<appender-ref ref="stderr"/>
|
||||
</logger>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
channel=#mobitopia
|
||||
server=irc.freenode.net
|
||||
server=irc.libera.chat
|
||||
#port=6667
|
||||
login=mobibot
|
||||
nick=mobibot
|
||||
#realname=mobibot
|
||||
|
||||
# Die command password, if any
|
||||
#die=changeme
|
||||
|
||||
# NickServ password
|
||||
ident=changepwd
|
||||
ident=changeme
|
||||
#ident-nick=nickserv
|
||||
#ident-msg=IDENTIFY changepwd
|
||||
|
||||
|
@ -14,20 +18,22 @@ ignore=chanserv,nickserv
|
|||
tags=mobile mobitopia
|
||||
tags-keywords=android ios apple google
|
||||
|
||||
weblog=http://www.mobitopia.org/
|
||||
feed=http://www.mobitopia.org/rss.xml
|
||||
backlogs=http://www.mobitopia.org/mobibot/logs
|
||||
|
||||
tell-max-days=5
|
||||
tell-max-size=50
|
||||
|
||||
#disabled-commands=die, ignore
|
||||
#disabled-modules=dice, joke
|
||||
|
||||
#
|
||||
# Credentials for: http://pinboard.in/
|
||||
#
|
||||
#pinboard-api-token=user\:TOKEN
|
||||
|
||||
#
|
||||
# Configure app at: https://apps.twitter.com/
|
||||
# Configure app at: https://developer.twitter.com/en/apps
|
||||
# and then: java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret>
|
||||
#
|
||||
#twitter-consumerKey=
|
||||
|
|
|
@ -1 +1,18 @@
|
|||
plugins {
|
||||
id "com.gradle.enterprise" version "3.6.3"
|
||||
}
|
||||
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
link("GitHub", "https://github.com/ethauvin/pinboard-poster/tree/master")
|
||||
if (System.getenv("CI")) {
|
||||
uploadInBackground = false
|
||||
publishOnFailure()
|
||||
tag "CI"
|
||||
}
|
||||
termsOfServiceUrl = "https://gradle.com/terms-of-service"
|
||||
termsOfServiceAgree = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'mobibot'
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* This file is automatically generated.
|
||||
* Do not modify! -- ALL CHANGES WILL BE ERASED!
|
||||
*/
|
||||
package net.thauvin.erik.mobibot;
|
||||
|
||||
import java.time.*;
|
||||
|
||||
/**
|
||||
* Provides semantic version information.
|
||||
*
|
||||
* @author <a href="https://github.com/ethauvin/semver">Semantic Version Annotation Processor</a>
|
||||
*/
|
||||
public final class ReleaseInfo {
|
||||
public static final String PROJECT = "mobibot";
|
||||
public static final LocalDateTime BUILDDATE =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(1585119580379L), ZoneId.systemDefault());
|
||||
public static final int MAJOR = 0;
|
||||
public static final int MINOR = 7;
|
||||
public static final int PATCH = 3;
|
||||
public static final String PRERELEASE = "beta";
|
||||
public static final String BUILDMETA = "760";
|
||||
public static final String VERSION = "0.7.3-beta+760";
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*/
|
||||
private ReleaseInfo() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Commands.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;
|
||||
|
||||
/**
|
||||
* The <code>commands</code>, <code>keywords</code> and <code>arguments</code>.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-26
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class Commands {
|
||||
/**
|
||||
* The link command.
|
||||
*/
|
||||
public static final String LINK_CMD = "L";
|
||||
/**
|
||||
* The view command.
|
||||
*/
|
||||
public static final String VIEW_CMD = "view";
|
||||
/**
|
||||
* The add (back)log command.
|
||||
*/
|
||||
static final String ADDLOG_CMD = "addlog";
|
||||
/**
|
||||
* The cycle command.
|
||||
*/
|
||||
static final String CYCLE_CMD = "cycle";
|
||||
/**
|
||||
* Debug command line argument.
|
||||
*/
|
||||
static final String DEBUG_ARG = "debug";
|
||||
/**
|
||||
* The debug command.
|
||||
*/
|
||||
static final String DEBUG_CMD = "debug";
|
||||
/**
|
||||
* The die command.
|
||||
*/
|
||||
static final String DIE_CMD = "die";
|
||||
/**
|
||||
* Help command line argument.
|
||||
*/
|
||||
static final String HELP_ARG = "help";
|
||||
/**
|
||||
* The help command.
|
||||
*/
|
||||
static final String HELP_CMD = "help";
|
||||
/**
|
||||
* The help on posting keyword.
|
||||
*/
|
||||
static final String HELP_POSTING_KEYWORD = "posting";
|
||||
/**
|
||||
* The help on tags keyword.
|
||||
*/
|
||||
static final String HELP_TAGS_KEYWORD = "tags";
|
||||
/**
|
||||
* The ignore command.
|
||||
*/
|
||||
static final String IGNORE_CMD = "ignore";
|
||||
/**
|
||||
* The ignore <code>me</code> keyword.
|
||||
*/
|
||||
static final String IGNORE_ME_KEYWORD = "me";
|
||||
/**
|
||||
* The info command.
|
||||
*/
|
||||
static final String INFO_CMD = "info";
|
||||
/**
|
||||
* The me command.
|
||||
*/
|
||||
static final String ME_CMD = "me";
|
||||
/**
|
||||
* The modules command.
|
||||
*/
|
||||
static final String MODULES_CMD = "modules";
|
||||
/**
|
||||
* The msg command.
|
||||
*/
|
||||
static final String MSG_CMD = "msg";
|
||||
/**
|
||||
* The nick command.
|
||||
*/
|
||||
static final String NICK_CMD = "nick";
|
||||
/**
|
||||
* Properties command line argument.
|
||||
*/
|
||||
static final String PROPS_ARG = "properties";
|
||||
/**
|
||||
* The recap command.
|
||||
*/
|
||||
static final String RECAP_CMD = "recap";
|
||||
/**
|
||||
* The say command.
|
||||
*/
|
||||
static final String SAY_CMD = "say";
|
||||
/**
|
||||
* The users command.
|
||||
*/
|
||||
static final String USERS_CMD = "users";
|
||||
/**
|
||||
* Properties version line argument.
|
||||
*/
|
||||
static final String VERSION_ARG = "version";
|
||||
/**
|
||||
* The version command.
|
||||
*/
|
||||
static final String VERSION_CMD = "version";
|
||||
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the constructor is called.
|
||||
*/
|
||||
private Commands() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
}
|
|
@ -1,108 +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
|
||||
*/
|
||||
class FeedReader implements Runnable {
|
||||
// Maximum number of feed items to display
|
||||
private static final int MAX_ITEMS = 5;
|
||||
|
||||
// Tab indent (4 spaces)
|
||||
private static final String TAB_INDENT = " ";
|
||||
|
||||
// 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.
|
||||
*/
|
||||
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();
|
||||
final SyndFeed feed = input.build(new XmlReader(new URL(url)));
|
||||
|
||||
SyndEntry item;
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
if (items.isEmpty()) {
|
||||
bot.send(sender, "There is currently nothing to view. Why don't you post something?");
|
||||
} else {
|
||||
for (int i = 0; (i < items.size()) && (i < MAX_ITEMS); i++) {
|
||||
item = items.get(i);
|
||||
bot.send(sender, item.getTitle());
|
||||
bot.send(sender, TAB_INDENT + Utils.green(item.getLink()));
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
bot.getLogger().debug("Invalid feed URL.", e);
|
||||
bot.send(sender, "The feed URL is invalid.");
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
* Pinboard.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;
|
||||
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink;
|
||||
import net.thauvin.erik.pinboard.PinboardPoster;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The class to handle posts to pinboard.in.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2017-05-17
|
||||
* @since 1.0
|
||||
*/
|
||||
class Pinboard {
|
||||
private final String ircServer;
|
||||
private final PinboardPoster pinboard;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Pinboard} instance.
|
||||
*
|
||||
* @param bot The bot's instance.
|
||||
* @param apiToken The API end point.
|
||||
* @param ircServer The IRC server.
|
||||
*/
|
||||
Pinboard(final Mobibot bot, final String apiToken, final String ircServer) {
|
||||
pinboard = new PinboardPoster(apiToken);
|
||||
this.ircServer = ircServer;
|
||||
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
final ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setLevel(Level.FINE);
|
||||
final Logger logger = pinboard.getLogger();
|
||||
logger.addHandler(consoleHandler);
|
||||
logger.setLevel(Level.FINE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a post to pinboard.in.
|
||||
*
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
@SuppressWarnings("Convert2Diamond")
|
||||
final void addPost(final EntryLink entry) {
|
||||
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
|
||||
@Override
|
||||
protected Boolean doInBackground() {
|
||||
return pinboard.addPin(entry.getLink(),
|
||||
entry.getTitle(),
|
||||
postedBy(entry),
|
||||
entry.getPinboardTags(),
|
||||
formatDate(entry.getDate()));
|
||||
}
|
||||
};
|
||||
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a post to pinboard.in.
|
||||
*
|
||||
* @param entry The entry to delete.
|
||||
*/
|
||||
@SuppressWarnings("Convert2Diamond")
|
||||
final void deletePost(final EntryLink entry) {
|
||||
final String link = entry.getLink();
|
||||
|
||||
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
|
||||
@Override
|
||||
protected Boolean doInBackground() {
|
||||
return pinboard.deletePin(link);
|
||||
}
|
||||
};
|
||||
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date to a UTC timestamp.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The date in {@link DateTimeFormatter#ISO_INSTANT} format.
|
||||
*/
|
||||
private String formatDate(final Date date) {
|
||||
return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(DateTimeFormatter.ISO_INSTANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns he pinboard.in extended attribution line.
|
||||
*
|
||||
* @param entry The entry.
|
||||
* @return The extended attribution line.
|
||||
*/
|
||||
private String postedBy(final EntryLink entry) {
|
||||
return "Posted by " + entry.getNick() + " on " + entry.getChannel() + " (" + ircServer + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a post to pinboard.in.
|
||||
*
|
||||
* @param oldUrl The old post URL.
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
@SuppressWarnings("Convert2Diamond")
|
||||
final void updatePost(final String oldUrl, final EntryLink entry) {
|
||||
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
|
||||
@Override
|
||||
protected Boolean doInBackground() {
|
||||
if (!oldUrl.equals(entry.getLink())) {
|
||||
pinboard.deletePin(oldUrl);
|
||||
|
||||
return pinboard.addPin(entry.getLink(),
|
||||
entry.getTitle(),
|
||||
postedBy(entry),
|
||||
entry.getPinboardTags(),
|
||||
formatDate(entry.getDate()));
|
||||
} else {
|
||||
return pinboard.addPin(entry.getLink(),
|
||||
entry.getTitle(),
|
||||
postedBy(entry),
|
||||
entry.getPinboardTags(),
|
||||
formatDate(entry.getDate()),
|
||||
true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
worker.execute();
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* TwitterOAuth.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 twitter4j.TwitterException;
|
||||
import twitter4j.TwitterFactory;
|
||||
import twitter4j.auth.AccessToken;
|
||||
import twitter4j.auth.RequestToken;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* The <code>TwitterOAuth</code> class.
|
||||
* <p>
|
||||
* Go to <a href="http://twitter.com/oauth_clients/new">http://twitter.com/oauth_clients/new</a> to register your bot.
|
||||
* </p>
|
||||
* Then execute:
|
||||
* <p>
|
||||
* <code>
|
||||
* java -cp "mobibot.jar:lib/*" net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret>
|
||||
* </code>
|
||||
* </p>
|
||||
* and follow the prompts/instructions.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @author <a href="http://twitter4j.org/en/code-examples.html#oauth" target="_blank">Twitter4J</a>
|
||||
* @created Sep 13, 2010
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseUtilityClass")
|
||||
public final class TwitterOAuth {
|
||||
/**
|
||||
* Twitter OAuth Client Registration.
|
||||
*
|
||||
* @param args The consumerKey and consumerSecret should be passed as arguments.
|
||||
* @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"})
|
||||
public static void main(final String[] args) throws TwitterException, IOException {
|
||||
if (args.length == 2) {
|
||||
final twitter4j.Twitter twitter = new TwitterFactory().getInstance();
|
||||
twitter.setOAuthConsumer(args[0], args[1]);
|
||||
final RequestToken requestToken = twitter.getOAuthRequestToken();
|
||||
AccessToken accessToken = null;
|
||||
try (final BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||
while (null == accessToken) {
|
||||
System.out.println("Open the following URL and grant access to your account:");
|
||||
System.out.println(requestToken.getAuthorizationURL());
|
||||
System.out.print("Enter the PIN (if available) or just hit enter.[PIN]:");
|
||||
final String pin = br.readLine();
|
||||
try {
|
||||
if (pin != null && pin.length() > 0) {
|
||||
accessToken = twitter.getOAuthAccessToken(requestToken, pin);
|
||||
} else {
|
||||
accessToken = twitter.getOAuthAccessToken();
|
||||
}
|
||||
|
||||
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());
|
||||
} catch (TwitterException te) {
|
||||
if (401 == te.getStatusCode()) {
|
||||
System.out.println("Unable to get the access token.");
|
||||
} else {
|
||||
te.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("Usage: " + TwitterOAuth.class.getName() + " <consumerKey> <consumerSecret>");
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
|
@ -1,317 +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 org.apache.commons.lang3.StringUtils;
|
||||
import org.jibble.pircbot.Colors;
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
/*
|
||||
* EntriesMgr.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.entries;
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndContent;
|
||||
import com.rometools.rome.feed.synd.SyndContentImpl;
|
||||
import com.rometools.rome.feed.synd.SyndEntry;
|
||||
import com.rometools.rome.feed.synd.SyndEntryImpl;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.feed.synd.SyndFeedImpl;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
import com.rometools.rome.io.SyndFeedOutput;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the feed entries.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-28
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class EntriesMgr {
|
||||
/**
|
||||
* The name of the file containing the current entries.
|
||||
*/
|
||||
public static final String CURRENT_XML = "current.xml";
|
||||
|
||||
/**
|
||||
* The name of the file containing the backlog entries.
|
||||
*/
|
||||
public static final String NAV_XML = "nav.xml";
|
||||
|
||||
/**
|
||||
* The .xml extension
|
||||
*/
|
||||
public static final String XML_EXT = ".xml";
|
||||
|
||||
// Maximum number of backlogs to keep
|
||||
private static final int MAX_BACKLOGS = 10;
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the constructor is called.
|
||||
*/
|
||||
private EntriesMgr() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the backlogs.
|
||||
*
|
||||
* @param file The file containing the backlogs.
|
||||
* @param history The history list.
|
||||
* @throws IOException If the file was not found or could not be read.
|
||||
* @throws FeedException If an error occurred while reading the feed.
|
||||
*/
|
||||
public static void loadBacklogs(final String file, final Collection<String> history)
|
||||
throws IOException, FeedException {
|
||||
history.clear();
|
||||
|
||||
final SyndFeedInput input = new SyndFeedInput();
|
||||
|
||||
try (final InputStreamReader reader =
|
||||
new InputStreamReader(Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) {
|
||||
|
||||
final SyndFeed feed = input.build(reader);
|
||||
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
SyndEntry item;
|
||||
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
item = items.get(i);
|
||||
history.add(item.getTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the current entries.
|
||||
*
|
||||
* @param file The file containing the current entries.
|
||||
* @param channel The channel
|
||||
* @param entries The entries.
|
||||
* @return The feed's last published date.
|
||||
* @throws IOException If the file was not found or could not be read.
|
||||
* @throws FeedException If an error occurred while reading the feed.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static String loadEntries(final String file, final String channel, final Collection<EntryLink> entries)
|
||||
throws IOException, FeedException {
|
||||
entries.clear();
|
||||
|
||||
final SyndFeedInput input = new SyndFeedInput();
|
||||
|
||||
final String today;
|
||||
|
||||
try (final InputStreamReader reader = new InputStreamReader(
|
||||
Files.newInputStream(Paths.get(file)), StandardCharsets.UTF_8)) {
|
||||
final SyndFeed feed = input.build(reader);
|
||||
|
||||
today = Utils.isoLocalDate(feed.getPublishedDate());
|
||||
|
||||
final List<SyndEntry> items = feed.getEntries();
|
||||
SyndEntry item;
|
||||
SyndContent description;
|
||||
String[] comments;
|
||||
String author;
|
||||
EntryLink entry;
|
||||
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
item = items.get(i);
|
||||
author = item.getAuthor()
|
||||
.substring(item.getAuthor().lastIndexOf('(') + 1, item.getAuthor().length() - 1);
|
||||
entry = new EntryLink(item.getLink(),
|
||||
item.getTitle(),
|
||||
author,
|
||||
channel,
|
||||
item.getPublishedDate(),
|
||||
item.getCategories());
|
||||
description = item.getDescription();
|
||||
comments = description.getValue().split("<br/>");
|
||||
|
||||
int split;
|
||||
for (final String comment : comments) {
|
||||
split = comment.indexOf(": ");
|
||||
|
||||
if (split != -1) {
|
||||
entry.addComment(comment.substring(split + 2).trim(), comment.substring(0, split).trim());
|
||||
}
|
||||
}
|
||||
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return today;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the entries.
|
||||
*
|
||||
* @param bot The bot object.
|
||||
* @param entries The entries array.
|
||||
* @param history The history array.
|
||||
* @param isDayBackup Set the true if the daily backup file should also be created.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"CE_CLASS_ENVY", "CC_CYCLOMATIC_COMPLEXITY"}, justification = "Yes, it does.")
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static void saveEntries(final Mobibot bot,
|
||||
final List<EntryLink> entries,
|
||||
final List<String> history,
|
||||
final boolean isDayBackup) {
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
bot.getLogger().debug("Saving the feeds...");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(bot.getLogsDir()) && StringUtils.isNotBlank(bot.getWeblogUrl())) {
|
||||
try {
|
||||
final SyndFeedOutput output = new SyndFeedOutput();
|
||||
SyndFeed rss = new SyndFeedImpl();
|
||||
final List<SyndEntry> items = new ArrayList<>(0);
|
||||
SyndEntry item;
|
||||
SyndContent description;
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.getLogsDir() + CURRENT_XML)), StandardCharsets.UTF_8)) {
|
||||
rss.setFeedType("rss_2.0");
|
||||
rss.setTitle(bot.getChannel() + " IRC Links");
|
||||
rss.setDescription("Links from " + bot.getIrcServer() + " on " + bot.getChannel());
|
||||
rss.setLink(bot.getWeblogUrl());
|
||||
rss.setPublishedDate(Calendar.getInstance().getTime());
|
||||
rss.setLanguage("en");
|
||||
|
||||
EntryLink entry;
|
||||
StringBuilder buff;
|
||||
EntryComment comment;
|
||||
|
||||
for (int i = (entries.size() - 1); i >= 0; --i) {
|
||||
entry = entries.get(i);
|
||||
|
||||
buff = new StringBuilder()
|
||||
.append("Posted by <b>")
|
||||
.append(entry.getNick())
|
||||
.append("</b> on <a href=\"irc://")
|
||||
.append(bot.getIrcServer()).append('/')
|
||||
.append(entry.getChannel())
|
||||
.append("\"><b>")
|
||||
.append(entry.getChannel())
|
||||
.append("</b></a>");
|
||||
|
||||
if (entry.getCommentsCount() > 0) {
|
||||
buff.append(" <br/><br/>");
|
||||
|
||||
final EntryComment[] comments = entry.getComments();
|
||||
|
||||
for (int j = 0; j < comments.length; j++) {
|
||||
comment = comments[j];
|
||||
|
||||
if (j > 0) {
|
||||
buff.append(" <br/>");
|
||||
}
|
||||
|
||||
buff.append(comment.getNick()).append(": ").append(comment.getComment());
|
||||
}
|
||||
}
|
||||
|
||||
item = new SyndEntryImpl();
|
||||
item.setLink(entry.getLink());
|
||||
description = new SyndContentImpl();
|
||||
description.setValue(buff.toString());
|
||||
item.setDescription(description);
|
||||
item.setTitle(entry.getTitle());
|
||||
item.setPublishedDate(entry.getDate());
|
||||
item.setAuthor(
|
||||
bot.getChannel().substring(1) + '@' + bot.getIrcServer() + " (" + entry.getNick() + ')');
|
||||
item.setCategories(entry.getTags());
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
rss.setEntries(items);
|
||||
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
bot.getLogger().debug("Writing the entries feed.");
|
||||
}
|
||||
|
||||
output.output(rss, fw);
|
||||
}
|
||||
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(
|
||||
bot.getLogsDir() + bot.getToday() + XML_EXT)), StandardCharsets.UTF_8)) {
|
||||
output.output(rss, fw);
|
||||
}
|
||||
|
||||
if (isDayBackup) {
|
||||
if (StringUtils.isNotBlank(bot.getBacklogsUrl())) {
|
||||
if (!history.contains(bot.getToday())) {
|
||||
history.add(bot.getToday());
|
||||
|
||||
while (history.size() > MAX_BACKLOGS) {
|
||||
history.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
try (final Writer fw = new OutputStreamWriter(
|
||||
Files.newOutputStream(Paths.get(bot.getLogsDir() + NAV_XML)), StandardCharsets.UTF_8)) {
|
||||
rss = new SyndFeedImpl();
|
||||
rss.setFeedType("rss_2.0");
|
||||
rss.setTitle(bot.getChannel() + " IRC Links Backlogs");
|
||||
rss.setDescription("Backlogs of Links from " + bot.getIrcServer() + " on "
|
||||
+ bot.getChannel());
|
||||
rss.setLink(bot.getBacklogsUrl());
|
||||
rss.setPublishedDate(Calendar.getInstance().getTime());
|
||||
|
||||
String date;
|
||||
items.clear();
|
||||
|
||||
for (int i = (history.size() - 1); i >= 0; --i) {
|
||||
date = history.get(i);
|
||||
|
||||
item = new SyndEntryImpl();
|
||||
item.setLink(bot.getBacklogsUrl() + date + ".xml");
|
||||
item.setTitle(date);
|
||||
description = new SyndContentImpl();
|
||||
description.setValue("Links for " + date);
|
||||
item.setDescription(description);
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
rss.setEntries(items);
|
||||
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
bot.getLogger().debug("Writing the backlog feed.");
|
||||
}
|
||||
|
||||
output.output(rss, fw);
|
||||
}
|
||||
} else {
|
||||
bot.getLogger().warn("Unable to generate the backlogs feed. No property configured.");
|
||||
}
|
||||
}
|
||||
} catch (FeedException | IOException e) {
|
||||
bot.getLogger().warn("Unable to generate the entries feed.", e);
|
||||
}
|
||||
} else {
|
||||
bot.getLogger()
|
||||
.warn("Unable to generate the entries feed. At least one of the required property is missing.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* EntriesUtils.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.entries;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Commands;
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
|
||||
/**
|
||||
* The <code>Utils</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-19
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class EntriesUtils {
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*/
|
||||
private EntriesUtils() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's comment for display on the channel.
|
||||
*
|
||||
* @param entryIndex The entry's index.
|
||||
* @param commentIndex The comment's index.
|
||||
* @param comment The {@link EntryComment comment} object.
|
||||
* @return The entry's comment.
|
||||
*/
|
||||
public static String buildComment(final int entryIndex, final int commentIndex, final EntryComment comment) {
|
||||
return (Commands.LINK_CMD + (entryIndex + 1) + '.' + (commentIndex + 1) + ": [" + comment.getNick() + "] "
|
||||
+ comment.getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's link for display on the channel.
|
||||
*
|
||||
* @param index The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @return The entry's link.
|
||||
* @see #buildLink(int, EntryLink, boolean)
|
||||
*/
|
||||
public static String buildLink(final int index, final EntryLink entry) {
|
||||
return buildLink(index, entry, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an entry's link for display on the channel.
|
||||
*
|
||||
* @param index The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @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.")
|
||||
public static String buildLink(final int index, final EntryLink entry, final boolean isView) {
|
||||
final StringBuilder buff = new StringBuilder().append(Commands.LINK_CMD).append(index + 1)
|
||||
.append(": ").append('[').append(entry.getNick()).append(']');
|
||||
|
||||
if (isView && entry.hasComments()) {
|
||||
buff.append("[+").append(entry.getCommentsCount()).append(']');
|
||||
}
|
||||
|
||||
buff.append(' ');
|
||||
|
||||
if (Constants.NO_TITLE.equals(entry.getTitle())) {
|
||||
buff.append(entry.getTitle());
|
||||
} else {
|
||||
buff.append(Utils.bold(entry.getTitle()));
|
||||
}
|
||||
|
||||
buff.append(" ( ").append(Utils.green(entry.getLink())).append(" )");
|
||||
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an entry's tags/categories for display on the channel.
|
||||
*
|
||||
* @param entryIndex The entry's index.
|
||||
* @param entry The {@link EntryLink entry} object.
|
||||
* @return The entry's tags.
|
||||
*/
|
||||
public static String buildTags(final int entryIndex, final EntryLink entry) {
|
||||
return (Commands.LINK_CMD + (entryIndex + 1) + "T: " + entry.getPinboardTags().replace(",", ", "));
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* EntryComment.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.entries;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* The class used to store comments associated to a specific entry.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Jan 31, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings({"PMD.DataClass"})
|
||||
public class EntryComment implements Serializable {
|
||||
// Serial version UID
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
// Creation date
|
||||
private final LocalDateTime date = LocalDateTime.now();
|
||||
|
||||
private String comment = "";
|
||||
private String nick = "";
|
||||
|
||||
/**
|
||||
* Creates a new comment.
|
||||
*
|
||||
* @param comment The new comment.
|
||||
* @param nick The nickname of the comment's author.
|
||||
*/
|
||||
public EntryComment(final String comment, final String nick) {
|
||||
this.comment = comment;
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new comment.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
protected EntryComment() {
|
||||
// Required for serialization
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment.
|
||||
*
|
||||
* @return The comment.
|
||||
*/
|
||||
public final String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment.
|
||||
*
|
||||
* @param comment The actual comment.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setComment(final String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's creation date.
|
||||
*
|
||||
* @return The date.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final LocalDateTime getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nickname of the author of the comment.
|
||||
*
|
||||
* @return The nickname.
|
||||
*/
|
||||
public final String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nickname of the author of the comment.
|
||||
*
|
||||
* @param nick The new nickname.
|
||||
*/
|
||||
public final void setNick(final String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
}
|
|
@ -1,406 +0,0 @@
|
|||
/*
|
||||
* EntryLink.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.entries;
|
||||
|
||||
import com.rometools.rome.feed.synd.SyndCategory;
|
||||
import com.rometools.rome.feed.synd.SyndCategoryImpl;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Constants;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The class used to store link entries.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created Jan 31, 2004
|
||||
* @since 1.0
|
||||
*/
|
||||
public class EntryLink implements Serializable {
|
||||
// Serial version UID
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
// Link's comments
|
||||
private final List<EntryComment> comments = new CopyOnWriteArrayList<>();
|
||||
|
||||
// Tags/categories
|
||||
private final List<SyndCategory> tags = new CopyOnWriteArrayList<>();
|
||||
|
||||
// Channel
|
||||
private String channel;
|
||||
|
||||
// Creation date
|
||||
private Date date = Calendar.getInstance().getTime();
|
||||
|
||||
// Link's URL
|
||||
private String link;
|
||||
|
||||
// Author's login
|
||||
private String login = "";
|
||||
|
||||
// Author's nickname
|
||||
private String nick;
|
||||
|
||||
// Link's title
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*
|
||||
* @param link The new entry's link.
|
||||
* @param title The new entry's title.
|
||||
* @param nick The nickname of the author of the link.
|
||||
* @param login The login of the author of the link.
|
||||
* @param channel The channel.
|
||||
* @param tags The entry's tags/categories.
|
||||
*/
|
||||
public EntryLink(final String link,
|
||||
final String title,
|
||||
final String nick,
|
||||
final String login,
|
||||
final String channel,
|
||||
final String tags) {
|
||||
this.link = link;
|
||||
this.title = title;
|
||||
this.nick = nick;
|
||||
this.login = login;
|
||||
this.channel = channel;
|
||||
|
||||
setTags(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entry.
|
||||
*
|
||||
* @param link The new entry's link.
|
||||
* @param title The new entry's title.
|
||||
* @param nick The nickname of the author of the link.
|
||||
* @param channel The channel.
|
||||
* @param date The entry date.
|
||||
* @param tags The entry's tags/categories.
|
||||
*/
|
||||
public EntryLink(final String link,
|
||||
final String title,
|
||||
final String nick,
|
||||
final String channel,
|
||||
final Date date,
|
||||
final List<SyndCategory> tags) {
|
||||
this.link = link;
|
||||
this.title = title;
|
||||
this.nick = nick;
|
||||
this.channel = channel;
|
||||
this.date = new Date(date.getTime());
|
||||
|
||||
setTags(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new comment.
|
||||
*
|
||||
* @param comment The actual comment.
|
||||
* @param nick The nickname of the author of the comment.
|
||||
* @return The total number of comments for this entry.
|
||||
*/
|
||||
public final int addComment(final String comment, final String nick) {
|
||||
comments.add(new EntryComment(comment, nick));
|
||||
|
||||
return (comments.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific comment.
|
||||
*
|
||||
* @param index The index of the comment to delete.
|
||||
*/
|
||||
public final void deleteComment(final int index) {
|
||||
if (index < comments.size()) {
|
||||
comments.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel the link was posted on.
|
||||
*
|
||||
* @return The channel
|
||||
*/
|
||||
public final String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel.
|
||||
*
|
||||
* @param channel The channel.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setChannel(final String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comment.
|
||||
*
|
||||
* @param index The comment's index.
|
||||
* @return The specific comment.
|
||||
*/
|
||||
public final EntryComment getComment(final int index) {
|
||||
return (comments.get(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the comments.
|
||||
*
|
||||
* @return The comments.
|
||||
*/
|
||||
public final EntryComment[] getComments() {
|
||||
return (comments.toArray(new EntryComment[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of comments.
|
||||
*
|
||||
* @return The count of comments.
|
||||
*/
|
||||
public final int getCommentsCount() {
|
||||
return comments.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's creation date.
|
||||
*
|
||||
* @return The date.
|
||||
*/
|
||||
public final Date getDate() {
|
||||
return new Date(date.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's link.
|
||||
*
|
||||
* @return The link.
|
||||
*/
|
||||
public final String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's link.
|
||||
*
|
||||
* @param link The new link.
|
||||
*/
|
||||
public final void setLink(final String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's author login.
|
||||
*
|
||||
* @return The login;
|
||||
*/
|
||||
public final String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's author login.
|
||||
*
|
||||
* @param login The new login.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public final void setLogin(final String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's author nickname.
|
||||
*
|
||||
* @return The nickname.
|
||||
*/
|
||||
public final String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's author nickname.
|
||||
*
|
||||
* @param nick The new nickname.
|
||||
*/
|
||||
public final void setNick(final String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags formatted for pinboard.in
|
||||
*
|
||||
* @return The tags as a comma-delimited string.
|
||||
*/
|
||||
public final String getPinboardTags() {
|
||||
final StringBuilder pinboardTags = new StringBuilder(nick);
|
||||
|
||||
for (final SyndCategory tag : tags) {
|
||||
pinboardTags.append(',');
|
||||
pinboardTags.append(tag.getName());
|
||||
}
|
||||
|
||||
return pinboardTags.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags.
|
||||
*
|
||||
* @return The tags.
|
||||
*/
|
||||
public final List<SyndCategory> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*
|
||||
* @param tags The space-delimited tags.
|
||||
*/
|
||||
@SuppressFBWarnings("STT_STRING_PARSING_A_FIELD")
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public final void setTags(final String tags) {
|
||||
if (tags != null) {
|
||||
final String[] parts = tags.replace(", ", " ").replace(',', ' ').split(" ");
|
||||
|
||||
SyndCategoryImpl tag;
|
||||
String part;
|
||||
char mod;
|
||||
|
||||
for (final String rawPart : parts) {
|
||||
part = rawPart.trim();
|
||||
|
||||
if (part.length() >= 2) {
|
||||
tag = new SyndCategoryImpl();
|
||||
tag.setName(part.substring(1).toLowerCase(Constants.LOCALE));
|
||||
|
||||
mod = part.charAt(0);
|
||||
|
||||
if (mod == '-') {
|
||||
// Don't remove the channel tag, if any
|
||||
if (!channel.substring(1).equals(tag.getName())) {
|
||||
this.tags.remove(tag);
|
||||
}
|
||||
} else if (mod == '+') {
|
||||
if (!this.tags.contains(tag)) {
|
||||
this.tags.add(tag);
|
||||
}
|
||||
} else {
|
||||
tag.setName(part.trim().toLowerCase(Constants.LOCALE));
|
||||
|
||||
if (!this.tags.contains(tag)) {
|
||||
this.tags.add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tags.
|
||||
*
|
||||
* @param tags The tags.
|
||||
*/
|
||||
final void setTags(final List<SyndCategory> tags) {
|
||||
this.tags.addAll(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comment's title.
|
||||
*
|
||||
* @return The title.
|
||||
*/
|
||||
public final String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment's title.
|
||||
*
|
||||
* @param title The new title.
|
||||
*/
|
||||
public final void setTitle(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry has comments.
|
||||
*
|
||||
* @return true if there are comments, false otherwise.
|
||||
*/
|
||||
public final boolean hasComments() {
|
||||
return (!comments.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry has tags.
|
||||
*
|
||||
* @return true if there are tags, false otherwise.
|
||||
*/
|
||||
public final boolean hasTags() {
|
||||
return (!tags.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* /** Sets a comment.
|
||||
*
|
||||
* @param index The comment's index.
|
||||
* @param comment The actual comment.
|
||||
* @param nick The nickname of the author of the comment.
|
||||
*/
|
||||
public final void setComment(final int index, final String comment, final String nick) {
|
||||
if (index < comments.size()) {
|
||||
comments.set(index, new EntryComment(comment, nick));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*
|
||||
* @return A string representation of the object.
|
||||
*/
|
||||
@Override
|
||||
public final String toString() {
|
||||
|
||||
return super.toString() + "[ channel -> '" + channel + '\'' + ", comments -> " + comments + ", date -> " + date
|
||||
+ ", link -> '" + link + '\'' + ", login -> '" + login + '\'' + ", nick -> '" + nick + '\''
|
||||
+ ", tags -> " + tags + ", title -> '" + title + '\'' + " ]";
|
||||
}
|
||||
}
|
|
@ -1,158 +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 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 List<String> commands = new ArrayList<>();
|
||||
final Map<String, String> properties = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Responds to a command.
|
||||
*
|
||||
* @param bot The bot's instance.
|
||||
* @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 Mobibot bot,
|
||||
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 bot The bot's instance.
|
||||
* @param sender The sender.
|
||||
* @param args The help arguments.
|
||||
* @param isPrivate Set to <code>true</code> if the response should be sent as a private message.
|
||||
*/
|
||||
public abstract void helpResponse(final Mobibot bot,
|
||||
final String sender,
|
||||
final String args,
|
||||
@SuppressWarnings("unused") final boolean isPrivate);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,105 +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 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() {
|
||||
super();
|
||||
commands.add(CALC_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(" ", "") + " = " + 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 Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
bot.send(calc(args));
|
||||
|
||||
} else {
|
||||
helpResponse(bot, sender, args, isPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To solve a mathematical calculation:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CALC_CMD + " <calculation>"));
|
||||
}
|
||||
}
|
|
@ -1,241 +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.NoticeMessage;
|
||||
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.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* The rates keyword.
|
||||
*/
|
||||
static final String CURRENCY_RATES_KEYWORD = "rates";
|
||||
|
||||
// Currency command
|
||||
private static final String CURRENCY_CMD = "currency";
|
||||
// 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() {
|
||||
super();
|
||||
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.
|
||||
* @throws ModuleException If an error occurs while converting.
|
||||
*/
|
||||
static Message convertCurrency(final String query) 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();
|
||||
Element cube;
|
||||
|
||||
for (final Element rawCube : cubes) {
|
||||
cube = rawCube;
|
||||
EXCHANGE_RATES.put(
|
||||
cube.getAttribute("currency").getValue(),
|
||||
cube.getAttribute("rate").getValue());
|
||||
}
|
||||
|
||||
EXCHANGE_RATES.put("EUR", "1");
|
||||
} catch (JDOMException e) {
|
||||
throw new ModuleException(query, "An error has occurred while parsing the exchange rates table.", e);
|
||||
} catch (IOException e) {
|
||||
throw new ModuleException(
|
||||
query, "An error has occurred while fetching the exchange rates table.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (EXCHANGE_RATES.isEmpty()) {
|
||||
return new ErrorMessage("Sorry, but the exchange rate table is empty.");
|
||||
} else {
|
||||
final String[] cmds = query.split(" ");
|
||||
|
||||
if (cmds.length == 4) {
|
||||
if (cmds[3].equals(cmds[1]) || "0".equals(cmds[0])) {
|
||||
return new ErrorMessage("You're kidding, right?");
|
||||
} else {
|
||||
try {
|
||||
final double amt = Double.parseDouble(cmds[0].replace(",", ""));
|
||||
final double from =
|
||||
Double.parseDouble(EXCHANGE_RATES.get(cmds[1].toUpperCase(Constants.LOCALE)));
|
||||
final double to = Double.parseDouble(EXCHANGE_RATES.get(cmds[3].toUpperCase(Constants.LOCALE)));
|
||||
|
||||
return new PublicMessage(
|
||||
NumberFormat.getCurrencyInstance(Locale.US).format(amt).substring(1)
|
||||
+ ' '
|
||||
+ cmds[1].toUpperCase(Constants.LOCALE)
|
||||
+ " = "
|
||||
+ NumberFormat.getCurrencyInstance(Locale.US)
|
||||
.format((amt * to) / from)
|
||||
.substring(1)
|
||||
+ ' '
|
||||
+ cmds[3].toUpperCase(Constants.LOCALE));
|
||||
} catch (Exception e) {
|
||||
throw new ModuleException("convertCurrency(" + query + ')',
|
||||
"The supported currencies are: " + EXCHANGE_RATES.keySet(), e);
|
||||
}
|
||||
}
|
||||
} else if (CURRENCY_RATES_KEYWORD.equals(query)) {
|
||||
|
||||
final StringBuilder buff = new StringBuilder().append('[').append(pubDate).append("]: ");
|
||||
|
||||
int i = 0;
|
||||
for (final Map.Entry<String, String> rate : EXCHANGE_RATES.entrySet()) {
|
||||
if (i > 0) {
|
||||
buff.append(", ");
|
||||
}
|
||||
buff.append(rate.getKey()).append(": ").append(rate.getValue());
|
||||
i++;
|
||||
}
|
||||
|
||||
return new NoticeMessage(buff.toString());
|
||||
}
|
||||
}
|
||||
return new ErrorMessage("The supported currencies are: " + EXCHANGE_RATES.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
synchronized (this) {
|
||||
if (!pubDate.equals(Utils.today())) {
|
||||
EXCHANGE_RATES.clear();
|
||||
}
|
||||
}
|
||||
|
||||
super.commandResponse(bot, sender, cmd, args, isPrivate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified currencies.
|
||||
*/
|
||||
@SuppressFBWarnings("REDOS")
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String query) {
|
||||
if (StringUtils.isNotBlank(sender) && StringUtils.isNotBlank(query)) {
|
||||
if (query.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+")) {
|
||||
try {
|
||||
final Message msg = convertCurrency(query);
|
||||
if (msg.isError()) {
|
||||
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, false);
|
||||
}
|
||||
bot.send(sender, msg);
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
helpResponse(bot, sender, CURRENCY_CMD + ' ' + query, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To convert from one currency to another:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD + " [100 USD to EUR]"));
|
||||
|
||||
if (args.endsWith(CURRENCY_CMD)) {
|
||||
bot.send(sender, "For a listing of currency rates:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD) + ' ' + CURRENCY_RATES_KEYWORD);
|
||||
bot.send(sender, "For a listing of supported currencies:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + CURRENCY_CMD));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +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;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
super();
|
||||
commands.add(DICE_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
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: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of "
|
||||
+ Utils.bold(playerTotal));
|
||||
|
||||
i = r.nextInt(6) + 1;
|
||||
y = r.nextInt(6) + 1;
|
||||
final int total = i + y;
|
||||
|
||||
bot.action(
|
||||
"rolled two dice: " + Utils.bold(i) + " and " + Utils.bold(y) + " for a total of " + Utils.bold(total));
|
||||
|
||||
if (playerTotal < total) {
|
||||
bot.action("wins.");
|
||||
} else if (playerTotal > total) {
|
||||
bot.action("lost.");
|
||||
} else {
|
||||
bot.action("tied.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To roll the dice:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + DICE_CMD));
|
||||
}
|
||||
}
|
|
@ -1,175 +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.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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";
|
||||
// Tab indent (4 spaces)
|
||||
private static final String TAB_INDENT = " ";
|
||||
|
||||
/**
|
||||
* Creates a new {@link GoogleSearch} instance.
|
||||
*/
|
||||
public GoogleSearch() {
|
||||
super();
|
||||
commands.add(GOOGLE_CMD);
|
||||
properties.put(GOOGLE_API_KEY_PROP, "");
|
||||
properties.put(GOOGLE_CSE_KEY_PROP, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
if (isEnabled()) {
|
||||
bot.send(sender, "To search Google:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + GOOGLE_CMD + " <query>"));
|
||||
} else {
|
||||
bot.send(sender, "The Google search module is disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches Google.
|
||||
*/
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String query) {
|
||||
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());
|
||||
}
|
||||
} else {
|
||||
helpResponse(bot, sender, query, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 String q = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
|
||||
|
||||
final URL url =
|
||||
new URL("https://www.googleapis.com/customsearch/v1?key="
|
||||
+ apiKey
|
||||
+ "&cx="
|
||||
+ cseKey
|
||||
+ "&q="
|
||||
+ q
|
||||
+ "&filter=1&num=5&alt=json");
|
||||
final URLConnection conn = url.openConnection();
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
try (final BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
|
||||
final JSONObject json = new JSONObject(sb.toString());
|
||||
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(TAB_INDENT + j.getString("link"), 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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,132 +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.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
super();
|
||||
commands.add(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 URLConnection conn = url.openConnection();
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
try (final BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
|
||||
final JSONObject json = new JSONObject(sb.toString());
|
||||
|
||||
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 Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
new Thread(() -> run(bot, sender, cmd, args)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random joke from <a href="http://www.icndb.com/">The Internet Chuck Norris Database</a>.
|
||||
*/
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String arg) {
|
||||
try {
|
||||
bot.send(Utils.cyan(randomJoke().getMessage()));
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To retrieve a random joke:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + JOKE_CMD));
|
||||
}
|
||||
}
|
|
@ -1,201 +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 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() {
|
||||
super();
|
||||
commands.add(LOOKUP_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Mobibot bot,
|
||||
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) {
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
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(bot, sender, args, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To perform a DNS lookup query:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + 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;
|
||||
}
|
||||
}
|
|
@ -1,100 +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 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() {
|
||||
super();
|
||||
commands.add(PING_CMD);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
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())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To ping the bot:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + PING_CMD));
|
||||
}
|
||||
}
|
|
@ -1,229 +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 okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
super();
|
||||
commands.add(STOCK_CMD);
|
||||
properties.put(ALPHAVANTAGE_API_KEY_PROP, "");
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "false positive?")
|
||||
private static JSONObject getJsonResponse(final Response response, final String debugMessage)
|
||||
throws IOException, ModuleException {
|
||||
if (response.isSuccessful()) {
|
||||
if (response.body() != null) {
|
||||
final JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string());
|
||||
|
||||
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, "Invalid Response (" + response.code() + ')');
|
||||
}
|
||||
} 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<>();
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
try {
|
||||
// Search for symbol/keywords
|
||||
Request request = new Request.Builder().url(
|
||||
ALAPHAVANTAGE_URL + "SYMBOL_SEARCH&keywords=" + symbol + "&apikey=" + apiKey).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
|
||||
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
|
||||
request = new Request.Builder().url(
|
||||
ALAPHAVANTAGE_URL + "GLOBAL_QUOTE&symbol=" + symbolInfo.getString("1. symbol") + "&apikey="
|
||||
+ apiKey).build();
|
||||
response = client.newCall(request).execute();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To retrieve a stock quote:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + STOCK_CMD + " <symbol|keywords>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified stock quote from Alpha Advantage.
|
||||
*/
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String symbol) {
|
||||
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(bot, sender, symbol, true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +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 net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage;
|
||||
import twitter4j.DirectMessage;
|
||||
import twitter4j.Status;
|
||||
import twitter4j.TwitterFactory;
|
||||
import twitter4j.conf.ConfigurationBuilder;
|
||||
|
||||
/**
|
||||
* 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 CONSUMER_KEY_PROP = "twitter-consumerKey";
|
||||
static final String CONSUMER_SECRET_PROP = "twitter-consumerSecret";
|
||||
static final String TOKEN_PROP = "twitter-token";
|
||||
static final String TOKEN_SECRET_PROP = "twitter-tokenSecret";
|
||||
// Twitter command
|
||||
private static final String TWITTER_CMD = "twitter";
|
||||
|
||||
/**
|
||||
* Creates a new {@link Twitter} instance.
|
||||
*/
|
||||
public Twitter() {
|
||||
super();
|
||||
commands.add(TWITTER_CMD);
|
||||
properties.put(CONSUMER_SECRET_PROP, "");
|
||||
properties.put(CONSUMER_KEY_PROP, "");
|
||||
properties.put(TOKEN_PROP, "");
|
||||
properties.put(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
if (isEnabled()) {
|
||||
bot.send(sender, "To post to Twitter:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TWITTER_CMD + " <message>"));
|
||||
} else {
|
||||
bot.send(sender, "The Twitter posting facility is disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts to twitter.
|
||||
*/
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String message) {
|
||||
try {
|
||||
bot.send(sender,
|
||||
post(sender, message + " (by " + sender + " on " + bot.getChannel() + ')', false).getMessage());
|
||||
} catch (ModuleException e) {
|
||||
bot.getLogger().warn(e.getDebugMessage(), e);
|
||||
bot.send(sender, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* War.java
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -32,77 +32,82 @@
|
|||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static net.thauvin.erik.mobibot.Utils.bold;
|
||||
|
||||
/**
|
||||
* The War module.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-28
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class War extends AbstractModule {
|
||||
// Random
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
// War command
|
||||
private static final String WAR_CMD = "war";
|
||||
// Deck of card
|
||||
private static final String[] WAR_DECK =
|
||||
new String[]{ "Ace", "King", "Queen", "Jack", "10", "9", "8", "7", "6", "5", "4", "3", "2" };
|
||||
// Suits for the deck of card
|
||||
private static final String[] WAR_SUITS = new String[]{ "Hearts", "Spades", "Diamonds", "Clubs" };
|
||||
|
||||
private static final String[] HEARTS =
|
||||
{"🂱", "🂾", "🂽", "🂼", "🂻", "🂺", "🂹", "🂸", "🂷", "🂶", "🂵", "🂴", "🂳", "🂲"};
|
||||
private static final String[] SPADES =
|
||||
{"🂡", "🂮", "🂭", "🂬", "🂫", "🂪", "🂩", "🂨", "🂧", "🂦", "🂥", "🂤", "🂣", "🂢"};
|
||||
private static final String[] DIAMONDS =
|
||||
{"🃁", "🃎", "🃍", "🃌", "🃋", "🃊", "🃉", "🃈", "🃇", "🃆", "🃅", "🃄", "🃃", "🃂"};
|
||||
private static final String[] CLUBS =
|
||||
{"🃑", "🃞", "🃝", "🃜", "🃛", "🃚", "🃙", "🃘", "🃗", "🃖", "🃕", "🃔", "🃓", "🃒"};
|
||||
|
||||
private static final String[][] DECK = {HEARTS, SPADES, DIAMONDS, CLUBS};
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
public War() {
|
||||
super();
|
||||
|
||||
commands.add(WAR_CMD);
|
||||
|
||||
help.add("To play war:");
|
||||
help.add(Utils.helpFormat("%c " + WAR_CMD));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "War";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
final SecureRandom r = new SecureRandom();
|
||||
|
||||
public void commandResponse(@NotNull final String channel, @NotNull final String cmd, @NotNull final String args,
|
||||
@NotNull final GenericMessageEvent event) {
|
||||
int i;
|
||||
int y;
|
||||
|
||||
while (true) {
|
||||
i = r.nextInt(WAR_DECK.length);
|
||||
y = r.nextInt(WAR_DECK.length);
|
||||
i = RANDOM.nextInt(HEARTS.length);
|
||||
y = RANDOM.nextInt(HEARTS.length);
|
||||
|
||||
bot.send(bot.getChannel(),
|
||||
sender + " drew the " + Utils.bold(WAR_DECK[i]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
|
||||
bot.action("drew the " + Utils.bold(WAR_DECK[y]) + " of " + WAR_SUITS[r.nextInt(WAR_SUITS.length)]);
|
||||
event.respond("you drew " + DECK[RANDOM.nextInt(DECK.length)][i]);
|
||||
event.getBot().sendIRC().action(channel, "drew " + DECK[RANDOM.nextInt(DECK.length)][y]);
|
||||
|
||||
if (i != y) {
|
||||
break;
|
||||
}
|
||||
|
||||
bot.send("This means " + Utils.bold("WAR") + '!');
|
||||
event.respond("This means " + bold("WAR") + '!');
|
||||
}
|
||||
|
||||
if (i < y) {
|
||||
bot.action("lost.");
|
||||
event.getBot().sendIRC().action(channel, "lost.");
|
||||
} else {
|
||||
bot.action("wins.");
|
||||
event.getBot().sendIRC().action(channel, "wins.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To play war:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WAR_CMD));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,244 +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 okhttp3.HttpUrl;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jibble.pircbot.Colors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
super();
|
||||
commands.add(WEATHER_CMD);
|
||||
properties.put(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) + " \u00B0F, " + Math.round(c) + " \u00B0C";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() + " [" + 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) {
|
||||
if (condition.indexOf(",") == -1) {
|
||||
condition.append(StringUtils.capitalize(w.getDescription()));
|
||||
} else {
|
||||
condition.append(", ").append(w.getDescription());
|
||||
}
|
||||
}
|
||||
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 {
|
||||
final HttpUrl url = Objects.requireNonNull(HttpUrl.parse(
|
||||
"https://openweathermap.org/find"))
|
||||
.newBuilder()
|
||||
.addQueryParameter("q",
|
||||
city + ',' + country)
|
||||
.build();
|
||||
messages.add(new NoticeMessage(url.toString(), 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To display weather information:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " <city> [, <country code>]"));
|
||||
bot.send(sender, "For example:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + WEATHER_CMD + " paris, fr"));
|
||||
bot.send(sender,
|
||||
"The default ISO 3166 country code is " + Utils.bold("US")
|
||||
+ ". Zip codes are supported in most countries.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the weather data from a specific city.
|
||||
*/
|
||||
@Override
|
||||
void run(final Mobibot bot, final String sender, final String cmd, final String args) {
|
||||
if (StringUtils.isNotBlank(args)) {
|
||||
try {
|
||||
final List<Message> messages = getWeather(args, properties.get(OWM_API_KEY_PROP));
|
||||
if (messages.get(0).isError()) {
|
||||
helpResponse(bot, sender, args, true);
|
||||
} 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(bot, sender, args, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static String wind(final Double w) {
|
||||
final double kmh = w * 1.60934;
|
||||
return Math.round(w) + " mph, " + Math.round(kmh) + " km/h";
|
||||
}
|
||||
}
|
|
@ -1,249 +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.Constants;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage;
|
||||
import net.thauvin.erik.mobibot.msg.Message;
|
||||
import net.thauvin.erik.mobibot.msg.PublicMessage;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoField;
|
||||
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() {
|
||||
super();
|
||||
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((query.substring(query.indexOf(' ') + 1).trim()
|
||||
.toUpperCase(Constants.LOCALE))));
|
||||
final String response;
|
||||
|
||||
if (tz != null) {
|
||||
if (BEATS_KEYWORD.equals(tz)) {
|
||||
response = ("The current Internet Time is: " + internetTime() + ' ' + BEATS_KEYWORD);
|
||||
} else {
|
||||
response = ZonedDateTime.now().withZoneSameInstant(ZoneId.of(tz)).format(
|
||||
DateTimeFormatter.ofPattern("'The time is 'HH:mm' on 'EEEE, d MMMM yyyy' in '"))
|
||||
+ tz.substring(tz.indexOf('/') + 1).replace('_', ' ');
|
||||
}
|
||||
} else {
|
||||
return new ErrorMessage("The supported countries/zones are: " + COUNTRIES_MAP.keySet());
|
||||
}
|
||||
|
||||
return new PublicMessage(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
final Message msg = worldTime(args);
|
||||
|
||||
if (isPrivate) {
|
||||
bot.send(sender, msg.getMessage(), true);
|
||||
} else {
|
||||
if (msg.isError()) {
|
||||
bot.send(sender, msg.getMessage());
|
||||
} else {
|
||||
bot.send(msg.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void helpResponse(final Mobibot bot, final String sender, final String args, final boolean isPrivate) {
|
||||
bot.send(sender, "To display a country's current date/time:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD) + " [<country code>]");
|
||||
|
||||
bot.send(sender, "For a listing of the supported countries:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TIME_CMD));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isPrivateMsgEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
/*
|
||||
* Message.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.msg;
|
||||
|
||||
import org.jibble.pircbot.Colors;
|
||||
|
||||
/**
|
||||
* The <code>Message</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Message {
|
||||
private String color = Colors.NORMAL;
|
||||
private boolean isError;
|
||||
private boolean isNotice;
|
||||
private boolean isPrivate;
|
||||
private String msg = "";
|
||||
|
||||
/**
|
||||
* Creates a new message.
|
||||
*/
|
||||
public Message() {
|
||||
// This constructor is intentionally empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param isNotice The notice flag.
|
||||
* @param isError The error flag.
|
||||
* @param isPrivate The Private message
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Message(final String message, final boolean isNotice, final boolean isError, final boolean isPrivate) {
|
||||
msg = message;
|
||||
this.isNotice = isNotice;
|
||||
this.isError = isError;
|
||||
this.isPrivate = isPrivate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param isNotice The notice flag.
|
||||
* @param isError The error flag.
|
||||
* @param isPrivate The Private message
|
||||
* @param color The color.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Message(final String message,
|
||||
final boolean isNotice,
|
||||
final boolean isError,
|
||||
final boolean isPrivate,
|
||||
final String color) {
|
||||
msg = message;
|
||||
this.isNotice = isNotice;
|
||||
this.isError = isError;
|
||||
this.isPrivate = isPrivate;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message color.
|
||||
*
|
||||
* @return The color.
|
||||
*/
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message.
|
||||
*
|
||||
* @return The message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message error flag.
|
||||
*
|
||||
* @return The error flag.
|
||||
*/
|
||||
public boolean isError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message notice flag.
|
||||
*
|
||||
* @return The notice flag.
|
||||
*/
|
||||
public boolean isNotice() {
|
||||
return isNotice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message private flag.
|
||||
*
|
||||
* @return The private flag.
|
||||
*/
|
||||
public boolean isPrivate() {
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color.
|
||||
*
|
||||
* @param color The new color.
|
||||
*/
|
||||
public void setColor(final String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message error flag.
|
||||
*
|
||||
* @param error The error flag.
|
||||
*/
|
||||
public void setError(final boolean error) {
|
||||
isError = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message.
|
||||
*
|
||||
* @param message The new message.
|
||||
*/
|
||||
public void setMessage(final String message) {
|
||||
msg = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message notice flag.
|
||||
*
|
||||
* @param isNotice The notice flag.
|
||||
*/
|
||||
public void setNotice(final boolean isNotice) {
|
||||
this.isNotice = isNotice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message private flag.
|
||||
*
|
||||
* @param isPrivate The private flag.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setPrivate(final boolean isPrivate) {
|
||||
this.isPrivate = isPrivate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Message{" + "color='" + color + '\'' + ", isError=" + isError + ", isNotice=" + isNotice
|
||||
+ ", isPrivate=" + isPrivate + ", msg='" + msg + '\'' + '}';
|
||||
}
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
* Tell.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.tell;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import net.thauvin.erik.mobibot.Commands;
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The <code>Tell</code> command.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2016-07-02
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Tell {
|
||||
/**
|
||||
* The tell command.
|
||||
*/
|
||||
public static final String TELL_CMD = "tell";
|
||||
|
||||
// Default maximum number of days to keep messages
|
||||
private static final int DEFAULT_TELL_MAX_DAYS = 7;
|
||||
// Default message max queue size
|
||||
private static final int DEFAULT_TELL_MAX_SIZE = 50;
|
||||
// Serialized object file extension
|
||||
private static final String SER_EXT = ".ser";
|
||||
// All keyword
|
||||
private static final String TELL_ALL_KEYWORD = "all";
|
||||
//T he delete command.
|
||||
private static final String TELL_DEL_KEYWORD = "del";
|
||||
|
||||
// Bot instance
|
||||
private final Mobibot bot;
|
||||
// Maximum number of days to keep messages
|
||||
private final int maxDays;
|
||||
// Message maximum queue size
|
||||
private final int maxSize;
|
||||
// Messages queue
|
||||
private final List<TellMessage> messages = new CopyOnWriteArrayList<>();
|
||||
// Serialized object file
|
||||
private final String serializedObject;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param bot The bot.
|
||||
* @param maxDays Max days.
|
||||
* @param maxSize Max size.
|
||||
*/
|
||||
public Tell(final Mobibot bot, final String maxDays, final String maxSize) {
|
||||
this.bot = bot;
|
||||
this.maxDays = Utils.getIntProperty(maxDays, DEFAULT_TELL_MAX_DAYS);
|
||||
this.maxSize = Utils.getIntProperty(maxSize, DEFAULT_TELL_MAX_SIZE);
|
||||
|
||||
// Load the message queue
|
||||
serializedObject = bot.getLogsDir() + bot.getName() + SER_EXT;
|
||||
messages.addAll(TellMessagesMgr.load(serializedObject, bot.getLogger()));
|
||||
|
||||
if (clean()) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*
|
||||
* @return <code>true</code> if the queue was cleaned.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
final boolean clean() {
|
||||
if (bot.getLogger().isDebugEnabled()) {
|
||||
bot.getLogger().debug("Cleaning the messages.");
|
||||
}
|
||||
|
||||
return TellMessagesMgr.clean(messages, maxDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with help.
|
||||
*
|
||||
* @param sender The sender.
|
||||
*/
|
||||
public void helpResponse(final String sender) {
|
||||
bot.send(sender, "To send a message to someone when they join the channel:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + " <nick> <message>"));
|
||||
|
||||
bot.send(sender, "To view queued and sent messages:");
|
||||
bot.send(sender, bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + Commands.VIEW_CMD));
|
||||
|
||||
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays) + Utils.plural(maxDays, " day.", " days."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if enabled.
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return maxSize > 0 && maxDays > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the commands.
|
||||
*
|
||||
* @param sender The sender's nick.
|
||||
* @param cmds The commands string.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "CC_CYCLOMATIC_COMPLEXITY", justification = "Working on it.")
|
||||
public void response(final String sender, final String cmds) {
|
||||
final String arrow = " --> ";
|
||||
if (StringUtils.isBlank(cmds)) {
|
||||
helpResponse(sender);
|
||||
} else if (cmds.startsWith(Commands.VIEW_CMD)) {
|
||||
if (bot.isOp(sender) && (Commands.VIEW_CMD + ' ' + TELL_ALL_KEYWORD).equals(cmds)) {
|
||||
if (!messages.isEmpty()) {
|
||||
for (final TellMessage message : messages) {
|
||||
bot.send(sender, Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
|
||||
+ " [ID: " + message.getId() + ", "
|
||||
+ (message.isReceived() ? "DELIVERED" : "QUEUED") + ']',
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
bot.send(sender, "There are no messages in the queue.", true);
|
||||
}
|
||||
} else {
|
||||
boolean hasMessage = false;
|
||||
|
||||
for (final TellMessage message : messages) {
|
||||
if (message.isMatch(sender)) {
|
||||
if (!hasMessage) {
|
||||
hasMessage = true;
|
||||
bot.send(sender, "Here are your messages: ", true);
|
||||
}
|
||||
|
||||
if (message.isReceived()) {
|
||||
bot.send(sender,
|
||||
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
|
||||
+ " [" + Utils.utcDateTime(message.getReceived()) + ", ID: "
|
||||
+ message.getId() + ", DELIVERED]",
|
||||
true);
|
||||
|
||||
} else {
|
||||
bot.send(sender,
|
||||
Utils.bold(message.getSender()) + arrow + Utils.bold(message.getRecipient())
|
||||
+ " [" + Utils.utcDateTime(message.getQueued()) + ", ID: "
|
||||
+ message.getId() + ", QUEUED]",
|
||||
true);
|
||||
}
|
||||
|
||||
bot.send(sender, bot.helpIndent(message.getMessage(), false), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasMessage) {
|
||||
bot.send(sender, "You have no messages in the queue.", true);
|
||||
} else {
|
||||
bot.send(sender, "To delete one or all delivered messages:");
|
||||
bot.send(sender,
|
||||
bot.helpIndent(bot.getNick() + ": " + TELL_CMD + ' ' + TELL_DEL_KEYWORD + " <id|"
|
||||
+ TELL_ALL_KEYWORD + '>'));
|
||||
bot.send(sender, "Messages are kept for " + Utils.bold(maxDays)
|
||||
+ Utils.plural(maxDays, " day.", " days."));
|
||||
}
|
||||
}
|
||||
} else if (cmds.startsWith(TELL_DEL_KEYWORD + ' ')) {
|
||||
final String[] split = cmds.split(" ");
|
||||
|
||||
if (split.length == 2) {
|
||||
final String id = split[1];
|
||||
boolean deleted = false;
|
||||
|
||||
if (TELL_ALL_KEYWORD.equalsIgnoreCase(id)) {
|
||||
for (final TellMessage message : messages) {
|
||||
if (message.getSender().equalsIgnoreCase(sender) && message.isReceived()) {
|
||||
messages.remove(message);
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
save();
|
||||
bot.send(sender, "Delivered messages have been deleted.", true);
|
||||
} else {
|
||||
bot.send(sender, "No delivered messages were found.", true);
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean found = false;
|
||||
|
||||
for (final TellMessage message : messages) {
|
||||
found = message.isMatchId(id);
|
||||
|
||||
if (found && (message.getSender().equalsIgnoreCase(sender) || bot.isOp(sender))) {
|
||||
messages.remove(message);
|
||||
|
||||
save();
|
||||
bot.send(sender, "Your message was deleted from the queue.", true);
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deleted) {
|
||||
if (found) {
|
||||
bot.send(sender, "Only messages that you sent can be deleted.", true);
|
||||
} else {
|
||||
bot.send(sender, "The specified message [ID " + id + "] could not be found.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender);
|
||||
}
|
||||
} else {
|
||||
final String[] split = cmds.split(" ", 2);
|
||||
|
||||
if (split.length == 2 && (StringUtils.isNotBlank(split[1]) && split[1].contains(" "))) {
|
||||
if (messages.size() < maxSize) {
|
||||
final TellMessage message = new TellMessage(sender, split[0], split[1].trim());
|
||||
|
||||
messages.add(message);
|
||||
|
||||
save();
|
||||
|
||||
bot.send(sender, "Message [ID " + message.getId() + "] was queued for "
|
||||
+ Utils.bold(message.getRecipient()), true);
|
||||
} else {
|
||||
bot.send(sender, "Sorry, the messages queue is currently full.", true);
|
||||
}
|
||||
} else {
|
||||
helpResponse(sender);
|
||||
}
|
||||
}
|
||||
|
||||
if (clean()) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages queue.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
final void save() {
|
||||
TellMessagesMgr.save(serializedObject, messages, bot.getLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
* @param isMessage The message flag.
|
||||
*/
|
||||
public void send(final String nickname, final boolean isMessage) {
|
||||
if (!nickname.equals(bot.getNick()) && isEnabled()) {
|
||||
messages.stream().filter(message -> message.isMatch(nickname)).forEach(
|
||||
message -> {
|
||||
if (message.getRecipient().equalsIgnoreCase(nickname) && !message.isReceived()) {
|
||||
if (message.getSender().equals(nickname)) {
|
||||
if (!isMessage) {
|
||||
bot.send(nickname, Utils.bold("You") + " wanted me to remind you: "
|
||||
+ Utils.reverseColor(message.getMessage()),
|
||||
true);
|
||||
|
||||
message.setIsReceived();
|
||||
message.setIsNotified();
|
||||
|
||||
save();
|
||||
}
|
||||
} else {
|
||||
bot.send(nickname, message.getSender() + " wanted me to tell you: "
|
||||
+ Utils.reverseColor(message.getMessage()),
|
||||
true);
|
||||
|
||||
message.setIsReceived();
|
||||
|
||||
save();
|
||||
}
|
||||
} else if (message.getSender().equalsIgnoreCase(nickname) && message.isReceived()
|
||||
&& !message.isNotified()) {
|
||||
bot.send(nickname,
|
||||
"Your message "
|
||||
+ Utils.reverseColor("[ID " + message.getId() + ']') + " was sent to "
|
||||
+ Utils.bold(message.getRecipient()) + " on "
|
||||
+ Utils.utcDateTime(message.getReceived()),
|
||||
true);
|
||||
|
||||
message.setIsNotified();
|
||||
|
||||
save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*
|
||||
* @param nickname The user's nickname.
|
||||
*/
|
||||
public void send(final String nickname) {
|
||||
send(nickname, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the messages queue size.
|
||||
*
|
||||
* @return The size.
|
||||
*/
|
||||
public int size() {
|
||||
return messages.size();
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* TellMessage.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.tell;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* The <code>TellMessage</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-24
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataClass")
|
||||
public class TellMessage implements Serializable {
|
||||
private static final long serialVersionUID = 2L;
|
||||
private final String id;
|
||||
private final String message;
|
||||
private final LocalDateTime queued;
|
||||
private final String recipient;
|
||||
private final String sender;
|
||||
private boolean isNotified;
|
||||
private boolean isReceived;
|
||||
private LocalDateTime received;
|
||||
|
||||
/**
|
||||
* Create a new message.
|
||||
*
|
||||
* @param sender The sender's nick.
|
||||
* @param recipient The recipient's nick.
|
||||
* @param message The message.
|
||||
*/
|
||||
TellMessage(final String sender, final String recipient, final String message) {
|
||||
this.sender = sender;
|
||||
this.recipient = recipient;
|
||||
this.message = message;
|
||||
|
||||
this.queued = LocalDateTime.now(Clock.systemUTC());
|
||||
this.id = this.queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message id.
|
||||
*
|
||||
* @return The message id.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message text.
|
||||
*
|
||||
* @return The text of the message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the queued date/time.
|
||||
*
|
||||
* @return <code>true</code> if the message is queued.
|
||||
*/
|
||||
LocalDateTime getQueued() {
|
||||
return queued;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of the received flag.
|
||||
*
|
||||
* @return <code>true</code> if the message has been received.
|
||||
*/
|
||||
public LocalDateTime getReceived() {
|
||||
return received;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message's recipient.
|
||||
*
|
||||
* @return The recipient of the message.
|
||||
*/
|
||||
String getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message's sender.
|
||||
*
|
||||
* @return The sender of the message.
|
||||
*/
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the message sender or recipient.
|
||||
*
|
||||
* @param nick The nickname to match with.
|
||||
* @return <code>true</code> if the nickname matches.
|
||||
*/
|
||||
boolean isMatch(final String nick) {
|
||||
return (sender.equalsIgnoreCase(nick) || recipient.equalsIgnoreCase(nick));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the message ID.
|
||||
*
|
||||
* @param id The ID to match with.
|
||||
* @return <code>true</code> if the id matches.
|
||||
*/
|
||||
boolean isMatchId(final String id) {
|
||||
return this.id.equals(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification flag state.
|
||||
*
|
||||
* @return <code>true</code> if the sender has been notified.
|
||||
*/
|
||||
boolean isNotified() {
|
||||
return isNotified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the received flag state.
|
||||
*
|
||||
* @return <code>true</code> if the message was received.
|
||||
*/
|
||||
public boolean isReceived() {
|
||||
return isReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the notified flag.
|
||||
*/
|
||||
void setIsNotified() {
|
||||
isNotified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the received flag.
|
||||
*/
|
||||
void setIsReceived() {
|
||||
received = LocalDateTime.now(Clock.systemUTC());
|
||||
isReceived = true;
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* TellMessagesMgr.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.tell;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Tell Messages Manager.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2014-04-26
|
||||
* @since 1.0
|
||||
*/
|
||||
final class TellMessagesMgr {
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the constructor is called.
|
||||
*/
|
||||
private TellMessagesMgr() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*
|
||||
* @param tellMessages The messages list.
|
||||
* @param tellMaxDays The maximum number of days to keep messages for.
|
||||
* @return <code>True</code> if the queue was cleaned.
|
||||
*/
|
||||
static boolean clean(final List<TellMessage> tellMessages, final int tellMaxDays) {
|
||||
final LocalDateTime today = LocalDateTime.now(Clock.systemUTC());
|
||||
|
||||
return tellMessages.removeIf(o -> o.getQueued().plusDays(tellMaxDays).isBefore(today));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the messages.
|
||||
*
|
||||
* @param file The serialized objects file.
|
||||
* @param logger The logger.
|
||||
* @return The {@link TellMessage} array.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<TellMessage> load(final String file, final Logger logger) {
|
||||
try {
|
||||
|
||||
try (final ObjectInput input = new ObjectInputStream(
|
||||
new BufferedInputStream(Files.newInputStream(Paths.get(file))))) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loading the messages.");
|
||||
}
|
||||
|
||||
return ((List<TellMessage>) input.readObject());
|
||||
}
|
||||
} catch (FileNotFoundException ignore) {
|
||||
// Do nothing
|
||||
} catch (IOException e) {
|
||||
logger.error("An IO error occurred loading the messages queue.", e);
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred loading the messages queue.", e);
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages.
|
||||
*
|
||||
* @param file The serialized objects file.
|
||||
* @param messages The {@link TellMessage} array.
|
||||
* @param logger The logger.
|
||||
*/
|
||||
public static void save(final String file, final List<TellMessage> messages, final Logger logger) {
|
||||
try {
|
||||
|
||||
try (final ObjectOutput output = new ObjectOutputStream(
|
||||
new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Saving the messages.");
|
||||
}
|
||||
|
||||
output.writeObject(messages);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to save messages queue.", e);
|
||||
}
|
||||
}
|
||||
}
|
164
src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt
Normal file
164
src/main/kotlin/net/thauvin/erik/mobibot/Addons.kt
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Addons.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.notContains
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import net.thauvin.erik.mobibot.modules.AbstractModule
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.util.Properties
|
||||
|
||||
/**
|
||||
* Modules and Commands addons.
|
||||
*/
|
||||
class Addons(private val props: Properties) {
|
||||
private val disabledModules = props.getProperty("disabled-modules", "").split(LinksMgr.TAG_MATCH.toRegex())
|
||||
private val disableCommands = props.getProperty("disabled-commands", "").split(LinksMgr.TAG_MATCH.toRegex())
|
||||
|
||||
val commands: MutableList<AbstractCommand> = mutableListOf()
|
||||
val modules: MutableList<AbstractModule> = mutableListOf()
|
||||
val names = Names
|
||||
|
||||
/**
|
||||
* Add a module with properties.
|
||||
*/
|
||||
fun add(module: AbstractModule) {
|
||||
with(module) {
|
||||
if (disabledModules.notContains(name, true)) {
|
||||
if (hasProperties()) {
|
||||
propertyKeys.forEach {
|
||||
setProperty(it, props.getProperty(it, ""))
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnabled) {
|
||||
modules.add(this)
|
||||
names.modules.add(name)
|
||||
names.commands.addAll(commands)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a command with properties.
|
||||
*/
|
||||
fun add(command: AbstractCommand) {
|
||||
with(command) {
|
||||
if (disableCommands.notContains(name, true)) {
|
||||
if (properties.isNotEmpty()) {
|
||||
properties.keys.forEach {
|
||||
setProperty(it, props.getProperty(it, ""))
|
||||
}
|
||||
}
|
||||
if (isEnabled()) {
|
||||
commands.add(this)
|
||||
if (isVisible) {
|
||||
if (isOpOnly) {
|
||||
names.ops.add(name)
|
||||
} else {
|
||||
names.commands.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command or module.
|
||||
*/
|
||||
fun exec(channel: String, cmd: String, args: String, event: GenericMessageEvent): Boolean {
|
||||
val cmds = if (event is PrivateMessageEvent) commands else commands.filter { it.isPublic }
|
||||
for (command in cmds) {
|
||||
if (command.name.startsWith(cmd)) {
|
||||
command.commandResponse(channel, args, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
val mods = if (event is PrivateMessageEvent) modules.filter { it.isPrivateMsgEnabled } else modules
|
||||
for (module in mods) {
|
||||
if (module.commands.contains(cmd)) {
|
||||
module.commandResponse(channel, cmd, args, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a command.
|
||||
*/
|
||||
fun match(channel: String, event: GenericMessageEvent): Boolean {
|
||||
for (command in commands) {
|
||||
if (command.matches(event.message)) {
|
||||
command.commandResponse(channel, event.message, event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Commands and Modules help.
|
||||
*/
|
||||
fun help(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
for (command in commands) {
|
||||
if (command.isVisible && command.name.startsWith(topic)) {
|
||||
return command.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
for (module in modules) {
|
||||
if (module.commands.contains(topic)) {
|
||||
return module.helpResponse(event)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds commands and modules names.
|
||||
*/
|
||||
object Names {
|
||||
val modules: MutableList<String> = mutableListOf()
|
||||
val commands: MutableList<String> = mutableListOf()
|
||||
val ops: MutableList<String> = mutableListOf()
|
||||
|
||||
fun sort() {
|
||||
modules.sort()
|
||||
commands.sort()
|
||||
ops.sort()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Constants.java
|
||||
* Constants.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,48 +29,69 @@
|
|||
* 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 java.util.Locale;
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
public final class Constants {
|
||||
object Constants {
|
||||
/**
|
||||
* The connect/read timeout in ms.
|
||||
*/
|
||||
public static final int CONNECT_TIMEOUT = 5000;
|
||||
const val CONNECT_TIMEOUT = 5000
|
||||
|
||||
/**
|
||||
* Default locale.
|
||||
* Debug command line argument.
|
||||
*/
|
||||
public static final Locale LOCALE = Locale.getDefault();
|
||||
const val DEBUG_ARG = "debug"
|
||||
|
||||
/**
|
||||
* Default IRC Port.
|
||||
*/
|
||||
const val DEFAULT_PORT = 6667
|
||||
|
||||
/**
|
||||
* Default IRC Server.
|
||||
*/
|
||||
const val DEFAULT_SERVER = "irc.libera.chat"
|
||||
|
||||
/**
|
||||
* Help command line argument.
|
||||
*/
|
||||
const val HELP_ARG = "help"
|
||||
|
||||
/**
|
||||
* The help command.
|
||||
*/
|
||||
const val HELP_CMD = "help"
|
||||
|
||||
/**
|
||||
* The link command.
|
||||
*/
|
||||
const val LINK_CMD = "L"
|
||||
|
||||
/**
|
||||
* The empty title string.
|
||||
*/
|
||||
public static final String NO_TITLE = "No Title";
|
||||
const val NO_TITLE = "No Title"
|
||||
|
||||
/**
|
||||
* Properties command line argument.
|
||||
*/
|
||||
const val PROPS_ARG = "properties"
|
||||
|
||||
/**
|
||||
* The tag command
|
||||
*/
|
||||
const val TAG_CMD = "T"
|
||||
|
||||
/**
|
||||
* The timer delay in minutes.
|
||||
*/
|
||||
public static final long TIMER_DELAY = 10L;
|
||||
/**
|
||||
* The twitter post flag property key.
|
||||
*/
|
||||
public static final String TWITTER_AUTOPOST_PROP = "twitter-auto-post";
|
||||
/**
|
||||
* The Twitter handle property key.
|
||||
*/
|
||||
public static final String TWITTER_HANDLE_PROP = "twitter-handle";
|
||||
const val TIMER_DELAY = 10L
|
||||
|
||||
/**
|
||||
* Disables the default constructor.
|
||||
* Properties version line argument.
|
||||
*/
|
||||
private Constants() {
|
||||
throw new UnsupportedOperationException("Illegal constructor call.");
|
||||
}
|
||||
const val VERSION_ARG = "version"
|
||||
}
|
93
src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt
Normal file
93
src/main/kotlin/net/thauvin/erik/mobibot/FeedReader.kt
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* FeedReader.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.FeedException
|
||||
import com.rometools.rome.io.SyndFeedInput
|
||||
import com.rometools.rome.io.XmlReader
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.entries.FeedsMgr
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.NoticeMessage
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Reads an RSS feed.
|
||||
*/
|
||||
class FeedReader(private val url: String, val event: GenericMessageEvent) : Runnable {
|
||||
private val logger: Logger = LoggerFactory.getLogger(FeedsMgr::class.java)
|
||||
|
||||
/**
|
||||
* Fetches the Feed's items.
|
||||
*/
|
||||
override fun run() {
|
||||
try {
|
||||
readFeed(url).forEach {
|
||||
event.sendMessage("", it)
|
||||
}
|
||||
} catch (e: FeedException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to parse the feed at $url", e)
|
||||
event.sendMessage("An error has occurred while parsing the feed: ${e.message}")
|
||||
} catch (e: IOException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to fetch the feed at $url", e)
|
||||
event.sendMessage("An error has occurred while fetching the feed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(FeedException::class, IOException::class)
|
||||
fun readFeed(url: String, maxItems: Int = 5): List<Message> {
|
||||
val messages = mutableListOf<Message>()
|
||||
val input = SyndFeedInput()
|
||||
XmlReader(URL(url)).use { reader ->
|
||||
val feed = input.build(reader)
|
||||
val items = feed.entries
|
||||
if (items.isEmpty()) {
|
||||
messages.add(NoticeMessage("There is currently nothing to view."))
|
||||
} else {
|
||||
items.take(maxItems).forEach {
|
||||
messages.add(NoticeMessage(it.title))
|
||||
messages.add(NoticeMessage(helpFormat(it.link.green(), false)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
}
|
||||
}
|
441
src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt
Normal file
441
src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* Mobibot.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.appendIfMissing
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.getIntProperty
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.lastOrEmpty
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||
import net.thauvin.erik.mobibot.commands.ChannelFeed
|
||||
import net.thauvin.erik.mobibot.commands.Cycle
|
||||
import net.thauvin.erik.mobibot.commands.Die
|
||||
import net.thauvin.erik.mobibot.commands.Ignore
|
||||
import net.thauvin.erik.mobibot.commands.Info
|
||||
import net.thauvin.erik.mobibot.commands.Me
|
||||
import net.thauvin.erik.mobibot.commands.Modules
|
||||
import net.thauvin.erik.mobibot.commands.Msg
|
||||
import net.thauvin.erik.mobibot.commands.Nick
|
||||
import net.thauvin.erik.mobibot.commands.Recap
|
||||
import net.thauvin.erik.mobibot.commands.Recap.Companion.storeRecap
|
||||
import net.thauvin.erik.mobibot.commands.Say
|
||||
import net.thauvin.erik.mobibot.commands.Users
|
||||
import net.thauvin.erik.mobibot.commands.Versions
|
||||
import net.thauvin.erik.mobibot.commands.links.Comment
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import net.thauvin.erik.mobibot.commands.links.Posting
|
||||
import net.thauvin.erik.mobibot.commands.links.Tags
|
||||
import net.thauvin.erik.mobibot.commands.links.View
|
||||
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||
import net.thauvin.erik.mobibot.modules.Calc
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter
|
||||
import net.thauvin.erik.mobibot.modules.Dice
|
||||
import net.thauvin.erik.mobibot.modules.GoogleSearch
|
||||
import net.thauvin.erik.mobibot.modules.Joke
|
||||
import net.thauvin.erik.mobibot.modules.Lookup
|
||||
import net.thauvin.erik.mobibot.modules.Ping
|
||||
import net.thauvin.erik.mobibot.modules.RockPaperScissors
|
||||
import net.thauvin.erik.mobibot.modules.StockQuote
|
||||
import net.thauvin.erik.mobibot.modules.War
|
||||
import net.thauvin.erik.mobibot.modules.Weather2
|
||||
import net.thauvin.erik.mobibot.modules.WorldTime
|
||||
import net.thauvin.erik.semver.Version
|
||||
import org.apache.commons.cli.CommandLine
|
||||
import org.apache.commons.cli.CommandLineParser
|
||||
import org.apache.commons.cli.DefaultParser
|
||||
import org.apache.commons.cli.HelpFormatter
|
||||
import org.apache.commons.cli.Option
|
||||
import org.apache.commons.cli.Options
|
||||
import org.apache.commons.cli.ParseException
|
||||
import org.pircbotx.Configuration
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.ListenerAdapter
|
||||
import org.pircbotx.hooks.events.ActionEvent
|
||||
import org.pircbotx.hooks.events.DisconnectEvent
|
||||
import org.pircbotx.hooks.events.JoinEvent
|
||||
import org.pircbotx.hooks.events.MessageEvent
|
||||
import org.pircbotx.hooks.events.NickChangeEvent
|
||||
import org.pircbotx.hooks.events.PartEvent
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.Properties
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Version(properties = "version.properties", className = "ReleaseInfo", template = "version.mustache", type = "kt")
|
||||
class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Properties) : ListenerAdapter() {
|
||||
// The bot configuration.
|
||||
private val config: Configuration
|
||||
|
||||
// Commands and Modules
|
||||
private val addons: Addons
|
||||
|
||||
// Tell module
|
||||
private val tell: Tell
|
||||
|
||||
/** Logger. */
|
||||
val logger: Logger = LoggerFactory.getLogger(Mobibot::class.java)
|
||||
|
||||
/**
|
||||
* Connects to the server and joins the channel.
|
||||
*/
|
||||
fun connect() {
|
||||
PircBotX(config).startBot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the default help.
|
||||
*/
|
||||
private fun helpDefault(event: GenericMessageEvent) {
|
||||
event.sendMessage("Type a URL on $channel to post it.")
|
||||
event.sendMessage("For more information on a specific command, type:")
|
||||
event.sendMessage(
|
||||
Utils.helpFormat(
|
||||
Utils.buildCmdSyntax(
|
||||
"%c ${Constants.HELP_CMD} <command>",
|
||||
event.bot().nick,
|
||||
event is PrivateMessageEvent
|
||||
)
|
||||
),
|
||||
)
|
||||
event.sendMessage("The commands are:")
|
||||
event.sendList(addons.names.commands, 8, isBold = true, isIndent = true)
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.sendMessage("The op commands are:")
|
||||
event.sendList(addons.names.ops, 8, isBold = true, isIndent = true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds with the default, commands or modules help.
|
||||
*/
|
||||
private fun helpResponse(event: GenericMessageEvent, topic: String) {
|
||||
if (topic.isBlank() || !addons.help(channel, topic.lowercase().trim(), event)) {
|
||||
helpDefault(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAction(event: ActionEvent?) {
|
||||
event?.channel?.let {
|
||||
if (channel == it.name) {
|
||||
event.user?.let { user ->
|
||||
storeRecap(user.nick, event.action, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisconnect(event: DisconnectEvent?) {
|
||||
event?.let {
|
||||
with(event.getBot<PircBotX>()) {
|
||||
LinksMgr.twitter.notification("$nick disconnected from irc://$serverHostname")
|
||||
}
|
||||
}
|
||||
LinksMgr.twitter.shutdown()
|
||||
}
|
||||
|
||||
override fun onPrivateMessage(event: PrivateMessageEvent?) {
|
||||
event?.user?.let { user ->
|
||||
if (logger.isTraceEnabled) logger.trace("<<< ${user.nick}: ${event.message}")
|
||||
val cmds = event.message.trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = cmds.lastOrEmpty().trim()
|
||||
if (cmd.startsWith(Constants.HELP_CMD)) { // help
|
||||
helpResponse(event, args)
|
||||
} else if (!addons.exec(channel, cmd, args, event)) { // Execute command or module
|
||||
helpDefault(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onJoin(event: JoinEvent?) {
|
||||
event?.user?.let { user ->
|
||||
with(event.getBot<PircBotX>()) {
|
||||
if (user.nick == nick) {
|
||||
LinksMgr.twitter.notification("$nick has joined ${event.channel.name} on irc://$serverHostname")
|
||||
} else {
|
||||
tell.send(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMessage(event: MessageEvent?) {
|
||||
event?.user?.let { user ->
|
||||
val sender = user.nick
|
||||
val message = event.message
|
||||
tell.send(event)
|
||||
if (message.matches("(?i)${Pattern.quote(event.bot().nick)}:.*".toRegex())) { // mobibot: <command>
|
||||
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||
val cmds = message.substring(message.indexOf(':') + 1).trim().split(" ".toRegex(), 2)
|
||||
val cmd = cmds[0].lowercase()
|
||||
val args = cmds.lastOrEmpty().trim()
|
||||
if (cmd.startsWith(Constants.HELP_CMD)) { // mobibot: help
|
||||
helpResponse(event, args)
|
||||
} else {
|
||||
// Execute module or command
|
||||
addons.exec(channel, cmd, args, event)
|
||||
}
|
||||
} else if (addons.match(channel, event)) { // Links, e.g.: https://www.example.com/ or L1: , etc.
|
||||
if (logger.isTraceEnabled) logger.trace(">>> $sender: $message")
|
||||
}
|
||||
storeRecap(sender, message, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNickChange(event: NickChangeEvent?) {
|
||||
event?.let { tell.send(event) }
|
||||
}
|
||||
|
||||
override fun onPart(event: PartEvent?) {
|
||||
event?.user?.let { user ->
|
||||
with(event.getBot<PircBotX>()) {
|
||||
if (user.nick == nick) {
|
||||
LinksMgr.twitter.notification("$nick has left ${event.channel.name} on irc://$serverHostname")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(Exception::class)
|
||||
fun main(args: Array<String>) {
|
||||
// Set up the command line options
|
||||
val options = Options()
|
||||
.addOption(
|
||||
Constants.HELP_ARG.substring(0, 1),
|
||||
Constants.HELP_ARG,
|
||||
false,
|
||||
"print this help message"
|
||||
)
|
||||
.addOption(
|
||||
Constants.DEBUG_ARG.substring(0, 1), Constants.DEBUG_ARG, false,
|
||||
"print debug & logging data directly to the console"
|
||||
)
|
||||
.addOption(
|
||||
Option.builder(Constants.PROPS_ARG.substring(0, 1)).hasArg()
|
||||
.argName("file")
|
||||
.desc("use alternate properties file")
|
||||
.longOpt(Constants.PROPS_ARG).build()
|
||||
)
|
||||
.addOption(
|
||||
Constants.VERSION_ARG.substring(0, 1),
|
||||
Constants.VERSION_ARG,
|
||||
false,
|
||||
"print version info"
|
||||
)
|
||||
|
||||
// Parse the command line
|
||||
val parser: CommandLineParser = DefaultParser()
|
||||
val commandLine: CommandLine
|
||||
try {
|
||||
commandLine = parser.parse(options, args)
|
||||
} catch (e: ParseException) {
|
||||
System.err.println("CLI Parsing failed. Reason: ${e.message}")
|
||||
exitProcess(1)
|
||||
}
|
||||
when {
|
||||
commandLine.hasOption(Constants.HELP_ARG[0]) -> {
|
||||
// Output the usage
|
||||
HelpFormatter().printHelp(Mobibot::class.java.name, options)
|
||||
}
|
||||
commandLine.hasOption(Constants.VERSION_ARG[0]) -> {
|
||||
// Output the version
|
||||
println("${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})")
|
||||
println(ReleaseInfo.WEBSITE)
|
||||
}
|
||||
else -> {
|
||||
// Load the properties
|
||||
val p = Properties()
|
||||
try {
|
||||
Files.newInputStream(
|
||||
Paths.get(commandLine.getOptionValue(Constants.PROPS_ARG[0], "./mobibot.properties"))
|
||||
).use { fis ->
|
||||
p.load(fis)
|
||||
}
|
||||
} catch (ignore: FileNotFoundException) {
|
||||
System.err.println("Unable to find properties file.")
|
||||
exitProcess(1)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open properties file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase())
|
||||
val channel = p.getProperty("channel")
|
||||
val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar)
|
||||
|
||||
// Redirect stdout and stderr
|
||||
if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) {
|
||||
try {
|
||||
val stdout = PrintStream(
|
||||
BufferedOutputStream(
|
||||
FileOutputStream(
|
||||
logsDir + channel.substring(1) + '.' + Utils.today() + ".log", true
|
||||
)
|
||||
), true
|
||||
)
|
||||
System.setOut(stdout)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open output (stdout) log file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
try {
|
||||
val stderr = PrintStream(
|
||||
BufferedOutputStream(
|
||||
FileOutputStream("$logsDir$nickname.err", true)
|
||||
), true
|
||||
)
|
||||
System.setErr(stderr)
|
||||
} catch (ignore: IOException) {
|
||||
System.err.println("Unable to open error (stderr) log file.")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the bot
|
||||
Mobibot(nickname, channel, logsDir, p).connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the bot.
|
||||
*/
|
||||
init {
|
||||
val ircServer = p.getProperty("server", Constants.DEFAULT_SERVER)
|
||||
config = Configuration.Builder().apply {
|
||||
name = nickname
|
||||
login = p.getProperty("login", nickname)
|
||||
realName = p.getProperty("realname", nickname)
|
||||
addServer(
|
||||
ircServer,
|
||||
p.getIntProperty("port", Constants.DEFAULT_PORT)
|
||||
)
|
||||
addAutoJoinChannel(channel)
|
||||
addListener(this@Mobibot)
|
||||
version = "${ReleaseInfo.PROJECT} ${ReleaseInfo.VERSION}"
|
||||
isAutoNickChange = true
|
||||
val identPwd = p.getProperty("ident")
|
||||
if (!identPwd.isNullOrBlank()) {
|
||||
nickservPassword = identPwd
|
||||
}
|
||||
val identNick = p.getProperty("ident-nick")
|
||||
if (!identNick.isNullOrBlank()) {
|
||||
nickservNick = identNick
|
||||
}
|
||||
val identMsg = p.getProperty("ident-msg")
|
||||
if (!identMsg.isNullOrBlank()) {
|
||||
nickservCustomMessage = identMsg
|
||||
}
|
||||
isAutoReconnect = true
|
||||
//socketConnectTimeout = Constants.CONNECT_TIMEOUT
|
||||
//socketTimeout = Constants.CONNECT_TIMEOUT
|
||||
//messageDelay = StaticDelay(500)
|
||||
}.buildConfiguration()
|
||||
|
||||
// Load the current entries
|
||||
with(LinksMgr) {
|
||||
entries.channel = channel
|
||||
entries.ircServer = ircServer
|
||||
entries.logsDir = logsDirPath
|
||||
entries.backlogs = p.getProperty("backlogs", "")
|
||||
entries.load()
|
||||
|
||||
// Set up pinboard
|
||||
pinboard.setApiToken(p.getProperty("pinboard-api-token", ""))
|
||||
}
|
||||
|
||||
addons = Addons(p)
|
||||
|
||||
// Load the commands
|
||||
addons.add(ChannelFeed(channel.removePrefix("#")))
|
||||
addons.add(Comment())
|
||||
addons.add(Cycle())
|
||||
addons.add(Die())
|
||||
addons.add(Ignore())
|
||||
addons.add(LinksMgr())
|
||||
addons.add(Me())
|
||||
addons.add(Modules(addons.names.modules))
|
||||
addons.add(Msg())
|
||||
addons.add(Nick())
|
||||
addons.add(Posting())
|
||||
addons.add(Recap())
|
||||
addons.add(Say())
|
||||
addons.add(Tags())
|
||||
|
||||
// Tell command
|
||||
tell = Tell("${logsDirPath}${nickname}.ser")
|
||||
addons.add(tell)
|
||||
|
||||
addons.add(LinksMgr.twitter)
|
||||
addons.add(Users())
|
||||
addons.add(Versions())
|
||||
addons.add(View())
|
||||
|
||||
// Load the modules
|
||||
addons.add(Calc())
|
||||
addons.add(CryptoPrices())
|
||||
addons.add(CurrencyConverter())
|
||||
addons.add(Dice())
|
||||
addons.add(GoogleSearch())
|
||||
addons.add(Info(tell))
|
||||
addons.add(Joke())
|
||||
addons.add(Lookup())
|
||||
addons.add(Ping())
|
||||
addons.add(RockPaperScissors())
|
||||
addons.add(StockQuote())
|
||||
addons.add(Weather2())
|
||||
addons.add(WorldTime())
|
||||
addons.add(War())
|
||||
|
||||
// Sort the addons
|
||||
addons.names.sort()
|
||||
}
|
||||
}
|
||||
|
125
src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt
Normal file
125
src/main/kotlin/net/thauvin/erik/mobibot/Pinboard.kt
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Pinboard.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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 kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.pinboard.PinboardPoster
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Handles posts to pinboard.in.
|
||||
*/
|
||||
class Pinboard {
|
||||
private val poster = PinboardPoster()
|
||||
|
||||
/**
|
||||
* Adds a pin.
|
||||
*/
|
||||
fun addPin(ircServer: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.addPin(
|
||||
entry.link,
|
||||
entry.title,
|
||||
entry.postedBy(ircServer),
|
||||
entry.pinboardTags,
|
||||
entry.date.toTimestamp()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pinboard API token.
|
||||
*/
|
||||
fun setApiToken(apiToken: String) {
|
||||
poster.apiToken = apiToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a pin.
|
||||
*/
|
||||
fun deletePin(entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
poster.deletePin(entry.link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a pin.
|
||||
*/
|
||||
fun updatePin(ircServer: String, oldUrl: String, entry: EntryLink) {
|
||||
if (poster.apiToken.isNotBlank()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
with(entry) {
|
||||
if (oldUrl != link) {
|
||||
poster.deletePin(oldUrl)
|
||||
}
|
||||
poster.addPin(link, title, entry.postedBy(ircServer), pinboardTags, date.toTimestamp())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date to a UTC timestamp.
|
||||
*/
|
||||
private fun Date.toTimestamp(): String {
|
||||
return ZonedDateTime.ofInstant(
|
||||
toInstant().truncatedTo(ChronoUnit.SECONDS),
|
||||
ZoneId.systemDefault()
|
||||
).format(DateTimeFormatter.ISO_INSTANT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pinboard.in extended attribution line.
|
||||
*/
|
||||
private fun EntryLink.postedBy(ircServer: String): String {
|
||||
return "Posted by $nick on $channel ( $ircServer )"
|
||||
}
|
||||
}
|
||||
|
112
src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt
Normal file
112
src/main/kotlin/net/thauvin/erik/mobibot/TwitterOAuth.kt
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* TwitterOAuth.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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 twitter4j.TwitterException
|
||||
import twitter4j.TwitterFactory
|
||||
import twitter4j.auth.AccessToken
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* The `TwitterOAuth` class.
|
||||
*
|
||||
* Go to [https://developer.twitter.com/en/apps](https://developer.twitter.com/en/apps) to register your bot.
|
||||
*
|
||||
* Then execute:
|
||||
*
|
||||
* `java -cp mobibot.jar net.thauvin.erik.mobibot.TwitterOAuth <consumerKey> <consumerSecret>`
|
||||
*
|
||||
* and follow the prompts/instructions.
|
||||
*
|
||||
* @author [Erik C. Thauvin](https://erik.thauvin.net)
|
||||
* @author [Twitter4J Example](https://twitter4j.org/en/code-examples.html#oauth)
|
||||
*/
|
||||
object TwitterOAuth {
|
||||
/**
|
||||
* Twitter OAuth Client Registration.
|
||||
*
|
||||
* @param args The consumerKey and consumerSecret should be passed as arguments.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(TwitterException::class, IOException::class)
|
||||
fun main(args: Array<String>) {
|
||||
if (args.size == 2) {
|
||||
with(TwitterFactory.getSingleton()) {
|
||||
setOAuthConsumer(args[0], args[1])
|
||||
val requestToken = oAuthRequestToken
|
||||
var accessToken: AccessToken? = null
|
||||
BufferedReader(InputStreamReader(System.`in`)).use { br ->
|
||||
while (null == accessToken) {
|
||||
print(
|
||||
"""
|
||||
Open the following URL and grant access to your account:
|
||||
|
||||
${requestToken.authorizationURL}
|
||||
|
||||
Enter the PIN (if available) or just hit enter. [PIN]: """.trimIndent()
|
||||
)
|
||||
val pin = br.readLine()
|
||||
try {
|
||||
accessToken = if (!pin.isNullOrEmpty()) {
|
||||
getOAuthAccessToken(requestToken, pin)
|
||||
} else {
|
||||
oAuthAccessToken
|
||||
}
|
||||
println(
|
||||
"""
|
||||
Please add the following to the bot's property file:
|
||||
|
||||
twitter-consumerKey=${args[0]}
|
||||
twitter-consumerSecret=${args[1]}
|
||||
twitter-token=${accessToken?.token}
|
||||
twitter-tokenSecret=${accessToken?.tokenSecret}
|
||||
""".trimIndent()
|
||||
)
|
||||
} catch (te: TwitterException) {
|
||||
if (401 == te.statusCode) {
|
||||
println("Unable to get the access token.")
|
||||
} else {
|
||||
te.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println("Usage: ${TwitterOAuth::class.java.name} <consumerKey> <consumerSecret>")
|
||||
}
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* TwitterTimer.kt
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -32,10 +32,11 @@
|
|||
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import java.util.*
|
||||
import net.thauvin.erik.mobibot.modules.Twitter
|
||||
import java.util.TimerTask
|
||||
|
||||
class TwitterTimer(var bot: Mobibot, private var index: Int) : TimerTask() {
|
||||
class TwitterTimer(private var twitter: Twitter, private var index: Int) : TimerTask() {
|
||||
override fun run() {
|
||||
bot.twitterAutoPost(index)
|
||||
twitter.postEntry(index)
|
||||
}
|
||||
}
|
391
src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt
Normal file
391
src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt
Normal file
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Utils.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot
|
||||
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import net.thauvin.erik.mobibot.msg.Message.Companion.DEFAULT_COLOR
|
||||
import org.jsoup.Jsoup
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.io.BufferedReader
|
||||
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.Properties
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
/**
|
||||
* Miscellaneous utilities.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
object Utils {
|
||||
private val searchFlags = arrayOf("%c", "%n")
|
||||
|
||||
/**
|
||||
* Appends a suffix to the end of the String if not present.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.appendIfMissing(suffix: Char): String {
|
||||
return if (last() != suffix) {
|
||||
"$this${suffix}"
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given int bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Int.bold(): String = toString().bold()
|
||||
|
||||
/**
|
||||
* Makes the given long bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Long.bold(): String = toString().bold()
|
||||
|
||||
/**
|
||||
* Makes the given string bold.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.bold(): String = colorize(Colors.BOLD)
|
||||
|
||||
/**
|
||||
* Returns the [PircBotX] instance.
|
||||
*/
|
||||
fun GenericMessageEvent.bot(): PircBotX {
|
||||
return getBot() as PircBotX
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a help command by replacing `%c` with the bot's pub/priv command, and `%n` with the bot's
|
||||
* nick.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String {
|
||||
val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick)
|
||||
return text.replaceEach(searchFlags, replace)
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalize a string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.capitalise(): String = lowercase().replaceFirstChar { it.uppercase() }
|
||||
|
||||
/**
|
||||
* Capitalize words
|
||||
*/
|
||||
fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.capitalise() }
|
||||
|
||||
|
||||
/**
|
||||
* Colorize a string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.colorize(color: String): String {
|
||||
return if (isNullOrEmpty()) {
|
||||
""
|
||||
} else if (color == DEFAULT_COLOR) {
|
||||
this
|
||||
} else if (Colors.BOLD == color || Colors.REVERSE == color) {
|
||||
color + this + color
|
||||
} else {
|
||||
color + this + Colors.NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string cyan.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.cyan(): String = colorize(Colors.CYAN)
|
||||
|
||||
/**
|
||||
* URL encodes the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun encodeUrl(s: String): String = URLEncoder.encode(s, StandardCharsets.UTF_8)
|
||||
|
||||
/**
|
||||
* Returns a property as an int.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Properties.getIntProperty(key: String, defaultValue: Int): Int {
|
||||
return getProperty(key)?.toIntOrDefault(defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string green.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.green(): String = colorize(Colors.DARK_GREEN)
|
||||
|
||||
/**
|
||||
* Returns a formatted help string.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String {
|
||||
val s = if (isBold) help.bold() else help
|
||||
return if (isIndent) s.prependIndent() else s
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the specified user is an operator on the [channel].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isChannelOp(channel: String, event: GenericMessageEvent): Boolean {
|
||||
return event.bot().userChannelDao.getChannel(channel).isOp(event.user)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last item of a list of strings or empty if none.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun List<String>.lastOrEmpty(): String {
|
||||
return if (this.size >= 2) {
|
||||
this.last()
|
||||
} else
|
||||
""
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the list does not contain the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun List<String>.notContains(text: String, ignoreCase: Boolean = false) = this.none { it.equals(text, ignoreCase) }
|
||||
|
||||
/**
|
||||
* Obfuscates the given string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.obfuscate(): String {
|
||||
return if (isNotBlank()) {
|
||||
"x".repeat(length)
|
||||
} else this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plural form of a word, if count > 1.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.plural(count: Long): String {
|
||||
return if (count > 1) "${this}s" else this
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string red.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.red(): String = colorize(Colors.RED)
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.replaceEach(search: Array<out String>, replace: Array<out String>): String {
|
||||
var result = this
|
||||
if (search.size == replace.size) {
|
||||
search.forEachIndexed { i, s ->
|
||||
result = result.replace(s, replace[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given string reverse color.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String?.reverseColor(): String = colorize(Colors.REVERSE)
|
||||
|
||||
/**
|
||||
* Send a formatted commands/modules, etc. list.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.sendList(
|
||||
list: List<String>,
|
||||
maxPerLine: Int,
|
||||
separator: String = " ",
|
||||
isBold: Boolean = false,
|
||||
isIndent: Boolean = false
|
||||
) {
|
||||
var i = 0
|
||||
while (i < list.size) {
|
||||
sendMessage(
|
||||
helpFormat(
|
||||
list.subList(i, list.size.coerceAtMost(i + maxPerLine)).joinToString(separator, truncated = ""),
|
||||
isBold,
|
||||
isIndent
|
||||
),
|
||||
)
|
||||
i += maxPerLine
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a [message].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.sendMessage(channel: String, message: Message) {
|
||||
if (message.isNotice) {
|
||||
bot().sendIRC().notice(user.nick, message.msg.colorize(message.color))
|
||||
} else if (message.isPrivate || this is PrivateMessageEvent || channel.isBlank()) {
|
||||
respondPrivateMessage(message.msg.colorize(message.color))
|
||||
} else {
|
||||
bot().sendIRC().message(channel, message.msg.colorize(message.color))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a response as a private message or notice.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun GenericMessageEvent.sendMessage(message: String) {
|
||||
if (this is PrivateMessageEvent) {
|
||||
respondPrivateMessage(message)
|
||||
} else {
|
||||
bot().sendIRC().notice(user.nick, message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns today's date.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun today(): String = LocalDateTime.now().toIsoLocalDate()
|
||||
|
||||
/**
|
||||
* Converts a string to an int.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun String.toIntOrDefault(defaultValue: Int): Int {
|
||||
return try {
|
||||
toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Date.toIsoLocalDate(): String {
|
||||
return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toIsoLocalDate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date as an ISO local date string.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun LocalDateTime.toIsoLocalDate(): String = format(DateTimeFormatter.ISO_LOCAL_DATE)
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun Date.toUtcDateTime(): String {
|
||||
return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault()).toUtcDateTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified date formatted as `yyyy-MM-dd HH:mm`.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun LocalDateTime.toUtcDateTime(): String = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
|
||||
/**
|
||||
* Converts XML/XHTML entities to plain text.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun unescapeXml(str: String): String = Jsoup.parse(str).text()
|
||||
|
||||
/**
|
||||
* Converts milliseconds to year month week day hour and minutes.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uptime(uptime: Long): String {
|
||||
uptime.toDuration(DurationUnit.MILLISECONDS).toComponents { wholeDays, hours, minutes, _, _ ->
|
||||
val years = wholeDays / 365
|
||||
var days = wholeDays % 365
|
||||
val months = days / 30
|
||||
days %= 30
|
||||
val weeks = days / 7
|
||||
days %= 7
|
||||
|
||||
with(StringBuffer()) {
|
||||
if (years > 0) {
|
||||
append(years).append(" year".plural(years)).append(' ')
|
||||
}
|
||||
if (months > 0) {
|
||||
append(weeks).append(" month".plural(months)).append(' ')
|
||||
}
|
||||
if (weeks > 0) {
|
||||
append(weeks).append(" week".plural(weeks)).append(' ')
|
||||
}
|
||||
if (days > 0) {
|
||||
append(days).append(" day".plural(days)).append(' ')
|
||||
}
|
||||
if (hours > 0) {
|
||||
append(hours).append(" hour".plural(hours.toLong())).append(' ')
|
||||
}
|
||||
|
||||
append(minutes).append(" minute".plural(minutes.toLong()))
|
||||
|
||||
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())) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* AbstractCommand.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
abstract class AbstractCommand {
|
||||
abstract val name: String
|
||||
abstract val help: List<String>
|
||||
abstract val isOpOnly: Boolean
|
||||
abstract val isPublic: Boolean
|
||||
abstract val isVisible: Boolean
|
||||
|
||||
val properties: MutableMap<String, String> = ConcurrentHashMap()
|
||||
|
||||
abstract fun commandResponse(channel: String, args: String, event: GenericMessageEvent)
|
||||
|
||||
open fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (!isOpOnly || isOpOnly == isChannelOp(channel, event)) {
|
||||
for (h in help) {
|
||||
event.sendMessage(
|
||||
buildCmdSyntax(h, event.bot().nick, event is PrivateMessageEvent || !isPublic),
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
open fun initProperties(vararg keys: String) {
|
||||
keys.forEach {
|
||||
properties[it] = ""
|
||||
}
|
||||
}
|
||||
|
||||
open fun isEnabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
open fun matches(message: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
open fun setProperty(key: String, value: String) {
|
||||
properties[key] = value
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* ChannelFeed.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.thauvin.erik.mobibot.FeedReader
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class ChannelFeed(channel: String) : AbstractCommand() {
|
||||
override val name = channel
|
||||
override val help = listOf("To list the last 5 posts from the channel's weblog feed:", helpFormat("%c $channel"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val FEED_PROP = "feed"
|
||||
}
|
||||
|
||||
init {
|
||||
initProperties(FEED_PROP)
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
runBlocking {
|
||||
launch {
|
||||
FeedReader(properties[FEED_PROP]!!, event).run()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabled(): Boolean {
|
||||
return !properties[FEED_PROP].isNullOrBlank()
|
||||
}
|
||||
}
|
64
src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt
Normal file
64
src/main/kotlin/net/thauvin/erik/mobibot/commands/Cycle.kt
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Cycle.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Cycle : AbstractCommand() {
|
||||
private val wait = 10
|
||||
override val name = "cycle"
|
||||
override val help = listOf("To have the bot leave the channel and come back:", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
runBlocking {
|
||||
sendIRC().message(channel, "${event.user.nick} asked me to leave. I'll be back!")
|
||||
userChannelDao.getChannel(channel).send().part()
|
||||
delay(wait * 1000L)
|
||||
sendIRC().joinChannel(channel)
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* LookupTest.java
|
||||
* Die.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -30,39 +30,37 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
import java.util.Arrays;
|
||||
class Die : AbstractCommand() {
|
||||
override val name = "die"
|
||||
override val help = emptyList<String>()
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
|
||||
public class LookupTest {
|
||||
@Test
|
||||
public void testLookupImpl() {
|
||||
AbstractModuleTest.testAbstractModule(new Lookup());
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
if (isChannelOp(channel, event) && (properties[DIE_PROP].isNullOrBlank() || args == properties[DIE_PROP])) {
|
||||
sendIRC().message(channel, "${event.user?.nick} has just signed my death sentence.")
|
||||
stopBotReconnect()
|
||||
sendIRC().quitServer("The Bot is Out There!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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");
|
||||
companion object {
|
||||
/**
|
||||
* Max days property.
|
||||
*/
|
||||
const val DIE_PROP = "die"
|
||||
}
|
||||
|
||||
@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();
|
||||
init {
|
||||
initProperties(DIE_PROP)
|
||||
}
|
||||
}
|
148
src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt
Normal file
148
src/main/kotlin/net/thauvin/erik/mobibot/commands/Ignore.kt
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Ignore.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Ignore : AbstractCommand() {
|
||||
private val me = "me"
|
||||
|
||||
init {
|
||||
initProperties(IGNORE_PROP)
|
||||
}
|
||||
|
||||
override val name = IGNORE_CMD
|
||||
override val help = listOf(
|
||||
"To ignore a link posted to the channel:",
|
||||
helpFormat("https://www.foo.bar %n"),
|
||||
"To check your ignore status:",
|
||||
helpFormat("%c $name"),
|
||||
"To toggle your ignore status:",
|
||||
helpFormat("%c $name $me")
|
||||
)
|
||||
private val helpOp = help.plus(
|
||||
arrayOf("To add/remove nicks from the ignored list:", helpFormat("%c $name <nick> [<nick> ...]"))
|
||||
)
|
||||
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val IGNORE_CMD = "ignore"
|
||||
const val IGNORE_PROP = IGNORE_CMD
|
||||
private val ignored = mutableSetOf<String>()
|
||||
|
||||
@JvmStatic
|
||||
fun isNotIgnored(nick: String): Boolean {
|
||||
return !ignored.contains(nick.lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val isMe = args.trim().equals(me, true)
|
||||
if (isMe || !isChannelOp(channel, event)) {
|
||||
val nick = event.user.nick.lowercase()
|
||||
ignoreNick(nick, isMe, event)
|
||||
} else {
|
||||
ignoreOp(args, event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
return if (isChannelOp(channel, event)) {
|
||||
for (h in helpOp) {
|
||||
event.sendMessage(buildCmdSyntax(h, event.bot().nick, true))
|
||||
}
|
||||
true
|
||||
} else {
|
||||
super.helpResponse(channel, topic, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ignoreNick(sender: String, isMe: Boolean, event: GenericMessageEvent) {
|
||||
if (isMe) {
|
||||
if (ignored.remove(sender)) {
|
||||
event.sendMessage("You are no longer ignored.")
|
||||
} else {
|
||||
ignored.add(sender)
|
||||
event.sendMessage("You are now ignored.")
|
||||
}
|
||||
} else {
|
||||
if (ignored.contains(sender)) {
|
||||
event.sendMessage("You are currently ignored.")
|
||||
} else {
|
||||
event.sendMessage("You are not currently ignored.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ignoreOp(args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotEmpty()) {
|
||||
val nicks = args.lowercase().split(" ")
|
||||
for (nick in nicks) {
|
||||
val ignore = if (me == nick) {
|
||||
nick.lowercase()
|
||||
} else {
|
||||
nick
|
||||
}
|
||||
if (!ignored.remove(ignore)) {
|
||||
ignored.add(ignore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ignored.size > 0) {
|
||||
event.sendMessage("The following nicks are ignored:")
|
||||
event.sendList(ignored.sorted(), 8, isIndent = true)
|
||||
} else {
|
||||
event.sendMessage("No one is currently ${"ignored".bold()}.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (IGNORE_PROP == key) {
|
||||
ignored.addAll(value.split(LinksMgr.TAG_MATCH.toRegex()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
76
src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt
Normal file
76
src/main/kotlin/net/thauvin/erik/mobibot/commands/Info.kt
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Info.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.capitalise
|
||||
import net.thauvin.erik.mobibot.Utils.green
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.uptime
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr
|
||||
import net.thauvin.erik.mobibot.commands.tell.Tell
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.lang.management.ManagementFactory
|
||||
|
||||
class Info(private val tell: Tell) : AbstractCommand() {
|
||||
private val allVersions = listOf(
|
||||
"${ReleaseInfo.PROJECT.capitalise()} ${ReleaseInfo.VERSION} (${ReleaseInfo.WEBSITE.green()})",
|
||||
"Written by ${ReleaseInfo.AUTHOR} (${ReleaseInfo.AUTHOR_URL.green()})"
|
||||
)
|
||||
override val name = "info"
|
||||
override val help = listOf("To view information about the bot:", helpFormat("%c $name"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
event.sendList(allVersions, 1)
|
||||
val info = StringBuilder()
|
||||
info.append("Uptime: ")
|
||||
.append(uptime(ManagementFactory.getRuntimeMXBean().uptime))
|
||||
.append(" [Entries: ")
|
||||
.append(LinksMgr.entries.links.size)
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (tell.isEnabled()) {
|
||||
info.append(", Messages: ").append(tell.size())
|
||||
}
|
||||
if (LinksMgr.twitter.isAutoPost) {
|
||||
info.append(", Twitter: ").append(LinksMgr.twitter.entriesCount())
|
||||
}
|
||||
}
|
||||
info.append(", Recap: ").append(Recap.recaps.size).append(']')
|
||||
event.sendMessage(info.toString())
|
||||
}
|
||||
}
|
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt
Normal file
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Me.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Me.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Me : AbstractCommand() {
|
||||
override val name = "me"
|
||||
override val help = listOf("To have the bot perform an action:", helpFormat("%c $name <action>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().action(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
60
src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt
Normal file
60
src/main/kotlin/net/thauvin/erik/mobibot/commands/Modules.kt
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Modules.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Modules(private val modules: List<String>) : AbstractCommand() {
|
||||
override val name = "modules"
|
||||
override val help = listOf("To view a list of enabled modules:", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (modules.isEmpty()) {
|
||||
event.respondPrivateMessage("There are no enabled modules.")
|
||||
} else {
|
||||
event.respondPrivateMessage("The enabled modules are: ")
|
||||
event.sendList(modules, 7, isIndent = true)
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* ThreadedModule.java
|
||||
* Msg.kt
|
||||
*
|
||||
* Copyright (c) 2004-2020, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -30,36 +30,32 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.thauvin.erik.mobibot.modules;
|
||||
package net.thauvin.erik.mobibot.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Mobibot;
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public abstract class ThreadedModule extends AbstractModule {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commandResponse(final Mobibot bot,
|
||||
final String sender,
|
||||
final String cmd,
|
||||
final String args,
|
||||
final boolean isPrivate) {
|
||||
if (isEnabled() && args.length() > 0) {
|
||||
new Thread(() -> run(bot, sender, cmd, args)).start();
|
||||
} else {
|
||||
helpResponse(bot, sender, args, isPrivate);
|
||||
class Msg : AbstractCommand() {
|
||||
override val name = "msg"
|
||||
override val help = listOf(
|
||||
"To have the bot send a private message to someone:",
|
||||
helpFormat("%c $name <nick> <text>")
|
||||
)
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
val msg = args.split(" ", limit = 2)
|
||||
if (args.length > 2) {
|
||||
event.bot().sendIRC().message(msg[0], msg[1])
|
||||
event.respondPrivateMessage("A message was sent to ${msg[0]}")
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the thread.
|
||||
*/
|
||||
abstract void run(Mobibot bot, String sender, @SuppressWarnings("unused") String cmd, String args);
|
||||
}
|
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt
Normal file
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Nick.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Nick.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Nick : AbstractCommand() {
|
||||
override val name = "nick"
|
||||
override val help = listOf("To change the bot's nickname:", helpFormat("%c $name <new_nick>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().changeNick(args)
|
||||
}
|
||||
}
|
||||
}
|
82
src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt
Normal file
82
src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Recap.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class Recap : AbstractCommand() {
|
||||
override val name = "recap"
|
||||
override val help = listOf(
|
||||
"To list the last 10 public channel messages:",
|
||||
helpFormat("%c $name")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val MAX_RECAPS = 10
|
||||
|
||||
@JvmField
|
||||
val recaps = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* Stores the last 10 public messages and actions.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun storeRecap(sender: String, message: String, isAction: Boolean) {
|
||||
recaps.add(
|
||||
LocalDateTime.now(Clock.systemUTC()).toUtcDateTime()
|
||||
+ " - $sender" + (if (isAction) " " else ": ") + message
|
||||
)
|
||||
if (recaps.size > MAX_RECAPS) {
|
||||
recaps.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (recaps.isNotEmpty()) {
|
||||
for (r in recaps) {
|
||||
event.sendMessage(r)
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("Sorry, nothing to recap.")
|
||||
}
|
||||
}
|
||||
}
|
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt
Normal file
52
src/main/kotlin/net/thauvin/erik/mobibot/commands/Say.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Say.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Say : AbstractCommand() {
|
||||
override val name = "say"
|
||||
override val help = listOf("To have the bot say something on the channel:", helpFormat("%c $name <text>"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.bot().sendIRC().message(channel, args)
|
||||
}
|
||||
}
|
||||
}
|
59
src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt
Normal file
59
src/main/kotlin/net/thauvin/erik/mobibot/commands/Users.kt
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Users.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Users : AbstractCommand() {
|
||||
override val name = "users"
|
||||
override val help = listOf("To list the users present on the channel:", helpFormat("%c $name"))
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val nicks = mutableListOf<String>()
|
||||
val ch = event.bot().userChannelDao.getChannel(channel)
|
||||
ch.users.forEach {
|
||||
if (it.channelsOpIn.contains(ch)) {
|
||||
nicks.add("@${it.nick}")
|
||||
} else {
|
||||
nicks.add(it.nick)
|
||||
}
|
||||
}
|
||||
event.sendList(nicks, 8)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* ErrorMessage.java
|
||||
* Versions.kt
|
||||
*
|
||||
* Copyright (c) 2004-2019, Erik C. Thauvin (erik@thauvin.net)
|
||||
* Copyright (c) 2004-2022, Erik C. Thauvin (erik@thauvin.net)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,41 +29,31 @@
|
|||
* 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.commands
|
||||
|
||||
package net.thauvin.erik.mobibot.msg;
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendList
|
||||
import net.thauvin.erik.mobibot.Utils.toIsoLocalDate
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
/**
|
||||
* The <code>ErrorMessage</code> class.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/" target="_blank">Erik C. Thauvin</a>
|
||||
* @created 2019-04-07
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ErrorMessage extends Message {
|
||||
/**
|
||||
* Creates a new error message.
|
||||
*
|
||||
* @param message The error message.
|
||||
*/
|
||||
public ErrorMessage(final String message) {
|
||||
super();
|
||||
this.setMessage(message);
|
||||
this.setError(true);
|
||||
this.setNotice(true);
|
||||
}
|
||||
class Versions : AbstractCommand() {
|
||||
private val allVersions = listOf(
|
||||
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILDDATE.toIsoLocalDate()})",
|
||||
"Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version")
|
||||
+ " (" + System.getProperty("os.arch") + ')',
|
||||
"Runtime: " + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version")
|
||||
)
|
||||
override val name = "versions"
|
||||
override val help = listOf("To view the versions data (bot, platform, java, etc.):", helpFormat("%c $name"))
|
||||
override val isOpOnly = true
|
||||
override val isPublic = false
|
||||
override val isVisible = true
|
||||
|
||||
/**
|
||||
* Creates a new error message.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param color The message color.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public ErrorMessage(final String message, final String color) {
|
||||
super();
|
||||
this.setMessage(message);
|
||||
this.setError(true);
|
||||
this.setNotice(true);
|
||||
this.setColor(color);
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.sendList(allVersions, 1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Comment.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.buildComment
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Comment : AbstractCommand() {
|
||||
override val name = COMMAND
|
||||
override val help = listOf(
|
||||
"To add a comment:",
|
||||
helpFormat("${Constants.LINK_CMD}1:This is a comment"),
|
||||
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1",
|
||||
"To edit a comment, use its label: ",
|
||||
helpFormat("${Constants.LINK_CMD}1.1:This is an edited comment"),
|
||||
"To delete a comment, use its label and a minus sign: ",
|
||||
helpFormat("${Constants.LINK_CMD}1.1:-")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val COMMAND = "comment"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split("[.:]".toRegex(), 3)
|
||||
val entryIndex = cmds[0].toInt() - 1
|
||||
|
||||
if (entryIndex < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) {
|
||||
val entry: EntryLink = LinksMgr.entries.links[entryIndex]
|
||||
val commentIndex = cmds[1].toInt() - 1
|
||||
if (commentIndex < entry.comments.size) {
|
||||
when (val cmd = cmds[2].trim()) {
|
||||
"" -> showComment(entry, entryIndex, commentIndex, event) // L1.1:
|
||||
"-" -> deleteComment(channel, entry, entryIndex, commentIndex, event) // L1.1:-
|
||||
else -> {
|
||||
if (cmd.startsWith('?')) { // L1.1:?<author>
|
||||
changeAuthor(channel, cmd, entry, entryIndex, commentIndex, event)
|
||||
} else { // L1.1:<comment>
|
||||
setComment(cmd, entry, entryIndex, commentIndex, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean {
|
||||
if (super.helpResponse(channel, topic, event)) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
event.sendMessage("To change a comment's author:")
|
||||
event.sendMessage(helpFormat("${Constants.LINK_CMD}1.1:?<nick>"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("^${Constants.LINK_CMD}[0-9]+\\.[0-9]+:.*".toRegex())
|
||||
}
|
||||
|
||||
private fun changeAuthor(
|
||||
channel: String,
|
||||
cmd: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
if (isChannelOp(channel, event) && cmd.length > 1) {
|
||||
val comment = entry.getComment(commentIndex)
|
||||
comment.nick = cmd.substring(1)
|
||||
event.sendMessage(buildComment(entryIndex, commentIndex, comment))
|
||||
LinksMgr.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the author of this comment for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteComment(
|
||||
channel: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
if (isChannelOp(channel, event) || event.user.nick == entry.getComment(commentIndex).nick) {
|
||||
entry.deleteComment(commentIndex)
|
||||
event.sendMessage("Comment ${buildLinkLabel(entryIndex)}.${commentIndex + 1} removed.")
|
||||
LinksMgr.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to delete this comment for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setComment(
|
||||
cmd: String,
|
||||
entry: EntryLink,
|
||||
entryIndex: Int,
|
||||
commentIndex: Int,
|
||||
event: GenericMessageEvent
|
||||
) {
|
||||
entry.setComment(commentIndex, cmd, event.user.nick)
|
||||
event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||
LinksMgr.entries.save()
|
||||
}
|
||||
|
||||
private fun showComment(entry: EntryLink, entryIndex: Int, commentIndex: Int, event: GenericMessageEvent) {
|
||||
event.sendMessage(buildComment(entryIndex, commentIndex, entry.getComment(commentIndex)))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* LinksMgr.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Pinboard
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.today
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored
|
||||
import net.thauvin.erik.mobibot.entries.Entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import net.thauvin.erik.mobibot.modules.Twitter
|
||||
import org.jsoup.Jsoup
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.io.IOException
|
||||
|
||||
class LinksMgr : AbstractCommand() {
|
||||
private val defaultTags: MutableList<String> = mutableListOf()
|
||||
private val keywords: MutableList<String> = mutableListOf()
|
||||
|
||||
override val name = Constants.LINK_CMD
|
||||
override val help = emptyList<String>()
|
||||
override val isOpOnly = false
|
||||
override val isPublic = false
|
||||
override val isVisible = false
|
||||
|
||||
init {
|
||||
initProperties(TAGS_PROP, KEYWORDS_PROP)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LINK_MATCH = "^[hH][tT][tT][pP](|[sS])://.*"
|
||||
const val KEYWORDS_PROP = "tags-keywords"
|
||||
const val TAGS_PROP = "tags"
|
||||
const val TAG_MATCH = ", *| +"
|
||||
|
||||
/**
|
||||
* Entries array
|
||||
*/
|
||||
@JvmField
|
||||
val entries = Entries()
|
||||
|
||||
/**
|
||||
* Pinboard handler.
|
||||
*/
|
||||
@JvmField
|
||||
val pinboard = Pinboard()
|
||||
|
||||
/**
|
||||
* Twitter handler.
|
||||
*/
|
||||
@JvmField
|
||||
val twitter = Twitter()
|
||||
|
||||
/**
|
||||
* Let the user know if the entries are too old to be modified.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isUpToDate(event: GenericMessageEvent): Boolean {
|
||||
if (entries.lastPubDate != today()) {
|
||||
event.sendMessage("The links are too old to be updated.")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.split(" ".toRegex(), 2)
|
||||
val sender = event.user.nick
|
||||
val botNick = event.bot().nick
|
||||
val login = event.user.login
|
||||
|
||||
if (isNotIgnored(sender) && (cmds.size == 1 || !cmds[1].contains(botNick))) {
|
||||
val link = cmds[0].trim()
|
||||
if (!isDupEntry(link, event)) {
|
||||
var title = ""
|
||||
val tags = ArrayList<String>(defaultTags)
|
||||
if (cmds.size == 2) {
|
||||
val data = cmds[1].trim().split("${Tags.COMMAND}:", limit = 2)
|
||||
title = data[0].trim()
|
||||
if (data.size > 1) {
|
||||
tags.addAll(data[1].split(TAG_MATCH.toRegex()))
|
||||
}
|
||||
}
|
||||
|
||||
if (title.isBlank()) {
|
||||
title = fetchTitle(link)
|
||||
}
|
||||
|
||||
if (title != Constants.NO_TITLE) {
|
||||
matchTagKeywords(title, tags)
|
||||
}
|
||||
|
||||
// Links are old, clear them
|
||||
if (entries.lastPubDate != today()) {
|
||||
entries.links.clear()
|
||||
}
|
||||
|
||||
val entry = EntryLink(link, title, sender, login, channel, tags)
|
||||
entries.links.add(entry)
|
||||
val index = entries.links.lastIndexOf(entry)
|
||||
event.sendMessage(buildLink(index, entry))
|
||||
|
||||
pinboard.addPin(event.bot().serverHostname, entry)
|
||||
|
||||
// Queue link for posting to Twitter.
|
||||
twitter.queueEntry(index)
|
||||
|
||||
entries.save()
|
||||
|
||||
if (Constants.NO_TITLE == entry.title) {
|
||||
event.sendMessage("Please specify a title, by typing:")
|
||||
event.sendMessage(helpFormat("${buildLinkLabel(index)}:|This is the title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun helpResponse(channel: String, topic: String, event: GenericMessageEvent): Boolean = false
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches(LINK_MATCH.toRegex())
|
||||
}
|
||||
|
||||
internal fun fetchTitle(link: String): String {
|
||||
try {
|
||||
val html = Jsoup.connect(link)
|
||||
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0")
|
||||
.get()
|
||||
val title = html.title()
|
||||
if (title.isNotBlank()) {
|
||||
return title
|
||||
}
|
||||
} catch (ignore: IOException) {
|
||||
// Do nothing
|
||||
}
|
||||
return Constants.NO_TITLE
|
||||
}
|
||||
|
||||
private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean {
|
||||
synchronized(entries) {
|
||||
return try {
|
||||
val match = entries.links.single { it.link == link }
|
||||
event.sendMessage(
|
||||
"Duplicate".bold() + " >> " + buildLink(entries.links.indexOf(match), match)
|
||||
)
|
||||
true
|
||||
} catch (ignore: NoSuchElementException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun matchTagKeywords(title: String, tags: MutableList<String>) {
|
||||
for (match in keywords) {
|
||||
val m = Regex.escape(match)
|
||||
if (title.matches("(?i).*\\b$m\\b.*".toRegex())) {
|
||||
tags.add(match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (KEYWORDS_PROP == key) {
|
||||
keywords.addAll(value.split(TAG_MATCH.toRegex()))
|
||||
} else if (TAGS_PROP == key) {
|
||||
defaultTags.addAll(value.split(TAG_MATCH.toRegex()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Posting.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Posting : AbstractCommand() {
|
||||
override val name = "posting"
|
||||
override val help = listOf(
|
||||
"Post a URL, by saying it on a line on its own:",
|
||||
helpFormat("<url> [<title>] ${Tags.COMMAND}: <+tag> [...]]"),
|
||||
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1",
|
||||
"To add a title, use its label and a pipe:",
|
||||
helpFormat("${Constants.LINK_CMD}1:|This is the title"),
|
||||
"To add a comment:",
|
||||
helpFormat("${Constants.LINK_CMD}1:This is a comment"),
|
||||
"I will reply with a label, for example: ${Constants.LINK_CMD.bold()}1.1",
|
||||
"To edit a comment, see: ",
|
||||
helpFormat("%c ${Constants.HELP_CMD} ${Comment.COMMAND}")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split(":", limit = 2)
|
||||
val entryIndex = cmds[0].toInt() - 1
|
||||
|
||||
if (entryIndex < entries.links.size) {
|
||||
val cmd = cmds[1].trim()
|
||||
if (cmd.isBlank()) {
|
||||
showEntry(entryIndex, event) // L1:
|
||||
} else if (LinksMgr.isUpToDate(event)) {
|
||||
if (cmd == "-") {
|
||||
removeEntry(channel, entryIndex, event) // L1:-
|
||||
} else {
|
||||
when (cmd[0]) {
|
||||
'|' -> changeTitle(cmd, entryIndex, event) // L1:|<title>
|
||||
'=' -> changeUrl(channel, cmd, entryIndex, event) // L1:=<url>
|
||||
'?' -> changeAuthor(channel, cmd, entryIndex, event) // L1:?<author>
|
||||
else -> addComment(cmd, entryIndex, event) // L1:<comment>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("${Constants.LINK_CMD}[0-9]+:.*".toRegex())
|
||||
}
|
||||
|
||||
private fun addComment(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
val commentIndex = entry.addComment(cmd, event.user.nick)
|
||||
val comment = entry.getComment(commentIndex)
|
||||
event.sendMessage(EntriesUtils.buildComment(entryIndex, commentIndex, comment))
|
||||
entries.save()
|
||||
}
|
||||
|
||||
private fun changeTitle(cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
entry.title = cmd.substring(1).trim()
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeUrl(channel: String, cmd: String, entryIndex: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[entryIndex]
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
val link = cmd.substring(1)
|
||||
if (link.matches(LinksMgr.LINK_MATCH.toRegex())) {
|
||||
val oldLink = entry.link
|
||||
entry.link = link
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, oldLink, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(entryIndex, entry))
|
||||
entries.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeAuthor(channel: String, cmd: String, index: Int, event: GenericMessageEvent) {
|
||||
if (isChannelOp(channel, event)) {
|
||||
if (cmd.length > 1) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
entry.nick = cmd.substring(1)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||
entries.save()
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the author of this link for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeEntry(channel: String, index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
LinksMgr.pinboard.deletePin(entry)
|
||||
LinksMgr.twitter.removeEntry(index)
|
||||
entries.links.removeAt(index)
|
||||
event.sendMessage("Entry ${EntriesUtils.buildLinkLabel(index)} removed.")
|
||||
entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to remove this entry for you.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEntry(index: Int, event: GenericMessageEvent) {
|
||||
val entry: EntryLink = entries.links[index]
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry))
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
}
|
||||
if (entry.comments.isNotEmpty()) {
|
||||
val comments = entry.comments
|
||||
for (i in comments.indices) {
|
||||
event.sendMessage(EntriesUtils.buildComment(index, i, comments[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Tags.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Constants
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class Tags : AbstractCommand() {
|
||||
override val name = COMMAND
|
||||
override val help = listOf(
|
||||
"To categorize or tag a URL, use its label and a ${Constants.TAG_CMD}:",
|
||||
helpFormat("${Constants.LINK_CMD}1${Constants.TAG_CMD}:<+tag|-tag> [...]")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val COMMAND = "tags"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val cmds = args.substring(1).split("${Constants.TAG_CMD}:", limit = 2)
|
||||
val index = cmds[0].toInt() - 1
|
||||
|
||||
if (index < LinksMgr.entries.links.size && LinksMgr.isUpToDate(event)) {
|
||||
val cmd = cmds[1].trim()
|
||||
val entry: EntryLink = LinksMgr.entries.links[index]
|
||||
if (cmd.isNotEmpty()) {
|
||||
if (entry.login == event.user.login || isChannelOp(channel, event)) {
|
||||
entry.setTags(cmd)
|
||||
LinksMgr.pinboard.updatePin(event.bot().serverHostname, entry.link, entry)
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
LinksMgr.entries.save()
|
||||
} else {
|
||||
event.sendMessage("Please ask a channel op to change the tags for you.")
|
||||
}
|
||||
} else {
|
||||
if (entry.tags.isNotEmpty()) {
|
||||
event.sendMessage(EntriesUtils.buildTags(index, entry))
|
||||
} else {
|
||||
event.sendMessage("The entry has no tags. Why don't add some?")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(message: String): Boolean {
|
||||
return message.matches("^${Constants.LINK_CMD}[0-9]+${Constants.TAG_CMD}:.*".toRegex())
|
||||
}
|
||||
}
|
125
src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt
Normal file
125
src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* View.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.links
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.lastOrEmpty
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.LinksMgr.Companion.entries
|
||||
import net.thauvin.erik.mobibot.entries.EntriesUtils
|
||||
import net.thauvin.erik.mobibot.entries.EntryLink
|
||||
import org.pircbotx.hooks.events.PrivateMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class View : AbstractCommand() {
|
||||
override val name = VIEW_CMD
|
||||
override val help = listOf(
|
||||
"To list or search the current URL posts:",
|
||||
helpFormat("%c $name [<start>] [<query>]")
|
||||
)
|
||||
override val isOpOnly = false
|
||||
override val isPublic = true
|
||||
override val isVisible = true
|
||||
|
||||
companion object {
|
||||
const val MAX_ENTRIES = 6
|
||||
const val VIEW_CMD = "view"
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (entries.links.isNotEmpty()) {
|
||||
val p = parseArgs(args)
|
||||
showPosts(p.first, p.second, event)
|
||||
} else {
|
||||
event.sendMessage("There is currently nothing to view. Why don't you post something?")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun parseArgs(args: String): Pair<Int, String> {
|
||||
var query = args.lowercase().trim()
|
||||
var start = 0
|
||||
if (query.isEmpty() && entries.links.size > MAX_ENTRIES) {
|
||||
start = entries.links.size - MAX_ENTRIES
|
||||
}
|
||||
if (query.matches("^\\d+(| .*)".toRegex())) { // view [<start>] [<query>]
|
||||
val split = query.split(" ", limit = 2)
|
||||
try {
|
||||
start = split[0].toInt() - 1
|
||||
query = split.lastOrEmpty().trim()
|
||||
if (start > entries.links.size) {
|
||||
start = 0
|
||||
}
|
||||
} catch (ignore: NumberFormatException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
return Pair(start, query)
|
||||
}
|
||||
|
||||
private fun showPosts(start: Int, query: String, event: GenericMessageEvent) {
|
||||
var index = start
|
||||
var entry: EntryLink
|
||||
var sent = 0
|
||||
while (index < entries.links.size && sent < MAX_ENTRIES) {
|
||||
entry = entries.links[index]
|
||||
if (query.isNotBlank()) {
|
||||
if (entry.matches(query)) {
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry, true))
|
||||
sent++
|
||||
}
|
||||
} else {
|
||||
event.sendMessage(EntriesUtils.buildLink(index, entry, true))
|
||||
sent++
|
||||
}
|
||||
index++
|
||||
if (sent == MAX_ENTRIES && index < entries.links.size) {
|
||||
event.sendMessage("To view more, try: ")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
buildCmdSyntax(
|
||||
"%c $name ${index + 1} $query",
|
||||
event.bot().nick,
|
||||
event is PrivateMessageEvent
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (sent == 0) {
|
||||
event.sendMessage("No matches. Please try again.")
|
||||
}
|
||||
}
|
||||
}
|
303
src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt
Normal file
303
src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Tell.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.tell
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.Utils.bot
|
||||
import net.thauvin.erik.mobibot.Utils.buildCmdSyntax
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.isChannelOp
|
||||
import net.thauvin.erik.mobibot.Utils.plural
|
||||
import net.thauvin.erik.mobibot.Utils.reverseColor
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import net.thauvin.erik.mobibot.Utils.toIntOrDefault
|
||||
import net.thauvin.erik.mobibot.Utils.toUtcDateTime
|
||||
import net.thauvin.erik.mobibot.commands.AbstractCommand
|
||||
import net.thauvin.erik.mobibot.commands.links.View
|
||||
import org.pircbotx.PircBotX
|
||||
import org.pircbotx.hooks.events.MessageEvent
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.pircbotx.hooks.types.GenericUserEvent
|
||||
|
||||
/**
|
||||
* The `Tell` command.
|
||||
*/
|
||||
class Tell(private val serialObject: String) : AbstractCommand() {
|
||||
// Messages queue
|
||||
private val messages: MutableList<TellMessage> = mutableListOf()
|
||||
|
||||
// Maximum number of days to keep messages
|
||||
private var maxDays = 7
|
||||
|
||||
// Message maximum queue size
|
||||
private var maxSize = 50
|
||||
|
||||
/**
|
||||
* The tell command.
|
||||
*/
|
||||
override val name = "tell"
|
||||
|
||||
override val help = listOf(
|
||||
"To send a message to someone when they join the channel:",
|
||||
helpFormat("%c $name <nick> <message>"),
|
||||
"To view queued and sent messages:",
|
||||
helpFormat("%c $name ${View.VIEW_CMD}"),
|
||||
"Messages are kept for ${maxDays.bold()}" + " day".plural(maxDays.toLong()) + '.'
|
||||
)
|
||||
override val isOpOnly: Boolean = false
|
||||
override val isPublic: Boolean = isEnabled()
|
||||
override val isVisible: Boolean = isEnabled()
|
||||
|
||||
/**
|
||||
* Cleans the messages queue.
|
||||
*/
|
||||
private fun clean(): Boolean {
|
||||
return TellMessagesMgr.clean(messages, maxDays.toLong())
|
||||
}
|
||||
|
||||
override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) {
|
||||
if (isEnabled()) {
|
||||
if (args.isBlank()) {
|
||||
helpResponse(channel, args, event)
|
||||
} else if (args.startsWith(View.VIEW_CMD)) {
|
||||
if (isChannelOp(channel, event) && "${View.VIEW_CMD} $TELL_ALL_KEYWORD" == args) {
|
||||
viewAll(event)
|
||||
} else {
|
||||
viewMessages(event)
|
||||
}
|
||||
} else if (args.startsWith("$TELL_DEL_KEYWORD ")) {
|
||||
deleteMessage(channel, args, event)
|
||||
} else {
|
||||
newMessage(channel, args, event)
|
||||
}
|
||||
if (clean()) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete message.
|
||||
private fun deleteMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val split = args.split(" ")
|
||||
if (split.size == 2) {
|
||||
val id = split[1]
|
||||
if (TELL_ALL_KEYWORD.equals(id, ignoreCase = true)) {
|
||||
if (messages.removeIf { it.sender.equals(event.user.nick, true) && it.isReceived }) {
|
||||
save()
|
||||
event.sendMessage("Delivered messages have been deleted.")
|
||||
} else {
|
||||
event.sendMessage("No delivered messages were found.")
|
||||
}
|
||||
} else {
|
||||
if (messages.removeIf {
|
||||
it.id == id &&
|
||||
(it.sender.equals(event.user.nick, true) || isChannelOp(channel, event))
|
||||
}) {
|
||||
save()
|
||||
event.sendMessage("The message was deleted from the queue.")
|
||||
} else {
|
||||
event.sendMessage("The specified message [ID $id] could not be found.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabled(): Boolean {
|
||||
return maxSize > 0 && maxDays > 0
|
||||
}
|
||||
|
||||
override fun setProperty(key: String, value: String) {
|
||||
super.setProperty(key, value)
|
||||
if (MAX_DAYS_PROP == key) {
|
||||
maxDays = value.toIntOrDefault(maxDays)
|
||||
} else if (MAX_SIZE_PROP == key) {
|
||||
maxSize = value.toIntOrDefault(maxSize)
|
||||
}
|
||||
}
|
||||
|
||||
// New message.
|
||||
private fun newMessage(channel: String, args: String, event: GenericMessageEvent) {
|
||||
val split = args.split(" ".toRegex(), 2)
|
||||
if (split.size == 2 && split[1].isNotBlank() && split[1].contains(" ")) {
|
||||
if (messages.size < maxSize) {
|
||||
val message = TellMessage(event.user.nick, split[0], split[1].trim())
|
||||
messages.add(message)
|
||||
save()
|
||||
event.sendMessage("Message [ID ${message.id}] was queued for ${message.recipient.bold()}")
|
||||
} else {
|
||||
event.sendMessage("Sorry, the messages queue is currently full.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(channel, args, event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the messages queue.
|
||||
*/
|
||||
private fun save() {
|
||||
TellMessagesMgr.save(serialObject, messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and sends messages.
|
||||
*/
|
||||
fun send(event: GenericUserEvent) {
|
||||
val nickname = event.user.nick
|
||||
if (isEnabled() && nickname != event.getBot<PircBotX>().nick) {
|
||||
messages.filter { it.isMatch(nickname) }.forEach { message ->
|
||||
if (message.recipient.equals(nickname, ignoreCase = true) && !message.isReceived) {
|
||||
if (message.sender == nickname) {
|
||||
if (event !is MessageEvent) {
|
||||
event.user.send().message(
|
||||
"${"You".bold()} wanted me to remind you: ${message.message.reverseColor()}"
|
||||
)
|
||||
message.isReceived = true
|
||||
message.isNotified = true
|
||||
save()
|
||||
}
|
||||
} else {
|
||||
event.user.send().message(
|
||||
"${message.sender} wanted me to tell you: ${message.message.reverseColor()}"
|
||||
)
|
||||
message.isReceived = true
|
||||
save()
|
||||
}
|
||||
} else if (message.sender.equals(nickname, ignoreCase = true) && message.isReceived
|
||||
&& !message.isNotified
|
||||
) {
|
||||
event.user.send().message(
|
||||
"Your message ${"[ID ${message.id}]".reverseColor()} was sent to "
|
||||
+ "${message.recipient.bold()} on ${message.receptionDate}"
|
||||
)
|
||||
message.isNotified = true
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the messages queue size.
|
||||
*
|
||||
* @return The size.
|
||||
*/
|
||||
fun size(): Int = messages.size
|
||||
|
||||
// View all messages.
|
||||
private fun viewAll(event: GenericMessageEvent) {
|
||||
if (messages.isNotEmpty()) {
|
||||
for (message in messages) {
|
||||
event.sendMessage(
|
||||
"${message.sender.bold()}$ARROW${message.recipient.bold()} [ID: ${message.id}, " +
|
||||
(if (message.isReceived) "DELIVERED]" else "QUEUED]")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
event.sendMessage("There are no messages in the queue.")
|
||||
}
|
||||
}
|
||||
|
||||
// View messages.
|
||||
private fun viewMessages(event: GenericMessageEvent) {
|
||||
var hasMessage = false
|
||||
for (message in messages.filter { it.isMatch(event.user.nick) }) {
|
||||
if (!hasMessage) {
|
||||
hasMessage = true
|
||||
event.sendMessage("Here are your messages: ")
|
||||
}
|
||||
if (message.isReceived) {
|
||||
event.sendMessage(
|
||||
message.sender.bold() + ARROW + message.recipient.bold() +
|
||||
" [${message.receptionDate.toUtcDateTime()}, ID: ${message.id.bold()}, DELIVERED]"
|
||||
)
|
||||
} else {
|
||||
event.sendMessage(
|
||||
message.sender.bold() + ARROW + message.recipient.bold() +
|
||||
" [${message.queued.toUtcDateTime()}, ID: ${message.id.bold()}, QUEUED]"
|
||||
)
|
||||
}
|
||||
event.sendMessage(helpFormat(message.message))
|
||||
}
|
||||
if (!hasMessage) {
|
||||
event.sendMessage("You have no messages in the queue.")
|
||||
} else {
|
||||
event.sendMessage("To delete one or all delivered messages:")
|
||||
event.sendMessage(
|
||||
helpFormat(
|
||||
buildCmdSyntax(
|
||||
"%c $name $TELL_DEL_KEYWORD <id|$TELL_ALL_KEYWORD>",
|
||||
event.bot().nick,
|
||||
true
|
||||
)
|
||||
)
|
||||
)
|
||||
event.sendMessage(help.last())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Max days property.
|
||||
*/
|
||||
const val MAX_DAYS_PROP = "tell-max-days"
|
||||
|
||||
/**
|
||||
* Max size property.
|
||||
*/
|
||||
const val MAX_SIZE_PROP = "tell-max-size"
|
||||
|
||||
// Arrow
|
||||
private const val ARROW = " --> "
|
||||
|
||||
// All keyword
|
||||
private const val TELL_ALL_KEYWORD = "all"
|
||||
|
||||
//T he delete command.
|
||||
private const val TELL_DEL_KEYWORD = "del"
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
init {
|
||||
initProperties(MAX_DAYS_PROP, MAX_SIZE_PROP)
|
||||
|
||||
// Load the message queue
|
||||
messages.addAll(TellMessagesMgr.load(serialObject))
|
||||
if (clean()) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* TellMessage.kt
|
||||
*
|
||||
* Copyright (c) 2004-2022, 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.commands.tell
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.Clock
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* The `TellMessage` class.
|
||||
*/
|
||||
class TellMessage(
|
||||
/**
|
||||
* Returns the message's sender.
|
||||
*/
|
||||
val sender: String,
|
||||
|
||||
/**
|
||||
* Returns the message's recipient.
|
||||
*/
|
||||
val recipient: String,
|
||||
|
||||
/**
|
||||
* Returns the message text.
|
||||
*/
|
||||
val message: String
|
||||
) : Serializable {
|
||||
/**
|
||||
* Returns the queued date/time.
|
||||
*/
|
||||
var queued: LocalDateTime = LocalDateTime.now(Clock.systemUTC())
|
||||
|
||||
/**
|
||||
* Returns the message id.
|
||||
*/
|
||||
var id: String = queued.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a notification was sent.
|
||||
*/
|
||||
var isNotified = false
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the message was received.
|
||||
*/
|
||||
var isReceived = false
|
||||
set(value) {
|
||||
if (value) {
|
||||
receptionDate = LocalDateTime.now(Clock.systemUTC())
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message creating date.
|
||||
*/
|
||||
var receptionDate: LocalDateTime = LocalDateTime.MIN
|
||||
|
||||
/**
|
||||
* Matches the message sender or recipient.
|
||||
*/
|
||||
fun isMatch(nick: String?): Boolean {
|
||||
return sender.equals(nick, ignoreCase = true) || recipient.equals(nick, ignoreCase = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ("TellMessage{id='$id', isNotified=$isNotified, isReceived=$isReceived, message='$message', " +
|
||||
"queued=$queued, received=$receptionDate, recipient='$recipient', sender='$sender'}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 2L
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue