mirror of
https://github.com/HMCore/Orbot.git
synced 2025-12-12 13:56:18 +00:00
Compare commits
10 Commits
v1.2
...
refactor-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
736c36d069 | ||
|
|
69220fba32 | ||
|
|
a8ec59c7ca | ||
|
|
fcaa8377c1 | ||
|
|
4c4e4dc992 | ||
|
|
8b98d4ba3c | ||
|
|
0877883e3c | ||
|
|
a78c2343da | ||
|
|
490a5dcd41 | ||
|
|
5969a2f221 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
servers.json
|
||||
admin.json
|
||||
test.json
|
||||
config.json
|
||||
service_channels.json
|
||||
*.hprof
|
||||
/build
|
||||
/.gradle
|
||||
/build/
|
||||
/.gradle/
|
||||
/.idea/
|
||||
16
.idea/codeStyles/Project.xml
generated
16
.idea/codeStyles/Project.xml
generated
@@ -1,6 +1,22 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
|
||||
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/dictionaries/wulkanat.xml
generated
1
.idea/dictionaries/wulkanat.xml
generated
@@ -2,6 +2,7 @@
|
||||
<dictionary name="wulkanat">
|
||||
<words>
|
||||
<w>crosspost</w>
|
||||
<w>hytale</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
228
.idea/workspace.xml
generated
228
.idea/workspace.xml
generated
@@ -1,9 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="1aabf22b-2f57-46ac-9973-367d8668ffd3" name="Default Changelist" comment="[1.1]">
|
||||
<list default="true" id="1aabf22b-2f57-46ac-9973-367d8668ffd3" name="Default Changelist" comment="no idea what that did">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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/DiscordRpc.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/de/wulkanat/DiscordRpc.kt" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -28,52 +31,6 @@
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Source Sets" type="e897c970:GradleViewContributor$SourceSetsNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="build" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="build setup" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="documentation" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="help" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="other" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||
<item name="HytaleUpdateBot" type="f1a62948:ProjectNode" />
|
||||
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||
<item name="verification" type="c8890929:TasksNode$1" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</tree_state>
|
||||
@@ -84,27 +41,37 @@
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Kotlin Object" />
|
||||
<option value="Kotlin File" />
|
||||
<option value="Class" />
|
||||
<option value="Kotlin Class" />
|
||||
<option value="Kotlin Object" />
|
||||
<option value="Kotlin File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="not-sure" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="HighlightingSettingsPerFile">
|
||||
<setting file="file://$PROJECT_DIR$/build.gradle" root0="SKIP_INSPECTION" />
|
||||
</component>
|
||||
<component name="MacroExpansionManager">
|
||||
<option name="directoryName" value="o7p0t8es" />
|
||||
</component>
|
||||
<component name="ProjectId" id="1g2oQiuUv1Bu6ZCW2NSVzB1V6Sc" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showExcludedFiles" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</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$/src" />
|
||||
<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" />
|
||||
@@ -115,6 +82,7 @@
|
||||
<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">
|
||||
@@ -122,7 +90,7 @@
|
||||
<recent name="E:\Projects\Kotlin_Proj\HytaleUpdateBot\src\main\kotlin\de\wulkanat" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Gradle.HytaleUpdateBot [fatJar]">
|
||||
<component name="RunManager" selected="Application.MainKt">
|
||||
<configuration name="HytaleUpdateBot [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
@@ -139,7 +107,9 @@
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="HytaleUpdateBot [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||
@@ -158,7 +128,9 @@
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="HytaleUpdateBot [fatJar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||
@@ -177,7 +149,9 @@
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="HytaleUpdateBot-all-1.0-SNAPSHOT.jar" type="JarApplication" temporary="true">
|
||||
@@ -200,13 +174,13 @@
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Gradle.HytaleUpdateBot [fatJar]" />
|
||||
<item itemvalue="Kotlin.MainKt" />
|
||||
<item itemvalue="Gradle.HytaleUpdateBot [build]" />
|
||||
<item itemvalue="Gradle.HytaleUpdateBot [clean]" />
|
||||
<item itemvalue="JAR Application.HytaleUpdateBot-all-1.0-SNAPSHOT.jar" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="project-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
@@ -232,106 +206,60 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1597438052596</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="3" />
|
||||
<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>
|
||||
<task id="LOCAL-00004" summary="Add service announcement channel">
|
||||
<created>1597839954908</created>
|
||||
<option name="number" value="00004" />
|
||||
<option name="presentableId" value="LOCAL-00004" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1597839954909</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00005" summary="prepare twitter integration">
|
||||
<created>1601042375685</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1601042375685</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="oldMeFiltersMigrated" value="true" />
|
||||
</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="1597434105164">
|
||||
<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="1597434105164" />
|
||||
<state x="-1050" y="581" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1597352463714">
|
||||
<screen x="-1050" y="105" width="1050" height="1640" />
|
||||
</state>
|
||||
<state x="-1050" y="581" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@-1050.105.1050.1640" timestamp="1597352463714" />
|
||||
<state x="633" y="446" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog" timestamp="1597362173063">
|
||||
<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="1597428556346">
|
||||
<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="1597428556346" />
|
||||
<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>
|
||||
<state x="739" y="173" width="484" height="693" key="#org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597362199927" />
|
||||
<state x="128" y="270" width="490" height="591" key="#xdebugger.evaluate" timestamp="1597332665464">
|
||||
<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 x="569" y="115" key="CommitChangelistDialog2" timestamp="1597438290366">
|
||||
<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="1597438290366" />
|
||||
<state width="1876" height="161" key="GridCell.Tab.0.bottom" timestamp="1597438298864">
|
||||
<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="1597438298864" />
|
||||
<state width="1876" height="161" key="GridCell.Tab.0.center" timestamp="1597438298864">
|
||||
<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="1597438298864" />
|
||||
<state width="1876" height="161" key="GridCell.Tab.0.left" timestamp="1597438298864">
|
||||
<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="1597438298864" />
|
||||
<state width="1876" height="161" key="GridCell.Tab.0.right" timestamp="1597438298864">
|
||||
<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="1597438298864" />
|
||||
<state width="1006" height="588" key="GridCell.Tab.1.bottom" timestamp="1597366506508">
|
||||
<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="1006" height="588" key="GridCell.Tab.1.bottom/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597366506508" />
|
||||
<state width="1006" height="588" key="GridCell.Tab.1.center" timestamp="1597366506506">
|
||||
<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="1006" height="588" key="GridCell.Tab.1.center/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597366506506" />
|
||||
<state width="1006" height="588" key="GridCell.Tab.1.left" timestamp="1597366506505">
|
||||
<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="1006" height="588" key="GridCell.Tab.1.left/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597366506505" />
|
||||
<state width="1006" height="588" key="GridCell.Tab.1.right" timestamp="1597366506507">
|
||||
<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="1006" height="588" key="GridCell.Tab.1.right/0.0.1920.1040/1920.-213.2560.1400/-1050.105.1050.1640@0.0.1920.1040" timestamp="1597366506507" />
|
||||
<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="1597438121430">
|
||||
<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="1597438121430" />
|
||||
<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="1597363843473">
|
||||
<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="1597363843473" />
|
||||
<MESSAGE value="Add service announcement channel" />
|
||||
<MESSAGE value="fix crash on missing permission add removeInactive command" />
|
||||
<MESSAGE value="prepare twitter integration" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="prepare twitter integration" />
|
||||
</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>22</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" />
|
||||
|
||||
122
README.md
122
README.md
@@ -1,82 +1,72 @@
|
||||
# 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.
|
||||
* first go to the release tab, download the jar, and put it in a folder
|
||||
* 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
|
||||
}
|
||||
```
|
||||
* add your servers to `servers.json`
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 15050067772322222,
|
||||
"mentionedRole": "everyone",
|
||||
"autoPublish":true
|
||||
},
|
||||
{
|
||||
"id": 74050067772325222,
|
||||
"mentionedRole": null,
|
||||
"autoPublish":false
|
||||
},
|
||||
{
|
||||
"id": 74050067772325222,
|
||||
"mentionedRole": "74036067771625222",
|
||||
"autoPublish":false
|
||||
}
|
||||
]
|
||||
```
|
||||
* add a `test.json` with the same schema as the `server.json`. When
|
||||
you enable test mode, the servers from there will be used instead allowing
|
||||
you to test if it works.
|
||||
## 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 | off | [Community|Partner|Verified only] Auto publish the message if in an announcement channel |
|
||||
| %!ping | none | everyone | roleName | What role to ping |
|
||||
| %!setMessage | message | Set a custom message when a blogpost arrives |
|
||||
| %!resetMessage | | Reset the custom message to none |
|
||||
| %!serviceChannel | add | remove | Add/remove channel from service notification list |
|
||||
| %!publishMessage | on | off | [Community|Partner|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 `config.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 `config.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 -Xmx1024m -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]`
|
||||
|
||||
I'm not 100% certain how much RAM the bot needs, default is typically `-Xmx256m`, and that lead to some issues, `-Xmx512m` is probably plenty, because my server has
|
||||
tons of unused ram I set it to `-Xmx2048m`, just try and look what works for you.
|
||||
|
||||
## 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
|
||||
Please edit the JSON file.
|
||||
You can force an update by calling
|
||||
```
|
||||
%!refreshList
|
||||
```
|
||||
### Testing
|
||||
Switching between test and production files
|
||||
```
|
||||
%!testMode
|
||||
%!fakeUpdate
|
||||
```
|
||||
```
|
||||
%!productionMode
|
||||
```
|
||||
**WARNING**: Initiating a fake update is not being cancelled by switching
|
||||
to production.
|
||||
### 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) |
|
||||
| !removeInactive | | Remove inactive channels |
|
||||
| !help | | Show a help dialog with all these commands |
|
||||
|
||||
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 `config.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
|
||||
in news channels
|
||||
|
||||
47
build.gradle
47
build.gradle
@@ -1,47 +0,0 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.61'
|
||||
}
|
||||
|
||||
group 'de.wulkanat'
|
||||
version '1.1'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
|
||||
compile 'net.dv8tion:JDA:4.2.0_189'
|
||||
compile 'org.jsoup:jsoup:1.13.1'
|
||||
compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes('Main-Class': 'de.wulkanat.MainKt')
|
||||
}
|
||||
}
|
||||
|
||||
task fatJar(type: Jar) {
|
||||
baseName = project.name + '-all'
|
||||
from((configurations.compile.findAll { !it.path.endsWith(".pom") }).collect {
|
||||
it.isDirectory() ? it : zipTree(it)
|
||||
})
|
||||
with jar
|
||||
manifest {
|
||||
attributes 'Main-Class': 'de.wulkanat.MainKt',
|
||||
'Implementation-Version': version
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
61
build.gradle.kts
Normal file
61
build.gradle.kts
Normal file
@@ -0,0 +1,61 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
val kotlinVersion = "1.4.10"
|
||||
|
||||
kotlin("jvm") version kotlinVersion
|
||||
kotlin("plugin.serialization") version kotlinVersion
|
||||
|
||||
id("org.jetbrains.kotlin.kapt") version kotlinVersion
|
||||
}
|
||||
|
||||
group = "de.wulkanat"
|
||||
version = "2.0.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven("https://kotlin.bintray.com/koltinx")
|
||||
maven("https://dl.bintray.com/kordlib/Kord")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test-junit"))
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
implementation("org.jsoup:jsoup:1.13.1")
|
||||
|
||||
implementation("dev.kord:kord-common:0.7.0-RC")
|
||||
implementation("com.gitlab.kordlib.kordx:kordx-commands-runtime-kord:0.3.4")
|
||||
implementation("com.gitlab.kordlib:kordx.emoji:0.4.0")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
|
||||
|
||||
kapt("com.gitlab.kordlib.kordx:kordx-commands-processor:0.3.4")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnit()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
tasks.withType<Jar> {
|
||||
manifest {
|
||||
attributes(mapOf(Pair("Main-Class", "de.wulkanat.MainKt")))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.create<Jar>("fatJar") {
|
||||
archiveBaseName.set("${project.name}-all")
|
||||
manifest {
|
||||
attributes["Implementation-Version"] = archiveVersion
|
||||
attributes["Main-Class"] = "de.wulkanat.MainKt"
|
||||
}
|
||||
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
|
||||
with(tasks.jar.get() as CopySpec)
|
||||
}
|
||||
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Thu Aug 13 18:41:46 CEST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
rootProject.name = 'HytaleUpdateBot'
|
||||
|
||||
2
settings.gradle.kts
Normal file
2
settings.gradle.kts
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = "HytaleUpdateBot"
|
||||
|
||||
@@ -1,142 +1,82 @@
|
||||
package de.wulkanat
|
||||
|
||||
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 com.gitlab.kordlib.common.entity.Snowflake
|
||||
import com.gitlab.kordlib.core.Kord
|
||||
import com.gitlab.kordlib.core.behavior.channel.createEmbed
|
||||
import com.gitlab.kordlib.core.entity.User
|
||||
import com.gitlab.kordlib.rest.builder.message.EmbedBuilder
|
||||
import de.wulkanat.files.Config
|
||||
import de.wulkanat.files.ServiceChannels
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.awt.Color
|
||||
|
||||
object Admin {
|
||||
val userId: Long
|
||||
val token: String
|
||||
val updateMs: Long
|
||||
|
||||
var testModeEnabled: Boolean = false
|
||||
set(value) {
|
||||
if (field == value)
|
||||
return
|
||||
|
||||
field = value
|
||||
|
||||
if (value) {
|
||||
jda?.presence?.setPresence(Activity.of(Activity.ActivityType.DEFAULT, "Testing mode, hold on..."), true)
|
||||
} else {
|
||||
jda?.presence?.setPresence(Activity.watching("for new Blogposts"), false)
|
||||
}
|
||||
|
||||
Channels.channels = Channels.refreshFromDisk()
|
||||
Admin.info()
|
||||
}
|
||||
|
||||
init {
|
||||
val admin = Json(JsonConfiguration.Stable).parse(AdminFile.serializer(), ADMIN_FILE.readText())
|
||||
userId = admin.adminId
|
||||
token = admin.token
|
||||
updateMs = admin.updateMs
|
||||
}
|
||||
|
||||
var jda: JDA? = null
|
||||
var jda: Kord? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
admin = value?.retrieveUserById(userId)?.complete()
|
||||
if (admin == null) {
|
||||
kotlin.io.println("Connection to de.wulkanat.Admin failed!")
|
||||
} else {
|
||||
kotlin.io.println("Connected to ${admin!!.name}. No further errors will be printed here.")
|
||||
GlobalScope.launch {
|
||||
admin = value?.getUser(Snowflake(Config.adminId))
|
||||
if (admin == null) {
|
||||
kotlin.io.println("Connection to de.wulkanat.Admin failed!")
|
||||
} else {
|
||||
kotlin.io.println("Connected to ${admin!!.username}. No further errors will be printed here.")
|
||||
}
|
||||
}
|
||||
}
|
||||
private var admin: User? = null
|
||||
var admin: User? = null
|
||||
|
||||
fun println(msg: String) {
|
||||
sendDevMessage(
|
||||
EmbedBuilder()
|
||||
.setTitle(msg)
|
||||
.setColor(Color.WHITE)
|
||||
.build(),
|
||||
msg
|
||||
)
|
||||
suspend fun println(msg: String) {
|
||||
sendDevMessage(msg) {
|
||||
title = msg
|
||||
color = Color.WHITE
|
||||
}
|
||||
}
|
||||
|
||||
fun printlnBlocking(msg: String) {
|
||||
senDevMessageBlocking(
|
||||
EmbedBuilder()
|
||||
.setTitle(msg)
|
||||
.setColor(Color.WHITE)
|
||||
.build(),
|
||||
msg
|
||||
)
|
||||
suspend fun error(msg: String, error: String, author: User? = null) {
|
||||
sendDevMessage("$msg\n\n$error") {
|
||||
title = msg
|
||||
description = error
|
||||
color = Color.RED
|
||||
author?.let { author {
|
||||
name = it.tag
|
||||
icon = it.avatar.url
|
||||
url = it.avatar.url
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
fun error(msg: String, error: String) {
|
||||
sendDevMessage(
|
||||
EmbedBuilder()
|
||||
.setTitle(msg)
|
||||
.setDescription(error)
|
||||
.setColor(Color.RED)
|
||||
.build()
|
||||
, "$msg\n\n${error}"
|
||||
)
|
||||
suspend fun warning(msg: String) {
|
||||
sendDevMessage(msg) {
|
||||
title = msg
|
||||
color = Color.YELLOW
|
||||
}
|
||||
}
|
||||
|
||||
fun errorBlocking(msg: String, error: Exception) {
|
||||
senDevMessageBlocking(
|
||||
EmbedBuilder()
|
||||
.setTitle(msg)
|
||||
.setDescription(error.message)
|
||||
.setColor(Color.RED)
|
||||
.build()
|
||||
, "$msg\n\n${error.message}"
|
||||
)
|
||||
}
|
||||
|
||||
fun warning(msg: String) {
|
||||
sendDevMessage(
|
||||
EmbedBuilder()
|
||||
.setTitle(msg)
|
||||
.setColor(Color.YELLOW)
|
||||
.build(),
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
fun info() {
|
||||
sendDevMessage(
|
||||
EmbedBuilder()
|
||||
.setTitle("Now watching for new Hytale Blogposts every ${updateMs / 1000}s")
|
||||
.setDescription(Channels.getServerNames().joinToString("\n"))
|
||||
.setColor(Color.GREEN)
|
||||
.build(),
|
||||
"Now watching for new Hytale BlogPosts"
|
||||
)
|
||||
suspend fun info() {
|
||||
sendDevMessage("Now watching for new Hytale BlogPosts") {
|
||||
title = "Now watching for new Hytale Blogposts every ${Config.updateMs / 1000}s"
|
||||
description = """
|
||||
${ServiceChannels.getServerNames().joinToString("\n")}
|
||||
|
||||
**_Service Channels_**
|
||||
${ServiceChannels.getServiceChannelServers().joinToString("\n")}
|
||||
""".trimIndent()
|
||||
color = Color.GREEN
|
||||
}
|
||||
}
|
||||
|
||||
fun silent(msg: String) {
|
||||
kotlin.io.println(msg)
|
||||
}
|
||||
|
||||
private fun senDevMessageBlocking(messageEmbed: MessageEmbed, fallback: String) {
|
||||
admin = jda!!.retrieveUserById(userId).complete()
|
||||
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
|
||||
private suspend inline fun sendDevMessage(fallback: String, crossinline embed: EmbedBuilder.() -> Unit) {
|
||||
val devChannel = admin?.getDmChannel() ?: kotlin.run {
|
||||
kotlin.io.println(fallback)
|
||||
return
|
||||
}
|
||||
|
||||
devChannel.complete()
|
||||
.sendMessage(messageEmbed).complete()
|
||||
}
|
||||
|
||||
private fun sendDevMessage(messageEmbed: MessageEmbed, fallback: String) {
|
||||
val devChannel = admin?.openPrivateChannel() ?: kotlin.run {
|
||||
kotlin.io.println(fallback)
|
||||
return
|
||||
}
|
||||
|
||||
devChannel.queue {
|
||||
it.sendMessage(messageEmbed).queue()
|
||||
}
|
||||
devChannel.createEmbed(embed)
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
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.JDA
|
||||
import net.dv8tion.jda.api.Permission
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed
|
||||
import net.dv8tion.jda.api.entities.TextChannel
|
||||
|
||||
object Channels {
|
||||
var jda: JDA? = null
|
||||
val json = Json(JsonConfiguration.Stable)
|
||||
|
||||
/**
|
||||
* List of (ServerID, ChannelID)
|
||||
*/
|
||||
var channels: MutableList<DiscordChannel> = refreshFromDisk()
|
||||
|
||||
fun sentToAll(messageEmbed: MessageEmbed) {
|
||||
if (jda == null)
|
||||
return
|
||||
|
||||
for (channel_pair in channels) {
|
||||
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
|
||||
|
||||
if (channel_pair.mentionedRole != null) {
|
||||
val message = if (channel_pair.mentionedRole == "everyone") {
|
||||
"New Blogpost @everyone"
|
||||
} else {
|
||||
"New Blogpost <@&${channel_pair.mentionedRole}>"
|
||||
}
|
||||
channel.sendMessage(message).queue()
|
||||
}
|
||||
channel.sendMessage(messageEmbed).queue {
|
||||
if (channel_pair.autoPublish) {
|
||||
it.crosspost().queue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkEveryonePermission() {
|
||||
for (channel_pair in channels) {
|
||||
val channel = jda!!.getTextChannelById(channel_pair.id) ?: continue
|
||||
|
||||
if (channel_pair.mentionedRole == "everyone" &&
|
||||
channel.guild.selfMember.hasPermission(Permission.MESSAGE_MENTION_EVERYONE)
|
||||
) {
|
||||
Admin.warning("Cannot mention everyone on ${channel.guild.name}")
|
||||
} else if (channel.guild.selfMember.hasPermission(Permission.MESSAGE_WRITE)) {
|
||||
Admin.warning("Cannot send any messages on ${channel.guild.name}")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshFromDisk(): MutableList<DiscordChannel> {
|
||||
return json.parse(
|
||||
DiscordChannel.serializer().list, (if (Admin.testModeEnabled) {
|
||||
TEST_FILE
|
||||
} else {
|
||||
SERVERS_FILE
|
||||
}).readText()
|
||||
).toMutableList()
|
||||
}
|
||||
|
||||
fun getServerNames(): List<String> {
|
||||
if (jda == null)
|
||||
return listOf()
|
||||
|
||||
return channels.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 = when (it.mentionedRole) {
|
||||
null -> ""
|
||||
"everyone" -> " @everyone"
|
||||
else -> " @${channel.guild.getRoleById(it.mentionedRole)?.name}"
|
||||
}
|
||||
"**${channel.guild.name}**\n#${channel.name}${role}"
|
||||
}
|
||||
}
|
||||
|
||||
fun testServerId(id: Long): TextChannel? {
|
||||
return jda?.getTextChannelById(id)
|
||||
}
|
||||
|
||||
fun addChannel(id: Long, role: String?) {
|
||||
channels.add(DiscordChannel(id, role))
|
||||
saveChannels()
|
||||
}
|
||||
|
||||
private fun saveChannels() {
|
||||
SERVERS_FILE.writeText(
|
||||
json.stringify(
|
||||
DiscordChannel.serializer().list,
|
||||
channels
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,50 +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 net.dv8tion.jda.api.events.ExceptionEvent
|
||||
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class Cli : ListenerAdapter() {
|
||||
override fun onPrivateMessageReceived(event: PrivateMessageReceivedEvent) {
|
||||
val msg = event.message.contentRaw
|
||||
if (event.author.idLong != Admin.userId ||
|
||||
!msg.startsWith("!")
|
||||
) {
|
||||
return
|
||||
}
|
||||
val command = msg.removePrefix("!").split(Regex("\\s+"))
|
||||
|
||||
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.")
|
||||
}
|
||||
"info" -> {
|
||||
Admin.info()
|
||||
}
|
||||
"refreshList" -> {
|
||||
Channels.channels = Channels.refreshFromDisk()
|
||||
Admin.info()
|
||||
}
|
||||
"testMode" -> {
|
||||
Admin.testModeEnabled = true
|
||||
}
|
||||
"productionMode" -> {
|
||||
Admin.testModeEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +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,
|
||||
val autoPublish: Boolean = false
|
||||
var mentionedRole: Long? = 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 TEST_FILE = File("test.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()))
|
||||
|
||||
17
src/main/kotlin/de/wulkanat/DiscordRpc.kt
Normal file
17
src/main/kotlin/de/wulkanat/DiscordRpc.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
// noop
|
||||
if (available) Admin.println("Back online") else Admin.error("Gone offline", "Can't reach Hytale server")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.wulkanat
|
||||
|
||||
import de.wulkanat.files.ServiceChannels
|
||||
import net.dv8tion.jda.api.JDABuilder
|
||||
import net.dv8tion.jda.api.entities.Activity
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent
|
||||
@@ -10,15 +11,17 @@ fun main() {
|
||||
val builder = JDABuilder.createLight(
|
||||
Admin.token,
|
||||
GatewayIntent.GUILD_MESSAGES, GatewayIntent.DIRECT_MESSAGES)
|
||||
.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
|
||||
ServiceChannels.client = builder
|
||||
Admin.jda = builder
|
||||
DiscordRpc.jda = builder
|
||||
Admin.info()
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(object : Thread() {
|
||||
@@ -32,7 +35,7 @@ fun main() {
|
||||
|
||||
timer("Updater", daemon = true, initialDelay = 0L, period = Admin.updateMs) {
|
||||
if (SiteWatcher.hasNewBlogPost()) {
|
||||
Channels.sentToAll(SiteWatcher.newestBlog!!.toMessageEmbed())
|
||||
ServiceChannels.sentToAll(SiteWatcher.newestBlog!!.toMessageEmbed())
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/main/kotlin/de/wulkanat/cli/AdminCli.kt
Normal file
109
src/main/kotlin/de/wulkanat/cli/AdminCli.kt
Normal file
@@ -0,0 +1,109 @@
|
||||
@file:AutoWired
|
||||
|
||||
package de.wulkanat.cli
|
||||
|
||||
import com.gitlab.kordlib.core.entity.channel.DmChannel
|
||||
import com.gitlab.kordlib.kordx.commands.annotation.AutoWired
|
||||
import com.gitlab.kordlib.kordx.commands.argument.primitive.BooleanArgument
|
||||
import com.gitlab.kordlib.kordx.commands.argument.text.StringArgument
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.precondition.precondition
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.prefix.kord
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.prefix.mention
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.respondEmbed
|
||||
import com.gitlab.kordlib.kordx.commands.kord.module.module
|
||||
import com.gitlab.kordlib.kordx.commands.model.command.invoke
|
||||
import com.gitlab.kordlib.kordx.commands.model.prefix.literal
|
||||
import com.gitlab.kordlib.kordx.commands.model.prefix.or
|
||||
import com.gitlab.kordlib.kordx.commands.model.prefix.prefix
|
||||
import de.wulkanat.Admin
|
||||
import de.wulkanat.extensions.alsoIf
|
||||
import de.wulkanat.extensions.isBotAdmin
|
||||
import de.wulkanat.files.ServiceChannels
|
||||
import de.wulkanat.model.BlogPostPreview
|
||||
import de.wulkanat.web.SiteWatcher
|
||||
|
||||
val prefixes = prefix {
|
||||
kord { mention() or literal("%!") }
|
||||
}
|
||||
|
||||
fun adminCommands() = module("admin-commands") {
|
||||
precondition { author.isBotAdmin && channel.asChannelOrNull() is DmChannel }
|
||||
|
||||
command("stop") {
|
||||
invoke {
|
||||
respond("Shutting down...")
|
||||
kord.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
command("info") {
|
||||
invoke {
|
||||
Admin.info()
|
||||
}
|
||||
}
|
||||
|
||||
command("fakeUpdate") {
|
||||
invoke {
|
||||
respond("THIS WILL CAUSE A MESSAGE ON **ALL** SERVERS.\nContinue? [y/n]")
|
||||
if (read(BooleanArgument(trueValue = "y", falseValue = "n"))) {
|
||||
respond("Sending fake update on next cycle")
|
||||
|
||||
SiteWatcher.newestBlog = BlogPostPreview(
|
||||
title = "FakePost",
|
||||
imgUrl = "",
|
||||
fullPostUrl = "",
|
||||
author = "wulkanat",
|
||||
date = "now",
|
||||
description = "Lorem Ipsum"
|
||||
)
|
||||
} else {
|
||||
respond("Aborting")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("serviceMessage") {
|
||||
invoke {
|
||||
respond("What's the title?")
|
||||
val title = read(StringArgument)
|
||||
respond("What's the message?")
|
||||
val message = read(StringArgument)
|
||||
respondEmbed {
|
||||
this.title = title
|
||||
description = message
|
||||
footer {
|
||||
text = "Is that correct? [y/n]"
|
||||
}
|
||||
}
|
||||
if (read(BooleanArgument(trueValue = "y", falseValue = "n"))) {
|
||||
respond("Sending")
|
||||
ServiceChannels.sendServiceMessage(title, message)
|
||||
} else {
|
||||
respond("Aborting")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("refreshList") {
|
||||
invoke {
|
||||
ServiceChannels.channels = ServiceChannels.refreshChannelsFromDisk()
|
||||
ServiceChannels.serviceChannels = ServiceChannels.refreshServiceChannelsFromDisk()
|
||||
Admin.info()
|
||||
}
|
||||
}
|
||||
|
||||
command("removeInactive") {
|
||||
invoke {
|
||||
respondEmbed {
|
||||
title = "Channels removed"
|
||||
|
||||
ServiceChannels.channels.removeAll { channel ->
|
||||
(ServiceChannels.testServerId(channel.id) == null).alsoIf(true) {
|
||||
field { name = channel.id.toString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
ServiceChannels.saveChannels()
|
||||
}
|
||||
}
|
||||
}
|
||||
159
src/main/kotlin/de/wulkanat/cli/OwnerCli.kt
Normal file
159
src/main/kotlin/de/wulkanat/cli/OwnerCli.kt
Normal file
@@ -0,0 +1,159 @@
|
||||
@file:AutoWired
|
||||
|
||||
package de.wulkanat.cli
|
||||
|
||||
import com.gitlab.kordlib.common.entity.Permission
|
||||
import com.gitlab.kordlib.kordx.commands.annotation.AutoWired
|
||||
import com.gitlab.kordlib.kordx.commands.argument.primitive.BooleanArgument
|
||||
import com.gitlab.kordlib.kordx.commands.argument.text.StringArgument
|
||||
import com.gitlab.kordlib.kordx.commands.kord.argument.RoleArgument
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.precondition.precondition
|
||||
import com.gitlab.kordlib.kordx.commands.kord.model.respondEmbed
|
||||
import com.gitlab.kordlib.kordx.commands.kord.module.module
|
||||
import com.gitlab.kordlib.kordx.commands.model.command.invoke
|
||||
import de.wulkanat.Admin
|
||||
import de.wulkanat.CustomMessage
|
||||
import de.wulkanat.ServiceChannel
|
||||
import de.wulkanat.files.ServiceChannels
|
||||
import java.awt.Color
|
||||
|
||||
// TODO: channel argument?
|
||||
fun ownerCommands() = module("owner-commands") {
|
||||
precondition {
|
||||
message.getAuthorAsMember()?.getPermissions()?.contains(Permission.Administrator) ?: false
|
||||
}
|
||||
|
||||
command("add") {
|
||||
invoke {
|
||||
if (ServiceChannels.addChannel(channel.id.longValue, null) == null) {
|
||||
respond("Already added.")
|
||||
} else {
|
||||
respond("Added.")
|
||||
Admin.info()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("remove") {
|
||||
invoke {
|
||||
val result = ServiceChannels.channels.removeAll { it.id == channel.id.longValue }
|
||||
ServiceChannels.saveChannels()
|
||||
if (result) {
|
||||
respond("Removed.")
|
||||
} else {
|
||||
respond("This channel is not registered.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("publish") {
|
||||
invoke(BooleanArgument(trueValue = "on", falseValue = "off")) { doAutoPublish ->
|
||||
ServiceChannels.channels.find { it.id == channel.id.longValue }?.also {
|
||||
it.autoPublish = doAutoPublish
|
||||
ServiceChannels.saveChannels()
|
||||
} ?: respond("Channel not registered")
|
||||
}
|
||||
}
|
||||
|
||||
command("ping") {
|
||||
invoke(RoleArgument) { role ->
|
||||
ServiceChannels.channels.find { it.id == channel.id.longValue }?.also {
|
||||
// TODO: @everyone
|
||||
it.mentionedRole = role.id.longValue
|
||||
ServiceChannels.saveChannels()
|
||||
} ?: respond("Channel not registered")
|
||||
}
|
||||
}
|
||||
|
||||
command("setMessage") {
|
||||
invoke(StringArgument) { message ->
|
||||
ServiceChannels.channels.find { it.id == channel.id.longValue}?.also {
|
||||
it.message = CustomMessage(message)
|
||||
respond("Set `$message` as a message.")
|
||||
} ?: respond("Channel not registered!")
|
||||
}
|
||||
}
|
||||
|
||||
command("resetMessage") {
|
||||
invoke {
|
||||
ServiceChannels.channels.find { it.id == channel.id.longValue }?.also {
|
||||
it.message = null
|
||||
respond("Reset to no message")
|
||||
} ?: respond("Channel not registered!")
|
||||
}
|
||||
}
|
||||
|
||||
command("serviceChannel") {
|
||||
invoke(BooleanArgument(trueValue = "add", falseValue = "remove")) { addChannel ->
|
||||
if (addChannel) {
|
||||
ServiceChannels.serviceChannels.find { it.id == channel.id.longValue }?.also {
|
||||
respond("Already a service channel")
|
||||
} ?: run {
|
||||
ServiceChannels.serviceChannels.add(ServiceChannel(channel.id.longValue))
|
||||
respond("Added as a service channel")
|
||||
}
|
||||
} else {
|
||||
respond(if (ServiceChannels.serviceChannels.removeAll { it.id == channel.id.longValue })
|
||||
"Channel removed" else "Not a service channel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("publishMessage") {
|
||||
invoke(BooleanArgument(trueValue = "on", falseValue = "off")) { doAutoPublish ->
|
||||
ServiceChannels.channels.find { it.id == channel.id.longValue }?.also {
|
||||
it.message?.pushAnnouncement = doAutoPublish
|
||||
ServiceChannels.saveChannels()
|
||||
respond("Auto publish is now ${if (doAutoPublish) "on" else "off"}")
|
||||
} ?: respond("Channel not registered!")
|
||||
}
|
||||
}
|
||||
|
||||
command("info") {
|
||||
invoke {
|
||||
respondEmbed {
|
||||
title = "Server Overview"
|
||||
color = Color.GREEN
|
||||
description = """
|
||||
${ServiceChannels.getServerNames(guild?.id?.longValue).joinToString("\n")}
|
||||
|
||||
**_Service Channels_**
|
||||
${ServiceChannels.getServiceChannelServers(guild?.id?.longValue).joinToString("\n")}
|
||||
""".trimIndent()
|
||||
Admin.admin?.let {
|
||||
author {
|
||||
name = it.username
|
||||
icon = it.avatar.url
|
||||
url = "https://github.com/wulkanat/BlogShot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command("report") {
|
||||
invoke {
|
||||
respond("What is the error you encountered?")
|
||||
val errorReport = read(StringArgument)
|
||||
respondEmbed {
|
||||
title = "Error Report Preview"
|
||||
color = Color.RED
|
||||
description = errorReport
|
||||
message.author?.let {
|
||||
author {
|
||||
name = it.username
|
||||
icon = it.avatar.url
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
respond("Send? [y/n]")
|
||||
if (read(BooleanArgument(trueValue = "y", falseValue = "n"))) {
|
||||
respond("Sent")
|
||||
Admin.error(guild?.asGuildOrNull()?.name ?: "Unknown Guild", errorReport, author)
|
||||
} else {
|
||||
respond("Aborting")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main/kotlin/de/wulkanat/extensions/File.kt
Normal file
11
src/main/kotlin/de/wulkanat/extensions/File.kt
Normal 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
|
||||
}
|
||||
8
src/main/kotlin/de/wulkanat/extensions/Generic.kt
Normal file
8
src/main/kotlin/de/wulkanat/extensions/Generic.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package de.wulkanat.extensions
|
||||
|
||||
inline fun <T> Boolean.alsoIf(other: T, body: () -> Unit): Boolean {
|
||||
if (this == other) {
|
||||
body()
|
||||
}
|
||||
return this
|
||||
}
|
||||
7
src/main/kotlin/de/wulkanat/extensions/User.kt
Normal file
7
src/main/kotlin/de/wulkanat/extensions/User.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package de.wulkanat.extensions
|
||||
|
||||
import com.gitlab.kordlib.core.entity.User
|
||||
import de.wulkanat.files.Config
|
||||
|
||||
val User.isBotAdmin: Boolean
|
||||
get() = id.longValue == Config.adminId
|
||||
26
src/main/kotlin/de/wulkanat/files/Config.kt
Normal file
26
src/main/kotlin/de/wulkanat/files/Config.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package de.wulkanat.files
|
||||
|
||||
import de.wulkanat.files.concept.SerializableObject
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object Config : SerializableObject<Config.Data>("config.json", Data(), Data.serializer()) {
|
||||
val adminId: Long
|
||||
get() = instance.adminId
|
||||
val token: String
|
||||
get() = instance.token
|
||||
val updateMs: Long
|
||||
get() = instance.updateMs
|
||||
val watchingMessage: String
|
||||
get() = instance.watchingMessage
|
||||
val offlineMessage: String
|
||||
get() = instance.offlineMessage
|
||||
|
||||
@Serializable
|
||||
data class Data(
|
||||
val adminId: Long = 12345,
|
||||
val token: String = "12345",
|
||||
val updateMs: Long = 30000,
|
||||
val watchingMessage: String = "for new Blogposts",
|
||||
val offlineMessage: String = "CONNECTION FAILED"
|
||||
)
|
||||
}
|
||||
4
src/main/kotlin/de/wulkanat/files/Servers.kt
Normal file
4
src/main/kotlin/de/wulkanat/files/Servers.kt
Normal file
@@ -0,0 +1,4 @@
|
||||
package de.wulkanat.files
|
||||
|
||||
object Servers {
|
||||
}
|
||||
182
src/main/kotlin/de/wulkanat/files/ServiceChannels.kt
Normal file
182
src/main/kotlin/de/wulkanat/files/ServiceChannels.kt
Normal file
@@ -0,0 +1,182 @@
|
||||
package de.wulkanat.files
|
||||
|
||||
import com.gitlab.kordlib.core.Kord
|
||||
import com.gitlab.kordlib.core.entity.Embed
|
||||
import com.gitlab.kordlib.rest.builder.message.EmbedBuilder
|
||||
import de.wulkanat.*
|
||||
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 java.awt.Color
|
||||
|
||||
object ServiceChannels {
|
||||
var client: Kord? = null
|
||||
|
||||
/**
|
||||
* List of (ServerID, ChannelID)
|
||||
*/
|
||||
var channels: MutableList<DiscordChannel> = refreshChannelsFromDisk()
|
||||
var serviceChannels: MutableList<ServiceChannel> = refreshServiceChannelsFromDisk()
|
||||
|
||||
fun sentToAll(messageEmbed: Embed) {
|
||||
if (client == null)
|
||||
return
|
||||
|
||||
for (channel_pair in channels) {
|
||||
try {
|
||||
val channel = client!!.getTextChannelById(channel_pair.id) ?: continue
|
||||
val customMessage = channel_pair.message?.message ?: ""
|
||||
|
||||
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(messageEmbed).queue {
|
||||
if (channel_pair.autoPublish) {
|
||||
it.crosspost().queue()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Admin.error("Error in server ${channel_pair.id}", e.message ?: e.localizedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = client!!.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 = client!!.getTextChannelById(channel_pair.id) ?: continue
|
||||
|
||||
if (channel_pair.mentionedRole == "everyone" &&
|
||||
channel.guild.selfMember.hasPermission(Permission.MESSAGE_MENTION_EVERYONE)
|
||||
) {
|
||||
Admin.warning("Cannot mention everyone on ${channel.guild.name}")
|
||||
} else if (channel.guild.selfMember.hasPermission(Permission.MESSAGE_WRITE)) {
|
||||
Admin.warning("Cannot send any messages on ${channel.guild.name}")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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 (client == null)
|
||||
return listOf()
|
||||
|
||||
return channels.filter { server == null || (client!!.getTextChannelById(it.id)?.guild?.idLong == server) }.map {
|
||||
val channel = client!!.getTextChannelById(it.id)
|
||||
if (channel == null) {
|
||||
Admin.warning("Channel ${it.id} is no longer active!")
|
||||
return@map "**${it.id}** *(inactive)*"
|
||||
}
|
||||
|
||||
val role = when (it.mentionedRole) {
|
||||
null -> ""
|
||||
"everyone" -> " @everyone"
|
||||
else -> " @${channel.guild.getRoleById(it.mentionedRole ?: "")?.name}"
|
||||
}
|
||||
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 (client == null)
|
||||
return listOf()
|
||||
|
||||
return serviceChannels.filter { server == null || (client!!.getTextChannelById(it.id)?.guild?.idLong == server) }.map {
|
||||
val channel = client!!.getTextChannelById(it.id)
|
||||
"**${channel?.guild?.name ?: it.id}** #${channel?.name ?: "(inactive)"}"
|
||||
}
|
||||
}
|
||||
|
||||
fun testServerId(id: Long): TextChannel? {
|
||||
return client?.getTextChannelById(id)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun saveChannels() {
|
||||
SERVERS_FILE.writeText(
|
||||
json.stringify(
|
||||
DiscordChannel.serializer().list,
|
||||
channels
|
||||
)
|
||||
)
|
||||
SERVICE_CHANNELS_FILE.writeText(
|
||||
json.stringify(
|
||||
ServiceChannel.serializer().list,
|
||||
serviceChannels
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.wulkanat.files.concept
|
||||
|
||||
import de.wulkanat.extensions.ensureExists
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
|
||||
abstract class SerializableObject<T>(
|
||||
fileName: String,
|
||||
defaultText: T? = null,
|
||||
private val childSerializer: KSerializer<T>
|
||||
) {
|
||||
private val json = Json { allowStructuredMapKeys = true }
|
||||
private val file = File(fileName).ensureExists(defaultText?.let { json.encodeToString(childSerializer, it) })
|
||||
var instance: T = json.decodeFromString(childSerializer, file.readText())
|
||||
|
||||
fun refresh() {
|
||||
instance = json.decodeFromString(childSerializer, file.readText())
|
||||
}
|
||||
|
||||
fun save() {
|
||||
file.writeText(json.encodeToString(childSerializer, instance))
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,26 @@
|
||||
package de.wulkanat.web
|
||||
|
||||
import de.wulkanat.Admin
|
||||
import de.wulkanat.DiscordRpc
|
||||
import de.wulkanat.model.BlogPostPreview
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jsoup.Jsoup
|
||||
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...")
|
||||
|
||||
suspend fun hasNewBlogPost(): Boolean {
|
||||
try {
|
||||
val doc = Jsoup.connect(BLOG_INDEX_URL).get()
|
||||
val doc = withContext(Dispatchers.IO) {
|
||||
// solved by `withContext`
|
||||
// https://stackoverflow.com/a/63332658
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
Jsoup.connect(BLOG_INDEX_URL).get()
|
||||
}
|
||||
val newBlog = BlogPostParser.getFistBlog(doc)
|
||||
|
||||
if (newestBlog == newBlog) {
|
||||
@@ -28,10 +35,17 @@ object SiteWatcher {
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user