Initial Library Work done
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
.idea
|
4
HEADER
Normal file
4
HEADER
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
36
Jenkinsfile
vendored
Normal file
36
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label "master"
|
||||
}
|
||||
tools {
|
||||
jdk "JAVA17"
|
||||
}
|
||||
stages {
|
||||
stage("Notify Discord") {
|
||||
steps {
|
||||
discordSend webhookURL: env.FDD_WH_ADMIN,
|
||||
title: "Build Started: SDLink-Core #${BUILD_NUMBER}",
|
||||
link: env.BUILD_URL,
|
||||
description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})"
|
||||
}
|
||||
}
|
||||
stage("Publish") {
|
||||
steps {
|
||||
sh "chmod +x ./gradlew"
|
||||
sh "./gradlew clean spotlessCheck publish"
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "./gradlew --stop"
|
||||
deleteDir()
|
||||
|
||||
discordSend webhookURL: env.FDD_WH_ADMIN,
|
||||
title: "Build Finished: SDLink-Core #${BUILD_NUMBER}",
|
||||
link: env.BUILD_URL,
|
||||
result: currentBuild.currentResult,
|
||||
description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})\nStatus: ${currentBuild.currentResult}"
|
||||
}
|
||||
}
|
||||
}
|
152
build.gradle
Normal file
152
build.gradle
Normal file
@@ -0,0 +1,152 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.github.johnrengelman.shadow' version '7.0.0'
|
||||
id 'maven-publish'
|
||||
id "com.diffplug.spotless" version "6.13.0"
|
||||
}
|
||||
apply plugin: 'java'
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(16)
|
||||
|
||||
group 'com.hypherionmc.sdlink'
|
||||
version = "${version_major}.${version_minor}.${version_patch}"
|
||||
|
||||
configurations {
|
||||
shaded
|
||||
shaded.transitive = true
|
||||
implementation.extendsFrom(shaded)
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "https://maven.firstdarkdev.xyz/releases" }
|
||||
maven { url "https://maven.firstdarkdev.xyz/snapshots" }
|
||||
maven { url "https://m2.dv8tion.net/releases" }
|
||||
maven { url "https://nexus.velocitypowered.com/repository/maven-public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Core dependencies, Shaded
|
||||
|
||||
// Discord
|
||||
shaded("pw.chew:jda-chewtils:${chewtils}") {
|
||||
exclude group: 'org.apache.commons'
|
||||
}
|
||||
shaded("net.dv8tion:JDA:${jda}") {
|
||||
exclude module: 'opus-java'
|
||||
exclude group: 'org.apache.commons'
|
||||
}
|
||||
shaded("club.minnced:discord-webhooks:${webhooks}")
|
||||
|
||||
// Utilities
|
||||
shaded("org.apache.commons:commons-collections4:${commons4}")
|
||||
shaded("com.github.oshi:oshi-core:${oshi}")
|
||||
shaded("org.jasypt:jasypt:${jasypt}:lite")
|
||||
shaded("io.jsondb:jsondb-core:${json_db}")
|
||||
|
||||
// Config
|
||||
shaded("me.hypherionmc.moon-config:core:${moonconfig}")
|
||||
shaded("me.hypherionmc.moon-config:toml:${moonconfig}")
|
||||
|
||||
|
||||
// Compile Only, Not Shaded
|
||||
|
||||
// Logging
|
||||
implementation("org.apache.logging.log4j:log4j-api:${log4j}")
|
||||
implementation("org.apache.logging.log4j:log4j-core:${log4j}")
|
||||
implementation("org.apache.logging.log4j:log4j-slf4j18-impl:${log4j}")
|
||||
|
||||
// Utilities
|
||||
implementation("org.apache.commons:commons-lang3:${commons}")
|
||||
implementation("commons-io:commons-io:${commonsio}")
|
||||
implementation("com.google.code.gson:gson:${gson}")
|
||||
implementation("com.google.guava:guava:31.1-jre")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
configurations = [project.configurations.shaded]
|
||||
dependencies {
|
||||
exclude(dependency('org.apache.logging.log4j:log4j-core:.*'))
|
||||
exclude(dependency('org.apache.logging.log4j:log4j-core:.*'))
|
||||
exclude(dependency('org.apache.logging.log4j:log4j-slf4j18-impl:.*'))
|
||||
exclude(dependency('org.apache.commons:commons-lang3:.*'))
|
||||
exclude(dependency('com.google.code.gson:.*'))
|
||||
exclude(dependency('javax:.*'))
|
||||
exclude(dependency('org.jetbrains:.*'))
|
||||
exclude(dependency('net.java.dev.jna:.*'))
|
||||
exclude(dependency('org.slf4j:.*'))
|
||||
|
||||
exclude 'org/slf4j/**'
|
||||
exclude 'META-INF/versions/9/**'
|
||||
exclude 'module-info.class'
|
||||
exclude 'org/apache/commons/lang3/**'
|
||||
|
||||
relocate 'org.apache.commons.collections4', shade_group + 'apache.commons.collections4'
|
||||
relocate 'javax.annotation', shade_group + 'javax.annotation'
|
||||
relocate 'gnu.trove', shade_group + 'gnu.trove'
|
||||
relocate 'com.fasterxml', shade_group + 'fasterxml'
|
||||
relocate 'club.minnced', shade_group + 'club.minnced'
|
||||
relocate 'com.iwebpp', shade_group + 'iwebpp'
|
||||
relocate 'com.jagrosh', shade_group + 'jagrosh'
|
||||
relocate 'com.neovisionaries', shade_group + 'neovisionaries'
|
||||
relocate 'me.hypherionmc.moonconfig', shade_group + 'moonconfig'
|
||||
relocate 'me.hypherionmc.jqlite', shade_group + 'jqlite'
|
||||
relocate 'net.dv8tion', shade_group + 'dv8tion'
|
||||
relocate 'okhttp3', shade_group + 'okhttp3'
|
||||
relocate 'okio', shade_group + 'okio'
|
||||
relocate 'org.json', shade_group + 'json'
|
||||
relocate 'org.sqlite', shade_group + 'sqlite'
|
||||
relocate 'pw.chew', shade_group + 'chew'
|
||||
relocate 'oshi', shade_group + 'oshi'
|
||||
relocate 'kotlin', shade_group + 'kotlin'
|
||||
relocate 'org.jasypt', shade_group + 'jasypt'
|
||||
relocate 'com.google', shade_group + 'google'
|
||||
relocate 'edu', shade_group + 'edu'
|
||||
relocate 'io', shade_group + 'io'
|
||||
relocate 'javassist', shade_group + 'javassist'
|
||||
relocate 'net', shade_group + 'net'
|
||||
relocate 'org.apache.commons.beanutils', shade_group + 'org.apache.commons.beanutils'
|
||||
relocate 'org.apache.commons.collections', shade_group + 'org.apache.commons.collections'
|
||||
relocate 'org.apache.commons.jxpath', shade_group + 'org.apache.commons.jxpath'
|
||||
relocate 'org.apache.commons.logging', shade_group + 'org.apache.commons.logging'
|
||||
relocate 'org.reflections', shade_group + 'org.reflections'
|
||||
}
|
||||
exclude 'META-INF/**'
|
||||
setArchiveClassifier('')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact(shadowJar) {
|
||||
builtBy shadowJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url System.getenv("MAVEN_URL")
|
||||
credentials {
|
||||
username System.getenv("MAVEN_USER")
|
||||
password System.getenv("MAVEN_PASS")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
it.options.encoding = 'UTF-8'
|
||||
it.options.release = 16
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
build.finalizedBy(shadowJar)
|
21
gradle.properties
Normal file
21
gradle.properties
Normal file
@@ -0,0 +1,21 @@
|
||||
version_major=0
|
||||
version_minor=0
|
||||
version_patch=2
|
||||
|
||||
shade_group=com.hypherionmc.sdlink.shaded.
|
||||
|
||||
# Core Dependencies
|
||||
jda=5.0.0-beta.9
|
||||
chewtils=2.0-SNAPSHOT
|
||||
webhooks=0.7.5
|
||||
commons4=4.4
|
||||
oshi=5.8.5
|
||||
moonconfig=1.0.9
|
||||
jasypt=1.9.3
|
||||
json_db=1.0.106
|
||||
|
||||
# Optional Dependencies
|
||||
log4j=2.17.2
|
||||
commons=3.12.0
|
||||
commonsio=2.11.0
|
||||
gson=2.10.1
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Sat May 20 21:34:44 SAST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
234
gradlew
vendored
Normal file
234
gradlew
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
21
license.txt
Normal file
21
license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2023 HypherionSA and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
21
readme.md
Normal file
21
readme.md
Normal file
@@ -0,0 +1,21 @@
|
||||
### SDLink Core
|
||||
|
||||
Minecraft independent code used by Simple Discord Link. This library contains all the core discord code, and code that is not
|
||||
dependent on Minecraft.
|
||||
|
||||
Requires JAVA 16 and Above!
|
||||
|
||||
***
|
||||
|
||||
#### Building Instructions (For contributors)
|
||||
|
||||
```gradle
|
||||
// Build a jar
|
||||
gradlew build
|
||||
|
||||
// To Publish
|
||||
gradlew publish
|
||||
|
||||
// Publish to mavenLocal()
|
||||
gradlew publishToMavenLocal
|
||||
```
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'sdlink-core'
|
||||
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.accounts;
|
||||
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Represents a Message Author for messages sent from Minecraft to Discord
|
||||
*/
|
||||
public class DiscordAuthor {
|
||||
|
||||
// User used for Server Messages
|
||||
public static final DiscordAuthor SERVER = new DiscordAuthor(sdLinkConfig.channelsAndWebhooks.serverName, sdLinkConfig.channelsAndWebhooks.serverAvatar, true);
|
||||
|
||||
private final String username;
|
||||
private final String avatar;
|
||||
private final boolean isServer;
|
||||
|
||||
/**
|
||||
* Internal. Use {@link #of(String, String)}
|
||||
* @param username The Username of the Author
|
||||
* @param avatar The avatar URL of the Author
|
||||
* @param isServer Is the Author the Minecraft Server
|
||||
*/
|
||||
private DiscordAuthor(String username, String avatar, boolean isServer) {
|
||||
this.username = username;
|
||||
this.avatar = avatar;
|
||||
this.isServer = isServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Discord Author
|
||||
* @param username The name/Username of the Author
|
||||
* @param uuid The Mojang UUID of the Author
|
||||
* @return A constructed {@link DiscordAuthor}
|
||||
*/
|
||||
public static DiscordAuthor of(String username, String uuid) {
|
||||
return new DiscordAuthor(
|
||||
username,
|
||||
SDLinkPlatform.minecraftHelper.isOnlineMode() ? sdLinkConfig.chatConfig.playerAvatarType.resolve(uuid) : username,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean isServer() {
|
||||
return isServer;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
}
|
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.accounts;
|
||||
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.managers.RoleManager;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.UserSnowflake;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Represents a Minecraft Account. Used for communication between this library and minecraft
|
||||
*/
|
||||
public class MinecraftAccount {
|
||||
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
private final boolean isOffline;
|
||||
private final boolean isValid;
|
||||
|
||||
/**
|
||||
* Internal. Use {@link #standard(String)} or {@link #offline(String)}
|
||||
* @param username The Username of the Player
|
||||
* @param uuid The UUID of the player
|
||||
* @param isOffline Is this an OFFLINE/Unauthenticated Account
|
||||
* @param isValid Is the account valid
|
||||
*/
|
||||
private MinecraftAccount(String username, UUID uuid, boolean isOffline, boolean isValid) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.isOffline = isOffline;
|
||||
this.isValid = isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert a Username to an online user account. If it can not, it will return an offline user
|
||||
* @param username The username to search for
|
||||
*/
|
||||
public static MinecraftAccount standard(String username) {
|
||||
Pair<String, UUID> player = fetchPlayer(username);
|
||||
|
||||
if (player.getRight() == null) {
|
||||
return offline(username);
|
||||
}
|
||||
|
||||
return new MinecraftAccount(
|
||||
player.getLeft(),
|
||||
player.getRight(),
|
||||
false,
|
||||
player.getRight() != null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a username to an offline account
|
||||
* @param username The Username to search for
|
||||
*/
|
||||
public static MinecraftAccount offline(String username) {
|
||||
Pair<String, UUID> player = offlinePlayer(username);
|
||||
return new MinecraftAccount(
|
||||
player.getLeft(),
|
||||
player.getRight(),
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return isOffline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a Minecraft account to a discord account
|
||||
* @param member The discord user
|
||||
* @param guild The server the command is run from
|
||||
*/
|
||||
public Result linkAccount(Member member, Guild guild) {
|
||||
if (getStoredAccount() == null) {
|
||||
return Result.error("We couldn't link your Minecraft and Discord Accounts together. If this error persists, please ask a staff member for help");
|
||||
}
|
||||
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
account.setDiscordID(member.getId());
|
||||
account.setAccountLinkCode("");
|
||||
|
||||
try {
|
||||
sdlinkDatabase.upsert(account);
|
||||
|
||||
String suffix = " [MC: " + this.username + "]";
|
||||
int availableChars = 32 - suffix.length();
|
||||
String nickname = member.getEffectiveName();
|
||||
|
||||
if (nickname.length() > availableChars) {
|
||||
nickname = nickname.substring(0, availableChars - 3) + "...";
|
||||
}
|
||||
|
||||
nickname += suffix;
|
||||
|
||||
try {
|
||||
member.modifyNickname(nickname).queue();
|
||||
|
||||
if (RoleManager.getLinkedRole() != null) {
|
||||
guild.addRoleToMember(UserSnowflake.fromId(member.getId()), RoleManager.getLinkedRole()).queue();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success("Your Discord and MC accounts have been linked");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Result.error("Failed to complete account linking. Please inform the server owner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink a previously linked Discord and Minecraft Account
|
||||
*/
|
||||
public Result unlinkAccount() {
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
if (account == null)
|
||||
return Result.error("No such account found in database");
|
||||
|
||||
try {
|
||||
sdlinkDatabase.remove(account, SDLinkAccount.class);
|
||||
return Result.success("Your discord and Minecraft accounts are no longer linked");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Result.error("We could not unlink your discord and Minecraft accounts. Please inform the server owner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if account database contains linking information
|
||||
* and a valid discord user for this account
|
||||
*/
|
||||
public boolean isAccountLinked() {
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
|
||||
if (account == null)
|
||||
return false;
|
||||
|
||||
User discordUser = getDiscordUser();
|
||||
return discordUser != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whitelist a Player on Minecraft and store the info the database
|
||||
* @param member The Discord Member that executed the command
|
||||
* @param guild The Discord Server the command was executed in
|
||||
*/
|
||||
public Result whitelistAccount(Member member, Guild guild) {
|
||||
if (getStoredAccount() == null) {
|
||||
return Result.error("We couldn't link your Minecraft and Discord Accounts together. If this error persists, please ask a staff member for help");
|
||||
}
|
||||
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
account.setDiscordID(member.getId());
|
||||
account.setWhitelistCode("");
|
||||
|
||||
try {
|
||||
if (!SDLinkPlatform.minecraftHelper.whitelistPlayer(MinecraftAccount.standard(account.getUsername())).isError()) {
|
||||
account.setWhitelisted(true);
|
||||
sdlinkDatabase.upsert(account);
|
||||
|
||||
// Auto Linking is enabled, so we link the Discord and MC accounts
|
||||
if (sdLinkConfig.whitelistingAndLinking.whitelisting.linkedWhitelist) {
|
||||
this.linkAccount(member, guild);
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success("Your account has been whitelisted");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Result.error("Failed to complete whitelisting. Please inform the server owner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a previously whitelisted account from Minecraft and the database
|
||||
*/
|
||||
public Result unwhitelistAccount() {
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
if (account == null)
|
||||
return Result.error("No such account found in database");
|
||||
|
||||
try {
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(account.getUsername());
|
||||
Result whitelistResult = SDLinkPlatform.minecraftHelper.unWhitelistPlayer(minecraftAccount);
|
||||
if (whitelistResult.isError()) {
|
||||
return whitelistResult;
|
||||
} else {
|
||||
sdlinkDatabase.remove(account, SDLinkAccount.class);
|
||||
|
||||
// Auto Linking is enabled. So we unlink the account
|
||||
if (sdLinkConfig.whitelistingAndLinking.whitelisting.linkedWhitelist) {
|
||||
this.unlinkAccount();
|
||||
}
|
||||
|
||||
return Result.success("Your account has been removed from the whitelist");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Result.error("We could not unwhitelist your account. Please inform the server owner");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player is whitelisted on the MC server and if the database
|
||||
* contains an entry for this player
|
||||
*/
|
||||
public boolean isAccountWhitelisted() {
|
||||
SDLinkAccount account = getStoredAccount();
|
||||
|
||||
if (account == null)
|
||||
return false;
|
||||
|
||||
return !SDLinkPlatform.minecraftHelper.isPlayerWhitelisted(MinecraftAccount.standard(account.getUsername())).isError() && account.isWhitelisted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stored account from the database
|
||||
*/
|
||||
public SDLinkAccount getStoredAccount() {
|
||||
return sdlinkDatabase.findById(this.uuid.toString(), SDLinkAccount.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Database Entry for this account.
|
||||
* Must only be used when a new entry is required
|
||||
*/
|
||||
public SDLinkAccount newDBEntry() {
|
||||
SDLinkAccount account = new SDLinkAccount();
|
||||
account.setOffline(this.isOffline);
|
||||
account.setUUID(this.uuid.toString());
|
||||
account.setWhitelisted(false);
|
||||
account.setUsername(this.username);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Discord Account name this player is linked to
|
||||
*/
|
||||
public String getDiscordName() {
|
||||
SDLinkAccount storedAccount = sdlinkDatabase.findById(this.uuid, SDLinkAccount.class);
|
||||
if (storedAccount == null || storedAccount.getDiscordID() == null || storedAccount.getDiscordID().isEmpty())
|
||||
return "Unlinked";
|
||||
|
||||
User discordUser = BotController.INSTANCE.getJDA().getUserById(storedAccount.getDiscordID());
|
||||
return discordUser == null ? "Unlinked" : discordUser.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Discord User this player is linked to
|
||||
*/
|
||||
public User getDiscordUser() {
|
||||
SDLinkAccount storedAccount = sdlinkDatabase.findById(this.uuid, SDLinkAccount.class);
|
||||
if (storedAccount == null || storedAccount.getDiscordID() == null || storedAccount.getDiscordID().isEmpty())
|
||||
return null;
|
||||
|
||||
return BotController.INSTANCE.getJDA().getUserById(storedAccount.getDiscordID());
|
||||
}
|
||||
|
||||
// Helper Methods
|
||||
private static Pair<String, UUID> fetchPlayer(String name) {
|
||||
try {
|
||||
BufferedReader read = new BufferedReader(new InputStreamReader(new URL("https://api.mojang.com/users/profiles/minecraft/" + name).openStream()));
|
||||
JSONObject obj = new JSONObject(new JSONTokener(read));
|
||||
String uuid = "";
|
||||
String returnname = name;
|
||||
|
||||
if (!obj.getString("name").isEmpty()) {
|
||||
returnname = obj.getString("name");
|
||||
}
|
||||
if (!obj.getString("id").isEmpty()) {
|
||||
uuid = obj.getString("id");
|
||||
}
|
||||
|
||||
read.close();
|
||||
return Pair.of(returnname, uuid.isEmpty() ? null : mojangIdToUUID(uuid));
|
||||
} catch (IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Pair.of("", null);
|
||||
}
|
||||
|
||||
private static UUID mojangIdToUUID(String id) {
|
||||
final List<String> strings = new ArrayList<>();
|
||||
strings.add(id.substring(0, 8));
|
||||
strings.add(id.substring(8, 12));
|
||||
strings.add(id.substring(12, 16));
|
||||
strings.add(id.substring(16, 20));
|
||||
strings.add(id.substring(20, 32));
|
||||
|
||||
return UUID.fromString(String.join("-", strings));
|
||||
}
|
||||
|
||||
private static Pair<String, UUID> offlinePlayer(String offlineName) {
|
||||
return Pair.of(offlineName, UUID.nameUUIDFromBytes(("OfflinePlayer:" + offlineName).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* The type of User Icon/Avatar that will be used for Discord Messages
|
||||
*/
|
||||
public enum AvatarType {
|
||||
AVATAR("https://mc-heads.net/avatar/{uuid}/512"),
|
||||
HEAD("https://mc-heads.net/head/{uuid}/512"),
|
||||
BODY("https://mc-heads.net/body/{uuid}"),
|
||||
COMBO("https://mc-heads.net/combo/{uuid}/512");
|
||||
|
||||
private final String url;
|
||||
|
||||
AvatarType(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public String resolve(String uuid) {
|
||||
return this.url.replace("{uuid}", uuid);
|
||||
}
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.CommentedConfig;
|
||||
import me.hypherionmc.moonconfig.core.Config;
|
||||
import me.hypherionmc.moonconfig.core.conversion.ObjectConverter;
|
||||
import me.hypherionmc.moonconfig.core.file.CommentedFileConfig;
|
||||
import com.hypherionmc.sdlink.core.util.EncryptionUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Main Config class for Loading, Saving and Upgrading configs
|
||||
*/
|
||||
public class ConfigController {
|
||||
|
||||
// Private internal variables
|
||||
private final File configFile;
|
||||
public static int configVer = 1;
|
||||
|
||||
// Instance of the currently loaded config
|
||||
public static SDLinkConfig sdLinkConfig;
|
||||
|
||||
public ConfigController() {
|
||||
File path = new File("config/");
|
||||
if (!path.exists())
|
||||
path.mkdirs();
|
||||
|
||||
this.configFile = new File(path.getAbsolutePath() + File.separator + "simple-discord-link.toml");
|
||||
initConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the Config File as needed.
|
||||
* This will either Create, Upgrade or load an existing config file
|
||||
*/
|
||||
private void initConfig() {
|
||||
Config.setInsertionOrderPreserved(true);
|
||||
if (!configFile.exists() || configFile.length() < 10) {
|
||||
SDLinkConfig config = new SDLinkConfig();
|
||||
saveConfig(config);
|
||||
performEncryption();
|
||||
} else {
|
||||
configUpgrade();
|
||||
performEncryption();
|
||||
}
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an existing config file into an instance of {@link SDLinkConfig}
|
||||
*/
|
||||
private void loadConfig() {
|
||||
ObjectConverter converter = new ObjectConverter();
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(configFile).build();
|
||||
config.load();
|
||||
sdLinkConfig = converter.toObject(config, SDLinkConfig::new);
|
||||
config.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an instance of {@link SDLinkConfig} to the config file
|
||||
* @param conf An instance of the config to save
|
||||
*/
|
||||
public void saveConfig(Object conf) {
|
||||
ObjectConverter converter = new ObjectConverter();
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(configFile).build();
|
||||
|
||||
converter.toConfig(conf, config);
|
||||
config.save();
|
||||
config.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle config structure changes between version changes
|
||||
*/
|
||||
private void configUpgrade() {
|
||||
CommentedFileConfig oldConfig = CommentedFileConfig.builder(configFile).build();
|
||||
CommentedFileConfig newConfig = CommentedFileConfig.builder(configFile).build();
|
||||
|
||||
newConfig.load();
|
||||
newConfig.clear();
|
||||
oldConfig.load();
|
||||
|
||||
if (oldConfig.getInt("general.configVersion") == configVer) {
|
||||
newConfig.close();
|
||||
oldConfig.close();
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectConverter objectConverter = new ObjectConverter();
|
||||
objectConverter.toConfig(new SDLinkConfig(), newConfig);
|
||||
|
||||
oldConfig.valueMap().forEach((key, value) -> {
|
||||
if (value instanceof CommentedConfig commentedConfig) {
|
||||
commentedConfig.valueMap().forEach((subKey, subValue) -> {
|
||||
if (newConfig.contains(key + "." + subKey)) {
|
||||
newConfig.set(key + "." + subKey, subValue);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (newConfig.contains(key)) {
|
||||
newConfig.set(key, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
configFile.renameTo(new File(configFile.getAbsolutePath().replace(".toml", ".old")));
|
||||
newConfig.set("general.configVersion", configVer);
|
||||
newConfig.save();
|
||||
newConfig.close();
|
||||
oldConfig.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply encryption to Bot-Token and Webhook URLS
|
||||
*/
|
||||
private void performEncryption() {
|
||||
CommentedFileConfig oldConfig = CommentedFileConfig.builder(configFile).build();
|
||||
oldConfig.load();
|
||||
|
||||
String botToken = oldConfig.getOrElse("botConfig.botToken", "");
|
||||
String chatWebhook = oldConfig.getOrElse("channelsAndWebhooks.webhooks.chatWebhook", "");
|
||||
String eventsWebhook = oldConfig.getOrElse("channelsAndWebhooks.webhooks.eventsWebhook", "");
|
||||
String consoleWebhook = oldConfig.getOrElse("channelsAndWebhooks.webhooks.consoleWebhook", "");
|
||||
|
||||
if (!botToken.isEmpty()) {
|
||||
botToken = EncryptionUtil.INSTANCE.encrypt(botToken);
|
||||
oldConfig.set("botConfig.botToken", botToken);
|
||||
}
|
||||
|
||||
if (!chatWebhook.isEmpty()) {
|
||||
chatWebhook = EncryptionUtil.INSTANCE.encrypt(chatWebhook);
|
||||
oldConfig.set("channelsAndWebhooks.webhooks.chatWebhook", chatWebhook);
|
||||
}
|
||||
|
||||
if (!eventsWebhook.isEmpty()) {
|
||||
eventsWebhook = EncryptionUtil.INSTANCE.encrypt(eventsWebhook);
|
||||
oldConfig.set("channelsAndWebhooks.webhooks.eventsWebhook", eventsWebhook);
|
||||
}
|
||||
|
||||
if (!consoleWebhook.isEmpty()) {
|
||||
consoleWebhook = EncryptionUtil.INSTANCE.encrypt(consoleWebhook);
|
||||
oldConfig.set("channelsAndWebhooks.webhooks.consoleWebhook", consoleWebhook);
|
||||
}
|
||||
|
||||
oldConfig.save();
|
||||
oldConfig.close();
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
import com.hypherionmc.sdlink.core.config.impl.*;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* The main mod config Structure
|
||||
*/
|
||||
public class SDLinkConfig {
|
||||
|
||||
@Path("general")
|
||||
@SpecComment("General Mod Config")
|
||||
public GeneralConfigSettings generalConfig = new GeneralConfigSettings();
|
||||
|
||||
@Path("botConfig")
|
||||
@SpecComment("Config specific to the discord bot")
|
||||
public BotConfigSettings botConfig = new BotConfigSettings();
|
||||
|
||||
@Path("channelsAndWebhooks")
|
||||
@SpecComment("Config relating to the discord channels and webhooks to use with the mod")
|
||||
public ChannelWebhookConfig channelsAndWebhooks = new ChannelWebhookConfig();
|
||||
|
||||
@Path("chat")
|
||||
@SpecComment("Configure which types of messages are delivered to Minecraft/Discord")
|
||||
public ChatSettingsConfig chatConfig = new ChatSettingsConfig();
|
||||
|
||||
@Path("messageFormatting")
|
||||
@SpecComment("Change the format in which messages are displayed")
|
||||
public MessageFormatting messageFormatting = new MessageFormatting();
|
||||
|
||||
@Path("messageDestinations")
|
||||
@SpecComment("Change in which channel messages appear")
|
||||
public MessageChannelConfig messageDestinations = new MessageChannelConfig();
|
||||
|
||||
@Path("whitelistingAndLinking")
|
||||
@SpecComment("Configure Whitelisting and Account Linking through the bot")
|
||||
public LinkAndWhitelistConfigSettings whitelistingAndLinking = new LinkAndWhitelistConfigSettings();
|
||||
|
||||
@Path("botCommands")
|
||||
@SpecComment("Enable or Disable certain bot commands")
|
||||
public BotCommandsConfig botCommands = new BotCommandsConfig();
|
||||
|
||||
@Path("linkedCommands")
|
||||
@SpecComment("Execute Minecraft commands in Discord")
|
||||
public LinkedCommandsConfig linkedCommands = new LinkedCommandsConfig();
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to allow disabling some bot commands
|
||||
*/
|
||||
public class BotCommandsConfig {
|
||||
|
||||
@Path("allowPlayerList")
|
||||
@SpecComment("Enable/Disable the Player List command")
|
||||
public boolean allowPlayerList = true;
|
||||
|
||||
@Path("allowServerStatus")
|
||||
@SpecComment("Enable/Disable the Server Status command")
|
||||
public boolean allowServerStatus = true;
|
||||
|
||||
@Path("allowHelpCommand")
|
||||
@SpecComment("Enable/Disable the Help command")
|
||||
public boolean allowHelpCommand = true;
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure for the Core bot settings
|
||||
*/
|
||||
public class BotConfigSettings {
|
||||
|
||||
@Path("botToken")
|
||||
@SpecComment("The token of the Discord Bot to use. This will be encrypted on first load. See https://sdlink.fdd-docs.com/initial-setup/ to find this")
|
||||
public String botToken = "";
|
||||
|
||||
@Path("statusUpdateInterval")
|
||||
@SpecComment("How often the Bot Status will update on Discord (in Seconds)")
|
||||
public int statusUpdateInterval = 30;
|
||||
|
||||
@Path("staffRole")
|
||||
@SpecComment("Role Name or ID that is allowed to use Staff Functions. If not defined, it will default back to Admin/Kick Perms")
|
||||
public String staffRole = "";
|
||||
|
||||
@Path("botStatus")
|
||||
@SpecComment("Control what the Discord Bot will display as it's status message")
|
||||
public BotStatus botStatus = new BotStatus();
|
||||
|
||||
@Path("topicUpdates")
|
||||
@SpecComment("Define how the bot should handle channel topic updates on the chat channel")
|
||||
public ChannelTopic channelTopic = new ChannelTopic();
|
||||
|
||||
@Path("invite")
|
||||
@SpecComment("Configure the in-game Discord Invite command")
|
||||
public DiscordInvite invite = new DiscordInvite();
|
||||
|
||||
public static class BotStatus {
|
||||
@Path("status")
|
||||
@SpecComment("Do not add Playing. A status to display on the bot. You can use %players% and %maxplayers% to show the number of players on the server")
|
||||
public String botStatus = "Minecraft";
|
||||
|
||||
@Path("botStatusType")
|
||||
@SpecComment("The type of the status displayed on the bot. Valid entries are: PLAYING, STREAMING, WATCHING, LISTENING")
|
||||
public Activity.ActivityType botStatusType = Activity.ActivityType.PLAYING;
|
||||
|
||||
@Path("botStatusStreamingURL")
|
||||
@SpecComment("The URL that will be used when the \"botStatusType\" is set to \"STREAMING\", required to display as \"streaming\".")
|
||||
public String botStatusStreamingURL = "https://twitch.tv/twitch";
|
||||
}
|
||||
|
||||
public static class ChannelTopic {
|
||||
@Path("doTopicUpdates")
|
||||
@SpecComment("Should the bot update the topic of your chat channel automatically every 6 Minutes")
|
||||
public boolean doTopicUpdates = true;
|
||||
|
||||
@Path("channelTopic")
|
||||
@SpecComment("A topic for the Chat Relay channel. You can use %player%, %maxplayers%, %uptime% or just leave it empty.")
|
||||
public String channelTopic = "Playing Minecraft with %players%/%maxplayers% people | Uptime: %uptime%";
|
||||
}
|
||||
|
||||
public static class DiscordInvite {
|
||||
@Path("inviteLink")
|
||||
@SpecComment("If this is defined, it will enable the in-game Discord command")
|
||||
public String inviteLink = "";
|
||||
|
||||
@Path("inviteMessage")
|
||||
@SpecComment("The message to show when someone uses /discord command. You can use %inviteurl%")
|
||||
public String inviteMessage = "Hey, check out our discord server here -> %inviteurl%";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to control Channels and Webhooks used by the bot
|
||||
*/
|
||||
public class ChannelWebhookConfig {
|
||||
|
||||
@Path("serverAvatar")
|
||||
@SpecComment("A DIRECT link to an image to use as the avatar for server messages. Also used for embeds")
|
||||
public String serverAvatar = "";
|
||||
|
||||
@Path("serverName")
|
||||
@SpecComment("The name to display for Server messages when using Webhooks")
|
||||
public String serverName = "Minecraft Server";
|
||||
|
||||
@Path("channels")
|
||||
@SpecComment("Config relating to the discord channels to use with the mod")
|
||||
public Channels channels = new Channels();
|
||||
|
||||
@Path("webhooks")
|
||||
@SpecComment("Config relating to the discord Webhooks to use with the mod")
|
||||
public Webhooks webhooks = new Webhooks();
|
||||
|
||||
public static class Channels {
|
||||
@Path("chatChannelID")
|
||||
@SpecComment("REQUIRED! The ID of the channel to post in and relay messages from. This is still needed, even in webhook mode")
|
||||
public long chatChannelID = 0;
|
||||
|
||||
@Path("eventsChannelID")
|
||||
@SpecComment("If this ID is set, event messages will be posted in this channel instead of the chat channel")
|
||||
public long eventsChannelID = 0;
|
||||
|
||||
@Path("consoleChannelID")
|
||||
@SpecComment("If this ID is set, console messages sent after the bot started will be relayed here, and you can execute minecraft commands here")
|
||||
public long consoleChannelID = 0;
|
||||
}
|
||||
|
||||
public static class Webhooks {
|
||||
@Path("enabled")
|
||||
@SpecComment("Prefer Webhook Messages over Standard Bot Messages")
|
||||
public boolean enabled = false;
|
||||
|
||||
@Path("chatWebhook")
|
||||
@SpecComment("The URL of the channel webhook to use for Chat Messages. Will be encrypted on first run")
|
||||
public String chatWebhook = "";
|
||||
|
||||
@Path("eventsWebhook")
|
||||
@SpecComment("The URL of the channel webhook to use for Server Messages Will be encrypted on first run")
|
||||
public String eventsWebhook = "";
|
||||
|
||||
@Path("consoleWebhook")
|
||||
@SpecComment("The URL of the channel webhook to use for Console Messages Will be encrypted on first run")
|
||||
public String consoleWebhook = "";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
import com.hypherionmc.sdlink.core.config.AvatarType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to control what types of messages are supported by the mod
|
||||
*/
|
||||
public class ChatSettingsConfig {
|
||||
|
||||
@Path("formatting")
|
||||
@SpecComment("Convert Discord to MC, and MC to Discord Formatting")
|
||||
public boolean formatting = true;
|
||||
|
||||
@Path("sendConsoleMessages")
|
||||
@SpecComment("Should console messages be sent to the Console Channel")
|
||||
public boolean sendConsoleMessages = false;
|
||||
|
||||
@Path("playerAvatarType")
|
||||
@SpecComment("The type of image to use as the player icon in messages. Valid entries are: AVATAR, HEAD, BODY, COMBO")
|
||||
public AvatarType playerAvatarType = AvatarType.HEAD;
|
||||
|
||||
@Path("relayTellRaw")
|
||||
@SpecComment("Should messages sent with TellRaw be sent to discord as a chat? (Experimental)")
|
||||
public boolean relayTellRaw = true;
|
||||
|
||||
@Path("relayFullCommands")
|
||||
@SpecComment("Should the entire command executed be relayed to discord, or only the name of the command")
|
||||
public boolean relayFullCommands = false;
|
||||
|
||||
@Path("ignoreBots")
|
||||
@SpecComment("Should messages from bots be relayed")
|
||||
public boolean ignoreBots = true;
|
||||
|
||||
@Path("serverStarting")
|
||||
@SpecComment("Should SERVER STARTING messages be shown")
|
||||
public boolean serverStarting = true;
|
||||
|
||||
@Path("serverStarted")
|
||||
@SpecComment("Should SERVER STARTED messages be shown")
|
||||
public boolean serverStarted = true;
|
||||
|
||||
@Path("serverStopping")
|
||||
@SpecComment("Should SERVER STOPPING messages be shown")
|
||||
public boolean serverStopping = true;
|
||||
|
||||
@Path("serverStopped")
|
||||
@SpecComment("Should SERVER STOPPED messages be shown")
|
||||
public boolean serverStopped = true;
|
||||
|
||||
@Path("playerMessages")
|
||||
@SpecComment("Should the chat be relayed")
|
||||
public boolean playerMessages = true;
|
||||
|
||||
@Path("playerJoin")
|
||||
@SpecComment("Should Player Join messages be posted")
|
||||
public boolean playerJoin = true;
|
||||
|
||||
@Path("playerLeave")
|
||||
@SpecComment("Should Player Leave messages be posted")
|
||||
public boolean playerLeave = true;
|
||||
|
||||
@Path("advancementMessages")
|
||||
@SpecComment("Should Advancement messages be posted")
|
||||
public boolean advancementMessages = true;
|
||||
|
||||
@Path("deathMessages")
|
||||
@SpecComment("Should Death Announcements be posted")
|
||||
public boolean deathMessages = true;
|
||||
|
||||
@Path("sendSayCommand")
|
||||
@SpecComment("Should Messages from the /say command be posted")
|
||||
public boolean sendSayCommand = true;
|
||||
|
||||
@Path("broadcastCommands")
|
||||
@SpecComment("Should commands be posted to discord")
|
||||
public boolean broadcastCommands = true;
|
||||
|
||||
@Path("ignoredCommands")
|
||||
@SpecComment("Commands that should not be broadcasted to discord")
|
||||
public List<String> ignoredCommands = new ArrayList<String>() {{ add("particle"); add("login"); add("execute"); }};
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
import com.hypherionmc.sdlink.core.config.ConfigController;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* General Mod Settings config Structure
|
||||
*/
|
||||
public class GeneralConfigSettings {
|
||||
|
||||
@Path("enabled")
|
||||
@SpecComment("Should the mod be enabled or not")
|
||||
public boolean enabled = true;
|
||||
|
||||
@Path("debugging")
|
||||
@SpecComment("Enable Additional Logging. Used for Fault Finding. WARNING: CAUSES LOG SPAM!")
|
||||
public boolean debugging = false;
|
||||
|
||||
@Path("configVersion")
|
||||
@SpecComment("Internal version control. DO NOT TOUCH!")
|
||||
public int configVersion = ConfigController.configVer;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to control Whitelisting and Account Linking
|
||||
*/
|
||||
public class LinkAndWhitelistConfigSettings {
|
||||
|
||||
@Path("whiteListing")
|
||||
@SpecComment("Control how the bot handles Whitelisting Players, if at all")
|
||||
public Whitelisting whitelisting = new Whitelisting();
|
||||
|
||||
@Path("accountLinking")
|
||||
@SpecComment("Control how the bot handles Discord -> MC Account Linking, if at all")
|
||||
public AccountLinking accountLinking = new AccountLinking();
|
||||
|
||||
public static class AccountLinking {
|
||||
@Path("accountlinking")
|
||||
@SpecComment("Allow users to Link their MC and Discord accounts")
|
||||
public boolean accountLinking = false;
|
||||
|
||||
@Path("linkedRole")
|
||||
@SpecComment("If a role ID (or name) is defined here, it will be assigned to players when their MC and Discord accounts are linked")
|
||||
public String linkedRole = "";
|
||||
|
||||
@Path("requireLinking")
|
||||
@SpecComment("Require users to link their Discord and Minecraft accounts before joining the server")
|
||||
public boolean requireLinking = false;
|
||||
}
|
||||
|
||||
public static class Whitelisting {
|
||||
@Path("whitelisting")
|
||||
@SpecComment("Should the bot be allowed to whitelist/un-whitelist players.")
|
||||
public boolean whitelisting = false;
|
||||
|
||||
@Path("linkedWhitelist")
|
||||
@SpecComment("Automatically link Minecraft and Discord Accounts when a user is whitelisted")
|
||||
public boolean linkedWhitelist = false;
|
||||
|
||||
@Path("staffOnlyWhitelist")
|
||||
@SpecComment("Should only staff be allowed to whitelist players")
|
||||
public boolean staffOnlyWhitelist = false;
|
||||
|
||||
@Path("autoWhitelistRole")
|
||||
@SpecComment("If a role ID (or name) is defined here, it will be assigned to players when they are whitelisted")
|
||||
public String autoWhitelistRole = "";
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Main Config Structure to control Discord -> MC Commands
|
||||
*/
|
||||
public class LinkedCommandsConfig {
|
||||
|
||||
@Path("enabled")
|
||||
@SpecComment("Should linked commands be enabled")
|
||||
public boolean enabled = false;
|
||||
|
||||
@Path("commands")
|
||||
@SpecComment("Commands to be linked")
|
||||
public List<Command> commands = new ArrayList<>();
|
||||
|
||||
public static class Command {
|
||||
@Path("mcCommand")
|
||||
@SpecComment("The Minecraft Command. Use %args% and %args(1-9)% (for example %args1%) to pass everything after the discordCommand to Minecraft")
|
||||
public String mcCommand;
|
||||
|
||||
@Path("discordCommand")
|
||||
@SpecComment("The command slug in discord. To be used as /mc slug or ~mc slug")
|
||||
public String discordCommand;
|
||||
|
||||
@Path("discordRole")
|
||||
@SpecComment("Discord Role Name of ID of the role that is allowed to execute this command. If empty, all players will be allowed to use this command")
|
||||
public String discordRole;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageDestination;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to control the destinations of messages
|
||||
*/
|
||||
public class MessageChannelConfig {
|
||||
|
||||
@Path("chat")
|
||||
@SpecComment("Control where CHAT messages are delivered")
|
||||
public DestinationObject chat = DestinationObject.of(MessageDestination.CHAT, false);
|
||||
|
||||
@Path("startStop")
|
||||
@SpecComment("Control where START/STOP messages are delivered")
|
||||
public DestinationObject startStop = DestinationObject.of(MessageDestination.EVENT, false);
|
||||
|
||||
@Path("joinLeave")
|
||||
@SpecComment("Control where JOIN/LEAVE messages are delivered")
|
||||
public DestinationObject joinLeave = DestinationObject.of(MessageDestination.EVENT, false);
|
||||
|
||||
@Path("advancements")
|
||||
@SpecComment("Control where ADVANCEMENT messages are delivered")
|
||||
public DestinationObject advancements = DestinationObject.of(MessageDestination.EVENT, false);
|
||||
|
||||
@Path("death")
|
||||
@SpecComment("Control where DEATH messages are delivered")
|
||||
public DestinationObject death = DestinationObject.of(MessageDestination.EVENT, false);
|
||||
|
||||
@Path("commands")
|
||||
@SpecComment("Control where COMMAND messages are delivered")
|
||||
public DestinationObject commands = DestinationObject.of(MessageDestination.EVENT, false);
|
||||
|
||||
public static class DestinationObject {
|
||||
@Path("channel")
|
||||
@SpecComment("The Channel the message will be delivered to. Valid entries are CHAT, EVENT, CONSOLE")
|
||||
public MessageDestination channel;
|
||||
|
||||
@Path("useEmbed")
|
||||
@SpecComment("Should the message be sent using EMBED style messages")
|
||||
public boolean useEmbed;
|
||||
|
||||
DestinationObject(MessageDestination destination, boolean useEmbed) {
|
||||
this.channel = destination;
|
||||
this.useEmbed = useEmbed;
|
||||
}
|
||||
|
||||
public static DestinationObject of(MessageDestination destination, boolean useEmbed) {
|
||||
return new DestinationObject(destination, useEmbed);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.config.impl;
|
||||
|
||||
import me.hypherionmc.moonconfig.core.conversion.Path;
|
||||
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Config Structure to control Discord/MC Message Formatting
|
||||
*/
|
||||
public class MessageFormatting {
|
||||
|
||||
@Path("mcPrefix")
|
||||
@SpecComment("Prefix to add to Minecraft when a message is relayed from Discord. Supports MC formatting. Use %user% for the Discord Username")
|
||||
public String mcPrefix = "\u00A7e[Discord]\u00A7r %user%: ";
|
||||
|
||||
@Path("serverStarting")
|
||||
@SpecComment("Server Starting Message")
|
||||
public String serverStarting = "*Server is starting...*";
|
||||
|
||||
@Path("serverStarted")
|
||||
@SpecComment("Server Started Message")
|
||||
public String serverStarted = "*Server has started. Enjoy!*";
|
||||
|
||||
@Path("serverStopping")
|
||||
@SpecComment("Server Stopping Message")
|
||||
public String serverStopping = "*Server is stopping...*";
|
||||
|
||||
@Path("serverStopped")
|
||||
@SpecComment("Server Stopped Message")
|
||||
public String serverStopped = "*Server has stopped...*";
|
||||
|
||||
@Path("playerJoined")
|
||||
@SpecComment("Player Joined Message. Use %player% to display the player name")
|
||||
public String playerJoined = "*%player% has joined the server!*";
|
||||
|
||||
@Path("playerLeft")
|
||||
@SpecComment("Player Left Message. Use %player% to display the player name")
|
||||
public String playerLeft = "*%player% has left the server!*";
|
||||
|
||||
@Path("achievements")
|
||||
@SpecComment("Achievement Messages. Available variables: %player%, %title%, %description%")
|
||||
public String achievements = "*%player% has made the advancement [%title%]: %description%*";
|
||||
|
||||
@Path("chat")
|
||||
@SpecComment("Chat Messages. THIS DOES NOT APPLY TO EMBED OR WEBHOOK MESSAGES. Available variables: %player%, %message%")
|
||||
public String chat = "%player%: %message%";
|
||||
|
||||
@Path("commands")
|
||||
@SpecComment("Command Messages. Available variables: %player%, %command%")
|
||||
public String commands = "%player% **executed command**: *%command%*";
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.database;
|
||||
|
||||
import io.jsondb.annotation.Document;
|
||||
import io.jsondb.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* JSON based database to hold accounts the bot has interacted with.
|
||||
* This is used for Account Linking and Whitelisting
|
||||
*/
|
||||
@Document(collection = "accounts", schemaVersion = "1.0")
|
||||
public class SDLinkAccount {
|
||||
@Id
|
||||
private String UUID;
|
||||
private String username;
|
||||
private String addedBy;
|
||||
private String discordID;
|
||||
private String accountLinkCode;
|
||||
private String whitelistCode;
|
||||
private boolean isWhitelisted;
|
||||
private boolean isOffline;
|
||||
|
||||
public String getAccountLinkCode() {
|
||||
if (accountLinkCode == null)
|
||||
return "";
|
||||
return accountLinkCode;
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return isOffline;
|
||||
}
|
||||
|
||||
public boolean isWhitelisted() {
|
||||
return isWhitelisted;
|
||||
}
|
||||
|
||||
public String getDiscordID() {
|
||||
if (discordID == null)
|
||||
return "";
|
||||
return discordID;
|
||||
}
|
||||
|
||||
public String getAddedBy() {
|
||||
if (addedBy == null)
|
||||
return "";
|
||||
return addedBy;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
if (username == null)
|
||||
return "";
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getUUID() {
|
||||
if (UUID == null)
|
||||
return "";
|
||||
return UUID;
|
||||
}
|
||||
|
||||
public String getWhitelistCode() {
|
||||
if (whitelistCode == null)
|
||||
return "";
|
||||
return whitelistCode;
|
||||
}
|
||||
|
||||
public void setAccountLinkCode(String accountLinkCode) {
|
||||
this.accountLinkCode = accountLinkCode;
|
||||
}
|
||||
|
||||
public void setAddedBy(String addedBy) {
|
||||
this.addedBy = addedBy;
|
||||
}
|
||||
|
||||
public void setDiscordID(String discordID) {
|
||||
this.discordID = discordID;
|
||||
}
|
||||
|
||||
public void setOffline(boolean offline) {
|
||||
isOffline = offline;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setUUID(String UUID) {
|
||||
this.UUID = UUID;
|
||||
}
|
||||
|
||||
public void setWhitelistCode(String whitelistCode) {
|
||||
this.whitelistCode = whitelistCode;
|
||||
}
|
||||
|
||||
public void setWhitelisted(boolean whitelisted) {
|
||||
isWhitelisted = whitelisted;
|
||||
}
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord;
|
||||
|
||||
import com.hypherionmc.sdlink.core.config.ConfigController;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.CommandManager;
|
||||
import com.hypherionmc.sdlink.core.discord.events.DiscordEventHandler;
|
||||
import com.hypherionmc.sdlink.core.managers.DatabaseManager;
|
||||
import com.hypherionmc.sdlink.core.managers.WebhookManager;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.hypherionmc.sdlink.core.util.EncryptionUtil;
|
||||
import com.hypherionmc.sdlink.core.util.ThreadedEventManager;
|
||||
import com.jagrosh.jdautilities.command.CommandClient;
|
||||
import com.jagrosh.jdautilities.command.CommandClientBuilder;
|
||||
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.ChunkingFilter;
|
||||
import net.dv8tion.jda.api.utils.MemberCachePolicy;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* The main Discord Bot class. This controls everything surrounding the bot itself
|
||||
*/
|
||||
public class BotController {
|
||||
|
||||
// Public instance of this class that can be called anywhere
|
||||
public static BotController INSTANCE;
|
||||
|
||||
// Thread Execution Manager
|
||||
public static final ScheduledExecutorService taskManager = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
// Required Variables
|
||||
private JDA _jda;
|
||||
private final EventWaiter eventWaiter = new EventWaiter();
|
||||
private final Logger logger;
|
||||
|
||||
/**
|
||||
* Construct a new instance of this class
|
||||
* @param logger A constructed {@link Logger} that the bot will use
|
||||
*/
|
||||
public static void newInstance(Logger logger) {
|
||||
if (INSTANCE != null) {
|
||||
INSTANCE.shutdownBot(false);
|
||||
}
|
||||
new BotController(logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL
|
||||
* @param logger A constructed {@link Logger} that the bot will use
|
||||
*/
|
||||
private BotController(Logger logger) {
|
||||
INSTANCE = this;
|
||||
this.logger = logger;
|
||||
|
||||
new ConfigController();
|
||||
|
||||
DatabaseManager.initialize();
|
||||
|
||||
// Initialize Webhook Clients
|
||||
WebhookManager.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the bot and handle all the startup work
|
||||
*/
|
||||
public void initializeBot() {
|
||||
if (sdLinkConfig == null) {
|
||||
logger.error("Failed to load config. Check your log for errors");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdLinkConfig.botConfig.botToken.isEmpty()) {
|
||||
logger.error("Missing bot token. Mod will be disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdLinkConfig.generalConfig.enabled)
|
||||
return;
|
||||
|
||||
try {
|
||||
String token = EncryptionUtil.INSTANCE.decrypt(sdLinkConfig.botConfig.botToken);
|
||||
_jda = JDABuilder.createLight(
|
||||
token,
|
||||
GatewayIntent.GUILD_MEMBERS,
|
||||
GatewayIntent.GUILD_MESSAGES,
|
||||
GatewayIntent.MESSAGE_CONTENT,
|
||||
GatewayIntent.GUILD_MESSAGE_REACTIONS
|
||||
)
|
||||
.setMemberCachePolicy(MemberCachePolicy.ALL)
|
||||
.setChunkingFilter(ChunkingFilter.ALL)
|
||||
.setBulkDeleteSplittingEnabled(true)
|
||||
.setEventManager(new ThreadedEventManager())
|
||||
.build();
|
||||
|
||||
// Setup Commands
|
||||
CommandClientBuilder clientBuilder = new CommandClientBuilder();
|
||||
clientBuilder.setOwnerId("354707828298088459");
|
||||
clientBuilder.setHelpWord("help");
|
||||
clientBuilder.useHelpBuilder(false);
|
||||
//clientBuilder.forceGuildOnly(750990873311051786L);
|
||||
|
||||
CommandClient commandClient = clientBuilder.build();
|
||||
CommandManager.INSTANCE.register(commandClient);
|
||||
|
||||
// Register Event Handlers
|
||||
_jda.addEventListener(commandClient, eventWaiter, new DiscordEventHandler());
|
||||
_jda.setAutoReconnect(true);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to connect to discord", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the bot is in a state to send messages to discord
|
||||
*/
|
||||
public boolean isBotReady() {
|
||||
if (sdLinkConfig == null)
|
||||
return false;
|
||||
|
||||
if (!sdLinkConfig.generalConfig.enabled)
|
||||
return false;
|
||||
|
||||
if (_jda == null)
|
||||
return false;
|
||||
|
||||
if (_jda.getStatus() == JDA.Status.SHUTTING_DOWN || _jda.getStatus() == JDA.Status.SHUTDOWN)
|
||||
return false;
|
||||
|
||||
return _jda.getStatus() == JDA.Status.CONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the Bot, without forcing a shutdown
|
||||
*/
|
||||
public void shutdownBot() {
|
||||
this.shutdownBot(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the Bot, optionally forcing a shutdown
|
||||
* @param forced Should the shutdown be forced
|
||||
*/
|
||||
public void shutdownBot(boolean forced) {
|
||||
if (_jda != null) {
|
||||
_jda.shutdown();
|
||||
}
|
||||
|
||||
WebhookManager.shutdown();
|
||||
|
||||
if (forced) {
|
||||
// Workaround for Bot thread hanging after server shutdown
|
||||
taskManager.schedule(() -> {
|
||||
taskManager.shutdownNow();
|
||||
System.exit(1);
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that whitelisting is set up properly, so the bot can use the feature
|
||||
*/
|
||||
public void checkWhiteListing() {
|
||||
if (!sdLinkConfig.whitelistingAndLinking.whitelisting.whitelisting)
|
||||
return;
|
||||
|
||||
if (SDLinkPlatform.minecraftHelper.checkWhitelisting().isError()) {
|
||||
getLogger().error("SDLink Whitelisting is enabled, but server side whitelisting is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public JDA getJDA() {
|
||||
return this._jda;
|
||||
}
|
||||
|
||||
public EventWaiter getEventWaiter() {
|
||||
return eventWaiter;
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.general.HelpSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.general.PlayerListSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.general.ServerStatusSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.linking.ConfirmAccountLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.linking.LinkAccountCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.linking.UnlinkAccountSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.linking.ViewLinkedAccountsCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.whitelist.ConfirmWhitelistSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.whitelist.UnWhitelistAccountSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.whitelist.ViewWhitelistedAccountsSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.whitelist.WhitelistAccountCommand;
|
||||
import com.jagrosh.jdautilities.command.CommandClient;
|
||||
import com.jagrosh.jdautilities.command.SlashCommand;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command Manager class to control how commands are registered to discord
|
||||
*/
|
||||
public class CommandManager {
|
||||
|
||||
public static final CommandManager INSTANCE = new CommandManager();
|
||||
|
||||
private final Set<SlashCommand> commands = new HashSet<>();
|
||||
|
||||
private CommandManager() {
|
||||
this.addCommands();
|
||||
}
|
||||
|
||||
private void addCommands() {
|
||||
// Register Account Linking commands, if linking is enabled
|
||||
if (sdLinkConfig.whitelistingAndLinking.accountLinking.accountLinking) {
|
||||
commands.add(new LinkAccountCommand());
|
||||
commands.add(new ConfirmAccountLinkSlashCommand());
|
||||
commands.add(new UnlinkAccountSlashCommand());
|
||||
commands.add(new ViewLinkedAccountsCommand());
|
||||
}
|
||||
|
||||
// Register Whitelist commands, if whitelisting is enabled
|
||||
if (sdLinkConfig.whitelistingAndLinking.whitelisting.whitelisting) {
|
||||
commands.add(new WhitelistAccountCommand());
|
||||
commands.add(new ConfirmWhitelistSlashCommand());
|
||||
commands.add(new ViewWhitelistedAccountsSlashCommand());
|
||||
commands.add(new UnWhitelistAccountSlashCommand());
|
||||
}
|
||||
|
||||
// Enable the Server Status command
|
||||
if (sdLinkConfig.botCommands.allowServerStatus) {
|
||||
commands.add(new ServerStatusSlashCommand());
|
||||
}
|
||||
|
||||
// Enable the Player List command
|
||||
if (sdLinkConfig.botCommands.allowPlayerList) {
|
||||
commands.add(new PlayerListSlashCommand());
|
||||
}
|
||||
|
||||
// Enable the Help command
|
||||
if (sdLinkConfig.botCommands.allowHelpCommand) {
|
||||
commands.add(new HelpSlashCommand());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL. Used to register slash commands
|
||||
* @param client The Discord Command Client instance
|
||||
*/
|
||||
public void register(CommandClient client) {
|
||||
commands.forEach(client::addSlashCommand);
|
||||
}
|
||||
|
||||
public Set<SlashCommand> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
// TODO Don't Forget This
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash;
|
||||
|
||||
import com.hypherionmc.sdlink.core.managers.RoleManager;
|
||||
import com.jagrosh.jdautilities.command.SlashCommand;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Extention of {@link SlashCommand} to implement our Permission handling
|
||||
*/
|
||||
public abstract class SDLinkSlashCommand extends SlashCommand {
|
||||
|
||||
public SDLinkSlashCommand(boolean requiresPerms) {
|
||||
if (requiresPerms) {
|
||||
if (RoleManager.getStaffRole() != null) {
|
||||
this.requiredRole = RoleManager.getStaffRole().getName();
|
||||
} else {
|
||||
this.userPermissions = new Permission[] { Permission.ADMINISTRATOR, Permission.KICK_MEMBERS };
|
||||
}
|
||||
}
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.general;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.commands.CommandManager;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.jagrosh.jdautilities.command.SlashCommand;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* The Help Command for the bot
|
||||
*/
|
||||
public class HelpSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public HelpSlashCommand() {
|
||||
super(false);
|
||||
this.name = "help";
|
||||
this.help = "Bot commands and help";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
Set<SlashCommand> commands = CommandManager.INSTANCE.getCommands();
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
builder.setTitle("Bot commands");
|
||||
builder.setColor(Color.BLUE);
|
||||
|
||||
commands.forEach(cmd -> builder.addField(cmd.getName(), cmd.getHelp(), false));
|
||||
event.replyEmbeds(builder.build()).setEphemeral(true).queue();
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.general;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.hypherionmc.sdlink.core.util.MessageUtil;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.EmbedPaginator;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to view a list of online players currently on the server
|
||||
*/
|
||||
public class PlayerListSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public PlayerListSlashCommand() {
|
||||
super(false);
|
||||
|
||||
this.name = "playerlist";
|
||||
this.help = "List currently online players on the server";
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
List<MinecraftAccount> players = SDLinkPlatform.minecraftHelper.getOnlinePlayers();
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
List<MessageEmbed> pages = new ArrayList<>();
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
|
||||
if (players.isEmpty()) {
|
||||
builder.setTitle("Online Players");
|
||||
builder.setColor(Color.RED);
|
||||
builder.setDescription("There are currently no players online");
|
||||
event.replyEmbeds(builder.build()).setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
EmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(event);
|
||||
|
||||
/**
|
||||
* Use Pagination to avoid message limits
|
||||
*/
|
||||
MessageUtil.listBatches(players, 10).forEach(p -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
count.getAndIncrement();
|
||||
builder.clear();
|
||||
builder.setTitle("Online Players - Page " + count.get() + "/" + (int)Math.ceil(((float)players.size() / 10)));
|
||||
builder.setColor(Color.GREEN);
|
||||
|
||||
p.forEach(account -> {
|
||||
sb.append("`").append(account.getUsername()).append("`");
|
||||
|
||||
if (account.getDiscordUser() != null) {
|
||||
sb.append(" - ").append(account.getDiscordUser().getAsMention());
|
||||
}
|
||||
sb.append("\r\n");
|
||||
});
|
||||
|
||||
builder.setDescription(sb.toString());
|
||||
pages.add(builder.build());
|
||||
});
|
||||
|
||||
paginator.setItems(pages);
|
||||
EmbedPaginator embedPaginator = paginator.build();
|
||||
|
||||
event.replyEmbeds(pages.get(0)).setEphemeral(false).queue(success ->
|
||||
success.retrieveOriginal().queue(msg -> embedPaginator.paginate(msg, 1)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.general;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.hypherionmc.sdlink.core.util.SystemUtils;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Informational command to give you a quick overview of the hardware/player
|
||||
* status of your server
|
||||
*/
|
||||
public class ServerStatusSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public ServerStatusSlashCommand() {
|
||||
super(true);
|
||||
|
||||
this.name = "status";
|
||||
this.help = "View information about your server";
|
||||
this.guildOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
Button refreshBtn = Button.danger("sdrefreshbtn", "Refresh");
|
||||
event.replyEmbeds(runStatusCommand()).addActionRow(refreshBtn).queue();
|
||||
}
|
||||
|
||||
public static MessageEmbed runStatusCommand() {
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = systemInfo.getHardware();
|
||||
CentralProcessor cpu = hal.getProcessor();
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
builder.setTitle("Server Information / Status");
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("**__System Information__**\r\n\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**CPU:**\r\n```\r\n")
|
||||
.append(cpu.toString())
|
||||
.append("```")
|
||||
.append("\r\n");
|
||||
|
||||
try {
|
||||
stringBuilder
|
||||
.append("**Memory:**\r\n```\r\n")
|
||||
.append(SystemUtils.byteToHuman(hal.getMemory().getAvailable()))
|
||||
.append(" free of ")
|
||||
.append(SystemUtils.byteToHuman(hal.getMemory().getTotal()))
|
||||
.append("```\r\n");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
stringBuilder
|
||||
.append("**OS:**\r\n```\r\n")
|
||||
.append(systemInfo.getOperatingSystem().toString())
|
||||
.append(" (")
|
||||
.append(systemInfo.getOperatingSystem().getBitness())
|
||||
.append(" bit)\r\n")
|
||||
.append("Version: ")
|
||||
.append(systemInfo.getOperatingSystem().getVersionInfo().getVersion())
|
||||
.append("```\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**System Uptime:**\r\n```\r\n")
|
||||
.append(SystemUtils.secondsToTimestamp(systemInfo.getOperatingSystem().getSystemUptime()))
|
||||
.append("```\r\n");
|
||||
|
||||
stringBuilder.append("**__Minecraft Information__**\r\n\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**Server Uptime:**\r\n```\r\n")
|
||||
.append(SystemUtils.secondsToTimestamp(SDLinkPlatform.minecraftHelper.getServerUptime()))
|
||||
.append("```\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**Server Version:**\r\n```\r\n")
|
||||
.append(SDLinkPlatform.minecraftHelper.getServerVersion())
|
||||
.append("```\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**Players Online:**\r\n```\r\n")
|
||||
.append(SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft())
|
||||
.append("/")
|
||||
.append(SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight())
|
||||
.append("```\r\n");
|
||||
|
||||
stringBuilder
|
||||
.append("**Whitelisting:**\r\n```\r\n")
|
||||
.append(!SDLinkPlatform.minecraftHelper.checkWhitelisting().isError() ? "Enabled" : "Disabled")
|
||||
.append("```\r\n");
|
||||
|
||||
builder.setDescription(stringBuilder.toString());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.linking;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to complete Discord -> MC Account Linking
|
||||
*/
|
||||
public class ConfirmAccountLinkSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public ConfirmAccountLinkSlashCommand() {
|
||||
super(false);
|
||||
this.name = "confirmlink";
|
||||
this.help = "Confirm your Minecraft Account to complete account linking";
|
||||
|
||||
this.options = Collections.singletonList(new OptionData(OptionType.INTEGER, "code", "The verification code from the Minecraft Kick Message").setRequired(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
int mcCode = event.getOption("code") != null ? event.getOption("code").getAsInt() : 0;
|
||||
|
||||
if (mcCode == 0) {
|
||||
event.reply("You need to provide a verification code").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class);
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("Sorry, but this server does not contain any stored players in its database").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
for (SDLinkAccount account : accounts) {
|
||||
if (account.getAccountLinkCode().equalsIgnoreCase(String.valueOf(mcCode))) {
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(account.getUsername());
|
||||
Result result = minecraftAccount.linkAccount(event.getMember(), event.getGuild());
|
||||
event.reply(result.getMessage()).setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.reply("Sorry, we could not verify your Minecraft account. Please try again").setEphemeral(true).queue();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.linking;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.util.SystemUtils;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import io.jsondb.InvalidJsonDbApiUsageException;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to start the Linking process of a Discord and MC Account
|
||||
* This will generate the verification code the player needs to enter, to
|
||||
* verify the account belongs to them
|
||||
*/
|
||||
public class LinkAccountCommand extends SDLinkSlashCommand {
|
||||
|
||||
public LinkAccountCommand() {
|
||||
super(false);
|
||||
this.name = "linkaccount";
|
||||
this.help = "Start the process of linking your Discord and MC Accounts";
|
||||
|
||||
this.options = Collections.singletonList(new OptionData(OptionType.STRING, "mcname", "Your Minecraft Username").setRequired(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
String mcName = event.getOption("mcname") != null ? event.getOption("mcname").getAsString() : "";
|
||||
|
||||
if (mcName.isEmpty()) {
|
||||
event.reply("You need to supply your Minecraft username").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(mcName);
|
||||
String confirmCode = String.valueOf(SystemUtils.generateRandomJoinCode());
|
||||
SDLinkAccount account = minecraftAccount.getStoredAccount();
|
||||
|
||||
if (account == null) {
|
||||
account = minecraftAccount.newDBEntry();
|
||||
account.setAccountLinkCode(confirmCode);
|
||||
|
||||
try {
|
||||
sdlinkDatabase.insert(account);
|
||||
event.reply("Please join the Minecraft server and check the Kick Message for your account link code. Then, run the command /confirmlink codehere to finish linking your accounts").setEphemeral(true).queue();
|
||||
} catch (InvalidJsonDbApiUsageException e) {
|
||||
e.printStackTrace();
|
||||
event.reply("Could not start account linking process. Please notify the server owner").setEphemeral(true).queue();
|
||||
}
|
||||
} else {
|
||||
if (account.getDiscordID() != null || !account.getDiscordID().isEmpty()) {
|
||||
event.reply("Sorry, this Minecraft account is already linked to a discord account").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
account.setAccountLinkCode(confirmCode);
|
||||
|
||||
try {
|
||||
sdlinkDatabase.upsert(account);
|
||||
event.reply("Please join the Minecraft server and check the Kick Message for your account link code. Then, run the command /confirmlink codehere to finish linking your accounts").setEphemeral(true).queue();
|
||||
} catch (InvalidJsonDbApiUsageException e) {
|
||||
e.printStackTrace();
|
||||
event.reply("Could not start account linking process. Please notify the server owner").setEphemeral(true).queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.linking;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to unlink a discord and minecraft account, that was previously linked
|
||||
*/
|
||||
public class UnlinkAccountSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public UnlinkAccountSlashCommand() {
|
||||
super(false);
|
||||
this.name = "unlinkaccount";
|
||||
this.help = "Unlink your previously linked Discord and Minecraft accounts";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class);
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("Sorry, but this server does not contain any stored players in its database").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
for (SDLinkAccount account : accounts) {
|
||||
if (account.getDiscordID() != null && account.getDiscordID().equalsIgnoreCase(event.getMember().getId())) {
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(account.getUsername());
|
||||
Result result = minecraftAccount.unlinkAccount();
|
||||
event.reply(result.getMessage()).setEphemeral(true).queue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.linking;
|
||||
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.util.MessageUtil;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.EmbedPaginator;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Staff Command to view a list of Linked Minecraft and Discord accounts
|
||||
*/
|
||||
public class ViewLinkedAccountsCommand extends SDLinkSlashCommand {
|
||||
|
||||
public ViewLinkedAccountsCommand() {
|
||||
super(true);
|
||||
|
||||
this.name = "linkedaccounts";
|
||||
this.help = "View a list of linked Discord and MC accounts";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
EmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(event);
|
||||
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class);
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
ArrayList<MessageEmbed> pages = new ArrayList<>();
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("There are no linked accounts for this discord").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageUtil.listBatches(accounts, 10).forEach(itm -> {
|
||||
count.getAndIncrement();
|
||||
builder.clear();
|
||||
builder.setTitle("Linked Accounts - Page " + count + "/" + (int)Math.ceil(((float)accounts.size() / 10)));
|
||||
builder.setColor(Color.GREEN);
|
||||
StringBuilder sBuilder = new StringBuilder();
|
||||
|
||||
itm.forEach(v -> {
|
||||
Member member = null;
|
||||
|
||||
if (v.getDiscordID() != null && !v.getDiscordID().isEmpty()) {
|
||||
member = event.getGuild().getMemberById(v.getDiscordID());
|
||||
}
|
||||
|
||||
sBuilder.append(v.getUsername()).append(" -> ").append(member == null ? "Unlinked" : member.getAsMention()).append("\r\n");
|
||||
});
|
||||
builder.setDescription(sBuilder);
|
||||
pages.add(builder.build());
|
||||
});
|
||||
|
||||
paginator.setItems(pages);
|
||||
EmbedPaginator embedPaginator = paginator.build();
|
||||
|
||||
event.replyEmbeds(pages.get(0)).setEphemeral(false).queue(success -> success.retrieveOriginal().queue(msg -> embedPaginator.paginate(msg, 1)));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.whitelist;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to confirm a Whitelist request
|
||||
*/
|
||||
public class ConfirmWhitelistSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public ConfirmWhitelistSlashCommand() {
|
||||
super(false);
|
||||
this.name = "whitelistconfirm";
|
||||
this.help = "Confirm your Minecraft Account to complete whitelisting";
|
||||
|
||||
this.options = Collections.singletonList(new OptionData(OptionType.INTEGER, "code", "The verification code from the Minecraft Kick Message").setRequired(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
int mcCode = event.getOption("code") != null ? event.getOption("code").getAsInt() : 0;
|
||||
|
||||
if (mcCode == 0) {
|
||||
event.reply("You need to provide a verification code").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class);
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("Sorry, but this server does not contain any stored players in its database").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
for (SDLinkAccount account : accounts) {
|
||||
if (account.getWhitelistCode().equalsIgnoreCase(String.valueOf(mcCode))) {
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(account.getUsername());
|
||||
Result result = minecraftAccount.whitelistAccount(event.getMember(), event.getGuild());
|
||||
event.reply(result.getMessage()).setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.reply("Sorry, we could not verify your Minecraft account. Please try again").setEphemeral(true).queue();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.whitelist;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Remove a player from the whitelist, that was previously whitelisted through the bot
|
||||
*/
|
||||
public class UnWhitelistAccountSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public UnWhitelistAccountSlashCommand() {
|
||||
super(false);
|
||||
this.name = "unwhitelist";
|
||||
this.help = "Remove your previously whitelisted Minecraft account";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class);
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("Sorry, but this server does not contain any stored players in its database").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
for (SDLinkAccount account : accounts) {
|
||||
if (account.getDiscordID().equalsIgnoreCase(event.getMember().getId())) {
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(account.getUsername());
|
||||
if (SDLinkPlatform.minecraftHelper.isPlayerWhitelisted(minecraftAccount).isError()) {
|
||||
event.reply("Your account is not whitelisted in Minecraft. Cannot remove your account").setEphemeral(true).queue();
|
||||
} else {
|
||||
Result result = minecraftAccount.unwhitelistAccount();
|
||||
event.reply(result.getMessage()).setEphemeral(true).queue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.whitelist;
|
||||
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.util.MessageUtil;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.EmbedPaginator;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Staff command to view whitelisted players on their server
|
||||
* This list only contains players whitelisted using the bot
|
||||
*/
|
||||
public class ViewWhitelistedAccountsSlashCommand extends SDLinkSlashCommand {
|
||||
|
||||
public ViewWhitelistedAccountsSlashCommand() {
|
||||
super(true);
|
||||
|
||||
this.name = "whitelisted";
|
||||
this.help = "View a list of Whitelisted MC accounts";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
EmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(event);
|
||||
|
||||
List<SDLinkAccount> accounts = sdlinkDatabase.findAll(SDLinkAccount.class).stream().filter(SDLinkAccount::isWhitelisted).toList();
|
||||
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
ArrayList<MessageEmbed> pages = new ArrayList<>();
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
|
||||
if (accounts.isEmpty()) {
|
||||
event.reply("There are no whitelisted accounts for this discord").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageUtil.listBatches(accounts, 10).forEach(itm -> {
|
||||
count.getAndIncrement();
|
||||
builder.clear();
|
||||
builder.setTitle("Whitelisted Accounts - Page " + count + "/" + (int)Math.ceil(((float)accounts.size() / 10)));
|
||||
builder.setColor(Color.GREEN);
|
||||
StringBuilder sBuilder = new StringBuilder();
|
||||
|
||||
itm.forEach(v -> {
|
||||
Member member = null;
|
||||
|
||||
if (v.getDiscordID() != null && !v.getDiscordID().isEmpty()) {
|
||||
member = event.getGuild().getMemberById(v.getDiscordID());
|
||||
}
|
||||
|
||||
sBuilder.append(v.getUsername()).append(" -> ").append(member == null ? "Unlinked" : member.getAsMention()).append("\r\n");
|
||||
});
|
||||
builder.setDescription(sBuilder);
|
||||
pages.add(builder.build());
|
||||
});
|
||||
|
||||
paginator.setItems(pages);
|
||||
EmbedPaginator embedPaginator = paginator.build();
|
||||
|
||||
event.replyEmbeds(pages.get(0)).setEphemeral(false).queue(success -> success.retrieveOriginal().queue(msg -> embedPaginator.paginate(msg, 1)));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.commands.slash.whitelist;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.hypherionmc.sdlink.core.util.SystemUtils;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import io.jsondb.InvalidJsonDbApiUsageException;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
import static com.hypherionmc.sdlink.core.managers.DatabaseManager.sdlinkDatabase;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Command to start the Whitelisting process
|
||||
* This will generate the verification code the player needs to enter, to
|
||||
* verify the account belongs to them
|
||||
*/
|
||||
public class WhitelistAccountCommand extends SDLinkSlashCommand {
|
||||
|
||||
public WhitelistAccountCommand() {
|
||||
super(sdLinkConfig.whitelistingAndLinking.whitelisting.staffOnlyWhitelist);
|
||||
|
||||
this.name = "whitelist";
|
||||
this.help = "Start the process of Whitelisting your Minecraft Account";
|
||||
|
||||
this.options = Collections.singletonList(new OptionData(OptionType.STRING, "mcname", "Your Minecraft Username").setRequired(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(SlashCommandEvent event) {
|
||||
String mcName = event.getOption("mcname") != null ? event.getOption("mcname").getAsString() : "";
|
||||
|
||||
if (mcName.isEmpty()) {
|
||||
event.reply("You need to supply your Minecraft username").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftAccount minecraftAccount = MinecraftAccount.standard(mcName);
|
||||
String confirmCode = String.valueOf(SystemUtils.generateRandomJoinCode());
|
||||
SDLinkAccount account = minecraftAccount.getStoredAccount();
|
||||
|
||||
if (account == null) {
|
||||
account = minecraftAccount.newDBEntry();
|
||||
account.setWhitelistCode(confirmCode);
|
||||
|
||||
try {
|
||||
sdlinkDatabase.insert(account);
|
||||
event.reply("Please join the Minecraft server and check the Kick Message for your account whitelist code. Then, run the command /whitelistconfirm codehere to finish whitelisting your account").setEphemeral(true).queue();
|
||||
} catch (InvalidJsonDbApiUsageException e) {
|
||||
e.printStackTrace();
|
||||
event.reply("Could not start account whitelisting process. Please notify the server owner").setEphemeral(true).queue();
|
||||
}
|
||||
} else {
|
||||
if (!account.getDiscordID().isEmpty() || SDLinkPlatform.minecraftHelper.isPlayerWhitelisted(minecraftAccount).isError()) {
|
||||
event.reply("Sorry, this Minecraft account is already whitelisted").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
account.setWhitelistCode(confirmCode);
|
||||
|
||||
try {
|
||||
sdlinkDatabase.upsert(account);
|
||||
event.reply("Please join the Minecraft server and check the Kick Message for your account whitelist code. Then, run the command /whitelistconfirm codehere to finish whitelisting your account").setEphemeral(true).queue();
|
||||
} catch (InvalidJsonDbApiUsageException e) {
|
||||
e.printStackTrace();
|
||||
event.reply("Could not start account whitelisting process. Please notify the server owner").setEphemeral(true).queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.events;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.discord.commands.slash.general.ServerStatusSlashCommand;
|
||||
import com.hypherionmc.sdlink.core.discord.hooks.BotReadyHooks;
|
||||
import com.hypherionmc.sdlink.core.discord.hooks.DiscordMessageHooks;
|
||||
import com.hypherionmc.sdlink.core.managers.ChannelManager;
|
||||
import com.hypherionmc.sdlink.core.managers.PermissionChecker;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.events.session.ReadyEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Class to provide Hooks for Discord Events, such as message received, and login
|
||||
* NOTE TO DEVELOPERS: Don't add ANY LOGIC IN HERE. Rather implement it in a seperate class,
|
||||
* and use these hooks to trigger that code
|
||||
*/
|
||||
public class DiscordEventHandler extends ListenerAdapter {
|
||||
|
||||
/**
|
||||
* The bot received a message
|
||||
*/
|
||||
@Override
|
||||
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
|
||||
if (event.isWebhookMessage())
|
||||
return;
|
||||
|
||||
if (event.getAuthor() == event.getJDA().getSelfUser())
|
||||
return;
|
||||
|
||||
if (!event.isFromGuild())
|
||||
return;
|
||||
|
||||
DiscordMessageHooks.discordMessageEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* The bot is connected to discord and ready to begin sending messages
|
||||
*/
|
||||
@Override
|
||||
public void onReady(@NotNull ReadyEvent event) {
|
||||
if (event.getJDA().getStatus() == JDA.Status.CONNECTED) {
|
||||
BotController.INSTANCE.getLogger().info("Successfully connected to discord");
|
||||
|
||||
PermissionChecker.checkBotSetup();
|
||||
ChannelManager.loadChannels();
|
||||
BotReadyHooks.startActivityUpdates(event);
|
||||
BotReadyHooks.startTopicUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A button was clicked.
|
||||
*/
|
||||
@Override
|
||||
public void onButtonInteraction(@NotNull ButtonInteractionEvent event) {
|
||||
if (event.getComponentId().equals("sdrefreshbtn")) {
|
||||
event.getMessage().editMessageEmbeds(ServerStatusSlashCommand.runStatusCommand()).queue();
|
||||
event.reply("Success!").setEphemeral(true).queue();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.hooks;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.managers.ChannelManager;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageDestination;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import com.hypherionmc.sdlink.core.util.SystemUtils;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildMessageChannel;
|
||||
import net.dv8tion.jda.api.events.session.ReadyEvent;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Hooks to run when the bot is ready
|
||||
*/
|
||||
public class BotReadyHooks {
|
||||
|
||||
/**
|
||||
* Update the bot activity
|
||||
* @param event The {@link ReadyEvent}
|
||||
*/
|
||||
public static void startActivityUpdates(ReadyEvent event) {
|
||||
BotController.taskManager.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
if (event.getJDA().getStatus() == JDA.Status.CONNECTED) {
|
||||
Activity act = Activity.of(sdLinkConfig.botConfig.botStatus.botStatusType, sdLinkConfig.botConfig.botStatus.botStatus
|
||||
.replace("%players%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft()))
|
||||
.replace("%maxplayers%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight())));
|
||||
|
||||
if (sdLinkConfig.botConfig.botStatus.botStatusType == Activity.ActivityType.STREAMING) {
|
||||
act = Activity.of(sdLinkConfig.botConfig.botStatus.botStatusType, sdLinkConfig.botConfig.botStatus.botStatus
|
||||
.replace("%players%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft()))
|
||||
.replace("%maxplayers%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight())),
|
||||
sdLinkConfig.botConfig.botStatus.botStatusStreamingURL);
|
||||
}
|
||||
|
||||
event.getJDA().getPresence().setActivity(act);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().info(e.getMessage());
|
||||
}
|
||||
}
|
||||
}, sdLinkConfig.botConfig.statusUpdateInterval, sdLinkConfig.botConfig.statusUpdateInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Chat Channel topic, if enabled
|
||||
*/
|
||||
public static void startTopicUpdates() {
|
||||
if (!sdLinkConfig.botConfig.channelTopic.doTopicUpdates)
|
||||
return;
|
||||
|
||||
BotController.taskManager.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
if (BotController.INSTANCE.isBotReady() && (sdLinkConfig.botConfig.channelTopic.channelTopic != null && !sdLinkConfig.botConfig.channelTopic.channelTopic.isEmpty())) {
|
||||
StandardGuildMessageChannel channel = ChannelManager.getDestinationChannel(MessageDestination.CHAT);
|
||||
if (channel != null) {
|
||||
String topic = sdLinkConfig.botConfig.channelTopic.channelTopic
|
||||
.replace("%players%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft()))
|
||||
.replace("%maxplayers%", String.valueOf(SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight()))
|
||||
.replace("%uptime%", SystemUtils.secondsToTimestamp(SDLinkPlatform.minecraftHelper.getServerUptime()));
|
||||
channel.getManager().setTopic(topic).queue();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().info(e.getMessage());
|
||||
}
|
||||
}
|
||||
}, 6, 6, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.discord.hooks;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.managers.ChannelManager;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageDestination;
|
||||
import com.hypherionmc.sdlink.core.services.SDLinkPlatform;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Hook class to handle messages the bot receives
|
||||
*/
|
||||
public class DiscordMessageHooks {
|
||||
|
||||
/**
|
||||
* Chat messages to be sent back to discord
|
||||
*/
|
||||
public static void discordMessageEvent(MessageReceivedEvent event) {
|
||||
try {
|
||||
if (event.getChannel().getIdLong() != ChannelManager.getDestinationChannel(MessageDestination.CHAT).getIdLong())
|
||||
return;
|
||||
|
||||
if (event.getAuthor().isBot() && sdLinkConfig.chatConfig.ignoreBots)
|
||||
return;
|
||||
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().info("Sending Message from {}: {}", event.getAuthor().getName(), event.getMessage().getContentStripped());
|
||||
}
|
||||
SDLinkPlatform.minecraftHelper.discordMessageReceived(event.getMember().getEffectiveName(), event.getMessage().getContentRaw());
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.managers;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageDestination;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildMessageChannel;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Load and Cache configured channels for later use
|
||||
*/
|
||||
public class ChannelManager {
|
||||
|
||||
private static StandardGuildMessageChannel consoleChannel;
|
||||
|
||||
private static final HashMap<MessageDestination, Pair<StandardGuildMessageChannel, Boolean>> channelMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Load configured channel, while always defaulting back to ChatChannel for channels that aren't configured
|
||||
*/
|
||||
public static void loadChannels() {
|
||||
channelMap.clear();
|
||||
|
||||
JDA jda = BotController.INSTANCE.getJDA();
|
||||
|
||||
StandardGuildMessageChannel chatChannel = jda.getChannelById(StandardGuildMessageChannel.class, sdLinkConfig.channelsAndWebhooks.channels.chatChannelID);
|
||||
StandardGuildMessageChannel eventChannel = jda.getChannelById(StandardGuildMessageChannel.class, sdLinkConfig.channelsAndWebhooks.channels.eventsChannelID);
|
||||
consoleChannel = jda.getChannelById(StandardGuildMessageChannel.class, sdLinkConfig.channelsAndWebhooks.channels.consoleChannelID);
|
||||
|
||||
if (chatChannel != null) {
|
||||
channelMap.put(MessageDestination.CHAT, Pair.of(chatChannel, false));
|
||||
}
|
||||
|
||||
channelMap.put(MessageDestination.EVENT, eventChannel != null ? Pair.of(eventChannel, false) : Pair.of(chatChannel, false));
|
||||
channelMap.put(MessageDestination.CONSOLE, consoleChannel != null ? Pair.of(consoleChannel, true) : Pair.of(chatChannel, false));
|
||||
}
|
||||
|
||||
public static StandardGuildMessageChannel getConsoleChannel() {
|
||||
return consoleChannel;
|
||||
}
|
||||
|
||||
public static StandardGuildMessageChannel getDestinationChannel(MessageDestination destination) {
|
||||
return channelMap.get(destination).getLeft();
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.managers;
|
||||
|
||||
import com.hypherionmc.sdlink.core.database.SDLinkAccount;
|
||||
import io.jsondb.JsonDBTemplate;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Helper class to initialize the JSON database
|
||||
*/
|
||||
public class DatabaseManager {
|
||||
|
||||
public static final JsonDBTemplate sdlinkDatabase = new JsonDBTemplate("sdlinkstorage", "com.hypherionmc.sdlink.core.database");
|
||||
|
||||
public static void initialize() {
|
||||
if (!sdlinkDatabase.collectionExists(SDLinkAccount.class)) {
|
||||
sdlinkDatabase.createCollection(SDLinkAccount.class);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.managers;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildMessageChannel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Startup permission checker hook to check if the bot has all the required permissions to function
|
||||
*/
|
||||
public class PermissionChecker {
|
||||
|
||||
// Invite URL for bot shown in server logs
|
||||
private static final String DISCORD_INVITE = "https://discord.com/api/oauth2/authorize?client_id={bot_id}&permissions=3154463760&scope=bot%20applications.commands";
|
||||
|
||||
// Base Permissions required by the bot to operate
|
||||
private static final List<Permission> BOT_PERMS = new ArrayList<>() {{
|
||||
add(Permission.NICKNAME_CHANGE);
|
||||
add(Permission.NICKNAME_MANAGE);
|
||||
add(Permission.MANAGE_WEBHOOKS);
|
||||
add(Permission.MESSAGE_SEND);
|
||||
add(Permission.MESSAGE_EMBED_LINKS);
|
||||
add(Permission.MESSAGE_HISTORY);
|
||||
add(Permission.MESSAGE_EXT_EMOJI);
|
||||
add(Permission.MANAGE_ROLES);
|
||||
add(Permission.MESSAGE_MANAGE);
|
||||
}};
|
||||
|
||||
// Basic channel permissions required by all channels
|
||||
private static final List<Permission> BASE_CHANNEL_PERMS = new ArrayList<>() {{
|
||||
add(Permission.VIEW_CHANNEL);
|
||||
add(Permission.MESSAGE_SEND);
|
||||
add(Permission.MESSAGE_EMBED_LINKS);
|
||||
add(Permission.MANAGE_WEBHOOKS);
|
||||
}};
|
||||
|
||||
/**
|
||||
* Run the permission checker to see if the bot has all the required permissions
|
||||
*/
|
||||
public static void checkBotSetup() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("\r\n").append("******************* Simple Discord Link Errors *******************").append("\r\n");
|
||||
AtomicInteger errCount = new AtomicInteger();
|
||||
BotController controller = BotController.INSTANCE;
|
||||
|
||||
if (!controller.isBotReady())
|
||||
return;
|
||||
|
||||
controller.getLogger().info("Discord Invite Link for Bot: {}", DISCORD_INVITE.replace("{bot_id}", controller.getJDA().getSelfUser().getId()));
|
||||
|
||||
if (controller.getJDA().getGuilds().isEmpty()) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append("Bot does not appear to be in any servers. You need to invite the bot to your discord server before chat relays will work. Use link ")
|
||||
.append(DISCORD_INVITE.replace("{bot_id}", controller.getJDA().getSelfUser().getId())).append(" to invite the bot.")
|
||||
.append("\r\n");
|
||||
} else {
|
||||
if (controller.getJDA().getGuilds().size() > 1) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append("Bot appears to be in multiple discord servers. This mod is only designed to work with a single discord server")
|
||||
.append("\r\n");
|
||||
} else {
|
||||
Guild guild = controller.getJDA().getGuilds().get(0);
|
||||
|
||||
if (guild != null) {
|
||||
Member bot = guild.getMemberById(controller.getJDA().getSelfUser().getIdLong());
|
||||
EnumSet<Permission> botPerms = bot.getPermissionsExplicit();
|
||||
|
||||
RoleManager.loadRequiredRoles(errCount, builder);
|
||||
|
||||
if (!botPerms.contains(Permission.ADMINISTRATOR)) {
|
||||
checkBotPerms(errCount, builder, botPerms);
|
||||
|
||||
checkChannelPerms(
|
||||
sdLinkConfig.channelsAndWebhooks.channels.chatChannelID,
|
||||
"Chat Channel",
|
||||
errCount,
|
||||
builder,
|
||||
bot,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
checkChannelPerms(
|
||||
sdLinkConfig.channelsAndWebhooks.channels.eventsChannelID,
|
||||
"Events Channel",
|
||||
errCount,
|
||||
builder,
|
||||
bot,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
checkChannelPerms(
|
||||
sdLinkConfig.channelsAndWebhooks.channels.consoleChannelID,
|
||||
"Console Channel",
|
||||
errCount,
|
||||
builder,
|
||||
bot,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errCount.get() > 0) {
|
||||
builder.append("\r\n").append("******************* Simple Discord Link Errors *******************").append("\r\n");
|
||||
controller.getLogger().info(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkBotPerms(AtomicInteger errCount, StringBuilder builder, EnumSet<Permission> permissions) {
|
||||
BOT_PERMS.forEach(perm -> {
|
||||
if (!permissions.contains(perm)) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append("Missing Bot Permission: ")
|
||||
.append(perm.getName())
|
||||
.append("\r\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void checkChannelPerms(Long channelID, String channelName, AtomicInteger errCount, StringBuilder builder, Member bot, boolean channelRequired, boolean isChat) {
|
||||
if (channelRequired && channelID == 0) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append(channelName)
|
||||
.append(" ID is not set.... This value is required")
|
||||
.append("\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
StandardGuildMessageChannel channel = BotController.INSTANCE.getJDA().getChannelById(StandardGuildMessageChannel.class, channelID);
|
||||
|
||||
if (channel == null) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append(channelName)
|
||||
.append(" ID does not point to a valid Discord Channel. Please double check this")
|
||||
.append("\r\n");
|
||||
} else {
|
||||
EnumSet<Permission> chatPerms = bot.getPermissionsExplicit(channel);
|
||||
|
||||
BASE_CHANNEL_PERMS.forEach(perm -> {
|
||||
if (!chatPerms.contains(perm)) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append("Missing ")
|
||||
.append(channelName)
|
||||
.append(" Channel Permission: ")
|
||||
.append(perm.getName())
|
||||
.append("\r\n");
|
||||
}
|
||||
});
|
||||
|
||||
if (isChat) {
|
||||
if (sdLinkConfig.botConfig.channelTopic.doTopicUpdates && !chatPerms.contains(Permission.MANAGE_CHANNEL)) {
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get()).append(") ").append("Missing Chat Channel Permission: Manage Channel. Topic updates will not work").append("\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.managers;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Load and Cache roles needed by the bot
|
||||
*/
|
||||
public class RoleManager {
|
||||
|
||||
private static Role staffRole;
|
||||
private static Role whitelistedRole;
|
||||
private static Role linkedRole;
|
||||
private static final HashMap<String, Role> commandRoles = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Check and load the roles required by the bot
|
||||
* @param errCount
|
||||
* @param builder
|
||||
*/
|
||||
public static void loadRequiredRoles(AtomicInteger errCount, StringBuilder builder) {
|
||||
if (!sdLinkConfig.botConfig.staffRole.isEmpty()) {
|
||||
staffRole = getRole(errCount, builder, "Staff", sdLinkConfig.botConfig.staffRole);
|
||||
}
|
||||
|
||||
if (!sdLinkConfig.whitelistingAndLinking.whitelisting.autoWhitelistRole.isEmpty()) {
|
||||
whitelistedRole = getRole(errCount, builder, "Whitelist", sdLinkConfig.whitelistingAndLinking.whitelisting.autoWhitelistRole);
|
||||
}
|
||||
|
||||
if (!sdLinkConfig.whitelistingAndLinking.accountLinking.linkedRole.isEmpty()) {
|
||||
linkedRole = getRole(errCount, builder, "Linked Account", sdLinkConfig.whitelistingAndLinking.accountLinking.linkedRole);
|
||||
}
|
||||
|
||||
if (sdLinkConfig.linkedCommands.enabled) {
|
||||
commandRoles.clear();
|
||||
sdLinkConfig.linkedCommands.commands.forEach(cmd -> {
|
||||
if (!cmd.discordRole.isEmpty()) {
|
||||
Role role = getRole(errCount, builder, cmd.discordCommand + " usage", cmd.discordRole);
|
||||
if (role != null) {
|
||||
commandRoles.putIfAbsent(cmd.discordCommand, role);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a role from either a Name or ID
|
||||
* @param errCount Counter holding the current error count
|
||||
* @param builder String builder that is used to build the error messages
|
||||
* @param roleIdentifier Log identifier for the role being loaded
|
||||
* @param roleID The ID or Name of the role to load
|
||||
* @return The role that matched or NULL
|
||||
*/
|
||||
private static Role getRole(AtomicInteger errCount, StringBuilder builder, String roleIdentifier, String roleID) {
|
||||
Role role = BotController.INSTANCE.getJDA().getRoleById(roleID);
|
||||
|
||||
if (role != null) {
|
||||
return role;
|
||||
} else {
|
||||
List<Role> roles = BotController.INSTANCE.getJDA().getRolesByName(roleID, true);
|
||||
if (!roles.isEmpty()) {
|
||||
return roles.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
errCount.incrementAndGet();
|
||||
builder.append(errCount.get())
|
||||
.append(") ")
|
||||
.append("Missing ")
|
||||
.append(roleIdentifier)
|
||||
.append(" Role. Role: ")
|
||||
.append(roleID)
|
||||
.append(" cannot be found in the server")
|
||||
.append("\r\n");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Role getLinkedRole() {
|
||||
return linkedRole;
|
||||
}
|
||||
|
||||
public static Role getStaffRole() {
|
||||
return staffRole;
|
||||
}
|
||||
|
||||
public static Role getWhitelistedRole() {
|
||||
return whitelistedRole;
|
||||
}
|
||||
|
||||
public static HashMap<String, Role> getCommandRoles() {
|
||||
return commandRoles;
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.managers;
|
||||
|
||||
import club.minnced.discord.webhook.WebhookClient;
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageDestination;
|
||||
import com.hypherionmc.sdlink.core.messaging.SDLinkWebhookClient;
|
||||
import com.hypherionmc.sdlink.core.util.EncryptionUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Load and cache Webhook clients for later use
|
||||
*/
|
||||
public class WebhookManager {
|
||||
|
||||
private static WebhookClient chatWebhookClient, eventWebhookClient, consoleWebhookClient;
|
||||
private static final HashMap<MessageDestination, WebhookClient> clientMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Load configured webhook clients
|
||||
* Webhooks that are not configured, will use their Channel ID instead
|
||||
*/
|
||||
public static void init() {
|
||||
clientMap.clear();
|
||||
|
||||
if (sdLinkConfig == null || !sdLinkConfig.channelsAndWebhooks.webhooks.enabled)
|
||||
return;
|
||||
|
||||
if (!sdLinkConfig.generalConfig.enabled)
|
||||
return;
|
||||
|
||||
if (!sdLinkConfig.channelsAndWebhooks.webhooks.chatWebhook.isEmpty()) {
|
||||
chatWebhookClient = new SDLinkWebhookClient(
|
||||
"Chat",
|
||||
EncryptionUtil.INSTANCE.decrypt(sdLinkConfig.channelsAndWebhooks.webhooks.chatWebhook)
|
||||
).build();
|
||||
BotController.INSTANCE.getLogger().info("Using Webhook for Chat Messages");
|
||||
}
|
||||
|
||||
if (!sdLinkConfig.channelsAndWebhooks.webhooks.eventsWebhook.isEmpty()) {
|
||||
eventWebhookClient = new SDLinkWebhookClient(
|
||||
"Events",
|
||||
EncryptionUtil.INSTANCE.decrypt(sdLinkConfig.channelsAndWebhooks.webhooks.eventsWebhook)
|
||||
).build();
|
||||
BotController.INSTANCE.getLogger().info("Using Webhook for Event Messages");
|
||||
}
|
||||
|
||||
if (!sdLinkConfig.channelsAndWebhooks.webhooks.consoleWebhook.isEmpty()) {
|
||||
consoleWebhookClient = new SDLinkWebhookClient(
|
||||
"Console",
|
||||
EncryptionUtil.INSTANCE.decrypt(sdLinkConfig.channelsAndWebhooks.webhooks.consoleWebhook)
|
||||
).build();
|
||||
BotController.INSTANCE.getLogger().info("Using Webhook for Console Messages");
|
||||
}
|
||||
|
||||
if (chatWebhookClient != null) {
|
||||
clientMap.put(MessageDestination.CHAT, chatWebhookClient);
|
||||
}
|
||||
|
||||
clientMap.put(MessageDestination.EVENT, eventWebhookClient);
|
||||
clientMap.put(MessageDestination.CONSOLE, consoleWebhookClient);
|
||||
}
|
||||
|
||||
public static WebhookClient getWebhookClient(MessageDestination destination) {
|
||||
return clientMap.get(destination);
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (chatWebhookClient != null) {
|
||||
chatWebhookClient.close();
|
||||
}
|
||||
if (eventWebhookClient != null) {
|
||||
eventWebhookClient.close();
|
||||
}
|
||||
if (consoleWebhookClient != null) {
|
||||
consoleWebhookClient.close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Specifies to what channel a message should be delivered
|
||||
*/
|
||||
public enum MessageDestination {
|
||||
CHAT,
|
||||
EVENT,
|
||||
CONSOLE;
|
||||
|
||||
public boolean isChat() {
|
||||
return this == CHAT;
|
||||
}
|
||||
|
||||
public boolean isEvent() {
|
||||
return this == EVENT;
|
||||
}
|
||||
|
||||
public boolean isConsole() {
|
||||
return this == CONSOLE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Used to specify the type of message being sent
|
||||
*/
|
||||
public enum MessageType {
|
||||
CHAT,
|
||||
START_STOP,
|
||||
JOIN_LEAVE,
|
||||
ADVANCEMENT,
|
||||
DEATH,
|
||||
COMMAND,
|
||||
CONSOLE
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Helper Class to return the result of interactions between Discord and Minecraft
|
||||
*/
|
||||
public class Result {
|
||||
|
||||
enum Type {
|
||||
ERROR,
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final String message;
|
||||
|
||||
private Result(Type type, String message) {
|
||||
this.type = type;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static Result success(String message) {
|
||||
return new Result(Type.SUCCESS, message);
|
||||
}
|
||||
|
||||
public static Result error(String message) {
|
||||
return new Result(Type.ERROR, message);
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return this.type == Type.ERROR;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging;
|
||||
|
||||
import club.minnced.discord.webhook.WebhookClientBuilder;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Wrapped {@link WebhookClientBuilder} for our webhooks
|
||||
*/
|
||||
public class SDLinkWebhookClient extends WebhookClientBuilder {
|
||||
|
||||
public SDLinkWebhookClient(String name, String url) {
|
||||
super(url);
|
||||
|
||||
this.setThreadFactory((job) -> {
|
||||
Thread thread = new Thread(job);
|
||||
thread.setName(name + " Webhook Thread");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
this.setWait(false);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging.discord;
|
||||
|
||||
import club.minnced.discord.webhook.WebhookClient;
|
||||
import club.minnced.discord.webhook.send.WebhookEmbed;
|
||||
import club.minnced.discord.webhook.send.WebhookEmbedBuilder;
|
||||
import club.minnced.discord.webhook.send.WebhookMessageBuilder;
|
||||
import com.hypherionmc.sdlink.core.accounts.DiscordAuthor;
|
||||
import com.hypherionmc.sdlink.core.config.impl.MessageChannelConfig;
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.managers.ChannelManager;
|
||||
import com.hypherionmc.sdlink.core.managers.WebhookManager;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageType;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildMessageChannel;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Represents a message sent from Minecraft to Discord
|
||||
* This ensures the message is properly formatted and configured
|
||||
*/
|
||||
public final class DiscordMessage {
|
||||
|
||||
private final MessageType messageType;
|
||||
private final DiscordAuthor author;
|
||||
private final String message;
|
||||
private final Runnable afterSend;
|
||||
|
||||
/**
|
||||
* Private instance. Use {@link DiscordMessageBuilder} to create an instance
|
||||
*/
|
||||
DiscordMessage(DiscordMessageBuilder builder) {
|
||||
this.messageType = builder.getMessageType();
|
||||
this.author = builder.getAuthor();
|
||||
this.message = builder.getMessage();
|
||||
this.afterSend = builder.getAfterSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to send the message to discord
|
||||
*/
|
||||
public void sendMessage() {
|
||||
if (!BotController.INSTANCE.isBotReady())
|
||||
return;
|
||||
|
||||
try {
|
||||
if (messageType == MessageType.CONSOLE) {
|
||||
sendConsoleMessage();
|
||||
} else {
|
||||
sendNormalMessage();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().error("Failed to send Discord Message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Non Console relay message to discord
|
||||
*/
|
||||
private void sendNormalMessage() {
|
||||
Triple<StandardGuildMessageChannel, WebhookClient, Boolean> channel = resolveDestination();
|
||||
|
||||
// Check if a webhook is configured, and use that instead
|
||||
if (channel.getMiddle() != null && sdLinkConfig.channelsAndWebhooks.webhooks.enabled) {
|
||||
WebhookMessageBuilder builder = new WebhookMessageBuilder();
|
||||
builder.setUsername(this.author.getUsername());
|
||||
if (!this.author.getAvatar().isEmpty()) {
|
||||
builder.setAvatarUrl(this.author.getAvatar());
|
||||
}
|
||||
|
||||
// Message must be an Embed
|
||||
if (channel.getRight()) {
|
||||
EmbedBuilder eb = buildEmbed(false);
|
||||
WebhookEmbed web = WebhookEmbedBuilder.fromJDA(eb.build()).build();
|
||||
builder.addEmbeds(web);
|
||||
} else {
|
||||
builder.setContent(message);
|
||||
}
|
||||
|
||||
channel.getMiddle().send(builder.build());
|
||||
} else {
|
||||
// Use the configured channel instead
|
||||
if (channel.getRight()) {
|
||||
EmbedBuilder eb = buildEmbed(true);
|
||||
channel.getLeft().sendMessageEmbeds(eb.build()).queue();
|
||||
} else {
|
||||
channel.getLeft().sendMessage(
|
||||
this.messageType == MessageType.CHAT ?
|
||||
sdLinkConfig.messageFormatting.chat.replace("%player%", author.getUsername()).replace("%message%", message)
|
||||
: message)
|
||||
.queue(success -> {
|
||||
if (afterSend != null) {
|
||||
afterSend.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used for console relay messages
|
||||
*/
|
||||
private void sendConsoleMessage() {
|
||||
try {
|
||||
if (!BotController.INSTANCE.isBotReady() || !sdLinkConfig.chatConfig.sendConsoleMessages)
|
||||
return;
|
||||
|
||||
StandardGuildMessageChannel channel = ChannelManager.getConsoleChannel();
|
||||
if (channel != null) {
|
||||
channel.sendMessage(this.message).queue();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().error("Failed to send console message", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (afterSend != null) {
|
||||
afterSend.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an embed with the supplied information
|
||||
* @param withAuthor Should the author be appended to the embed. Not used for Webhooks
|
||||
*/
|
||||
private EmbedBuilder buildEmbed(boolean withAuthor) {
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
|
||||
if (withAuthor) {
|
||||
builder.setAuthor(
|
||||
this.author.getUsername(),
|
||||
null,
|
||||
this.author.getAvatar().isEmpty() ? null : this.author.getAvatar()
|
||||
);
|
||||
}
|
||||
|
||||
builder.setDescription(message);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out where the message must be delivered to, based on the config values
|
||||
*/
|
||||
private Triple<StandardGuildMessageChannel, WebhookClient, Boolean> resolveDestination() {
|
||||
switch (messageType) {
|
||||
case CHAT -> {
|
||||
MessageChannelConfig.DestinationObject chat = sdLinkConfig.messageDestinations.chat;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(chat.channel),
|
||||
WebhookManager.getWebhookClient(chat.channel),
|
||||
chat.useEmbed
|
||||
);
|
||||
}
|
||||
case START_STOP -> {
|
||||
MessageChannelConfig.DestinationObject startStop = sdLinkConfig.messageDestinations.startStop;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(startStop.channel),
|
||||
WebhookManager.getWebhookClient(startStop.channel),
|
||||
startStop.useEmbed
|
||||
);
|
||||
}
|
||||
case JOIN_LEAVE -> {
|
||||
MessageChannelConfig.DestinationObject joinLeave = sdLinkConfig.messageDestinations.joinLeave;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(joinLeave.channel),
|
||||
WebhookManager.getWebhookClient(joinLeave.channel),
|
||||
joinLeave.useEmbed
|
||||
);
|
||||
}
|
||||
case ADVANCEMENT -> {
|
||||
MessageChannelConfig.DestinationObject advancement = sdLinkConfig.messageDestinations.advancements;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(advancement.channel),
|
||||
WebhookManager.getWebhookClient(advancement.channel),
|
||||
advancement.useEmbed
|
||||
);
|
||||
}
|
||||
case DEATH -> {
|
||||
MessageChannelConfig.DestinationObject death = sdLinkConfig.messageDestinations.death;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(death.channel),
|
||||
WebhookManager.getWebhookClient(death.channel),
|
||||
death.useEmbed
|
||||
);
|
||||
}
|
||||
case COMMAND -> {
|
||||
MessageChannelConfig.DestinationObject command = sdLinkConfig.messageDestinations.commands;
|
||||
return Triple.of(
|
||||
ChannelManager.getDestinationChannel(command.channel),
|
||||
WebhookManager.getWebhookClient(command.channel),
|
||||
command.useEmbed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This code should never be reached, but it's added here as a fail-safe
|
||||
MessageChannelConfig.DestinationObject chat = sdLinkConfig.messageDestinations.chat;
|
||||
return Triple.of(ChannelManager.getDestinationChannel(chat.channel), WebhookManager.getWebhookClient(chat.channel), chat.useEmbed);
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.messaging.discord;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.DiscordAuthor;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageType;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Used to construct a {@link DiscordMessage} to be sent back to discord
|
||||
*/
|
||||
public final class DiscordMessageBuilder {
|
||||
|
||||
private final MessageType messageType;
|
||||
private DiscordAuthor author;
|
||||
private String message;
|
||||
private Runnable afterSend;
|
||||
|
||||
/**
|
||||
* Construct a discord message
|
||||
* @param messageType The type of message being sent
|
||||
*/
|
||||
public DiscordMessageBuilder(MessageType messageType) {
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Author to the message
|
||||
*/
|
||||
public DiscordMessageBuilder author(DiscordAuthor author) {
|
||||
this.author = author;
|
||||
|
||||
if (author.getUsername().equalsIgnoreCase("server")) {
|
||||
this.author = DiscordAuthor.SERVER;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Actual message that will be sent
|
||||
*/
|
||||
public DiscordMessageBuilder message(String message) {
|
||||
message = message.replace("<@", "");
|
||||
message = message.replace("@everyone", "");
|
||||
message = message.replace("@here", "");
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DiscordMessageBuilder afterSend(Runnable afterSend) {
|
||||
this.afterSend = afterSend;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Discord Message ready to be sent
|
||||
*/
|
||||
public DiscordMessage build() {
|
||||
if (this.author == null) {
|
||||
this.author = DiscordAuthor.SERVER;
|
||||
}
|
||||
|
||||
if (this.message == null) {
|
||||
this.message = "";
|
||||
}
|
||||
|
||||
return new DiscordMessage(this);
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public MessageType getMessageType() {
|
||||
return messageType;
|
||||
}
|
||||
|
||||
public DiscordAuthor getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public Runnable getAfterSend() {
|
||||
return afterSend;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.services;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.services.helpers.IMinecraftHelper;
|
||||
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Service loader for library services
|
||||
*/
|
||||
public class SDLinkPlatform {
|
||||
|
||||
public static IMinecraftHelper minecraftHelper = load(IMinecraftHelper.class);
|
||||
|
||||
public static <T> T load(Class<T> clazz) {
|
||||
final T loadedService = ServiceLoader.load(clazz)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName()));
|
||||
BotController.INSTANCE.getLogger().debug("Loaded {} for service {}", loadedService, clazz);
|
||||
return loadedService;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.services.helpers;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.MinecraftAccount;
|
||||
import com.hypherionmc.sdlink.core.messaging.Result;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Service to bridge communication between the Library and Minecraft
|
||||
*/
|
||||
public interface IMinecraftHelper {
|
||||
|
||||
void discordMessageReceived(String username, String message);
|
||||
Result checkWhitelisting();
|
||||
Result isPlayerWhitelisted(MinecraftAccount account);
|
||||
Result whitelistPlayer(MinecraftAccount account);
|
||||
Result unWhitelistPlayer(MinecraftAccount account);
|
||||
List<MinecraftAccount> getWhitelistedPlayers();
|
||||
Pair<Integer, Integer> getPlayerCounts();
|
||||
List<MinecraftAccount> getOnlinePlayers();
|
||||
long getServerUptime();
|
||||
String getServerVersion();
|
||||
Result executeMinecraftCommand(String command, String args);
|
||||
boolean isOnlineMode();
|
||||
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.util;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
|
||||
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Util Class to handle Encryption/Decryption of Bot-Tokens and Webhook URLS
|
||||
* Since people DON'T READ THE COMMENTS and leave these in-tact,
|
||||
* they are now encrypted by default
|
||||
*/
|
||||
public final class EncryptionUtil {
|
||||
|
||||
public static EncryptionUtil INSTANCE = getInstance();
|
||||
private final boolean canRun;
|
||||
|
||||
// Instance of the Encryptor Used
|
||||
private final StandardPBEStringEncryptor encryptor;
|
||||
|
||||
private static EncryptionUtil getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new EncryptionUtil();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private EncryptionUtil() {
|
||||
String encCode = "";
|
||||
|
||||
File storageDir = new File("sdlinkstorage");
|
||||
if (storageDir.exists())
|
||||
storageDir.mkdirs();
|
||||
|
||||
// Try to read a saved encryption key, or try to save a new one
|
||||
try {
|
||||
File encKey = new File(storageDir.getAbsolutePath() + File.separator + "sdlink.enc");
|
||||
if (!encKey.exists()) {
|
||||
FileUtils.writeStringToFile(encKey, getSaltString(), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
encCode = FileUtils.readFileToString(encKey, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
BotController.INSTANCE.getLogger().error("Failed to initialize Encryption", e);
|
||||
}
|
||||
|
||||
encryptor = new StandardPBEStringEncryptor();
|
||||
encryptor.setPassword(encCode);
|
||||
|
||||
canRun = !encCode.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will Encrypt the string passed into it, if it's not already encrypted
|
||||
* @param input The string to be encrypted
|
||||
* @return The encrypted string
|
||||
*/
|
||||
public String encrypt(String input) {
|
||||
if (!canRun)
|
||||
return input;
|
||||
|
||||
if (isEncrypted(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
input = "enc:" + input;
|
||||
return encryptor.encrypt(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts an encrypted string
|
||||
* @param input The encrypted String
|
||||
* @return The Plain Text String
|
||||
*/
|
||||
public String decrypt(String input) {
|
||||
if (!canRun)
|
||||
return input;
|
||||
|
||||
input = internalDecrypt(input);
|
||||
|
||||
if (input.startsWith("enc:")) {
|
||||
input = input.replaceFirst("enc:", "");
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// Used internally
|
||||
private String internalDecrypt(String input) {
|
||||
if (!canRun)
|
||||
return input;
|
||||
return encryptor.decrypt(input);
|
||||
}
|
||||
|
||||
// Test if String is encrypted
|
||||
private boolean isEncrypted(String input) {
|
||||
try {
|
||||
String temp = internalDecrypt(input);
|
||||
return temp.startsWith("enc:");
|
||||
} catch (EncryptionOperationNotPossibleException ignored) {
|
||||
// String is likely not encrypted
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate Random codes for encryption/decryption
|
||||
private String getSaltString() {
|
||||
String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
StringBuilder salt = new StringBuilder();
|
||||
Random rnd = new Random();
|
||||
while (salt.length() < 18) {
|
||||
int index = (int) (rnd.nextFloat() * SALTCHARS.length());
|
||||
salt.append(SALTCHARS.charAt(index));
|
||||
}
|
||||
return salt.toString();
|
||||
}
|
||||
|
||||
}
|
126
src/main/java/com/hypherionmc/sdlink/core/util/LogReader.java
Normal file
126
src/main/java/com/hypherionmc/sdlink/core/util/LogReader.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.util;
|
||||
|
||||
import com.hypherionmc.sdlink.core.accounts.DiscordAuthor;
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.hypherionmc.sdlink.core.messaging.MessageType;
|
||||
import com.hypherionmc.sdlink.core.messaging.discord.DiscordMessage;
|
||||
import com.hypherionmc.sdlink.core.messaging.discord.DiscordMessageBuilder;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Core;
|
||||
import org.apache.logging.log4j.core.Filter;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.logging.log4j.core.config.Property;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.hypherionmc.sdlink.core.config.ConfigController.sdLinkConfig;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Log Appender to allow messages to be relayed from the Game Console to Discord
|
||||
*/
|
||||
@Plugin(name = "SDLinkLogging", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE)
|
||||
public class LogReader extends AbstractAppender {
|
||||
|
||||
public static String logs = "";
|
||||
private long time;
|
||||
private Thread messageScheduler;
|
||||
private static boolean isDevEnv = false;
|
||||
|
||||
protected LogReader(String name, Filter filter) {
|
||||
super(name, filter, null, true, new Property[0]);
|
||||
}
|
||||
|
||||
@PluginFactory
|
||||
public static LogReader createAppender(
|
||||
@PluginAttribute("name") String name,
|
||||
@PluginElement("Filter") Filter filter) {
|
||||
return new LogReader(name, filter);
|
||||
}
|
||||
|
||||
public static void init(boolean isDev) {
|
||||
isDevEnv = isDev;
|
||||
LogReader da = LogReader.createAppender("SDLinkLogging", null);
|
||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addAppender(da);
|
||||
da.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event) {
|
||||
if (BotController.INSTANCE.isBotReady()) {
|
||||
if (event.getLevel().intLevel() < Level.DEBUG.intLevel()) {
|
||||
logs += formatMessage(event) + "\n";
|
||||
scheduleMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String formatMessage(LogEvent event) {
|
||||
String devString = "**[" + formatTime(event.getTimeMillis()) + "]** " +
|
||||
"**[" + event.getThreadName() + "/" + event.getLevel().name() + "]** " +
|
||||
"**(" + event.getLoggerName().substring(event.getLoggerName().lastIndexOf(".") + 1) + ")** *" +
|
||||
event.getMessage().getFormattedMessage() + "*";
|
||||
|
||||
String prodString = "**[" + formatTime(event.getTimeMillis()) + "]** " +
|
||||
"**[" + event.getThreadName() + "/" + event.getLevel().name() + "]** *" +
|
||||
event.getMessage().getFormattedMessage() + "*";
|
||||
|
||||
return isDevEnv ? devString : prodString;
|
||||
}
|
||||
|
||||
private String formatTime(long millis) {
|
||||
DateFormat obj = new SimpleDateFormat("HH:mm:ss");
|
||||
Date res = new Date(millis);
|
||||
return obj.format(res);
|
||||
}
|
||||
|
||||
private void scheduleMessage() {
|
||||
time = System.currentTimeMillis();
|
||||
if (messageScheduler == null || !messageScheduler.isAlive()) {
|
||||
messageScheduler = new Thread(() -> {
|
||||
while (true) {
|
||||
if (!BotController.INSTANCE.isBotReady())
|
||||
return;
|
||||
if (System.currentTimeMillis() - time > 250) {
|
||||
if (logs.length() > 2000) {
|
||||
logs = logs.substring(0, 1999);
|
||||
}
|
||||
|
||||
DiscordMessage discordMessage = new DiscordMessageBuilder(MessageType.CONSOLE)
|
||||
.message(logs)
|
||||
.author(DiscordAuthor.SERVER)
|
||||
.build();
|
||||
|
||||
if (sdLinkConfig.chatConfig.sendConsoleMessages) {
|
||||
discordMessage.sendMessage();
|
||||
}
|
||||
|
||||
logs = "";
|
||||
break;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(30);
|
||||
} catch (InterruptedException e) {
|
||||
if (sdLinkConfig.generalConfig.debugging) {
|
||||
BotController.INSTANCE.getLogger().error("Failed to send console message: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
messageScheduler.start();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.util;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import com.jagrosh.jdautilities.command.SlashCommandEvent;
|
||||
import com.jagrosh.jdautilities.menu.EmbedPaginator;
|
||||
import net.dv8tion.jda.api.exceptions.PermissionException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Util classes to help manage certain discord message actions
|
||||
*/
|
||||
public class MessageUtil {
|
||||
|
||||
/**
|
||||
* Create an Embed Paginator for use with Slash Commands
|
||||
* @param event The event of the executed command
|
||||
*/
|
||||
public static EmbedPaginator.Builder defaultPaginator(SlashCommandEvent event) {
|
||||
return new EmbedPaginator.Builder()
|
||||
.setTimeout(1, TimeUnit.MINUTES)
|
||||
.setEventWaiter(BotController.INSTANCE.getEventWaiter())
|
||||
.waitOnSinglePage(true)
|
||||
.setFinalAction(m -> {
|
||||
try {
|
||||
m.clearReactions().queue();
|
||||
m.delete().queue();
|
||||
} catch(PermissionException ex) {
|
||||
ex.printStackTrace();
|
||||
event.reply(ex.getMessage()).setEphemeral(true).queue();
|
||||
}
|
||||
})
|
||||
.setText((BiFunction<Integer, Integer, String>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a large list of items into smaller sublists. This is to help with Discord limits on pagination
|
||||
* @param source The list of objects to split
|
||||
* @param length How many entries are allowed per sub-list
|
||||
*/
|
||||
public static <T> Stream<List<T>> listBatches(List<T> source, int length) {
|
||||
if (length <= 0)
|
||||
throw new IllegalArgumentException("length = " + length);
|
||||
int size = source.size();
|
||||
if (size <= 0)
|
||||
return Stream.empty();
|
||||
int fullChunks = (size - 1) / length;
|
||||
return IntStream.range(0, fullChunks + 1).mapToObj(
|
||||
n -> source.subList(n * length, n == fullChunks ? size : (n + 1) * length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #listBatches(List, int)}, but for HashMaps
|
||||
*/
|
||||
public static <K, V> List<Map<K, V>> splitMap(Map<K, V> map, int size) {
|
||||
List<List<Map.Entry<K, V>>> list = Lists.newArrayList(Iterables.partition(map.entrySet(), size));
|
||||
|
||||
return list.stream()
|
||||
.map(entries ->
|
||||
entries.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
101
src/main/java/com/hypherionmc/sdlink/core/util/SystemUtils.java
Normal file
101
src/main/java/com/hypherionmc/sdlink/core/util/SystemUtils.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.util;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SystemUtils {
|
||||
|
||||
/**
|
||||
* Convert Bytes into a human-readable format, like 1GB
|
||||
* From https://stackoverflow.com/a/3758880
|
||||
* @param bytes The Size in Bytes
|
||||
* @return The size formatted in KB, MB, GB, TB, PB etc
|
||||
*/
|
||||
public static String byteToHuman(long bytes) {
|
||||
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
|
||||
if (absB < 1024) {
|
||||
return bytes + " B";
|
||||
}
|
||||
long value = absB;
|
||||
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
|
||||
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
|
||||
value >>= 10;
|
||||
ci.next();
|
||||
}
|
||||
value *= Long.signum(bytes);
|
||||
return String.format("%.1f %ciB", value / 1024.0, ci.current());
|
||||
}
|
||||
|
||||
// Time Conversion
|
||||
public static final List<Long> times = Arrays.asList(
|
||||
TimeUnit.DAYS.toMillis(365),
|
||||
TimeUnit.DAYS.toMillis(30),
|
||||
TimeUnit.DAYS.toMillis(1),
|
||||
TimeUnit.HOURS.toMillis(1),
|
||||
TimeUnit.MINUTES.toMillis(1),
|
||||
TimeUnit.SECONDS.toMillis(1));
|
||||
|
||||
public static final List<String> timesString = Arrays.asList("year", "month", "day", "hour", "minute", "second");
|
||||
|
||||
/**
|
||||
* Unix Timestamp to Duration
|
||||
* @param duration Unix Timestamp
|
||||
* @return Formatted Duration
|
||||
*/
|
||||
public static String toDuration(long duration) {
|
||||
StringBuffer res = new StringBuffer();
|
||||
for (int i = 0; i < times.size(); i++) {
|
||||
Long current = times.get(i);
|
||||
long temp = duration / current;
|
||||
if (temp > 0) {
|
||||
res.append(temp).append(" ").append(timesString.get(i)).append(temp != 1 ? "s" : "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ("".equals(res.toString()))
|
||||
return "0 seconds ago";
|
||||
else
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Seconds into a Timestamp
|
||||
* @param sec Input in seconds
|
||||
*/
|
||||
public static String secondsToTimestamp(long sec) {
|
||||
long seconds = sec % 60;
|
||||
long minutes = (sec / 60) % 60;
|
||||
long hours = (sec / 3600) % 24;
|
||||
long days = sec / (3600 * 24);
|
||||
|
||||
String timeString = String.format("%02d hour(s), %02d minute(s), %02d second(s)", hours, minutes, seconds);
|
||||
|
||||
if (days > 0) {
|
||||
timeString = String.format("%d day(s), %s", days, timeString);
|
||||
}
|
||||
|
||||
return timeString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random verification code for Whitelisting and Account Linking
|
||||
*/
|
||||
public static int generateRandomJoinCode() {
|
||||
return new Random().ints(1000, 9999).findFirst().getAsInt();
|
||||
}
|
||||
|
||||
/*public static boolean hasPermission(BotController controller, Member member) {
|
||||
if (controller.getAdminRole() != null) {
|
||||
return member.getRoles().stream().anyMatch(r -> r.getIdLong() == controller.getAdminRole().getIdLong());
|
||||
}
|
||||
return member.hasPermission(Permission.ADMINISTRATOR) || member.hasPermission(Permission.KICK_MEMBERS);
|
||||
}*/
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of sdlink-core, licensed under the MIT License (MIT).
|
||||
* Copyright HypherionSA and Contributors
|
||||
*/
|
||||
package com.hypherionmc.sdlink.core.util;
|
||||
|
||||
import com.hypherionmc.sdlink.core.discord.BotController;
|
||||
import net.dv8tion.jda.api.events.GenericEvent;
|
||||
import net.dv8tion.jda.api.hooks.InterfacedEventManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author HypherionSA
|
||||
* Run discord events in seperate threads
|
||||
*/
|
||||
public class ThreadedEventManager extends InterfacedEventManager {
|
||||
|
||||
@Override
|
||||
public void handle(@NotNull GenericEvent event) {
|
||||
BotController.taskManager.submit(() -> super.handle(event));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user