diff --git a/README.md b/README.md index 1cda64c..6f86c24 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # SSSBliss -The "Bliss SMP" plugin described by Madness4Ever +Advanced Anti-Grief & Chat Filter +----------------------------------------------------- + +- Control access to dangerous commands +- Deny command blocks & minecart commands +- Prevent Malicious NBT +- Most Advanced Profanity Filter +- Advanced Anti-Spam +- Auto-punish system +- Webhook logging +- Quality Customer Supourt + +----------------------------------------------------- + +## How to use + +1. Download the plugin on spigot +2. Upload it to your /plugins/ folder +3. Use plugman to `/plugman load sssbliss` or restart your server +4. Open a ticket and send your server ID +5. Wait for a response from staff saying your license is verified +6. Use plugman to `/plugman load sssbliss` or restart your server +7. Configure sssbliss in /plugins/SSSBliss/config.yml + +----------------------------------------------------- diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5592830 --- /dev/null +++ b/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'java' +} + +group = project.group +version = project.version + +jar { + from { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + configurations.runtimeClasspath.collect { + it.isDirectory() ? it : zipTree(it) + } + } +} + +repositories { + mavenCentral() + maven { + name = 'spigotmc-repo' + url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' + } + maven { + url = uri("https://repo.papermc.io/repository/maven-public/") + } + maven { + name = 'sonatype' + url = 'https://oss.sonatype.org/content/groups/public/' + } +} + +dependencies { + compileOnly 'io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT' + implementation 'com.google.code.gson:gson:2.10.1' + implementation files("libs/PDK-1.3.4.jar") +} + +def targetJavaVersion = 17 +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } +} + +tasks.withType(JavaCompile).configureEach { + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + options.release = targetJavaVersion + } +} + +processResources { + def props = [version: version] + inputs.properties props + filteringCharset 'UTF-8' + filesMatching('plugin.yml') { + expand props + } +} + +compileJava.options.encoding("UTF-8") + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..bec3843 --- /dev/null +++ b/build.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Run Gradle build +./gradlew build + +# Check if the build was successful +if [ $? -eq 0 ]; then + echo "Gradle build successful." + + # SFTP upload + SFTP_HOST="192.168.1.199" + SFTP_USER="trouper" + SFTP_PASSWORD="Trouper12()1" + SFTP_REMOTE_DIR="/home/trouper/docker/data/plugins/" + + # Create a temporary file with a unique name + TEMP_FILE=$(mktemp) + + # Specify the local file to upload + LOCAL_FILE="/run/media/trouper/'1TB drive'/IJ/IdeaProjects/SSSBliss/build/libs/SSSBliss-0.2.4.jar" + + # Write the SFTP commands to the temporary file + echo "put $LOCAL_FILE $SFTP_REMOTE_DIR" > "$TEMP_FILE" + echo "bye" >> "$TEMP_FILE" + + # Use sftp non-interactively with the specified commands + sftp -oStrictHostKeyChecking=no -oBatchMode=no -b "$TEMP_FILE" "$SFTP_USER@$SFTP_HOST" < '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + 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 + + +# 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"' + +# 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 \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# 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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@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=. +@rem This is normally unused +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% equ 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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libs/JDWebHooks-1.0.1.jar b/libs/JDWebHooks-1.0.1.jar new file mode 100644 index 0000000..b3a94cb Binary files /dev/null and b/libs/JDWebHooks-1.0.1.jar differ diff --git a/libs/PDK-1.3.4.jar b/libs/PDK-1.3.4.jar new file mode 100644 index 0000000..e949bf1 Binary files /dev/null and b/libs/PDK-1.3.4.jar differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..40369d1 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'SSSBliss' diff --git a/src/main/java/io/github/thetrouper/sssbliss/SSSBliss.java b/src/main/java/io/github/thetrouper/sssbliss/SSSBliss.java new file mode 100644 index 0000000..d999c3e --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/SSSBliss.java @@ -0,0 +1,139 @@ +package io.github.thetrouper.sssbliss; + +import io.github.itzispyder.pdk.PDK; +import io.github.itzispyder.pdk.utils.misc.JsonSerializable; +import io.github.thetrouper.sssbliss.cmds.*; +import io.github.thetrouper.sssbliss.data.config.*; +import io.github.thetrouper.sssbliss.events.*; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.logging.Logger; + +public final class SSSBliss extends JavaPlugin { + + + private static SSSBliss instance; + private static final File cfgfile = new File("plugins/SSSBliss/main-config.json"); + private static final File nbtcfg = new File("plugins/SSSBliss/nbt-config.json"); + private static final File strctcfg = new File("plugins/SSSBliss/strict.json"); + private static final File swrcfg = new File("plugins/SSSBliss/swears.json"); + private static final File fpcfg = new File("plugins/SSSBliss/false-positives.json"); + private static final File advcfg = new File("plugins/SSSBliss/advanced-config.json"); + + public static MainConfig mainConfig = JsonSerializable.load(cfgfile, MainConfig.class, new MainConfig()); + public static LanguageFile language; + public static final PluginManager manager = Bukkit.getPluginManager(); + + public static final Logger log = Bukkit.getLogger(); + public static boolean usesDynamicIP; + + /** + * Plugin startup logic + */ + @Override + public void onEnable() { + + log.info("\n]======------ Pre-load started! ------======["); + PDK.init(this); + instance = this; + + log.info("Loading Config..."); + + loadConfig(); + + log.info("Language Status: (" + language.get("if-you-see-this-lang-is-broken") + ")"); + + startup(); + } + + public void startup() { + log.info("\n]======----- Loading SSSBliss! -----======["); + + // Plugin startup logic + log.info("Starting Up! (" + getDescription().getVersion() + ")..."); + + // Enable Functions + + // Commands + new SSSBlissCommand().register(); + new ChatClickCallback().register(); + + // Events + new CMDBlockExecute().register(); + + + // Scheduled timers + log.info("Finished!\n" + + " ____ __ ___ \n" + + "/\\ _`\\ /\\ \\__ __ /\\_ \\ \n" + + "\\ \\,\\L\\_\\ __ ___\\ \\ ,_\\/\\_\\ ___ __\\//\\ \\ \n" + + " \\/_\\__ \\ /'__`\\/' _ `\\ \\ \\/\\/\\ \\ /' _ `\\ /'__`\\\\ \\ \\ \n" + + " /\\ \\L\\ \\/\\ __//\\ \\/\\ \\ \\ \\_\\ \\ \\/\\ \\/\\ \\/\\ __/ \\_\\ \\_ \n" + + " \\ `\\____\\ \\____\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\_\\ \\____\\/\\____\\\n" + + " \\/_____/\\/____/\\/_/\\/_/\\/__/ \\/_/\\/_/\\/_/\\/____/\\/____/\n" + + " ]====---- Advanced Anti-Grief & Chat Filter ----====["); + } + + public void loadConfig() { + + // Init + mainConfig = JsonSerializable.load(cfgfile,MainConfig.class,new MainConfig()); + + // Save + mainConfig.save(); + + log.info("Loading Dictionary (" + SSSBliss.mainConfig.plugin.lang + ")..."); + + language = JsonSerializable.load(LanguageFile.PATH,LanguageFile.class,new LanguageFile()); + language.save(); + } + + + /** + * Plugin shutdown logic + */ + @Override + public void onDisable() { + // Plugin shutdown logic + log.info("SSSBliss has disabled! (" + getDescription().getVersion() + ") Your server is now no longer protected!"); + } + + /** + * Checks if a player is trusted. + * @param player the player to check + * @return true if the player is trusted, false otherwise + */ + public static boolean isTrusted(Player player) { + return SSSBliss.mainConfig.plugin.trustedPlayers.contains(player.getUniqueId().toString()); + } + + /** + * Checks if a command is a logged command. + * @param command the command to check + * @return true if the command is logged, false otherwise + */ + public static boolean isLoggedCommand(String command) { + return SSSBliss.mainConfig.plugin.logged.contains(command); + } + + /** + * Checks if a command is dangerous. + * @param command the command to check + * @return true if the command is dangerous, false otherwise + */ + public static boolean isDangerousCommand(String command) { + return SSSBliss.mainConfig.plugin.dangerous.contains(command); + } + /** + * Returns an instance of this plugin + * @return an instance of this plugin + */ + public static SSSBliss getInstance() { + return instance; + } + +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/cmds/ChatClickCallback.java b/src/main/java/io/github/thetrouper/sssbliss/cmds/ChatClickCallback.java new file mode 100644 index 0000000..0e470c3 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/cmds/ChatClickCallback.java @@ -0,0 +1,30 @@ +package io.github.thetrouper.sssbliss.cmds; + +import io.github.itzispyder.pdk.commands.Args; +import io.github.itzispyder.pdk.commands.CommandRegistry; +import io.github.itzispyder.pdk.commands.CustomCommand; +import io.github.itzispyder.pdk.commands.Permission; +import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; +import io.github.itzispyder.pdk.utils.misc.Cooldown; +import io.github.thetrouper.sssbliss.SSSBliss; +import io.github.thetrouper.sssbliss.server.util.Text; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.UUID; +@CommandRegistry(value = "sssblisscallback", permission = @Permission("sssbliss.callbacks")) +public class ChatClickCallback implements CustomCommand { + Cooldown fpReportCooldown = new Cooldown<>(); + @Override + public void dispatchCommand(CommandSender sender, Args args) { + Player p = (Player) sender; + switch (args.get(0).toString()) { + + } + } + + @Override + public void dispatchCompletions(CompletionBuilder b) { + b.then(b.arg("a_you","b_must","c_be","d_called","e_before","f_running","g_a","h_callback")); + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/cmds/SSSBlissCommand.java b/src/main/java/io/github/thetrouper/sssbliss/cmds/SSSBlissCommand.java new file mode 100644 index 0000000..ba212b1 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/cmds/SSSBlissCommand.java @@ -0,0 +1,68 @@ +package io.github.thetrouper.sssbliss.cmds; + +import io.github.itzispyder.pdk.commands.Args; +import io.github.itzispyder.pdk.commands.CommandRegistry; +import io.github.itzispyder.pdk.commands.CustomCommand; +import io.github.itzispyder.pdk.commands.Permission; +import io.github.itzispyder.pdk.commands.completions.CompletionBuilder; +import io.github.thetrouper.sssbliss.SSSBliss; +import io.github.thetrouper.sssbliss.server.util.CipherUtils; +import io.github.thetrouper.sssbliss.server.util.Text; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.HashSet; +@CommandRegistry(value = "sssbliss",permission = @Permission("sssbliss.debug")) +public class SSSBlissCommand implements CustomCommand { + public static boolean debugMode; + @Override + public void dispatchCommand(CommandSender commandSender, Args args) { + Player p = (Player) commandSender; + SSSBliss instance = SSSBliss.getInstance(); + switch (args.get(0).toString()) { + case "reload" -> { + if (!SSSBliss.isTrusted(p)) return; + p.sendMessage(Text.prefix("Reloading SSSBliss!")); + SSSBliss.log.info("[SSSBliss] Re-Initializing SSSBliss!"); + instance.loadConfig(); + } + case "full-system-check" -> { + + } + case "debug" -> { + switch (args.get(1).toString()) { + case "lang" -> { + p.sendMessage(SSSBliss.language.get("exmaple-message")); + } + case "toggle" -> { + debugMode = !debugMode; + p.sendMessage(Text.prefix((debugMode ? "Enabled" : "Disabled") + " debug mode.")); + } + case "encrypt" -> { + final String enc = CipherUtils.encrypt(args.getAll(2).toString()); + final String check = CipherUtils.decrypt(enc); + final String main = Text.prefix("Successfully encrypted \"&e" + check + "&7\" using AES.\n &7> &b" + enc); + SSSBliss.log.info(args.getAll(2).toString() + "\n" + enc + "\n" + check); + TextComponent message = new TextComponent(); + message.setText(main); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new net.md_5.bungee.api.chat.hover.content.Text("&bClick to copy!"))); + message.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, enc)); + p.spigot().sendMessage(message); + } + } + } + } + } + + @Override + public void dispatchCompletions(CompletionBuilder b) { + b.then(b.arg("reload","getheat","full-system-check")); + b.then(b.arg("debug").then( + b.arg("antiswear","antispam","lang","toggle"))); + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/data/Emojis.java b/src/main/java/io/github/thetrouper/sssbliss/data/Emojis.java new file mode 100644 index 0000000..8d04d9a --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/data/Emojis.java @@ -0,0 +1,33 @@ +package io.github.thetrouper.sssbliss.data; + +public class Emojis { + public static String space = "<:space:1125871914334818446>"; + public static String rightSort = "<:rightSort:1125785837255270520>"; + public static String arrowRight = "<:arrowRight:1125785471520354304>"; + public static String rightDoubleArrow = "<:rightDoubleArrow:1125785800353783868>"; + public static String activity = "<:activity:1125785527468167178>"; + public static String alarm = "<:alarm:1125790301873770606>"; + public static String target = "<:target:1125788461371232307>"; + public static String bot = "<:bot:1125791121851826206>"; + public static String cancel = "<:cancel:1125785769471127694>"; + public static String creation = "<:creation:1125790610729730109>"; + public static String date = "<:date:1125790434443145297>"; + public static String kick = "<:kick:1125785612595761212>"; + public static String members = "<:members:1125791101199077426>"; + public static String mute = "<:mute:1125789032937435247>"; + public static String noDM = "<:noDM:1125790359423824022>"; + public static String owner = "<:owner:1125791175559876669>"; + public static String potentialDanger = "<:potentialDanger:1125788513971998741>"; + public static String roles = "<:roles:1125790513933594645>"; + public static String separator = "<:separator:1125790817626357861>"; + public static String splash = "<:splash:1125791213933563905>"; + public static String success = "<:success:1125785728161419356>"; + public static String suspicious = "<:suspicious:1125790709371371682>"; + public static String trustedAdmin = "<:trustedAdmin:1125785574591180822>"; + public static String upvoter = "<:upvoter:1125790659735977994>"; + public static String vanity = "<:vanity:1125791060594004039>"; + public static String webhook = "<:webhook:1125790545638330388>"; + public static String failure = "<:failure:1125241087909429369>"; + public static String nuke = "<:nuke:1125244368807280702>"; + public static String member = "<:member:1125244044407218176>"; +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/data/config/LanguageFile.java b/src/main/java/io/github/thetrouper/sssbliss/data/config/LanguageFile.java new file mode 100644 index 0000000..0e980ff --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/data/config/LanguageFile.java @@ -0,0 +1,63 @@ +package io.github.thetrouper.sssbliss.data.config; + +import io.github.itzispyder.pdk.utils.misc.JsonSerializable; +import io.github.thetrouper.sssbliss.SSSBliss; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +public class LanguageFile implements JsonSerializable { + public static final File PATH = new File(SSSBliss.getInstance().getDataFolder(), "/lang/" + SSSBliss.mainConfig.plugin.lang); + private final Map dictionary = new HashMap<>() {{ + put("if-you-see-this-lang-is-broken", "SSSBliss language is working!"); + put("no-permission", "§cInsufficient Permissions!"); + put("cooldown", "This action is on cooldown!"); + put("false-positive-report-success", "Successfully reported a false positive!"); + put("no-online-player", "§cYou must provide an online player to send a message to!"); + put("no-message-provided", "§cYou must provide a message to send!"); + put("elevating-perms", "Elevating your permissions..."); + put("log-elevating-perms", "Elevating the permissions of %s"); + put("already-op", "You are already a server operator!"); + put("log-already-op", "The permissions of %s are already elevated! Retrying..."); + put("no-trust", "You are not a trusted user!"); + put("no-user-reply", "§cYou have nobody to reply to!"); + put("spy-enabled", "SocialSpy is now enabled."); + put("spy-disabled", "SocialSpy is now disabled."); + put("action-automatic", "§7This action was preformed automatically\n§7by the §bSSSBliss Anti-Spam§7 algorithm."); + put("action-automatic-reportable", "§7This action was preformed automatically \n§7by the §bSSSBliss Profanity Filter§7 algorithm!\n§8§o(Click to report false positive)"); + put("unicode-warn", "§cDo not send non-standard unicode in chat!"); + put("message-sent", "§d§lMessage §8» §b[§fYou §e>§f %1$s§b] §7%2$s"); + put("message-received", "§d§lMessage §8» §b[§f%1$s §e>§f You§b] §7%2$s"); + put("spy-message", "§d§lSpy §8» §b§n%1$s§7 has messaged §b§n%2$s§7."); + put("spy-message-hover", "§8]==-- §d§lSocialSpy §8--==[\n§bSender: §f%1$S\n§bReceiver: §f%2$S\n§bMessage: §f%3$S"); + put("profanity-block-notification", "§b§n%1$s§7 has triggered the anti-swear! §8(§c%2$s§7/§4%3$s§8)"); + put("profanity-block-warn", "§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute! §7§o(Hover for more info)"); + put("profanity-mute-warn", "You have been auto-muted for repeated violation of the profanity filter! §7§o(Hover for more info)"); + put("profanity-mute-notification", "§b§n%1$s§7 has been auto-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)"); + put("profanity-filter-notification-hover", "§8]==-- §d§lSSSBliss §8--==[\n§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§8§o(Click to report false positive)"); + put("severity-notification-hover", "§8]==-- §d§lSSSBliss §8--==[\n§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§bSeverity: §c%3$s\n§7§o(click to report false positive)"); + put("slur-mute-warn", "§cYou have been insta-punished by the anti-slur! §7§o(Hover for more info)"); + put("slur-mute-notification", "§b§n%1$s§7 has been insta-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)"); + put("spam-notification", "§b§n%1$s§7 might be spamming! §8(§c%2$s§7/§4%3$s§8)"); + put("spam-notification-hover", "§8]==-- §d§lSSSBliss §8--==[\n§bPrevious: §f%1$s\n§bCurrent: §f%2$s\n§bSimilarity §f%3$s"); + put("spam-block-warn", "Do not spam in chat! Please wait before sending another message."); + put("spam-mute-warn", "§cYou have been auto-punished for violating the anti-spam repetitively!"); + put("spam-mute-notification", "§b§n%1$s§7 has been auto-muted by the anti spam! §8(§c%2$s§7/§4%3$s§8)"); + }}; + public LanguageFile() {} + + @Override + public File getFile() { + return PATH; + } + public String get(String key) { + return dictionary.getOrDefault(key,key); + } + public Map getDictionary() { + return dictionary; + } + public String format(String input) { + return input; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/thetrouper/sssbliss/data/config/MainConfig.java b/src/main/java/io/github/thetrouper/sssbliss/data/config/MainConfig.java new file mode 100644 index 0000000..e2506ed --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/data/config/MainConfig.java @@ -0,0 +1,104 @@ +package io.github.thetrouper.sssbliss.data.config; + +import io.github.itzispyder.pdk.utils.misc.JsonSerializable; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class MainConfig implements JsonSerializable { + + @Override + public File getFile() { + File file = new File("plugins/SSSBliss/main-config.json"); + file.getParentFile().mkdirs(); + return file; + } + public Plugin plugin = new Plugin(); + public Chat chat = new Chat(); + + public class Plugin { + public String license = "null"; + public String prefix = "§d§lSSSBliss §8» §7"; + public String webhook = "https://discord.com/api/webhooks/id/token"; + public String lang = "en-us.json"; + public List trustedPlayers = new ArrayList<>() {{ + add("049460f7-21cb-42f5-8059-d42752bf406f"); + }}; + public boolean blockSpecific = true; + public boolean preventNBT = true; + public boolean preventCmdBlockPlace = true; + public boolean preventCmdBlockUse = true; + public boolean preventCmdBlockChange = true; + public boolean preventCmdCartPlace = true; + public boolean preventCmdCartUse = true; + public boolean cmdBlockOpCheck = true; + public List dangerous = new ArrayList<>() {{ + add("op"); + add("deop"); + add("stop"); + add("restart"); + add("execute"); + add("sudo"); + add("esudo"); + add("fill"); + add("setblock"); + add("data"); + add("whitelist"); + }}; + public boolean logDangerous = true; + public boolean logCmdBlocks = true; + public boolean logNBT = true; + public boolean logSpecific = false; + public List logged = new ArrayList<>() {{ + add("give"); + add("item"); + }}; + public boolean deop = true; + public boolean nbtPunish = false; + public boolean cmdBlockPunish = false; + public boolean commandPunish = false; + public boolean specificPunish = false; + public List punishCommands = new ArrayList<>() {{ + add("smite %player%"); + add("ban %player% ]=- SSSBliss -=[ You have been banned for attempting a dangerous action. If you believe this to be a mistake, please contact the server owner."); + }}; + public boolean reopCommand = false; + } + + public class Chat { + public AntiSwear antiSwear = new AntiSwear(); + public AntiSpam antiSpam = new AntiSpam(); + public boolean antiUnicode = true; + + public class AntiSpam { + public boolean antiSpamEnabled = true; + public int defaultGain = 1; + public int lowGain = 2; + public int mediumGain = 4; + public int highGain = 6; + public int heatDecay = 1; + public int blockHeat = 10; + public int punishHeat = 25; + public boolean clearChat = true; + public String chatClearCommand = "cc"; + public String spamPunishCommand = "mute %player% 1m Please refrain from spamming!"; + public boolean logSpam = true; + } + public class AntiSwear { + public boolean antiSwearEnabled = true; + public int lowScore = 0; + public int mediumLowScore = 1; + public int mediumScore = 3; + public int mediumHighScore = 5; + public int highScore = 7; + public int scoreDecay = 3; + public int punishScore = 20; + public boolean strictInstaPunish = true; + public String swearPunishCommand = "mute %player% 15m Do not attempt to bypass the Profanity Filter"; + public String strictPunishCommand = "mute %player% 1h Discriminatory speech is not tolerated on this server!"; + public boolean logSwears = true; + } + + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/events/CMDBlockExecute.java b/src/main/java/io/github/thetrouper/sssbliss/events/CMDBlockExecute.java new file mode 100644 index 0000000..1eaa5f8 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/events/CMDBlockExecute.java @@ -0,0 +1,7 @@ +package io.github.thetrouper.sssbliss.events; + +import io.github.itzispyder.pdk.events.CustomListener; + +public class CMDBlockExecute implements CustomListener { + +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/sound/SoundPlayer.java b/src/main/java/io/github/thetrouper/sssbliss/server/sound/SoundPlayer.java new file mode 100644 index 0000000..5425083 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/sound/SoundPlayer.java @@ -0,0 +1,263 @@ +/** + * This file is for tutorial purposes made by ImproperIssues. Distribute if you want :) + * + * I made this cuz Bukkit API sounds management is trash. + * by ImproperIssues + */ + + +package io.github.thetrouper.sssbliss.server.sound; + +import io.github.thetrouper.sssbliss.SSSBliss; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class SoundPlayer { + + private Location location; + private Sound sound; + private float volume; + private float pitch; + + /** + * Constructs a new sound, this aims to add more methods to + * the Bukkit APIs Sound class, as they don't have many + * methods to use. + * + * @param location Location + * @param sound Sound + * @param volume float + * @param pitch float + */ + public SoundPlayer(Location location, Sound sound, float volume, float pitch) { + this.location = location; + this.sound = sound; + this.pitch = pitch; + this.volume = volume; + } + + + /** + * Plays a sound to a player but at the store location + * + * @param player Player + */ + public void play(Player player) { + player.playSound(this.location,this.sound,this.volume,this.pitch); + } + + /** + * Plays a sound to a player but at the player's location + * + * @param player Player + */ + public void playAt(Player player) { + player.playSound(player.getLocation(),this.sound,this.volume,this.pitch); + } + + /** + * Plays the sound to all players within a distance, but at the stored location. + * + * @param distance double + */ + public void playWithin(double distance) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p != null && p.getWorld() == this.location.getWorld() && p.getLocation().distanceSquared(this.location) < distance) { + p.playSound(this.location,this.sound,this.volume,this.pitch); + } + } + } + + /** + * Plays the sound to all players within a distance, but at the players' location. + * + * @param distance double + */ + public void playWithinAt(double distance) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p != null && p.getWorld() == this.location.getWorld() && p.getLocation().distanceSquared(this.location) < distance) { + p.playSound(p.getLocation(),this.sound,this.volume,this.pitch); + } + } + } + + + /** + * Plays the sound to all players on the server, but at the stored location. + */ + public void playAll() { + for (Player p : Bukkit.getOnlinePlayers()) p.playSound(this.location,this.sound,this.volume,this.pitch); + } + + /** + * Plays the sound to all players on the server, but at the players' location. + */ + public void playAllAt() { + for (Player p : Bukkit.getOnlinePlayers()) p.playSound(p.getLocation(),this.sound,this.volume,this.pitch); + } + + /** + * Repeats a sound to a player, but at the stored location. + * + * @param player Player + * @param times int + * @param tickDelay int + */ + public void repeat(Player player, int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + play(player); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + /** + * Repeats a sound to a player, but at the player's location. + * + * @param player Player + * @param times int + * @param tickDelay int + */ + public void repeatAt(Player player, int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + playAt(player); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + /** + * Repeats a sound to all players on the server, but at the stored location. + * + * @param times int + * @param tickDelay int + */ + public void repeatAll(int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + playAll(); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + /** + * Repeats a sound to all players on the server, but at the players' location. + * + * @param times int + * @param tickDelay int + */ + public void repeatAllAt(int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + playAllAt(); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + /** + * Repeats a sound to all players within a radius, but at the stored location. + * + * @param radius double + * @param times int + * @param tickDelay int + */ + public void repeatAll(double radius,int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + playWithin(radius); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + /** + * Repeats a sound to all players within a radius, but at the players' location. + * + * @param distance double + * @param times int + * @param tickDelay int + */ + public void repeatAllAt(double distance, int times, int tickDelay) { + new BukkitRunnable() { + int i = 0; + @Override + public void run() { + if (i < times) { + playWithinAt(distance); + i ++; + } else { + this.cancel(); + } + } + }.runTaskTimer(SSSBliss.getInstance(),0,tickDelay); + } + + public Sound getSound() { + return sound; + } + + public float getPitch() { + return pitch; + } + + public float getVolume() { + return volume; + } + + public Location getLocation() { + return location; + } + + public void setPitch(float pitch) { + this.pitch = pitch; + } + + public void setVolume(float volume) { + this.volume = volume; + } + + public void setSound(Sound sound) { + this.sound = sound; + } + + public void setLocation(Location location) { + this.location = location; + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/CipherUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/CipherUtils.java new file mode 100644 index 0000000..d3f7d8b --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/CipherUtils.java @@ -0,0 +1,35 @@ +package io.github.thetrouper.sssbliss.server.util; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.util.Base64; + +public class CipherUtils { + private static final String secretKey = "GG8T885O4Yd/86OMVFdL0w=="; // 16, 24, or 32 bytes + private static final String algorithm = "AES"; + public static String encrypt(String strToEncrypt) { + try { + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm); + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); + byte[] encryptedBytes = cipher.doFinal(strToEncrypt.getBytes()); + return Base64.getEncoder().encodeToString(encryptedBytes); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String decrypt(String strToDecrypt) { + try { + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm); + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); + byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)); + return new String(decryptedBytes); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/FileUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/FileUtils.java new file mode 100644 index 0000000..a03a02a --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/FileUtils.java @@ -0,0 +1,95 @@ +package io.github.thetrouper.sssbliss.server.util; + +import io.github.thetrouper.sssbliss.SSSBliss; +import org.bukkit.inventory.ItemStack; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +public class FileUtils { + public static boolean folderExists(String folderName) { + File folder = new File(SSSBliss.getInstance().getDataFolder(), folderName); + return folder.exists() && folder.isDirectory(); + } + public static void createFolder(String folderName) { + File folder = new File(SSSBliss.getInstance().getDataFolder(), folderName); + if (!folder.exists()) { + folder.mkdirs(); + } + } + public static String createNBTLog(String contents) { + ServerUtils.sendDebugMessage("FileUtils: Creating NBT log"); + String fileName = "nbt_log-" + Randomizer.generateID(); + + File dataFolder = SSSBliss.getInstance().getDataFolder(); + + File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); + if (!loggedNBTFolder.exists()) { + loggedNBTFolder.mkdirs(); + } + + File file = new File(loggedNBTFolder, fileName + ".txt"); + try { + if (!file.exists()) { + file.createNewFile(); + } + + BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); + writer.append(contents); + writer.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + return fileName; + } + + public static String createNBTLog(ItemStack i) { + ServerUtils.sendDebugMessage("FileUtils: Creating NBT log"); + + String item = i.getType().name().toLowerCase() + i.getItemMeta().getAsString(); + + String fileName = "nbt_log-" + Randomizer.generateID(); + + File dataFolder = SSSBliss.getInstance().getDataFolder(); + + File loggedNBTFolder = new File(dataFolder,"LoggedNBT"); + if (!loggedNBTFolder.exists()) { + loggedNBTFolder.mkdirs(); + } + + File file = new File(loggedNBTFolder, fileName + ".txt"); + try { + if (!file.exists()) { + file.createNewFile(); + } + + BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); + writer.append(item); + writer.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + return fileName; + } + + public static String createCommandLog(String command) { + String fileName = "command_log-" + Randomizer.generateID(); + File file = new File(SSSBliss.getInstance().getDataFolder() + "/LoggedCommands/" + fileName + ".txt"); + try { + if (!file.exists()) { + file.createNewFile(); + } + + BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); + writer.append(command); + writer.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + return fileName; + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/FileValidationUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/FileValidationUtils.java new file mode 100644 index 0000000..744ca5b --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/FileValidationUtils.java @@ -0,0 +1,20 @@ +package io.github.thetrouper.sssbliss.server.util; + +import java.io.File; + +public final class FileValidationUtils { + + public static boolean validate(File file) { + try { + if (!file.getParentFile().exists()) + if (!file.getParentFile().mkdirs()) + return false; + if (!file.exists()) + return file.createNewFile(); + return true; + } + catch (Exception ex) { + return false; + } + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/GPTUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/GPTUtils.java new file mode 100644 index 0000000..690e366 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/GPTUtils.java @@ -0,0 +1,68 @@ +package io.github.thetrouper.sssbliss.server.util; + +public class GPTUtils { + // I'd be surprised if anyone knew how tf this shi works, I just asked GPT to write it. + public static double calculateSimilarity(String str1, String str2) { + int len1 = str1.length(); + int len2 = str2.length(); + + int[][] dp = new int[len1 + 1][len2 + 1]; + + for (int i = 0; i <= len1; i++) { + dp[i][0] = i; + } + + for (int j = 0; j <= len2; j++) { + dp[0][j] = j; + } + + for (int i = 1; i <= len1; i++) { + for (int j = 1; j <= len2; j++) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])); + } + } + } + + int maxLen = Math.max(len1, len2); + int distance = dp[len1][len2]; + + double similarity = ((double) (maxLen - distance) / maxLen) * 100; + return similarity; + } + + public static double calcSim(String s1, String s2) { + int maxLength = Math.max(s1.length(), s2.length()); + if (maxLength == 0) { + return 100.0; + } + + int distance = calculateLevenshteinDistance(s1, s2); + double similarity = ((double) (maxLength - distance) / maxLength) * 100.0; + + return similarity; + } + + public static int calculateLevenshteinDistance(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + + for (int i = 0; i <= s1.length(); i++) { + dp[i][0] = i; + } + + for (int j = 0; j <= s2.length(); j++) { + dp[0][j] = j; + } + + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + int cost = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? 0 : 1; + dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost); + } + } + + return dp[s1.length()][s2.length()]; + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/MathUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/MathUtils.java new file mode 100644 index 0000000..4bc64e4 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/MathUtils.java @@ -0,0 +1,58 @@ +package io.github.thetrouper.sssbliss.server.util; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public final class MathUtils { + + public static double avg(Integer... ints) { + final List list = Arrays.stream(ints).filter(Objects::nonNull).toList(); + return avg(list); + } + + public static double avg(List ints) { + double sum = 0.0; + for (Integer i : ints) sum += i; + return sum / ints.size(); + } + + public static double round(double value, int nthPlace) { + return Math.floor(value * nthPlace) / nthPlace; + } + + public static String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1)); + } + return result.toString(); + } + + public static String SHA512(String input) { + try { + + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + + + byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + + StringBuilder hexString = new StringBuilder(2 * encodedHash.length); + for (byte b : encodedHash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/Randomizer.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/Randomizer.java new file mode 100644 index 0000000..4833819 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/Randomizer.java @@ -0,0 +1,78 @@ +package io.github.thetrouper.sssbliss.server.util; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * Randomize items from a list + * @param list of? + */ +public class Randomizer { + public static long generateID() { + Date now = new Date(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String formattedDate = dateFormat.format(now); + long id = Long.parseLong(formattedDate); + + return id; + } + + private final List array; + + /** + * From array list + * @param array list + */ + public Randomizer(List array) { + this.array = array; + } + + /** + * From set + * @param array set + */ + public Randomizer(Set array) { + this.array = new ArrayList<>(array); + } + + /** + * From array + * @param array array + */ + public Randomizer(T[] array) { + this.array = List.of(array); + } + + /** + * Pick random from the array + * @return random of list of? + */ + public T pickRand() { + return array.get(rand(array.size() - 1)); + } + + /** + * Generates a random integer from 1 to (max) + * @param max max value + * @return random + */ + public static int rand(int max) { + if (max <= 0) throw new IllegalArgumentException("max cannot be less than 1!"); + return (int) Math.ceil(Math.random() * max); + } + + /** + * Generates a random integer from (min) to (max) + * @param min min value + * @param max max value + * @return random + */ + public static int rand(int min, int max) { + if (max <= 0 || min <= 0) throw new IllegalArgumentException("max or min cannot be less than 1!"); + if (max <= min) throw new IllegalArgumentException("max cannot be less than or equal to min!"); + return min + (int) Math.floor(Math.random() * (max - min + 1)); + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/ServerUtils.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/ServerUtils.java new file mode 100644 index 0000000..3ee8571 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/ServerUtils.java @@ -0,0 +1,105 @@ +package io.github.thetrouper.sssbliss.server.util; + +import io.github.thetrouper.sssbliss.SSSBliss; +import io.github.thetrouper.sssbliss.cmds.SSSBlissCommand; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.metadata.MetadataValue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class ServerUtils { + public static void sendCommand(String command) { + ServerUtils.sendDebugMessage("Getting scheduler"); + Bukkit.getScheduler().scheduleSyncDelayedTask(SSSBliss.getInstance(), () -> { + try { + ServerUtils.sendDebugMessage("Attempting to run command..."); + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command); + } catch (Exception e) { + e.printStackTrace(); + } + },1); + } + public static void sendDebugMessage(String message) { + if (SSSBlissCommand.debugMode) { + String log = "[SSSBliss] [DEBUG]: " + message; + SSSBliss.log.info(log); + for (Player trustedPlayer : Bukkit.getOnlinePlayers()) { + if (SSSBliss.isTrusted(trustedPlayer)) { + trustedPlayer.sendMessage("§d§lSSSBliss §7[§bDEBUG§7] §8» §7" + message); + } + } + } + } + + public static List getPlayers() { + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } + + public static List getStaff() { + return getPlayers().stream().filter(Player -> Player.hasPermission("sssbliss.staff")).toList(); + } + + public static void forEachPlayer(Consumer consumer) { + getPlayers().forEach(consumer); + } + + public static void forEachStaff(Consumer consumer) { + getStaff().forEach(consumer); + } + + public static void dmEachPlayer(Predicate condition, String dm) { + forEachPlayer(p -> { + if (condition.test(p)) p.sendMessage(dm); + }); + } + + public static void dmEachPlayer(String dm) { + forEachPlayer(p -> p.sendMessage(dm)); + } + + public static void forEachSpecified(Iterable players, Consumer consumer) { + players.forEach(consumer); + } + + public static void forEachSpecified(Consumer consumer, Player... players) { + Arrays.stream(players).forEach(consumer); + } + public static void forEachPlayerRun(Predicate condition, Consumer task) { + forEachPlayer(p -> { + if (condition.test(p)) { + task.accept(p); + } + }); + } + public static void sendActionBar(Player p, String msg) { + p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(msg)); + } + + public static boolean hasBlockBelow(Player player, Material material) { + for (int y = player.getLocation().getBlockY() - 1; y >= player.getLocation().getBlockY() - 12; y--) { + if (player.getWorld().getBlockAt(player.getLocation().getBlockX(), y, player.getLocation().getBlockZ()).getType() == material) { + return true; + } + } + return false; + } + + public static boolean isVanished(Player player) { + for (MetadataValue meta : player.getMetadata("vanished")) { + if (meta.asBoolean()) return true; + } + return false; + } + + public static String[] unVanishedPlayers() { + return io.github.itzispyder.pdk.utils.ServerUtils.players(ServerUtils::isVanished).stream().map(Player::getName).toArray(String[]::new); + } +} diff --git a/src/main/java/io/github/thetrouper/sssbliss/server/util/Text.java b/src/main/java/io/github/thetrouper/sssbliss/server/util/Text.java new file mode 100644 index 0000000..4da3dd6 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sssbliss/server/util/Text.java @@ -0,0 +1,49 @@ +package io.github.thetrouper.sssbliss.server.util; + + +import io.github.thetrouper.sssbliss.SSSBliss; + +import java.util.Map; +import java.util.regex.PatternSyntaxException; + +public class Text { + public static final char SECTION_SYMBOL = (char)167; + + public static String color(String msg) { + return msg.replace('&', SECTION_SYMBOL); + } + public static String prefix(String text) { + String prefix = SSSBliss.mainConfig.plugin.prefix; + return color(prefix + text); + } + public static String removeFirstColor(String input) { + if (input.startsWith("\u00a7")) { + if (input.length() > 2) { + return input.substring(2); + } else { + return ""; + } + } else { + return input; + } + } + public static String replaceRepeatingLetters(String message) { + StringBuilder result = new StringBuilder(); + char prevChar = '\0'; + int count = 0; + + for (char c : message.toCharArray()) { + if (c == prevChar) { + count++; + if (count <= 3) { + result.append(c); + } + } else { + prevChar = c; + count = 1; + result.append(c); + } + } + return result.toString(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..0ce2241 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,111 @@ +# SSSBliss 0.2.0 +# ____ __ ___ +#/\ _`\ /\ \__ __ /\_ \ +#\ \,\L\_\ __ ___\ \ ,_\/\_\ ___ __\//\ \ +# \/_\__ \ /'__`\/' _ `\ \ \/\/\ \ /' _ `\ /'__`\\ \ \ +# /\ \L\ \/\ __//\ \/\ \ \ \_\ \ \/\ \/\ \/\ __/ \_\ \_ +# \ `\____\ \____\ \_\ \_\ \__\\ \_\ \_\ \_\ \____\/\____\ +# \/_____/\/____/\/_/\/_/\/__/ \/_/\/_/\/_/\/____/\/____/ +# ]======------ Configuration & Setup Guide ------=====[ +# SSSBliss is inspired by WickBot.com +# Be sure to check out their amazing discord bot! +# If you need help, join the discord! +# https://discord.gg/sssblissmc v Use below if it expired +# https://sssblissauth.000webhostapp.com/discord.html +# +config: + plugin: + key: "beta" # Put your license key here. If you do not have one, join the discord to verify your purchase + lang: "en-us.json" # Languages + # -------------------------------- + # Anti-Nuke Setup (Do this first) + # -------------------------------- + prefix: "§d§lSSSBliss §8» §7" # Prefix of the plugin. Line below is the discord webhook for logs to be sent to + webhook: "https://discord.com/api/webhooks/id/token" + trusted: # List the UUIDs of players who are trusted, will bypass the plugin and be immune to logs and are able to re-op themeselves, as well as whitelist command blocks + - "049460f7-21cb-42f5-8059-d42752bf406f" # obvWolf + block-specific: true # Defaulted true | Weather or not to block ALL plugin specific commands from non-trusted members (EX: minecraft:execute) these will not be logged. + prevent-nbt: true # Defaulted true | Should NBT items be blocked from the creative hotbar + prevent-cmdblock-place: true # Defaulted true | Should Placing a command block get blocked + prevent-cmdblock-use: true # Defaulted true | Should using a command block get blocked + prevent-cmdblock-change: true # Defaulted true | Should changing a command block get blocked + prevent-cmdcart-place: true # Defaulted true | Should placing a command cart get blocked + prevent-cmdcart-use: true # Defaulted true | Should using a command cart get blocked + cmdblock-op-check: true # Defaulted true | Will check if a player is op'd before preforming actions against command blocks (To prevent spam from non oped users attempting command blocks, which they cant by default) + dangerous: # These commands can only be run by "trusted" users + - "op" + - "deop" + - "stop" + - "restart" + - "execute" # Could run commands as a trusted player + - "sudo" # same as above + - "esudo" # WATCH OUT FOR ESSENTIALS ALIASES !!! + - "fill" # Most client side nukers use it + - "setblock" # could setblock a command block with anything + - "data" # Could modify a command block to whatever they wanted + - "whitelist" # Could add other players to the whitelist + log-dangerous: true # Default true | Weather or not to log to discord when a dangerous command is executed + log-cmdblocks: true # Defaulted true | Log attempts of command-block place-ery in discord + log-nbt: true # Defaulted true | Should items and their NBT's be logged to discord + log-specific: false # Default false | Weather or not to log to discord when a plugin specific command is executed + logged: # Commands that will always be logged to discord when executed. + - "give" + - "item" + deop: true # Defaulted true | This will remove an untrusted player's operator permissions whenever they attempt dangerous actions + nbt-punish: false # Defaulted false | This will punish a player when they attempt to use an NBT item + cmdblock-punish: false # Defaulted false | This will punish a player when they attempt to use a command block + command-punish: false # Defaulted false | This will ban punish player when they attempt to use a dangerous command + specific-punish: false # Defaulted false | This will punish a player when they run a specific command (Not recomended) + punish-commands: # Commands to run when a dangerous action is to be punished. Use %player% for the punished player's name + - "smite %player%" + - "ban %player% ]=- SSSBliss -=[ You have been banned for attempting a dangerous action. If you believe this to be a mistake, please contact the server owner." + reop-command: false # Defaulted false | This enables the command allowing trusted players to op themselves if they get deoped. + # ------------------------------- + # Chat Filter Setup & AntiSpam + # ------------------------------- + chat: + anti-unicode: true # Default true | Prevents all non A-Z 0-9 + specials from being sent in chat + anti-spam: + # AntiSpam Heat system + enabled: true # Default true | Enables/disables the entire anti-spam + default-gain: 1 # Default 1 | Heat gained as base for every message + low-gain: 2 # Default 2 | Heat gained when message is >25% similar + medium-gain: 4 # Default 4 | Heat gained when message is >50% similar + high-gain: 6 # Default 6 | Heat gained for >90% similarity + heat-decay: 1 # Default 1 | Heat lost every second + block-heat: 10 # Default 10 | The heat required to block the message + punish-heat: 25 # Default 25 | The heat required to punish the player + clear-chat: true # Default true | will run the clear chat command before a spam punishment + chat-clear-command: "cc" # Default cc | Command for if "clear-chat" is enabled + punish-command: "mute %player% 1m Please refrain from spamming!" # (Use %player% for the player's name placeholder) + log-spam: true # Default true | logs spam punishments to the webhook + anti-swear: + enabled: true # Default true | Enables/disables the entire anti-swear + low-score: 0 # Default 0 | How much score should you gain for not attempting a bypass + medium-low-score: 1 # Default 1 | How much score should you gain for "bad "bypass attempt + medium-score: 3 # Default 3 | How much score should you gain for "ok" bypass attempt + medium-high-score: 5 # Default 5 | How much score should you gain for "good" bypass attempt + high-score: 7 # Default 7 | How much score should be gained for "extreme" attempt to bypass + score-decay: 3 # Default 3 | Rate at which score is lost every minute + punish-score: 20 # Default 20 | how much score is required to get punished + strict-insta-punish: true # Default true | Should players get insta punished for any words on the "strict" list? + punish-command: "mute %player% 15m Do not attempt to bypass the Profanity Filter" + strict-command: "mute %player% 1h Discriminatory speech is not tolerated on this server!" + log-swear: true # Default true | Logs swear punishments to the webhook + leet-patterns: # Replacement patterns for "l33t" strings + '0': o + '1': i + '3': e + '4': a + '5': s + '6': g + '7': l + $: s + '!': i + '|': i + +: t + '#': h + '@': a + <: c + V: u + v: u \ No newline at end of file diff --git a/src/main/resources/false-positives.yml b/src/main/resources/false-positives.yml new file mode 100644 index 0000000..dae5905 --- /dev/null +++ b/src/main/resources/false-positives.yml @@ -0,0 +1,55 @@ +false-positives: # Words that will falsly flag the anti-swear + - but then + - was scamming + - an alt + - can also + - analysis + - analytics + - arsenal + - assassin + - as saying + - assert + - assign + - assimil + - assist + - associat + - assum + - assur + - basement + - bass + - cass + - butter + - canvass + - cocktail + - cumber + - document + - evaluate + - exclusive + - expensive + - explain + - expression + - grape + - grass + - harass + - hotwater + - identit + - kassa + - kassi + - lass + - leafage + - libshitz + - magnacumlaude + - mass + - mocha + - pass + - phoebe + - phoenix + - push it + - sassy + - saturday + - scrap + - serfage + - sexist + - shoe + - stitch + - therapist \ No newline at end of file diff --git a/src/main/resources/lang/en-us.json b/src/main/resources/lang/en-us.json new file mode 100644 index 0000000..f4df638 --- /dev/null +++ b/src/main/resources/lang/en-us.json @@ -0,0 +1,38 @@ +{ + "dictionary" : { + "if-you-see-this-lang-is-broken" : "SSSBliss language is working!", + "no-permission" : "§cInsufficient Permissions!", + "cooldown" : "This action is on cooldown!", + "false-positive-report-success" : "Successfully reported a false positive!", + "no-online-player" : "§cYou must provide an online player to send a message to!", + "no-message-provided" : "§cYou must provide a message to send!", + "elevating-perms" : "Elevating your permissions...", + "log-elevating-perms" : "Elevating the permissions of %s", + "already-op" : "You are already a server operator!", + "log-already-op" : "The permissions of %s are already elevated! Retrying...", + "no-trust" : "You are not a trusted user!", + "no-user-reply" : "§cYou have nobody to reply to!", + "spy-enabled" : "SocialSpy is now enabled.", + "spy-disabled" : "SocialSpy is now disabled.", + "action-automatic" : "§7This action was preformed automatically\n§7by the §bSSSBliss Anti-Spam§7 algorithm.", + "action-automatic-reportable" : "§7This action was preformed automatically \n§7by the §bSSSBliss Profanity Filter§7 algorithm!\n§8§o(Click to report false positive)", + "unicode-warn" : "§cDo not send non standard unicode in chat!", + "message-sent" : "§d§lMessage §8» §b[§fYou §e>§f %1$s§b] §7%2$s", + "message-received" : "§d§lMessage §8» §b[§f%1$s §e>§f You§b] §7%2$s", + "spy-message" : "§d§lSpy §8» §b§n%1$s§7 has messaged §b§n%2$s§7.", + "spy-message-hover" : "§8]==-- §d§lSocialSpy §8--==[\n§bSender: §f%1$S\n§bReceiver: §f%2$S\n§bMessage: §f%3$S", + "profanity-mute-warn" : "You have been auto muted for repeated violation of the profanity filter! §7§o(Hover for more info)", + "profanity-mute-notification" : "§b§n%1$s§7 has been auto-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)", + "slur-mute-warn" : "§cYou have been insta-punished by the anti-slur! §7§o(Hover for more info)", + "slur-mute-notification" : "§b§n%1$s§7 has been insta-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)", + "swear-block-warn" : "§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute! §7§o(Hover for more info)", + "swear-block-notification" : "§b§n%1$s§7 has triggered the anti-swear! §8(§c%2$s§7/§4%3$s§8)", + "spam-notification" : "§b§n%1$s§7 might be spamming! §8(§c%2$s§7/§4%3$s§8)", + "spam-notification-hover" : "§8]==-- §d§lSSSBliss §8--==[\n§bPrevious: §f%1$s\n§bCurrent: §f%2$s\n§bSimilarity §f%3$s", + "spam-block-warn" : "Do not spam in chat! Please wait before sending another message.", + "spam-mute-warn" : "§cYou have been auto-punished for violating the anti-spam repetitively!", + "spam-mute-notification" : "§b§n%1$s§7 has been auto-muted by the anti spam! §8(§c%2$s§7/§4%3$s§8)", + "filter-notification-hover" : "§8]==-- §d§lSSSBliss §8--==[\n§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§8§o(Click to report false positive)", + "severity-notification-hover" : "§8]==-- §d§lSSSBliss §8--==[\n§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§bSeverity: §c%3$s\n§7§o(click to report false positive)" + } +} \ No newline at end of file diff --git a/src/main/resources/nbt-config.yml b/src/main/resources/nbt-config.yml new file mode 100644 index 0000000..8d0b36a --- /dev/null +++ b/src/main/resources/nbt-config.yml @@ -0,0 +1,57 @@ +nbt: + allow-name: true # Defaulted to true, weather or not to allow all item names durring creative inv event + allow-lore: true # Defaulted to true, weather or not to allow all item lore during creative inv event + allow-attributes: false # defaulted to false, weather or not to allow item attributes in a creative inv event + + # Enchants + global-max-enchant: 5 # Defaulted to 5, if any enchantment is above this, it will get deleted. Set to 0 to disable all enchants from creative inv + # It is recommended to keep the ones defaulted to 1 at 1, as this will keep people who spam 32k onto every enchant will get caught + + # All items + max-mending: 1 # Defaulted to 1 + max-unbreaking: 3 # Defaulted to 3 + max-vanishing: 1 # Defaulted to 1 + + # ARMOR + max-aqua-affinity: 1 # Defaulted to 1 + max-blast-protection: 4 # Defaulted to 4 + max-curse-of-binding: 1 # Defaulted to 1 + max-depth-strider: 3 # Defaulted to 3 + max-feather-falling: 4 # Defaulted to 4 + max-fire-protection: 4 # Defaulted to 4 + max-frost-walker: 2 # Defaulted to 2 + max-projectile-protection: 4 # Defaulted to 4 + max-protection: 4 # Defaulted to 4 + max-respiration: 3 # Defaulted to 3 + max-soul-speed: 3 # Defaulted to 3 + max-thorns: 3 # Defaulted to 3 + max-swift-sneak: 3 # Defaulted to 3 + + # MELEE WEAPONS + max-bane-of-arthropods: 5 # Defaulted to 5 + max-efficiency: 5 # Defaulted to 5 + max-fire-aspect: 2 # Defaulted to 2 + max-looting: 3 # Defaulted to 3 + max-impaling: 5 # Defaulted to 5 + max-knockback: 2 # Defaulted to 2 + max-sharpness: 5 # Defaulted to 5 + max-smite: 5 # Defaulted to 5 + max-sweeping-edge: 3 # Defaulted to 3 + + # RANGED WEAPONS + max-channeling: 1 # Defaulted to 1 + max-flame: 1 # Defaulted to 1 + max-infinity: 1 # Defaulted to 1 + max-loyalty: 3 # Defaulted to 3 + max-riptide: 3 # Defaulted to 3 + max-multishot: 1 # Defaulted to 1 + max-piercing: 4 # Defaulted to 4 + max-power: 5 # Defaulted to 5 + max-punch: 2 # Defaulted to 2 + max-quick-charge: 3 # Defaulted to 3 + + # TOOLS + max-fortune: 3 # Defaulted to 3 + max-luck-of-the-sea: 3 # Defaulted to 3 + max-lure: 3 # Defaulted to 3 + max-silk-touch: 1 # Defaulted to 1 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..2b7a05b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,96 @@ +name: SSSBliss +version: '${version}' +main: io.github.thetrouper.sssbliss.SSSBliss +api-version: 1.19 +authors: [ TheTrouper ] +description: Detect Block and Ban players who attempt to grief your server. +website: https://thetrouper.github.io/ +permissions: + sssbliss.message: + description: Access to the direct messages + default: op + sssbliss.reply: + description: Reply commands + sssbliss.debug: + description: Permission to use debug commands + default: op + sssbliss.staff: + description: Receive anti-swear and anti-spam warnings + default: op + sssbliss.chat.antiswear.flags: + description: See antiSwear flags + default: op + sssbliss.chat.antiswear.bypass: + description: Bypass the antiSwear + default: op + sssbliss.chat.antispam.flags: + description: See antispam flags + default: op + sssbliss.chat.antispam.bypass: + description: Bypass the antispam + default: op + sssbliss.chat.*: + description: bypass all chat rules and see all flags + default: op + children: + sssbliss.chat.antiswear.flags: true + sssbliss.chat.antiswear.bypass: true + sssbliss.chat.antispam.flags: true + sssbliss.chat.antispam.bypass: true +commands: + sssbliss: + description: A command for testing. + usage: /sssbliss + permission: sssbliss.info + permission-message: You do not have permission! + reop: + description: Allows trusted players to elevate their permissions + usage: /reop + socialspy: + permission: sssbliss.spy + usage: /socialspy + permission-message: You do not have permission to use this command! + description: View direct messages sent between players + aliases: + - spy + - sspy + msg: + permission: sssbliss.message + usage: /msg [] + permission-message: You do not have permission to message through sssbliss! + description: Send messages directly to players + aliases: + - message + - etell + - tell + - t + - ewhisper + - whisper + - w + - privatemessage + - pm + - m + - directmessage + - dm + - sssblissmessage + - sm + - stell + - smsg + reply: + description: Reply to the last person messaging you + usage: /r [] + permission: sssbliss.reply + permission-message: You do not have permission to reply through sssbliss! + aliases: + - r + - er + - rply + - ereply + - sr + - sreply + - sssblissreply + sssblisscallback: + description: Callback for chat click events + usage: /sssblisscallback + permission: sssbliss.callbacks + permission-message: You have not been given permission to use SSSBliss Chat Callbacks! \ No newline at end of file diff --git a/src/main/resources/strict.yml b/src/main/resources/strict.yml new file mode 100644 index 0000000..998d25e --- /dev/null +++ b/src/main/resources/strict.yml @@ -0,0 +1,9 @@ +strict: # Very bad words to insta-punish for + - nigg + - niger + - nlgg + - nlger + - njgg + - tranny + - fag + - beaner \ No newline at end of file diff --git a/src/main/resources/swears.yml b/src/main/resources/swears.yml new file mode 100644 index 0000000..01f7927 --- /dev/null +++ b/src/main/resources/swears.yml @@ -0,0 +1,79 @@ +blacklisted: # Swears to check for + - anal + - anus + - arse + - ass + - ballsack + - balls + - bastard + - bitch + - btch + - biatch + - blowjob + - bollock + - bollok + - boner + - boob + - bugger + - butt + - choad + - clitoris + - cock + - coon + - crap + - cum + - cunt + - dick + - dildo + - douchebag + - dyke + - feck + - fellate + - fellatio + - felching + - fuck + - fudgepacker + - flange + - gtfo + - hoe + - horny + - incest + - jerk + - jizz + - labia + - masturb + - muff + - nazi + - nipple + - nips + - nude + - pedophile + - penis + - piss + - poop + - porn + - prick + - prostit + - pube + - pussie + - pussy + - queer + - rape + - rapist + - retard + - rimjob + - scrotum + - sex + - shit + - slut + - spunk + - stfu + - suckmy + - tits + - tittie + - titty + - turd + - twat + - vagina + - wank + - whore \ No newline at end of file