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