8 Commits

Author SHA1 Message Date
Wieland Schöbl
a78c2343da Add service announcement channel 2020-08-19 14:25:54 +02:00
Wieland Schöbl
490a5dcd41 Add ability to customize message 2020-08-18 10:35:13 +02:00
Wieland Schöbl
5969a2f221 Add self-configuration feature 2020-08-18 00:39:15 +02:00
Wieland Schöbl
e58f4dc0c5 [1.1] 2020-08-14 22:51:57 +02:00
Wieland Schöbl
2c7797c112 Merge remote-tracking branch 'origin/master' 2020-08-14 22:49:14 +02:00
Wieland Schöbl
7bf483ab32 Add auto publish feature 2020-08-14 22:47:32 +02:00
Wieland Schöbl
2741206977 Add auto publish feature 2020-08-14 22:43:53 +02:00
Wieland Schöbl
d15fb92acf make shutdown hook work correctly 2020-08-14 02:19:15 +02:00
20 changed files with 764 additions and 225 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
servers.json
admin.json
service_channels.json
*.hprof
/build
/.gradle

7
.idea/dictionaries/wulkanat.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="wulkanat">
<words>
<w>crosspost</w>
</words>
</dictionary>
</component>

1
.idea/gradle.xml generated
View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

217
.idea/workspace.xml generated
View File

@@ -2,11 +2,19 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="1aabf22b-2f57-46ac-9973-367d8668ffd3" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DiscordRpc.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/extensions/File.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Admin.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Admin.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Cli.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Cli.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/AdminCli.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/AdminCli.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Channels.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Channels.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DataIO.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/Main.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/OwnerCli.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/OwnerCli.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/SiteWatcher.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/web/SiteWatcher.kt" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -87,9 +95,10 @@
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Kotlin File" />
<option value="Class" />
<option value="Kotlin Class" />
<option value="Kotlin Object" />
<option value="Kotlin File" />
</list>
</option>
</component>
@@ -105,6 +114,8 @@
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/build/libs" />
<property name="project.structure.last.edited" value="Modules" />
<property name="project.structure.proportion" value="0.15" />
<property name="project.structure.side.proportion" value="0.2" />
@@ -114,12 +125,54 @@
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
<recent name="de.wulkanat" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\build\libs" />
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src\main\kotlin\de\wulkanat" />
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot" />
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src\main\kotlin\de\wulkanat" />
</key>
</component>
<component name="RunManager" selected="JAR Application.HytaleUpdateBot-all-1.0-SNAPSHOT.jar">
<component name="RunManager" selected="Kotlin.MainKt">
<configuration name="HytaleUpdateBot [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot [fatJar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
@@ -139,44 +192,30 @@
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot [jar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="jar" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot-1.0-SNAPSHOT-all.jar" type="JarApplication" temporary="true">
<option name="JAR_PATH" value="$PROJECT_DIR$/build/libs/HytaleUpdateBot-1.0-SNAPSHOT-all.jar" />
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot-1.0-SNAPSHOT.jar" type="JarApplication" temporary="true">
<option name="JAR_PATH" value="$PROJECT_DIR$/build/libs/HytaleUpdateBot-1.0-SNAPSHOT.jar" />
<method v="2" />
</configuration>
<configuration name="HytaleUpdateBot-all-1.0-SNAPSHOT.jar" type="JarApplication" temporary="true">
<option name="JAR_PATH" value="$PROJECT_DIR$/build/libs/HytaleUpdateBot-all-1.0-SNAPSHOT.jar" />
<method v="2" />
</configuration>
<configuration name="MainKt" type="JetRunConfigurationType" temporary="true" nameIsGenerated="true">
<module name="HytaleUpdateBot.main" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="MAIN_CLASS_NAME" value="de.wulkanat.MainKt" />
<option name="WORKING_DIRECTORY" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="JAR Application.HytaleUpdateBot-all-1.0-SNAPSHOT.jar" />
<item itemvalue="JAR Application.HytaleUpdateBot-1.0-SNAPSHOT-all.jar" />
<item itemvalue="Kotlin.MainKt" />
<item itemvalue="Gradle.HytaleUpdateBot [fatJar]" />
<item itemvalue="JAR Application.HytaleUpdateBot-1.0-SNAPSHOT.jar" />
<item itemvalue="Gradle.HytaleUpdateBot [jar]" />
<item itemvalue="Gradle.HytaleUpdateBot [build]" />
<item itemvalue="Gradle.HytaleUpdateBot [clean]" />
<item itemvalue="JAR Application.HytaleUpdateBot-all-1.0-SNAPSHOT.jar" />
</list>
</recent_temporary>
</component>
@@ -191,13 +230,40 @@
<option name="presentableId" value="Default" />
<updated>1597322033373</updated>
</task>
<task id="LOCAL-00001" summary="Add auto publish feature">
<created>1597437833375</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1597437833375</updated>
</task>
<task id="LOCAL-00002" summary="Add auto publish feature">
<created>1597438052596</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1597438052596</updated>
</task>
<task id="LOCAL-00003" summary="[1.1]">
<created>1597438317540</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1597438317540</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Add auto publish feature" />
<MESSAGE value="[1.1]" />
<option name="LAST_COMMIT_MESSAGE" value="[1.1]" />
</component>
<component name="WindowStateProjectService">
<state x="552" y="179" key="#Project_Structure" timestamp="1597338262424">
<state x="552" y="179" key="#Project_Structure" timestamp="1597687666334">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="552" y="179" key="#Project_Structure/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597338262424" />
<state x="552" y="179" key="#Project_Structure/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597687666334" />
<state x="-1050" y="581" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1597352463714">
<screen x="-1050" y="105" width="1050" height="1640" />
</state>
@@ -206,6 +272,10 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="633" y="446" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362173063" />
<state x="690" y="268" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1597831342920">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="690" y="268" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597831342920" />
<state x="739" y="173" width="484" height="693" key="#org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog" timestamp="1597362199927">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@@ -214,61 +284,90 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="128" y="270" width="490" height="591" key="#xdebugger.evaluate/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597332665464" />
<state width="1876" height="161" key="GridCell.Tab.0.bottom" timestamp="1597362407964">
<state x="569" y="115" key="CommitChangelistDialog2" timestamp="1597438316655">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="569" y="115" key="CommitChangelistDialog2/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597438316655" />
<state x="740" y="238" key="FileChooserDialogImpl" timestamp="1597605616287">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="740" y="238" key="FileChooserDialogImpl/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597605616287" />
<state width="1876" height="161" key="GridCell.Tab.0.bottom" timestamp="1597839551368">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.0.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329412" />
<state width="1876" height="161" key="GridCell.Tab.0.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362407964" />
<state width="1876" height="161" key="GridCell.Tab.0.center" timestamp="1597362407964">
<state width="1876" height="161" key="GridCell.Tab.0.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597839551368" />
<state width="1876" height="161" key="GridCell.Tab.0.center" timestamp="1597839551368">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.0.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329411" />
<state width="1876" height="161" key="GridCell.Tab.0.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362407964" />
<state width="1876" height="161" key="GridCell.Tab.0.left" timestamp="1597362407964">
<state width="1876" height="161" key="GridCell.Tab.0.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597839551368" />
<state width="1876" height="161" key="GridCell.Tab.0.left" timestamp="1597839551368">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.0.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329411" />
<state width="1876" height="161" key="GridCell.Tab.0.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362407964" />
<state width="1876" height="161" key="GridCell.Tab.0.right" timestamp="1597362407964">
<state width="1876" height="161" key="GridCell.Tab.0.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597839551368" />
<state width="1876" height="161" key="GridCell.Tab.0.right" timestamp="1597839551368">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.0.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329411" />
<state width="1876" height="161" key="GridCell.Tab.0.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362407964" />
<state width="1006" height="588" key="GridCell.Tab.1.bottom" timestamp="1597351329412">
<screen x="-1050" y="105" width="1050" height="1640" />
<state width="1876" height="161" key="GridCell.Tab.0.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597839551368" />
<state width="1876" height="348" key="GridCell.Tab.1.bottom" timestamp="1597837225399">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.1.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329412" />
<state width="1876" height="348" key="GridCell.Tab.1.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597339868293" />
<state width="1006" height="588" key="GridCell.Tab.1.center" timestamp="1597351329412">
<screen x="-1050" y="105" width="1050" height="1640" />
<state width="1876" height="348" key="GridCell.Tab.1.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597837225399" />
<state width="1876" height="348" key="GridCell.Tab.1.center" timestamp="1597837225398">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.1.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329412" />
<state width="1876" height="348" key="GridCell.Tab.1.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597339868293" />
<state width="1006" height="588" key="GridCell.Tab.1.left" timestamp="1597351329412">
<screen x="-1050" y="105" width="1050" height="1640" />
<state width="1876" height="348" key="GridCell.Tab.1.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597837225398" />
<state width="1876" height="348" key="GridCell.Tab.1.left" timestamp="1597837225398">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.1.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329412" />
<state width="1876" height="348" key="GridCell.Tab.1.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597339868293" />
<state width="1006" height="588" key="GridCell.Tab.1.right" timestamp="1597351329412">
<screen x="-1050" y="105" width="1050" height="1640" />
<state width="1876" height="348" key="GridCell.Tab.1.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597837225398" />
<state width="1876" height="348" key="GridCell.Tab.1.right" timestamp="1597837225399">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1006" height="588" key="GridCell.Tab.1.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597351329412" />
<state width="1876" height="348" key="GridCell.Tab.1.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597339868293" />
<state width="1876" height="348" key="GridCell.Tab.1.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597837225399" />
<state x="672" y="237" key="MultipleFileMergeDialog" timestamp="1597438068748">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="672" y="237" key="MultipleFileMergeDialog/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597438068748" />
<state x="94" y="257" key="SettingsEditor" timestamp="1597361509050">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="-1040" y="568" key="SettingsEditor/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597353858648" />
<state x="94" y="257" key="SettingsEditor/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597361509050" />
<state x="552" y="254" key="Vcs.Push.Dialog.v2" timestamp="1597438319085">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="552" y="254" key="Vcs.Push.Dialog.v2/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597438319085" />
<state x="2582" y="100" key="new project wizard" timestamp="1597605657341">
<screen x="1920" y="-213" width="2560" height="1400" />
</state>
<state x="2582" y="100" key="new project wizard/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@1920.-213.2560.1400" timestamp="1597605657341" />
<state x="616" y="240" key="run.anything.popup" timestamp="1597325088886">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="616" y="240" key="run.anything.popup/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597325088886" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup" timestamp="1597333756907">
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup" timestamp="1597702900013">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597333756907" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597702900013" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/src/main/kotlin/de/wulkanat/AdminCli.kt</url>
<line>18</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
<watches-manager>
<configuration name="JetRunConfigurationType">
<watch expression="((org.jsoup.nodes.Element.NodeList)((Document)doc).childNodes).get(2)" custom="org.jsoup.nodes.Element.NodeList,org.jsoup.nodes.Document" />

View File

@@ -1,47 +1,68 @@
# BlogShot
A bot that automatically polls the newest blogpost from [Hytale News Tab](https://www.hytale.com/news) and posts a message into servers if there is a new one.
## Setup
Okay, this isn't really meant for you to setup, if you want it though it first is easier to just dm me on Twitter [@tale_talk](https://twitter.com/tale_talk) so I can add you to the server list.
If you *really* want to set it up yourself, fine.
1. first go to the release tab, download the jar, and put it in a folder
2. Add two files in the root of the repo, an `admin.json` and a `servers.json`.
Add your Discord ID (not name), Bot token, and update frequency to the `admin.json`:
```json
{"adminId": 12345678910,"token": "AOGH@(AKnjsfjiJijaig3ijgG92jaij","updateMs":30000}
```
3. add an empty array to your `servers.json`
```json
[]
```
Not sure, but it might be that multiline JSON doesn't work.
## Add to your server
Click [this](https://discord.com/api/oauth2/authorize?client_id=743447329901641799&permissions=150528&scope=bot) link to invite
the bot to your server. Please note that only people with *Administrator* permission will be able to
configure it.
You can type `%!info` to get an overview over all available commands.
## Commands
| **Command** | **Arguments** | **Info** |
|------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| %!add | | Add current channel to the notified list |
| %!remove | | Remove current channel to the notified list |
| %!publish | on &#124; off | [Community&#124;Partner&#124;Verified only] Auto publish the message if in an announcement channel |
| %!ping | none &#124; everyone &#124; roleName | What role to ping |
| %!setMessage | message | Set a custom message when a blogpost arrives |
| %!resetMessage | | Reset the custom message to none |
| %!serviceChannel | add &#124; remove | Add/remove channel from service notification list |
| %!publishMessage | on &#124; off | [Community&#124;Partner&#124;Verified only] Auto publish the custom message if in an announcement channel |
| %!info | | Show an overview about all channels registered on this server |
| %!report | Your message | Report an issue to the Bot Admin (this will share your user name so they can contact you) |
| %!help | | Show a help dialog with all these commands |
## Self Hosting
Okay, this isn't really meant for you to setup, but if you *really* want to set it up yourself, fine.
Go to the release tab, download the jar, and put it in a folder.
Start the server with `java -jar [server-file-name]` If you put in everything correctly,
the bot should message you on Discord.
*Note:* You need to invite the bot into a server before it can message you.
Run it once (it should crash or print an error), so `admin.json`, `servers.json` and `service_channels.json`
are being created.
Add your Discord ID `adminId` (not name), Bot token `token`, and update frequency `updateMs` to the `admin.json`,
optionally you can add your own messages for when the bot is looking and when it can't reach Hytale Servers.
If you verified that everything works correctly, you can start the server in the background, on Linux that is
`nohup java -jar [server-file-name]`. To stop it you can either type `!stop` in the Admin Console (Discord PM) or
if the bot is unresponsive the the PID of it through `ps -ef` and `kill [pid]`
## Compiling yourself
I developed it under Windows, and had some trouble compiling it on Linux. You mileage may vary.
## Admin commands
Start the server with `java -jar [server-file-name]` If you put in everything correctly, the bot should message you on Discord.
### Adding Servers
```
%!addChannel [channelID] [roleID/everyone]
```
Second argument is optional.
### Cause a fake update (test if it works)
```
%!fakeUpdate
```
### Stop the server from within Discord
```
%!stop
```
### Show servers, channels and roles
```
%!info
```
| **Command** | **Arguments** | **Info** |
|------------------|-------|---------------------|
| !info | | Show all registered channels and servers. |
| !stop | | Stop the server (useful when running in `nohup`) |
| !serviceMessage | message | Send a service message to all registered channels |
| !fakeUpdate | | Cause a fake update (**WARNING**: This will show on **ALL** registered servers) |
| !refreshList | | Refresh servers and service channels from disk (if you manually edit the JSON files) |
These commands will work in every channel, but will be ignored if they don't come from you, however the bot will always respond in a private message.
It will also print errors directly in a Discord private message.
These commands will only work by private messaging the bot (and will be ignored if they don't
come from the admin registered in the `admin.json`.
## TODO
Mainly reaction roles for convenience, self setup on invite to server, Twitter integration.
Mainly reaction roles for convenience, Twitter integration to either be even faster or to brag how much faster
we were over the official Hytale Twitter.
## Other
Thanks to [Forcellrus](https://github.com/Forcellrus/Discord-Auto-Publisher) for discovering a way to auto publish messages
in news channels

View File

@@ -4,7 +4,7 @@ plugins {
}
group 'de.wulkanat'
version '1.0-SNAPSHOT'
version '1.4'
repositories {
mavenCentral()

View File

@@ -0,0 +1,27 @@
import net.dv8tion.jda.internal.requests.Method;
import net.dv8tion.jda.internal.requests.Route;
import java.lang.reflect.Constructor;
public class Inaccessibles {
/**
* This is private by default
*
* @param method look
* @param route somewhere
* @return else
*/
public static Route getRoute(Method method, String route) {
try {
Constructor<?> constructor = Route.class.getDeclaredConstructor(Method.class, String.class);
constructor.setAccessible(true);
return (Route) constructor.newInstance(method, route);
} catch (Exception e) {
return null;
}
}
public static String toUnsignedString(long num) {
return Long.toUnsignedString(num);
}
}

View File

@@ -4,6 +4,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.Activity
import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.entities.User
import java.awt.Color
@@ -12,12 +13,16 @@ object Admin {
val userId: Long
val token: String
val updateMs: Long
val message: String
val offlineMessage: String
init {
val admin = Json(JsonConfiguration.Stable).parse(AdminFile.serializer(), ADMIN_FILE.readText())
userId = admin.adminId
token = admin.token
updateMs = admin.updateMs
message = admin.watchingMessage
offlineMessage = admin.offlineMessage
}
var jda: JDA? = null
@@ -31,7 +36,7 @@ object Admin {
kotlin.io.println("Connected to ${admin!!.name}. No further errors will be printed here.")
}
}
private var admin: User? = null
var admin: User? = null
fun println(msg: String) {
sendDevMessage(
@@ -53,14 +58,21 @@ object Admin {
)
}
fun error(msg: String, error: Exception) {
fun error(msg: String, error: String, author: User? = null) {
sendDevMessage(
EmbedBuilder()
.setTitle(msg)
.setDescription(error.message)
.setDescription(error)
.setColor(Color.RED)
.run {
if (author == null) {
this
} else {
this.setAuthor(author.asTag, author.avatarUrl, author.avatarUrl)
}
}
.build()
, "$msg\n\n${error.message}"
, "$msg\n\n${error}"
)
}
@@ -89,7 +101,12 @@ object Admin {
sendDevMessage(
EmbedBuilder()
.setTitle("Now watching for new Hytale Blogposts every ${updateMs / 1000}s")
.setDescription(Channels.getServerNames().joinToString("\n"))
.setDescription("""
${Channels.getServerNames().joinToString("\n")}
**_Service Channels_**
${Channels.getServiceChannelServers().joinToString("\n")}
""".trimIndent())
.setColor(Color.GREEN)
.build(),
"Now watching for new Hytale BlogPosts"
@@ -101,17 +118,17 @@ object Admin {
}
private fun senDevMessageBlocking(messageEmbed: MessageEmbed, fallback: String) {
admin = jda!!.retrieveUserById(userId).complete()
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
kotlin.io.println(fallback)
return
}
devChannel.queue {
it.sendMessage(messageEmbed).complete()
}
devChannel.complete()
.sendMessage(messageEmbed).complete()
}
private fun sendDevMessage(messageEmbed: MessageEmbed, fallback: String) {
fun sendDevMessage(messageEmbed: MessageEmbed, fallback: String) {
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
kotlin.io.println(fallback)
return

View File

@@ -0,0 +1,50 @@
package de.wulkanat
import de.wulkanat.model.BlogPostPreview
import net.dv8tion.jda.api.hooks.ListenerAdapter
import de.wulkanat.web.SiteWatcher
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent
import kotlin.system.exitProcess
class AdminCli : ListenerAdapter() {
override fun onPrivateMessageReceived(event: PrivateMessageReceivedEvent) {
val msg = event.message.contentRaw
if (event.author.idLong != Admin.userId ||
!msg.startsWith("!")
) {
return
}
val command = Regex("[^\\s`]+|`[^`]*`").findAll(msg.removePrefix("!")).toList()
when (command[0].value) {
"stop" -> exitProcess(1)
"fakeUpdate" -> {
SiteWatcher.newestBlog = BlogPostPreview(
title = "FakePost",
imgUrl = "",
fullPostUrl = "",
author = "wulkanat",
date = "now",
description = "Lorem Ipsum"
)
Admin.println("Posting on next update cycle.")
}
"info" -> {
Admin.info()
}
"serviceMessage" -> {
if (command.size != 3) {
Admin.println("Enclose message and title in backticks (`)")
} else {
Channels.sendServiceMessage(command[1].value.trim('`'), command[2].value.trim('`'))
}
}
"refreshList" -> {
Channels.channels = Channels.refreshChannelsFromDisk()
Channels.serviceChannels = Channels.refreshServiceChannelsFromDisk()
Admin.info()
}
}
}
}

View File

@@ -1,21 +0,0 @@
package de.wulkanat
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
class Bot : ListenerAdapter() {
override fun onMessageReceived(event: MessageReceivedEvent) {
val message = event.message
if (message.contentRaw == "!ping") {
val channel = message.channel
val time = System.currentTimeMillis()
channel.sendMessage("Pong!")
.queue {
it.editMessageFormat("Pong: %d ms", System.currentTimeMillis() - time)
.queue()
}
}
}
}

View File

@@ -1,42 +1,91 @@
package de.wulkanat
import de.wulkanat.extensions.crosspost
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.entities.TextChannel
import net.dv8tion.jda.api.exceptions.ErrorResponseException
import java.awt.Color
object Channels {
var jda: JDA? = null
val json = Json(JsonConfiguration.Stable)
/**
* List of (ServerID, ChannelID)
*/
val channels: MutableList<DiscordChannel> =
json.parse(DiscordChannel.serializer().list, SERVERS_FILE.readText()).toMutableList()
var channels: MutableList<DiscordChannel> = refreshChannelsFromDisk()
var serviceChannels: MutableList<ServiceChannel> = refreshServiceChannelsFromDisk()
fun sentToAll(messageEmbed: MessageEmbed) {
if (jda == null)
return
for (channel_pair in channels) {
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
try {
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
val customMessage = channel_pair.message?.message ?: ""
if (channel_pair.mentionedRole != null) {
val message = if (channel_pair.mentionedRole == "everyone") {
"New Blogpost @everyone"
} else {
"New Blogpost <@&${channel_pair.mentionedRole}>"
if (channel_pair.mentionedRole != null) {
val message = if (channel_pair.mentionedRole == "everyone") {
"@everyone $customMessage"
} else {
"<@&${channel_pair.mentionedRole}> $customMessage"
}
channel.sendMessage(message).queue {
if (channel_pair.message?.pushAnnouncement == true) {
it.crosspost().queue()
}
}
} else if (channel_pair.message != null) {
channel.sendMessage(customMessage).queue {
if (channel_pair.message?.pushAnnouncement == true) {
it.crosspost().queue()
}
}
}
channel.sendMessage(message).queue()
channel.sendMessage(messageEmbed).queue {
if (channel_pair.autoPublish) {
it.crosspost().queue()
}
}
} catch (e: ErrorResponseException) {
Admin.error("Error in server", e.message ?: e.localizedMessage)
}
channel.sendMessage(messageEmbed).queue()
}
}
fun sendServiceMessage(title: String, message: String) {
val serviceMessage = EmbedBuilder()
.setTitle(title)
.setDescription(message)
.setColor(Color.WHITE)
.setAuthor(Admin.admin?.name, Admin.admin?.avatarUrl, Admin.admin?.avatarUrl)
.setFooter("This was sent by a human.")
.build()
for (channelInfo in serviceChannels) {
val channel = jda!!.getTextChannelById(channelInfo.id)
channel?.sendMessage(serviceMessage)?.queue()
}
Admin.println("Service message distributed to ${serviceChannels.size} channels.")
Admin.sendDevMessage(serviceMessage, """
***************
SERVICE MESSAGE
$title
-------
$message
***************
""".trimIndent())
}
fun checkEveryonePermission() {
for (channel_pair in channels) {
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
@@ -52,25 +101,51 @@ object Channels {
}
}
fun getServerNames(): List<String> {
fun refreshChannelsFromDisk(): MutableList<DiscordChannel> {
return json.parse(
DiscordChannel.serializer().list, (SERVERS_FILE).readText()
).toMutableList()
}
fun refreshServiceChannelsFromDisk(): MutableList<ServiceChannel> {
return json.parse(
ServiceChannel.serializer().list, (SERVICE_CHANNELS_FILE).readText()
).toMutableList()
}
fun getServerNames(server: Long? = null): List<String> {
if (jda == null)
return listOf()
return channels.map {
return channels.filter { server == null || (jda!!.getTextChannelById(it.id)?.guild?.idLong == server) }.map {
val channel = jda!!.getTextChannelById(it.id)
if (channel == null) {
Admin.warning("Channel ${it.id} is no longer active!")
return@map "**${it.id}** *(inactive)*"
}
val role = if (it.mentionedRole == null) {
""
} else if (it.mentionedRole == "everyone") {
" @everyone"
} else {
" @${channel.guild.getRoleById(it.mentionedRole)?.name}"
val role = when (it.mentionedRole) {
null -> ""
"everyone" -> " @everyone"
else -> " @${channel.guild.getRoleById(it.mentionedRole ?: "")?.name}"
}
"**${channel.guild.name}**\n#${channel.name}${role}"
val publish = if (it.autoPublish) " (publish)" else ""
"**${channel.guild.name}** #${channel.name}${role}${publish}${if (it.message == null) {
""
} else {
"\n*${it.message!!.message}*${if (it.message!!.pushAnnouncement) " (publish)" else ""}"
}
}"
}
}
fun getServiceChannelServers(server: Long? = null): List<String> {
if (jda == null)
return listOf()
return serviceChannels.filter { server == null || (jda!!.getTextChannelById(it.id)?.guild?.idLong == server) }.map {
val channel = jda!!.getTextChannelById(it.id)
"**${channel?.guild?.name ?: it.id}** #${channel?.name ?: "(inactive)"}"
}
}
@@ -78,16 +153,28 @@ object Channels {
return jda?.getTextChannelById(id)
}
fun addChannel(id: Long, role: String?) {
channels.add(DiscordChannel(id, role))
fun addChannel(id: Long, role: String?): DiscordChannel? {
if (channels.find { it.id == id } != null) {
return null
}
val out = DiscordChannel(id, role)
channels.add(out)
saveChannels()
return out
}
private fun saveChannels() {
fun saveChannels() {
SERVERS_FILE.writeText(
json.stringify(
DiscordChannel.serializer().list,
channels
))
)
)
SERVICE_CHANNELS_FILE.writeText(
json.stringify(
ServiceChannel.serializer().list,
serviceChannels
)
)
}
}

View File

@@ -1,62 +0,0 @@
package de.wulkanat
import de.wulkanat.model.BlogPostPreview
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import de.wulkanat.web.SiteWatcher
import kotlin.system.exitProcess
class Cli : ListenerAdapter() {
override fun onMessageReceived(event: MessageReceivedEvent) {
val msg = event.message.contentRaw
if (event.author.idLong != Admin.userId ||
!msg.startsWith("%!")
) {
return
}
val command = msg.removePrefix("%!").split(" ")
try {
when (command[0]) {
"stop" -> exitProcess(1)
"fakeUpdate" -> {
SiteWatcher.newestBlog = BlogPostPreview(
title = "FakePost",
imgUrl = "",
fullPostUrl = "",
author = "wulkanat",
date = "now",
description = "Lorem Ipsum"
)
Admin.println("Posting on next update cycle.")
}
"addChannel" -> {
val channel = command[1].toLong()
var role: String? = null
if (command.size == 3) {
role = command[2]
}
val serverChannel = Channels.testServerId(channel)
val roleName = serverChannel?.guild?.getRoleById(role ?: "")
if (serverChannel != null) {
if (roleName != null || role == null || role == "everyone") {
Channels.addChannel(channel, role)
Admin.println("Added server '${serverChannel.name}' for role '${roleName ?: role}'")
} else {
Admin.warning("Unknown Role ID")
}
} else {
Admin.warning("Unknown Channel ID")
}
}
"info" -> {
Admin.info()
}
}
} catch (e: ArrayIndexOutOfBoundsException) {
// noop
}
}
}

View File

@@ -1,20 +1,43 @@
package de.wulkanat
import de.wulkanat.extensions.ensureExists
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import java.io.File
@Serializable
data class DiscordChannel(
val id: Long,
val mentionedRole: String? = null
var mentionedRole: String? = null,
var autoPublish: Boolean = false,
var message: CustomMessage? = null
)
@Serializable
data class ServiceChannel(
val id: Long
)
@Serializable
data class CustomMessage(
var message: String,
var pushAnnouncement: Boolean = false
)
@Serializable
data class AdminFile(
val adminId: Long,
val token: String,
val updateMs: Long
val adminId: Long = 12345,
val token: String = "12345",
val updateMs: Long = 30000,
val watchingMessage: String = "for new Blogposts",
val offlineMessage: String = "CONNECTION FAILED"
)
val SERVERS_FILE = File("servers.json")
val ADMIN_FILE = File("admin.json")
val json = Json(JsonConfiguration.Stable)
val SERVERS_FILE = File("servers.json").ensureExists(json.stringify(DiscordChannel.serializer().list, listOf()))
val SERVICE_CHANNELS_FILE =
File("service_channels.json").ensureExists(json.stringify(ServiceChannel.serializer().list, listOf()))
val ADMIN_FILE = File("admin.json").ensureExists(json.stringify(AdminFile.serializer(), AdminFile()))

View File

@@ -0,0 +1,15 @@
package de.wulkanat
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.Activity
object DiscordRpc {
var jda: JDA? = null
fun updatePresence(available: Boolean) {
jda ?: return
jda!!.presence.activity = Activity.watching(if (available) Admin.message else Admin.offlineMessage)
jda!!.presence.isIdle = !available
}
}

View File

@@ -0,0 +1,10 @@
package de.wulkanat
import net.dv8tion.jda.api.events.ExceptionEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
class ErrorHandler : ListenerAdapter() {
override fun onException(event: ExceptionEvent) {
Admin.error(event.cause.message ?: event.cause.localizedMessage, event.cause.stackTrace.toString())
}
}

View File

@@ -7,25 +7,28 @@ import de.wulkanat.web.SiteWatcher
import kotlin.concurrent.timer
fun main() {
// TODO: move toke into file
val builder = JDABuilder.createLight(
Admin.token,
GatewayIntent.GUILD_MESSAGES, GatewayIntent.DIRECT_MESSAGES)
.addEventListeners(Bot())
.setActivity(Activity.watching("for new Blogposts"))
.setActivity(Activity.watching(Admin.message))
.build()
builder.addEventListener(Cli())
builder.addEventListener(AdminCli())
builder.addEventListener(ErrorHandler())
builder.addEventListener(OwnerCli())
builder.awaitReady()
Channels.jda = builder
Admin.jda = builder
DiscordRpc.jda = builder
Admin.info()
Runtime.getRuntime().addShutdownHook(object : Thread() {
override fun run() {
println("Shutting down...")
Admin.printlnBlocking("Shutting down")
println("Sending shutdown notice to Admin, waiting 5s...")
Admin.println("Shutting down")
sleep(5000)
}
})

View File

@@ -0,0 +1,215 @@
package de.wulkanat
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import java.awt.Color
class OwnerCli : ListenerAdapter() {
private val prefix = "%!"
override fun onMessageReceived(event: MessageReceivedEvent) {
val msg = event.message.contentRaw
// Only accept admin requests
if (event.message.member?.hasPermission(Permission.ADMINISTRATOR) != true || !msg.startsWith(prefix)) {
return
}
val command = msg.removePrefix(prefix).split(Regex("\\s+"))
val channelId = event.message.channel.idLong
when (command.first()) {
"add" -> {
val result = Channels.addChannel(channelId, null)
if (result == null) {
event.message.channel.sendMessage("Already added.").queue()
} else {
event.message.channel.sendMessage("Added.").queue()
Admin.info()
}
}
"remove" -> {
val result = Channels.channels.removeAll { it.id == channelId }
Channels.saveChannels()
if (result) {
event.message.channel.sendMessage("Removed.").queue()
} else {
event.message.channel.sendMessage("This channel is not registered.").queue()
}
}
"publish" -> {
val result = Channels.channels.find { it.id == channelId }
if (result != null) {
if (command.size > 1 && listOf("on", "off").contains(command[1])) {
result.autoPublish = command[1] == "on"
Channels.saveChannels()
event.message.channel.sendMessage("Auto publish is now ${command[1]}").queue()
} else {
event.message.channel.sendMessage("Usage: `${prefix}publish [on|off]`")
}
} else {
event.message.channel.sendMessage("Channel not registered.").queue()
}
}
"ping" -> {
val result = Channels.channels.find { it.id == channelId }
if (result != null) {
if (command.size > 1) {
val roles = event.message.guild.getRolesByName(command[1], false)
result.mentionedRole = when {
command[1] == "everyone" -> {
event.message.channel.sendMessage("Now pinging everyone.").queue()
"everyone"
}
command[1] == "none" -> {
event.message.channel.sendMessage("Now pinging none.").queue()
null
}
roles.firstOrNull() != null -> {
event.message.channel.sendMessage("Now pinging ${roles.first().name}").queue()
roles.first().id
}
else -> {
event.message.channel.sendMessage("Unknown role.").queue()
result.mentionedRole
}
}
Channels.saveChannels()
} else {
event.message.channel.sendMessage("Usage: `${prefix}ping [everyone|none|roleName]`")
}
} else {
event.message.channel.sendMessage("Channel is not registered.").queue()
}
}
"setMessage" -> {
val result = Channels.channels.find { it.id == channelId }
if (result != null) {
if (command.size > 1) {
val message = event.message.contentRaw.removePrefix("${prefix}setMessage").trim()
result.message = CustomMessage(message)
Channels.saveChannels()
event.message.channel.sendMessage("Set `$message` as message.").queue()
} else {
event.message.channel.sendMessage("Usage: `${prefix}setMessage [message]`")
}
} else {
event.message.channel.sendMessage("Channel is not registered.").queue()
}
}
"resetMessage" -> {
val result = Channels.channels.find { it.id == channelId }
if (result != null) {
result.message = null
Channels.saveChannels()
event.message.channel.sendMessage("Reset to no message.").queue()
} else {
event.message.channel.sendMessage("Channel is not registered.").queue()
}
}
"serviceChannel" -> {
if (command.size > 1 && listOf("add", "remove").contains(command[1])) {
if (command[1] == "add") {
if (Channels.serviceChannels.find { it.id == channelId } != null) {
event.message.channel.sendMessage("Already a service channel.").queue()
} else {
Channels.serviceChannels.add(ServiceChannel(channelId))
Channels.saveChannels()
event.message.channel.sendMessage("Added as service channel.").queue()
}
} else {
event.message.channel.sendMessage(
if (Channels.serviceChannels.removeAll { it.id == channelId }) "Channel removed."
else "Not a service channel."
).queue()
}
Channels.saveChannels()
} else {
event.message.channel.sendMessage("Usage: `${prefix}serviceChannel [add|remove]`")
}
}
"publishMessage" -> {
val result = Channels.channels.find { it.id == channelId }
if (result != null) {
if (result.message != null) {
if (command.size > 1 && listOf("on", "off").contains(command[1])) {
result.message?.pushAnnouncement = command[1] == "on"
Channels.saveChannels()
event.message.channel.sendMessage("Auto publish (message) is now ${command[1]}").queue()
} else {
event.message.channel.sendMessage("Usage: `${prefix}publishMessage [on|off]`")
}
} else {
event.message.channel.sendMessage("Channel has no custom message.").queue()
}
} else {
event.message.channel.sendMessage("Channel not registered.").queue()
}
}
"info" -> {
event.message.channel.sendMessage(
EmbedBuilder()
.setTitle("Server overview")
.setColor(Color.GREEN)
.setDescription("""
${Channels.getServerNames(event.message.guild.idLong).joinToString("\n")}
**_Service Channels_**
${Channels.getServiceChannelServers(event.message.guild.idLong).joinToString("\n")}
""".trimIndent())
.setAuthor(Admin.admin?.name, Admin.admin?.avatarUrl, Admin.admin?.avatarUrl)
.build()
).queue()
}
"report" -> {
val errorReport = event.message.contentRaw.removePrefix("${prefix}report")
Admin.error(event.message.guild.name, errorReport, event.author)
event.message.channel.sendMessage(
EmbedBuilder()
.setTitle("Error Report Received")
.setColor(Color.RED)
.setDescription(errorReport)
.setAuthor(Admin.admin?.name, Admin.admin?.avatarUrl, Admin.admin?.avatarUrl)
.build()
).queue()
}
"help" -> {
event.message.channel.sendMessage(
EmbedBuilder()
.setTitle("Help")
.setColor(Color.YELLOW)
.setAuthor(Admin.admin?.name, Admin.admin?.avatarUrl, Admin.admin?.avatarUrl)
.setDescription(
"""
**${prefix}add**
Add this channel to the notified list
**${prefix}serviceChannel [add|remove]**
Add or remove this channel to receive service message from the bot developer (recommended)
**${prefix}remove**
Remove this channel to the notified list
**${prefix}publish [on|off]**
[Community|Partner|Verified only] Auto publish the message if in an announcement channel
**${prefix}ping [none|everyone|roleName]**
What role to ping
**${prefix}setMessage [message]**
Set a custom message to show
**${prefix}resetMessage**
Reset the message
**${prefix}info**
Show an overview about all channels registered on this server
**${prefix}report**
Report an issue to the Bot Admin (this will share your user name so they can contact you)
**${prefix}help**
Show this message
""".trimIndent()
)
.build()
).queue()
}
}
}
}

View File

@@ -0,0 +1,11 @@
package de.wulkanat.extensions
import java.io.File
fun File.ensureExists(defaultText: String? = null): File {
if (!this.exists()) {
this.createNewFile()
this.writeText(defaultText ?: return this)
}
return this
}

View File

@@ -0,0 +1,28 @@
package de.wulkanat.extensions
import Inaccessibles
import net.dv8tion.jda.api.entities.Message
import net.dv8tion.jda.api.entities.MessageChannel
import net.dv8tion.jda.api.requests.restaction.MessageAction
import net.dv8tion.jda.internal.requests.Method
import net.dv8tion.jda.internal.requests.Route
import net.dv8tion.jda.internal.requests.restaction.MessageActionImpl
import net.dv8tion.jda.internal.utils.Checks
fun MessageChannel.crosspostById(messageId: String): MessageAction {
Checks.isSnowflake(messageId, "Message ID")
val route = CROSSPOST_MESSAGE.compile(id, messageId)
return MessageActionImpl(jda, route, this).append("This is not of your interest.")
}
fun Message.crosspost(): MessageAction {
val messageId = Inaccessibles.toUnsignedString(idLong)
return channel.crosspostById(messageId)
}
val CROSSPOST_MESSAGE: Route = Inaccessibles.getRoute(
Method.POST,
"channels/{channel_id}/messages/{message_id}/crosspost"
)

View File

@@ -1,6 +1,7 @@
package de.wulkanat.web
import de.wulkanat.Admin
import de.wulkanat.DiscordRpc
import de.wulkanat.model.BlogPostPreview
import org.jsoup.Jsoup
import java.io.IOException
@@ -8,10 +9,9 @@ import java.io.IOException
object SiteWatcher {
private const val BLOG_INDEX_URL = "https://www.hytale.com/news"
var newestBlog: BlogPostPreview? = null
private var siteOnline = false
fun hasNewBlogPost(): Boolean {
Admin.silent("Updating...")
try {
val doc = Jsoup.connect(BLOG_INDEX_URL).get()
val newBlog = BlogPostParser.getFistBlog(doc)
@@ -27,11 +27,18 @@ object SiteWatcher {
newestBlog = newBlog
}
} catch (e: IOException) {
Admin.error("Connection to Hytale Server failed", e)
Admin.error("Connection to Hytale Server failed", e.message ?: e.localizedMessage)
siteOnline = false
DiscordRpc.updatePresence(siteOnline)
return false
}
if (siteOnline) {
siteOnline = true
DiscordRpc.updatePresence(siteOnline)
}
return true
}
}