From 7d9cba95328212163f713daff38b8255347882d2 Mon Sep 17 00:00:00 2001 From: William Date: Tue, 19 Mar 2024 18:17:16 +0000 Subject: [PATCH] v3: New config structure, built-in Velocity packet handling (#196) * build: bump to 2.8 * build: require Java 17, 1.17.1+ on Spigot * build: publish platform-specific APIs * build: velocity build pipeline refactor * refactor: move stuff around, Player -> OnlineUser * build: minimize jars * build: update dependabot config * build: fix version targets in CI flows * refactor: migrate config files to Exll's configlib * refactor: more signed chat R&D * feat: add packet-level chat handling support * deps: bump ConfigLib to 4.5.0 * feat: add Regex filter, close #188 * deps: bump Spicord * fix: license headers * deps: bump ProfanityCheckerAPI to v3.0 --- .github/dependabot.yml | 22 +- .github/workflows/ci.yml | 14 +- .github/workflows/pr_tests.yml | 4 +- .github/workflows/release.yml | 14 +- .github/workflows/update_docs.yml | 15 +- README.md | 2 +- build.gradle | 145 ++- bukkit/build.gradle | 10 +- .../huskchat/{bukkit => }/BukkitHuskChat.java | 192 ++-- .../{bukkit => api}/BukkitHuskChatAPI.java | 19 +- .../bukkit/event/BukkitEventDispatcher.java | 79 -- .../{bukkit => }/command/BukkitCommand.java | 38 +- .../BukkitBroadcastMessageEvent.java} | 13 +- .../BukkitChatMessageEvent.java} | 17 +- .../{bukkit => }/event/BukkitEvent.java | 8 +- .../huskchat/event/BukkitEventProvider.java | 71 ++ .../BukkitPrivateMessageEvent.java} | 19 +- .../{bukkit => }/listener/BukkitListener.java | 39 +- .../BukkitPlaceholderAPIReplacer.java} | 13 +- .../BukkitUser.java} | 44 +- bukkit/src/main/resources/plugin.yml | 4 +- bungee/build.gradle | 8 +- .../{bungeecord => }/BungeeHuskChat.java | 218 ++--- .../BungeeHuskChatAPI.java | 19 +- .../event/BungeeEventDispatcher.java | 61 -- .../bungeecord/player/BungeePlayer.java | 109 --- .../command/BungeeCommand.java | 40 +- .../BungeeBroadcastMessageEvent.java} | 15 +- .../BungeeChatMessageEvent.java} | 15 +- .../{bungeecord => }/event/BungeeEvent.java | 13 +- .../huskchat/event/BungeeEventProvider.java | 55 ++ .../BungeePrivateMessageEvent.java} | 21 +- .../getter/BungeePermsDataGetter.java | 21 +- .../listener/BungeeListener.java | 30 +- .../william278/huskchat/user/BungeeUser.java | 75 ++ bungee/src/main/resources/bungee.yml | 2 +- common/build.gradle | 26 +- .../net/william278/huskchat/HuskChat.java | 83 +- .../net/william278/huskchat/HuskChatAPI.java | 77 -- .../william278/huskchat/api/HuskChatAPI.java | 112 +++ .../william278/huskchat/channel/Channel.java | 208 ++--- .../huskchat/command/BroadcastCommand.java | 8 +- .../huskchat/command/ChannelCommand.java | 37 +- .../huskchat/command/CommandBase.java | 10 +- .../huskchat/command/HuskChatCommand.java | 6 +- .../huskchat/command/LocalSpyCommand.java | 26 +- .../huskchat/command/MessageCommand.java | 16 +- .../command/OptOutMessageCommand.java | 14 +- .../huskchat/command/ReplyCommand.java | 18 +- .../huskchat/command/ShortcutCommand.java | 25 +- .../huskchat/command/SocialSpyCommand.java | 26 +- .../william278/huskchat/config/Channels.java | 121 +++ .../huskchat/config/ConfigProvider.java | 209 +++++ .../william278/huskchat/config/Filters.java | 72 ++ .../william278/huskchat/config/Locales.java | 109 ++- .../william278/huskchat/config/Settings.java | 842 +++++------------- .../huskchat/discord/DiscordHook.java | 27 +- .../huskchat/discord/SpicordHook.java | 52 +- .../william278/huskchat/discord/WebHook.java | 4 +- ...eEvent.java => BroadcastMessageEvent.java} | 8 +- ...essageEvent.java => ChatMessageEvent.java} | 8 +- ...ventDispatcher.java => EventProvider.java} | 10 +- ...ageEvent.java => PrivateMessageEvent.java} | 12 +- .../huskchat/filter/AdvertisingFilterer.java | 18 +- .../huskchat/filter/AsciiFilter.java | 17 +- .../huskchat/filter/CapsFilter.java | 33 +- .../huskchat/filter/ChatFilter.java | 74 +- .../{replacer => filter}/EmojiReplacer.java | 58 +- .../huskchat/filter/FilterProvider.java | 90 ++ .../huskchat/filter/ProfanityFilterer.java | 59 +- .../huskchat/filter/RegexFilter.java | 76 ++ .../huskchat/filter/RepeatFilter.java | 35 +- .../huskchat/filter/SpamFilter.java | 40 +- .../huskchat/getter/DataGetter.java | 16 +- .../huskchat/getter/DefaultDataGetter.java | 16 +- .../huskchat/getter/LuckPermsDataGetter.java | 16 +- .../huskchat/listener/PlayerListener.java | 62 +- .../huskchat/message/BroadcastMessage.java | 64 +- .../huskchat/message/ChatMessage.java | 209 ++--- .../huskchat/message/PrivateMessage.java | 82 +- .../placeholders/DefaultReplacer.java | 10 +- .../placeholders/PAPIProxyBridgeReplacer.java | 8 +- .../placeholders/PlaceholderReplacer.java | 4 +- .../huskchat/player/PlayerCache.java | 291 ------ .../huskchat/replacer/ReplacerFilter.java | 39 - .../ConsoleUser.java} | 33 +- .../Player.java => user/OnlineUser.java} | 65 +- .../net/william278/huskchat/user/User.java | 58 ++ .../william278/huskchat/user/UserCache.java | 214 +++++ .../huskchat/util/AudiencesProvider.java | 66 ++ common/src/main/resources/config.yml | 237 ----- common/src/main/resources/locales/bg-bg.yml | 91 +- common/src/main/resources/locales/de-de.yml | 91 +- common/src/main/resources/locales/en-gb.yml | 91 +- common/src/main/resources/locales/es-mx.yml | 91 +- common/src/main/resources/locales/fr-fr.yml | 82 +- common/src/main/resources/locales/hu-hu.yml | 91 +- common/src/main/resources/locales/it-it.yml | 91 +- common/src/main/resources/locales/ru-ru.yml | 91 +- common/src/main/resources/locales/zh-cn.yml | 91 +- .../filter/AdvertisingFilterTests.java | 16 +- .../huskchat/filter/AsciiFilterTests.java | 10 +- .../huskchat/filter/CapsFilterTests.java | 26 +- .../huskchat/filter/ProfanityFilterTests.java | 19 +- .../TestOnlineUser.java} | 27 +- docs/Setup.md | 4 +- gradle.properties | 9 +- velocity/build.gradle | 28 +- .../{velocity => }/VelocityHuskChat.java | 244 ++--- .../VelocityHuskChatAPI.java | 18 +- .../command/VelocityCommand.java | 41 +- .../VelocityBroadcastMessageEvent.java} | 15 +- .../VelocityChatMessageEvent.java} | 15 +- .../{velocity => }/event/VelocityEvent.java | 17 +- .../huskchat/event/VelocityEventProvider.java | 54 ++ .../VelocityPrivateMessageEvent.java} | 21 +- .../listener/VelocityChatListener.java | 50 ++ .../listener/VelocityEventChatListener.java | 46 + .../listener/VelocityPacketChatListener.java | 156 ++++ .../VelocityPlayerListener.java} | 35 +- .../VelocityUser.java} | 53 +- .../event/VelocityEventDispatcher.java | 54 -- .../src/main/resources/velocity-metadata.yml | 2 + .../src/main/resources/velocity-plugin.json | 2 +- 124 files changed, 3674 insertions(+), 3738 deletions(-) rename bukkit/src/main/java/net/william278/huskchat/{bukkit => }/BukkitHuskChat.java (55%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit => api}/BukkitHuskChatAPI.java (78%) delete mode 100644 bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEventDispatcher.java rename bukkit/src/main/java/net/william278/huskchat/{bukkit => }/command/BukkitCommand.java (70%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit/event/BroadcastMessageEvent.java => event/BukkitBroadcastMessageEvent.java} (78%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit/event/ChatMessageEvent.java => event/BukkitChatMessageEvent.java} (78%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit => }/event/BukkitEvent.java (85%) create mode 100644 bukkit/src/main/java/net/william278/huskchat/event/BukkitEventProvider.java rename bukkit/src/main/java/net/william278/huskchat/{bukkit/event/PrivateMessageEvent.java => event/BukkitPrivateMessageEvent.java} (74%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit => }/listener/BukkitListener.java (59%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit/placeholders/PlaceholderAPIReplacer.java => placeholders/BukkitPlaceholderAPIReplacer.java} (73%) rename bukkit/src/main/java/net/william278/huskchat/{bukkit/player/BukkitPlayer.java => user/BukkitUser.java} (55%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord => }/BungeeHuskChat.java (55%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord => api}/BungeeHuskChatAPI.java (78%) delete mode 100644 bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEventDispatcher.java delete mode 100644 bungee/src/main/java/net/william278/huskchat/bungeecord/player/BungeePlayer.java rename bungee/src/main/java/net/william278/huskchat/{bungeecord => }/command/BungeeCommand.java (69%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord/event/BroadcastMessageEvent.java => event/BungeeBroadcastMessageEvent.java} (73%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord/event/ChatMessageEvent.java => event/BungeeChatMessageEvent.java} (77%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord => }/event/BungeeEvent.java (81%) create mode 100644 bungee/src/main/java/net/william278/huskchat/event/BungeeEventProvider.java rename bungee/src/main/java/net/william278/huskchat/{bungeecord/event/PrivateMessageEvent.java => event/BungeePrivateMessageEvent.java} (69%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord => }/getter/BungeePermsDataGetter.java (80%) rename bungee/src/main/java/net/william278/huskchat/{bungeecord => }/listener/BungeeListener.java (67%) create mode 100644 bungee/src/main/java/net/william278/huskchat/user/BungeeUser.java delete mode 100644 common/src/main/java/net/william278/huskchat/HuskChatAPI.java create mode 100644 common/src/main/java/net/william278/huskchat/api/HuskChatAPI.java create mode 100644 common/src/main/java/net/william278/huskchat/config/Channels.java create mode 100644 common/src/main/java/net/william278/huskchat/config/ConfigProvider.java create mode 100644 common/src/main/java/net/william278/huskchat/config/Filters.java rename common/src/main/java/net/william278/huskchat/event/{IBroadcastMessageEvent.java => BroadcastMessageEvent.java} (83%) rename common/src/main/java/net/william278/huskchat/event/{IChatMessageEvent.java => ChatMessageEvent.java} (85%) rename common/src/main/java/net/william278/huskchat/event/{EventDispatcher.java => EventProvider.java} (63%) rename common/src/main/java/net/william278/huskchat/event/{IPrivateMessageEvent.java => PrivateMessageEvent.java} (77%) rename common/src/main/java/net/william278/huskchat/{replacer => filter}/EmojiReplacer.java (53%) create mode 100644 common/src/main/java/net/william278/huskchat/filter/FilterProvider.java create mode 100644 common/src/main/java/net/william278/huskchat/filter/RegexFilter.java delete mode 100644 common/src/main/java/net/william278/huskchat/player/PlayerCache.java delete mode 100644 common/src/main/java/net/william278/huskchat/replacer/ReplacerFilter.java rename common/src/main/java/net/william278/huskchat/{player/ConsolePlayer.java => user/ConsoleUser.java} (74%) rename common/src/main/java/net/william278/huskchat/{player/Player.java => user/OnlineUser.java} (61%) create mode 100644 common/src/main/java/net/william278/huskchat/user/User.java create mode 100644 common/src/main/java/net/william278/huskchat/user/UserCache.java create mode 100644 common/src/main/java/net/william278/huskchat/util/AudiencesProvider.java delete mode 100644 common/src/main/resources/config.yml rename common/src/test/java/net/william278/huskchat/{player/TestPlayer.java => user/TestOnlineUser.java} (74%) rename velocity/src/main/java/net/william278/huskchat/{velocity => }/VelocityHuskChat.java (51%) rename velocity/src/main/java/net/william278/huskchat/{velocity => api}/VelocityHuskChatAPI.java (78%) rename velocity/src/main/java/net/william278/huskchat/{velocity => }/command/VelocityCommand.java (69%) rename velocity/src/main/java/net/william278/huskchat/{velocity/event/BroadcastMessageEvent.java => event/VelocityBroadcastMessageEvent.java} (73%) rename velocity/src/main/java/net/william278/huskchat/{velocity/event/ChatMessageEvent.java => event/VelocityChatMessageEvent.java} (77%) rename velocity/src/main/java/net/william278/huskchat/{velocity => }/event/VelocityEvent.java (74%) create mode 100644 velocity/src/main/java/net/william278/huskchat/event/VelocityEventProvider.java rename velocity/src/main/java/net/william278/huskchat/{velocity/event/PrivateMessageEvent.java => event/VelocityPrivateMessageEvent.java} (68%) create mode 100644 velocity/src/main/java/net/william278/huskchat/listener/VelocityChatListener.java create mode 100644 velocity/src/main/java/net/william278/huskchat/listener/VelocityEventChatListener.java create mode 100644 velocity/src/main/java/net/william278/huskchat/listener/VelocityPacketChatListener.java rename velocity/src/main/java/net/william278/huskchat/{velocity/listener/VelocityListener.java => listener/VelocityPlayerListener.java} (54%) rename velocity/src/main/java/net/william278/huskchat/{velocity/player/VelocityPlayer.java => user/VelocityUser.java} (50%) delete mode 100644 velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEventDispatcher.java create mode 100644 velocity/src/main/resources/velocity-metadata.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a0a14d38..f1f2c87f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,7 +2,23 @@ version: 2 updates: - - package-ecosystem: "gradle" # See documentation for possible values - directory: "/" # Location of package manifests + # CI workflow action updates + - package-ecosystem: "github-actions" + directory: "/" schedule: - interval: "daily" \ No newline at end of file + interval: "weekly" + commit-message: + prefix: "ci" + + # Gradle package updates + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "deps" + ignore: + - dependency-name: 'org.spigotmc:spigot-api' + - dependency-name: 'org.papermc:paper-api' + - dependency-name: 'net.md-5:bungeecord-api' + - dependency-name: 'net.dv8tion:JDA' \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ab9e0db..344b3c66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '16' + java-version: '17' distribution: 'temurin' - name: Install Python uses: actions/setup-python@v4 @@ -70,12 +70,6 @@ jobs: hangar-version-type: Alpha modrinth-version-type: alpha game-versions: | - 1.16 - 1.16.1 - 1.16.2 - 1.16.3 - 1.16.4 - 1.16.5 1.17 1.17.1 1.18 @@ -89,6 +83,8 @@ jobs: 1.20 1.20.1 1.20.2 + 1.20.3 + 1.20.4 modrinth-dependencies: | unsignedvelocity | suggests | * signedvelocity | suggests | * @@ -100,7 +96,7 @@ jobs: spigot paper folia - java: 16 + java: 17 - name: Upload GitHub Artifact if: success() || failure() uses: actions/upload-artifact@v2 diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml index e64bcda7..106cd1dc 100644 --- a/.github/workflows/pr_tests.yml +++ b/.github/workflows/pr_tests.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '16' + java-version: '17' distribution: 'temurin' - name: Install Python uses: actions/setup-python@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e6d2aa7..6e24e2b8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,10 +17,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '16' + java-version: '17' distribution: 'temurin' - name: Install Python uses: actions/setup-python@v4 @@ -58,12 +58,6 @@ jobs: version-type: release changelog: ${{ github.event.release.body }} game-versions: | - 1.16 - 1.16.1 - 1.16.2 - 1.16.3 - 1.16.4 - 1.16.5 1.17 1.17.1 1.18 @@ -77,6 +71,8 @@ jobs: 1.20 1.20.1 1.20.2 + 1.20.3 + 1.20.4 loaders: | bungeecord velocity @@ -88,7 +84,7 @@ jobs: signedvelocity | suggests | * papiproxybridge | suggests | * placeholderapi | suggests | * - java: 16 + java: 17 - name: Upload GitHub Artifact uses: actions/upload-artifact@v2 if: success() || failure() diff --git a/.github/workflows/update_docs.yml b/.github/workflows/update_docs.yml index fd188eb0..f4f90ef1 100644 --- a/.github/workflows/update_docs.yml +++ b/.github/workflows/update_docs.yml @@ -17,12 +17,9 @@ jobs: deploy-wiki: runs-on: ubuntu-latest steps: - - name: 'Checkout Code' - uses: actions/checkout@v3 - - name: 'Push Changes to Wiki' - uses: Andrew-Chen-Wang/github-wiki-action@v3 - env: - WIKI_DIR: 'docs/' - GH_TOKEN: ${{ github.token }} - GH_MAIL: 'actions@github.com' - GH_NAME: 'github-actions[bot]' \ No newline at end of file + - name: 'Checkout for CI 🛎️' + uses: actions/checkout@v4 + - name: 'Push Docs to Github Wiki 📄️' + uses: Andrew-Chen-Wang/github-wiki-action@v4 + with: + path: 'docs' \ No newline at end of file diff --git a/README.md b/README.md index fe7025a9..3688beb3 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ It's designed to be easy to configure with an elegant out-of-box setup, while al **⭐ Filters & replacers** — Customisable spam limiting filter, anti-advertising & special emoji -**⭐ Modern formatting** — Utilise modern 1.16+ formatting, with RGB and Gradient support via [MineDown](https://github.com/Phoenix616/MineDown) +**⭐ Modern formatting** — Utilise modern formatting, with RGB and Gradient support via [MineDown](https://github.com/Phoenix616/MineDown) ## Building To build HuskChat, you'll need python (>=`v3.6`) with associated packages installed; `jep` and `alt-profanity-check`. diff --git a/build.gradle b/build.gradle index 9a643bc5..a6d7d990 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,9 @@ defaultTasks 'licenseFormat', 'build' ext { set 'version', version.toString() set 'description', description.toString() + + set 'velocity_api_version', velocity_api_version.toString() + set 'velocity_minimum_build', velocity_minimum_build.toString() } allprojects { @@ -24,19 +27,29 @@ allprojects { apply plugin: 'java' compileJava.options.encoding = 'UTF-8' - compileJava.options.release.set 16 + compileJava.options.release.set 17 + javadoc.options.encoding = 'UTF-8' + javadoc.options.addStringOption('Xdoclint:none', '-quiet') repositories { mavenLocal() mavenCentral() - maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } - maven { url 'https://repo.velocitypowered.com/snapshots/' } - maven { url 'https://repo.minebench.de/' } - maven { url 'https://repo.wea-ondara.net/repository/public/' } - maven { url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } - maven { url 'https://jitpack.io' } - maven { url 'https://repo.william278.net/releases/' } - maven { url 'https://mvn-repo.arim.space/lesser-gpl3/' } + maven { url = 'https://repo.william278.net/velocity/' } + maven { url = 'https://repo.codemc.io/repository/maven-releases/' } + maven { url = 'https://repo.william278.net/releases/' } + maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } + maven { url = 'https://repo.minebench.de/' } + maven { url = 'https://repo.wea-ondara.net/repository/public/' } + maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } + maven { url = 'https://jitpack.io' } + maven { url = 'https://mvn-repo.arim.space/lesser-gpl3/' } + } + + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.1' + testCompileOnly 'org.jetbrains:annotations:24.1.0' } license { @@ -45,6 +58,10 @@ allprojects { newLine = true } + test { + useJUnitPlatform() + } + processResources { filesMatching(['**/*.json', '**/*.yml']) { filter ReplaceTokens as Class, beginToken: '${', endToken: '}', @@ -53,70 +70,104 @@ allprojects { } } + subprojects { version rootProject.version archivesBaseName = "${rootProject.name}-${project.name.capitalize()}" - compileJava.options.encoding = 'UTF-8' jar { from '../LICENSE' } - if (['bungee', 'velocity', 'bukkit', 'plugin'].contains(project.name)) { - shadowJar { + shadowJar { + destinationDirectory.set(file("$rootDir/target")) + archiveClassifier.set('') + } + + // API publishing + if (['common','bukkit', 'velocity', 'bungee'].contains(project.name)) { + java { + withSourcesJar() + withJavadocJar() + } + sourcesJar { destinationDirectory.set(file("$rootDir/target")) - archiveClassifier.set('') } + javadocJar { + destinationDirectory.set(file("$rootDir/target")) + } + shadowJar.dependsOn(sourcesJar, javadocJar) - jar.dependsOn shadowJar - clean.delete "$rootDir/target" - } - - if (['plugin'].contains(project.name)) { publishing { - repositories { - if (System.getenv("RELEASES_MAVEN_USERNAME") != null) { - maven { - name = "william278-releases" - url = "https://repo.william278.net/releases" - credentials { - username = System.getenv("RELEASES_MAVEN_USERNAME") - password = System.getenv("RELEASES_MAVEN_PASSWORD") - } - authentication { - basic(BasicAuthentication) - } + if (['common'].contains(project.name)) { + publications { + mavenJavaCommon(MavenPublication) { + groupId = 'net.william278.huskchat' + artifactId = 'huskchat-common' + version = "$rootProject.version" + artifact shadowJar + artifact sourcesJar + artifact javadocJar + } + } + } + + if (['bukkit'].contains(project.name)) { + publications { + mavenJavaBukkit(MavenPublication) { + groupId = 'net.william278.huskchat' + artifactId = 'huskchat-bukkit' + version = "$rootProject.version" + artifact shadowJar + artifact sourcesJar + artifact javadocJar } } - if (System.getenv("SNAPSHOTS_MAVEN_USERNAME") != null) { - maven { - name = "william278-snapshots" - url = "https://repo.william278.net/snapshots" - credentials { - username = System.getenv("SNAPSHOTS_MAVEN_USERNAME") - password = System.getenv("SNAPSHOTS_MAVEN_PASSWORD") - } - authentication { - basic(BasicAuthentication) - } + } + + if (['velocity'].contains(project.name)) { + publications { + mavenJavaVelocity(MavenPublication) { + groupId = 'net.william278.huskchat' + artifactId = 'huskchat-velocity' + version = "$rootProject.version" + artifact shadowJar + artifact sourcesJar + artifact javadocJar } } } - publications { - mavenJava(MavenPublication) { - groupId = 'net.william278' - artifactId = "huskchat" - version = "$rootProject.version" - artifact shadowJar + if (['bungee'].contains(project.name)) { + publications { + mavenJavaBungee(MavenPublication) { + groupId = 'net.william278.huskchat' + artifactId = 'huskchat-bungee' + version = "$rootProject.version" + artifact shadowJar + artifact sourcesJar + artifact javadocJar + } } } } } + + jar.dependsOn shadowJar + clean.delete "$rootDir/target" } + logger.lifecycle("Building HuskChat ${version} by William278") +java { + def javaVersion = JavaVersion.toVersion(javaVersion) + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + withSourcesJar() + withJavadocJar() +} + @SuppressWarnings('GrMethodMayBeStatic') def versionMetadata() { // Get if there is a tag for this commit diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 46d8492f..5296f0d7 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -5,12 +5,14 @@ dependencies { implementation 'net.kyori:adventure-platform-bukkit:4.3.1' implementation 'space.arim.morepaperlib:morepaperlib:0.4.3' - compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' - compileOnly 'commons-io:commons-io:2.15.0' - compileOnly 'dev.dejvokep:boosted-yaml:1.3.1' + compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT' + compileOnly 'commons-io:commons-io:2.15.1' compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT' compileOnly 'me.clip:placeholderapi:2.11.5' compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'org.projectlombok:lombok:1.18.30' + + annotationProcessor 'org.projectlombok:lombok:1.18.30' } shadowJar { @@ -30,4 +32,6 @@ shadowJar { //noinspection GroovyAssignabilityCheck exclude dependency(':slf4j-api') } + + minimize() } \ No newline at end of file diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChat.java b/bukkit/src/main/java/net/william278/huskchat/BukkitHuskChat.java similarity index 55% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChat.java rename to bukkit/src/main/java/net/william278/huskchat/BukkitHuskChat.java index 91e883f1..1ad2e9c8 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChat.java +++ b/bukkit/src/main/java/net/william278/huskchat/BukkitHuskChat.java @@ -17,64 +17,64 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit; +package net.william278.huskchat; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.william278.desertwell.util.Version; -import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; -import net.william278.huskchat.bukkit.command.BukkitCommand; -import net.william278.huskchat.bukkit.event.BukkitEventDispatcher; -import net.william278.huskchat.bukkit.listener.BukkitListener; -import net.william278.huskchat.bukkit.placeholders.PlaceholderAPIReplacer; -import net.william278.huskchat.bukkit.player.BukkitPlayer; +import net.william278.huskchat.api.BukkitHuskChatAPI; +import net.william278.huskchat.command.BukkitCommand; import net.william278.huskchat.command.ShortcutCommand; +import net.william278.huskchat.config.Channels; +import net.william278.huskchat.config.Filters; import net.william278.huskchat.config.Locales; import net.william278.huskchat.config.Settings; import net.william278.huskchat.discord.DiscordHook; -import net.william278.huskchat.event.EventDispatcher; +import net.william278.huskchat.event.BukkitEventProvider; +import net.william278.huskchat.filter.ChatFilter; import net.william278.huskchat.getter.DataGetter; import net.william278.huskchat.getter.DefaultDataGetter; import net.william278.huskchat.getter.LuckPermsDataGetter; +import net.william278.huskchat.listener.BukkitListener; +import net.william278.huskchat.placeholders.BukkitPlaceholderAPIReplacer; import net.william278.huskchat.placeholders.DefaultReplacer; import net.william278.huskchat.placeholders.PlaceholderReplacer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.BukkitUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.bukkit.command.CommandMap; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import space.arim.morepaperlib.MorePaperLib; +import java.nio.file.Path; import java.util.*; import java.util.logging.Level; -public class BukkitHuskChat extends JavaPlugin implements HuskChat { - - // Instance provider - private static BukkitHuskChat instance; - - public static BukkitHuskChat getInstance() { - return instance; - } +@Getter +public class BukkitHuskChat extends JavaPlugin implements HuskChat, BukkitEventProvider { private MorePaperLib morePaperLib; private BukkitAudiences audiences; + private final List filtersAndReplacers = new ArrayList<>(); + private final UserCache userCache = new UserCache(); + private final List placeholderReplacers = new ArrayList<>(); + + @Setter private Settings settings; - private List commands; - private BukkitEventDispatcher eventDispatcher; - private DiscordHook discordHook; + @Setter private Locales locales; - private DataGetter playerDataGetter; - private PlayerCache playerCache; - private List placeholders; - - @Override - public void onLoad() { - // Set instance for easy cross-class referencing - instance = this; - BukkitHuskChatAPI.register(this); - } + @Setter + private Channels channels; + @Setter + private Filters filterSettings; + @Setter + @Getter(AccessLevel.NONE) + private DiscordHook discordHook; + private DataGetter dataGetter; @Override public void onEnable() { @@ -90,98 +90,42 @@ public void onEnable() { // Load discord hook this.loadDiscordHook(); - // Load saved social spy state - this.playerCache = new PlayerCache(this); - // Setup player data getter if (isPluginPresent("LuckPerms")) { - this.playerDataGetter = new LuckPermsDataGetter(); + this.dataGetter = new LuckPermsDataGetter(); } else { - this.playerDataGetter = new DefaultDataGetter(); + this.dataGetter = new DefaultDataGetter(); } // Setup placeholder parser - this.placeholders = new ArrayList<>(); - this.placeholders.add(new DefaultReplacer(this)); - if (getSettings().doPlaceholderAPI() && isPluginPresent("PlaceholderAPI")) { - this.placeholders.add(new PlaceholderAPIReplacer()); + this.placeholderReplacers.add(new DefaultReplacer(this)); + if (getSettings().getPlaceholder().isUsePapi() && isPluginPresent("PlaceholderAPI")) { + this.placeholderReplacers.add(new BukkitPlaceholderAPIReplacer()); } - // Create the event dispatcher - eventDispatcher = new BukkitEventDispatcher(this); - // Register events getServer().getPluginManager().registerEvents(new BukkitListener(this), this); // Register commands & channel shortcuts - commands = new ArrayList<>(BukkitCommand.Type.getCommands(this)); - commands.addAll(getSettings().getChannels().values().stream() - .flatMap(channel -> channel.getShortcutCommands().stream().map(command -> new BukkitCommand( + BukkitCommand.Type.registerAll(this); + getChannels().getChannels().forEach(channel -> channel.getShortcutCommands() + .forEach(command -> new BukkitCommand( new ShortcutCommand(command, channel.getId(), this), this - ))) - .toList()); + ))); + + // Register API + BukkitHuskChatAPI.register(this); // Initialise metrics and log this.checkForUpdates(); log(Level.INFO, "Enabled HuskChat version " + this.getVersion()); } - @NotNull - @Override - public Settings getSettings() { - return settings; - } - - @Override - public void setSettings(@NotNull Settings settings) { - this.settings = settings; - } - - @NotNull - @Override - public Locales getLocales() { - return locales; - } - - @Override - public void setLocales(@NotNull Locales locales) { - this.locales = locales; - } - - @NotNull - @Override - public EventDispatcher getEventDispatcher() { - return eventDispatcher; - } - - @NotNull - @Override - public PlayerCache getPlayerCache() { - return playerCache; - } - - @NotNull - @Override - public List getPlaceholderReplacers() { - return placeholders; - } - - @NotNull - @Override - public DataGetter getDataGetter() { - return playerDataGetter; - } - @Override public Optional getDiscordHook() { return Optional.ofNullable(discordHook); } - @Override - public void setDiscordHook(@NotNull DiscordHook discordHook) { - this.discordHook = discordHook; - } - @NotNull @Override public Version getVersion() { @@ -202,35 +146,29 @@ public String getPlatform() { } @Override - public Optional getPlayer(@NotNull UUID uuid) { + public Optional getPlayer(@NotNull UUID uuid) { return Optional.ofNullable(getServer().getPlayer(uuid)) - .map(BukkitPlayer::adapt); + .map(player -> BukkitUser.adapt(player, this)); } @Override - public Optional findPlayer(@NotNull String username) { - return Optional.ofNullable(getServer().getPlayerExact(username)) - .map(BukkitPlayer::adapt); + public Optional findPlayer(@NotNull String username) { + return Optional.ofNullable(getServer().getPlayer(username)) + .map(player -> BukkitUser.adapt(player, this)); } @Override - public Collection getOnlinePlayers() { + public @NotNull Collection getOnlinePlayers() { return getServer().getOnlinePlayers().stream() - .map(user -> (Player) BukkitPlayer.adapt(user)) + .map(user -> (OnlineUser) BukkitUser.adapt(user, this)) .toList(); } @Override - public Collection getOnlinePlayersOnServer(@NotNull Player player) { + public @NotNull Collection getOnlinePlayersOnServer(@NotNull OnlineUser player) { return getOnlinePlayers(); } - @NotNull - @Override - public Audience getConsole() { - return audiences.console(); - } - @Override public boolean isPluginPresent(@NotNull String dependency) { return getServer().getPluginManager().getPlugin(dependency) != null; @@ -245,20 +183,34 @@ public void log(@NotNull Level level, @NotNull String message, @NotNull Throwabl getLogger().log(level, message); } + @NotNull + public CommandMap getCommandMap() { + return morePaperLib.commandRegistration().getServerCommandMap(); + } + + + @NotNull @Override - public BukkitHuskChatAPI getAPI() { - return BukkitHuskChatAPI.getInstance(); + public Audience getAudience(@NotNull UUID user) { + return audiences.player(user); } @NotNull - public BukkitAudiences getAudience() { - return audiences; + @Override + public Audience getConsole() { + return audiences.console(); } + @Override @NotNull - public CommandMap getCommandMap() { - return morePaperLib.commandRegistration().getServerCommandMap(); + public Path getConfigDirectory() { + return getDataFolder().toPath(); } + @NotNull + @Override + public BukkitHuskChat getPlugin() { + return this; + } } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChatAPI.java b/bukkit/src/main/java/net/william278/huskchat/api/BukkitHuskChatAPI.java similarity index 78% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChatAPI.java rename to bukkit/src/main/java/net/william278/huskchat/api/BukkitHuskChatAPI.java index af345b2f..731fe085 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/BukkitHuskChatAPI.java +++ b/bukkit/src/main/java/net/william278/huskchat/api/BukkitHuskChatAPI.java @@ -17,20 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit; +package net.william278.huskchat.api; +import net.william278.huskchat.BukkitHuskChat; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; -import net.william278.huskchat.bukkit.player.BukkitPlayer; +import net.william278.huskchat.user.BukkitUser; import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("unused") public class BukkitHuskChatAPI extends HuskChatAPI { - public BukkitHuskChatAPI(HuskChat plugin) { + + private BukkitHuskChatAPI(@NotNull HuskChat plugin) { super(plugin); } + @NotNull public static BukkitHuskChatAPI getInstance() { return (BukkitHuskChatAPI) instance; } @@ -45,10 +48,12 @@ public static void register(@NotNull BukkitHuskChat plugin) { /** * Adapts a platform-specific Player object to a cross-platform Player object + * * @param player Must be a platform-specific Player object, e.g. a Velocity Player - * @return {@link BukkitPlayer} + * @return {@link BukkitUser} */ - public BukkitPlayer adaptPlayer(@NotNull Player player) { - return BukkitPlayer.adapt(player); + @NotNull + public BukkitUser adaptPlayer(@NotNull Player player) { + return BukkitUser.adapt(player, plugin); } } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEventDispatcher.java b/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEventDispatcher.java deleted file mode 100644 index 0a3b8a6d..00000000 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEventDispatcher.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.bukkit.event; - -import net.william278.huskchat.bukkit.BukkitHuskChat; -import net.william278.huskchat.event.EventDispatcher; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class BukkitEventDispatcher implements EventDispatcher { - - private final BukkitHuskChat plugin; - - public BukkitEventDispatcher(@NotNull BukkitHuskChat plugin) { - this.plugin = plugin; - } - - @Override - public CompletableFuture dispatchChatMessageEvent(@NotNull Player player, - @NotNull String message, - @NotNull String channelId) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - final ChatMessageEvent event = new ChatMessageEvent(player, message, channelId); - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getServer().getPluginManager().callEvent(event); - completableFuture.complete(event); - }); - return completableFuture; - } - - @Override - public CompletableFuture dispatchPrivateMessageEvent(@NotNull Player sender, - @NotNull List receivers, - @NotNull String message) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - final PrivateMessageEvent event = new PrivateMessageEvent(sender, receivers, message); - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getServer().getPluginManager().callEvent(event); - completableFuture.complete(event); - }); - return completableFuture; - } - - @Override - public CompletableFuture dispatchBroadcastMessageEvent(@NotNull Player sender, - @NotNull String message) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - final BroadcastMessageEvent event = new BroadcastMessageEvent(sender, message); - plugin.getServer().getScheduler().runTask(plugin, () -> { - plugin.getServer().getPluginManager().callEvent(event); - completableFuture.complete(event); - }); - return completableFuture; - } - -} diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/command/BukkitCommand.java b/bukkit/src/main/java/net/william278/huskchat/command/BukkitCommand.java similarity index 70% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/command/BukkitCommand.java rename to bukkit/src/main/java/net/william278/huskchat/command/BukkitCommand.java index c0fa9f64..84d4abcf 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/command/BukkitCommand.java +++ b/bukkit/src/main/java/net/william278/huskchat/command/BukkitCommand.java @@ -17,12 +17,11 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.command; +package net.william278.huskchat.command; -import net.william278.huskchat.bukkit.BukkitHuskChat; -import net.william278.huskchat.bukkit.player.BukkitPlayer; -import net.william278.huskchat.command.*; -import net.william278.huskchat.player.ConsolePlayer; +import net.william278.huskchat.BukkitHuskChat; +import net.william278.huskchat.user.BukkitUser; +import net.william278.huskchat.user.ConsoleUser; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -50,9 +49,9 @@ public BukkitCommand(@NotNull CommandBase command, @NotNull BukkitHuskChat plugi @Override public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { if (sender instanceof Player player) { - command.onExecute(BukkitPlayer.adapt(player), args); + command.onExecute(BukkitUser.adapt(player, plugin), args); } else { - command.onExecute(ConsolePlayer.create(plugin), args); + command.onExecute(ConsoleUser.wrap(plugin), args); } return true; } @@ -62,7 +61,7 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLab public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { if (sender instanceof Player player && player.hasPermission(command.getPermission())) { - return command.onTabComplete(BukkitPlayer.adapt(player), args); + return command.onTabComplete(BukkitUser.adapt(player, plugin), args); } return List.of(); } @@ -71,15 +70,15 @@ public List tabComplete(@NotNull CommandSender sender, @NotNull String a public enum Type { HUSKCHAT((plugin) -> Optional.of(new BukkitCommand(new HuskChatCommand(plugin), plugin))), CHANNEL((plugin) -> Optional.of(new BukkitCommand(new ChannelCommand(plugin), plugin))), - MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BukkitCommand(new MessageCommand(plugin), plugin)) : Optional.empty()), - REPLY((plugin) -> plugin.getSettings().isDoMessageCommand() + REPLY((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BukkitCommand(new ReplyCommand(plugin), plugin)) : Optional.empty()), - OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BukkitCommand(new OptOutMessageCommand(plugin), plugin)) : Optional.empty()), - BROADCAST((plugin) -> plugin.getSettings().isDoBroadcastCommand() + BROADCAST((plugin) -> plugin.getSettings().getBroadcastCommand().isEnabled() ? Optional.of(new BukkitCommand(new BroadcastCommand(plugin), plugin)) : Optional.empty()), - SOCIAL_SPY((plugin) -> plugin.getSettings().doSocialSpyCommand() + SOCIAL_SPY((plugin) -> plugin.getSettings().getSocialSpy().isEnabled() ? Optional.of(new BukkitCommand(new SocialSpyCommand(plugin), plugin)) : Optional.empty()); private final Function> commandSupplier; @@ -88,17 +87,12 @@ public enum Type { this.commandSupplier = commandSupplier; } - @NotNull - private Optional create(@NotNull BukkitHuskChat plugin) { - return commandSupplier.apply(plugin); + private void register(@NotNull BukkitHuskChat plugin) { + commandSupplier.apply(plugin); } - @NotNull - public static List getCommands(@NotNull BukkitHuskChat plugin) { - return Arrays.stream(values()) - .map(type -> type.create(plugin)) - .filter(Optional::isPresent).map(Optional::get) - .toList(); + public static void registerAll(@NotNull BukkitHuskChat plugin) { + Arrays.stream(values()).forEach(type -> type.register(plugin)); } } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BroadcastMessageEvent.java b/bukkit/src/main/java/net/william278/huskchat/event/BukkitBroadcastMessageEvent.java similarity index 78% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/event/BroadcastMessageEvent.java rename to bukkit/src/main/java/net/william278/huskchat/event/BukkitBroadcastMessageEvent.java index cd8c25f2..04988fbe 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BroadcastMessageEvent.java +++ b/bukkit/src/main/java/net/william278/huskchat/event/BukkitBroadcastMessageEvent.java @@ -17,19 +17,18 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; -public class BroadcastMessageEvent extends BukkitEvent implements IBroadcastMessageEvent { +public class BukkitBroadcastMessageEvent extends BukkitEvent implements BroadcastMessageEvent { private static final HandlerList HANDLERS = new HandlerList(); private String message; - public BroadcastMessageEvent(@NotNull Player player, @NotNull String message) { + public BukkitBroadcastMessageEvent(@NotNull OnlineUser player, @NotNull String message) { super(player); this.message = message; } @@ -47,7 +46,7 @@ public HandlerList getHandlers() { @NotNull @Override - public Player getSender() { + public OnlineUser getSender() { return player; } @@ -58,7 +57,7 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.player = sender; } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/ChatMessageEvent.java b/bukkit/src/main/java/net/william278/huskchat/event/BukkitChatMessageEvent.java similarity index 78% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/event/ChatMessageEvent.java rename to bukkit/src/main/java/net/william278/huskchat/event/BukkitChatMessageEvent.java index e49cea20..faf88b99 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/ChatMessageEvent.java +++ b/bukkit/src/main/java/net/william278/huskchat/event/BukkitChatMessageEvent.java @@ -17,22 +17,21 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; -public class ChatMessageEvent extends BukkitEvent implements IChatMessageEvent { +public class BukkitChatMessageEvent extends BukkitEvent implements ChatMessageEvent { private static final HandlerList HANDLERS = new HandlerList(); private String message; private String channelId; - protected ChatMessageEvent(@NotNull Player player, - @NotNull String message, - @NotNull String channelId) { + protected BukkitChatMessageEvent(@NotNull OnlineUser player, + @NotNull String message, + @NotNull String channelId) { super(player); this.message = message; this.channelId = channelId; @@ -51,7 +50,7 @@ public HandlerList getHandlers() { @NotNull @Override - public Player getSender() { + public OnlineUser getSender() { return player; } @@ -68,7 +67,7 @@ public String getChannelId() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.player = sender; } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEvent.java b/bukkit/src/main/java/net/william278/huskchat/event/BukkitEvent.java similarity index 85% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEvent.java rename to bukkit/src/main/java/net/william278/huskchat/event/BukkitEvent.java index 4a8bc146..83cf76a7 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/BukkitEvent.java +++ b/bukkit/src/main/java/net/william278/huskchat/event/BukkitEvent.java @@ -17,9 +17,9 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.event; +package net.william278.huskchat.event; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; @@ -27,9 +27,9 @@ public abstract class BukkitEvent extends Event implements Cancellable { private boolean cancelled; - protected net.william278.huskchat.player.Player player; + protected OnlineUser player; - protected BukkitEvent(@NotNull Player player) { + protected BukkitEvent(@NotNull OnlineUser player) { this.player = player; } diff --git a/bukkit/src/main/java/net/william278/huskchat/event/BukkitEventProvider.java b/bukkit/src/main/java/net/william278/huskchat/event/BukkitEventProvider.java new file mode 100644 index 00000000..9c554812 --- /dev/null +++ b/bukkit/src/main/java/net/william278/huskchat/event/BukkitEventProvider.java @@ -0,0 +1,71 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.event; + +import net.william278.huskchat.BukkitHuskChat; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public interface BukkitEventProvider extends EventProvider { + + @Override + default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, + @NotNull String message, + @NotNull String channelId) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + final BukkitChatMessageEvent event = new BukkitChatMessageEvent(player, message, channelId); + getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { + getPlugin().getServer().getPluginManager().callEvent(event); + completableFuture.complete(event); + }); + return completableFuture; + } + + @Override + default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, + @NotNull List receivers, + @NotNull String message) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + final BukkitPrivateMessageEvent event = new BukkitPrivateMessageEvent(sender, receivers, message); + getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { + getPlugin().getServer().getPluginManager().callEvent(event); + completableFuture.complete(event); + }); + return completableFuture; + } + + @Override + default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, + @NotNull String message) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + final BukkitBroadcastMessageEvent event = new BukkitBroadcastMessageEvent(sender, message); + getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { + getPlugin().getServer().getPluginManager().callEvent(event); + completableFuture.complete(event); + }); + return completableFuture; + } + + BukkitHuskChat getPlugin(); + +} diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/PrivateMessageEvent.java b/bukkit/src/main/java/net/william278/huskchat/event/BukkitPrivateMessageEvent.java similarity index 74% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/event/PrivateMessageEvent.java rename to bukkit/src/main/java/net/william278/huskchat/event/BukkitPrivateMessageEvent.java index 07f0f59f..a8f6bdb9 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/event/PrivateMessageEvent.java +++ b/bukkit/src/main/java/net/william278/huskchat/event/BukkitPrivateMessageEvent.java @@ -17,22 +17,21 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; import java.util.List; -public class PrivateMessageEvent extends BukkitEvent implements IPrivateMessageEvent { +public class BukkitPrivateMessageEvent extends BukkitEvent implements PrivateMessageEvent { private static final HandlerList HANDLERS = new HandlerList(); - private List recipients; + private List recipients; private String message; - public PrivateMessageEvent(@NotNull Player player, @NotNull List recipients, @NotNull String message) { + public BukkitPrivateMessageEvent(@NotNull OnlineUser player, @NotNull List recipients, @NotNull String message) { super(player); this.recipients = recipients; this.message = message; @@ -51,13 +50,13 @@ public HandlerList getHandlers() { @Override @NotNull - public Player getSender() { + public OnlineUser getSender() { return player; } @NotNull @Override - public List getRecipients() { + public List getRecipients() { return recipients; } @@ -68,12 +67,12 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.player = sender; } @Override - public void setRecipients(@NotNull List recipients) { + public void setRecipients(@NotNull List recipients) { this.recipients = recipients; } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/listener/BukkitListener.java b/bukkit/src/main/java/net/william278/huskchat/listener/BukkitListener.java similarity index 59% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/listener/BukkitListener.java rename to bukkit/src/main/java/net/william278/huskchat/listener/BukkitListener.java index 0e89c43b..c7f69304 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/listener/BukkitListener.java +++ b/bukkit/src/main/java/net/william278/huskchat/listener/BukkitListener.java @@ -17,13 +17,12 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.listener; +package net.william278.huskchat.listener; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.bukkit.player.BukkitPlayer; -import net.william278.huskchat.listener.PlayerListener; +import net.william278.huskchat.channel.Channel; import net.william278.huskchat.message.ChatMessage; -import org.bukkit.entity.Player; +import net.william278.huskchat.user.BukkitUser; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -32,6 +31,8 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + public class BukkitListener extends PlayerListener implements Listener { public BukkitListener(@NotNull HuskChat plugin) { @@ -40,23 +41,28 @@ public BukkitListener(@NotNull HuskChat plugin) { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerChat(AsyncPlayerChatEvent e) { - final Player player = e.getPlayer(); - boolean shouldCancel = new ChatMessage( - plugin.getPlayerCache().getPlayerChannel(player.getUniqueId()), - BukkitPlayer.adapt(player), - e.getMessage(), - plugin - ).dispatch(); - if (shouldCancel) { + // Verify they are in a channel + final BukkitUser player = BukkitUser.adapt(e.getPlayer(), plugin); + final Optional channel = plugin.getChannels().getChannel( + plugin.getUserCache().getPlayerChannel(player.getUuid()) + ); + if (channel.isEmpty()) { + plugin.getLocales().sendMessage(player, "error_no_channel"); + return; + } + + // Send the chat message, determine if the event should be canceled + if (new ChatMessage(channel.get(), player, e.getMessage(), plugin).dispatch()) { e.setCancelled(true); } } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerJoin(PlayerJoinEvent e) { - final BukkitPlayer player = BukkitPlayer.adapt(e.getPlayer()); + final BukkitUser player = BukkitUser.adapt(e.getPlayer(), plugin); super.handlePlayerSwitchServer(player, player.getServerName()); - if (plugin.getSettings().doJoinMessages() || !plugin.getSettings().getJoinQuitBroadcastScope().isPassThrough) { + if (plugin.getSettings().getJoinAndQuitMessages().getJoin().isEnabled() + || !plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope().isPassThrough()) { e.setJoinMessage(null); } super.handlePlayerJoin(player); @@ -64,10 +70,11 @@ public void onPlayerJoin(PlayerJoinEvent e) { @EventHandler public void onPlayerQuit(PlayerQuitEvent e) { - if (plugin.getSettings().doQuitMessages() || !plugin.getSettings().getJoinQuitBroadcastScope().isPassThrough) { + if (plugin.getSettings().getJoinAndQuitMessages().getQuit().isEnabled() + || !plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope().isPassThrough()) { e.setQuitMessage(null); } - super.handlePlayerQuit(BukkitPlayer.adapt(e.getPlayer())); + super.handlePlayerQuit(BukkitUser.adapt(e.getPlayer(), plugin)); } } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/placeholders/PlaceholderAPIReplacer.java b/bukkit/src/main/java/net/william278/huskchat/placeholders/BukkitPlaceholderAPIReplacer.java similarity index 73% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/placeholders/PlaceholderAPIReplacer.java rename to bukkit/src/main/java/net/william278/huskchat/placeholders/BukkitPlaceholderAPIReplacer.java index e112a468..d2d04ec8 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/placeholders/PlaceholderAPIReplacer.java +++ b/bukkit/src/main/java/net/william278/huskchat/placeholders/BukkitPlaceholderAPIReplacer.java @@ -17,22 +17,21 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.placeholders; +package net.william278.huskchat.placeholders; import me.clip.placeholderapi.PlaceholderAPI; -import net.william278.huskchat.bukkit.player.BukkitPlayer; -import net.william278.huskchat.placeholders.PlaceholderReplacer; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.BukkitUser; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; -public class PlaceholderAPIReplacer implements PlaceholderReplacer { +public class BukkitPlaceholderAPIReplacer implements PlaceholderReplacer { @Override - public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull Player player) { + public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player) { return CompletableFuture.completedFuture(PlaceholderAPI.setPlaceholders( - ((BukkitPlayer) player).getBukkitPlayer(), + ((BukkitUser) player).getPlayer(), message )); } diff --git a/bukkit/src/main/java/net/william278/huskchat/bukkit/player/BukkitPlayer.java b/bukkit/src/main/java/net/william278/huskchat/user/BukkitUser.java similarity index 55% rename from bukkit/src/main/java/net/william278/huskchat/bukkit/player/BukkitPlayer.java rename to bukkit/src/main/java/net/william278/huskchat/user/BukkitUser.java index 6f66afd9..e5c52739 100644 --- a/bukkit/src/main/java/net/william278/huskchat/bukkit/player/BukkitPlayer.java +++ b/bukkit/src/main/java/net/william278/huskchat/user/BukkitUser.java @@ -17,39 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.bukkit.player; +package net.william278.huskchat.user; -import net.kyori.adventure.audience.Audience; -import net.william278.huskchat.bukkit.BukkitHuskChat; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.HuskChat; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.UUID; +public class BukkitUser extends OnlineUser { + private final Player player; -public class BukkitPlayer implements Player { - - private final BukkitHuskChat plugin = BukkitHuskChat.getInstance(); - private final org.bukkit.entity.Player player; - - private BukkitPlayer(@NotNull org.bukkit.entity.Player player) { + private BukkitUser(@NotNull Player player, @NotNull HuskChat plugin) { + super(player.getName(), player.getUniqueId(), plugin); this.player = player; } @NotNull - public static BukkitPlayer adapt(@NotNull org.bukkit.entity.Player player) { - return new BukkitPlayer(player); - } - - @NotNull - @Override - public String getName() { - return player.getName(); - } - - @NotNull - @Override - public UUID getUuid() { - return player.getUniqueId(); + public static BukkitUser adapt(@NotNull Player player, @NotNull HuskChat plugin) { + return new BukkitUser(player, plugin); } @Override @@ -69,18 +53,12 @@ public int getPlayersOnServer() { } @Override - public boolean hasPermission(String node) { + public boolean hasPermission(@NotNull String node) { return player.hasPermission(node); } @NotNull - @Override - public Audience getAudience() { - return plugin.getAudience().player(player); - } - - @NotNull - public org.bukkit.entity.Player getBukkitPlayer() { + public Player getPlayer() { return player; } diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 68b5c47b..662f18bb 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -2,8 +2,8 @@ name: 'HuskChat' version: '${version}' description: '${description}' author: 'William278' -main: 'net.william278.huskchat.bukkit.BukkitHuskChat' -api-version: 1.16 +main: 'net.william278.huskchat.BukkitHuskChat' +api-version: 1.17 folia-supported: true softdepend: - 'LuckPerms' diff --git a/bungee/build.gradle b/bungee/build.gradle index 9a10bf42..35ac85bc 100644 --- a/bungee/build.gradle +++ b/bungee/build.gradle @@ -5,11 +5,13 @@ dependencies { implementation 'net.kyori:adventure-platform-bungeecord:4.3.1' compileOnly 'net.md-5:bungeecord-api:1.20-R0.1-SNAPSHOT' - compileOnly 'commons-io:commons-io:2.15.0' - compileOnly 'dev.dejvokep:boosted-yaml:1.3.1' + compileOnly 'commons-io:commons-io:2.15.1' compileOnly 'net.alpenblock:BungeePerms:4.0-dev-143' compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT' compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'org.projectlombok:lombok:1.18.30' + + annotationProcessor 'org.projectlombok:lombok:1.18.30' } shadowJar { @@ -28,4 +30,6 @@ shadowJar { //noinspection GroovyAssignabilityCheck exclude dependency(':slf4j-api') } + + minimize() } \ No newline at end of file diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChat.java b/bungee/src/main/java/net/william278/huskchat/BungeeHuskChat.java similarity index 55% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChat.java rename to bungee/src/main/java/net/william278/huskchat/BungeeHuskChat.java index 19896749..4b91ad3a 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChat.java +++ b/bungee/src/main/java/net/william278/huskchat/BungeeHuskChat.java @@ -17,113 +17,112 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord; +package net.william278.huskchat; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.platform.bungeecord.BungeeAudiences; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; import net.william278.desertwell.util.Version; -import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; -import net.william278.huskchat.bungeecord.command.BungeeCommand; -import net.william278.huskchat.bungeecord.event.BungeeEventDispatcher; -import net.william278.huskchat.bungeecord.getter.BungeePermsDataGetter; -import net.william278.huskchat.bungeecord.listener.BungeeListener; -import net.william278.huskchat.bungeecord.player.BungeePlayer; +import net.william278.huskchat.api.BungeeHuskChatAPI; +import net.william278.huskchat.command.BungeeCommand; import net.william278.huskchat.command.ShortcutCommand; +import net.william278.huskchat.config.Channels; +import net.william278.huskchat.config.Filters; import net.william278.huskchat.config.Locales; import net.william278.huskchat.config.Settings; import net.william278.huskchat.discord.DiscordHook; +import net.william278.huskchat.event.BungeeEventProvider; +import net.william278.huskchat.filter.ChatFilter; +import net.william278.huskchat.getter.BungeePermsDataGetter; import net.william278.huskchat.getter.DataGetter; import net.william278.huskchat.getter.DefaultDataGetter; import net.william278.huskchat.getter.LuckPermsDataGetter; +import net.william278.huskchat.listener.BungeeListener; import net.william278.huskchat.placeholders.DefaultReplacer; import net.william278.huskchat.placeholders.PAPIProxyBridgeReplacer; import net.william278.huskchat.placeholders.PlaceholderReplacer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.BungeeUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.bstats.bungeecord.Metrics; import org.jetbrains.annotations.NotNull; import java.io.InputStream; +import java.nio.file.Path; import java.util.*; import java.util.logging.Level; -public final class BungeeHuskChat extends Plugin implements HuskChat { +@Getter +public final class BungeeHuskChat extends Plugin implements HuskChat, BungeeEventProvider { // bStats ID private static final int METRICS_ID = 11882; - // Instance provider - private static BungeeHuskChat instance; - - public static BungeeHuskChat getInstance() { - return instance; - } + private final List filtersAndReplacers = new ArrayList<>(); + private final UserCache userCache = new UserCache(); + private final List placeholderReplacers = new ArrayList<>(); + @Getter(AccessLevel.NONE) private BungeeAudiences audiences; + @Setter private Settings settings; - private List commands; - private BungeeEventDispatcher eventDispatcher; - private DiscordHook discordHook; + @Setter private Locales locales; - private DataGetter playerDataGetter; - private PlayerCache playerCache; - private List placeholders; - + @Setter + private Channels channels; + @Setter + private Filters filterSettings; + @Setter + @Getter(AccessLevel.NONE) + private DiscordHook discordHook; + private DataGetter dataGetter; @Override - public void onLoad() { - // Set instance for easy cross-class referencing - instance = this; - - // Create the event dispatcher, register audiences - eventDispatcher = new BungeeEventDispatcher(getProxy()); + public void onEnable() { + // Setup audiences audiences = BungeeAudiences.create(this); - BungeeHuskChatAPI.register(this); - } - @Override - public void onEnable() { - // Load config and locale files + // Load config files this.loadConfig(); + this.loadFilters(); - // Load discord hook - this.loadDiscordHook(); - - // Load saved social spy state - this.playerCache = new PlayerCache(this); + // Load API + BungeeHuskChatAPI.register(this); // Setup player data getter if (isPluginPresent("LuckPerms")) { - this.playerDataGetter = new LuckPermsDataGetter(); + this.dataGetter = new LuckPermsDataGetter(); } else { if (isPluginPresent("BungeePerms")) { - this.playerDataGetter = new BungeePermsDataGetter(); + this.dataGetter = new BungeePermsDataGetter(); } else { - this.playerDataGetter = new DefaultDataGetter(); + this.dataGetter = new DefaultDataGetter(); } } // Setup placeholder parser - this.placeholders = new ArrayList<>(); - this.placeholders.add(new DefaultReplacer(this)); - if (getSettings().doPlaceholderAPI() && isPluginPresent("PAPIProxyBridge")) { - this.placeholders.add(new PAPIProxyBridgeReplacer(this)); + this.placeholderReplacers.add(new DefaultReplacer(this)); + if (getSettings().getPlaceholder().isUsePapi() && isPluginPresent("PAPIProxyBridge")) { + this.placeholderReplacers.add(new PAPIProxyBridgeReplacer(this)); } + // Setup Discord + this.loadDiscordHook(); + // Register events getProxy().getPluginManager().registerListener(this, new BungeeListener(this)); // Register commands & channel shortcuts - commands = new ArrayList<>(BungeeCommand.Type.getCommands(this)); - commands.addAll(getSettings().getChannels().values().stream() - .flatMap(channel -> channel.getShortcutCommands().stream().map(command -> new BungeeCommand( + BungeeCommand.Type.registerAll(this); + getChannels().getChannels().forEach(channel -> channel.getShortcutCommands() + .forEach(command -> new BungeeCommand( new ShortcutCommand(command, channel.getId(), this), this - ))) - .toList()); + ))); // Initialise metrics and log new Metrics(this, METRICS_ID); @@ -131,27 +130,6 @@ public void onEnable() { log(Level.INFO, "Enabled HuskChat version " + this.getVersion()); } - @Override - @NotNull - public Settings getSettings() { - return settings; - } - - @Override - public void setSettings(@NotNull Settings settings) { - this.settings = settings; - } - - @NotNull - @Override - public Locales getLocales() { - return locales; - } - - @Override - public void setLocales(@NotNull Locales locales) { - this.locales = locales; - } @NotNull @Override @@ -171,73 +149,36 @@ public String getPlatform() { return ProxyServer.getInstance().getName(); } - @NotNull - @Override - public List getPlaceholderReplacers() { - return placeholders; - } - - @Override - @NotNull - public DataGetter getDataGetter() { - return playerDataGetter; - } - @Override public Optional getDiscordHook() { return Optional.ofNullable(discordHook); } @Override - public void setDiscordHook(@NotNull DiscordHook discordHook) { - this.discordHook = discordHook; - } - - @NotNull - public BungeeEventDispatcher getEventDispatcher() { - return eventDispatcher; - } - - @Override - @NotNull - public PlayerCache getPlayerCache() { - return playerCache; - } - - @Override - public Optional getPlayer(@NotNull UUID uuid) { + public Optional getPlayer(@NotNull UUID uuid) { final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid); if (player != null) { - return Optional.of(BungeePlayer.adapt(player)); + return Optional.of(BungeeUser.adapt(player, this)); } else { return Optional.empty(); } } @Override - public Collection getOnlinePlayers() { - ArrayList crossPlatform = new ArrayList<>(); + @NotNull + public Collection getOnlinePlayers() { + ArrayList crossPlatform = new ArrayList<>(); for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { - crossPlatform.add(BungeePlayer.adapt(player)); + crossPlatform.add(BungeeUser.adapt(player, this)); } return crossPlatform; } - @Override - public Collection getOnlinePlayersOnServer(@NotNull Player player) { - ArrayList crossPlatform = new ArrayList<>(); - BungeePlayer.toBungee(player).ifPresent(bungeePlayer -> { - for (ProxiedPlayer playerOnServer : bungeePlayer.getServer().getInfo().getPlayers()) { - crossPlatform.add(BungeePlayer.adapt(playerOnServer)); - } - }); - return crossPlatform; - } - @Override @NotNull - public Audience getConsole() { - return audiences.console(); + public Collection getOnlinePlayersOnServer(@NotNull OnlineUser user) { + return ((BungeeUser) user).getPlayer().getServer().getInfo().getPlayers().stream() + .map(player -> (OnlineUser) BungeeUser.adapt(player, this)).toList(); } @Override @@ -245,6 +186,12 @@ public InputStream getResource(@NotNull String path) { return getResourceAsStream(path); } + @Override + @NotNull + public Path getConfigDirectory() { + return getDataFolder().toPath(); + } + @Override public boolean isPluginPresent(@NotNull String dependency) { return ProxyServer.getInstance().getPluginManager().getPlugin(dependency) != null; @@ -259,26 +206,21 @@ public void log(@NotNull Level level, @NotNull String message, @NotNull Throwabl getLogger().log(level, message); } - @NotNull - public BungeeAudiences getAudience() { - return audiences; - } - @Override - public Optional findPlayer(@NotNull String username) { + public Optional findPlayer(@NotNull String username) { if (username.isEmpty()) { return Optional.empty(); } - final Optional optionalPlayer; + final Optional optionalPlayer; if (ProxyServer.getInstance().getPlayer(username) != null) { final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(username); - optionalPlayer = Optional.of(BungeePlayer.adapt(player)); + optionalPlayer = Optional.of(BungeeUser.adapt(player, this)); } else { final List matchedPlayers = ProxyServer.getInstance().matchPlayer(username) .stream().filter(val -> val.getName().startsWith(username)).sorted().toList(); - if (matchedPlayers.size() > 0) { - optionalPlayer = Optional.of(BungeePlayer.adapt(matchedPlayers.get(0))); + if (!matchedPlayers.isEmpty()) { + optionalPlayer = Optional.of(BungeeUser.adapt(matchedPlayers.get(0), this)); } else { optionalPlayer = Optional.empty(); } @@ -286,8 +228,22 @@ public Optional findPlayer(@NotNull String username) { return optionalPlayer; } + @NotNull + @Override + public Audience getAudience(@NotNull UUID user) { + return audiences.player(user); + } + + @NotNull @Override - public BungeeHuskChatAPI getAPI() { - return BungeeHuskChatAPI.getInstance(); + public Audience getConsole() { + return audiences.console(); } + + @NotNull + @Override + public HuskChat getPlugin() { + return this; + } + } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChatAPI.java b/bungee/src/main/java/net/william278/huskchat/api/BungeeHuskChatAPI.java similarity index 78% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChatAPI.java rename to bungee/src/main/java/net/william278/huskchat/api/BungeeHuskChatAPI.java index 5802f61a..430d73c1 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/BungeeHuskChatAPI.java +++ b/bungee/src/main/java/net/william278/huskchat/api/BungeeHuskChatAPI.java @@ -17,20 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord; +package net.william278.huskchat.api; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.william278.huskchat.BungeeHuskChat; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; -import net.william278.huskchat.bungeecord.player.BungeePlayer; +import net.william278.huskchat.user.BungeeUser; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("unused") public class BungeeHuskChatAPI extends HuskChatAPI { - public BungeeHuskChatAPI(HuskChat plugin) { + + private BungeeHuskChatAPI(@NotNull HuskChat plugin) { super(plugin); } + @NotNull public static BungeeHuskChatAPI getInstance() { return (BungeeHuskChatAPI) instance; } @@ -45,10 +48,12 @@ public static void register(@NotNull BungeeHuskChat plugin) { /** * Adapts a platform-specific Player object to a cross-platform Player object + * * @param player Must be a platform-specific Player object, e.g. a Velocity Player - * @return {@link BungeePlayer} + * @return {@link BungeeUser} */ - public BungeePlayer adaptPlayer(@NotNull ProxiedPlayer player) { - return BungeePlayer.adapt(player); + @NotNull + public BungeeUser adaptPlayer(@NotNull ProxiedPlayer player) { + return BungeeUser.adapt(player, plugin); } } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEventDispatcher.java b/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEventDispatcher.java deleted file mode 100644 index 15f3c106..00000000 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEventDispatcher.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.bungeecord.event; - -import net.md_5.bungee.api.ProxyServer; -import net.william278.huskchat.event.EventDispatcher; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class BungeeEventDispatcher implements EventDispatcher { - private final ProxyServer server; - - public BungeeEventDispatcher(ProxyServer server) { - this.server = server; - } - - // In order to keep compatibility with the Velocity implementation, the Bungee events also return CompletableFuture - @Override - public CompletableFuture dispatchChatMessageEvent(@NotNull Player sender, @NotNull String message, @NotNull String channelId) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - completableFuture.complete(server.getPluginManager().callEvent(new ChatMessageEvent(sender, message, channelId))); - return completableFuture; - } - - @Override - public CompletableFuture dispatchPrivateMessageEvent(@NotNull Player sender, @NotNull List receivers, @NotNull String message) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - completableFuture.complete(server.getPluginManager().callEvent(new PrivateMessageEvent(sender, receivers, message))); - return completableFuture; - } - - @Override - public CompletableFuture dispatchBroadcastMessageEvent(@NotNull Player sender, @NotNull String message) { - final CompletableFuture completableFuture = new CompletableFuture<>(); - completableFuture.complete(server.getPluginManager().callEvent(new BroadcastMessageEvent(sender, message))); - return completableFuture; - } -} diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/player/BungeePlayer.java b/bungee/src/main/java/net/william278/huskchat/bungeecord/player/BungeePlayer.java deleted file mode 100644 index 5b8a486a..00000000 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/player/BungeePlayer.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.bungeecord.player; - -import net.kyori.adventure.audience.Audience; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.william278.huskchat.bungeecord.BungeeHuskChat; -import net.william278.huskchat.player.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; -import java.util.UUID; - -/** - * Bungee implementation of a cross-platform {@link Player} - */ -public class BungeePlayer implements Player { - - private final BungeeHuskChat plugin = BungeeHuskChat.getInstance(); - private final ProxiedPlayer player; - - private BungeePlayer(@NotNull ProxiedPlayer player) { - this.player = player; - } - - @Override - @NotNull - public String getName() { - return player.getName(); - } - - @NotNull - @Override - public UUID getUuid() { - return player.getUniqueId(); - } - - @Override - public int getPing() { - return player.getPing(); - } - - @Override - @NotNull - public String getServerName() { - return player.getServer().getInfo().getName(); - } - - @Override - public int getPlayersOnServer() { - return player.getServer().getInfo().getPlayers().size(); - } - - @Override - public boolean hasPermission(String s) { - return player.hasPermission(s); - } - - @NotNull - @Override - public Audience getAudience() { - return plugin.getAudience().player(player); - } - - /** - * Adapts a cross-platform {@link Player} to a bungee {@link CommandSender} object - * - * @param player {@link Player} to adapt - * @return The {@link ProxiedPlayer} object, {@code null} if they are offline - */ - public static Optional toBungee(@NotNull Player player) { - ProxiedPlayer proxiedPlayer = ProxyServer.getInstance().getPlayer(player.getUuid()); - if (proxiedPlayer != null) { - return Optional.of(proxiedPlayer); - } else { - return Optional.empty(); - } - } - - /** - * Adapts a bungee {@link ProxiedPlayer} to a cross-platform {@link Player} object - * - * @param player {@link ProxiedPlayer} to adapt - * @return The {@link Player} object - */ - public static BungeePlayer adapt(@NotNull ProxiedPlayer player) { - return new BungeePlayer(player); - } - -} diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/command/BungeeCommand.java b/bungee/src/main/java/net/william278/huskchat/command/BungeeCommand.java similarity index 69% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/command/BungeeCommand.java rename to bungee/src/main/java/net/william278/huskchat/command/BungeeCommand.java index faf0113b..31a487d4 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/command/BungeeCommand.java +++ b/bungee/src/main/java/net/william278/huskchat/command/BungeeCommand.java @@ -17,16 +17,15 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.command; +package net.william278.huskchat.command; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.TabExecutor; -import net.william278.huskchat.bungeecord.BungeeHuskChat; -import net.william278.huskchat.bungeecord.player.BungeePlayer; -import net.william278.huskchat.command.*; -import net.william278.huskchat.player.ConsolePlayer; +import net.william278.huskchat.BungeeHuskChat; +import net.william278.huskchat.user.BungeeUser; +import net.william278.huskchat.user.ConsoleUser; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -51,16 +50,16 @@ public BungeeCommand(@NotNull CommandBase command, @NotNull BungeeHuskChat plugi @Override public void execute(CommandSender sender, String[] args) { if (sender instanceof ProxiedPlayer player) { - command.onExecute(BungeePlayer.adapt(player), args); + command.onExecute(BungeeUser.adapt(player, plugin), args); } else { - command.onExecute(ConsolePlayer.create(plugin), args); + command.onExecute(ConsoleUser.wrap(plugin), args); } } @Override public Iterable onTabComplete(CommandSender sender, String[] args) { if (sender instanceof ProxiedPlayer player && player.hasPermission(command.getPermission())) { - return command.onTabComplete(BungeePlayer.adapt(player), args); + return command.onTabComplete(BungeeUser.adapt(player, plugin), args); } return List.of(); } @@ -68,17 +67,17 @@ public Iterable onTabComplete(CommandSender sender, String[] args) { public enum Type { HUSKCHAT((plugin) -> Optional.of(new BungeeCommand(new HuskChatCommand(plugin), plugin))), CHANNEL((plugin) -> Optional.of(new BungeeCommand(new ChannelCommand(plugin), plugin))), - MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BungeeCommand(new MessageCommand(plugin), plugin)) : Optional.empty()), - REPLY((plugin) -> plugin.getSettings().isDoMessageCommand() + REPLY((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BungeeCommand(new ReplyCommand(plugin), plugin)) : Optional.empty()), - OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new BungeeCommand(new OptOutMessageCommand(plugin), plugin)) : Optional.empty()), - BROADCAST((plugin) -> plugin.getSettings().isDoBroadcastCommand() + BROADCAST((plugin) -> plugin.getSettings().getBroadcastCommand().isEnabled() ? Optional.of(new BungeeCommand(new BroadcastCommand(plugin), plugin)) : Optional.empty()), - SOCIAL_SPY((plugin) -> plugin.getSettings().doSocialSpyCommand() + SOCIAL_SPY((plugin) -> plugin.getSettings().getSocialSpy().isEnabled() ? Optional.of(new BungeeCommand(new SocialSpyCommand(plugin), plugin)) : Optional.empty()), - LOCAL_SPY((plugin) -> plugin.getSettings().doLocalSpyCommand() + LOCAL_SPY((plugin) -> plugin.getSettings().getLocalSpy().isEnabled() ? Optional.of(new BungeeCommand(new LocalSpyCommand(plugin), plugin)) : Optional.empty()); private final Function> commandSupplier; @@ -87,17 +86,12 @@ public enum Type { this.commandSupplier = commandSupplier; } - @NotNull - private Optional create(@NotNull BungeeHuskChat plugin) { - return commandSupplier.apply(plugin); + private void register(@NotNull BungeeHuskChat plugin) { + commandSupplier.apply(plugin); } - @NotNull - public static List getCommands(@NotNull BungeeHuskChat plugin) { - return Arrays.stream(values()) - .map(type -> type.create(plugin)) - .filter(Optional::isPresent).map(Optional::get) - .toList(); + public static void registerAll(@NotNull BungeeHuskChat plugin) { + Arrays.stream(values()).forEach(type -> type.register(plugin)); } } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BroadcastMessageEvent.java b/bungee/src/main/java/net/william278/huskchat/event/BungeeBroadcastMessageEvent.java similarity index 73% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/event/BroadcastMessageEvent.java rename to bungee/src/main/java/net/william278/huskchat/event/BungeeBroadcastMessageEvent.java index 7b50974d..74f0e545 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BroadcastMessageEvent.java +++ b/bungee/src/main/java/net/william278/huskchat/event/BungeeBroadcastMessageEvent.java @@ -17,24 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public class BroadcastMessageEvent extends BungeeEvent implements IBroadcastMessageEvent { - private Player sender; +public class BungeeBroadcastMessageEvent extends BungeeEvent implements BroadcastMessageEvent { + private OnlineUser sender; private String message; - public BroadcastMessageEvent(Player sender, String message) { + public BungeeBroadcastMessageEvent(OnlineUser sender, String message) { this.sender = sender; this.message = message; } @NotNull @Override - public Player getSender() { + public OnlineUser getSender() { return sender; } @@ -45,7 +44,7 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/ChatMessageEvent.java b/bungee/src/main/java/net/william278/huskchat/event/BungeeChatMessageEvent.java similarity index 77% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/event/ChatMessageEvent.java rename to bungee/src/main/java/net/william278/huskchat/event/BungeeChatMessageEvent.java index abb14b0c..f50f68c7 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/ChatMessageEvent.java +++ b/bungee/src/main/java/net/william278/huskchat/event/BungeeChatMessageEvent.java @@ -17,18 +17,17 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public class ChatMessageEvent extends BungeeEvent implements IChatMessageEvent { - private Player sender; +public class BungeeChatMessageEvent extends BungeeEvent implements ChatMessageEvent { + private OnlineUser sender; private String message; private String channelId; - public ChatMessageEvent(Player sender, String message, String channelId) { + public BungeeChatMessageEvent(OnlineUser sender, String message, String channelId) { this.sender = sender; this.message = message; this.channelId = channelId; @@ -36,7 +35,7 @@ public ChatMessageEvent(Player sender, String message, String channelId) { @Override @NotNull - public Player getSender() { + public OnlineUser getSender() { return sender; } @@ -53,7 +52,7 @@ public String getChannelId() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEvent.java b/bungee/src/main/java/net/william278/huskchat/event/BungeeEvent.java similarity index 81% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEvent.java rename to bungee/src/main/java/net/william278/huskchat/event/BungeeEvent.java index f8f3df04..32d26eb2 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/BungeeEvent.java +++ b/bungee/src/main/java/net/william278/huskchat/event/BungeeEvent.java @@ -17,19 +17,18 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.event; +package net.william278.huskchat.event; +import lombok.Getter; +import lombok.Setter; import net.md_5.bungee.api.plugin.Cancellable; import net.md_5.bungee.api.plugin.Event; +@Setter +@Getter public class BungeeEvent extends Event implements Cancellable { + private boolean cancelled = false; - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } - public boolean isCancelled() { - return cancelled; - } } diff --git a/bungee/src/main/java/net/william278/huskchat/event/BungeeEventProvider.java b/bungee/src/main/java/net/william278/huskchat/event/BungeeEventProvider.java new file mode 100644 index 00000000..ddee335c --- /dev/null +++ b/bungee/src/main/java/net/william278/huskchat/event/BungeeEventProvider.java @@ -0,0 +1,55 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.event; + +import net.md_5.bungee.api.ProxyServer; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public interface BungeeEventProvider extends EventProvider { + + // To keep compatibility with the Velocity implementation, the Bungee events also return CompletableFuture + @Override + default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser sender, @NotNull String message, @NotNull String channelId) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeeChatMessageEvent(sender, message, channelId))); + return completableFuture; + } + + @Override + default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List receivers, @NotNull String message) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeePrivateMessageEvent(sender, receivers, message))); + return completableFuture; + } + + @Override + default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, @NotNull String message) { + final CompletableFuture completableFuture = new CompletableFuture<>(); + completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeeBroadcastMessageEvent(sender, message))); + return completableFuture; + } + + ProxyServer getProxy(); + +} diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/PrivateMessageEvent.java b/bungee/src/main/java/net/william278/huskchat/event/BungeePrivateMessageEvent.java similarity index 69% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/event/PrivateMessageEvent.java rename to bungee/src/main/java/net/william278/huskchat/event/BungeePrivateMessageEvent.java index be8d7af9..2a611d76 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/event/PrivateMessageEvent.java +++ b/bungee/src/main/java/net/william278/huskchat/event/BungeePrivateMessageEvent.java @@ -17,20 +17,19 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; -public class PrivateMessageEvent extends BungeeEvent implements IPrivateMessageEvent { - private Player sender; - private List recipients; +public class BungeePrivateMessageEvent extends BungeeEvent implements PrivateMessageEvent { + private OnlineUser sender; + private List recipients; private String message; - public PrivateMessageEvent(@NotNull Player sender, @NotNull List recipients, @NotNull String message) { + public BungeePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List recipients, @NotNull String message) { this.sender = sender; this.recipients = recipients; this.message = message; @@ -38,13 +37,13 @@ public PrivateMessageEvent(@NotNull Player sender, @NotNull List recipie @Override @NotNull - public Player getSender() { + public OnlineUser getSender() { return sender; } @NotNull @Override - public List getRecipients() { + public List getRecipients() { return recipients; } @@ -55,12 +54,12 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } @Override - public void setRecipients(@NotNull List recipients) { + public void setRecipients(@NotNull List recipients) { this.recipients = recipients; } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/getter/BungeePermsDataGetter.java b/bungee/src/main/java/net/william278/huskchat/getter/BungeePermsDataGetter.java similarity index 80% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/getter/BungeePermsDataGetter.java rename to bungee/src/main/java/net/william278/huskchat/getter/BungeePermsDataGetter.java index ee7cb933..403ff356 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/getter/BungeePermsDataGetter.java +++ b/bungee/src/main/java/net/william278/huskchat/getter/BungeePermsDataGetter.java @@ -17,13 +17,12 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.getter; +package net.william278.huskchat.getter; import net.alpenblock.bungeeperms.BungeePerms; import net.alpenblock.bungeeperms.PermissionsManager; import net.alpenblock.bungeeperms.User; -import net.william278.huskchat.getter.DataGetter; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -41,7 +40,7 @@ public BungeePermsDataGetter() { } @Override - public String getPlayerFullName(@NotNull Player player) { + public String getPlayerFullName(@NotNull OnlineUser player) { final Optional prefix = getPlayerPrefix(player); final Optional suffix = getPlayerSuffix(player); return (prefix.isPresent() ? prefix : "") + player.getName() @@ -49,12 +48,12 @@ public String getPlayerFullName(@NotNull Player player) { } @Override - public String getPlayerName(@NotNull Player player) { + public String getPlayerName(@NotNull OnlineUser player) { return player.getName(); } @Override - public Optional getPlayerPrefix(@NotNull Player player) { + public Optional getPlayerPrefix(@NotNull OnlineUser player) { try { return Optional.of(permissionsManager.getMainGroup(getUser(player)).getPrefix()); } catch (NullPointerException e) { @@ -63,7 +62,7 @@ public Optional getPlayerPrefix(@NotNull Player player) { } @Override - public Optional getPlayerSuffix(@NotNull Player player) { + public Optional getPlayerSuffix(@NotNull OnlineUser player) { try { return Optional.of(permissionsManager.getMainGroup(getUser(player)).getSuffix()); } catch (NullPointerException e) { @@ -72,7 +71,7 @@ public Optional getPlayerSuffix(@NotNull Player player) { } @Override - public Optional getPlayerGroupName(@NotNull Player player) { + public Optional getPlayerGroupName(@NotNull OnlineUser player) { try { return Optional.of(permissionsManager.getMainGroup(getUser(player)).getName()); } catch (NullPointerException e) { @@ -81,7 +80,7 @@ public Optional getPlayerGroupName(@NotNull Player player) { } @Override - public Optional getPlayerGroupDisplayName(@NotNull Player player) { + public Optional getPlayerGroupDisplayName(@NotNull OnlineUser player) { try { return Optional.of(permissionsManager.getMainGroup(getUser(player)).getDisplay()); } catch (NullPointerException e) { @@ -90,13 +89,13 @@ public Optional getPlayerGroupDisplayName(@NotNull Player player) { } @Override - public Optional getTextFromNode(@NotNull Player player, @NotNull String nodePrefix) { + public Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix) { final String prefix = nodePrefix.endsWith(".") ? nodePrefix : nodePrefix + "."; return getUser(player).getPerms().stream().filter(node -> node.startsWith(prefix)).findFirst() .map(node -> node.length() > prefix.length() ? node.substring(prefix.length()) : ""); } - private User getUser(Player player) { + private User getUser(OnlineUser player) { return permissionsManager.getUser(player.getUuid()); } } diff --git a/bungee/src/main/java/net/william278/huskchat/bungeecord/listener/BungeeListener.java b/bungee/src/main/java/net/william278/huskchat/listener/BungeeListener.java similarity index 67% rename from bungee/src/main/java/net/william278/huskchat/bungeecord/listener/BungeeListener.java rename to bungee/src/main/java/net/william278/huskchat/listener/BungeeListener.java index 2a56f70f..98677c41 100644 --- a/bungee/src/main/java/net/william278/huskchat/bungeecord/listener/BungeeListener.java +++ b/bungee/src/main/java/net/william278/huskchat/listener/BungeeListener.java @@ -17,7 +17,7 @@ * limitations under the License. */ -package net.william278.huskchat.bungeecord.listener; +package net.william278.huskchat.listener; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ChatEvent; @@ -28,11 +28,13 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.bungeecord.player.BungeePlayer; -import net.william278.huskchat.listener.PlayerListener; +import net.william278.huskchat.channel.Channel; import net.william278.huskchat.message.ChatMessage; +import net.william278.huskchat.user.BungeeUser; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + public class BungeeListener extends PlayerListener implements Listener { public BungeeListener(@NotNull HuskChat plugin) { @@ -45,10 +47,18 @@ public void onPlayerChat(ChatEvent e) { return; } - final ProxiedPlayer player = (ProxiedPlayer) e.getSender(); - boolean shouldCancel = new ChatMessage(plugin.getPlayerCache().getPlayerChannel(player.getUniqueId()), - BungeePlayer.adapt(player), e.getMessage(), plugin).dispatch(); - if (shouldCancel) { + // Verify they are in a channel + final BungeeUser player = BungeeUser.adapt((ProxiedPlayer) e.getSender(), plugin); + final Optional channel = plugin.getChannels().getChannel( + plugin.getUserCache().getPlayerChannel(player.getUuid()) + ); + if (channel.isEmpty()) { + plugin.getLocales().sendMessage(player, "error_no_channel"); + return; + } + + // Send the chat message, determine if the event should be canceled + if (new ChatMessage(channel.get(), player, e.getMessage(), plugin).dispatch()) { e.setCancelled(true); } } @@ -56,18 +66,18 @@ public void onPlayerChat(ChatEvent e) { @EventHandler(priority = EventPriority.HIGH) public void onPlayerChangeServer(ServerSwitchEvent e) { final String server = e.getPlayer().getServer().getInfo().getName(); - final BungeePlayer player = BungeePlayer.adapt(e.getPlayer()); + final BungeeUser player = BungeeUser.adapt(e.getPlayer(), plugin); this.handlePlayerSwitchServer(player, server); } @EventHandler public void onPlayerJoinNetwork(PostLoginEvent e) { - super.handlePlayerJoin(BungeePlayer.adapt(e.getPlayer())); + super.handlePlayerJoin(BungeeUser.adapt(e.getPlayer(), plugin)); } @EventHandler public void onPlayerQuitNetwork(PlayerDisconnectEvent e) { - super.handlePlayerQuit(BungeePlayer.adapt(e.getPlayer())); + super.handlePlayerQuit(BungeeUser.adapt(e.getPlayer(), plugin)); } } diff --git a/bungee/src/main/java/net/william278/huskchat/user/BungeeUser.java b/bungee/src/main/java/net/william278/huskchat/user/BungeeUser.java new file mode 100644 index 00000000..b7cccad0 --- /dev/null +++ b/bungee/src/main/java/net/william278/huskchat/user/BungeeUser.java @@ -0,0 +1,75 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.user; + +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.william278.huskchat.HuskChat; +import org.jetbrains.annotations.NotNull; + +/** + * Bungee implementation of a cross-platform {@link OnlineUser} + */ +public class BungeeUser extends OnlineUser { + private final ProxiedPlayer player; + + private BungeeUser(@NotNull ProxiedPlayer player, @NotNull HuskChat plugin) { + super(player.getName(), player.getUniqueId(), plugin); + this.player = player; + } + + /** + * Adapts a bungee {@link ProxiedPlayer} to a cross-platform {@link OnlineUser} object + * + * @param player {@link ProxiedPlayer} to adapt + * @param plugin the plugin instance + * @return The {@link OnlineUser} object + */ + @NotNull + public static BungeeUser adapt(@NotNull ProxiedPlayer player, @NotNull HuskChat plugin) { + return new BungeeUser(player, plugin); + } + + @Override + public int getPing() { + return player.getPing(); + } + + @Override + @NotNull + public String getServerName() { + return player.getServer().getInfo().getName(); + } + + @Override + public int getPlayersOnServer() { + return player.getServer().getInfo().getPlayers().size(); + } + + @Override + public boolean hasPermission(String s) { + return player.hasPermission(s); + } + + @NotNull + public ProxiedPlayer getPlayer() { + return player; + } + +} diff --git a/bungee/src/main/resources/bungee.yml b/bungee/src/main/resources/bungee.yml index cf29122e..94b49388 100644 --- a/bungee/src/main/resources/bungee.yml +++ b/bungee/src/main/resources/bungee.yml @@ -2,7 +2,7 @@ name: 'HuskChat' version: '${version}' description: '${description}' author: 'William278' -main: 'net.william278.huskchat.bungeecord.BungeeHuskChat' +main: 'net.william278.huskchat.BungeeHuskChat' softDepends: - 'LuckPerms' - 'PAPIProxyBridge' diff --git a/common/build.gradle b/common/build.gradle index cbde88df..9562a47c 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -2,27 +2,25 @@ plugins { id 'java-library' } -test { - useJUnitPlatform() -} - dependencies { - api 'commons-io:commons-io:2.15.0' - api 'dev.dejvokep:boosted-yaml:1.3.1' + api 'commons-io:commons-io:2.15.1' api 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT' - api 'net.william278:ProfanityCheckerAPI:2.0.1' + api 'net.william278:profanitycheckerapi:3.0' api 'net.william278:desertwell:2.0.4' api 'dev.vankka:mcdiscordreserializer:4.3.0' + api 'de.exlll:configlib-yaml:4.5.0' - compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'net.kyori:adventure-api:4.15.0' + compileOnly 'net.kyori:adventure-platform-api:4.3.2' compileOnly 'net.william278:papiproxybridge:1.3' - compileOnly 'net.kyori:adventure-api:4.14.0' compileOnly 'net.luckperms:api:5.4' - compileOnly 'com.github.Spicord.Spicord:spicord-common:5.3.0' + compileOnly 'com.github.Spicord.Spicord:spicord-common:v5-SNAPSHOT' compileOnly 'net.dv8tion:JDA:5.0.0-beta.4' + compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'org.projectlombok:lombok:1.18.30' + + testImplementation 'net.william278:profanitycheckerapi:3.0' + testImplementation 'net.kyori:adventure-api:4.15.0' - testImplementation 'net.william278:ProfanityCheckerAPI:2.0.1' - testImplementation 'net.kyori:adventure-api:4.14.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1' + annotationProcessor 'org.projectlombok:lombok:1.18.30' } \ No newline at end of file diff --git a/common/src/main/java/net/william278/huskchat/HuskChat.java b/common/src/main/java/net/william278/huskchat/HuskChat.java index a44ea1ad..8e6a9537 100644 --- a/common/src/main/java/net/william278/huskchat/HuskChat.java +++ b/common/src/main/java/net/william278/huskchat/HuskChat.java @@ -19,82 +19,47 @@ package net.william278.huskchat; -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.kyori.adventure.audience.Audience; import net.william278.desertwell.util.UpdateChecker; import net.william278.desertwell.util.Version; -import net.william278.huskchat.config.Locales; -import net.william278.huskchat.config.Settings; +import net.william278.huskchat.config.ConfigProvider; import net.william278.huskchat.discord.DiscordHook; import net.william278.huskchat.discord.SpicordHook; import net.william278.huskchat.discord.WebHook; -import net.william278.huskchat.event.EventDispatcher; +import net.william278.huskchat.event.EventProvider; +import net.william278.huskchat.filter.FilterProvider; import net.william278.huskchat.getter.DataGetter; import net.william278.huskchat.placeholders.PlaceholderReplacer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; +import net.william278.huskchat.util.AudiencesProvider; import org.jetbrains.annotations.NotNull; -import java.io.File; -import java.io.InputStream; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.logging.Level; -public interface HuskChat { +public interface HuskChat extends AudiencesProvider, ConfigProvider, FilterProvider, EventProvider { int SPIGOT_RESOURCE_ID = 94496; - @NotNull - Settings getSettings(); - - void setSettings(@NotNull Settings settings); - - @NotNull - Locales getLocales(); - - void setLocales(@NotNull Locales locales); - - default void loadConfig() { - try { - // Set settings - this.setSettings(new Settings(YamlDocument.create( - new File(getDataFolder(), "config.yml"), - Objects.requireNonNull(getResource("config.yml")), - GeneralSettings.builder().setUseDefaults(false).build(), - LoaderSettings.builder().setAutoUpdate(true).build(), - DumperSettings.builder().setEncoding(DumperSettings.Encoding.UNICODE).build(), - UpdaterSettings.builder().setVersioning(new BasicVersioning("config-version")).build() - ))); - this.setLocales(new Locales(this)); - } catch (Throwable e) { - log(Level.SEVERE, "Failed to load plugin config/locale files", e); - } - } - // Initialize webhook dispatcher default void loadDiscordHook() { - if (getSettings().doDiscordIntegration()) { - setDiscordHook(getSettings().useSpicord() && isPluginPresent("Spicord") + if (getSettings().getDiscord().isEnabled()) { + setDiscordHook(getSettings().getDiscord().getSpicord().isEnabled() && isPluginPresent("Spicord") ? new SpicordHook(this) : new WebHook(this)); } } @NotNull - EventDispatcher getEventDispatcher(); - - @NotNull - PlayerCache getPlayerCache(); + UserCache getUserCache(); @NotNull List getPlaceholderReplacers(); - default CompletableFuture replacePlaceholders(@NotNull Player player, @NotNull String message) { + default CompletableFuture replacePlaceholders(@NotNull OnlineUser player, @NotNull String message) { CompletableFuture future = CompletableFuture.completedFuture(message); for (PlaceholderReplacer replacer : getPlaceholderReplacers()) { future = future.thenComposeAsync(toFormat -> replacer.formatPlaceholders(toFormat, player)); @@ -118,20 +83,15 @@ default CompletableFuture replacePlaceholders(@NotNull Player player, @N @NotNull String getPlatform(); - Optional getPlayer(@NotNull UUID uuid); - - Optional findPlayer(@NotNull String username); + Optional getPlayer(@NotNull UUID uuid); - Collection getOnlinePlayers(); - - Collection getOnlinePlayersOnServer(@NotNull Player player); + Optional findPlayer(@NotNull String username); @NotNull - Audience getConsole(); - - File getDataFolder(); + Collection getOnlinePlayers(); - InputStream getResource(@NotNull String path); + @NotNull + Collection getOnlinePlayersOnServer(@NotNull OnlineUser player); boolean isPluginPresent(@NotNull String dependency); @@ -145,7 +105,7 @@ default UpdateChecker getUpdateChecker() { } default void checkForUpdates() { - if (getSettings().doCheckForUpdates()) { + if (getSettings().isCheckForUpdates()) { getUpdateChecker().check().thenAccept(checked -> { if (!checked.isUpToDate()) { log(Level.WARNING, "A new version of HuskChat is available: v" @@ -157,5 +117,4 @@ default void checkForUpdates() { void log(@NotNull Level level, @NotNull String message, @NotNull Throwable... throwable); - HuskChatAPI getAPI(); } diff --git a/common/src/main/java/net/william278/huskchat/HuskChatAPI.java b/common/src/main/java/net/william278/huskchat/HuskChatAPI.java deleted file mode 100644 index ea6e7042..00000000 --- a/common/src/main/java/net/william278/huskchat/HuskChatAPI.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat; - -import net.william278.huskchat.message.BroadcastMessage; -import net.william278.huskchat.message.ChatMessage; -import net.william278.huskchat.message.PrivateMessage; -import net.william278.huskchat.player.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -@SuppressWarnings("unused") -public class HuskChatAPI { - protected static HuskChatAPI instance; - protected final HuskChat plugin; - - protected HuskChatAPI(@NotNull HuskChat plugin) { - this.plugin = plugin; - } - - public static HuskChatAPI getInstance() { - return instance; - } - - /** - * Returns the player's current channel - */ - public String getPlayerChannel(@NotNull Player player) { - return plugin.getPlayerCache().getPlayerChannel(player.getUuid()); - } - - /** - * Sets the player's channel - */ - public void setPlayerChannel(@NotNull Player player, @NotNull String channel) { - plugin.getPlayerCache().setPlayerChannel(player.getUuid(), channel); - } - - /** - * Sends a chat message on behalf of a player - */ - public void sendChatMessage(@NotNull String targetChannelId, @NotNull Player sender, @NotNull String message) { - new ChatMessage(targetChannelId, sender, message, plugin).dispatch(); - } - - /** - * Sends a broadcast message - */ - public void sendBroadcastMessage(@NotNull Player sender, @NotNull String message) { - new BroadcastMessage(sender, message, plugin).dispatch(); - } - - /** - * Sends a private message on behalf of a player - */ - public void sendPrivateMessage(@NotNull Player sender, @NotNull List targetUsernames, @NotNull String message) { - new PrivateMessage(sender, targetUsernames, message, plugin).dispatch(); - } -} diff --git a/common/src/main/java/net/william278/huskchat/api/HuskChatAPI.java b/common/src/main/java/net/william278/huskchat/api/HuskChatAPI.java new file mode 100644 index 00000000..e384353c --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/api/HuskChatAPI.java @@ -0,0 +1,112 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.api; + +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.message.BroadcastMessage; +import net.william278.huskchat.message.ChatMessage; +import net.william278.huskchat.message.PrivateMessage; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * The HuskChat API + * + * @since 3.0 + */ +@SuppressWarnings("unused") +public class HuskChatAPI { + protected static HuskChatAPI instance; + protected final HuskChat plugin; + + protected HuskChatAPI(@NotNull HuskChat plugin) { + this.plugin = plugin; + } + + @NotNull + public static HuskChatAPI getInstance() { + return instance; + } + + /** + * Returns the player's current channel + * + * @param player The player to get the channel for + * @since 3.0 + */ + @NotNull + public String getPlayerChannel(@NotNull OnlineUser player) { + return plugin.getUserCache().getPlayerChannel(player.getUuid()); + } + + /** + * Sets the player's channel + * + * @param player The player to set the channel for + * @param channel The channel to set + * @since 3.0 + */ + public void setPlayerChannel(@NotNull OnlineUser player, @NotNull String channel) { + plugin.getUserCache().setPlayerChannel(player.getUuid(), channel); + } + + /** + * Sends a chat message on behalf of a player + * + * @param targetChannelId The ID of the channel to send the message to + * @param sender The player sending the message + * @param message The message to send + * @throws IllegalArgumentException if the target channel does not exist + * @since 3.0 + */ + public void sendChatMessage(@NotNull String targetChannelId, @NotNull OnlineUser sender, @NotNull String message) { + final Channel channel = plugin.getChannels().getChannel(targetChannelId).orElseThrow( + () -> new IllegalArgumentException("The target channel does not exist") + ); + new ChatMessage(channel, sender, message, plugin).dispatch(); + } + + /** + * Sends a broadcast message + * + * @param sender The player sending the message + * @param message The message to send + * @since 3.0 + */ + public void sendBroadcastMessage(@NotNull OnlineUser sender, @NotNull String message) { + new BroadcastMessage(sender, message, plugin).dispatch(); + } + + /** + * Sends a private message on behalf of a player + * + * @param sender The player sending the message + * @param targetUsernames The usernames of the players to send the message to + * @param message The message to send + * @since 3.0 + */ + public void sendPrivateMessage(@NotNull OnlineUser sender, @NotNull List targetUsernames, + @NotNull String message) { + new PrivateMessage(sender, targetUsernames, message, plugin).dispatch(); + } +} diff --git a/common/src/main/java/net/william278/huskchat/channel/Channel.java b/common/src/main/java/net/william278/huskchat/channel/Channel.java index 591007be..66b068ec 100644 --- a/common/src/main/java/net/william278/huskchat/channel/Channel.java +++ b/common/src/main/java/net/william278/huskchat/channel/Channel.java @@ -19,174 +19,132 @@ package net.william278.huskchat.channel; +import de.exlll.configlib.Configuration; +import lombok.*; +import net.william278.huskchat.config.Settings; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -import java.util.Set; - +import java.util.Optional; + +@Builder +@Getter +@Setter +@Configuration +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class Channel { - private final String id; - private final String format; - private final BroadcastScope broadcastScope; - private List shortcutCommands = new ArrayList<>(); - private List restrictedServers = new ArrayList<>(); - private String sendPermission; - private String receivePermission; - private boolean logMessages; - private boolean filter; + private String id; - /** - * Creates a channel with the specified ID and basic format - * - * @param id The ID of the channel - * @param format The channel format - * @param broadcastScope The {@link BroadcastScope} of this channel - */ - public Channel(@NotNull String id, @NotNull String format, @NotNull BroadcastScope broadcastScope) { - this.id = id; - this.format = format; - this.broadcastScope = broadcastScope; - } + @Builder.Default + private String format = "<%sender%> "; - /** - * The ID of the channel - */ - @NotNull - public String getId() { - return id; - } + private BroadcastScope broadcastScope; - /** - * Message format of the channel - */ - @NotNull - public String getFormat() { - return format; - } - - /** - * {@link BroadcastScope} of the channel - */ - @NotNull - public BroadcastScope getBroadcastScope() { - return broadcastScope; - } - - - /** - * A {@link Set} of shortcut commands users can execute to access the channel - */ - @NotNull - public List getShortcutCommands() { - return shortcutCommands; - } - - - /** - * A {@link ArrayList} of servers where this channel cannot be used - */ - public void setShortcutCommands(List shortcutCommands) { - this.shortcutCommands = shortcutCommands; - } - - - /** - * Permission node required to switch to and send messages to this channel - */ - @NotNull - public List getRestrictedServers() { - return restrictedServers; - } + @Builder.Default + private boolean logToConsole = true; - /** - * Permission node required to receive messages from this channel. Note that channels with a {@link BroadcastScope} of {@code PASSTHROUGH}, {@code LOCAL_PASSTHROUGH} or {@code GLOBAL_PASSTHROUGH} will pass messages to the backend server, which will not necessarily check for this node, meaning players without permission may receive channel messages., - */ - public void setRestrictedServers(List restrictedServers) { - this.restrictedServers = restrictedServers; - } - - /** - * Whether this channel should have its messages logged to console - */ - @Nullable - public String getSendPermission() { - return sendPermission; - } - - /** - * String identifier of the channel - */ - public void setSendPermission(@Nullable String sendPermission) { - this.sendPermission = sendPermission; - } - - /** - * Whether this channel should automatically apply filters to messages - */ - @Nullable - public String getReceivePermission() { - return receivePermission; - } + @Builder.Default + private List restrictedServers = new ArrayList<>(); - public void setReceivePermission(@Nullable String receivePermission) { - this.receivePermission = receivePermission; - } + @Builder.Default + private boolean filtered = true; - public boolean doLogMessages() { - return logMessages; - } + @Builder.Default + private ChannelPermissions permissions = new ChannelPermissions(); - public void setLogMessages(boolean logMessages) { - this.logMessages = logMessages; - } + @Builder.Default + @Getter(AccessLevel.NONE) + private List shortcutCommands = new ArrayList<>(); - public boolean isFilter() { - return filter; - } + @Builder + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @Setter + public static class ChannelPermissions { + @Nullable + @Builder.Default + private String send = null; + + @Nullable + @Builder.Default + private String receive = null; + + public Optional getSend() { + return Optional.ofNullable(send); + } - public void setFilter(boolean filter) { - this.filter = filter; + public Optional getReceive() { + return Optional.ofNullable(receive); + } } /** * The broadcast scope of a channel, defining how messages will be handled */ + @Getter + @AllArgsConstructor public enum BroadcastScope { /** - * Message is broadcast globally to those with permissions via the proxy + * The message is broadcast globally to those with permissions via the proxy */ GLOBAL(false), /** - * Message is broadcast via the proxy to players who have permission and are on the same server as the source + * The message is broadcast via the proxy to players who have permission and are on + * the same server as the source */ LOCAL(false), /** - * Message is not handled by the proxy and is instead passed to the backend server + * The message is not handled by the proxy and is instead passed to the backend server */ PASSTHROUGH(true), /** - * Message is broadcast via the proxy to players who have permission and are on the same server as the source and is additionally passed to the backend server + * The message is broadcast via the proxy to players who have permission and are on the same server + * as the source and is additionally passed to the backend server */ LOCAL_PASSTHROUGH(true), /** - * Message is broadcast globally to those with permissions via the proxy and is additionally passed to the backend server + * The message is broadcast globally to those with permissions via the proxy + * and is additionally passed to the backend server */ GLOBAL_PASSTHROUGH(true); - /** - * Whether the broadcast should be passed to the backend server - */ - public final boolean isPassThrough; + private final boolean passThrough; - BroadcastScope(boolean isPassThrough) { - this.isPassThrough = isPassThrough; + public boolean isOneOf(BroadcastScope... scopes) { + for (BroadcastScope scope : scopes) { + if (this == scope) { + return true; + } + } + return false; } } + + @NotNull + public List getShortcutCommands() { + return Settings.formatCommands(shortcutCommands); + } + + public boolean isServerRestricted(@NotNull String server) { + return restrictedServers.stream().anyMatch(server::equalsIgnoreCase); + } + + public boolean canUserSend(@NotNull OnlineUser user) { + return permissions.getSend().isEmpty() || user.hasPermission(permissions.getSend().get()); + } + + public boolean canUserReceive(@NotNull OnlineUser user) { + return permissions.getReceive().isEmpty() || user.hasPermission(permissions.getReceive().get()); + } + } diff --git a/common/src/main/java/net/william278/huskchat/command/BroadcastCommand.java b/common/src/main/java/net/william278/huskchat/command/BroadcastCommand.java index f58e7abc..0f0e16fa 100644 --- a/common/src/main/java/net/william278/huskchat/command/BroadcastCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/BroadcastCommand.java @@ -21,7 +21,7 @@ import net.william278.huskchat.HuskChat; import net.william278.huskchat.message.BroadcastMessage; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -30,11 +30,11 @@ public class BroadcastCommand extends CommandBase { public BroadcastCommand(@NotNull HuskChat plugin) { - super(plugin.getSettings().getBroadcastCommandAliases(), "", plugin); + super(plugin.getSettings().getBroadcastCommand().getBroadcastAliases(), "", plugin); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { if (player.hasPermission(getPermission())) { if (args.length >= 1) { StringJoiner message = new StringJoiner(" "); @@ -51,7 +51,7 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } } diff --git a/common/src/main/java/net/william278/huskchat/command/ChannelCommand.java b/common/src/main/java/net/william278/huskchat/command/ChannelCommand.java index 7c7fcc70..301863fe 100644 --- a/common/src/main/java/net/william278/huskchat/command/ChannelCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/ChannelCommand.java @@ -20,11 +20,12 @@ package net.william278.huskchat.command; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -32,18 +33,18 @@ public class ChannelCommand extends CommandBase { public ChannelCommand(@NotNull HuskChat plugin) { - super(plugin.getSettings().getChannelCommandAliases(), "", plugin); + super(plugin.getChannels().getChannelCommandAliases(), "", plugin); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { - if (player instanceof ConsolePlayer) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { + if (player instanceof ConsoleUser) { plugin.getLocales().sendMessage(player, "error_in_game_only"); return; } if (player.hasPermission(getPermission())) { if (args.length == 1) { - plugin.getPlayerCache().switchPlayerChannel(player, args[0]); + plugin.getUserCache().switchPlayerChannel(player, args[0], plugin); } else { plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); } @@ -53,27 +54,25 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { if (!player.hasPermission(getPermission())) { return List.of(); } if (args.length <= 1) { - return getChannelIdsWithSendPermission(player).stream().filter(val -> - val.toLowerCase().startsWith((args.length >= 1) ? args[0].toLowerCase() : "")) - .sorted().collect(Collectors.toList()); + return getUsableChannels(player).stream() + .filter(val -> val.toLowerCase().startsWith((args.length == 1) ? args[0].toLowerCase() : "")) + .sorted().toList(); } return List.of(); } @NotNull - public Set getChannelIdsWithSendPermission(Player player) { - final Set channelsWithPermission = new HashSet<>(); - plugin.getSettings().getChannels().forEach((id, channel) -> { - if (channel.getSendPermission() == null || player.hasPermission(channel.getSendPermission())) { - channelsWithPermission.add(channel.getId()); - } - }); - return channelsWithPermission; + @Unmodifiable + public Set getUsableChannels(@NotNull OnlineUser player) { + return plugin.getChannels().getChannels().stream() + .filter(c -> c.getPermissions().getSend().map(player::hasPermission).orElse(true)) + .map(Channel::getId) + .collect(Collectors.toSet()); } } diff --git a/common/src/main/java/net/william278/huskchat/command/CommandBase.java b/common/src/main/java/net/william278/huskchat/command/CommandBase.java index 42b4dace..1a356b0c 100644 --- a/common/src/main/java/net/william278/huskchat/command/CommandBase.java +++ b/common/src/main/java/net/william278/huskchat/command/CommandBase.java @@ -20,7 +20,7 @@ package net.william278.huskchat.command; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -47,19 +47,19 @@ public CommandBase(@NotNull List aliases, @NotNull String usage, @NotNul /** * Fires when the command is executed * - * @param player {@link Player} executing the command + * @param player {@link OnlineUser} executing the command * @param args Command arguments */ - public abstract void onExecute(@NotNull Player player, @NotNull String[] args); + public abstract void onExecute(@NotNull OnlineUser player, @NotNull String[] args); /** * What should be returned when the player attempts to TAB complete the command * - * @param player {@link Player} doing the TAB completion + * @param player {@link OnlineUser} doing the TAB completion * @param args Current command arguments * @return List of String arguments to offer TAB suggestions */ - public abstract List onTabComplete(@NotNull Player player, @NotNull String[] args); + public abstract List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args); /** * Get the primary command alias diff --git a/common/src/main/java/net/william278/huskchat/command/HuskChatCommand.java b/common/src/main/java/net/william278/huskchat/command/HuskChatCommand.java index 2e109c87..51f3259e 100644 --- a/common/src/main/java/net/william278/huskchat/command/HuskChatCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/HuskChatCommand.java @@ -25,7 +25,7 @@ import net.william278.desertwell.about.AboutMenu; import net.william278.desertwell.util.UpdateChecker; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -65,7 +65,7 @@ public HuskChatCommand(@NotNull HuskChat plugin) { } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { if (!player.hasPermission(getPermission())) { plugin.getLocales().sendMessage(player, "error_no_permission"); return; @@ -95,7 +95,7 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { if (!player.hasPermission(getPermission())) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/command/LocalSpyCommand.java b/common/src/main/java/net/william278/huskchat/command/LocalSpyCommand.java index e0b8cdb1..e4c7463e 100644 --- a/common/src/main/java/net/william278/huskchat/command/LocalSpyCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/LocalSpyCommand.java @@ -20,9 +20,9 @@ package net.william278.huskchat.command; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -33,23 +33,23 @@ public class LocalSpyCommand extends CommandBase { public LocalSpyCommand(@NotNull HuskChat plugin) { - super(plugin.getSettings().getLocalSpyCommandAliases(), "[color]", plugin); + super(plugin.getSettings().getLocalSpy().getLocalspyAliases(), "[color]", plugin); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { - if (player instanceof ConsolePlayer) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { + if (player instanceof ConsoleUser) { plugin.getLocales().sendMessage(player, "error_in_game_only"); return; } if (player.hasPermission(getPermission())) { if (args.length == 1) { - PlayerCache.SpyColor color; - Optional selectedColor = PlayerCache.SpyColor.getColor(args[0]); + UserCache.SpyColor color; + Optional selectedColor = UserCache.SpyColor.getColor(args[0]); if (selectedColor.isPresent()) { try { color = selectedColor.get(); - plugin.getPlayerCache().setLocalSpy(player, color); + plugin.getUserCache().setLocalSpy(player, color); plugin.getLocales().sendMessage(player, "local_spy_toggled_on_color", color.colorCode, color.name().toLowerCase().replaceAll("_", " ")); } catch (IOException e) { @@ -58,16 +58,16 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { return; } } - if (!plugin.getPlayerCache().isLocalSpying(player)) { + if (!plugin.getUserCache().isLocalSpying(player)) { try { - plugin.getPlayerCache().setLocalSpy(player); + plugin.getUserCache().setLocalSpy(player); plugin.getLocales().sendMessage(player, "local_spy_toggled_on"); } catch (IOException e) { plugin.log(Level.SEVERE, "Failed to save local spy state to spies file"); } } else { try { - plugin.getPlayerCache().removeLocalSpy(player); + plugin.getUserCache().removeLocalSpy(player); plugin.getLocales().sendMessage(player, "local_spy_toggled_off"); } catch (IOException e) { plugin.log(Level.SEVERE, "Failed to save local spy state to spies file"); @@ -79,7 +79,7 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/command/MessageCommand.java b/common/src/main/java/net/william278/huskchat/command/MessageCommand.java index 6772c5eb..b643d452 100644 --- a/common/src/main/java/net/william278/huskchat/command/MessageCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/MessageCommand.java @@ -21,7 +21,7 @@ import net.william278.huskchat.HuskChat; import net.william278.huskchat.message.PrivateMessage; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -30,13 +30,15 @@ public class MessageCommand extends CommandBase { public MessageCommand(@NotNull HuskChat plugin) { super( - plugin.getSettings().getMessageCommandAliases(), - plugin.getSettings().doGroupMessages() ? " " : " ", - plugin); + plugin.getSettings().getMessageCommand().getMsgAliases(), + plugin.getSettings().getMessageCommand().getGroupMessages().isEnabled() + ? " " : " ", + plugin + ); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { if (player.hasPermission(getPermission())) { if (args.length >= 2) { StringJoiner message = new StringJoiner(" "); @@ -67,10 +69,10 @@ private List getTargetPlayers(String playerList) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { if (args.length <= 1) { final ArrayList userNames = new ArrayList<>(); - for (Player connectedPlayer : plugin.getOnlinePlayers()) { + for (OnlineUser connectedPlayer : plugin.getOnlinePlayers()) { if (!player.getUuid().equals(connectedPlayer.getUuid())) { userNames.add(connectedPlayer.getName()); } diff --git a/common/src/main/java/net/william278/huskchat/command/OptOutMessageCommand.java b/common/src/main/java/net/william278/huskchat/command/OptOutMessageCommand.java index 2ef309b5..dd48b29a 100644 --- a/common/src/main/java/net/william278/huskchat/command/OptOutMessageCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/OptOutMessageCommand.java @@ -20,8 +20,8 @@ package net.william278.huskchat.command; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -35,19 +35,19 @@ public OptOutMessageCommand(@NotNull HuskChat plugin) { } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { - PlayerCache.getLastMessengers(player.getUuid()).ifPresentOrElse(lastMessengers -> { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { + UserCache.getLastMessengers(player.getUuid()).ifPresentOrElse(lastMessengers -> { if (lastMessengers.size() <= 1) { plugin.getLocales().sendMessage(player, "error_last_message_not_group"); return; } for (UUID uuid : lastMessengers) { - PlayerCache.getLastMessengers(uuid).ifPresent(last -> last.remove(player.getUuid())); + UserCache.getLastMessengers(uuid).ifPresent(last -> last.remove(player.getUuid())); } String playerList = lastMessengers.stream().flatMap(u -> plugin.getPlayer(u).stream()) - .map(Player::getName).collect(Collectors.joining(", ")); + .map(OnlineUser::getName).collect(Collectors.joining(", ")); StringBuilder builder = new StringBuilder(); int lastComma = playerList.lastIndexOf(','); builder.append(playerList, 0, lastComma); @@ -60,7 +60,7 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/command/ReplyCommand.java b/common/src/main/java/net/william278/huskchat/command/ReplyCommand.java index f6f4bf1b..b793a078 100644 --- a/common/src/main/java/net/william278/huskchat/command/ReplyCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/ReplyCommand.java @@ -21,9 +21,9 @@ import net.william278.huskchat.HuskChat; import net.william278.huskchat.message.PrivateMessage; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -31,14 +31,14 @@ public class ReplyCommand extends CommandBase { public ReplyCommand(@NotNull HuskChat plugin) { - super(plugin.getSettings().getReplyCommandAliases(), "", plugin); + super(plugin.getSettings().getMessageCommand().getReplyAliases(), "", plugin); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { if (player.hasPermission(getPermission())) { if (args.length >= 1) { - final Optional> lastMessengers = PlayerCache.getLastMessengers(player.getUuid()); + final Optional> lastMessengers = UserCache.getLastMessengers(player.getUuid()); if (lastMessengers.isEmpty()) { plugin.getLocales().sendMessage(player, "error_reply_no_messages"); return; @@ -46,8 +46,8 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { final ArrayList lastPlayers = new ArrayList<>(); for (UUID lastMessenger : lastMessengers.get()) { - if (ConsolePlayer.isConsolePlayer(lastMessenger)) { - lastPlayers.add(ConsolePlayer.create(plugin).getName()); + if (ConsoleUser.isConsolePlayer(lastMessenger)) { + lastPlayers.add(ConsoleUser.wrap(plugin).getName()); } else { plugin.getPlayer(lastMessenger).ifPresent(onlineMessenger -> lastPlayers.add(onlineMessenger.getName())); } @@ -84,7 +84,7 @@ public String getPermission() { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/command/ShortcutCommand.java b/common/src/main/java/net/william278/huskchat/command/ShortcutCommand.java index c30efc0e..7c9fa3ac 100644 --- a/common/src/main/java/net/william278/huskchat/command/ShortcutCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/ShortcutCommand.java @@ -22,11 +22,12 @@ import net.william278.huskchat.HuskChat; import net.william278.huskchat.channel.Channel; import net.william278.huskchat.message.ChatMessage; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; +import java.util.Optional; import java.util.StringJoiner; public class ShortcutCommand extends CommandBase { @@ -38,30 +39,34 @@ public ShortcutCommand(@NotNull String command, @NotNull String channelId, @NotN } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { if (player.hasPermission(getPermission())) { if (args.length == 0) { // Console can't chat in the same way as players can, it can only use commands. // So no need to allow it to switch channels. - if (player instanceof ConsolePlayer) { + if (player instanceof ConsoleUser) { plugin.getLocales().sendMessage(player, "error_console_switch_channels"); return; } - plugin.getPlayerCache().switchPlayerChannel(player, channelId); + plugin.getUserCache().switchPlayerChannel(player, channelId, plugin); } else { StringJoiner message = new StringJoiner(" "); for (String arg : args) { message.add(arg); } - Channel channel = plugin.getSettings().getChannels().get(channelId); + final Optional optionalChannel = plugin.getChannels().getChannel(channelId); + if (optionalChannel.isEmpty()) { + plugin.getLocales().sendMessage(player, "error_no_channel"); + return; + } - if (channel.getBroadcastScope().isPassThrough) { + final Channel channel = optionalChannel.get(); + if (channel.getBroadcastScope().isPassThrough()) { plugin.getLocales().sendMessage(player, "error_passthrough_shortcut_command"); return; } - - new ChatMessage(channelId, player, message.toString(), plugin).dispatch(); + new ChatMessage(channel, player, message.toString(), plugin).dispatch(); } } else { plugin.getLocales().sendMessage(player, "error_no_permission"); @@ -75,7 +80,7 @@ public String getPermission() { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/command/SocialSpyCommand.java b/common/src/main/java/net/william278/huskchat/command/SocialSpyCommand.java index 1ec7c02c..727ba8f5 100644 --- a/common/src/main/java/net/william278/huskchat/command/SocialSpyCommand.java +++ b/common/src/main/java/net/william278/huskchat/command/SocialSpyCommand.java @@ -20,9 +20,9 @@ package net.william278.huskchat.command; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -33,23 +33,23 @@ public class SocialSpyCommand extends CommandBase { public SocialSpyCommand(@NotNull HuskChat plugin) { - super(plugin.getSettings().getSocialSpyCommandAliases(), "[color]", plugin); + super(plugin.getSettings().getSocialSpy().getSocialspyAliases(), "[color]", plugin); } @Override - public void onExecute(@NotNull Player player, @NotNull String[] args) { - if (player instanceof ConsolePlayer) { + public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { + if (player instanceof ConsoleUser) { plugin.getLocales().sendMessage(player, "error_in_game_only"); return; } if (player.hasPermission(getPermission())) { if (args.length == 1) { - PlayerCache.SpyColor color; - Optional selectedColor = PlayerCache.SpyColor.getColor(args[0]); + UserCache.SpyColor color; + Optional selectedColor = UserCache.SpyColor.getColor(args[0]); if (selectedColor.isPresent()) { try { color = selectedColor.get(); - plugin.getPlayerCache().setSocialSpy(player, color); + plugin.getUserCache().setSocialSpy(player, color); plugin.getLocales().sendMessage(player, "social_spy_toggled_on_color", color.colorCode, color.name().toLowerCase().replaceAll("_", " ")); } catch (IOException e) { @@ -58,16 +58,16 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { return; } } - if (!plugin.getPlayerCache().isSocialSpying(player)) { + if (!plugin.getUserCache().isSocialSpying(player)) { try { - plugin.getPlayerCache().setSocialSpy(player); + plugin.getUserCache().setSocialSpy(player); plugin.getLocales().sendMessage(player, "social_spy_toggled_on"); } catch (IOException e) { plugin.log(Level.SEVERE, "Failed to save social spy state to spies file"); } } else { try { - plugin.getPlayerCache().removeSocialSpy(player); + plugin.getUserCache().removeSocialSpy(player); plugin.getLocales().sendMessage(player, "social_spy_toggled_off"); } catch (IOException e) { plugin.log(Level.SEVERE, "Failed to save social spy state to spies file"); @@ -79,7 +79,7 @@ public void onExecute(@NotNull Player player, @NotNull String[] args) { } @Override - public List onTabComplete(@NotNull Player player, @NotNull String[] args) { + public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { return List.of(); } diff --git a/common/src/main/java/net/william278/huskchat/config/Channels.java b/common/src/main/java/net/william278/huskchat/config/Channels.java new file mode 100644 index 00000000..5d8702e4 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/config/Channels.java @@ -0,0 +1,121 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.config; + +import de.exlll.configlib.Comment; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.channel.Channel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Class for loading and storing {@link Channel}s + */ +@SuppressWarnings("FieldMayBeFinal") +@Getter +@Configuration +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Channels { + + static final String CONFIG_HEADER = """ + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ HuskChat - Channels ┃ + ┃ Developed by William278 ┃ + ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + ┣╸ Information: https://william278.net/project/huskchat/ + ┗╸ Channels Help: https://william278.net/docs/huskchat/channels/"""; + + @Comment("The default chat channel players are placed in (can be overridden by server_default_channels)") + private String defaultChannel = "global"; + + @Comment("Map of server names to a channel players will be automatically moved into when they join that server") + private Map serverDefaultChannels = Map.of("example", "global"); + + @Comment("The format of log messages (applies to channels with logging enabled)") + private String channelLogFormat = "[CHAT] [%channel%] %sender%: "; + + @Comment("Aliases for the /channel command") + @Getter(AccessLevel.NONE) + private List channelCommandAliases = List.of("channel", "c"); + + @Comment("Channel definitions") + private List channels = List.of( + // Local channel + Channel.builder() + .id("local") + .format("%fullname%&r&f: ") + .broadcastScope(Channel.BroadcastScope.LOCAL) + .shortcutCommands(List.of("/local", "/l")) + .build(), + + // Global channel + Channel.builder() + .id("global") + .format("�fb9a&[G]&r&f %fullname%&r&f: ") + .broadcastScope(Channel.BroadcastScope.GLOBAL) + .shortcutCommands(List.of("/global", "/g")) + .build(), + + // Staff channel + Channel.builder() + .id("staff") + .format("&e[Staff] %name%: &7") + .broadcastScope(Channel.BroadcastScope.GLOBAL) + .filtered(false) + .permissions(Channel.ChannelPermissions.builder() + .send("huskchat.channel.staff.send") + .receive("huskchat.channel.staff.receive") + .build()) + .shortcutCommands(List.of("/staff", "/sc")) + .build(), + + // HelpOp channel + Channel.builder() + .id("helpop") + .format("�fb9a&[HelpOp] %name%:&7") + .broadcastScope(Channel.BroadcastScope.GLOBAL) + .filtered(false) + .permissions(Channel.ChannelPermissions.builder() + .receive("huskchat.channel.helpop.receive") + .build()) + .shortcutCommands(List.of("/helpop", "/helpme")) + .build() + ); + + public Optional getChannel(@Nullable String channelId) { + if (channelId == null) { + return Optional.empty(); + } + return channels.stream().filter(channel -> channel.getId().equalsIgnoreCase(channelId)).findFirst(); + } + + @NotNull + public List getChannelCommandAliases() { + return Settings.formatCommands(channelCommandAliases); + } + +} diff --git a/common/src/main/java/net/william278/huskchat/config/ConfigProvider.java b/common/src/main/java/net/william278/huskchat/config/ConfigProvider.java new file mode 100644 index 00000000..7afbe527 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/config/ConfigProvider.java @@ -0,0 +1,209 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.config; + +import de.exlll.configlib.NameFormatters; +import de.exlll.configlib.YamlConfigurationProperties; +import de.exlll.configlib.YamlConfigurationStore; +import de.exlll.configlib.YamlConfigurations; +import net.william278.huskchat.HuskChat; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Level; + +/** + * Interface for getting and setting data from plugin configuration files + * + * @since 3.0 + */ +public interface ConfigProvider { + + @NotNull + YamlConfigurationProperties.Builder YAML_CONFIGURATION_PROPERTIES = YamlConfigurationProperties.newBuilder() + .charset(StandardCharsets.UTF_8) + .setNameFormatter(NameFormatters.LOWER_UNDERSCORE); + + default void loadConfig() { + loadSettings(); + loadChannels(); + loadFilterSettings(); + loadLocales(); + } + + /** + * Get the plugin settings, read from the config file + * + * @return the plugin settings + * @since 3.0 + */ + @NotNull + Settings getSettings(); + + /** + * Set the plugin settings + * + * @param settings The settings to set + * @since 3.0 + */ + void setSettings(@NotNull Settings settings); + + /** + * Load the plugin settings from the config file + * + * @since 3.0 + */ + default void loadSettings() { + setSettings(YamlConfigurations.update( + getConfigDirectory().resolve("config.yml"), + Settings.class, + YAML_CONFIGURATION_PROPERTIES.header(Settings.CONFIG_HEADER).build() + )); + } + + /** + * Get the channel settings, read from the config file + * + * @return the channel settings + * @since 3.0 + */ + @NotNull + Channels getChannels(); + + /** + * Set the plugin channel settings + * + * @param channels The channel settings to set + * @since 3.0 + */ + void setChannels(@NotNull Channels channels); + + /** + * Load the plugin channel settings from the config file + * + * @since 3.0 + */ + default void loadChannels() { + setChannels(YamlConfigurations.update( + getConfigDirectory().resolve("channels.yml"), + Channels.class, + YAML_CONFIGURATION_PROPERTIES.header(Channels.CONFIG_HEADER).build() + )); + } + + + /** + * Get the filter settings, read from the config file + * + * @return the Filter settings + * @since 3.0 + */ + @NotNull + Filters getFilterSettings(); + + /** + * Set the plugin filter settings + * + * @param Filters The Filter settings to set + * @since 3.0 + */ + void setFilterSettings(@NotNull Filters Filters); + + /** + * Load the plugin Filter settings from the config file + * + * @since 3.0 + */ + default void loadFilterSettings() { + setFilterSettings(YamlConfigurations.update( + getConfigDirectory().resolve("filters.yml"), + Filters.class, + YAML_CONFIGURATION_PROPERTIES.header(Filters.CONFIG_HEADER).build() + )); + } + + /** + * Get the locales for the plugin + * + * @return the locales for the plugin + * @since 3.0 + */ + @NotNull + Locales getLocales(); + + /** + * Set the locales for the plugin + * + * @param locales The locales to set + * @since 3.0 + */ + void setLocales(@NotNull Locales locales); + + /** + * Load the locales from the config file + * + * @since 3.0 + */ + default void loadLocales() { + final YamlConfigurationStore store = new YamlConfigurationStore<>( + Locales.class, YAML_CONFIGURATION_PROPERTIES.header(Locales.CONFIG_HEADER).build() + ); + // Read existing locales if present + final Path path = getConfigDirectory().resolve(String.format("messages-%s.yml", getSettings().getLanguage())); + if (Files.exists(path)) { + setLocales(store.load(path)); + return; + } +String a = String.format("locales/%s.yml", getSettings().getLanguage()); + // Otherwise, save and read the default locales + try (InputStream input = getResource(String.format("locales/%s.yml", getSettings().getLanguage()))) { + final Locales locales = store.read(input); + store.save(locales, path); + setLocales(locales); + } catch (Throwable e) { + getPlugin().log(Level.SEVERE, "An error occurred loading the locales (invalid lang code?)", e); + } + } + + /** + * Get a plugin resource + * + * @param name The name of the resource + * @return the resource, if found + * @since 3.0 + */ + InputStream getResource(@NotNull String name); + + /** + * Get the plugin config directory + * + * @return the plugin config directory + * @since 1.0 + */ + @NotNull + Path getConfigDirectory(); + + @NotNull + HuskChat getPlugin(); + +} \ No newline at end of file diff --git a/common/src/main/java/net/william278/huskchat/config/Filters.java b/common/src/main/java/net/william278/huskchat/config/Filters.java new file mode 100644 index 00000000..0bcf1c0b --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/config/Filters.java @@ -0,0 +1,72 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.config; + +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.filter.ChatFilter; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class for loading and storing Chat Filters + */ +@SuppressWarnings("FieldMayBeFinal") +@Getter +@Configuration +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Filters { + + static final String CONFIG_HEADER = """ + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ HuskChat - Channels ┃ + ┃ Developed by William278 ┃ + ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + ┣╸ Information: https://william278.net/project/huskchat/ + ┗╸ Channels Help: https://william278.net/docs/huskchat/channels/"""; + + private Map filters = new HashMap<>(Map.of( + ChatFilter.Type.ADVERTISING, ChatFilter.Type.ADVERTISING.getDefaultSettings(), + ChatFilter.Type.CAPS, ChatFilter.Type.CAPS.getDefaultSettings(), + ChatFilter.Type.REPEAT, ChatFilter.Type.REPEAT.getDefaultSettings(), + ChatFilter.Type.SPAM, ChatFilter.Type.SPAM.getDefaultSettings(), + ChatFilter.Type.PROFANITY, ChatFilter.Type.PROFANITY.getDefaultSettings(), + ChatFilter.Type.ASCII, ChatFilter.Type.ASCII.getDefaultSettings(), + ChatFilter.Type.REGEX, ChatFilter.Type.REGEX.getDefaultSettings() + )); + + private Map replacers = new HashMap<>(Map.of( + ChatFilter.Type.EMOJI, ChatFilter.Type.EMOJI.getDefaultSettings() + )); + + public boolean isFilterEnabled(@NotNull ChatFilter.Type type) { + return filters.get(type).isEnabled(); + } + + public boolean isReplacerEnabled(@NotNull ChatFilter.Type type) { + return replacers.get(type).isEnabled(); + } + + +} diff --git a/common/src/main/java/net/william278/huskchat/config/Locales.java b/common/src/main/java/net/william278/huskchat/config/Locales.java index acb47b92..541f01ee 100644 --- a/common/src/main/java/net/william278/huskchat/config/Locales.java +++ b/common/src/main/java/net/william278/huskchat/config/Locales.java @@ -19,52 +19,52 @@ package net.william278.huskchat.config; +import de.exlll.configlib.Configuration; import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDownParser; -import dev.dejvokep.boostedyaml.YamlDocument; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextColor; import net.william278.huskchat.HuskChat; import net.william278.huskchat.channel.Channel; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.InputStream; import java.util.*; -import java.util.logging.Level; +@SuppressWarnings("FieldMayBeFinal") +@Getter +@Configuration +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class Locales { - private final HuskChat plugin; - private final Map locales = new LinkedHashMap<>(); + static final String CONFIG_HEADER = """ + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ HuskChat - Locales ┃ + ┃ Developed by William278 ┃ + ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + ┣╸ See plugin about menu for international locale credits + ┣╸ Formatted in MineDown: https://github.com/Phoenix616/MineDown + ┗╸ Translate HuskClaims: https://william278.net/docs/huskchat/translations"""; + private static final String SILENT_JOIN_PERMISSION = "huskchat.silent_join"; private static final String SILENT_QUIT_PERMISSION = "huskchat.silent_quit"; + static final String DEFAULT_LOCALE = "en-gb"; - public Locales(@NotNull HuskChat plugin) { - this.plugin = plugin; - final String languagePath = "locales/" + plugin.getSettings().getLanguage() + ".yml"; - try (InputStream stream = Objects.requireNonNull(plugin.getResource(languagePath))) { - final YamlDocument document = YamlDocument.create(new File(plugin.getDataFolder(), - "messages-" + plugin.getSettings().getLanguage() + ".yml"), stream); - locales.clear(); - for (String messageId : document.getRoutesAsStrings(false)) { - locales.put(messageId, document.getString(messageId, "")); - } - } catch (Throwable e) { - plugin.log(Level.SEVERE, "Failed to load messages file", e); - } - } + // The raw set of locales loaded from yaml + Map locales = new TreeMap<>(); @Nullable public String getRawLocale(@NotNull String id) { return locales.get(id); } - public void sendMessage(@NotNull Player player, @NotNull String id, @NotNull String... replacements) { + public void sendMessage(@NotNull OnlineUser player, @NotNull String id, @NotNull String... replacements) { String locale = getRawLocale(id); // Don't send empty messages @@ -83,8 +83,8 @@ public void sendMessage(@NotNull Player player, @NotNull String id, @NotNull Str player.sendMessage(new MineDown(locale)); } - public void sendChannelMessage(@NotNull Player target, @NotNull Player sender, @NotNull Channel channel, - @NotNull String message) { + public void sendChannelMessage(@NotNull OnlineUser target, @NotNull OnlineUser sender, @NotNull Channel channel, + @NotNull String message, @NotNull HuskChat plugin) { plugin.replacePlaceholders(sender, channel.getFormat()).thenAccept(replaced -> { final Component format = new MineDown(replaced).toComponent(); final TextComponent.Builder builder = Component.text().append(format); @@ -99,10 +99,11 @@ public void sendChannelMessage(@NotNull Player target, @NotNull Player sender, @ }); } - public void sendOutboundPrivateMessage(@NotNull Player sender, @NotNull List recipients, @NotNull String message) { + public void sendOutboundPrivateMessage(@NotNull OnlineUser sender, @NotNull List recipients, + @NotNull String message, @NotNull HuskChat plugin) { plugin.replacePlaceholders(recipients.get(0), recipients.size() == 1 - ? plugin.getSettings().getOutboundMessageFormat() - : plugin.getSettings().getGroupOutboundMessageFormat() + ? plugin.getSettings().getMessageCommand().getFormat().getOutbound() + : plugin.getSettings().getMessageCommand().getFormat().getGroupOutbound() ).thenAccept(replaced -> { if (recipients.size() > 1) { replaced = replaced.replace("%group_amount_subscript%", superscriptNumber(recipients.size() - 1)) @@ -142,10 +143,11 @@ private TextColor getFormatColor(@NotNull Component component) { return color; } - public void sendInboundPrivateMessage(List recipients, Player sender, String message) { + public void sendInboundPrivateMessage(@NotNull List recipients, @NotNull OnlineUser sender, + @NotNull String message, @NotNull HuskChat plugin) { plugin.replacePlaceholders(sender, recipients.size() == 1 - ? plugin.getSettings().getInboundMessageFormat() - : plugin.getSettings().getGroupInboundMessageFormat() + ? plugin.getSettings().getMessageCommand().getFormat().getInbound() + : plugin.getSettings().getMessageCommand().getFormat().getGroupInbound() ).thenAccept(replaced -> { if (recipients.size() > 1) { replaced = replaced.replace("%group_amount_subscript%", superscriptNumber(recipients.size() - 1)) @@ -164,15 +166,15 @@ public void sendInboundPrivateMessage(List recipients, Player sender, St builder.append(Component.text(message).color(getFormatColor(format))); } - for (final Player recipient : recipients) { + for (final OnlineUser recipient : recipients) { recipient.sendMessage(builder.build()); } }); } - public void sendLocalSpy(@NotNull Player spy, @NotNull PlayerCache.SpyColor spyColor, @NotNull Player sender, - @NotNull Channel channel, @NotNull String message) { - plugin.replacePlaceholders(sender, plugin.getSettings().getLocalSpyFormat()) + public void sendLocalSpy(@NotNull OnlineUser spy, @NotNull UserCache.SpyColor spyColor, @NotNull OnlineUser sender, + @NotNull Channel channel, @NotNull String message, @NotNull HuskChat plugin) { + plugin.replacePlaceholders(sender, plugin.getSettings().getLocalSpy().getFormat()) .thenAccept(replaced -> { final TextComponent.Builder componentBuilder = Component.text() .append(new MineDown(replaced.replace("%spy_color%", spyColor.colorCode) @@ -182,10 +184,11 @@ public void sendLocalSpy(@NotNull Player spy, @NotNull PlayerCache.SpyColor spyC }); } - public void sendSocialSpy(@NotNull Player spy, @NotNull PlayerCache.SpyColor spyColor, @NotNull Player sender, - @NotNull List receivers, @NotNull String message) { + public void sendSocialSpy(@NotNull OnlineUser spy, @NotNull UserCache.SpyColor spyColor, @NotNull OnlineUser sender, + @NotNull List receivers, @NotNull String message, @NotNull HuskChat plugin) { plugin.replacePlaceholders(sender, receivers.size() == 1 - ? plugin.getSettings().getSocialSpyFormat() : plugin.getSettings().getSocialSpyGroupFormat() + ? plugin.getSettings().getSocialSpy().getFormat() + : plugin.getSettings().getSocialSpy().getGroupFormat() .replace("%sender_", "%") ).thenAccept(senderReplaced -> plugin.replacePlaceholders(receivers.get(0), senderReplaced .replace("%receiver_", "%") @@ -202,38 +205,32 @@ public void sendSocialSpy(@NotNull Player spy, @NotNull PlayerCache.SpyColor spy })); } - public void sendFormattedBroadcastMessage(@NotNull Player player, @NotNull String message) { - final TextComponent.Builder componentBuilder = Component.text(); - componentBuilder.append(new MineDown(plugin.getSettings().getBroadcastMessageFormat()).toComponent()); - componentBuilder.append(new MineDown(message).disable(MineDownParser.Option.ADVANCED_FORMATTING).toComponent()); - player.sendMessage(componentBuilder.build()); - } - - public void sendJoinMessage(@NotNull Player player) { + public void sendJoinMessage(@NotNull OnlineUser player, @NotNull HuskChat plugin) { if (player.hasPermission(SILENT_JOIN_PERMISSION)) { return; } plugin.replacePlaceholders(player, plugin.getDataGetter().getTextFromNode(player, "huskchat.join_message") - .orElse(plugin.getSettings().getJoinMessageFormat())) - .thenAccept(replaced -> sendJoinQuitMessage(player, new MineDown(replaced).toComponent())); + .orElse(plugin.getSettings().getJoinAndQuitMessages().getJoin().getFormat())) + .thenAccept(replaced -> sendJoinQuitMessage(player, new MineDown(replaced).toComponent(), plugin)); } - public void sendQuitMessage(@NotNull Player player) { + public void sendQuitMessage(@NotNull OnlineUser player, @NotNull HuskChat plugin) { if (player.hasPermission(SILENT_QUIT_PERMISSION)) { return; } plugin.replacePlaceholders(player, plugin.getDataGetter().getTextFromNode(player, "huskchat.quit_message") - .orElse(plugin.getSettings().getQuitMessageFormat())) - .thenAccept(replaced -> sendJoinQuitMessage(player, new MineDown(replaced).toComponent())); + .orElse(plugin.getSettings().getJoinAndQuitMessages().getQuit().getFormat())) + .thenAccept(replaced -> sendJoinQuitMessage(player, new MineDown(replaced).toComponent(), plugin)); } // Dispatch a join/quit message to the correct server - private void sendJoinQuitMessage(@NotNull Player player, @NotNull Component component) { + private void sendJoinQuitMessage(@NotNull OnlineUser player, @NotNull Component component, + @NotNull HuskChat plugin) { boolean local = List.of(Channel.BroadcastScope.LOCAL, Channel.BroadcastScope.LOCAL_PASSTHROUGH) - .contains(plugin.getSettings().getJoinQuitBroadcastScope()); - for (Player online : plugin.getOnlinePlayers()) { + .contains(plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope()); + for (OnlineUser online : plugin.getOnlinePlayers()) { if (local && !online.getServerName().equals(player.getServerName())) { continue; } @@ -243,9 +240,9 @@ private void sendJoinQuitMessage(@NotNull Player player, @NotNull Component comp // Returns a newline-separated list of player names @NotNull - public final String getGroupMemberList(@NotNull List players, @NotNull String delimiter) { + public final String getGroupMemberList(@NotNull List players, @NotNull String delimiter) { final StringJoiner memberList = new StringJoiner(delimiter); - for (Player player : players) { + for (OnlineUser player : players) { memberList.add(player.getName()); } return memberList.toString(); diff --git a/common/src/main/java/net/william278/huskchat/config/Settings.java b/common/src/main/java/net/william278/huskchat/config/Settings.java index 7039f7a5..37659a84 100644 --- a/common/src/main/java/net/william278/huskchat/config/Settings.java +++ b/common/src/main/java/net/william278/huskchat/config/Settings.java @@ -19,714 +19,286 @@ package net.william278.huskchat.config; -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.block.implementation.Section; +import de.exlll.configlib.Comment; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; import net.william278.huskchat.channel.Channel; -import net.william278.huskchat.discord.WebHook; -import net.william278.huskchat.filter.*; -import net.william278.huskchat.replacer.EmojiReplacer; +import net.william278.huskchat.discord.DiscordHook; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; -import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * Class for loading and storing plugin settings and {@link Channel channels} + * Class for loading and storing plugin settings */ +@SuppressWarnings("FieldMayBeFinal") +@Getter +@Configuration +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class Settings { - // Top level options - private String language; - private boolean checkForUpdates; + static final String CONFIG_HEADER = """ + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ HuskChat - Config ┃ + ┃ Developed by William278 ┃ + ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + ┣╸ Information: https://william278.net/project/huskchat/ + ┣╸ Config Help: https://william278.net/docs/huskchat/config-files/ + ┗╸ Documentation: https://william278.net/docs/huskchat/"""; + @Comment("Locale of the default language file to use. Docs: https://william278.net/docs/huskclaims/translations") + private String language = Locales.DEFAULT_LOCALE; - // Placeholder support - private boolean doPlaceholderAPI; - private long papiProxyBridgeCacheTime; + @Comment("Whether to automatically check for plugin updates on startup") + private boolean checkForUpdates = true; + @Comment("Whether to handle chat packets directly for better 1.19+ support (may cause rare compatibility issues)") + private boolean usePacketListening = true; - // Channel config - private String defaultChannel; - private HashMap serverDefaultChannels; - private HashMap channels; - private String channelLogFormat; - private List channelCommandAliases; + @Comment("Placeholder settings") + private PlaceholderSettings placeholder = new PlaceholderSettings(); + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class PlaceholderSettings { + @Comment("Use PlaceholderAPI. If you're on Bungee/Velocity, this requires PAPIProxyBridge installed") + private boolean usePapi = true; - // Message command config - private boolean doMessageCommand; - private boolean doGroupMessages; - private int maxGroupMessageSize; - private List messageCommandAliases; - private List replyCommandAliases; - private String inboundMessageFormat; - private String outboundMessageFormat; - private String groupInboundMessageFormat; - private String groupOutboundMessageFormat; - private boolean logPrivateMessages; - private boolean censorPrivateMessages; - private String messageLogFormat; - private List messageCommandRestrictedServers; - - // Social spy - private boolean doSocialSpyCommand; - private String socialSpyFormat; - private String socialSpyGroupFormat; - private List socialSpyCommandAliases; - - - // Local spy - private boolean doLocalSpyCommand; - private String localSpyFormat; - private List excludedLocalSpyChannels; - private List localSpyCommandAliases; - - - // Broadcast command - private boolean doBroadcastCommand; - private List broadcastCommandAliases; - private String broadcastMessageFormat; - private boolean logBroadcasts; - private String broadcastLogFormat; - - - // Chat filters - private Map> chatFilters; - - // Join and Quit messages - private boolean doJoinMessages; - private String joinMessageFormat; - private boolean doQuitMessages; - private String quitMessageFormat; - private Channel.BroadcastScope joinQuitBroadcastScope; - - - // Discord integration - private boolean doDiscordIntegration; - private Map webhookUrls; - private WebHook.Format webhookFormat; - private boolean useSpicord; - private String discordUsernameFormat; - private Map spicordReceiveChannelMap; - private Map spicordSendChannelMap; - - // Server names - private Map serverNameReplacement; - - - public Settings(@NotNull YamlDocument configFile) { - this.loadConfig(configFile); + @Comment("If using PAPIProxyBridge, how long to cache placeholders for (in milliseconds)") + private long cacheTime = 3000; } - private void loadConfig(@NotNull YamlDocument configFile) { - // Language file - language = configFile.getString("language", "en-gb"); - checkForUpdates = configFile.getBoolean("check_for_updates", true); - - // Placeholders - doPlaceholderAPI = configFile.getBoolean("placeholders.use_papi", true); - papiProxyBridgeCacheTime = configFile.getLong("placeholders.cache_time", 1000L); - - // Channels - defaultChannel = configFile.getString("default_channel", "global"); - channelLogFormat = configFile.getString("channel_log_format", "[CHAT] [%channel%] %sender%: "); - channels = new LinkedHashMap<>(); - channels.putAll(fetchChannels(configFile)); - serverDefaultChannels = getServerDefaultChannels(configFile); - channelCommandAliases = (configFile.contains("channel_command_aliases")) ? - getCommandsFromList(configFile.getStringList("channel_command_aliases")) : - Collections.singletonList("channel"); - - // Message command options - doMessageCommand = configFile.getBoolean("message_command.enabled", true); - doGroupMessages = configFile.getBoolean("message_command.group_messages.enabled", true); - maxGroupMessageSize = configFile.getInt("message_command.group_messages.max_size", 5); - inboundMessageFormat = configFile.getString("message_command.format.inbound", - "�fb9a&%name% &8→ �fb9a&You&8: &f"); - outboundMessageFormat = configFile.getString("message_command.format.outbound", - "�fb9a&You &8→ �fb9a&%name%&8 &f"); - groupInboundMessageFormat = configFile.getString("message_command.format.group_inbound", - "�fb9a&%name% &8→ �fb9a&You[₍₊%group_amount_subscript%₎](gray show_text=&7%group_members%)&8: &f"); - groupOutboundMessageFormat = configFile.getString("message_command.format.group_outbound", - "�fb9a&You &8→ �fb9a&%name%[₍₊%group_amount_subscript%₎](gray show_text=&7%group_members%)&8: &f"); - logPrivateMessages = configFile.getBoolean("message_command.log_to_console", true); - censorPrivateMessages = configFile.getBoolean("message_command.censor", false); - messageLogFormat = configFile.getString("message_command.log_format", "[MSG] [%sender% -> %receiver%]: "); - messageCommandRestrictedServers = configFile.getStringList("message_command.restricted_servers"); - messageCommandAliases = (configFile.contains("message_command.msg_aliases")) ? - getCommandsFromList(configFile.getStringList("message_command.msg_aliases")) : - Collections.singletonList("msg"); - replyCommandAliases = new ArrayList<>(); - replyCommandAliases = (configFile.contains("message_command.reply_aliases")) ? - getCommandsFromList(configFile.getStringList("message_command.reply_aliases")) : - Collections.singletonList("reply"); - - // Social spy - doSocialSpyCommand = configFile.getBoolean("social_spy.enabled", true); - socialSpyFormat = configFile.getString("social_spy.format", "&e[Spy] &7%sender% &8→ &7%receiver%:%spy_color% "); - socialSpyGroupFormat = configFile.getString("social_spy.group_format", "&e[Spy] &7%name% &8→ &7%receiver_name%[₍₊%group_amount_subscript%₎](gray show_text=&7%group_members%):%spy_color% "); - socialSpyCommandAliases = (configFile.contains("social_spy.socialspy_aliases")) ? - getCommandsFromList(configFile.getStringList("social_spy.socialspy_aliases")) : - Collections.singletonList("socialspy"); - - // Local spy - doLocalSpyCommand = configFile.getBoolean("local_spy.enabled", true); - localSpyFormat = configFile.getString("local_spy.format", "&e[Spy] &7[%channel%] %name%&8:%spy_color% "); - excludedLocalSpyChannels = (configFile.contains("local_spy.excluded_local_channels")) ? configFile.getStringList("local_spy.excluded_local_channels") : new ArrayList<>(); - localSpyCommandAliases = (configFile.contains("local_spy.localspy_aliases")) ? - getCommandsFromList(configFile.getStringList("local_spy.localspy_aliases")) : - Collections.singletonList("localspy"); - - // Broadcast command - doBroadcastCommand = configFile.getBoolean("broadcast_command.enabled", true); - broadcastCommandAliases = (configFile.contains("broadcast_command.broadcast_aliases")) ? - getCommandsFromList(configFile.getStringList("broadcast_command.broadcast_aliases")) : - Collections.singletonList("broadcast"); - broadcastMessageFormat = configFile.getString("broadcast_command.format", "&6[Broadcast]&e "); - logBroadcasts = configFile.getBoolean("broadcast_command.log_to_console", true); - broadcastLogFormat = configFile.getString("broadcast_command.log_format", "[BROADCAST]: "); - - // Chat filters - chatFilters = fetchChatFilters(configFile); - - // Join and Quit messages - doJoinMessages = configFile.getBoolean("join_and_quit_messages.join.enabled", false); - joinMessageFormat = configFile.getString("join_and_quit_messages.join.format", "&e%name% joined the network"); - doQuitMessages = configFile.getBoolean("join_and_quit_messages.quit.enabled", false); - quitMessageFormat = configFile.getString("join_and_quit_messages.quit.format", "&e%name% left the network"); - joinQuitBroadcastScope = Channel.BroadcastScope.valueOf(configFile.getString("join_and_quit_messages.broadcast_scope", "GLOBAL").toUpperCase(Locale.ENGLISH)); - - // Discord integration - webhookFormat = WebHook.Format.getMessageFormat(configFile.getString("discord.format_style", "inline")) - .orElse(WebHook.Format.INLINE); - webhookUrls = fetchWebhookUrls(configFile); - doDiscordIntegration = configFile.getBoolean("discord.enabled", false); - useSpicord = configFile.getBoolean("discord.spicord.enabled", true); - discordUsernameFormat = configFile.getString("discord.username_format", "@%discord_handle%"); - spicordReceiveChannelMap = new LinkedHashMap<>(); - if (configFile.contains("discord.spicord.receive_channel_map")) { - for (String channelID : configFile.getSection("discord.spicord.receive_channel_map").getRoutesAsStrings(false)) { - spicordReceiveChannelMap.put(channelID, configFile.getString("discord.spicord.receive_channel_map." + channelID)); - } - } - spicordSendChannelMap = new LinkedHashMap<>(); - if (configFile.contains("discord.spicord.send_channel_map")) { - for (String channelID : configFile.getSection("discord.spicord.send_channel_map").getRoutesAsStrings(false)) { - spicordSendChannelMap.put(channelID, configFile.getString("discord.spicord.send_channel_map." + channelID)); - } - } + @Comment("Message comamnd settings") + private MessageSettings messageCommand = new MessageSettings(); - // Server name replacement - serverNameReplacement = new LinkedHashMap<>(); - Section serverNameReplacementSection = configFile.getSection("server_name_replacement"); - if (serverNameReplacementSection != null) { - for (String s : serverNameReplacementSection.getRoutesAsStrings(false)) { - serverNameReplacement.put(s, serverNameReplacementSection.getString(s)); - } - } - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class MessageSettings { + @Comment("Whether to enable the /msg command") + private boolean enabled = true; - /** - * Returns {@link Channel} data from the proxy {@link YamlDocument} - * - * @param configFile The proxy {@link YamlDocument} - * @return {@link HashMap} of {@link Channel} data listed in the config file - * @throws IllegalArgumentException if a channel contains an invalid broadcast scope - */ - private HashMap fetchChannels(YamlDocument configFile) throws IllegalArgumentException { - final HashMap channels = new HashMap<>(); - for (String channelID : configFile.getSection("channels").getRoutesAsStrings(false)) { - // Get channel format and scope and create channel object - final String format = configFile.getString("channels." + channelID + ".format", "%fullname%&r: "); - final String broadcastScope = configFile.getString("channels." + channelID + ".broadcast_scope", "GLOBAL").toUpperCase(); - Channel channel = new Channel(channelID.toLowerCase(), format, Channel.BroadcastScope.valueOf(broadcastScope)); - - // Read shortcut commands - if (configFile.contains("channels." + channelID + ".shortcut_commands")) { - channel.setShortcutCommands(getCommandsFromList(configFile.getStringList("channels." + channelID + ".shortcut_commands"))); - } + @Comment("List of command aliases for /msg") + @Getter(AccessLevel.NONE) + private List msgAliases = List.of("/msg", "/m", "/tell", "/whisper", "/w", "/pm"); - // Read shortcut commands - if (configFile.contains("channels." + channelID + ".restricted_servers")) { - channel.setRestrictedServers(configFile.getStringList("channels." + channelID + ".restricted_servers")); - } + @Comment("List of command aliases for /reply") + @Getter(AccessLevel.NONE) + private List replyAliases = List.of("/reply", "/r"); - // Read optional parameters - channel.setSendPermission(configFile.getString("channels." + channelID + ".permissions.send", null)); - channel.setReceivePermission(configFile.getString("channels." + channelID + ".permissions.receive", null)); - channel.setLogMessages(configFile.getBoolean("channels." + channelID + ".log_to_console", true)); - channel.setFilter(configFile.getBoolean("channels." + channelID + ".filtered", true)); + @Comment("Whether to apply censorship filters on private messages") + private boolean censor = false; - channels.put(channelID, channel); - } - return channels; - } + @Comment("Whether to log private messages to the console") + private boolean logToConsole = true; - /** - * Returns a {@link Set} of {@link ChatFilter}s to use for filtering chat messages - * - * @param configFile The proxy {@link YamlDocument} - * @return {@link ChatFilter}s to use - */ - private Map> fetchChatFilters(YamlDocument configFile) { - Map> filters = new HashMap<>(); - clearChatFilters(); // Clear and dispose of any existing ProfanityChecker instances - - for (String channelID : configFile.getSection("channels").getRoutesAsStrings(false)) { - filters.put(channelID, new ArrayList<>()); - } - - filters.put("private_messages", new ArrayList<>()); - filters.put("broadcast_messages", new ArrayList<>()); - - // Filters - if (configFile.getBoolean("chat_filters.advertising_filter.enabled", true)) { - List channels = configFile.getStringList("chat_filters.advertising_filter.channels"); + @Comment("Logging format for private messages") + private String logFormat = "[MSG] [%sender% -> %receiver%]: "; - if (configFile.getBoolean("chat_filters.advertising_filter.private_messages", false)) { - channels.add("private_messages"); - } + @Comment("Group private message settings") + private GroupSettings groupMessages = new GroupSettings(); - if (configFile.getBoolean("chat_filters.advertising_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class GroupSettings { + @Comment("Whether to enable group private messages (/msg Player1,Player2,...)") + private boolean enabled = true; - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new AdvertisingFilterer()); - } + @Comment("Maximum amount of players in a group message") + private int maxSize = 10; } - if (configFile.getBoolean("chat_filters.caps_filter.enabled", true)) { - List channels = configFile.getStringList("chat_filters.caps_filter.channels"); - if (configFile.getBoolean("chat_filters.caps_filter.private_messages", false)) { - channels.add("private_messages"); - } - - if (configFile.getBoolean("chat_filters.caps_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } - - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new CapsFilter(configFile.getDouble("chat_filters.caps_filter.max_caps_percentage", 0.4))); - } + @Comment("Formats for private messages (uses MineDown)") + private MessageFormat format = new MessageFormat(); + + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class MessageFormat { + private String inbound = "&e&l%name% &8→ &e&lYou&8: &f"; + private String outbound = "&e&lYou &8→ &e&l%name%&8: &f"; + private String groupInbound = "&e&l%name% &8→ &e&lYou[₍₊%group_amount_subscript%₎](gray show_text=&7%group_members%)&8: &f"; + private String groupOutbound = "&e&lYou &8→ &e&l%name%[₍₊%group_amount_subscript%₎](gray show_text=&7%group_members%)&8: &f"; } - if (configFile.getBoolean("chat_filters.spam_filter.enabled", true)) { - List channels = configFile.getStringList("chat_filters.spam_filter.channels"); - if (configFile.getBoolean("chat_filters.spam_filter.private_messages", false)) { - channels.add("private_messages"); - } + @Comment("(Bungee/Velocity only) List of servers where private messages cannot be sent") + private List restrictedServers = List.of(); - if (configFile.getBoolean("chat_filters.spam_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } - - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new SpamFilter(configFile.getInt("chat_filters.spam_filter.period_seconds", 4), - configFile.getInt("chat_filters.spam_filter.messages_per_period", 3))); - } + @NotNull + public List getMsgAliases() { + return formatCommands(msgAliases); } - if (configFile.getBoolean("chat_filters.repeat_filter.enabled", true)) { - List channels = configFile.getStringList("chat_filters.repeat_filter.channels"); - if (configFile.getBoolean("chat_filters.repeat_filter.private_messages", false)) { - channels.add("private_messages"); - } - - if (configFile.getBoolean("chat_filters.repeat_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } - - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new RepeatFilter(configFile.getInt("chat_filters.repeat_filter.previous_messages_to_check", 2))); - } + @NotNull + public List getReplyAliases() { + return formatCommands(replyAliases); } - if (configFile.getBoolean("chat_filters.profanity_filter.enabled", false)) { - List channels = configFile.getStringList("chat_filters.profanity_filter.channels"); - if (configFile.getBoolean("chat_filters.profanity_filter.private_messages", false)) { - channels.add("private_messages"); - } - - if (configFile.getBoolean("chat_filters.profanity_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new ProfanityFilterer(ProfanityFilterer.ProfanityFilterMode.valueOf( - configFile.getString("chat_filters.profanity_filter.mode", "TOLERANCE").toUpperCase()), - configFile.getDouble("chat_filters.profanity_filter.tolerance", 0.78d), - configFile.getString("chat_filters.profanity_filter.library_path", ""))); - } - } - if (configFile.getBoolean("chat_filters.ascii_filter.enabled", false)) { - List channels = configFile.getStringList("chat_filters.ascii_filter.channels"); - - if (configFile.getBoolean("chat_filters.ascii_filter.private_messages", false)) { - channels.add("private_messages"); - } - - if (configFile.getBoolean("chat_filters.ascii_filter.broadcast_messages", false)) { - channels.add("broadcast_messages"); - } - - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new AsciiFilter()); - } - } - - // Replacers - if (configFile.getBoolean("message_replacers.emoji_replacer.enabled", true)) { - HashMap emojiSequences = new HashMap<>(); - boolean caseInsensitive = configFile.getBoolean("message_replacers.emoji_replacer.case_insensitive", false); - for (String characters : configFile.getSection("message_replacers.emoji_replacer.emoji").getRoutesAsStrings(false)) { - if (!caseInsensitive) { - emojiSequences.put(characters, configFile.getString("message_replacers.emoji_replacer.emoji." + characters)); - } else { - emojiSequences.put(characters.toLowerCase(Locale.ROOT), configFile.getString("message_replacers.emoji_replacer.emoji." + characters)); - } - } - - List channels = configFile.getStringList("message_replacers.emoji_replacer.channels"); - - if (configFile.getBoolean("chat_filters.emoji_replacer.private_messages", true)) { - channels.add("private_messages"); - } - - if (configFile.getBoolean("chat_filters.emoji_replacer.broadcast_messages", true)) { - channels.add("broadcast_messages"); - } - - for (String channel : channels) { - if (!filters.containsKey(channel)) continue; - filters.get(channel).add(new EmojiReplacer(emojiSequences, caseInsensitive)); - } - } - - return filters; - } - - /** - * Returns a map of Discord webhook URLs for each channel - * - * @param configFile The configuration file - * @return A map of webhook URLs for each channel - */ - private Map fetchWebhookUrls(@NotNull YamlDocument configFile) { - final Map webhookUrls = new HashMap<>(); - try { - if (configFile.contains("discord.channel_webhooks")) { - for (String channelID : configFile.getSection("discord.channel_webhooks").getRoutesAsStrings(false)) { - if (!channels.containsKey(channelID)) { - continue; - } - webhookUrls.put(channelID, new URL(configFile.getString("discord.channel_webhooks." + channelID))); - } - } - } catch (MalformedURLException e) { - doDiscordIntegration = false; - } - return webhookUrls; - } - - /** - * Returns a {@link java.util.Map} of servers to the default channel to enforce on that server - * - * @param configFile The proxy {@link YamlDocument} - * @return {@link java.util.Map} of servers and their default channels - */ - private HashMap getServerDefaultChannels(YamlDocument configFile) { - final HashMap serverDefaults = new HashMap<>(); - if (configFile.contains("server_default_channels")) { - for (String server : configFile.getSection("server_default_channels").getRoutesAsStrings(false)) { - String channelId = configFile.getString("server_default_channels." + server); - serverDefaults.put(server, channelId); - } - } - return serverDefaults; } - /** - * Returns a {@link Set} of formatted command strings from a list - * - * @param rawCommands Raw commands prepended with {@code /} - * @return formatted set of command strings - */ - private List getCommandsFromList(List rawCommands) { - List commands = new ArrayList<>(); - for (String command : rawCommands) { - commands.add(command.substring(1)); - } - return commands; - } + @Comment("Social spy settings (see other users' private messages)") + private SocialSpySettings socialSpy = new SocialSpySettings(); - /** - * Clears the chat filters and disposes of the existing ProfanityFilter - */ - private void clearChatFilters() { - chatFilters = new HashMap<>(); - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class SocialSpySettings { + private boolean enabled = true; + private String format = "&e[Spy] &7%name% &8→ &7%receiver_name%:%spy_color% "; + private String groupFormat = "&e[Spy] &7%name% &8→ &7%receiver_name% [₍₊%group_amount_subscript%₎](gray show_text=&7%group_members% suggest_command=/msg %group_members_comma_separated% ):%spy_color% "; + @Getter(AccessLevel.NONE) + private List socialspyAliases = List.of("/socialspy", "/ss"); - /** - * Returns the aliases from a list of aliases - * - * @param aliases The alias list - * @return The actual command aliases - */ - public String[] getAliases(List aliases) { - if (aliases.size() <= 1) { - return new String[0]; - } - String[] aliasList = new String[aliases.size() - 1]; - for (int i = 1; i < aliases.size(); i++) { - aliasList[i - 1] = aliases.get(i); + @NotNull + public List getSocialspyAliases() { + return formatCommands(socialspyAliases); } - return aliasList; } - @NotNull - public String getLanguage() { - return language; - } - - public boolean doCheckForUpdates() { - return checkForUpdates; - } - public boolean doPlaceholderAPI() { - return doPlaceholderAPI; - } - - public long getPapiProxyBridgeCacheTime() { - return papiProxyBridgeCacheTime; - } + @Comment("(Bungee/Velocity only) Local spy settings (see local messages on other servers)") + private LocalSpySettings localSpy = new LocalSpySettings(); - @NotNull - public String getDefaultChannel() { - return defaultChannel; - } - - @NotNull - public Map getServerDefaultChannels() { - return serverDefaultChannels; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class LocalSpySettings { + private boolean enabled = true; + private String format = "&e[Spy] &7%name% &8→ &7%receiver_name%:%spy_color% "; + @Getter(AccessLevel.NONE) + private List localspyAliases = List.of("/socialspy", "/ss"); + @Comment("List of channels to exclude from local spy") + private List excludedLocalChannels = List.of(); - @NotNull - public Map getChannels() { - return channels; - } - - @NotNull - public String getChannelLogFormat() { - return channelLogFormat; - } - - @NotNull - public List getChannelCommandAliases() { - return channelCommandAliases; - } - - public boolean isDoMessageCommand() { - return doMessageCommand; + @NotNull + public List getLocalspyAliases() { + return formatCommands(localspyAliases); + } } - public boolean doGroupMessages() { - return doGroupMessages; - } + @Comment("Broadcast command settings") + private BroadcastSettings broadcastCommand = new BroadcastSettings(); - public int getMaxGroupMessageSize() { - return maxGroupMessageSize; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class BroadcastSettings { + private boolean enabled = true; + @Getter(AccessLevel.NONE) + private List broadcastAliases = List.of("/broadcast", "/alert"); + private String format = "&6[Broadcast]&e "; + private boolean logToConsole = true; + private String logFormat = "[BROADCAST]: "; - @NotNull - public List getMessageCommandAliases() { - return messageCommandAliases; + @NotNull + public List getBroadcastAliases() { + return formatCommands(broadcastAliases); + } } - @NotNull - public List getReplyCommandAliases() { - return replyCommandAliases; - } + @Comment("Join and quit message settings") + private JoinQuitSettings joinAndQuitMessages = new JoinQuitSettings(); - @NotNull - public String getInboundMessageFormat() { - return inboundMessageFormat; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class JoinQuitSettings { - @NotNull - public String getOutboundMessageFormat() { - return outboundMessageFormat; - } + @Comment({"Use the \"huskchat.join_message.[text]\" permission to override this.", + "Use the \"huskchat.silent_join\" permission to silence for a user."}) + private ConnectionMessage join = new ConnectionMessage(true, "&e%name% joined the network"); - @NotNull - public String getGroupInboundMessageFormat() { - return groupInboundMessageFormat; - } + @Comment({"Use the \"huskchat.quit_message.[text]\" permission to override this.", + "Use the \"huskchat.silent_quit\" permission to silence for a user."}) + private ConnectionMessage quit = new ConnectionMessage(true, "&e%name% left the network"); - @NotNull - public String getGroupOutboundMessageFormat() { - return groupOutboundMessageFormat; - } + @Comment("Note that on Velocity/Bungee, PASSTHROUGH modes won't cancel local join/quit messages") + private Channel.BroadcastScope broadcastScope = Channel.BroadcastScope.GLOBAL; - public boolean doLogPrivateMessages() { - return logPrivateMessages; + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class ConnectionMessage { + private boolean enabled; + private String format; + } } - public boolean isCensorPrivateMessages() { - return censorPrivateMessages; - } + @Comment("Discord integration settings. Docs: https://william278.net/docs/huskchat/discord-hook") + private DiscordSettings discord = new DiscordSettings(); - @NotNull - public String getMessageLogFormat() { - return messageLogFormat; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class DiscordSettings { + @Comment("Enable hooking into Discord via Webhooks and/or Spicord") + private boolean enabled = false; - @NotNull - public List getMessageCommandRestrictedServers() { - return messageCommandRestrictedServers; - } + @Comment("Discord message format style (INLINE or EMBEDDED)") + private DiscordHook.Format formatStyle = DiscordHook.Format.INLINE; - public boolean doSocialSpyCommand() { - return doSocialSpyCommand; - } + @Comment("Send messages in channels to a webhook by mapped URL") + @Getter(AccessLevel.NONE) + private Map channelWebhooks = new HashMap<>(); - @NotNull - public String getSocialSpyFormat() { - return socialSpyFormat; - } + @Comment("Whether to hook into Spicord for two-way chat") + private SpicordSettings spicord = new SpicordSettings(); - @NotNull - public String getSocialSpyGroupFormat() { - return socialSpyGroupFormat; - } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class SpicordSettings { + @Comment("Requires Spicord installed and \"huskchat\" added to the \"addons\" in config.toml") + private boolean enabled = true; - @NotNull - public List getSocialSpyCommandAliases() { - return socialSpyCommandAliases; - } + @Comment("Format of Discord users in-game. Note this doesn't support other placeholders") + private String usernameFormat = "@%discord_handle%"; - public boolean doLocalSpyCommand() { - return doLocalSpyCommand; - } + @Comment("Send in-game messages on these channels to a specified Discord channel (by numeric ID)") + private Map receiveChannelMap = new HashMap<>(Map.of( + "global", "123456789012345678" + )); - @NotNull - public String getLocalSpyFormat() { - return localSpyFormat; - } + @Comment("Send Discord messages on these channels (by numeric ID) to a specified in-game channel") + private Map sendChannelMap = new HashMap<>(Map.of( + "123456789012345678", "global" + )); + } - /** - * Returns if a channel is excluded from local spy messages - * - * @param channel {@link Channel} to check - * @return {@code true} if the channel should be excluded from /localspy; {@code false} otherwise - */ - public boolean isLocalSpyChannelExcluded(@NotNull Channel channel) { - for (String excludedChannel : excludedLocalSpyChannels) { - if (excludedChannel.equals(channel.getId())) { - return true; + @NotNull + @Unmodifiable + public Map getChannelWebhooks() throws IllegalStateException { + final Map webhookMap = new HashMap<>(); + for (String channel : channelWebhooks.keySet()) { + try { + webhookMap.put(channel, new URI(channelWebhooks.get(channel)).toURL()); + } catch (Throwable e) { + throw new IllegalStateException("Invalid URL for Discord webhook: " + channelWebhooks.get(channel)); + } } + return webhookMap; } - return false; - } - - @NotNull - public List getLocalSpyCommandAliases() { - return localSpyCommandAliases; - } - - public boolean isDoBroadcastCommand() { - return doBroadcastCommand; - } - - @NotNull - public List getBroadcastCommandAliases() { - return broadcastCommandAliases; - } - - @NotNull - public String getBroadcastMessageFormat() { - return broadcastMessageFormat; - } - - public boolean doLogBroadcasts() { - return logBroadcasts; - } - - @NotNull - public String getBroadcastLogFormat() { - return broadcastLogFormat; - } - - public boolean doJoinMessages() { - return doJoinMessages; - } - - @NotNull - public String getJoinMessageFormat() { - return joinMessageFormat; - } - - public boolean doQuitMessages() { - return doQuitMessages; - } - - @NotNull - public String getQuitMessageFormat() { - return quitMessageFormat; - } - - @NotNull - public Channel.BroadcastScope getJoinQuitBroadcastScope() { - return joinQuitBroadcastScope; - } - - @NotNull - public Map> getChatFilters() { - return chatFilters; - } - - public boolean doDiscordIntegration() { - return doDiscordIntegration; - } - - @NotNull - public Map getWebhookUrls() { - return webhookUrls; } - @NotNull - public WebHook.Format getDiscordMessageFormat() { - return webhookFormat; - } - - public boolean useSpicord() { - return useSpicord; - } - - @NotNull - public String getDiscordUsernameFormat() { - return discordUsernameFormat; - } - - @NotNull - public Map getSpicordReceiveChannelMap() { - return spicordReceiveChannelMap; - } - - @NotNull - public Map getSpicordSendChannelMap() { - return spicordSendChannelMap; - } + @Comment("Custom names to display wherever you use the \"%server%\" placeholder instead of their default name") + private Map serverNameReplacement = new HashMap<>(); @NotNull - public Map getServerNameReplacement() { - return serverNameReplacement; + public static List formatCommands(@NotNull List rawCommands) { + return rawCommands.stream().map(c -> c.startsWith("/") ? c.substring(1) : c).toList(); } } diff --git a/common/src/main/java/net/william278/huskchat/discord/DiscordHook.java b/common/src/main/java/net/william278/huskchat/discord/DiscordHook.java index 28774bca..953e61b6 100644 --- a/common/src/main/java/net/william278/huskchat/discord/DiscordHook.java +++ b/common/src/main/java/net/william278/huskchat/discord/DiscordHook.java @@ -28,13 +28,11 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; -import java.util.Optional; public interface DiscordHook { void postMessage(@NotNull ChatMessage message); - /** * Get the discord chat message json for the given message. * @@ -43,14 +41,14 @@ public interface DiscordHook { * @return the json message as a byte array */ static byte[] getDiscordMessageJson(@NotNull HuskChat plugin, @NotNull ChatMessage message) { - return plugin.getSettings().getDiscordMessageFormat() + return plugin.getSettings().getDiscord().getFormatStyle() .getPostMessageFormat(plugin) - .replace("{SENDER_UUID}", message.sender.getUuid().toString()) - .replace("{SENDER_CHANNEL}", message.targetChannelId) + .replace("{SENDER_UUID}", message.getSender().getUuid().toString()) + .replace("{SENDER_CHANNEL}", message.getChannel().getId()) .replace("{CURRENT_TIMESTAMP}", ZonedDateTime.now() .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)) - .replace("{SENDER_USERNAME}", message.sender.getName()) - .replace("{CHAT_MESSAGE}", message.message + .replace("{SENDER_USERNAME}", message.getSender().getName()) + .replace("{CHAT_MESSAGE}", message.getMessage() .replace("\\", "\\\\") .replace("\"", "\\\"")) .getBytes(StandardCharsets.UTF_8); @@ -64,21 +62,6 @@ enum Format { EMBEDDED, INLINE; - /** - * Get the discord message format by name if it exists - * - * @param formatName The name of the message format - * @return the {@link Format} - */ - public static Optional getMessageFormat(@NotNull String formatName) { - for (Format format : Format.values()) { - if (format.name().equalsIgnoreCase(formatName)) { - return Optional.of(format); - } - } - return Optional.empty(); - } - @NotNull private String getPostMessageFormat(@NotNull HuskChat plugin) { try { diff --git a/common/src/main/java/net/william278/huskchat/discord/SpicordHook.java b/common/src/main/java/net/william278/huskchat/discord/SpicordHook.java index ef3e1cf1..17498a68 100644 --- a/common/src/main/java/net/william278/huskchat/discord/SpicordHook.java +++ b/common/src/main/java/net/william278/huskchat/discord/SpicordHook.java @@ -31,8 +31,10 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import net.william278.huskchat.HuskChat; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.config.Settings; import net.william278.huskchat.message.ChatMessage; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import org.spicord.Spicord; import org.spicord.api.addon.SimpleAddon; @@ -62,28 +64,28 @@ public SpicordHook(@NotNull HuskChat plugin) { @Override public void postMessage(@NotNull ChatMessage message) { - if (message.sender instanceof SpicordPlayer) { + if (message.getSender() instanceof SpicordOnlineUser) { return; } CompletableFuture.runAsync(() -> this.addon.sendMessage(message)); } - public static class SpicordPlayer implements Player { - - private final HuskChat plugin; + public static class SpicordOnlineUser extends OnlineUser { + private final Settings.DiscordSettings settings; private final User discordUser; private final Message context; - private SpicordPlayer(@NotNull HuskChat plugin, @NotNull User discordUser, @NotNull Message context) { - this.plugin = plugin; + private SpicordOnlineUser(@NotNull HuskChat plugin, @NotNull User discordUser, @NotNull Message context) { + super("", UUID.nameUUIDFromBytes(discordUser.getId().getBytes()), plugin); this.discordUser = discordUser; this.context = context; + this.settings = plugin.getSettings().getDiscord(); } @NotNull @Override public String getName() { - return plugin.getSettings().getDiscordUsernameFormat() + return settings.getSpicord().getUsernameFormat() .replaceAll( "%discord_handle%", getDiscriminatorString() @@ -104,12 +106,6 @@ private Optional getDiscriminatorString() { } } - @NotNull - @Override - public UUID getUuid() { - return UUID.nameUUIDFromBytes(discordUser.getId().getBytes()); - } - @Override public int getPing() { return 0; @@ -153,16 +149,18 @@ public Audience getAudience() { private static class Addon extends SimpleAddon { private final HuskChat plugin; + private final Settings.DiscordSettings settings; private DiscordBot bot; private Addon(@NotNull HuskChat plugin) { super("HuskChat", "huskchat", "William278", plugin.getVersion().toString()); this.plugin = plugin; + this.settings = plugin.getSettings().getDiscord(); } private void sendMessage(@NotNull ChatMessage message) { final Optional discordChannelId = Optional.ofNullable( - plugin.getSettings().getSpicordReceiveChannelMap().get(message.targetChannelId) + settings.getSpicord().getReceiveChannelMap().get(message.getChannel().getId()) ).flatMap(id -> { try { return Optional.of(Long.parseLong(id.trim())); @@ -199,22 +197,22 @@ private void sendMessage(@NotNull ChatMessage message) { } private void dispatchMessage(@NotNull ChatMessage message, @NotNull GuildMessageChannel channel) { - final Format format = plugin.getSettings().getDiscordMessageFormat(); + final Format format = settings.getFormatStyle(); channel.sendMessage(new MessageCreateBuilder() // Disable mentions .setAllowedMentions(List.of()) // Embedded formatting .setEmbeds(format == Format.EMBEDDED ? List.of(new EmbedBuilder() - .setDescription(message.message) + .setDescription(message.getMessage()) .setColor(0x00fb9a) .setFooter( String.format("%s • %s", - message.sender.getName(), - message.sender.getServerName() + message.getSender().getName(), + message.getSender().getServerName() ), String.format("https://minotar.net/avatar/%s/64", - message.sender.getUuid() + message.getSender().getUuid() ) ) .setTimestamp(OffsetDateTime.now()) @@ -222,7 +220,7 @@ private void dispatchMessage(@NotNull ChatMessage message, @NotNull GuildMessage // Inline formatting .setContent(format == Format.INLINE ? String.format("### %s\n%s", - message.sender.getName(), message.message) : null) + message.getSender().getName(), message.getMessage()) : null) .build() ).queue(); @@ -242,16 +240,16 @@ public void onMessageReceived(@NotNull DiscordBot bot, @NotNull MessageReceivedE } // Get the channel ID, send an in-game message. - final Optional serverChannelId = Optional.ofNullable( - plugin.getSettings().getSpicordSendChannelMap().get(event.getGuildChannel().getId()) - ); - if (serverChannelId.isEmpty()) { + final Optional serverChannel = Optional.ofNullable( + settings.getSpicord().getSendChannelMap().get(event.getGuildChannel().getId()) + ).flatMap(plugin.getChannels()::getChannel); + if (serverChannel.isEmpty()) { return; } new ChatMessage( - serverChannelId.get(), - new SpicordPlayer(plugin, event.getAuthor(), event.getMessage()), + serverChannel.get(), + new SpicordOnlineUser(plugin, event.getAuthor(), event.getMessage()), event.getMessage().getContentRaw(), plugin ).dispatch(); diff --git a/common/src/main/java/net/william278/huskchat/discord/WebHook.java b/common/src/main/java/net/william278/huskchat/discord/WebHook.java index 90d6b212..2d78e169 100644 --- a/common/src/main/java/net/william278/huskchat/discord/WebHook.java +++ b/common/src/main/java/net/william278/huskchat/discord/WebHook.java @@ -40,7 +40,7 @@ public class WebHook implements DiscordHook { // Get the webhook URL for a channel by its ID private Optional getWebhookUrl(@NotNull String channelId) { - final Map urls = plugin.getSettings().getWebhookUrls(); + final Map urls = plugin.getSettings().getDiscord().getChannelWebhooks(); if (urls.containsKey(channelId)) { return Optional.of(urls.get(channelId)); } @@ -58,7 +58,7 @@ public WebHook(@NotNull HuskChat plugin) { */ @Override public void postMessage(@NotNull ChatMessage message) { - CompletableFuture.runAsync(() -> getWebhookUrl(message.targetChannelId).ifPresent(webhookUrl -> { + CompletableFuture.runAsync(() -> getWebhookUrl(message.getChannel().getId()).ifPresent(webhookUrl -> { try { final HttpURLConnection webhookConnection = (HttpURLConnection) webhookUrl.openConnection(); webhookConnection.setRequestMethod("POST"); diff --git a/common/src/main/java/net/william278/huskchat/event/IBroadcastMessageEvent.java b/common/src/main/java/net/william278/huskchat/event/BroadcastMessageEvent.java similarity index 83% rename from common/src/main/java/net/william278/huskchat/event/IBroadcastMessageEvent.java rename to common/src/main/java/net/william278/huskchat/event/BroadcastMessageEvent.java index 2a36daad..6fc4394a 100644 --- a/common/src/main/java/net/william278/huskchat/event/IBroadcastMessageEvent.java +++ b/common/src/main/java/net/william278/huskchat/event/BroadcastMessageEvent.java @@ -19,17 +19,17 @@ package net.william278.huskchat.event; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public interface IBroadcastMessageEvent extends EventBase { +public interface BroadcastMessageEvent extends EventBase { @NotNull - Player getSender(); + OnlineUser getSender(); @NotNull String getMessage(); - void setSender(@NotNull Player sender); + void setSender(@NotNull OnlineUser sender); void setMessage(@NotNull String message); } diff --git a/common/src/main/java/net/william278/huskchat/event/IChatMessageEvent.java b/common/src/main/java/net/william278/huskchat/event/ChatMessageEvent.java similarity index 85% rename from common/src/main/java/net/william278/huskchat/event/IChatMessageEvent.java rename to common/src/main/java/net/william278/huskchat/event/ChatMessageEvent.java index 5cbd4a93..f532fd3b 100644 --- a/common/src/main/java/net/william278/huskchat/event/IChatMessageEvent.java +++ b/common/src/main/java/net/william278/huskchat/event/ChatMessageEvent.java @@ -19,19 +19,19 @@ package net.william278.huskchat.event; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public interface IChatMessageEvent extends EventBase { +public interface ChatMessageEvent extends EventBase { @NotNull - Player getSender(); + OnlineUser getSender(); @NotNull String getMessage(); @NotNull String getChannelId(); - void setSender(@NotNull Player sender); + void setSender(@NotNull OnlineUser sender); void setMessage(@NotNull String message); @SuppressWarnings("unused") void setChannelId(@NotNull String channelId); diff --git a/common/src/main/java/net/william278/huskchat/event/EventDispatcher.java b/common/src/main/java/net/william278/huskchat/event/EventProvider.java similarity index 63% rename from common/src/main/java/net/william278/huskchat/event/EventDispatcher.java rename to common/src/main/java/net/william278/huskchat/event/EventProvider.java index 89f54cf6..ce245520 100644 --- a/common/src/main/java/net/william278/huskchat/event/EventDispatcher.java +++ b/common/src/main/java/net/william278/huskchat/event/EventProvider.java @@ -19,18 +19,18 @@ package net.william278.huskchat.event; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.concurrent.CompletableFuture; -public interface EventDispatcher { +public interface EventProvider { - CompletableFuture dispatchChatMessageEvent(@NotNull Player player, @NotNull String message, @NotNull String channelId); + CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, @NotNull String message, @NotNull String channelId); - CompletableFuture dispatchPrivateMessageEvent(@NotNull Player sender, @NotNull List receivers, @NotNull String message); + CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List receivers, @NotNull String message); - CompletableFuture dispatchBroadcastMessageEvent(@NotNull Player sender, @NotNull String message); + CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, @NotNull String message); } diff --git a/common/src/main/java/net/william278/huskchat/event/IPrivateMessageEvent.java b/common/src/main/java/net/william278/huskchat/event/PrivateMessageEvent.java similarity index 77% rename from common/src/main/java/net/william278/huskchat/event/IPrivateMessageEvent.java rename to common/src/main/java/net/william278/huskchat/event/PrivateMessageEvent.java index 567cf579..ed5731e1 100644 --- a/common/src/main/java/net/william278/huskchat/event/IPrivateMessageEvent.java +++ b/common/src/main/java/net/william278/huskchat/event/PrivateMessageEvent.java @@ -19,23 +19,23 @@ package net.william278.huskchat.event; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; -public interface IPrivateMessageEvent extends EventBase { +public interface PrivateMessageEvent extends EventBase { @NotNull - Player getSender(); + OnlineUser getSender(); @NotNull - List getRecipients(); + List getRecipients(); @NotNull String getMessage(); - void setSender(@NotNull Player sender); + void setSender(@NotNull OnlineUser sender); @SuppressWarnings("unused") - void setRecipients(@NotNull List Recipients); + void setRecipients(@NotNull List Recipients); void setMessage(@NotNull String message); } diff --git a/common/src/main/java/net/william278/huskchat/filter/AdvertisingFilterer.java b/common/src/main/java/net/william278/huskchat/filter/AdvertisingFilterer.java index 6c1cf0d7..676bca56 100644 --- a/common/src/main/java/net/william278/huskchat/filter/AdvertisingFilterer.java +++ b/common/src/main/java/net/william278/huskchat/filter/AdvertisingFilterer.java @@ -19,7 +19,7 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.regex.Pattern; @@ -29,6 +29,10 @@ */ public class AdvertisingFilterer extends ChatFilter { + public AdvertisingFilterer(@NotNull FilterSettings settings) { + super(settings); + } + /** * Lifted from https://gist.github.com/dperini/729294 * Licensed under MIT 3.0 @@ -75,21 +79,27 @@ public class AdvertisingFilterer extends ChatFilter { "$", Pattern.CASE_INSENSITIVE); + @NotNull + public static FilterSettings getDefaultSettings() { + return new FilterSettings(); + } + @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { return !(domainPattern.matcher(message).matches()); } @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_advertising"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.advertising"; } + } diff --git a/common/src/main/java/net/william278/huskchat/filter/AsciiFilter.java b/common/src/main/java/net/william278/huskchat/filter/AsciiFilter.java index aa7b1d88..2ed65290 100644 --- a/common/src/main/java/net/william278/huskchat/filter/AsciiFilter.java +++ b/common/src/main/java/net/william278/huskchat/filter/AsciiFilter.java @@ -19,7 +19,7 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.regex.Pattern; @@ -34,20 +34,29 @@ public class AsciiFilter extends ChatFilter { */ private final Pattern asciiPattern = Pattern.compile("^[\\u0000-\\u007F]*$"); + public AsciiFilter(@NotNull FilterSettings settings) { + super(settings); + } + + @NotNull + public static FilterSettings getDefaultSettings() { + return new FilterSettings(); + } + @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { return asciiPattern.matcher(message).matches(); } @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_ascii"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.ascii"; } diff --git a/common/src/main/java/net/william278/huskchat/filter/CapsFilter.java b/common/src/main/java/net/william278/huskchat/filter/CapsFilter.java index a3c7f9d2..6de01b37 100644 --- a/common/src/main/java/net/william278/huskchat/filter/CapsFilter.java +++ b/common/src/main/java/net/william278/huskchat/filter/CapsFilter.java @@ -19,7 +19,12 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; /** @@ -27,14 +32,17 @@ */ public class CapsFilter extends ChatFilter { - private final double capsPercentage; + public CapsFilter(FilterSettings settings) { + super(settings); + } - public CapsFilter(double capsPercentage) { - this.capsPercentage = capsPercentage; + @NotNull + public static FilterSettings getDefaultSettings() { + return new CapsFilterSettings(); } @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { double messageLength = message.length(); if (messageLength <= 5) { return true; @@ -46,19 +54,28 @@ public boolean isAllowed(@NotNull Player player, @NotNull String message) { } } double capsProportion = (double) capsLetters / messageLength; - return !(capsProportion > capsPercentage); + return !(capsProportion > ((CapsFilterSettings) settings).getMaxCapsPercentage()); } @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_caps"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.caps"; } + + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PACKAGE) + public static class CapsFilterSettings extends FilterSettings { + public double maxCapsPercentage = 0.4d; + } + } diff --git a/common/src/main/java/net/william278/huskchat/filter/ChatFilter.java b/common/src/main/java/net/william278/huskchat/filter/ChatFilter.java index 96842159..79904959 100644 --- a/common/src/main/java/net/william278/huskchat/filter/ChatFilter.java +++ b/common/src/main/java/net/william278/huskchat/filter/ChatFilter.java @@ -19,21 +19,33 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.*; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; + /** * An abstract representation of a chat filterer */ +@SuppressWarnings("FieldMayBeFinal") +@Getter +@Setter +@AllArgsConstructor public abstract class ChatFilter { + protected final FilterSettings settings; + /** * Takes a user's message and returns true if the message passes the filter * * @param message The user's message * @return {@code true} if the filter allows the message to pass; {@code false} otherwise */ - public abstract boolean isAllowed(@NotNull Player sender, @NotNull String message); + public abstract boolean isAllowed(@NotNull OnlineUser sender, @NotNull String message); /** * The ID of the locale to send the player if their message fails the filter @@ -41,7 +53,7 @@ public abstract class ChatFilter { * @return the failure message ID */ @NotNull - public abstract String getFailureErrorMessageId(); + public abstract String getDisallowedLocale(); /** * The permission node users can have to bypass this filter @@ -49,6 +61,60 @@ public abstract class ChatFilter { * @return filter bypass permission node */ @NotNull - public abstract String getFilterIgnorePermission(); + public abstract String getIgnorePermission(); + + @SuppressWarnings("FieldMayBeFinal") + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PACKAGE) + public static class FilterSettings { + protected boolean enabled = true; + private List channels = List.of("global", "local"); + private boolean privateMessages = true; + private boolean broadcastMessages = false; + } + + @Getter + @AllArgsConstructor + public enum Type { + // Filters + ADVERTISING(AdvertisingFilterer.getDefaultSettings(), AdvertisingFilterer::new), + CAPS(CapsFilter.getDefaultSettings(), CapsFilter::new), + SPAM(SpamFilter.getDefaultSettings(), SpamFilter::new), + PROFANITY(ProfanityFilterer.getDefaultSettings(), ProfanityFilterer::new), + REPEAT(RepeatFilter.getDefaultSettings(), RepeatFilter::new), + ASCII(AsciiFilter.getDefaultSettings(), AsciiFilter::new), + REGEX(RegexFilter.getDefaultSettings(), RegexFilter::new), + + // Replacers + EMOJI(EmojiReplacer.getDefaultSettings(), EmojiReplacer::new); + + private final FilterSettings defaultSettings; + private final Function creator; + + @NotNull + public String toString() { + return this.name().toLowerCase(Locale.ENGLISH); + } + } + + /** + * A special kind of {@link ChatFilter} that can modify the contents of a message + */ + public abstract static class ReplacerFilter extends ChatFilter { + + public ReplacerFilter(@NotNull FilterSettings settings) { + super(settings); + } + + /** + * Replace the input message from the user + * + * @param message The input message + * @return The output - replaced message + */ + @NotNull + public abstract String replace(@NotNull String message); + } } diff --git a/common/src/main/java/net/william278/huskchat/replacer/EmojiReplacer.java b/common/src/main/java/net/william278/huskchat/filter/EmojiReplacer.java similarity index 53% rename from common/src/main/java/net/william278/huskchat/replacer/EmojiReplacer.java rename to common/src/main/java/net/william278/huskchat/filter/EmojiReplacer.java index 75617f49..6d7dd733 100644 --- a/common/src/main/java/net/william278/huskchat/replacer/EmojiReplacer.java +++ b/common/src/main/java/net/william278/huskchat/filter/EmojiReplacer.java @@ -17,26 +17,27 @@ * limitations under the License. */ -package net.william278.huskchat.replacer; +package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.StringJoiner; /** * A {@link ReplacerFilter} that replaces chat emoji with the character emote */ -public class EmojiReplacer extends ReplacerFilter { +public class EmojiReplacer extends ChatFilter.ReplacerFilter { - private final HashMap emoticons; - private final boolean caseInsensitive; - - public EmojiReplacer(HashMap emoticons, boolean caseInsensitive) { - this.emoticons = emoticons; - this.caseInsensitive = caseInsensitive; + public EmojiReplacer(@NotNull FilterSettings settings) { + super(settings); } @Override @@ -44,16 +45,17 @@ public EmojiReplacer(HashMap emoticons, boolean caseInsensitive) public String replace(@NotNull String message) { String[] words = message.split(" "); StringJoiner replacedMessage = new StringJoiner(" "); + final EmojiReplacerSettings settings = (EmojiReplacerSettings) this.settings; for (String word : words) { - for (String emoteFormat : emoticons.keySet()) { - if (!caseInsensitive) { + for (String emoteFormat : settings.getEmoji().keySet()) { + if (!settings.isCaseInsensitive()) { if (word.equals(emoteFormat)) { - word = emoticons.get(emoteFormat); + word = settings.getEmoji().get(emoteFormat); break; } } else { if (word.toLowerCase(Locale.ROOT).equals(emoteFormat)) { - word = emoticons.get(emoteFormat); + word = settings.getEmoji().get(emoteFormat); break; } } @@ -63,21 +65,45 @@ public String replace(@NotNull String message) { return replacedMessage.toString(); } + @NotNull + public static FilterSettings getDefaultSettings() { + return new EmojiReplacerSettings(); + } + @Override - public boolean isAllowed(@NotNull Player sender, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser sender, @NotNull String message) { return true; } @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { throw new UnsupportedOperationException("EmojiReplacer does not support failure messages"); } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.emoji_replacer"; } + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class EmojiReplacerSettings extends FilterSettings { + private boolean caseInsensitive = false; + private Map emoji = new HashMap<>(Map.of( + ":)", "☺", + ":smile:", "☺", + ":-)", "☺", + ":(", "☹", + ":frown:", "☹", + ":-(", "☹", + ":fire:", "🔥", + ":heart:", "❤", + "<3", "❤", + ":star:", "⭐" + )); + } + } diff --git a/common/src/main/java/net/william278/huskchat/filter/FilterProvider.java b/common/src/main/java/net/william278/huskchat/filter/FilterProvider.java new file mode 100644 index 00000000..ab637f8b --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/filter/FilterProvider.java @@ -0,0 +1,90 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.filter; + +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.config.Filters; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; + +public interface FilterProvider { + + List getFiltersAndReplacers(); + + default void loadFilters() { + final Filters settings = getPlugin().getFilterSettings(); + settings.getFilters().entrySet().stream() + .filter(entry -> entry.getValue().isEnabled()) + .forEach(entry -> { + final ChatFilter.Type type = entry.getKey(); + final ChatFilter.FilterSettings filterSettings = entry.getValue(); + getFiltersAndReplacers().add(type.getCreator().apply(filterSettings)); + getPlugin().log(Level.INFO, "Loaded %s filter".formatted(type.name())); + }); + } + + default Optional filter(@NotNull OnlineUser sender, @NotNull String message, + @NotNull List filters) { + boolean bypass = sender.hasPermission("huskchat.bypass_filters"); + final StringBuilder filtered = new StringBuilder(message); + for (ChatFilter filter : filters) { + if (sender.hasPermission(filter.getIgnorePermission())) { + continue; + } + if (filter instanceof ChatFilter.ReplacerFilter replacer) { + filtered.replace(0, filtered.length(), replacer.replace(filtered.toString())); + } + if (!bypass && !filter.isAllowed(sender, message)) { + getPlugin().getLocales().sendMessage(sender, filter.getDisallowedLocale()); + return Optional.empty(); + } + } + return Optional.of(filtered.toString()); + } + + default List getChannelFilters(@NotNull Channel channel) { + return getFiltersAndReplacers().stream() + .filter(filter -> filter.getSettings().getChannels().contains(channel.getId())) + .toList(); + } + + default List getMessageFilters() { + return getFiltersAndReplacers().stream() + .filter(filter -> filter.getSettings().isPrivateMessages()) + .toList(); + } + + default List getBroadcastFilters() { + return getFiltersAndReplacers().stream() + .filter(filter -> filter.getSettings().isBroadcastMessages()) + .toList(); + } + + + @NotNull + HuskChat getPlugin(); + + +} diff --git a/common/src/main/java/net/william278/huskchat/filter/ProfanityFilterer.java b/common/src/main/java/net/william278/huskchat/filter/ProfanityFilterer.java index 793f5667..03131b02 100644 --- a/common/src/main/java/net/william278/huskchat/filter/ProfanityFilterer.java +++ b/common/src/main/java/net/william278/huskchat/filter/ProfanityFilterer.java @@ -19,11 +19,11 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.Getter; +import net.william278.huskchat.user.OnlineUser; import net.william278.profanitycheckerapi.ProfanityChecker; -import net.william278.profanitycheckerapi.ProfanityCheckerBuilder; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * A {@link ChatFilter} that filters against profanity using machine learning @@ -31,18 +31,21 @@ * machine learning algorithm to determine the probability that a string contains profanity */ public class ProfanityFilterer extends ChatFilter { - + @NotNull - private final ProfanityCheckerBuilder builder; + private final ProfanityChecker.ProfanityCheckerBuilder builder; + + public ProfanityFilterer(@NotNull FilterSettings settings) { + super(settings); - public ProfanityFilterer(@NotNull ProfanityFilterMode filterMode, double thresholdValue, - @Nullable String libraryPath) { + final ProfanityFilterSettings profanitySettings = (ProfanityFilterSettings) settings; this.builder = ProfanityChecker.builder(); - if (libraryPath != null && !libraryPath.isBlank()) { - builder.withLibraryPath(libraryPath); + if (profanitySettings.getLibraryPath() != null && !profanitySettings.getLibraryPath().isBlank()) { + builder.libraryPath(profanitySettings.getLibraryPath()); } - if (filterMode == ProfanityFilterMode.TOLERANCE) { - builder.withThresholdChecking(thresholdValue); + if (profanitySettings.getMode() == ProfanityFilterMode.TOLERANCE) { + builder.useThreshold(true); + builder.threshold(profanitySettings.getTolerance()); } initialize(); } @@ -55,13 +58,18 @@ private void initialize() { System.out.println("Initialized the profanity checker and hooked into the jep interpreter"); } catch (UnsatisfiedLinkError | IllegalStateException e) { throw new RuntimeException("Failed to initialize ProfanityChecker (" + e.getMessage() + ")" + - "Please ensure that the jep library is installed and the library path is correct. " + - "Consult the HuskChat docs for more information on this error.", e); + "Please ensure that the jep library is installed and the library path is correct. " + + "Consult the HuskChat docs for more information on this error.", e); } } + @NotNull + public static FilterSettings getDefaultSettings() { + return new ProfanityFilterSettings(); + } + @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { try (final ProfanityChecker checker = builder.build()) { return !checker.isProfane(message); } catch (UnsatisfiedLinkError e) { @@ -72,13 +80,13 @@ public boolean isAllowed(@NotNull Player player, @NotNull String message) { @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_profanity"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.profanity"; } @@ -97,4 +105,23 @@ public enum ProfanityFilterMode { TOLERANCE } + @Getter + @Configuration + public static class ProfanityFilterSettings extends FilterSettings { + public String libraryPath = ""; + public ProfanityFilterMode mode = ProfanityFilterMode.AUTOMATIC; + public double tolerance = 0.78d; + + private ProfanityFilterSettings() { + this.enabled = false; + } + + protected ProfanityFilterSettings(@NotNull String libraryPath, @NotNull ProfanityFilterMode mode, + double tolerance) { + this.libraryPath = libraryPath; + this.mode = mode; + this.tolerance = tolerance; + } + } + } diff --git a/common/src/main/java/net/william278/huskchat/filter/RegexFilter.java b/common/src/main/java/net/william278/huskchat/filter/RegexFilter.java new file mode 100644 index 00000000..6bf8b099 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/filter/RegexFilter.java @@ -0,0 +1,76 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.filter; + +import de.exlll.configlib.Configuration; +import lombok.Getter; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class RegexFilter extends ChatFilter { + + public RegexFilter(@NotNull FilterSettings settings) { + super(settings); + } + + @Override + public boolean isAllowed(@NotNull OnlineUser sender, @NotNull String message) { + if (!settings.isEnabled()) { + return true; + } + for (String pattern : ((RegexFilterSettings) settings).getPatterns()) { + if (message.matches(pattern)) { + return false; + } + } + return true; + } + + @Override + @NotNull + public String getDisallowedLocale() { + return "error_chat_filter_regex"; + } + + @Override + @NotNull + public String getIgnorePermission() { + return "huskchat.ignore_filters.regex"; + } + + @NotNull + public static FilterSettings getDefaultSettings() { + return new RegexFilterSettings(); + } + + @Getter + @Configuration + public static class RegexFilterSettings extends FilterSettings { + private List patterns = new ArrayList<>(); + + private RegexFilterSettings() { + this.enabled = false; + } + } + +} diff --git a/common/src/main/java/net/william278/huskchat/filter/RepeatFilter.java b/common/src/main/java/net/william278/huskchat/filter/RepeatFilter.java index 0ef01792..2aed0b01 100644 --- a/common/src/main/java/net/william278/huskchat/filter/RepeatFilter.java +++ b/common/src/main/java/net/william278/huskchat/filter/RepeatFilter.java @@ -19,7 +19,11 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.HashMap; @@ -36,15 +40,18 @@ public class RepeatFilter extends ChatFilter { */ private final HashMap> userMessageQueues; - private final int previousMessagesToCheck; - - public RepeatFilter(int previousMessagesToCheck) { - this.previousMessagesToCheck = previousMessagesToCheck; + public RepeatFilter(@NotNull FilterSettings settings) { + super(settings); this.userMessageQueues = new HashMap<>(); } + @NotNull + public static FilterSettings getDefaultSettings() { + return new RepeatFilterSettings(); + } + @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { if (!userMessageQueues.containsKey(player.getUuid())) { userMessageQueues.put(player.getUuid(), new LinkedList<>()); } @@ -54,7 +61,8 @@ public boolean isAllowed(@NotNull Player player, @NotNull String message) { return false; } } - if (userMessageQueues.get(player.getUuid()).size() + 1 > previousMessagesToCheck) { + if (userMessageQueues.get(player.getUuid()).size() + 1 > + ((RepeatFilterSettings) settings).getPreviousMessagesToCheck()) { userMessageQueues.get(player.getUuid()).removeLast(); } } @@ -64,13 +72,22 @@ public boolean isAllowed(@NotNull Player player, @NotNull String message) { @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_repeat"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.spam"; } + + + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class RepeatFilterSettings extends FilterSettings { + public int previousMessagesToCheck = 5; + } + } diff --git a/common/src/main/java/net/william278/huskchat/filter/SpamFilter.java b/common/src/main/java/net/william278/huskchat/filter/SpamFilter.java index 36e54f5b..2ffd171c 100644 --- a/common/src/main/java/net/william278/huskchat/filter/SpamFilter.java +++ b/common/src/main/java/net/william278/huskchat/filter/SpamFilter.java @@ -19,7 +19,11 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; +import de.exlll.configlib.Configuration; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.time.Instant; @@ -38,26 +42,28 @@ public class SpamFilter extends ChatFilter { */ private final HashMap> userMessageQueues; - private final int periodLength; - private final int maxMessagesPerPeriod; - - public SpamFilter(int periodLength, int maxMessagesPerPeriod) { - this.periodLength = periodLength; - this.maxMessagesPerPeriod = maxMessagesPerPeriod; + public SpamFilter(@NotNull FilterSettings settings) { + super(settings); this.userMessageQueues = new HashMap<>(); } + @NotNull + public static FilterSettings getDefaultSettings() { + return new SpamFilterSettings(); + } + @Override - public boolean isAllowed(@NotNull Player player, @NotNull String message) { + public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { if (!userMessageQueues.containsKey(player.getUuid())) { userMessageQueues.put(player.getUuid(), new LinkedList<>()); } final long currentTimestamp = Instant.now().getEpochSecond(); if (!userMessageQueues.get(player.getUuid()).isEmpty()) { - if (currentTimestamp > userMessageQueues.get(player.getUuid()).getLast() + periodLength) { + final SpamFilterSettings spam = (SpamFilterSettings) settings; + if (currentTimestamp > userMessageQueues.get(player.getUuid()).getLast() + spam.getPeriodSeconds()) { userMessageQueues.get(player.getUuid()).removeLast(); } - if (userMessageQueues.get(player.getUuid()).size() > maxMessagesPerPeriod) { + if (userMessageQueues.get(player.getUuid()).size() > spam.getMessagesPerPeriod()) { return false; } } @@ -67,13 +73,23 @@ public boolean isAllowed(@NotNull Player player, @NotNull String message) { @Override @NotNull - public String getFailureErrorMessageId() { + public String getDisallowedLocale() { return "error_chat_filter_spam"; } @Override @NotNull - public String getFilterIgnorePermission() { + public String getIgnorePermission() { return "huskchat.ignore_filters.spam"; } + + + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class SpamFilterSettings extends FilterSettings { + public int periodSeconds = 4; + public int messagesPerPeriod = 3; + } + } diff --git a/common/src/main/java/net/william278/huskchat/getter/DataGetter.java b/common/src/main/java/net/william278/huskchat/getter/DataGetter.java index 9c872710..673cb6dd 100644 --- a/common/src/main/java/net/william278/huskchat/getter/DataGetter.java +++ b/common/src/main/java/net/william278/huskchat/getter/DataGetter.java @@ -19,7 +19,7 @@ package net.william278.huskchat.getter; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -29,18 +29,18 @@ */ public abstract class DataGetter { - public abstract String getPlayerFullName(@NotNull Player player); + public abstract String getPlayerFullName(@NotNull OnlineUser player); - public abstract String getPlayerName(@NotNull Player player); + public abstract String getPlayerName(@NotNull OnlineUser player); - public abstract Optional getPlayerPrefix(@NotNull Player player); + public abstract Optional getPlayerPrefix(@NotNull OnlineUser player); - public abstract Optional getPlayerSuffix(@NotNull Player player); + public abstract Optional getPlayerSuffix(@NotNull OnlineUser player); - public abstract Optional getPlayerGroupName(@NotNull Player player); + public abstract Optional getPlayerGroupName(@NotNull OnlineUser player); - public abstract Optional getPlayerGroupDisplayName(@NotNull Player player); + public abstract Optional getPlayerGroupDisplayName(@NotNull OnlineUser player); - public abstract Optional getTextFromNode(@NotNull Player player, @NotNull String nodePrefix); + public abstract Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix); } diff --git a/common/src/main/java/net/william278/huskchat/getter/DefaultDataGetter.java b/common/src/main/java/net/william278/huskchat/getter/DefaultDataGetter.java index 926af646..75084009 100644 --- a/common/src/main/java/net/william278/huskchat/getter/DefaultDataGetter.java +++ b/common/src/main/java/net/william278/huskchat/getter/DefaultDataGetter.java @@ -19,7 +19,7 @@ package net.william278.huskchat.getter; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -34,37 +34,37 @@ public DefaultDataGetter() { } @Override - public String getPlayerFullName(@NotNull Player player) { + public String getPlayerFullName(@NotNull OnlineUser player) { return player.getName(); } @Override - public String getPlayerName(@NotNull Player player) { + public String getPlayerName(@NotNull OnlineUser player) { return player.getName(); } @Override - public Optional getPlayerPrefix(@NotNull Player player) { + public Optional getPlayerPrefix(@NotNull OnlineUser player) { return Optional.empty(); } @Override - public Optional getPlayerSuffix(@NotNull Player player) { + public Optional getPlayerSuffix(@NotNull OnlineUser player) { return Optional.empty(); } @Override - public Optional getPlayerGroupName(@NotNull Player player) { + public Optional getPlayerGroupName(@NotNull OnlineUser player) { return Optional.empty(); } @Override - public Optional getPlayerGroupDisplayName(@NotNull Player player) { + public Optional getPlayerGroupDisplayName(@NotNull OnlineUser player) { return Optional.empty(); } @Override - public Optional getTextFromNode(@NotNull Player player, @NotNull String nodePrefix) { + public Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix) { return Optional.empty(); } diff --git a/common/src/main/java/net/william278/huskchat/getter/LuckPermsDataGetter.java b/common/src/main/java/net/william278/huskchat/getter/LuckPermsDataGetter.java index bdb2e913..5cb0ea49 100644 --- a/common/src/main/java/net/william278/huskchat/getter/LuckPermsDataGetter.java +++ b/common/src/main/java/net/william278/huskchat/getter/LuckPermsDataGetter.java @@ -23,7 +23,7 @@ import net.luckperms.api.LuckPermsProvider; import net.luckperms.api.model.group.Group; import net.luckperms.api.model.user.User; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -42,7 +42,7 @@ public LuckPermsDataGetter() { } @Override - public String getPlayerFullName(@NotNull Player player) { + public String getPlayerFullName(@NotNull OnlineUser player) { return getUser(player.getUuid()) .map(User::getCachedData).map(data -> { final StringBuilder fullName = new StringBuilder(); @@ -65,26 +65,26 @@ public String getPlayerFullName(@NotNull Player player) { } @Override - public String getPlayerName(@NotNull Player player) { + public String getPlayerName(@NotNull OnlineUser player) { return player.getName(); } @Override - public Optional getPlayerPrefix(@NotNull Player player) { + public Optional getPlayerPrefix(@NotNull OnlineUser player) { return getUser(player.getUuid()).flatMap(user -> Optional.ofNullable( user.getCachedData().getMetaData().getPrefix() )); } @Override - public Optional getPlayerSuffix(@NotNull Player player) { + public Optional getPlayerSuffix(@NotNull OnlineUser player) { return getUser(player.getUuid()).flatMap(user -> Optional.ofNullable( user.getCachedData().getMetaData().getSuffix() )); } @Override - public Optional getPlayerGroupName(@NotNull Player player) { + public Optional getPlayerGroupName(@NotNull OnlineUser player) { return getUser(player.getUuid()).flatMap(user -> { final Group group = api.getGroupManager().getGroup(user.getPrimaryGroup()); if (group == null) { @@ -95,7 +95,7 @@ public Optional getPlayerGroupName(@NotNull Player player) { } @Override - public Optional getPlayerGroupDisplayName(@NotNull Player player) { + public Optional getPlayerGroupDisplayName(@NotNull OnlineUser player) { return getUser(player.getUuid()).flatMap(user -> { final Group group = api.getGroupManager().getGroup(user.getPrimaryGroup()); if (group == null) { @@ -109,7 +109,7 @@ public Optional getPlayerGroupDisplayName(@NotNull Player player) { } @Override - public Optional getTextFromNode(@NotNull Player player, @NotNull String nodePrefix) { + public Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix) { return getUser(player.getUuid()).flatMap(user -> Optional.ofNullable( user.getCachedData().getMetaData().getMetaValue(nodePrefix) )); diff --git a/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java b/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java index 53c064c3..5a55a532 100644 --- a/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java +++ b/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java @@ -19,61 +19,55 @@ package net.william278.huskchat.listener; +import lombok.AllArgsConstructor; import net.william278.huskchat.HuskChat; import net.william278.huskchat.channel.Channel; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.Map; +@AllArgsConstructor public abstract class PlayerListener { protected final HuskChat plugin; - public PlayerListener(@NotNull HuskChat plugin) { - this.plugin = plugin; - } - - /** - * Handle a player switching server - * - * @param player The player changing server - * @param newServer The name of the server they are changing to - */ - public final void handlePlayerSwitchServer(@NotNull Player player, @NotNull String newServer) { - final Map defaultChannels = plugin.getSettings().getServerDefaultChannels(); + // Handle server switches + public final void handlePlayerSwitchServer(@NotNull OnlineUser player, @NotNull String newServer) { + // Switch to the default channel for the server if there is one + final Map defaultChannels = plugin.getChannels().getServerDefaultChannels(); if (defaultChannels.containsKey(newServer)) { - plugin.getPlayerCache().switchPlayerChannel(player, defaultChannels.get(newServer)); - } else { - for (Channel channel : plugin.getSettings().getChannels().values()) { - if (channel.getId().equalsIgnoreCase(plugin.getPlayerCache().getPlayerChannel(player.getUuid()))) { - for (String restrictedServer : channel.getRestrictedServers()) { - if (restrictedServer.equalsIgnoreCase(newServer)) { - plugin.getPlayerCache().switchPlayerChannel(player, plugin.getSettings().getDefaultChannel()); - break; - } - } - break; - } - } + plugin.getUserCache().switchPlayerChannel(player, defaultChannels.get(newServer), plugin); + return; } + + // Switch channels to the default one if their current channel is now restricted + final String currentChannel = plugin.getUserCache().getPlayerChannel(player.getUuid()); + plugin.getChannels().getChannels().stream() + .filter(channel -> channel.getId().equalsIgnoreCase(currentChannel)) + .findFirst().flatMap(channel -> channel.getRestrictedServers().stream() + .filter(restrictedServer -> restrictedServer.equalsIgnoreCase(newServer)) + .findFirst()).ifPresent(restricted -> plugin.getUserCache() + .switchPlayerChannel(player, plugin.getChannels().getDefaultChannel(), plugin)); } - public final void handlePlayerJoin(@NotNull Player player) { - if (plugin.getSettings().getJoinQuitBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { + // Handle player joins + public final void handlePlayerJoin(@NotNull OnlineUser player) { + if (plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { return; } - if (plugin.getSettings().doJoinMessages()) { - plugin.getLocales().sendJoinMessage(player); + if (plugin.getSettings().getJoinAndQuitMessages().getJoin().isEnabled()) { + plugin.getLocales().sendJoinMessage(player, plugin); } } - public final void handlePlayerQuit(@NotNull Player player) { - if (plugin.getSettings().getJoinQuitBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { + // Handle player quits + public final void handlePlayerQuit(@NotNull OnlineUser player) { + if (plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { return; } - if (plugin.getSettings().doQuitMessages()) { - plugin.getLocales().sendQuitMessage(player); + if (plugin.getSettings().getJoinAndQuitMessages().getQuit().isEnabled()) { + plugin.getLocales().sendQuitMessage(player, plugin); } } diff --git a/common/src/main/java/net/william278/huskchat/message/BroadcastMessage.java b/common/src/main/java/net/william278/huskchat/message/BroadcastMessage.java index 55a8269d..aad50c0a 100644 --- a/common/src/main/java/net/william278/huskchat/message/BroadcastMessage.java +++ b/common/src/main/java/net/william278/huskchat/message/BroadcastMessage.java @@ -19,63 +19,65 @@ package net.william278.huskchat.message; +import de.themoep.minedown.adventure.MineDown; +import de.themoep.minedown.adventure.MineDownParser; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.filter.ChatFilter; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.replacer.ReplacerFilter; +import net.william278.huskchat.config.Settings; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; +import java.util.Optional; import java.util.logging.Level; /** * Represents a broadcast message to be sent to everyone */ public class BroadcastMessage { - private final Player sender; - private String message; + + private final Settings.BroadcastSettings settings; + private final OnlineUser sender; private final HuskChat plugin; + private String message; - public BroadcastMessage(@NotNull Player sender, @NotNull String message, @NotNull HuskChat plugin) { + public BroadcastMessage(@NotNull OnlineUser sender, @NotNull String message, @NotNull HuskChat plugin) { this.sender = sender; - this.message = message; this.plugin = plugin; + this.settings = plugin.getSettings().getBroadcastCommand(); + this.message = message; } /** * Dispatch the broadcast message to be sent */ public void dispatch() { - plugin.getEventDispatcher().dispatchBroadcastMessageEvent(sender, message).thenAccept(event -> { - if (event.isCancelled()) return; - + plugin.fireBroadcastMessageEvent(sender, message).thenAccept(event -> { + if (event.isCancelled()) { + return; + } message = event.getMessage(); // If the message is to be filtered, then perform filter checks (unless they have the bypass permission) - if (!sender.hasPermission("huskchat.bypass_filters")) { - for (ChatFilter filter : plugin.getSettings().getChatFilters().get("broadcast_messages")) { - if (sender.hasPermission(filter.getFilterIgnorePermission())) { - continue; - } - if (!filter.isAllowed(sender, message)) { - plugin.getLocales().sendMessage(sender, filter.getFailureErrorMessageId()); - return; - } - if (filter instanceof ReplacerFilter replacer) { - message = replacer.replace(message); - } - } - } - - // Dispatch the broadcast to all players - for (Player player : plugin.getOnlinePlayers()) { - plugin.getLocales().sendFormattedBroadcastMessage(player, message); + final Optional filtered = plugin.filter(sender, message, plugin.getBroadcastFilters()); + if (filtered.isEmpty()) { + return; } + message = filtered.get(); - // Log to console if enabled - if (plugin.getSettings().doLogBroadcasts()) { - plugin.log(Level.INFO, plugin.getSettings().getBroadcastLogFormat() + message); + // Send the broadcast + plugin.getOnlinePlayers().forEach(this::sendMessage); + if (settings.isLogToConsole()) { + plugin.log(Level.INFO, settings.getLogFormat() + message); } }); } + public void sendMessage(@NotNull OnlineUser player) { + final TextComponent.Builder componentBuilder = Component.text(); + componentBuilder.append(new MineDown(plugin.getSettings().getBroadcastCommand().getFormat()).toComponent()); + componentBuilder.append(new MineDown(message).disable(MineDownParser.Option.ADVANCED_FORMATTING).toComponent()); + player.sendMessage(componentBuilder.build()); + } + } \ No newline at end of file diff --git a/common/src/main/java/net/william278/huskchat/message/ChatMessage.java b/common/src/main/java/net/william278/huskchat/message/ChatMessage.java index 4b89d2fe..68dc50ca 100644 --- a/common/src/main/java/net/william278/huskchat/message/ChatMessage.java +++ b/common/src/main/java/net/william278/huskchat/message/ChatMessage.java @@ -19,35 +19,39 @@ package net.william278.huskchat.message; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import net.william278.huskchat.HuskChat; import net.william278.huskchat.channel.Channel; -import net.william278.huskchat.filter.ChatFilter; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; -import net.william278.huskchat.replacer.ReplacerFilter; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; /** * Represents a message to be sent in a chat channel */ +@Getter public class ChatMessage { - public final HuskChat plugin; - public final String targetChannelId; - public Player sender; - public String message; + @Getter(AccessLevel.PRIVATE) + private final HuskChat plugin; - public ChatMessage(@NotNull String targetChannelId, @NotNull Player sender, - @NotNull String message, @NotNull HuskChat plugin) { - this.targetChannelId = targetChannelId; + @Setter + private Channel channel; + @Setter + private OnlineUser sender; + @Setter + private String message; + + public ChatMessage(@NotNull Channel channel, @NotNull OnlineUser sender, @NotNull String message, + @NotNull HuskChat plugin) { + this.channel = channel; this.sender = sender; this.message = message; this.plugin = plugin; @@ -56,53 +60,46 @@ public ChatMessage(@NotNull String targetChannelId, @NotNull Player sender, /** * Dispatch the message to be sent * - * @return true if the (Player)ChatEvent should be canceled in the proxy-specific code + * @return true if the message should be canceled (thus not passed through) */ public boolean dispatch() { - AtomicReference channel = new AtomicReference<>(plugin.getSettings().getChannels().get(targetChannelId)); - - if (channel.get() == null) { - plugin.getLocales().sendMessage(sender, "error_no_channel"); + final AtomicReference channel = new AtomicReference<>(this.getChannel()); + final Optional sendPermission = channel.get().getPermissions().getSend(); + if (sendPermission.isPresent() && !getSender().hasPermission(sendPermission.get())) { + getPlugin().getLocales().sendMessage(getSender(), "error_no_permission_send", channel.get().getId()); return true; } - // Verify that the player has permission to send in the channel - if (channel.get().getSendPermission() != null) { - if (!sender.hasPermission(channel.get().getSendPermission())) { - plugin.getLocales().sendMessage(sender, "error_no_permission_send", channel.get().getId()); - return true; - } - } - // Verify that the player is not sending a message from a server where channel access is restricted for (String restrictedServer : channel.get().getRestrictedServers()) { - if (restrictedServer.equalsIgnoreCase(sender.getServerName())) { - plugin.getLocales().sendMessage(sender, "error_channel_restricted_server", channel.get().getId()); + if (restrictedServer.equalsIgnoreCase(getSender().getServerName())) { + getPlugin().getLocales().sendMessage(getSender(), "error_channel_restricted_server", channel.get().getId()); return true; } } // Determine the players who will receive the message; - Channel.BroadcastScope broadcastScope = channel.get().getBroadcastScope(); + Channel.BroadcastScope scope = channel.get().getBroadcastScope(); // There's no point in allowing the console to send to local chat as it's not actually in any servers; // the message won't get sent to anyone - if (sender instanceof ConsolePlayer && (broadcastScope == Channel.BroadcastScope.LOCAL || - broadcastScope == Channel.BroadcastScope.LOCAL_PASSTHROUGH)) { - plugin.getLocales().sendMessage(sender, "error_console_local_scope"); + if (getSender() instanceof ConsoleUser && (scope == Channel.BroadcastScope.LOCAL || + scope == Channel.BroadcastScope.LOCAL_PASSTHROUGH)) { + getPlugin().getLocales().sendMessage(getSender(), "error_console_local_scope"); return true; } - StringBuilder msg = new StringBuilder(message); - if (!ChatMessage.passesFilters(plugin, sender, msg, channel.get())) { + final Optional formatted = getPlugin().filter(getSender(), getMessage(), getPlugin().getChannelFilters(channel.get())); + if (formatted.isEmpty()) { return true; } - message = msg.toString(); + setMessage(formatted.get()); - HashSet messageRecipients = new HashSet<>(); - switch (broadcastScope) { - case GLOBAL, GLOBAL_PASSTHROUGH -> messageRecipients.addAll(plugin.getOnlinePlayers()); - case LOCAL, LOCAL_PASSTHROUGH -> messageRecipients.addAll(plugin.getOnlinePlayersOnServer(sender)); + HashSet messageRecipients = new HashSet<>(); + switch (scope) { + case GLOBAL, GLOBAL_PASSTHROUGH -> messageRecipients.addAll(getPlugin().getOnlinePlayers()); + case LOCAL, LOCAL_PASSTHROUGH -> + messageRecipients.addAll(getPlugin().getOnlinePlayersOnServer(getSender())); default -> { } // No message recipients if the channel is exclusively passed through; let the backend handle it } @@ -110,102 +107,72 @@ public boolean dispatch() { // The events API has no effect on messages in passthrough channels. // Local/global passthrough channels will have their proxy-side message affected, // and non-passthrough messages will also be affected by the API. - plugin.getEventDispatcher().dispatchChatMessageEvent(sender, message, targetChannelId).thenAccept(event -> { - if (event.isCancelled()) return; - - sender = event.getSender(); + getPlugin().fireChatMessageEvent(getSender(), getMessage(), channel.get().getId()).thenAccept(event -> { + if (event.isCancelled()) { + return; + } + // Handle event changes + setSender(event.getSender()); + setMessage(event.getMessage()); if (!event.getChannelId().equals(channel.get().getId())) { - if (plugin.getSettings().getChannels().containsKey(event.getChannelId())) { - channel.set(plugin.getSettings().getChannels().get(event.getChannelId())); - } + getPlugin().getChannels().getChannel(event.getChannelId()).ifPresent(channel::set); } - message = event.getMessage(); - // Dispatch message to all applicable users in the scope with permission who are not on a restricted server - MESSAGE_DISPATCH: - for (Player recipient : messageRecipients) { - if (channel.get().getReceivePermission() != null) { - if (!recipient.hasPermission(channel.get().getReceivePermission()) && !(recipient.getUuid().equals(sender.getUuid()))) { - continue; - } + messageRecipients.forEach(recipient -> { + final Optional receivePermission = channel.get().getPermissions().getReceive(); + boolean isSender = recipient.getUuid().equals(getSender().getUuid()); + if (!isSender && receivePermission.isPresent() && !recipient.hasPermission(receivePermission.get())) { + return; } - for (String restrictedServer : channel.get().getRestrictedServers()) { - if (restrictedServer.equalsIgnoreCase(recipient.getServerName())) { - continue MESSAGE_DISPATCH; - } + if (channel.get().isServerRestricted(recipient.getServerName())) { + return; } - - plugin.getLocales().sendChannelMessage(recipient, sender, channel.get(), message); - } - - // If the message is on a local channel, dispatch local spy messages to appropriate spies. - if (broadcastScope == Channel.BroadcastScope.LOCAL || broadcastScope == Channel.BroadcastScope.LOCAL_PASSTHROUGH) { - if (plugin.getSettings().doLocalSpyCommand()) { - if (!plugin.getSettings().isLocalSpyChannelExcluded(channel.get())) { - final Map spies = plugin.getPlayerCache().getLocalSpyMessageReceivers(sender.getServerName(), plugin); - for (Player spy : spies.keySet()) { - if (spy.getUuid().equals(sender.getUuid())) { - continue; - } - if (!spy.hasPermission("huskchat.command.localspy")) { - try { - plugin.getPlayerCache().removeLocalSpy(spy); - } catch (IOException e) { - plugin.log(Level.SEVERE, "Failed to remove local spy after failed permission check", e); - } - continue; + getPlugin().getLocales().sendChannelMessage(recipient, getSender(), channel.get(), getMessage(), getPlugin()); + + + // If the message is on a local channel, dispatch local spy messages to appropriate spies. + if (getPlugin().getSettings().getLocalSpy().isEnabled() + && !getPlugin().getSettings().getLocalSpy().getExcludedLocalChannels().contains(channel.get().getId()) + && scope.isOneOf(Channel.BroadcastScope.LOCAL, Channel.BroadcastScope.LOCAL_PASSTHROUGH)) { + /*final Map spies = getPlugin().getPlayerCache() + .getLocalSpyMessageReceivers(getSender().getServerName(), getPlugin()); + for (OnlineUser spy : spies.keySet()) { + if (spy.getUuid().equals(getSender().getUuid())) { + continue; + } + if (!spy.hasPermission("huskchat.command.localspy")) { + try { + getPlugin().getPlayerCache().removeLocalSpy(spy); + } catch (IOException e) { + getPlugin().log(Level.SEVERE, "Failed to remove local spy after failed permission check", e); } - final PlayerCache.SpyColor color = spies.get(spy); - plugin.getLocales().sendLocalSpy(spy, color, sender, channel.get(), message); + continue; } - } + final UserCache.SpyColor color = spies.get(spy); + getPlugin().getLocales().sendLocalSpy(spy, color, getSender(), channel.get(), getMessage(), getPlugin()); + }*/ } - } - // Log a message to console if enabled on the channel - if (channel.get().doLogMessages()) { - final String logFormat = plugin.getSettings().getChannelLogFormat() - .replaceAll("%channel%", channel.get().getId().toUpperCase()) - .replaceAll("%sender%", sender.getName()); - plugin.log(Level.INFO, logFormat + message); - } + // Log a message to console if enabled on the channel + if (channel.get().isLogToConsole()) { + final String logFormat = getPlugin().getChannels().getChannelLogFormat() + .replaceAll("%channel%", channel.get().getId().toUpperCase()) + .replaceAll("%sender%", getSender().getName()); + getPlugin().log(Level.INFO, logFormat + getMessage()); + } - // Dispatch message to a Discord webhook if enabled - if (plugin.getSettings().doDiscordIntegration()) { - plugin.getDiscordHook().ifPresent(hook -> hook.postMessage(this)); - } + // Dispatch message to a Discord webhook if enabled + if (getPlugin().getSettings().getDiscord().isEnabled()) { + getPlugin().getDiscordHook().ifPresent(hook -> hook.postMessage(this)); + } + }); }); // Non-passthrough messages should always be canceled in the proxy-specific code - return !broadcastScope.isPassThrough; + return !scope.isPassThrough(); } - // This is a static method to allow for filters to be applied to passthrough channels due to those not going through this class - // The StringBuilder allows us to modify the message if a replacer requires it. - // Returns true if it passes all chat filters. - public static boolean passesFilters(@NotNull HuskChat plugin, @NotNull Player sender, @NotNull StringBuilder message, @NotNull Channel channel) { - // If the message is to be filtered, then perform filter checks (unless they have the bypass permission) - if (channel.isFilter() && !sender.hasPermission("huskchat.bypass_filters")) { - for (ChatFilter filter : plugin.getSettings().getChatFilters().getOrDefault(channel.getId(), List.of())) { - if (sender.hasPermission(filter.getFilterIgnorePermission())) { - continue; - } - if (!filter.isAllowed(sender, message.toString())) { - plugin.getLocales().sendMessage(sender, filter.getFailureErrorMessageId()); - return false; - } - - if (filter instanceof ReplacerFilter replacer && !channel.getBroadcastScope().isPassThrough) { - String msg = message.toString(); - message.delete(0, message.length()); - message.append(replacer.replace(msg)); - } - } - } - - return true; - } } \ No newline at end of file diff --git a/common/src/main/java/net/william278/huskchat/message/PrivateMessage.java b/common/src/main/java/net/william278/huskchat/message/PrivateMessage.java index 950d872a..e460d9cc 100644 --- a/common/src/main/java/net/william278/huskchat/message/PrivateMessage.java +++ b/common/src/main/java/net/william278/huskchat/message/PrivateMessage.java @@ -20,11 +20,10 @@ package net.william278.huskchat.message; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.filter.ChatFilter; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; -import net.william278.huskchat.replacer.ReplacerFilter; +import net.william278.huskchat.config.Settings; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -37,16 +36,18 @@ */ public class PrivateMessage { private final HuskChat plugin; + private final Settings.MessageSettings settings; private final List targetUsernames; private final String message; - private Player sender; + private OnlineUser sender; - public PrivateMessage(@NotNull Player sender, @NotNull List targetUsernames, @NotNull String message, + public PrivateMessage(@NotNull OnlineUser sender, @NotNull List targetUsernames, @NotNull String message, @NotNull HuskChat plugin) { this.sender = sender; this.targetUsernames = targetUsernames; this.message = message; this.plugin = plugin; + this.settings = plugin.getSettings().getMessageCommand(); } /** @@ -54,7 +55,7 @@ public PrivateMessage(@NotNull Player sender, @NotNull List targetUserna */ public void dispatch() { // Verify that the player is not sending a message from a server where channel access is restricted - for (String restrictedServer : plugin.getSettings().getMessageCommandRestrictedServers()) { + for (String restrictedServer : settings.getRestrictedServers()) { if (restrictedServer.equalsIgnoreCase(sender.getServerName())) { plugin.getLocales().sendMessage(sender, "error_message_restricted_server"); return; @@ -62,18 +63,18 @@ public void dispatch() { } // Verify that the player is not sending a group message when they are turned off - if (targetUsernames.size() > 1 && !plugin.getSettings().doGroupMessages()) { + if (targetUsernames.size() > 1 && !settings.getGroupMessages().isEnabled()) { plugin.getLocales().sendMessage(sender, "error_group_messages_disabled"); return; } // Validate message targets - final ArrayList targetPlayers = new ArrayList<>(); + final ArrayList targetPlayers = new ArrayList<>(); final HashSet targetUUIDs = new HashSet<>(); for (String targetUsername : targetUsernames) { - Optional targetPlayer; - if (ConsolePlayer.isConsolePlayer(targetUsername)) { - targetPlayer = Optional.of(ConsolePlayer.create(plugin)); + Optional targetPlayer; + if (ConsoleUser.isConsolePlayer(targetUsername)) { + targetPlayer = Optional.of(ConsoleUser.wrap(plugin)); } else { targetPlayer = plugin.findPlayer(targetUsername); } @@ -96,7 +97,7 @@ public void dispatch() { } // Validate that there aren't too many users - final int maxGroupMembers = plugin.getSettings().getMaxGroupMessageSize(); + final int maxGroupMembers = settings.getGroupMessages().getMaxSize(); if (targetPlayers.size() > maxGroupMembers) { plugin.getLocales().sendMessage(sender, "error_group_messages_max", Integer.toString(maxGroupMembers)); return; @@ -112,79 +113,68 @@ public void dispatch() { return; } - AtomicReference finalMessage = new AtomicReference<>(message); - // If the message is to be filtered, then perform filter checks (unless they have the bypass permission) - if (!sender.hasPermission("huskchat.bypass_filters")) { - for (ChatFilter filter : plugin.getSettings().getChatFilters().get("private_messages")) { - if (sender.hasPermission(filter.getFilterIgnorePermission())) { - continue; - } - if (plugin.getSettings().isCensorPrivateMessages() && !filter.isAllowed(sender, finalMessage.get())) { - plugin.getLocales().sendMessage(sender, filter.getFailureErrorMessageId()); - return; - } - if (filter instanceof ReplacerFilter replacer) { - finalMessage.set(replacer.replace(finalMessage.get())); - } - } + final Optional filtered = plugin.filter(sender, message, plugin.getMessageFilters()); + if (filtered.isEmpty()) { + return; } + final AtomicReference finalMessage = new AtomicReference<>(filtered.get()); - plugin.getEventDispatcher().dispatchPrivateMessageEvent(sender, targetPlayers, finalMessage.get()).thenAccept(event -> { + plugin.firePrivateMessageEvent(sender, targetPlayers, finalMessage.get()).thenAccept(event -> { if (event.isCancelled()) return; sender = event.getSender(); - final List receivers = event.getRecipients(); + final List receivers = event.getRecipients(); finalMessage.set(event.getMessage()); // Show that the message has been sent - PlayerCache.setLastMessenger(sender.getUuid(), receivers); - plugin.getLocales().sendOutboundPrivateMessage(sender, receivers, finalMessage.get()); + UserCache.setLastMessenger(sender.getUuid(), receivers); + plugin.getLocales().sendOutboundPrivateMessage(sender, receivers, finalMessage.get(), plugin); // Show the received message - for (Player target : receivers) { - final ArrayList receivedMessageFrom = new ArrayList<>(receivers); + for (OnlineUser target : receivers) { + final ArrayList receivedMessageFrom = new ArrayList<>(receivers); receivedMessageFrom.removeIf(player -> player.getUuid().equals(target.getUuid())); receivedMessageFrom.add(0, sender); - PlayerCache.setLastMessenger(target.getUuid(), receivedMessageFrom); + UserCache.setLastMessenger(target.getUuid(), receivedMessageFrom); } - plugin.getLocales().sendInboundPrivateMessage(receivers, sender, finalMessage.get()); + plugin.getLocales().sendInboundPrivateMessage(receivers, sender, finalMessage.get(), plugin); // Show a message to social spies - if (plugin.getSettings().doSocialSpyCommand()) { + if (plugin.getSettings().getSocialSpy().isEnabled()) { if (!(sender.hasPermission("huskchat.command.socialspy.bypass") || receivers.stream() .findFirst().orElseThrow(() -> new IllegalStateException("No receivers available for message")) .hasPermission("huskchat.command.socialspy.bypass"))) { - final Map spies = plugin.getPlayerCache().getSocialSpyMessageReceivers(receivers); - for (Player spy : spies.keySet()) { + final Map spies = plugin.getUserCache().getSocialSpies(receivers, plugin); + for (OnlineUser spy : spies.keySet()) { if (spy.getUuid().equals(sender.getUuid())) { continue; } if (!spy.hasPermission("huskchat.command.socialspy")) { try { - plugin.getPlayerCache().removeSocialSpy(spy); + plugin.getUserCache().removeSocialSpy(spy); } catch (IOException e) { plugin.log(Level.SEVERE, "Failed to remove social spy after failed permission check", e); } continue; } - final PlayerCache.SpyColor color = spies.get(spy); - plugin.getLocales().sendSocialSpy(spy, color, sender, receivers, finalMessage.get()); + final UserCache.SpyColor color = spies.get(spy); + plugin.getLocales().sendSocialSpy(spy, color, sender, receivers, finalMessage.get(), plugin); } } } // Log the private message to console if that is enabled - if (plugin.getSettings().doLogPrivateMessages()) { + if (settings.isLogToConsole()) { // Log all recipients of the message final StringJoiner formattedPlayers = new StringJoiner(", "); - for (Player player : receivers) { + for (OnlineUser player : receivers) { formattedPlayers.add(player.getName()); } - final String logFormat = plugin.getSettings().getMessageLogFormat() + final String logFormat = settings.getLogFormat() .replaceAll("%sender%", sender.getName()) .replaceAll("%receiver%", formattedPlayers.toString()); plugin.log(Level.INFO, logFormat + finalMessage); diff --git a/common/src/main/java/net/william278/huskchat/placeholders/DefaultReplacer.java b/common/src/main/java/net/william278/huskchat/placeholders/DefaultReplacer.java index 4b6befca..9dd6148b 100644 --- a/common/src/main/java/net/william278/huskchat/placeholders/DefaultReplacer.java +++ b/common/src/main/java/net/william278/huskchat/placeholders/DefaultReplacer.java @@ -20,7 +20,7 @@ package net.william278.huskchat.placeholders; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.text.SimpleDateFormat; @@ -40,7 +40,7 @@ public DefaultReplacer(@NotNull HuskChat plugin) { } @Override - public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull Player player) { + public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player) { return Placeholder.replace(message, plugin, player); } @@ -127,10 +127,10 @@ public enum Placeholder { /** * Function to replace placeholders with a real value */ - private final BiFunction replacer; + private final BiFunction replacer; private final Set aliases = new HashSet<>(); - Placeholder(@NotNull BiFunction replacer, @NotNull String... aliases) { + Placeholder(@NotNull BiFunction replacer, @NotNull String... aliases) { this.replacer = replacer; this.aliases.add(this.name().toLowerCase(Locale.ENGLISH)); this.aliases.addAll(Set.of(aliases)); @@ -144,7 +144,7 @@ public enum Placeholder { * @param player The player to replace placeholders for * @return The string with placeholders replaced */ - private static CompletableFuture replace(@NotNull String format, @NotNull HuskChat plugin, @NotNull Player player) { + private static CompletableFuture replace(@NotNull String format, @NotNull HuskChat plugin, @NotNull OnlineUser player) { for (Placeholder placeholder : values()) { for (String alias : placeholder.aliases) { format = format.replace("%" + alias + "%", placeholder.replacer.apply(plugin, player)); diff --git a/common/src/main/java/net/william278/huskchat/placeholders/PAPIProxyBridgeReplacer.java b/common/src/main/java/net/william278/huskchat/placeholders/PAPIProxyBridgeReplacer.java index 4d95c0e0..631326a8 100644 --- a/common/src/main/java/net/william278/huskchat/placeholders/PAPIProxyBridgeReplacer.java +++ b/common/src/main/java/net/william278/huskchat/placeholders/PAPIProxyBridgeReplacer.java @@ -20,7 +20,7 @@ package net.william278.huskchat.placeholders; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import net.william278.papiproxybridge.api.PlaceholderAPI; import org.jetbrains.annotations.NotNull; @@ -31,12 +31,12 @@ public class PAPIProxyBridgeReplacer implements PlaceholderReplacer { private final PlaceholderAPI instance; public PAPIProxyBridgeReplacer(@NotNull HuskChat plugin) { - this.instance = PlaceholderAPI.getInstance(); - instance.setCacheExpiry(plugin.getSettings().getPapiProxyBridgeCacheTime()); + this.instance = PlaceholderAPI.createInstance(); + instance.setCacheExpiry(plugin.getSettings().getPlaceholder().getCacheTime()); } @Override - public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull Player player) { + public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player) { return instance.formatPlaceholders(message, player.getUuid()); } } diff --git a/common/src/main/java/net/william278/huskchat/placeholders/PlaceholderReplacer.java b/common/src/main/java/net/william278/huskchat/placeholders/PlaceholderReplacer.java index 91870b7f..9a1cbe9e 100644 --- a/common/src/main/java/net/william278/huskchat/placeholders/PlaceholderReplacer.java +++ b/common/src/main/java/net/william278/huskchat/placeholders/PlaceholderReplacer.java @@ -19,13 +19,13 @@ package net.william278.huskchat.placeholders; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; public interface PlaceholderReplacer { - CompletableFuture formatPlaceholders(@NotNull String message, @NotNull Player player); + CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player); } diff --git a/common/src/main/java/net/william278/huskchat/player/PlayerCache.java b/common/src/main/java/net/william278/huskchat/player/PlayerCache.java deleted file mode 100644 index 23cc9ccd..00000000 --- a/common/src/main/java/net/william278/huskchat/player/PlayerCache.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.player; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.block.implementation.Section; -import net.william278.huskchat.HuskChat; -import net.william278.huskchat.channel.Channel; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.logging.Level; - -/** - * A cache for persisting player data - */ -public class PlayerCache { - - private final HuskChat plugin; - private final Map playerChannels; - private final HashMap localSpies = new HashMap<>(); - private final HashMap socialSpies = new HashMap<>(); - - public PlayerCache(@NotNull HuskChat plugin) { - this.plugin = plugin; - this.playerChannels = new LinkedHashMap<>(); - - try { - final YamlDocument spies = YamlDocument.create(new File(plugin.getDataFolder(), "spies.yml")); - if (spies.contains("local")) { - Section local = spies.getSection("local"); - - for (Object name : local.getKeys()) { - localSpies.put(UUID.fromString(name.toString()), - SpyColor.valueOf(local.getSection(name.toString()).getString("color"))); - } - } - - if (spies.contains("social")) { - Section social = spies.getSection("social"); - - for (Object name : social.getKeys()) { - socialSpies.put(UUID.fromString(name.toString()), - SpyColor.valueOf(social.getSection(name.toString()).getString("color"))); - } - } - } catch (IOException e) { - plugin.log(Level.WARNING, "Error loading spy data", e); - } - } - - public String getPlayerChannel(UUID uuid) { - if (!playerChannels.containsKey(uuid)) { - return plugin.getSettings().getDefaultChannel(); - } - return playerChannels.get(uuid); - } - - public void setPlayerChannel(UUID uuid, String playerChannel) { - playerChannels.put(uuid, playerChannel); - } - - - /** - * Switch the {@link Player}'s channel - * - * @param player {@link Player} to switch the channel of - * @param channelID ID of the channel to switch to - */ - public void switchPlayerChannel(@NotNull Player player, @NotNull String channelID) { - final Channel channel = plugin.getSettings().getChannels().get(channelID); - if (channel == null) { - plugin.getLocales().sendMessage(player, "error_invalid_channel"); - return; - } - - if (channel.getSendPermission() != null) { - if (!player.hasPermission(channel.getSendPermission())) { - plugin.getLocales().sendMessage(player, "error_no_permission_send", channel.getId()); - return; - } - } - setPlayerChannel(player.getUuid(), channel.getId()); - plugin.getLocales().sendMessage(player, "channel_switched", channel.getId()); - } - - - // Map of users last private message target for /reply command - @NotNull - private static final Map> lastMessagePlayers = new HashMap<>(); - - public static Optional> getLastMessengers(@NotNull UUID uuid) { - if (lastMessagePlayers.containsKey(uuid)) { - return Optional.of(lastMessagePlayers.get(uuid)); - } - return Optional.empty(); - } - - public static void setLastMessenger(@NotNull UUID playerToSet, @NotNull List lastMessengers) { - final HashSet uuidPlayers = new HashSet<>(); - for (Player player : lastMessengers) { - uuidPlayers.add(player.getUuid()); - } - lastMessagePlayers.put(playerToSet, uuidPlayers); - } - - public boolean isSocialSpying(@NotNull Player player) { - return socialSpies.containsKey(player.getUuid()); - } - - public void setSocialSpy(@NotNull Player player) throws IOException { - socialSpies.put(player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); - addSpy("social", player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); - } - - public void setSocialSpy(@NotNull Player player, @NotNull SpyColor spyColor) throws IOException { - socialSpies.put(player.getUuid(), spyColor); - addSpy("social", player.getUuid(), spyColor); - } - - public void removeSocialSpy(@NotNull Player player) throws IOException { - socialSpies.remove(player.getUuid()); - removeSpy("social", player.getUuid()); - } - - // Determines who is going to receive a spy message - @NotNull - public Map getSocialSpyMessageReceivers(@NotNull List messageRecipients) { - final Map receivers = new LinkedHashMap<>(); - - calculateSpies: - for (UUID player : socialSpies.keySet()) { - final SpyColor color = socialSpies.get(player); - final Optional spy = plugin.getPlayer(player); - if (spy.isEmpty()) { - continue; - } - for (Player messageRecipient : messageRecipients) { - if (player.equals(messageRecipient.getUuid())) { - continue calculateSpies; - } - } - receivers.put(spy.get(), color); - } - return receivers; - } - - public boolean isLocalSpying(Player player) { - return localSpies.containsKey(player.getUuid()); - } - - public void setLocalSpy(Player player) throws IOException { - localSpies.put(player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); - addSpy("local", player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); - } - - public void setLocalSpy(Player player, SpyColor spyColor) throws IOException { - localSpies.put(player.getUuid(), spyColor); - addSpy("local", player.getUuid(), spyColor); - } - - public void removeLocalSpy(Player player) throws IOException { - localSpies.remove(player.getUuid()); - removeSpy("local", player.getUuid()); - } - - @NotNull - public Map getLocalSpyMessageReceivers(String localMessageServer, HuskChat implementor) { - final Map receivers = new LinkedHashMap<>(); - for (UUID player : localSpies.keySet()) { - final SpyColor color = localSpies.get(player); - final Optional spy = implementor.getPlayer(player); - if (spy.isEmpty()) { - continue; - } - if (spy.get().getServerName().equals(localMessageServer)) { - continue; - } - receivers.put(spy.get(), color); - } - return receivers; - } - - // Adds spy state to data file - public void addSpy(String type, UUID uuid, SpyColor spyColor) throws IOException { - if (!type.equals("local") && !type.equals("social")) { - return; - } - final YamlDocument spies = YamlDocument.create(new File(plugin.getDataFolder(), "spies.yml")); - - if (!spies.contains(type)) { - spies.createSection(type); - } - if (!spies.getSection(type).contains(uuid.toString())) { - spies.getSection(type).createSection(uuid.toString()); - } - - spies.getSection(type) - .getSection(uuid.toString()) - .set("color", spyColor.toString()); - spies.save(); - } - - // Removes spy state from data file - public void removeSpy(String type, UUID uuid) throws IOException { - if (!type.equals("local") && !type.equals("social")) return; - - YamlDocument spies = YamlDocument.create(new File(plugin.getDataFolder(), "spies.yml")); - - if (!spies.contains(type)) return; - if (!spies.getSection(type).contains(uuid.toString())) return; - - spies.getSection(type) - .remove(uuid.toString()); - - if (spies.getSection(type).getKeys().size() == 0) { - spies.remove(type); - } - - spies.save(); - } - - - /** - * Color used for displaying chat - */ - public enum SpyColor { - DARK_RED("&4"), - RED("&c"), - GOLD("&6"), - YELLOW("&e"), - DARK_GREEN("&2"), - GREEN("&a"), - AQUA("&b"), - DARK_AQUA("&3"), - DARK_BLUE("&1"), - BLUE("&9"), - LIGHT_PURPLE("&d"), - DARK_PURPLE("&5"), - WHITE("&f"), - GRAY("&7"), - DARK_GRAY("&8"), - BLACK("&9"); - - public static final SpyColor DEFAULT_SPY_COLOR = DARK_GRAY; - public final String colorCode; - - SpyColor(@NotNull String colorCode) { - this.colorCode = colorCode; - } - - @NotNull - public static List getColorStrings() { - List colors = new ArrayList<>(); - for (SpyColor color : SpyColor.values()) { - colors.add(color.name().toLowerCase()); - } - return colors; - } - - public static Optional getColor(@NotNull String colorInput) { - for (SpyColor color : SpyColor.values()) { - if (color.colorCode.replace("&", "").equals(colorInput.replace("&", "")) - || color.name().equalsIgnoreCase(colorInput.toUpperCase())) { - return Optional.of(color); - } - } - return Optional.empty(); - } - } - -} diff --git a/common/src/main/java/net/william278/huskchat/replacer/ReplacerFilter.java b/common/src/main/java/net/william278/huskchat/replacer/ReplacerFilter.java deleted file mode 100644 index f0a0b6f0..00000000 --- a/common/src/main/java/net/william278/huskchat/replacer/ReplacerFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.replacer; - -import net.william278.huskchat.filter.ChatFilter; -import org.jetbrains.annotations.NotNull; - -/** - * A special kind of {@link ChatFilter} that can modify the contents of a message - */ -public abstract class ReplacerFilter extends ChatFilter { - - /** - * Replace the input message from the user - * - * @param message The input message - * @return The output - replaced message - */ - @NotNull - public abstract String replace(@NotNull String message); - -} diff --git a/common/src/main/java/net/william278/huskchat/player/ConsolePlayer.java b/common/src/main/java/net/william278/huskchat/user/ConsoleUser.java similarity index 74% rename from common/src/main/java/net/william278/huskchat/player/ConsolePlayer.java rename to common/src/main/java/net/william278/huskchat/user/ConsoleUser.java index 421e094b..ae0babb8 100644 --- a/common/src/main/java/net/william278/huskchat/player/ConsolePlayer.java +++ b/common/src/main/java/net/william278/huskchat/user/ConsoleUser.java @@ -17,7 +17,7 @@ * limitations under the License. */ -package net.william278.huskchat.player; +package net.william278.huskchat.user; import net.kyori.adventure.audience.Audience; import net.william278.huskchat.HuskChat; @@ -25,26 +25,13 @@ import java.util.UUID; -public class ConsolePlayer implements Player { +public class ConsoleUser extends OnlineUser { - private static final UUID consoleUUID = new UUID(0, 0); - private static final String consoleUsername = "[CONSOLE]"; - private final HuskChat plugin; + private static final UUID CONSOLE_UUID = new UUID(0, 0); + private static final String CONSOLE_USERNAME = "[CONSOLE]"; - private ConsolePlayer(@NotNull HuskChat plugin) { - this.plugin = plugin; - } - - @Override - @NotNull - public String getName() { - return consoleUsername; - } - - @Override - @NotNull - public UUID getUuid() { - return consoleUUID; + private ConsoleUser(@NotNull HuskChat plugin) { + super(CONSOLE_USERNAME, CONSOLE_UUID, plugin); } @Override @@ -81,8 +68,8 @@ public Audience getAudience() { * @return The ConsolePlayer */ @NotNull - public static ConsolePlayer create(@NotNull HuskChat plugin) { - return new ConsolePlayer(plugin); + public static ConsoleUser wrap(@NotNull HuskChat plugin) { + return new ConsoleUser(plugin); } /** @@ -92,7 +79,7 @@ public static ConsolePlayer create(@NotNull HuskChat plugin) { * @return {@code true} if the UUID is the console */ public static boolean isConsolePlayer(@NotNull UUID uuid) { - return uuid.equals(consoleUUID); + return uuid.equals(CONSOLE_UUID); } /** @@ -102,7 +89,7 @@ public static boolean isConsolePlayer(@NotNull UUID uuid) { * @return {@code true} if the username is the console */ public static boolean isConsolePlayer(@NotNull String username) { - return username.equalsIgnoreCase(consoleUsername); + return username.equalsIgnoreCase(CONSOLE_USERNAME); } } diff --git a/common/src/main/java/net/william278/huskchat/player/Player.java b/common/src/main/java/net/william278/huskchat/user/OnlineUser.java similarity index 61% rename from common/src/main/java/net/william278/huskchat/player/Player.java rename to common/src/main/java/net/william278/huskchat/user/OnlineUser.java index b19092ce..4b56e72d 100644 --- a/common/src/main/java/net/william278/huskchat/player/Player.java +++ b/common/src/main/java/net/william278/huskchat/user/OnlineUser.java @@ -17,42 +17,40 @@ * limitations under the License. */ -package net.william278.huskchat.player; +package net.william278.huskchat.user; import de.themoep.minedown.adventure.MineDown; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; +import net.william278.huskchat.HuskChat; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.TestOnly; import java.util.UUID; /** * Abstract cross-platform Player object */ -public interface Player { +public abstract class OnlineUser extends User { - /** - * Return the player's name - * - * @return the player's name - */ - @NotNull - String getName(); + protected HuskChat plugin; - /** - * Return the player's {@link UUID} - * - * @return the player {@link UUID} - */ - @NotNull - UUID getUuid(); + protected OnlineUser(@NotNull String username, @NotNull UUID uuid, @NotNull HuskChat plugin) { + super(username, uuid); + this.plugin = plugin; + } + + @TestOnly + protected OnlineUser(@NotNull String username, @NotNull UUID uuid) { + super(username, uuid); + } /** * Return the player's ping * * @return the player's ping */ - int getPing(); + public abstract int getPing(); /** * Return the name of the server the player is connected to @@ -60,14 +58,14 @@ public interface Player { * @return player's server name */ @NotNull - String getServerName(); + public abstract String getServerName(); /** * Return the number of people on that player's server * * @return player count on the player's server */ - int getPlayersOnServer(); + public abstract int getPlayersOnServer(); /** * Returns if the player has the permission node @@ -75,31 +73,18 @@ public interface Player { * @param node The permission node string * @return {@code true} if the player has the node; {@code false} otherwise */ - boolean hasPermission(String node); + public abstract boolean hasPermission(String node); - /** - * Get the audience for this player - * - * @return the audience for this player - */ @NotNull - Audience getAudience(); + public Audience getAudience() { + return plugin.getAudience(getUuid()); + } - /** - * Send a message to the player - * - * @param mineDown the message to send - */ - default void sendMessage(@NotNull MineDown mineDown) { - this.sendMessage(mineDown.toComponent()); + public void sendMessage(@NotNull Component message) { + getAudience().sendMessage(message); } - /** - * Send a message to the player - * - * @param component the message to send - */ - default void sendMessage(@NotNull Component component) { - getAudience().sendMessage(component); + public void sendMessage(@NotNull MineDown mineDown) { + sendMessage(mineDown.toComponent()); } } diff --git a/common/src/main/java/net/william278/huskchat/user/User.java b/common/src/main/java/net/william278/huskchat/user/User.java new file mode 100644 index 00000000..7d346119 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/user/User.java @@ -0,0 +1,58 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.user; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +public class User implements Comparable { + + @NotNull + private String name; + + @NotNull + private UUID uuid; + + @NotNull + public static User of(@NotNull UUID uuid, @NotNull String name) { + return new User(name, uuid); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof User user)) { + return false; + } + return user.getUuid().equals(uuid); + } + + @Override + public int compareTo(@NotNull User o) { + return name.compareTo(o.getName()); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/william278/huskchat/user/UserCache.java b/common/src/main/java/net/william278/huskchat/user/UserCache.java new file mode 100644 index 00000000..80ce5d67 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/user/UserCache.java @@ -0,0 +1,214 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.user; + +import de.exlll.configlib.Configuration; +import lombok.NoArgsConstructor; +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.channel.Channel; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.*; + +/** + * A cache for persisting player data + */ +@Configuration +@NoArgsConstructor +public class UserCache { + + private Map playerChannels = new HashMap<>(); + private HashMap localSpies = new HashMap<>(); + private HashMap socialSpies = new HashMap<>(); + + @NotNull + public String getPlayerChannel(@NotNull UUID uuid) { + return playerChannels.get(uuid); + } + + public void setPlayerChannel(@NotNull UUID uuid, @NotNull String channelId) { + playerChannels.put(uuid, channelId); + } + + + /** + * Switch the {@link OnlineUser}'s channel + * + * @param player {@link OnlineUser} to switch the channel of + * @param channelId ID of the channel to switch to + */ + public void switchPlayerChannel(@NotNull OnlineUser player, @NotNull String channelId, @NotNull HuskChat plugin) { + final Optional optionalChannel = plugin.getChannels().getChannel(channelId); + if (optionalChannel.isEmpty()) { + plugin.getLocales().sendMessage(player, "error_invalid_channel"); + return; + } + + final Channel channel = optionalChannel.get(); + if (!channel.canUserSend(player)) { + plugin.getLocales().sendMessage(player, "error_no_permission_send", channel.getId()); + return; + } + setPlayerChannel(player.getUuid(), channel.getId()); + plugin.getLocales().sendMessage(player, "channel_switched", channel.getId()); + } + + + // Map of users last private message target for /reply command + @NotNull + private static final Map> lastMessagePlayers = new HashMap<>(); + + public static Optional> getLastMessengers(@NotNull UUID uuid) { + if (lastMessagePlayers.containsKey(uuid)) { + return Optional.of(lastMessagePlayers.get(uuid)); + } + return Optional.empty(); + } + + public static void setLastMessenger(@NotNull UUID playerToSet, @NotNull List lastMessengers) { + final HashSet uuidPlayers = new HashSet<>(); + for (OnlineUser player : lastMessengers) { + uuidPlayers.add(player.getUuid()); + } + lastMessagePlayers.put(playerToSet, uuidPlayers); + } + + public boolean isSocialSpying(@NotNull OnlineUser player) { + return socialSpies.containsKey(player.getUuid()); + } + + public void setSocialSpy(@NotNull OnlineUser player) throws IOException { + socialSpies.put(player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); + } + + public void setSocialSpy(@NotNull OnlineUser player, @NotNull SpyColor spyColor) throws IOException { + socialSpies.put(player.getUuid(), spyColor); + } + + public void removeSocialSpy(@NotNull OnlineUser player) throws IOException { + socialSpies.remove(player.getUuid()); + } + + // Determines who is going to receive a spy message + @NotNull + public Map getSocialSpies(@NotNull List recipients, @NotNull HuskChat plugin) { + final Map receivers = new LinkedHashMap<>(); + + calculateSpies: + for (UUID player : socialSpies.keySet()) { + final SpyColor color = socialSpies.get(player); + final Optional spy = plugin.getPlayer(player); + if (spy.isEmpty()) { + continue; + } + for (OnlineUser messageRecipient : recipients) { + if (player.equals(messageRecipient.getUuid())) { + continue calculateSpies; + } + } + receivers.put(spy.get(), color); + } + return receivers; + } + + public boolean isLocalSpying(OnlineUser player) { + return localSpies.containsKey(player.getUuid()); + } + + public void setLocalSpy(OnlineUser player) throws IOException { + localSpies.put(player.getUuid(), SpyColor.DEFAULT_SPY_COLOR); + } + + public void setLocalSpy(OnlineUser player, SpyColor spyColor) throws IOException { + localSpies.put(player.getUuid(), spyColor); + } + + public void removeLocalSpy(OnlineUser player) throws IOException { + localSpies.remove(player.getUuid()); + } + + @NotNull + public Map getLocalSpies(@NotNull String server, @NotNull HuskChat plugin) { + final Map receivers = new LinkedHashMap<>(); + for (UUID player : localSpies.keySet()) { + final SpyColor color = localSpies.get(player); + final Optional spy = plugin.getPlayer(player); + if (spy.isEmpty()) { + continue; + } + if (spy.get().getServerName().equals(server)) { + continue; + } + receivers.put(spy.get(), color); + } + return receivers; + } + + + /** + * Color used for displaying chat + */ + public enum SpyColor { + DARK_RED("&4"), + RED("&c"), + GOLD("&6"), + YELLOW("&e"), + DARK_GREEN("&2"), + GREEN("&a"), + AQUA("&b"), + DARK_AQUA("&3"), + DARK_BLUE("&1"), + BLUE("&9"), + LIGHT_PURPLE("&d"), + DARK_PURPLE("&5"), + WHITE("&f"), + GRAY("&7"), + DARK_GRAY("&8"), + BLACK("&9"); + + public static final SpyColor DEFAULT_SPY_COLOR = DARK_GRAY; + public final String colorCode; + + SpyColor(@NotNull String colorCode) { + this.colorCode = colorCode; + } + + @NotNull + public static List getColorStrings() { + List colors = new ArrayList<>(); + for (SpyColor color : SpyColor.values()) { + colors.add(color.name().toLowerCase()); + } + return colors; + } + + public static Optional getColor(@NotNull String colorInput) { + for (SpyColor color : SpyColor.values()) { + if (color.colorCode.replace("&", "").equals(colorInput.replace("&", "")) + || color.name().equalsIgnoreCase(colorInput.toUpperCase())) { + return Optional.of(color); + } + } + return Optional.empty(); + } + } + +} diff --git a/common/src/main/java/net/william278/huskchat/util/AudiencesProvider.java b/common/src/main/java/net/william278/huskchat/util/AudiencesProvider.java new file mode 100644 index 00000000..9e3f7bf3 --- /dev/null +++ b/common/src/main/java/net/william278/huskchat/util/AudiencesProvider.java @@ -0,0 +1,66 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.util; + + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.Audiences; +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +/** + * Interface for providing the {@link ConsoleUser} and {@link Audiences} instances + * + * @since 3.0 + */ +public interface AudiencesProvider { + + + /** + * Get the {@link Audience} instance for the given {@link UUID} + * + * @param user the {@link OnlineUser} to get the {@link UUID} for + * @return the {@link Audience} instance + */ + @NotNull + Audience getAudience(@NotNull UUID user); + + @NotNull + Audience getConsole(); + + /** + * Get the {@link ConsoleUser} instance + * + * @return the {@link ConsoleUser} instance + * @since 3.0 + */ + @NotNull + default ConsoleUser getConsoleUser() { + return ConsoleUser.wrap(getPlugin()); + } + + @NotNull + HuskChat getPlugin(); + +} \ No newline at end of file diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml deleted file mode 100644 index b26e9103..00000000 --- a/common/src/main/resources/config.yml +++ /dev/null @@ -1,237 +0,0 @@ -# ------------------------------ -# | HuskChat Config | -# | Developed by William278 | -# ------------------------------ -# Configuration guide: https://github.com/WiIIiam278/HuskChat/wiki -# To modify individual message locales, see the messages file. - -# General options. Default channel should match one of the channels below. -language: 'en-gb' -check_for_updates: true -default_channel: global -channel_log_format: '[CHAT] [%channel%] %sender%: ' -channel_command_aliases: # Must contain at least one item; the first being the primary alias of the command - - /channel - - /c - -# Placeholder configuration -placeholders: - use_papi: true # For proxy setups, requires PAPIProxyBridge on both the proxy/backend servers - cache_time: 3000 # If using PAPIProxyBridge, how long to cache placeholders for in milliseconds - -# Chat channel configuration. -# - You can edit the default channels and make your own if you would like. -# - Channels that have permissions set require them to send and receive messages respectively. -channels: - local: - format: '%fullname%&r&f: ' - broadcast_scope: LOCAL - log_to_console: true - shortcut_commands: - - /local - - /l - global: - format: '�fb9a&[G]&r&f %fullname%&r&f: ' - broadcast_scope: GLOBAL - log_to_console: true - shortcut_commands: - - /global - - /g - #restricted_servers: # Set where it is not possible to use this channel - # - minigame - staff: - format: '&e[Staff] %name%: &7' - broadcast_scope: GLOBAL - log_to_console: true - filtered: false - permissions: - send: 'huskchat.channel.staff.send' - receive: 'huskchat.channel.staff.receive' - shortcut_commands: - - /staff - - /sc - helpop: - format: '�fb9a&[HelpOp] %name%:&7 ' - broadcast_scope: GLOBAL - log_to_console: true - filtered: false - permissions: - receive: 'huskchat.channel.helpop.receive' - shortcut_commands: - - /helpop - - /helpme - -# Server Default Channel Configuration -# - Set a channel that the player will automatically be set to when changing servers -# - Players will still be able to switch channels and use /msg and /r unless you add servers to the restricted_servers list -#server_default_channels: -# minigame: local - -# Options for the /msg and /r commands -message_command: - enabled: true - msg_aliases: - - /msg - - /m - - /tell - - /whisper - - /w - - /pm - reply_aliases: - - /reply - - /r - log_to_console: true - log_format: '[MSG] [%sender% -> %receiver%]: ' - group_messages: # Whether to allow sending and replying to a message in a group (/msg User1,User2 ) - enabled: true - max_size: 10 - format: # Formats for the /msg command (MineDown syntax). A separate format is used for group private messages. - inbound: '�fb9a&%name% &8→ �fb9a&You&8:&f ' - outbound: '�fb9a&You &8→ �fb9a&%name%&8:&f ' - group_inbound: '�fb9a&%name% &8→ �fb9a&You [₍₊%group_amount_subscript%₎](gray show_text=&7%group_members% suggest_command=/msg %group_members_comma_separated% )&8:&f ' - group_outbound: '�fb9a&You &8→ �fb9a&%name% [₍₊%group_amount_subscript%₎](gray show_text=&7%group_members% suggest_command=/msg %group_members_comma_separated% )&8:&f ' - #restricted_servers: # Set where /msg and /r cannot be used - # - hub - -# Options for the /socialspy command -social_spy: - enabled: true - format: '&e[Spy] &7%name% &8→ &7%receiver_name%:%spy_color% ' - group_format: '&e[Spy] &7%name% &8→ &7%receiver_name% [₍₊%group_amount_subscript%₎](gray show_text=&7%group_members% suggest_command=/msg %group_members_comma_separated% ):%spy_color% ' - socialspy_aliases: - - /socialspy - - /ss -# Options for the /localspy command -local_spy: - enabled: true - format: '&e[Spy] &7[%channel%] %name%&8:%spy_color% ' - localspy_aliases: - - /localspy - - /ls - #excluded_local_channels: # Channels to exclude from local spying - # - local -# Options for the /broadcast command -broadcast_command: - enabled: true - broadcast_aliases: - - /broadcast - - /alert - format: '&6[Broadcast]&e ' - log_to_console: true - log_format: '[BROADCAST]: ' - -# Chat filter options -chat_filters: - # Filters against IP addresses and links - advertising_filter: - enabled: true - private_messages: false - broadcast_messages: false - channels: - - global - - local - # Filters against CAPS use; specify the maximum % a message can contain capital letters - caps_filter: - enabled: true - max_caps_percentage: 0.4 - private_messages: false - broadcast_messages: false - channels: - - global - - local - # Filters against users sending messages too quickly (configure how many messages users can send over a period of seconds) - spam_filter: - enabled: true - period_seconds: 4 - messages_per_period: 3 - private_messages: false - broadcast_messages: false - channels: - - global - - local - # Filters against profanity using machine learning. Requires Python 3.8+ on the server with jep and alt-profanity-check installed. - profanity_filter: - enabled: false - library_path: '' # Define a directory path for the jep library - mode: AUTOMATIC # Filter rule - AUTOMATIC or TOLERANCE. - tolerance: 0.78 # If using TOLERANCE mode, the algorithm will determine a profanity probability and compare with this to filter. - private_messages: false - broadcast_messages: false - channels: - - global - - local - repeat_filter: - enabled: true - previous_messages_to_check: 2 - private_messages: false - broadcast_messages: false - channels: - - global - - local - # Filters against the use of non-ASCII (Unicode) characters - ascii_filter: - enabled: false - private_messages: false - broadcast_messages: false - channels: - - global - - local - -# Chat message replacer options -message_replacers: - # Replaces text emoticons in messages with the correct emoji - emoji_replacer: - enabled: true - case_insensitive: false - private_messages: true - broadcast_messages: true - channels: - - global - - local - - helpop - - staff - emoji: # Emote character options: https://gist.githubusercontent.com/WiIIiam278/b74a6af6d9670350a60ad09d63b67169/raw/d8b596471c812eb2b68638469cb779d928bd733f/minecraft_unicode_characters.txt - ':)': '☺' - ':smile:': '☺' - ':-)': '☺' - ':(': '☹' - ':frown:': '☹' - ':-(': '☹' - ':fire:': '🔥' - - -# Options for customizing player join and quit messages -join_and_quit_messages: - join: - enabled: false - # Use the huskchat.join_message.[text] permission to override this per-group if needed - # Use the huskchat.silent_join permission for a player to join without sending a message - format: '&e%name% joined the network' - quit: - enabled: false - # Use the huskchat.quit_message.[text] permission to override this per-group if needed - # Use the huskchat.silent_quit permission for a player to quit without sending a message - format: '&e%name% left the network' - broadcast_scope: GLOBAL # Note that on Velocity/Bungee, PASSTHROUGH modes won't cancel local join/quit messages - -# Discord webhook options, which lets you send messages to a Discord channel -discord: - enabled: false - format_style: 'inline' # Format style to display Discord messages in - 'inline' or 'embedded' - channel_webhooks: - global: 'https://discord.com/api/webhooks/channel_id/secret' - spicord: - enabled: true # If Spicord is installed, use that for two-way message support - username_format: '@%discord_handle%' # Format of Discord users in-game. Note this doesn't support other placeholders - #receive_channel_map: # Send in-game messages on these channels to a specified Discord channel (by numeric ID) - # global: '123456789012345678' - #send_channel_map: # Send Discord messages on these channels (by numeric ID) to a specified in-game channel - # '123456789012345678': 'global' - -# If you want %servername% to show something other than the server name, -# set it here and uncomment this section -#server_name_replacement: -# lobby: hub - -# Version of the config file -config-version: 2 \ No newline at end of file diff --git a/common/src/main/resources/locales/bg-bg.yml b/common/src/main/resources/locales/bg-bg.yml index 011ff457..5eea333a 100644 --- a/common/src/main/resources/locales/bg-bg.yml +++ b/common/src/main/resources/locales/bg-bg.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | bg-bg, by Pukejoy_1 | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда.](#ff7e5e)' -error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте: %1%](#ff7e5e)' -channel_switched: '[Вече говорите в](#00fb9a) [%1%](#00fb9a bold) [чата!](#00fb9a)' -error_no_permission_send: '[Грешка:](#ff3300) [Нямате право да говорите в %1% чата.](#ff7e5e)' -error_invalid_channel: '[Грешка:](#ff3300) [Моля посочете валиден чат канал.](#ff7e5e)' -error_invalid_channel_command: '[Грешка:](#ff3300) [Този канал е невалиден](#ff7e5e)' -error_no_channel: '[Грешка:](#ff3300) [Опитвате се да говорите в невалиден чат канал.](#ff7e5e) [Използвайте /channel, за да преминете към валиден такъв.](#ff7e5e show_text=&#ff7e5e&Натиснете тук, за да препоръчаме командата suggest_command=/channel )' -error_player_not_found: '[Грешка:](#ff3300) [Не можахме да намерим посоченият играч; те онлайн ли са?](#ff7e5e)' -error_cannot_message_self: '[Грешка:](#ff3300) [Не можете да пишете на себе си!](#ff7e5e)' -error_reply_no_messages: '[Грешка:](#ff3300) [Никой не Ви е изпратил съобщение, на което да отговорите обратно!](#ff7e5e)' -error_reply_not_online: '[Грешка:](#ff3300) [Последният човек, на когото сте писали вече не е онлайн.](#ff7e5e)' -error_message_restricted_server: '[Грешка:](#ff3300) [Не можете да пишете на играчи от този сървър.](#ff7e5e)' -error_message_recipient_restricted_server: '[Грешка:](#ff3300) [Този играч е в сървър, в който съобщения не могат да бъдат получавани.](#ff7e5e)' -error_channel_restricted_server: '[Грешка:](#ff3300) [Не можете да говорите в %1% чата от този сървър.](#ff7e5e)' -social_spy_toggled_on: '[Вече шпионирате над личните съобщения.](#00fb9a)' -social_spy_toggled_on_color: '[Вече шпионирате над личните съобщения в](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Вече не шпионирате над личните съобщения.](#00fb9a)' -local_spy_toggled_on: '[Вече шпионирате над локалните канали на други сървъри.](#00fb9a)' -local_spy_toggled_on_color: '[Вече шпионирате над локалния чат в](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Вече не шпионирате над локалните канали на други сървъри.](#00fb9a)' -error_chat_filter_advertising: '[Не можете да рекламирате или да публикувате линкове в чата.](#ff7e5e)' -error_chat_filter_profanity: '[Не можете да използвате ругатни в чата.](#ff7e5e)' -error_chat_filter_caps: '[Моля, не използвайте толкова капс в чата.](#ff7e5e)' -error_chat_filter_spam: '[Моля изчакайте! Изпращате съобщения прекалено бързо.](#ff7e5e)' -error_chat_filter_ascii: '[Не можете да използвате специални символи в чата.](#ff7e5e)' -error_chat_filter_repeat: '[Вие вече сте изпратили това съобщение наскоро!](#ff7e5e)' -error_in_game_only: 'Грешка: Тази команда може да бъде използвана само от играта.' -error_console_local_scope: 'Грешка: Изпращането на съобщения от конзолата до канали с локален обхват не се поддържа.' -error_console_switch_channels: 'Грешка: Не можете да преминете към друг канал от конзолата. Моля, вместо това използвайте съкратената команда.' -error_group_messages_disabled: '[Грешка:](#ff3300) [Не можете да изпращате съобщения до много човека.](#ff7e5e)' -error_group_messages_max: '[Грешка:](#ff3300) [Можете да изпращате съобщения до максимум от %1% човека наведнъж.](#ff7e5e)' -error_players_not_found: '[Грешка:](#ff3300) [Не можахме да намерим нито един от посочените играчи; те онлайн ли са?](#ff7e5e)' -error_reply_none_online: '[Грешка:](#ff3300) [Никого от последната група от хора, на която сте писали не е онлайн.](#ff7e5e)' -error_last_message_not_group: '[Грешка:](#ff3300) [Последното съобщение, което сте получили не е било групово съобщение.](#ff7e5e)' -error_no_messages_opt_out: '[Грешка:](#ff3300) [Вие все още не сте изпращали нито пък получавали групови съобщения.](#ff7e5e)' -removed_from_group_message: '[Премахнати сте от груповите съобщения между:](#00fb9a) %1%' -list_conjunction: 'и' -error_passthrough_shortcut_command: '[Грешка:](#ff3300) [Изпращането на съобщения през преходни канали използвайки съкратени команди не е поддържано. Моля превключете към каналът първо.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда.](#ff7e5e)' + error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте: %1%](#ff7e5e)' + channel_switched: '[Вече говорите в](#00fb9a) [%1%](#00fb9a bold) [чата!](#00fb9a)' + error_no_permission_send: '[Грешка:](#ff3300) [Нямате право да говорите в %1% чата.](#ff7e5e)' + error_invalid_channel: '[Грешка:](#ff3300) [Моля посочете валиден чат канал.](#ff7e5e)' + error_invalid_channel_command: '[Грешка:](#ff3300) [Този канал е невалиден](#ff7e5e)' + error_no_channel: '[Грешка:](#ff3300) [Опитвате се да говорите в невалиден чат канал.](#ff7e5e) [Използвайте /channel, за да преминете към валиден такъв.](#ff7e5e show_text=&#ff7e5e&Натиснете тук, за да препоръчаме командата suggest_command=/channel )' + error_player_not_found: '[Грешка:](#ff3300) [Не можахме да намерим посоченият играч; те онлайн ли са?](#ff7e5e)' + error_cannot_message_self: '[Грешка:](#ff3300) [Не можете да пишете на себе си!](#ff7e5e)' + error_reply_no_messages: '[Грешка:](#ff3300) [Никой не Ви е изпратил съобщение, на което да отговорите обратно!](#ff7e5e)' + error_reply_not_online: '[Грешка:](#ff3300) [Последният човек, на когото сте писали вече не е онлайн.](#ff7e5e)' + error_message_restricted_server: '[Грешка:](#ff3300) [Не можете да пишете на играчи от този сървър.](#ff7e5e)' + error_message_recipient_restricted_server: '[Грешка:](#ff3300) [Този играч е в сървър, в който съобщения не могат да бъдат получавани.](#ff7e5e)' + error_channel_restricted_server: '[Грешка:](#ff3300) [Не можете да говорите в %1% чата от този сървър.](#ff7e5e)' + social_spy_toggled_on: '[Вече шпионирате над личните съобщения.](#00fb9a)' + social_spy_toggled_on_color: '[Вече шпионирате над личните съобщения в](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Вече не шпионирате над личните съобщения.](#00fb9a)' + local_spy_toggled_on: '[Вече шпионирате над локалните канали на други сървъри.](#00fb9a)' + local_spy_toggled_on_color: '[Вече шпионирате над локалния чат в](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Вече не шпионирате над локалните канали на други сървъри.](#00fb9a)' + error_chat_filter_advertising: '[Не можете да рекламирате или да публикувате линкове в чата.](#ff7e5e)' + error_chat_filter_profanity: '[Не можете да използвате ругатни в чата.](#ff7e5e)' + error_chat_filter_caps: '[Моля, не използвайте толкова капс в чата.](#ff7e5e)' + error_chat_filter_spam: '[Моля изчакайте! Изпращате съобщения прекалено бързо.](#ff7e5e)' + error_chat_filter_ascii: '[Не можете да използвате специални символи в чата.](#ff7e5e)' + error_chat_filter_repeat: '[Вие вече сте изпратили това съобщение наскоро!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Грешка: Тази команда може да бъде използвана само от играта.' + error_console_local_scope: 'Грешка: Изпращането на съобщения от конзолата до канали с локален обхват не се поддържа.' + error_console_switch_channels: 'Грешка: Не можете да преминете към друг канал от конзолата. Моля, вместо това използвайте съкратената команда.' + error_group_messages_disabled: '[Грешка:](#ff3300) [Не можете да изпращате съобщения до много човека.](#ff7e5e)' + error_group_messages_max: '[Грешка:](#ff3300) [Можете да изпращате съобщения до максимум от %1% човека наведнъж.](#ff7e5e)' + error_players_not_found: '[Грешка:](#ff3300) [Не можахме да намерим нито един от посочените играчи; те онлайн ли са?](#ff7e5e)' + error_reply_none_online: '[Грешка:](#ff3300) [Никого от последната група от хора, на която сте писали не е онлайн.](#ff7e5e)' + error_last_message_not_group: '[Грешка:](#ff3300) [Последното съобщение, което сте получили не е било групово съобщение.](#ff7e5e)' + error_no_messages_opt_out: '[Грешка:](#ff3300) [Вие все още не сте изпращали нито пък получавали групови съобщения.](#ff7e5e)' + removed_from_group_message: '[Премахнати сте от груповите съобщения между:](#00fb9a) %1%' + list_conjunction: 'и' + error_passthrough_shortcut_command: '[Грешка:](#ff3300) [Изпращането на съобщения през преходни канали използвайки съкратени команди не е поддържано. Моля превключете към каналът първо.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/de-de.yml b/common/src/main/resources/locales/de-de.yml index 48977818..4a92d0f3 100644 --- a/common/src/main/resources/locales/de-de.yml +++ b/common/src/main/resources/locales/de-de.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | de-de, by Ceddix | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen.](#ff7e5e)' -error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze: %1%](#ff7e5e)' -channel_switched: '[Du sprichst nun im](#00fb9a) [%1%](#00fb9a bold) [Chat!](#00fb9a)' -error_no_permission_send: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen für den %1% Chat.](#ff7e5e)' -error_invalid_channel: '[Fehler:](#ff3300) [Bitte gib einen gültigen Chat-Kanal an.](#ff7e5e)' -error_invalid_channel_command: '[Fehler:](#ff3300) [Dieser Kanal ist ungültig](#ff7e5e)' -error_no_channel: '[Fehler:](#ff3300) [Du versuchst, in einem ungültigen Chat-Kanal zu schreiben.](#ff7e5e) [Nutze /channel um zu einem gültigen zu wechseln.](#ff7e5e show_text=&#ff7e5e&Klicke hier, um den Befehl vorzuschlagen suggest_command=/channel )' -error_player_not_found: '[Fehler:](#ff3300) [Der angegebene Spieler konnte nicht gefunden werden; ist er online?](#ff7e5e)' -error_cannot_message_self: '[Fehler:](#ff3300) [Du kannst dir nicht selbst schreiben!](#ff7e5e)' -error_reply_no_messages: '[Fehler:](#ff3300) [Es gibt niemanden, der dir geschrieben hat, dem du antworten kannst!](#ff7e5e)' -error_reply_not_online: '[Fehler:](#ff3300) [Die Person, der du zulest geschrieben hast, ist nicht mehr online.](#ff7e5e)' -error_message_restricted_server: '[Fehler:](#ff3300) [Du kannst Spielern von diesem Server aus nicht schreiben.](#ff7e5e)' -error_message_recipient_restricted_server: '[Fehler:](#ff3300) [Dieser Spieler befindet sich auf einem Server, auf dem keine Nachrichten empfangen werden können.](#ff7e5e)' -error_channel_restricted_server: '[Fehler:](#ff3300) [Du kannst von diesem Server aus nicht im %1% Chat schreiben.](#ff7e5e)' -social_spy_toggled_on: '[Du spionierst jetzt private Nachrichten aus.](#00fb9a)' -social_spy_toggled_on_color: '[Du spionierst jetzt private Nachrichten in](#00fb9a) %1%%2% [aus.](#00fb9a)' -social_spy_toggled_off: '[Du spionierst keine privaten Nachrichten mehr aus.](#00fb9a)' -local_spy_toggled_on: '[Du spionierst jetzt die lokalen Chat-Kanäle anderer Server aus.](#00fb9a)' -local_spy_toggled_on_color: '[Du spionierst jetzt den lokalen Chat-Kanal in](#00fb9a) %1%%2% [aus.](#00fb9a)' -local_spy_toggled_off: '[Du spionierst jetzt nicht mehr die lokalen Chat-Kanäle anderer Server aus.](#00fb9a)' -error_chat_filter_advertising: '[Du kannst im Chat keine Werbung machen oder Links posten.](#ff7e5e)' -error_chat_filter_profanity: '[Du darfst im Chat keine Schimpfwörter verwenden.](#ff7e5e)' -error_chat_filter_caps: '[Bitte achte auf deine Großbuchstaben im Chat.](#ff7e5e)' -error_chat_filter_spam: '[Bitte warte! Du sendest deine Nachrichten zu schnell.](#ff7e5e)' -error_chat_filter_ascii: '[Du kannst keine Sonderzeichen im Chat verwenden.](#ff7e5e)' -error_chat_filter_repeat: '[Du hast diese Nachricht bereits vor kurzem verschickt!](#ff7e5e)' -error_in_game_only: 'Fehler: Dieser Befehl kann nur im Spiel verwendet werden.' -error_console_local_scope: 'Fehler: Das Senden von Nachrichten von der Konsole an Kanäle mit einem lokalen Bereich wird nicht unterstützt.' -error_console_switch_channels: 'Fehler: Du kannst von der Konsole aus nicht zu einem anderen Kanal wechseln. Bitte verwende stattdessen den Shortcut-Befehl.' -error_group_messages_disabled: '[Fehler:](#ff3300) [Du kannst nicht mehreren Personen gleichzeitig schreiben.](#ff7e5e)' -error_group_messages_max: '[Fehler:](#ff3300) [Du kannst nur maximal %1% Personen gleichzeitig schreiben.](#ff7e5e)' -error_players_not_found: '[Fehler:](#ff3300) [Es konnte keiner der angegebenen Spieler gefunden werden; sind sie online?](#ff7e5e)' -error_reply_none_online: '[Fehler:](#ff3300) [Niemand aus der letzten Gruppe von Personen, denen du geschrieben hast, ist noch online.](#ff7e5e)' -error_last_message_not_group: '[Fehler:](#ff3300) [Die letzte Nachricht, die du empfangen hast, war keine Gruppennachricht.](#ff7e5e)' -error_no_messages_opt_out: '[Fehler:](#ff3300) [Du hast noch keine Gruppennachricht gesendet oder empfangen.](#ff7e5e)' -removed_from_group_message: '[Du wurdest von den Gruppennachrichten zwischen](#00fb9a) %1% [entfernt.](#00fb9a)' -list_conjunction: 'und' -error_passthrough_shortcut_command: '[Fehler:](#ff3300) [Das Senden von Nachrichten an Passthrough-Kanäle mit Hilfe von Shortcut-Befehlen wird nicht unterstützt. Bitte wechsle zuerst in den entsprechenden Kanal.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| Du verwendest die neueste Version von HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| Eine neue Version von HuskChat ist verfügbar: v%1% (Deine Version: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen.](#ff7e5e)' + error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze: %1%](#ff7e5e)' + channel_switched: '[Du sprichst nun im](#00fb9a) [%1%](#00fb9a bold) [Chat!](#00fb9a)' + error_no_permission_send: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen für den %1% Chat.](#ff7e5e)' + error_invalid_channel: '[Fehler:](#ff3300) [Bitte gib einen gültigen Chat-Kanal an.](#ff7e5e)' + error_invalid_channel_command: '[Fehler:](#ff3300) [Dieser Kanal ist ungültig](#ff7e5e)' + error_no_channel: '[Fehler:](#ff3300) [Du versuchst, in einem ungültigen Chat-Kanal zu schreiben.](#ff7e5e) [Nutze /channel um zu einem gültigen zu wechseln.](#ff7e5e show_text=&#ff7e5e&Klicke hier, um den Befehl vorzuschlagen suggest_command=/channel )' + error_player_not_found: '[Fehler:](#ff3300) [Der angegebene Spieler konnte nicht gefunden werden; ist er online?](#ff7e5e)' + error_cannot_message_self: '[Fehler:](#ff3300) [Du kannst dir nicht selbst schreiben!](#ff7e5e)' + error_reply_no_messages: '[Fehler:](#ff3300) [Es gibt niemanden, der dir geschrieben hat, dem du antworten kannst!](#ff7e5e)' + error_reply_not_online: '[Fehler:](#ff3300) [Die Person, der du zulest geschrieben hast, ist nicht mehr online.](#ff7e5e)' + error_message_restricted_server: '[Fehler:](#ff3300) [Du kannst Spielern von diesem Server aus nicht schreiben.](#ff7e5e)' + error_message_recipient_restricted_server: '[Fehler:](#ff3300) [Dieser Spieler befindet sich auf einem Server, auf dem keine Nachrichten empfangen werden können.](#ff7e5e)' + error_channel_restricted_server: '[Fehler:](#ff3300) [Du kannst von diesem Server aus nicht im %1% Chat schreiben.](#ff7e5e)' + social_spy_toggled_on: '[Du spionierst jetzt private Nachrichten aus.](#00fb9a)' + social_spy_toggled_on_color: '[Du spionierst jetzt private Nachrichten in](#00fb9a) %1%%2% [aus.](#00fb9a)' + social_spy_toggled_off: '[Du spionierst keine privaten Nachrichten mehr aus.](#00fb9a)' + local_spy_toggled_on: '[Du spionierst jetzt die lokalen Chat-Kanäle anderer Server aus.](#00fb9a)' + local_spy_toggled_on_color: '[Du spionierst jetzt den lokalen Chat-Kanal in](#00fb9a) %1%%2% [aus.](#00fb9a)' + local_spy_toggled_off: '[Du spionierst jetzt nicht mehr die lokalen Chat-Kanäle anderer Server aus.](#00fb9a)' + error_chat_filter_advertising: '[Du kannst im Chat keine Werbung machen oder Links posten.](#ff7e5e)' + error_chat_filter_profanity: '[Du darfst im Chat keine Schimpfwörter verwenden.](#ff7e5e)' + error_chat_filter_caps: '[Bitte achte auf deine Großbuchstaben im Chat.](#ff7e5e)' + error_chat_filter_spam: '[Bitte warte! Du sendest deine Nachrichten zu schnell.](#ff7e5e)' + error_chat_filter_ascii: '[Du kannst keine Sonderzeichen im Chat verwenden.](#ff7e5e)' + error_chat_filter_repeat: '[Du hast diese Nachricht bereits vor kurzem verschickt!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Fehler: Dieser Befehl kann nur im Spiel verwendet werden.' + error_console_local_scope: 'Fehler: Das Senden von Nachrichten von der Konsole an Kanäle mit einem lokalen Bereich wird nicht unterstützt.' + error_console_switch_channels: 'Fehler: Du kannst von der Konsole aus nicht zu einem anderen Kanal wechseln. Bitte verwende stattdessen den Shortcut-Befehl.' + error_group_messages_disabled: '[Fehler:](#ff3300) [Du kannst nicht mehreren Personen gleichzeitig schreiben.](#ff7e5e)' + error_group_messages_max: '[Fehler:](#ff3300) [Du kannst nur maximal %1% Personen gleichzeitig schreiben.](#ff7e5e)' + error_players_not_found: '[Fehler:](#ff3300) [Es konnte keiner der angegebenen Spieler gefunden werden; sind sie online?](#ff7e5e)' + error_reply_none_online: '[Fehler:](#ff3300) [Niemand aus der letzten Gruppe von Personen, denen du geschrieben hast, ist noch online.](#ff7e5e)' + error_last_message_not_group: '[Fehler:](#ff3300) [Die letzte Nachricht, die du empfangen hast, war keine Gruppennachricht.](#ff7e5e)' + error_no_messages_opt_out: '[Fehler:](#ff3300) [Du hast noch keine Gruppennachricht gesendet oder empfangen.](#ff7e5e)' + removed_from_group_message: '[Du wurdest von den Gruppennachrichten zwischen](#00fb9a) %1% [entfernt.](#00fb9a)' + list_conjunction: 'und' + error_passthrough_shortcut_command: '[Fehler:](#ff3300) [Das Senden von Nachrichten an Passthrough-Kanäle mit Hilfe von Shortcut-Befehlen wird nicht unterstützt. Bitte wechsle zuerst in den entsprechenden Kanal.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| Du verwendest die neueste Version von HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| Eine neue Version von HuskChat ist verfügbar: v%1% (Deine Version: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 1e2d5cb0..d6b9e308 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | en-gb, by William278 | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Error:](#ff3300) [You do not have permission to execute that command.](#ff7e5e)' -error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage: %1%](#ff7e5e)' -channel_switched: '[You are now talking in](#00fb9a) [%1%](#00fb9a bold) [chat!](#00fb9a)' -error_no_permission_send: '[Error:](#ff3300) [You do not have permission to talk in %1% chat.](#ff7e5e)' -error_invalid_channel: '[Error:](#ff3300) [Please specify a valid chat channel.](#ff7e5e)' -error_invalid_channel_command: '[Error:](#ff3300) [That channel is invalid](#ff7e5e)' -error_no_channel: '[Error:](#ff3300) [You are trying to talk in an invalid chat channel.](#ff7e5e) [Use /channel to switch to a valid one.](#ff7e5e show_text=&#ff7e5e&Click here to suggest command suggest_command=/channel )' -error_player_not_found: '[Error:](#ff3300) [Could not find the specified player; are they online?](#ff7e5e)' -error_cannot_message_self: '[Error:](#ff3300) [You can''t message yourself!](#ff7e5e)' -error_reply_no_messages: '[Error:](#ff3300) [There is nobody who has sent you a message to reply to!](#ff7e5e)' -error_reply_not_online: '[Error:](#ff3300) [The last person you messaged is no longer online.](#ff7e5e)' -error_message_restricted_server: '[Error:](#ff3300) [You cannot message players from this server.](#ff7e5e)' -error_message_recipient_restricted_server: '[Error:](#ff3300) [That player is on a server where messages cannot be received.](#ff7e5e)' -error_channel_restricted_server: '[Error:](#ff3300) [You cannot talk in %1% chat from this server.](#ff7e5e)' -social_spy_toggled_on: '[You are now spying on private messages.](#00fb9a)' -social_spy_toggled_on_color: '[You are now spying on private messages in](#00fb9a) %1%%2%' -social_spy_toggled_off: '[You are no longer spying on private messages.](#00fb9a)' -local_spy_toggled_on: '[You are now spying on other servers'' local chat channels.](#00fb9a)' -local_spy_toggled_on_color: '[You are now spying on local chat in](#00fb9a) %1%%2%' -local_spy_toggled_off: '[You are no longer spying on other servers'' local chat channels.](#00fb9a)' -error_chat_filter_advertising: '[You cannot advertise or post links in chat.](#ff7e5e)' -error_chat_filter_profanity: '[You cannot use profanity in chat.](#ff7e5e)' -error_chat_filter_caps: '[Please watch your caps in chat.](#ff7e5e)' -error_chat_filter_spam: '[Please wait! You are sending messages too quickly.](#ff7e5e)' -error_chat_filter_ascii: '[You cannot use special characters in chat.](#ff7e5e)' -error_chat_filter_repeat: '[You''ve already sent that message recently!](#ff7e5e)' -error_in_game_only: 'Error: That command can only be used in-game.' -error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' -error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' -error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' -error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' -error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' -error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' -error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' -error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' -removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' -list_conjunction: 'and' -error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Error:](#ff3300) [You do not have permission to execute that command.](#ff7e5e)' + error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage: %1%](#ff7e5e)' + channel_switched: '[You are now talking in](#00fb9a) [%1%](#00fb9a bold) [chat!](#00fb9a)' + error_no_permission_send: '[Error:](#ff3300) [You do not have permission to talk in %1% chat.](#ff7e5e)' + error_invalid_channel: '[Error:](#ff3300) [Please specify a valid chat channel.](#ff7e5e)' + error_invalid_channel_command: '[Error:](#ff3300) [That channel is invalid](#ff7e5e)' + error_no_channel: '[Error:](#ff3300) [You are trying to talk in an invalid chat channel.](#ff7e5e) [Use /channel to switch to a valid one.](#ff7e5e show_text=&#ff7e5e&Click here to suggest command suggest_command=/channel )' + error_player_not_found: '[Error:](#ff3300) [Could not find the specified player; are they online?](#ff7e5e)' + error_cannot_message_self: '[Error:](#ff3300) [You can''t message yourself!](#ff7e5e)' + error_reply_no_messages: '[Error:](#ff3300) [There is nobody who has sent you a message to reply to!](#ff7e5e)' + error_reply_not_online: '[Error:](#ff3300) [The last person you messaged is no longer online.](#ff7e5e)' + error_message_restricted_server: '[Error:](#ff3300) [You cannot message players from this server.](#ff7e5e)' + error_message_recipient_restricted_server: '[Error:](#ff3300) [That player is on a server where messages cannot be received.](#ff7e5e)' + error_channel_restricted_server: '[Error:](#ff3300) [You cannot talk in %1% chat from this server.](#ff7e5e)' + social_spy_toggled_on: '[You are now spying on private messages.](#00fb9a)' + social_spy_toggled_on_color: '[You are now spying on private messages in](#00fb9a) %1%%2%' + social_spy_toggled_off: '[You are no longer spying on private messages.](#00fb9a)' + local_spy_toggled_on: '[You are now spying on other servers'' local chat channels.](#00fb9a)' + local_spy_toggled_on_color: '[You are now spying on local chat in](#00fb9a) %1%%2%' + local_spy_toggled_off: '[You are no longer spying on other servers'' local chat channels.](#00fb9a)' + error_chat_filter_advertising: '[You cannot advertise or post links in chat.](#ff7e5e)' + error_chat_filter_profanity: '[You cannot use profanity in chat.](#ff7e5e)' + error_chat_filter_caps: '[Please watch your caps in chat.](#ff7e5e)' + error_chat_filter_spam: '[Please wait! You are sending messages too quickly.](#ff7e5e)' + error_chat_filter_ascii: '[You cannot use special characters in chat.](#ff7e5e)' + error_chat_filter_repeat: '[You''ve already sent that message recently!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Error: That command can only be used in-game.' + error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' + error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' + error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' + error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' + error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' + error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' + error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' + error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' + removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' + list_conjunction: 'and' + error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/es-mx.yml b/common/src/main/resources/locales/es-mx.yml index 9803ae2a..d41e267f 100644 --- a/common/src/main/resources/locales/es-mx.yml +++ b/common/src/main/resources/locales/es-mx.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | es-mx por jume | -# ------------------------------ -# Si desea utilizar un idioma diferente, puede cambiarlo en config.yml. -# Cambia la apariencia/texto de los mensajes en el plugin usando esta configuración. -# Esta configuración utiliza el formato MineDown, con amplio soporte para colores y formatos personalizados. -# Para obtener ayuda sobre el formato, consulte: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Error:](#ff3300) [No tienes permiso para ejecutar ese comando.](#ff7e5e)' -error_invalid_syntax: '[Error:](#ff3300) [Sintaxis incorrecta. Uso: %1%](#ff7e5e)' -channel_switched: '[Estás hablando en:](#00fb9a) [%1%](#00fb9a bold) [chat!](#00fb9a)' -error_no_permission_send: '[Error:](#ff3300) [No tienes permiso para hablar en el chat %1%.](#ff7e5e)' -error_invalid_channel: '[Error:](#ff3300) [Por favor especifica un canal de chat válido.](#ff7e5e)' -error_invalid_channel_command: '[Error:](#ff3300) [Ese canal es inválido.](#ff7e5e)' -error_no_channel: '[Error:](#ff3300) [Estás intentando hablar en un canal de chat inválido.](#ff7e5e) [Usa /channel para cambiar a uno válido.](#ff7e5e show_text=&#ff7e5e&Click aqui para comandos recomendados suggest_command=/channel )' -error_player_not_found: '[Error:](#ff3300) [No se pudo encontrar al jugador especificado; ¿está en línea?](#ff7e5e)' -error_cannot_message_self: '[Error:](#ff3300) [¡No puedes enviarte mensajes a ti mismo, tas tu loco!](#ff7e5e)' -error_reply_no_messages: '[Error:](#ff3300) [¡Nadie te ha enviado un mensaje para responder, el forever alone le dicen!](#ff7e5e)' -error_reply_not_online: '[Error:](#ff3300) [La última persona a la que le enviaste un mensaje ya no está en línea, inserte musica sad aqui :( .](#ff7e5e)' -error_message_restricted_server: '[Error:](#ff3300) [No puedes enviar mensajes a jugadores de este servidor.](#ff7e5e)' -error_message_recipient_restricted_server: '[Error:](#ff3300) [Ese jugador está en un servidor donde no se pueden recibir mensajes, entendiste mi ciela.](#ff7e5e)' -error_channel_restricted_server: '[Error:](#ff3300) [No puedes hablar en el chat %1% desde este servidor.](#ff7e5e)' -social_spy_toggled_on: '[Estás espiando mensajes privados, chismoso.](#00fb9a)' -social_spy_toggled_on_color: '[Hay, jesus de veracruz, Estás espiando mensajes privados en](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Ya no estás espiando mensajes privados, buen chico.](#00fb9a)' -local_spy_toggled_on: '[Estás espiando los canales de chat locales de otros servidores.](#00fb9a)' -local_spy_toggled_on_color: '[Estás espiando el chat local en](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Ya no estás espiando los canales de chat locales de otros servidores.](#00fb9a)' -error_chat_filter_advertising: '[No puedes hacer publicidad o compartir enlaces en el chat.](#ff7e5e)' -error_chat_filter_profanity: '[No puedes usar lenguaje ofensivo en el chat, no somos albañiles pinche grosero.](#ff7e5e)' -error_chat_filter_caps: '[Por favor, evita escribir en mayúsculas en el chat.](#ff7e5e)' -error_chat_filter_spam: '[Por favor, espera. Estás enviando mensajes demasiado rápido, ya no tomes cafe.](#ff7e5e)' -error_chat_filter_ascii: '[No puedes usar caracteres especiales en el chat.](#ff7e5e)' -error_chat_filter_repeat: '[Ya enviaste ese mensaje recientemente.](#ff7e5e)' -error_in_game_only: 'Error: Ese comando solo puede ser usado dentro del juego.' -error_console_local_scope: 'No se pueden enviar mensajes desde la consola a canales con alcance local.' -error_console_switch_channels: 'Error: No puedes cambiar a otro canal desde la consola. Por favor, usa el comando abreviado en su lugar.' -error_group_messages_disabled: '[Error:](#ff3300) [No puedes enviar mensajes a múltiples personas.](#ff7e5e)' -error_group_messages_max: '[Error:](#ff3300) [Solo puedes enviar mensajes a un máximo de %1% personas a la vez.](#ff7e5e)' -error_players_not_found: '[Error:](#ff3300) [No se pudo encontrar a ningún jugador especificado; ¿están en línea?¿existen? o tal vez son solo un invento de tu imaginacion](#ff7e5e)' -error_reply_none_online: '[Error:](#ff3300) [El último mensaje que recibiste no fue un mensaje grupal.](#ff7e5e)' -error_last_message_not_group: '[Error:](#ff3300) [Aún no has enviado ni recibido mensajes grupales.](#ff7e5e)' -error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' -removed_from_group_message: '[Fuiste eliminado del mensaje grupal entre: %1%]' -list_conjunction: 'and' -error_passthrough_shortcut_command: '[Error:](#ff3300) [No se puede enviar mensajes a canales de paso utilizando comandos abreviados. Por favor, cambia al canal primero.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Error:](#ff3300) [No tienes permiso para ejecutar ese comando.](#ff7e5e)' + error_invalid_syntax: '[Error:](#ff3300) [Sintaxis incorrecta. Uso: %1%](#ff7e5e)' + channel_switched: '[Estás hablando en:](#00fb9a) [%1%](#00fb9a bold) [chat!](#00fb9a)' + error_no_permission_send: '[Error:](#ff3300) [No tienes permiso para hablar en el chat %1%.](#ff7e5e)' + error_invalid_channel: '[Error:](#ff3300) [Por favor especifica un canal de chat válido.](#ff7e5e)' + error_invalid_channel_command: '[Error:](#ff3300) [Ese canal es inválido.](#ff7e5e)' + error_no_channel: '[Error:](#ff3300) [Estás intentando hablar en un canal de chat inválido.](#ff7e5e) [Usa /channel para cambiar a uno válido.](#ff7e5e show_text=&#ff7e5e&Click aqui para comandos recomendados suggest_command=/channel )' + error_player_not_found: '[Error:](#ff3300) [No se pudo encontrar al jugador especificado; ¿está en línea?](#ff7e5e)' + error_cannot_message_self: '[Error:](#ff3300) [¡No puedes enviarte mensajes a ti mismo, tas tu loco!](#ff7e5e)' + error_reply_no_messages: '[Error:](#ff3300) [¡Nadie te ha enviado un mensaje para responder, el forever alone le dicen!](#ff7e5e)' + error_reply_not_online: '[Error:](#ff3300) [La última persona a la que le enviaste un mensaje ya no está en línea, inserte musica sad aqui :( .](#ff7e5e)' + error_message_restricted_server: '[Error:](#ff3300) [No puedes enviar mensajes a jugadores de este servidor.](#ff7e5e)' + error_message_recipient_restricted_server: '[Error:](#ff3300) [Ese jugador está en un servidor donde no se pueden recibir mensajes, entendiste mi ciela.](#ff7e5e)' + error_channel_restricted_server: '[Error:](#ff3300) [No puedes hablar en el chat %1% desde este servidor.](#ff7e5e)' + social_spy_toggled_on: '[Estás espiando mensajes privados, chismoso.](#00fb9a)' + social_spy_toggled_on_color: '[Hay, jesus de veracruz, Estás espiando mensajes privados en](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Ya no estás espiando mensajes privados, buen chico.](#00fb9a)' + local_spy_toggled_on: '[Estás espiando los canales de chat locales de otros servidores.](#00fb9a)' + local_spy_toggled_on_color: '[Estás espiando el chat local en](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Ya no estás espiando los canales de chat locales de otros servidores.](#00fb9a)' + error_chat_filter_advertising: '[No puedes hacer publicidad o compartir enlaces en el chat.](#ff7e5e)' + error_chat_filter_profanity: '[No puedes usar lenguaje ofensivo en el chat, no somos albañiles pinche grosero.](#ff7e5e)' + error_chat_filter_caps: '[Por favor, evita escribir en mayúsculas en el chat.](#ff7e5e)' + error_chat_filter_spam: '[Por favor, espera. Estás enviando mensajes demasiado rápido, ya no tomes cafe.](#ff7e5e)' + error_chat_filter_ascii: '[No puedes usar caracteres especiales en el chat.](#ff7e5e)' + error_chat_filter_repeat: '[Ya enviaste ese mensaje recientemente.](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Error: Ese comando solo puede ser usado dentro del juego.' + error_console_local_scope: 'No se pueden enviar mensajes desde la consola a canales con alcance local.' + error_console_switch_channels: 'Error: No puedes cambiar a otro canal desde la consola. Por favor, usa el comando abreviado en su lugar.' + error_group_messages_disabled: '[Error:](#ff3300) [No puedes enviar mensajes a múltiples personas.](#ff7e5e)' + error_group_messages_max: '[Error:](#ff3300) [Solo puedes enviar mensajes a un máximo de %1% personas a la vez.](#ff7e5e)' + error_players_not_found: '[Error:](#ff3300) [No se pudo encontrar a ningún jugador especificado; ¿están en línea?¿existen? o tal vez son solo un invento de tu imaginacion](#ff7e5e)' + error_reply_none_online: '[Error:](#ff3300) [El último mensaje que recibiste no fue un mensaje grupal.](#ff7e5e)' + error_last_message_not_group: '[Error:](#ff3300) [Aún no has enviado ni recibido mensajes grupales.](#ff7e5e)' + error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' + removed_from_group_message: '[Fuiste eliminado del mensaje grupal entre: %1%]' + list_conjunction: 'and' + error_passthrough_shortcut_command: '[Error:](#ff3300) [No se puede enviar mensajes a canales de paso utilizando comandos abreviados. Por favor, cambia al canal primero.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/fr-fr.yml b/common/src/main/resources/locales/fr-fr.yml index 69e6ab8d..d7de75a5 100644 --- a/common/src/main/resources/locales/fr-fr.yml +++ b/common/src/main/resources/locales/fr-fr.yml @@ -1,40 +1,42 @@ -error_no_permission: '[Erreur :](#ff3300) [Vous n''avez pas la permission d''exécuter cette commande.](#ff7e5e)' -error_invalid_syntax: '[Erreur :](#ff3300) [Syntaxe incorrecte. Utilisation : %1%](#ff7e5e)' -channel_switched: '[Vous parlez maintenant dans le](#00fb9a) [%1%](#00fb9a bold) [chat !](#00fb9a)' -error_no_permission_send: '[Erreur :](#ff3300) [Vous n''avez pas la permission de parler dans le chat %1%.](#ff7e5e)' -error_invalid_channel: '[Erreur :](#ff3300) [Veuillez spécifier un canal de discussion valide.](#ff7e5e)' -error_invalid_channel_command: '[Erreur :](#ff3300) [Ce canal est invalide.](#ff7e5e)' -error_no_channel: '[Erreur :](#ff3300) [Vous essayez de parler dans un canal de discussion invalide.](#ff7e5e) [Utilisez /channel pour passer à un canal valide.](#ff7e5e show_text=&#ff7e5e&Clic ici pour suggerer la commande suggest_command=/channel )' -error_player_not_found: '[Erreur :](#ff3300) [Impossible de trouver le joueur spécifié.](#ff7e5e)' -error_cannot_message_self: '[Erreur :](#ff3300) [Vous ne pouvez pas vous envoyer de messages à vous-même !](#ff7e5e)' -error_reply_no_messages: '[Erreur :](#ff3300) [Il n''y a personne qui vous a envoyé un message auquel vous pouvez répondre !](#ff7e5e)' -error_reply_not_online: '[Erreur :](#ff3300) [La dernière personne à qui vous avez envoyé un message n''est plus en ligne.](#ff7e5e)' -error_message_restricted_server: '[Erreur :](#ff3300) [Vous ne pouvez pas envoyer de messages aux joueurs de ce serveur.](#ff7e5e)' -error_message_recipient_restricted_server: '[Erreur :](#ff3300) [Ce joueur se trouve sur un serveur où les messages ne peuvent pas être reçus.](#ff7e5e)' -error_channel_restricted_server: '[Erreur :](#ff3300) [Vous ne pouvez pas parler dans %1% chat à partir de ce serveur.](#ff7e5e)' -social_spy_toggled_on: '[Vous espionnez maintenant les messages privés.](#00fb9a)' -social_spy_toggled_on_color: '[Vous espionnez maintenant les messages privés dans](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Vous n''espionnez plus les messages privés.](#00fb9a)' -local_spy_toggled_on: '[Vous espionnez maintenant les canaux de discussion locaux des autres serveurs.](#00fb9a)' -local_spy_toggled_on_color: '[Vous êtes en train d''espionner le chat local dans](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Vous n''espionnez plus les canaux de discussion locaux des autres serveurs.](#00fb9a)' -error_chat_filter_advertising: '[Vous ne pouvez pas faire de publicité ou publier des liens dans le chat.](#ff7e5e)' -error_chat_filter_profanity: '[Vous ne pouvez pas utiliser d''injures dans le chat.](#ff7e5e)' -error_chat_filter_caps: '[Attention aux majuscules dans le chat.](#ff7e5e)' -error_chat_filter_spam: '[Veuillez patienter ! Vous envoyez des messages trop rapidement.](#ff7e5e)' -error_chat_filter_ascii: '[Vous ne pouvez pas utiliser de caractères spéciaux dans le chat.](#ff7e5e)' -error_chat_filter_repeat: '[Vous avez déjà envoyé ce message récemment !](#ff7e5e)' -error_in_game_only: 'Erreur : Cette commande ne peut être utilisée que dans le jeu.' -error_console_local_scope: 'Erreur : L''envoi de messages depuis la console vers des canaux ayant une portée locale n''est pas pris en charge.' -error_console_switch_channels: 'Erreur : Vous ne pouvez pas passer à un autre canal à partir de la console. Veuillez utiliser la commande de raccourci à la place.' -error_group_messages_disabled: '[Erreur :](#ff3300) [Vous ne pouvez pas envoyer de messages à plusieurs personnes.](#ff7e5e)' -error_group_messages_max: '[Erreur :](#ff3300) [Vous ne pouvez envoyer des messages qu''à un maximum de %1% de personnes à la fois.](#ff7e5e)' -error_players_not_found: '[Erreur :](#ff3300) [Il n''a pas été possible de trouver l''un des joueurs spécifiés.](#ff7e5e)' -error_reply_none_online: '[Erreur :](#ff3300) [Personne dans le dernier groupe de personnes à qui vous avez envoyé un message n''est encore en ligne.](#ff7e5e)' -error_last_message_not_group: '[Erreur :](#ff3300) [Le dernier message que vous avez reçu n''était pas un message de groupe.](#ff7e5e)' -error_no_messages_opt_out: '[Erreur :](#ff3300) [Vous n''avez pas encore envoyé ou reçu de messages de groupe.](#ff7e5e)' -removed_from_group_message: '[Je vous ai retiré de la liste des messages du groupe entre :](#00fb9a) %1%' -list_conjunction: 'et' -error_passthrough_shortcut_command: '[Erreur :](#ff3300) [L''envoi de messages à des canaux de passage à l''aide de commandes de raccourci n''est pas pris en charge. Veuillez d''abord vous connecter au canal.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| Vous utilisez la dernière version de HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [|Une nouvelle version de HuskChat est disponible : v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Erreur :](#ff3300) [Vous n''avez pas la permission d''exécuter cette commande.](#ff7e5e)' + error_invalid_syntax: '[Erreur :](#ff3300) [Syntaxe incorrecte. Utilisation : %1%](#ff7e5e)' + channel_switched: '[Vous parlez maintenant dans le](#00fb9a) [%1%](#00fb9a bold) [chat !](#00fb9a)' + error_no_permission_send: '[Erreur :](#ff3300) [Vous n''avez pas la permission de parler dans le chat %1%.](#ff7e5e)' + error_invalid_channel: '[Erreur :](#ff3300) [Veuillez spécifier un canal de discussion valide.](#ff7e5e)' + error_invalid_channel_command: '[Erreur :](#ff3300) [Ce canal est invalide.](#ff7e5e)' + error_no_channel: '[Erreur :](#ff3300) [Vous essayez de parler dans un canal de discussion invalide.](#ff7e5e) [Utilisez /channel pour passer à un canal valide.](#ff7e5e show_text=&#ff7e5e&Clic ici pour suggerer la commande suggest_command=/channel )' + error_player_not_found: '[Erreur :](#ff3300) [Impossible de trouver le joueur spécifié.](#ff7e5e)' + error_cannot_message_self: '[Erreur :](#ff3300) [Vous ne pouvez pas vous envoyer de messages à vous-même !](#ff7e5e)' + error_reply_no_messages: '[Erreur :](#ff3300) [Il n''y a personne qui vous a envoyé un message auquel vous pouvez répondre !](#ff7e5e)' + error_reply_not_online: '[Erreur :](#ff3300) [La dernière personne à qui vous avez envoyé un message n''est plus en ligne.](#ff7e5e)' + error_message_restricted_server: '[Erreur :](#ff3300) [Vous ne pouvez pas envoyer de messages aux joueurs de ce serveur.](#ff7e5e)' + error_message_recipient_restricted_server: '[Erreur :](#ff3300) [Ce joueur se trouve sur un serveur où les messages ne peuvent pas être reçus.](#ff7e5e)' + error_channel_restricted_server: '[Erreur :](#ff3300) [Vous ne pouvez pas parler dans %1% chat à partir de ce serveur.](#ff7e5e)' + social_spy_toggled_on: '[Vous espionnez maintenant les messages privés.](#00fb9a)' + social_spy_toggled_on_color: '[Vous espionnez maintenant les messages privés dans](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Vous n''espionnez plus les messages privés.](#00fb9a)' + local_spy_toggled_on: '[Vous espionnez maintenant les canaux de discussion locaux des autres serveurs.](#00fb9a)' + local_spy_toggled_on_color: '[Vous êtes en train d''espionner le chat local dans](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Vous n''espionnez plus les canaux de discussion locaux des autres serveurs.](#00fb9a)' + error_chat_filter_advertising: '[Vous ne pouvez pas faire de publicité ou publier des liens dans le chat.](#ff7e5e)' + error_chat_filter_profanity: '[Vous ne pouvez pas utiliser d''injures dans le chat.](#ff7e5e)' + error_chat_filter_caps: '[Attention aux majuscules dans le chat.](#ff7e5e)' + error_chat_filter_spam: '[Veuillez patienter ! Vous envoyez des messages trop rapidement.](#ff7e5e)' + error_chat_filter_ascii: '[Vous ne pouvez pas utiliser de caractères spéciaux dans le chat.](#ff7e5e)' + error_chat_filter_repeat: '[Vous avez déjà envoyé ce message récemment !](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Erreur : Cette commande ne peut être utilisée que dans le jeu.' + error_console_local_scope: 'Erreur : L''envoi de messages depuis la console vers des canaux ayant une portée locale n''est pas pris en charge.' + error_console_switch_channels: 'Erreur : Vous ne pouvez pas passer à un autre canal à partir de la console. Veuillez utiliser la commande de raccourci à la place.' + error_group_messages_disabled: '[Erreur :](#ff3300) [Vous ne pouvez pas envoyer de messages à plusieurs personnes.](#ff7e5e)' + error_group_messages_max: '[Erreur :](#ff3300) [Vous ne pouvez envoyer des messages qu''à un maximum de %1% de personnes à la fois.](#ff7e5e)' + error_players_not_found: '[Erreur :](#ff3300) [Il n''a pas été possible de trouver l''un des joueurs spécifiés.](#ff7e5e)' + error_reply_none_online: '[Erreur :](#ff3300) [Personne dans le dernier groupe de personnes à qui vous avez envoyé un message n''est encore en ligne.](#ff7e5e)' + error_last_message_not_group: '[Erreur :](#ff3300) [Le dernier message que vous avez reçu n''était pas un message de groupe.](#ff7e5e)' + error_no_messages_opt_out: '[Erreur :](#ff3300) [Vous n''avez pas encore envoyé ou reçu de messages de groupe.](#ff7e5e)' + removed_from_group_message: '[Je vous ai retiré de la liste des messages du groupe entre :](#00fb9a) %1%' + list_conjunction: 'et' + error_passthrough_shortcut_command: '[Erreur :](#ff3300) [L''envoi de messages à des canaux de passage à l''aide de commandes de raccourci n''est pas pris en charge. Veuillez d''abord vous connecter au canal.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| Vous utilisez la dernière version de HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [|Une nouvelle version de HuskChat est disponible : v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/hu-hu.yml b/common/src/main/resources/locales/hu-hu.yml index e50842c6..e5eedd5f 100644 --- a/common/src/main/resources/locales/hu-hu.yml +++ b/common/src/main/resources/locales/hu-hu.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | hu-hu, by MalzSmith | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Hiba:](#ff3300) [Nincs jogod a parancs használatához.](#ff7e5e)' -error_invalid_syntax: '[Hiba:](#ff3300) [Érvénytelen bevitel. Helyesen: %1%](#ff7e5e)' -channel_switched: '[Te most a](#00fb9a) [%1%](#00fb9a bold) [csatornát használod!](#00fb9a)' -error_no_permission_send: '[Hiba:](#ff3300) Nincs jogod a %1% csatornát használni.](#ff7e5e)' -error_invalid_channel: '[Hiba:](#ff3300) [Kérlek adj meg érvényes csatornát!](#ff7e5e)' -error_invalid_channel_command: '[Hiba:](#ff3300) [Ez a csatorna nem létezik!](#ff7e5e)' -error_no_channel: '[Hiba:](#ff3300) [Érvénytelen csatornán próbálsz csevegni.](#ff7e5e) [A /channel paranccsal tudsz érvényes csatornát választani.](#ff7e5e show_text=&#ff7e5e&suggest_command=/channel )' -error_player_not_found: '[Hiba:](#ff3300) [Játékos nem található.](#ff7e5e)' -error_cannot_message_self: '[Hiba:](#ff3300) [Nem küldhetsz üzenetet magadnak!](#ff7e5e)' -error_reply_no_messages: '[Hiba:](#ff3300) [Nincs senki, akinek az üzenetére válaszolhatnál!](#ff7e5e)' -error_reply_not_online: '[Hiba:](#ff3300) [Az utolsó személy, akinek üzenetet küldtél már nem online.](#ff7e5e)' -error_message_restricted_server: '[Hiba:](#ff3300) [Erről a szerverről nem tudsz üzenetet küldeni a játékosoknak!](#ff7e5e)' -error_message_recipient_restricted_server: '[Hiba:](#ff3300) [Ez a játékos olyan szerveren van, ahol nem tudja fogadni az üzenetedet.](#ff7e5e)' -error_channel_restricted_server: '[Hiba:](#ff3300) [Nem tudod a %1% csatornát használni erről a szerverről.](#ff7e5e)' -social_spy_toggled_on: '[Látod a játékosok privát üzeneteit.](#00fb9a)' -social_spy_toggled_on_color: '[Látod a játékosok privát üzeneteit, itt:](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Mostantól nem látod a játékosok privát üzeneteit.](#00fb9a)' -local_spy_toggled_on: '[Mostantól látod a lokális üzeneteket.](#00fb9a)' -local_spy_toggled_on_color: '[Mostantól látod a lokális üzeneteket, itt:](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Mostantól nem látod más szerverek lokális üzeneteit.](#00fb9a)' -error_chat_filter_advertising: '[Kérlek ne küldj hirdetéseket és linkeket!](#ff7e5e)' -error_chat_filter_profanity: '[Kérlek ne használj trágár szavakat!](#ff7e5e)' -error_chat_filter_caps: '[Kérlek ne írj csupa nagybetűvel!](#ff7e5e)' -error_chat_filter_spam: '[Kérlek várj! Túl gyorsan küldesz üzenetet.](#ff7e5e)' -error_chat_filter_ascii: '[Nem használhatsz speciális karaktereket a chat-en!](#ff7e5e)' -error_chat_filter_repeat: '[Nemrég már elküldted ezt az üzentet!](#ff7e5e)' -error_in_game_only: 'Hiba: Ez a parancs csak játékon belül használható!' -error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' -error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' -error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' -error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' -error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' -error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' -error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' -error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' -removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' -list_conjunction: 'and' -error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Hiba:](#ff3300) [Nincs jogod a parancs használatához.](#ff7e5e)' + error_invalid_syntax: '[Hiba:](#ff3300) [Érvénytelen bevitel. Helyesen: %1%](#ff7e5e)' + channel_switched: '[Te most a](#00fb9a) [%1%](#00fb9a bold) [csatornát használod!](#00fb9a)' + error_no_permission_send: '[Hiba:](#ff3300) Nincs jogod a %1% csatornát használni.](#ff7e5e)' + error_invalid_channel: '[Hiba:](#ff3300) [Kérlek adj meg érvényes csatornát!](#ff7e5e)' + error_invalid_channel_command: '[Hiba:](#ff3300) [Ez a csatorna nem létezik!](#ff7e5e)' + error_no_channel: '[Hiba:](#ff3300) [Érvénytelen csatornán próbálsz csevegni.](#ff7e5e) [A /channel paranccsal tudsz érvényes csatornát választani.](#ff7e5e show_text=&#ff7e5e&suggest_command=/channel )' + error_player_not_found: '[Hiba:](#ff3300) [Játékos nem található.](#ff7e5e)' + error_cannot_message_self: '[Hiba:](#ff3300) [Nem küldhetsz üzenetet magadnak!](#ff7e5e)' + error_reply_no_messages: '[Hiba:](#ff3300) [Nincs senki, akinek az üzenetére válaszolhatnál!](#ff7e5e)' + error_reply_not_online: '[Hiba:](#ff3300) [Az utolsó személy, akinek üzenetet küldtél már nem online.](#ff7e5e)' + error_message_restricted_server: '[Hiba:](#ff3300) [Erről a szerverről nem tudsz üzenetet küldeni a játékosoknak!](#ff7e5e)' + error_message_recipient_restricted_server: '[Hiba:](#ff3300) [Ez a játékos olyan szerveren van, ahol nem tudja fogadni az üzenetedet.](#ff7e5e)' + error_channel_restricted_server: '[Hiba:](#ff3300) [Nem tudod a %1% csatornát használni erről a szerverről.](#ff7e5e)' + social_spy_toggled_on: '[Látod a játékosok privát üzeneteit.](#00fb9a)' + social_spy_toggled_on_color: '[Látod a játékosok privát üzeneteit, itt:](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Mostantól nem látod a játékosok privát üzeneteit.](#00fb9a)' + local_spy_toggled_on: '[Mostantól látod a lokális üzeneteket.](#00fb9a)' + local_spy_toggled_on_color: '[Mostantól látod a lokális üzeneteket, itt:](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Mostantól nem látod más szerverek lokális üzeneteit.](#00fb9a)' + error_chat_filter_advertising: '[Kérlek ne küldj hirdetéseket és linkeket!](#ff7e5e)' + error_chat_filter_profanity: '[Kérlek ne használj trágár szavakat!](#ff7e5e)' + error_chat_filter_caps: '[Kérlek ne írj csupa nagybetűvel!](#ff7e5e)' + error_chat_filter_spam: '[Kérlek várj! Túl gyorsan küldesz üzenetet.](#ff7e5e)' + error_chat_filter_ascii: '[Nem használhatsz speciális karaktereket a chat-en!](#ff7e5e)' + error_chat_filter_repeat: '[Nemrég már elküldted ezt az üzentet!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Hiba: Ez a parancs csak játékon belül használható!' + error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' + error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' + error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' + error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' + error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' + error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' + error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' + error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' + removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' + list_conjunction: 'and' + error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| You are running the latest version of HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| A new version of HuskChat is available: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/it-it.yml b/common/src/main/resources/locales/it-it.yml index 82aee805..f86b5ad7 100644 --- a/common/src/main/resources/locales/it-it.yml +++ b/common/src/main/resources/locales/it-it.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | it-it, by xF3d3#2608 | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di eseguire questo comando.](#ff7e5e)' -error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa: %1%](#ff7e5e)' -channel_switched: '[Adesso stai parlando in:](#00fb9a) [%1%](#00fb9a bold)' -error_no_permission_send: '[Errore:](#ff3300) [Non hai il permesso per parlare in %1%.](#ff7e5e)' -error_invalid_channel: '[Errore:](#ff3300) [Per favore specifica un canale valido.](#ff7e5e)' -error_invalid_channel_command: '[Errore:](#ff3300) [Questo canale non è valido!](#ff7e5e)' -error_no_channel: '[Errore:](#ff3300) [Stai provando a parlare in un canale non valido!](#ff7e5e) [Usa /channel per parlare in un canale valido.](#ff7e5e show_text=&#ff7e5e&Click here to suggest command suggest_command=/channel )' -error_player_not_found: '[Errore:](#ff3300) [Impossibile trovare il giocatore specificato; è online?](#ff7e5e)' -error_cannot_message_self: '[Errore:](#ff3300) [Non puoi mandare un messaggio a te stesso!](#ff7e5e)' -error_reply_no_messages: '[Errore:](#ff3300) [Non hai nessun messagio al quale rispondere!](#ff7e5e)' -error_reply_not_online: '[Errore:](#ff3300) [L''ultima persona con cui ti sei messaggiato non è online!](#ff7e5e)' -error_message_restricted_server: '[Errore:](#ff3300) [Non puoi mandare messaggi ai giocatori da questo server.](#ff7e5e)' -error_message_recipient_restricted_server: '[Errore:](#ff3300) [Questo giocatore è in un server dal quale non può ricevere messaggi.](#ff7e5e)' -error_channel_restricted_server: '[Errore:](#ff3300) [Non puoi parlare in %1% da questo server.](#ff7e5e)' -social_spy_toggled_on: '[Stai ora spiando i messaggi privati.](#00fb9a)' -social_spy_toggled_on_color: '[Stai ora spiando i messaggi privati in](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Non stai più spiando i messaggi privati.](#00fb9a)' -local_spy_toggled_on: '[Stai ora spiando i canali locali degli altri server.](#00fb9a)' -local_spy_toggled_on_color: '[Stai ora spiando il canale locale in](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Non stai più spiando i canali locali degli altri server.](#00fb9a)' -error_chat_filter_advertising: '[Non puoi promuovere o mandare link in chat.](#ff7e5e)' -error_chat_filter_profanity: '[Non puoi bestemmiare in chat.](#ff7e5e)' -error_chat_filter_caps: '[Non puoi usare il caps in chat!](#ff7e5e)' -error_chat_filter_spam: '[Per favore aspetta! Stai mandando messaggi troppo velocemente.](#ff7e5e)' -error_chat_filter_ascii: '[Non puoi usare caratteri speciali in chat.](#ff7e5e)' -error_chat_filter_repeat: '[Hai già mandato questo messaggio recentemente!](#ff7e5e)' -error_in_game_only: 'Error: That command can only be used in-game.' -error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' -error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' -error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' -error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' -error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' -error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' -error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' -error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' -removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' -list_conjunction: 'and' -error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di eseguire questo comando.](#ff7e5e)' + error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa: %1%](#ff7e5e)' + channel_switched: '[Adesso stai parlando in:](#00fb9a) [%1%](#00fb9a bold)' + error_no_permission_send: '[Errore:](#ff3300) [Non hai il permesso per parlare in %1%.](#ff7e5e)' + error_invalid_channel: '[Errore:](#ff3300) [Per favore specifica un canale valido.](#ff7e5e)' + error_invalid_channel_command: '[Errore:](#ff3300) [Questo canale non è valido!](#ff7e5e)' + error_no_channel: '[Errore:](#ff3300) [Stai provando a parlare in un canale non valido!](#ff7e5e) [Usa /channel per parlare in un canale valido.](#ff7e5e show_text=&#ff7e5e&Click here to suggest command suggest_command=/channel )' + error_player_not_found: '[Errore:](#ff3300) [Impossibile trovare il giocatore specificato; è online?](#ff7e5e)' + error_cannot_message_self: '[Errore:](#ff3300) [Non puoi mandare un messaggio a te stesso!](#ff7e5e)' + error_reply_no_messages: '[Errore:](#ff3300) [Non hai nessun messagio al quale rispondere!](#ff7e5e)' + error_reply_not_online: '[Errore:](#ff3300) [L''ultima persona con cui ti sei messaggiato non è online!](#ff7e5e)' + error_message_restricted_server: '[Errore:](#ff3300) [Non puoi mandare messaggi ai giocatori da questo server.](#ff7e5e)' + error_message_recipient_restricted_server: '[Errore:](#ff3300) [Questo giocatore è in un server dal quale non può ricevere messaggi.](#ff7e5e)' + error_channel_restricted_server: '[Errore:](#ff3300) [Non puoi parlare in %1% da questo server.](#ff7e5e)' + social_spy_toggled_on: '[Stai ora spiando i messaggi privati.](#00fb9a)' + social_spy_toggled_on_color: '[Stai ora spiando i messaggi privati in](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Non stai più spiando i messaggi privati.](#00fb9a)' + local_spy_toggled_on: '[Stai ora spiando i canali locali degli altri server.](#00fb9a)' + local_spy_toggled_on_color: '[Stai ora spiando il canale locale in](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Non stai più spiando i canali locali degli altri server.](#00fb9a)' + error_chat_filter_advertising: '[Non puoi promuovere o mandare link in chat.](#ff7e5e)' + error_chat_filter_profanity: '[Non puoi bestemmiare in chat.](#ff7e5e)' + error_chat_filter_caps: '[Non puoi usare il caps in chat!](#ff7e5e)' + error_chat_filter_spam: '[Per favore aspetta! Stai mandando messaggi troppo velocemente.](#ff7e5e)' + error_chat_filter_ascii: '[Non puoi usare caratteri speciali in chat.](#ff7e5e)' + error_chat_filter_repeat: '[Hai già mandato questo messaggio recentemente!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Error: That command can only be used in-game.' + error_console_local_scope: 'Error: Sending messages from console to channels with a local scope is unsupported.' + error_console_switch_channels: 'Error: You cannot switch to another channel from console. Please use the shortcut command instead.' + error_group_messages_disabled: '[Error:](#ff3300) [You cannot send messages to multiple people.](#ff7e5e)' + error_group_messages_max: '[Error:](#ff3300) [You can only send messages to a maximum of %1% people at once.](#ff7e5e)' + error_players_not_found: '[Error:](#ff3300) [Could not find any of the specified players; are they online?](#ff7e5e)' + error_reply_none_online: '[Error:](#ff3300) [Nobody in the last group of people you messaged are still online.](#ff7e5e)' + error_last_message_not_group: '[Error:](#ff3300) [The last message you received was not a group message.](#ff7e5e)' + error_no_messages_opt_out: '[Error:](#ff3300) [You have not yet sent or received group messages.](#ff7e5e)' + removed_from_group_message: '[Removed you from the group message between:](#00fb9a) %1%' + list_conjunction: 'and' + error_passthrough_shortcut_command: '[Error:](#ff3300) [Sending messages to passthrough channels using shortcut commands is not supported. Please switch to the channel first.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/ru-ru.yml b/common/src/main/resources/locales/ru-ru.yml index 944054b7..edc1b8f8 100644 --- a/common/src/main/resources/locales/ru-ru.yml +++ b/common/src/main/resources/locales/ru-ru.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | ru-ru, by Apexo01 | -# ------------------------------ -# If you'd like to use a different language, you can change it in the config.yml -# Change the appearance/text of messages in the plugin using this config. -# This config makes use of MineDown formatting, with extensive support for custom colors & formats. -# For formatting help, see: https://github.com/Phoenix616/MineDown - -error_no_permission: '[Ошибка:](#ff3300) [Недостаточно прав для вызова команды.](#ff7e5e)' -error_invalid_syntax: '[Ошибка:](#ff3300) [Неверный синтакс. Использование: %1%](#ff7e5e)' -channel_switched: '[Сейчас вы говорите в](#00fb9a) [%1%](#00fb9a bold) [чате!](#00fb9a)' -error_no_permission_send: '[Ошибка:](#ff3300) [Недостаточно прав говорить в %1% чате.](#ff7e5e)' -error_invalid_channel: '[Ошибка:](#ff3300) [Пожайлуста укажите валидный канал чата.](#ff7e5e)' -error_invalid_channel_command: '[Ошибка:](#ff3300) [Этот канал невалиден](#ff7e5e)' -error_no_channel: '[Ошибка:](#ff3300) [Вы пытаетесь говорить в невалидном канале чата.](#ff7e5e) [Используйте /channel чтобы переключится на исправный.](#ff7e5e show_text=&#ff7e5e&Кликните сюда чтобы преложить команду suggest_command=/channel )' -error_player_not_found: '[Ошибка:](#ff3300) [Не удалось найти указанного игрока; есть ли он в сети?](#ff7e5e)' -error_cannot_message_self: '[Ошибка:](#ff3300) [Вы не можете отправить самому себе!](#ff7e5e)' -error_reply_no_messages: '[Ошибка:](#ff3300) [Нет никого, кто бы отправил вам сообщение, на которое можно было бы ответить!](#ff7e5e)' -error_reply_not_online: '[Ошибка:](#ff3300) [Последний человек, с которым вы переписывались, больше не находится в сети.](#ff7e5e)' -error_message_restricted_server: '[Ошибка:](#ff3300) [Вы не можете писать игрокам с этого сервера.](#ff7e5e)' -error_message_recipient_restricted_server: '[Ошибка:](#ff3300) [Этот игрок находится на сервере, где сообщения не могут быть получены.](#ff7e5e)' -error_channel_restricted_server: '[Ошибка:](#ff3300) [Вы не можете общаться в %1% чате с этого сервера.](#ff7e5e)' -social_spy_toggled_on: '[Теперь вы следите за личными сообщениями.](#00fb9a)' -social_spy_toggled_on_color: '[Теперь вы следите за личными сообщениями в](#00fb9a) %1%%2%' -social_spy_toggled_off: '[Теперь вы больше не следите за личными сообщениями.](#00fb9a)' -local_spy_toggled_on: '[Теперь вы шпионите за локальными чат-каналами других серверов.](#00fb9a)' -local_spy_toggled_on_color: '[Теперь вы шпионите за локальным чатом в](#00fb9a) %1%%2%' -local_spy_toggled_off: '[Вы больше не шпионите за локальными чат-каналами других серверов.](#00fb9a)' -error_chat_filter_advertising: '[В чате нельзя рекламировать или размещать ссылки.](#ff7e5e)' -error_chat_filter_profanity: '[В чате нельзя использовать ненормативную лексику.](#ff7e5e)' -error_chat_filter_caps: '[Пожалуйста, следите за своими заглавными буквами в чате.](#ff7e5e)' -error_chat_filter_spam: '[Пожалуйста, подождите! Вы отправляете сообщения слишком быстро.](#ff7e5e)' -error_chat_filter_ascii: '[В чате нельзя использовать специальные символы.](#ff7e5e)' -error_chat_filter_repeat: '[Вы уже отправили это сообщение недавно!](#ff7e5e)' -error_in_game_only: 'Ошибка: Эта команда может быть использована только в игре.' -error_console_local_scope: 'Ошибка: Отправка сообщений из консоли в каналы с локальной областью видимости не поддерживается.' -error_console_switch_channels: 'Ошибка: Переключение на другой канал с консоли невозможно. Вместо этого используйте команду быстрого доступа.' -error_group_messages_disabled: '[Ошибка:](#ff3300) [Вы не можете отправлять сообщения нескольким людям.](#ff7e5e)' -error_group_messages_max: '[Ошибка:](#ff3300) [Одновременно можно отправлять сообщения не более чем %1% людей.](#ff7e5e)' -error_players_not_found: '[Ошибка:](#ff3300) [Не удалось найти ни одного из указанных игроков; есть ли они в сети?](#ff7e5e)' -error_reply_none_online: '[Ошибка:](#ff3300) [Никто из последней группы людей, с которыми вы переписывались, все еще не в сети.](#ff7e5e)' -error_last_message_not_group: '[Ошибка:](#ff3300) [Вы еще не отправляли и не получали групповые сообщения.](#ff7e5e)' -error_no_messages_opt_out: '[Ошибка:](#ff3300) [Вы еще не отправляли и не получали групповые сообщения.](#ff7e5e)' -removed_from_group_message: '[Удалил вас из группового сообщения между:](#00fb9a) %1%' -list_conjunction: и -error_passthrough_shortcut_command: '[Ошибка:](#ff3300) [Отправка сообщений в сквозные каналы с помощью команд быстрого доступа не поддерживается. Пожалуйста, сначала переключитесь на канал.](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| Вы используете поледнюю версию HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| Новая версия HuskChat уже доступна: v%1% (используется: v%2%).](#ff7e5e)' +locales: + error_no_permission: '[Ошибка:](#ff3300) [Недостаточно прав для вызова команды.](#ff7e5e)' + error_invalid_syntax: '[Ошибка:](#ff3300) [Неверный синтакс. Использование: %1%](#ff7e5e)' + channel_switched: '[Сейчас вы говорите в](#00fb9a) [%1%](#00fb9a bold) [чате!](#00fb9a)' + error_no_permission_send: '[Ошибка:](#ff3300) [Недостаточно прав говорить в %1% чате.](#ff7e5e)' + error_invalid_channel: '[Ошибка:](#ff3300) [Пожайлуста укажите валидный канал чата.](#ff7e5e)' + error_invalid_channel_command: '[Ошибка:](#ff3300) [Этот канал невалиден](#ff7e5e)' + error_no_channel: '[Ошибка:](#ff3300) [Вы пытаетесь говорить в невалидном канале чата.](#ff7e5e) [Используйте /channel чтобы переключится на исправный.](#ff7e5e show_text=&#ff7e5e&Кликните сюда чтобы преложить команду suggest_command=/channel )' + error_player_not_found: '[Ошибка:](#ff3300) [Не удалось найти указанного игрока; есть ли он в сети?](#ff7e5e)' + error_cannot_message_self: '[Ошибка:](#ff3300) [Вы не можете отправить самому себе!](#ff7e5e)' + error_reply_no_messages: '[Ошибка:](#ff3300) [Нет никого, кто бы отправил вам сообщение, на которое можно было бы ответить!](#ff7e5e)' + error_reply_not_online: '[Ошибка:](#ff3300) [Последний человек, с которым вы переписывались, больше не находится в сети.](#ff7e5e)' + error_message_restricted_server: '[Ошибка:](#ff3300) [Вы не можете писать игрокам с этого сервера.](#ff7e5e)' + error_message_recipient_restricted_server: '[Ошибка:](#ff3300) [Этот игрок находится на сервере, где сообщения не могут быть получены.](#ff7e5e)' + error_channel_restricted_server: '[Ошибка:](#ff3300) [Вы не можете общаться в %1% чате с этого сервера.](#ff7e5e)' + social_spy_toggled_on: '[Теперь вы следите за личными сообщениями.](#00fb9a)' + social_spy_toggled_on_color: '[Теперь вы следите за личными сообщениями в](#00fb9a) %1%%2%' + social_spy_toggled_off: '[Теперь вы больше не следите за личными сообщениями.](#00fb9a)' + local_spy_toggled_on: '[Теперь вы шпионите за локальными чат-каналами других серверов.](#00fb9a)' + local_spy_toggled_on_color: '[Теперь вы шпионите за локальным чатом в](#00fb9a) %1%%2%' + local_spy_toggled_off: '[Вы больше не шпионите за локальными чат-каналами других серверов.](#00fb9a)' + error_chat_filter_advertising: '[В чате нельзя рекламировать или размещать ссылки.](#ff7e5e)' + error_chat_filter_profanity: '[В чате нельзя использовать ненормативную лексику.](#ff7e5e)' + error_chat_filter_caps: '[Пожалуйста, следите за своими заглавными буквами в чате.](#ff7e5e)' + error_chat_filter_spam: '[Пожалуйста, подождите! Вы отправляете сообщения слишком быстро.](#ff7e5e)' + error_chat_filter_ascii: '[В чате нельзя использовать специальные символы.](#ff7e5e)' + error_chat_filter_repeat: '[Вы уже отправили это сообщение недавно!](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: 'Ошибка: Эта команда может быть использована только в игре.' + error_console_local_scope: 'Ошибка: Отправка сообщений из консоли в каналы с локальной областью видимости не поддерживается.' + error_console_switch_channels: 'Ошибка: Переключение на другой канал с консоли невозможно. Вместо этого используйте команду быстрого доступа.' + error_group_messages_disabled: '[Ошибка:](#ff3300) [Вы не можете отправлять сообщения нескольким людям.](#ff7e5e)' + error_group_messages_max: '[Ошибка:](#ff3300) [Одновременно можно отправлять сообщения не более чем %1% людей.](#ff7e5e)' + error_players_not_found: '[Ошибка:](#ff3300) [Не удалось найти ни одного из указанных игроков; есть ли они в сети?](#ff7e5e)' + error_reply_none_online: '[Ошибка:](#ff3300) [Никто из последней группы людей, с которыми вы переписывались, все еще не в сети.](#ff7e5e)' + error_last_message_not_group: '[Ошибка:](#ff3300) [Вы еще не отправляли и не получали групповые сообщения.](#ff7e5e)' + error_no_messages_opt_out: '[Ошибка:](#ff3300) [Вы еще не отправляли и не получали групповые сообщения.](#ff7e5e)' + removed_from_group_message: '[Удалил вас из группового сообщения между:](#00fb9a) %1%' + list_conjunction: и + error_passthrough_shortcut_command: '[Ошибка:](#ff3300) [Отправка сообщений в сквозные каналы с помощью команд быстрого доступа не поддерживается. Пожалуйста, сначала переключитесь на канал.](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| Вы используете поледнюю версию HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| Новая версия HuskChat уже доступна: v%1% (используется: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index 53965d31..c00edd96 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -1,49 +1,42 @@ -# ------------------------------ -# | HuskChat Messages | -# | zh-CN, by 10935336 | -# ------------------------------ -# 如果你想使用不同的语言,你可以在 config.yml 中更改。 -# 使用此配置更改插件中消息的样式或文本。 -# 此配置使用 MineDown 格式,广泛支持自定义颜色和格式。 -# 格式化帮助见: https://github.com/Phoenix616/MineDown - -error_no_permission: '[错误:](#ff3300) [你没有执行该指令的权限。](#ff7e5e)' -error_invalid_syntax: '[错误:](#ff3300) [语法不正确。用法: %1%](#ff7e5e)' -channel_switched: '[你已切换到](#00fb9a) [%1%](#00fb9a bold) [频道!](#00fb9a)' -error_no_permission_send: '[错误:](#ff3300) [你没有在 %1% 频道中说话的权限。](#ff7e5e)' -error_invalid_channel: '[错误:](#ff3300) [请指定一个有效的聊天频道。](#ff7e5e)' -error_invalid_channel_command: '[错误:](#ff3300) [频道无效](#ff7e5e)' -error_no_channel: '[错误:](#ff3300) [你正在一个无效的频道中说话。](#ff7e5e) [使用 /channel 切换到一个有效的频道。](#ff7e5e show_text=&#ff7e5e&点击这里获得命令建议 suggest_command=/channel )' -error_player_not_found: '[错误:](#ff3300) [找不到指定玩家;他们在线吗?](#ff7e5e)' -error_cannot_message_self: '[错误:](#ff3300) [你不能给自己发消息!](#ff7e5e)' -error_reply_no_messages: '[错误:](#ff3300) [没有人给你发可以回复的消息!](#ff7e5e)' -error_reply_not_online: '[错误:](#ff3300) [你最后发消息的人已经不在线了。](#ff7e5e)' -error_message_restricted_server: '[错误:](#ff3300) [你不能给这个服务器的玩家发送信息。](#ff7e5e)' -error_message_recipient_restricted_server: '[错误:](#ff3300) [该玩家在一个不能接收信息的服务器上。](#ff7e5e)' -error_channel_restricted_server: '[错误:](#ff3300) [你不能在这个服务器的 %1% 频道中说话](#ff7e5e)' -social_spy_toggled_on: '[你正在窥探私聊。](#00fb9a)' -social_spy_toggled_on_color: '[你正在窥探私聊于](#00fb9a) %1%%2%' -social_spy_toggled_off: '[你不再窥探私聊了。](#00fb9a)' -local_spy_toggled_on: '[你正在窥探其他服务器的本地聊天频道。](#00fb9a)' -local_spy_toggled_on_color: '[你正在窥探本地聊天于](#00fb9a) %1%%2%' -local_spy_toggled_off: '[你不再窥探其他服务器的本地聊天频道了。](#00fb9a)' -error_chat_filter_advertising: '[你不能发广告或链接。](#ff7e5e)' -error_chat_filter_profanity: '[不能说脏话哦](#ff7e5e)' -error_chat_filter_caps: '[请注意你在聊天中的大写字母。](#ff7e5e)' -error_chat_filter_spam: '[请稍等!你发送消息的速度太快了。](#ff7e5e)' -error_chat_filter_ascii: '[你不能再聊天中使用特殊字符。](#ff7e5e)' -error_chat_filter_repeat: '[你刚刚才发过这条信息](#ff7e5e)' -error_in_game_only: '错误: 该命令只能在游戏中使用.' -error_console_local_scope: '错误: 不支持从控制台发送消息到本地频道。' -error_console_switch_channels: '错误: 你不能在控制台切换到另一个频道,请改用快捷命令' -error_group_messages_disabled: '[错误:](#ff3300) [你不能向多个人发送消息](#ff7e5e)' -error_group_messages_max: '[错误:](#ff3300) [你一次最多只能向 %1% 个人发送消息](#ff7e5e)' -error_players_not_found: '[错误:](#ff3300) [找不到任何指定玩家;他们在线吗?](#ff7e5e)' -error_reply_none_online: '[错误:](#ff3300) [你最后发消息的那组人中没人还在线。](#ff7e5e)' -error_last_message_not_group: '[错误:](#ff3300) [你收到的最后一条信息不是群组信息。](#ff7e5e)' -error_no_messages_opt_out: '[错误:](#ff3300) [你还没有发送或接收群组信息。](#ff7e5e)' -removed_from_group_message: '[你被从群组信息中移除:](#00fb9a) %1%' -list_conjunction: '和' -error_passthrough_shortcut_command: '[错误:](#ff3300) [不支持使用快捷命令向 passthrough 频道发送消息。请先切换到该频道。](#ff7e5e)' -up_to_date: '[HuskChat](#00fb9a bold) [| 你正在使用最新版本的HuskChat (v%1%).](#00fb9a)' -update_available: '[HuskChat](#ff7e5e bold) [| 一个新版本的HuskChat已经可以更新: v%1% (当前: v%2%).](#ff7e5e)' \ No newline at end of file +locales: + error_no_permission: '[错误:](#ff3300) [你没有执行该指令的权限。](#ff7e5e)' + error_invalid_syntax: '[错误:](#ff3300) [语法不正确。用法: %1%](#ff7e5e)' + channel_switched: '[你已切换到](#00fb9a) [%1%](#00fb9a bold) [频道!](#00fb9a)' + error_no_permission_send: '[错误:](#ff3300) [你没有在 %1% 频道中说话的权限。](#ff7e5e)' + error_invalid_channel: '[错误:](#ff3300) [请指定一个有效的聊天频道。](#ff7e5e)' + error_invalid_channel_command: '[错误:](#ff3300) [频道无效](#ff7e5e)' + error_no_channel: '[错误:](#ff3300) [你正在一个无效的频道中说话。](#ff7e5e) [使用 /channel 切换到一个有效的频道。](#ff7e5e show_text=&#ff7e5e&点击这里获得命令建议 suggest_command=/channel )' + error_player_not_found: '[错误:](#ff3300) [找不到指定玩家;他们在线吗?](#ff7e5e)' + error_cannot_message_self: '[错误:](#ff3300) [你不能给自己发消息!](#ff7e5e)' + error_reply_no_messages: '[错误:](#ff3300) [没有人给你发可以回复的消息!](#ff7e5e)' + error_reply_not_online: '[错误:](#ff3300) [你最后发消息的人已经不在线了。](#ff7e5e)' + error_message_restricted_server: '[错误:](#ff3300) [你不能给这个服务器的玩家发送信息。](#ff7e5e)' + error_message_recipient_restricted_server: '[错误:](#ff3300) [该玩家在一个不能接收信息的服务器上。](#ff7e5e)' + error_channel_restricted_server: '[错误:](#ff3300) [你不能在这个服务器的 %1% 频道中说话](#ff7e5e)' + social_spy_toggled_on: '[你正在窥探私聊。](#00fb9a)' + social_spy_toggled_on_color: '[你正在窥探私聊于](#00fb9a) %1%%2%' + social_spy_toggled_off: '[你不再窥探私聊了。](#00fb9a)' + local_spy_toggled_on: '[你正在窥探其他服务器的本地聊天频道。](#00fb9a)' + local_spy_toggled_on_color: '[你正在窥探本地聊天于](#00fb9a) %1%%2%' + local_spy_toggled_off: '[你不再窥探其他服务器的本地聊天频道了。](#00fb9a)' + error_chat_filter_advertising: '[你不能发广告或链接。](#ff7e5e)' + error_chat_filter_profanity: '[不能说脏话哦](#ff7e5e)' + error_chat_filter_caps: '[请注意你在聊天中的大写字母。](#ff7e5e)' + error_chat_filter_spam: '[请稍等!你发送消息的速度太快了。](#ff7e5e)' + error_chat_filter_ascii: '[你不能再聊天中使用特殊字符。](#ff7e5e)' + error_chat_filter_repeat: '[你刚刚才发过这条信息](#ff7e5e)' + error_chat_filter_regex: '[Your message contains blocked text.](#ff7e5e)' + error_in_game_only: '错误: 该命令只能在游戏中使用.' + error_console_local_scope: '错误: 不支持从控制台发送消息到本地频道。' + error_console_switch_channels: '错误: 你不能在控制台切换到另一个频道,请改用快捷命令' + error_group_messages_disabled: '[错误:](#ff3300) [你不能向多个人发送消息](#ff7e5e)' + error_group_messages_max: '[错误:](#ff3300) [你一次最多只能向 %1% 个人发送消息](#ff7e5e)' + error_players_not_found: '[错误:](#ff3300) [找不到任何指定玩家;他们在线吗?](#ff7e5e)' + error_reply_none_online: '[错误:](#ff3300) [你最后发消息的那组人中没人还在线。](#ff7e5e)' + error_last_message_not_group: '[错误:](#ff3300) [你收到的最后一条信息不是群组信息。](#ff7e5e)' + error_no_messages_opt_out: '[错误:](#ff3300) [你还没有发送或接收群组信息。](#ff7e5e)' + removed_from_group_message: '[你被从群组信息中移除:](#00fb9a) %1%' + list_conjunction: '和' + error_passthrough_shortcut_command: '[错误:](#ff3300) [不支持使用快捷命令向 passthrough 频道发送消息。请先切换到该频道。](#ff7e5e)' + up_to_date: '[HuskChat](#00fb9a bold) [| 你正在使用最新版本的HuskChat (v%1%).](#00fb9a)' + update_available: '[HuskChat](#ff7e5e bold) [| 一个新版本的HuskChat已经可以更新: v%1% (当前: v%2%).](#ff7e5e)' \ No newline at end of file diff --git a/common/src/test/java/net/william278/huskchat/filter/AdvertisingFilterTests.java b/common/src/test/java/net/william278/huskchat/filter/AdvertisingFilterTests.java index 7fc75c62..f913a46c 100644 --- a/common/src/test/java/net/william278/huskchat/filter/AdvertisingFilterTests.java +++ b/common/src/test/java/net/william278/huskchat/filter/AdvertisingFilterTests.java @@ -19,42 +19,42 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.TestPlayer; +import net.william278.huskchat.user.TestOnlineUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class AdvertisingFilterTests { - AdvertisingFilterer filterer = new AdvertisingFilterer(); + AdvertisingFilterer filterer = new AdvertisingFilterer(new ChatFilter.FilterSettings()); @Test public void testSentence() { - Assertions.assertTrue(filterer.isAllowed(new TestPlayer(), "This is an example sentence!")); + Assertions.assertTrue(filterer.isAllowed(new TestOnlineUser(), "This is an example sentence!")); } @Test public void testFullUrl() { - Assertions.assertFalse(filterer.isAllowed(new TestPlayer(), "https://william278.net")); + Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "https://william278.net")); } @Test public void testPartialUrl() { - Assertions.assertFalse(filterer.isAllowed(new TestPlayer(), "william278.net")); + Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "william278.net")); } @Test public void testSubDomainUrl() { - Assertions.assertFalse(filterer.isAllowed(new TestPlayer(), "example.william278.net")); + Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "example.william278.net")); } @Test public void testSubDomainUrlWithPort() { - Assertions.assertFalse(filterer.isAllowed(new TestPlayer(), "william278.net:25565")); + Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "william278.net:25565")); } @Test public void testTopLevelDomain() { - Assertions.assertTrue(filterer.isAllowed(new TestPlayer(), ".net")); + Assertions.assertTrue(filterer.isAllowed(new TestOnlineUser(), ".net")); } } diff --git a/common/src/test/java/net/william278/huskchat/filter/AsciiFilterTests.java b/common/src/test/java/net/william278/huskchat/filter/AsciiFilterTests.java index 089e57ed..611dfbdc 100644 --- a/common/src/test/java/net/william278/huskchat/filter/AsciiFilterTests.java +++ b/common/src/test/java/net/william278/huskchat/filter/AsciiFilterTests.java @@ -19,27 +19,27 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.TestPlayer; +import net.william278.huskchat.user.TestOnlineUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class AsciiFilterTests { - AsciiFilter asciiFilter = new AsciiFilter(); + AsciiFilter asciiFilter = new AsciiFilter(new ChatFilter.FilterSettings()); @Test public void testAsciiSentence() { - Assertions.assertTrue(asciiFilter.isAllowed(new TestPlayer(), "This is a test sentence")); + Assertions.assertTrue(asciiFilter.isAllowed(new TestOnlineUser(), "This is a test sentence")); } @Test public void testAsciiSentenceWithMathematicalSymbolsAndPunctuation() { - Assertions.assertTrue(asciiFilter.isAllowed(new TestPlayer(), "This is a (test) sentence with [mathematical symbols], like + - = != etc :-)")); + Assertions.assertTrue(asciiFilter.isAllowed(new TestOnlineUser(), "This is a (test) sentence with [mathematical symbols], like + - = != etc :-)")); } @Test public void testUnicodeSentence() { - Assertions.assertFalse(asciiFilter.isAllowed(new TestPlayer(), "• This is a test sentence with ♣ UNICODE ♣ characters •")); + Assertions.assertFalse(asciiFilter.isAllowed(new TestOnlineUser(), "• This is a test sentence with ♣ UNICODE ♣ characters •")); } } diff --git a/common/src/test/java/net/william278/huskchat/filter/CapsFilterTests.java b/common/src/test/java/net/william278/huskchat/filter/CapsFilterTests.java index f3d7145b..4dba2ab8 100644 --- a/common/src/test/java/net/william278/huskchat/filter/CapsFilterTests.java +++ b/common/src/test/java/net/william278/huskchat/filter/CapsFilterTests.java @@ -19,59 +19,59 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.TestPlayer; +import net.william278.huskchat.user.TestOnlineUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class CapsFilterTests { - CapsFilter capsFilter50Percent = new CapsFilter(0.5); - CapsFilter capsFilter25Percent = new CapsFilter(0.25); - CapsFilter capsFilter100Percent = new CapsFilter(1D); + CapsFilter capsFilter50Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(0.5)); + CapsFilter capsFilter25Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(0.25)); + CapsFilter capsFilter100Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(1.0)); @Test public void testCapsFilter_50PercentCaps_FullCaps() { - Assertions.assertFalse(capsFilter50Percent.isAllowed(new TestPlayer(), "THIS IS A TEST MESSAGE")); + Assertions.assertFalse(capsFilter50Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); } @Test public void testCapsFilter_50PercentCaps_LowerCase() { - Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestPlayer(), "this is a test message")); + Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestOnlineUser(), "this is a test message")); } @Test public void testCapsFilter_50PercentCaps_HalfCaps() { - Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestPlayer(), "this is a TEST MESSAGE")); + Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); } @Test public void testCapsFilter_25PercentCaps_FullCaps() { - Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestPlayer(), "THIS IS A TEST MESSAGE")); + Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); } @Test public void testCapsFilter_25PercentCaps_LowerCase() { - Assertions.assertTrue(capsFilter25Percent.isAllowed(new TestPlayer(), "this is a test message")); + Assertions.assertTrue(capsFilter25Percent.isAllowed(new TestOnlineUser(), "this is a test message")); } @Test public void testCapsFilter_25PercentCaps_HalfCaps() { - Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestPlayer(), "this is a TEST MESSAGE")); + Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); } @Test public void testCapsFilter_100PercentCaps_FullCaps() { - Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestPlayer(), "THIS IS A TEST MESSAGE")); + Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); } @Test public void testCapsFilter_100PercentCaps_LowerCase() { - Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestPlayer(), "this is a test message")); + Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "this is a test message")); } @Test public void testCapsFilter_100PercentCaps_HalfCaps() { - Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestPlayer(), "this is a TEST MESSAGE")); + Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); } } diff --git a/common/src/test/java/net/william278/huskchat/filter/ProfanityFilterTests.java b/common/src/test/java/net/william278/huskchat/filter/ProfanityFilterTests.java index 2e26b8b4..9129b346 100644 --- a/common/src/test/java/net/william278/huskchat/filter/ProfanityFilterTests.java +++ b/common/src/test/java/net/william278/huskchat/filter/ProfanityFilterTests.java @@ -19,46 +19,49 @@ package net.william278.huskchat.filter; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.TestPlayer; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.TestOnlineUser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ProfanityFilterTests { private final ProfanityFilterer filterer = new ProfanityFilterer( - ProfanityFilterer.ProfanityFilterMode.TOLERANCE, 0.8d, null); + new ProfanityFilterer.ProfanityFilterSettings( + "", ProfanityFilterer.ProfanityFilterMode.TOLERANCE, 0.8d + ) + ); @Test public void givenSentenceContainingProfanity_testIsProfane() { - final Player dummyPlayer = new TestPlayer(); + final OnlineUser dummyPlayer = new TestOnlineUser(); Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "This is a fucking test sentence")); Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "Shit")); } @Test public void givenNormalSentences_testIsNotProfane() { - final Player dummyPlayer = new TestPlayer(); + final OnlineUser dummyPlayer = new TestOnlineUser(); Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "AHOJ")); Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "Hello")); } @Test public void givenObfuscatedProfanity_testIsProfane() { - final Player dummyPlayer = new TestPlayer(); + final OnlineUser dummyPlayer = new TestOnlineUser(); Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "You're a fuck1ng idiot")); Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "Shut the h3ll up")); } @Test public void givenScunthorpe_testIsNotProfane() { - final Player dummyPlayer = new TestPlayer(); + final OnlineUser dummyPlayer = new TestOnlineUser(); Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "Scunthorpe")); } @Test public void givenLeetSpeak_testIsNotProfane() { - final Player dummyPlayer = new TestPlayer(); + final OnlineUser dummyPlayer = new TestOnlineUser(); Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "1337")); } } diff --git a/common/src/test/java/net/william278/huskchat/player/TestPlayer.java b/common/src/test/java/net/william278/huskchat/user/TestOnlineUser.java similarity index 74% rename from common/src/test/java/net/william278/huskchat/player/TestPlayer.java rename to common/src/test/java/net/william278/huskchat/user/TestOnlineUser.java index 8d80c13b..f59cb088 100644 --- a/common/src/test/java/net/william278/huskchat/player/TestPlayer.java +++ b/common/src/test/java/net/william278/huskchat/user/TestOnlineUser.java @@ -17,40 +17,21 @@ * limitations under the License. */ -package net.william278.huskchat.player; +package net.william278.huskchat.user; import net.kyori.adventure.audience.Audience; import org.jetbrains.annotations.NotNull; import java.util.UUID; -public class TestPlayer implements Player { +public class TestOnlineUser extends OnlineUser { private final static int TEST_PLAYER_PING = 5; private final static String TEST_PLAYER_SERVER = "test"; private final static int TEST_PLAYER_SERVER_PLAYER_COUNT = 1; - private final UUID uuid; - private final String name; - - /** - * Implementation of a {@link Player} for unit testing - */ - public TestPlayer() { - this.uuid = UUID.randomUUID(); - this.name = UUID.randomUUID().toString().split("-")[0]; - } - - @Override - @NotNull - public String getName() { - return name; - } - - @NotNull - @Override - public UUID getUuid() { - return uuid; + public TestOnlineUser() { + super(UUID.randomUUID().toString().split("-")[0], UUID.randomUUID()); } @Override diff --git a/docs/Setup.md b/docs/Setup.md index 05f8b06c..925a8e67 100644 --- a/docs/Setup.md +++ b/docs/Setup.md @@ -1,8 +1,8 @@ This will walk you through installing HuskChat on your Velocity or BungeeCord/Waterfall-based proxy server. ## Requirements -* Java 16+ -* A Spigot (1.16.5+) Minecraft server (for single-server setups) OR a Velocity (recommended) or BungeeCord/Waterfall-based proxy server +* Java 17+ +* A Spigot (1.17.1+) Minecraft server (for single-server setups) OR a Velocity (recommended) or BungeeCord/Waterfall-based proxy server > **Warning:** The Spigot plugin "NoChatReports" will not work with HuskChat. Use FreedomChat instead if you wish to eliminate chat report warnings. diff --git a/gradle.properties b/gradle.properties index dbb6d296..b908de8e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,11 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.parallel=true org.gradle.daemon=true -javaVersion=16 +javaVersion=17 -plugin_version=2.7.1 +plugin_version=3.0 plugin_archive=huskchat -plugin_description=A simple & customizable no-frills Minecraft chat system \ No newline at end of file +plugin_description=A simple & customizable no-frills Minecraft chat system + +velocity_api_version=3.3.0 +velocity_minimum_build=329 \ No newline at end of file diff --git a/velocity/build.gradle b/velocity/build.gradle index 6413f43e..4c79190e 100644 --- a/velocity/build.gradle +++ b/velocity/build.gradle @@ -1,13 +1,23 @@ +plugins { + id 'xyz.jpenilla.run-velocity' version '2.2.2' +} + dependencies { implementation project(path: ':common') - implementation 'org.bstats:bstats-velocity:3.0.2' - compileOnly 'com.velocitypowered:velocity-api:3.2.0-SNAPSHOT' - compileOnly 'commons-io:commons-io:2.15.0' - compileOnly 'dev.dejvokep:boosted-yaml:1.3.1' + compileOnly "com.velocitypowered:velocity-api:${velocity_api_version}-SNAPSHOT" + compileOnly "com.velocitypowered:velocity-proxy:${velocity_api_version}-SNAPSHOT" + + compileOnly 'io.netty:netty-codec-http:4.1.106.Final' + compileOnly 'it.unimi.dsi:fastutil:8.5.12' + compileOnly 'commons-io:commons-io:2.15.1' compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT' compileOnly 'org.jetbrains:annotations:24.1.0' + compileOnly 'org.projectlombok:lombok:1.18.30' + compileOnly 'net.kyori:adventure-nbt:4.15.0' + + annotationProcessor 'org.projectlombok:lombok:1.18.30' } shadowJar { @@ -21,9 +31,19 @@ shadowJar { relocate 'org.jetbrains', 'net.william278.huskchat.libraries' relocate 'org.intellij', 'net.william278.huskchat.libraries' relocate 'org.bstats', 'net.william278.huskchat.libraries.bstats' + relocate 'com.github.retrooper', 'net.william278.huskchat.libraries' + relocate 'io.github.retrooper', 'net.william278.huskchat.libraries' dependencies { //noinspection GroovyAssignabilityCheck exclude dependency(':slf4j-api') } + + minimize() +} + +tasks { + runVelocity { + velocityVersion("${velocity_api_version}-SNAPSHOT") + } } \ No newline at end of file diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChat.java b/velocity/src/main/java/net/william278/huskchat/VelocityHuskChat.java similarity index 51% rename from velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChat.java rename to velocity/src/main/java/net/william278/huskchat/VelocityHuskChat.java index 093a7c0f..b2308569 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChat.java +++ b/velocity/src/main/java/net/william278/huskchat/VelocityHuskChat.java @@ -17,7 +17,7 @@ * limitations under the License. */ -package net.william278.huskchat.velocity; +package net.william278.huskchat; import com.google.inject.Inject; import com.velocitypowered.api.event.Subscribe; @@ -26,123 +26,121 @@ import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import net.kyori.adventure.audience.Audience; import net.william278.desertwell.util.Version; -import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; +import net.william278.huskchat.api.VelocityHuskChatAPI; import net.william278.huskchat.command.ShortcutCommand; +import net.william278.huskchat.command.VelocityCommand; +import net.william278.huskchat.config.Channels; +import net.william278.huskchat.config.Filters; import net.william278.huskchat.config.Locales; import net.william278.huskchat.config.Settings; import net.william278.huskchat.discord.DiscordHook; +import net.william278.huskchat.event.VelocityEventProvider; +import net.william278.huskchat.filter.ChatFilter; import net.william278.huskchat.getter.DataGetter; import net.william278.huskchat.getter.DefaultDataGetter; import net.william278.huskchat.getter.LuckPermsDataGetter; +import net.william278.huskchat.listener.VelocityEventChatListener; +import net.william278.huskchat.listener.VelocityPacketChatListener; +import net.william278.huskchat.listener.VelocityPlayerListener; import net.william278.huskchat.placeholders.DefaultReplacer; import net.william278.huskchat.placeholders.PAPIProxyBridgeReplacer; import net.william278.huskchat.placeholders.PlaceholderReplacer; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.player.PlayerCache; -import net.william278.huskchat.velocity.command.VelocityCommand; -import net.william278.huskchat.velocity.event.VelocityEventDispatcher; -import net.william278.huskchat.velocity.listener.VelocityListener; -import net.william278.huskchat.velocity.player.VelocityPlayer; +import net.william278.huskchat.user.OnlineUser; +import net.william278.huskchat.user.UserCache; +import net.william278.huskchat.user.VelocityUser; import org.bstats.velocity.Metrics; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -import java.io.File; import java.io.InputStream; import java.nio.file.Path; import java.util.*; import java.util.logging.Level; @Plugin(id = "huskchat") -public class VelocityHuskChat implements HuskChat { +@Getter +public class VelocityHuskChat implements HuskChat, VelocityEventProvider { // bStats ID private static final int METRICS_ID = 14187; - // Instance provider - private static VelocityHuskChat instance; - - public static VelocityHuskChat getInstance() { - return instance; - } - // Plugin version private final PluginContainer container; private final Logger logger; private final Metrics.Factory metrics; - private final Path dataDirectory; + private final Path configDirectory; private final ProxyServer server; - private List commands; + private final List filtersAndReplacers = new ArrayList<>(); + private final UserCache userCache = new UserCache(); + private final List placeholderReplacers = new ArrayList<>(); + + @Setter private Settings settings; - private VelocityEventDispatcher eventDispatcher; - private DiscordHook discordHook; + @Setter private Locales locales; - private DataGetter playerDataGetter; - private PlayerCache playerCache; - private List placeholders; + @Setter + private Channels channels; + @Setter + private Filters filterSettings; + @Setter + @Getter(AccessLevel.NONE) + private DiscordHook discordHook; + private DataGetter dataGetter; @Inject public VelocityHuskChat(@NotNull ProxyServer server, @NotNull org.slf4j.Logger logger, - @DataDirectory Path dataDirectory, @NotNull Metrics.Factory metrics, + @DataDirectory Path configDirectory, @NotNull Metrics.Factory metrics, @NotNull PluginContainer pluginContainer) { - instance = this; - this.server = server; this.logger = logger; - this.dataDirectory = dataDirectory; + this.configDirectory = configDirectory; this.metrics = metrics; this.container = pluginContainer; - - VelocityHuskChatAPI.register(this); } @Subscribe public void onProxyInitialization(@NotNull ProxyInitializeEvent event) { - // Check plugin compat - if (!isSigningPluginInstalled()) { - return; - } - // Load config and locale files this.loadConfig(); // Load discord hook this.loadDiscordHook(); - // Load event dispatcher - this.eventDispatcher = new VelocityEventDispatcher(server); - - // Load saved social spy state - this.playerCache = new PlayerCache(this); - // Setup player data getter if (isPluginPresent("luckperms")) { - this.playerDataGetter = new LuckPermsDataGetter(); + this.dataGetter = new LuckPermsDataGetter(); } else { - this.playerDataGetter = new DefaultDataGetter(); + this.dataGetter = new DefaultDataGetter(); } // Setup PlaceholderParser - this.placeholders = new ArrayList<>(); - this.placeholders.add(new DefaultReplacer(this)); - if (getSettings().doPlaceholderAPI() && isPluginPresent("papiproxybridge")) { - this.placeholders.add(new PAPIProxyBridgeReplacer(this)); + this.placeholderReplacers.add(new DefaultReplacer(this)); + if (getSettings().getPlaceholder().isUsePapi() && isPluginPresent("papiproxybridge")) { + this.placeholderReplacers.add(new PAPIProxyBridgeReplacer(this)); } // Register events - getProxyServer().getEventManager().register(this, new VelocityListener(this)); + getProxyServer().getEventManager().register(this, new VelocityPlayerListener(this)); + if (getSettings().isUsePacketListening()) { + new VelocityPacketChatListener(this).register(); + } else { + getProxyServer().getEventManager().register(this, new VelocityEventChatListener(this)); + } // Register commands & channel shortcuts - this.commands = new ArrayList<>(VelocityCommand.Type.getCommands(this)); - this.commands.addAll(getSettings().getChannels().values().stream() - .flatMap(channel -> channel.getShortcutCommands().stream().map(command -> new VelocityCommand( + VelocityCommand.Type.registerAll(this); + getChannels().getChannels().forEach(channel -> channel.getShortcutCommands() + .forEach(command -> new VelocityCommand( new ShortcutCommand(command, channel.getId(), this), this - ))) - .toList()); + ))); + + VelocityHuskChatAPI.register(this); // Initialise metrics and log this.metrics.make(this, METRICS_ID); @@ -150,71 +148,11 @@ public void onProxyInitialization(@NotNull ProxyInitializeEvent event) { log(Level.INFO, "Enabled HuskChat version " + getVersion()); } - // Ensures a signing plugin is installed - private boolean isSigningPluginInstalled() { - boolean usvPresent = isPluginPresent("unsignedvelocity"); - boolean svPresent = isPluginPresent("signedvelocity"); - if (usvPresent && svPresent) { - log(Level.SEVERE, "Both UnsignedVelocity and SignedVelocity are present!\n" + - "Please uninstall UnsignedVelocity. HuskChat will now be disabled." - ); - return false; - } - if (!(usvPresent || svPresent)) { - log(Level.WARNING, "Neither UnsignedVelocity nor SignedVelocity are present!\n" + - "Install SignedVelocity (https://modrinth.com/plugin/signedvelocity) for 1.19+ support."); - } else if (usvPresent) { - log(Level.WARNING, "UnsignedVelocity is deprecated; please install SignedVelocity " + - " (https://modrinth.com/plugin/signedvelocity) instead for better support."); - } - return true; - } - - @NotNull - @Override - public Locales getLocales() { - return locales; - } - - @Override - public void setLocales(@NotNull Locales locales) { - this.locales = locales; - } - - @Override - @NotNull - public Settings getSettings() { - return settings; - } - - @Override - public void setSettings(@NotNull Settings settings) { - this.settings = settings; - } - - @NotNull - @Override - public VelocityEventDispatcher getEventDispatcher() { - return eventDispatcher; - } - - @Override - @NotNull - public PlayerCache getPlayerCache() { - return playerCache; - } - - @Override public Optional getDiscordHook() { return Optional.ofNullable(discordHook); } - @Override - public void setDiscordHook(@NotNull DiscordHook discordHook) { - this.discordHook = discordHook; - } - @NotNull @Override public Version getVersion() { @@ -235,65 +173,42 @@ public String getPlatform() { } - @NotNull @Override - public List getPlaceholderReplacers() { - return placeholders; + public Optional getPlayer(@NotNull UUID uuid) { + return getProxyServer().getPlayer(uuid).map(player -> VelocityUser.adapt(player, this)); } @Override @NotNull - public DataGetter getDataGetter() { - return playerDataGetter; - } - - @Override - public Optional getPlayer(@NotNull UUID uuid) { - final Optional player = getProxyServer().getPlayer(uuid); - return player.map(VelocityPlayer::adapt); - } - - @Override - public Collection getOnlinePlayers() { - final ArrayList velocityPlayers = new ArrayList<>(); - for (com.velocitypowered.api.proxy.Player player : getProxyServer().getAllPlayers()) { - velocityPlayers.add(VelocityPlayer.adapt(player)); - } - return velocityPlayers; - } - - @Override - public Collection getOnlinePlayersOnServer(@NotNull Player serverPlayer) { - final ArrayList velocityPlayers = new ArrayList<>(); - VelocityPlayer.toVelocity(serverPlayer).flatMap(com.velocitypowered.api.proxy.Player::getCurrentServer).ifPresent(serverConnection -> { - for (com.velocitypowered.api.proxy.Player connectedPlayer : serverConnection.getServer().getPlayersConnected()) { - velocityPlayers.add(VelocityPlayer.adapt(connectedPlayer)); - } - }); - return velocityPlayers; + public Collection getOnlinePlayers() { + return getProxyServer().getAllPlayers().stream() + .map(player -> (OnlineUser) VelocityUser.adapt(player, this)).toList(); } @Override @NotNull - public Audience getConsole() { - return getProxyServer().getConsoleCommandSource(); + public Collection getOnlinePlayersOnServer(@NotNull OnlineUser user) { + return ((VelocityUser) user).getPlayer().getCurrentServer() + .map(conn -> conn.getServer().getPlayersConnected().stream() + .map(player -> (OnlineUser) VelocityUser.adapt(player, this)).toList()) + .orElseGet(Collections::emptyList); } @Override - public Optional findPlayer(@NotNull String username) { + public Optional findPlayer(@NotNull String username) { if (username.isEmpty()) { return Optional.empty(); } - final Optional optionalPlayer; + final Optional optionalPlayer; if (getProxyServer().getPlayer(username).isPresent()) { final com.velocitypowered.api.proxy.Player player = getProxyServer().getPlayer(username).get(); - optionalPlayer = Optional.of(VelocityPlayer.adapt(player)); + optionalPlayer = Optional.of(VelocityUser.adapt(player, this)); } else { final List matchedPlayers = getProxyServer().matchPlayer(username) .stream().filter(val -> val.getUsername().startsWith(username)).sorted().toList(); if (!matchedPlayers.isEmpty()) { - optionalPlayer = Optional.of(VelocityPlayer.adapt(matchedPlayers.get(0))); + optionalPlayer = Optional.of(VelocityUser.adapt(matchedPlayers.get(0), this)); } else { optionalPlayer = Optional.empty(); } @@ -301,18 +216,10 @@ public Optional findPlayer(@NotNull String username) { return optionalPlayer; } - - // Get the data folder - @NotNull - @Override - public File getDataFolder() { - return dataDirectory.toFile(); - } - @Nullable @Override public InputStream getResource(@NotNull String path) { - return HuskChat.class.getClassLoader().getResourceAsStream(path); + return getClass().getClassLoader().getResourceAsStream(path); } @Override @@ -346,8 +253,21 @@ public void log(@NotNull Level level, @NotNull String message, @NotNull Throwabl } } + @NotNull + @Override + public Audience getAudience(@NotNull UUID user) { + return getProxyServer().getPlayer(user).map(player -> (Audience) player).orElse(Audience.empty()); + } + + @NotNull + @Override + public Audience getConsole() { + return getProxyServer().getConsoleCommandSource(); + } + + @NotNull @Override - public VelocityHuskChatAPI getAPI() { - return VelocityHuskChatAPI.getInstance(); + public HuskChat getPlugin() { + return this; } } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChatAPI.java b/velocity/src/main/java/net/william278/huskchat/api/VelocityHuskChatAPI.java similarity index 78% rename from velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChatAPI.java rename to velocity/src/main/java/net/william278/huskchat/api/VelocityHuskChatAPI.java index 30f5d54a..8128b3f3 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/VelocityHuskChatAPI.java +++ b/velocity/src/main/java/net/william278/huskchat/api/VelocityHuskChatAPI.java @@ -17,20 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.velocity; +package net.william278.huskchat.api; import com.velocitypowered.api.proxy.Player; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.HuskChatAPI; -import net.william278.huskchat.velocity.player.VelocityPlayer; +import net.william278.huskchat.VelocityHuskChat; +import net.william278.huskchat.user.VelocityUser; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("unused") public class VelocityHuskChatAPI extends HuskChatAPI { - public VelocityHuskChatAPI(HuskChat plugin) { + + private VelocityHuskChatAPI(@NotNull HuskChat plugin) { super(plugin); } + @NotNull public static VelocityHuskChatAPI getInstance() { return (VelocityHuskChatAPI) instance; } @@ -46,9 +49,10 @@ public static void register(@NotNull VelocityHuskChat plugin) { /** * Adapts a platform-specific Player object to a cross-platform Player object * @param player Must be a platform-specific Player object, e.g. a Velocity Player - * @return {@link VelocityPlayer} + * @return {@link VelocityUser} */ - public VelocityPlayer adaptPlayer(@NotNull Player player) { - return VelocityPlayer.adapt(player); + @NotNull + public VelocityUser adaptPlayer(@NotNull Player player) { + return VelocityUser.adapt(player, plugin); } } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/command/VelocityCommand.java b/velocity/src/main/java/net/william278/huskchat/command/VelocityCommand.java similarity index 69% rename from velocity/src/main/java/net/william278/huskchat/velocity/command/VelocityCommand.java rename to velocity/src/main/java/net/william278/huskchat/command/VelocityCommand.java index 234cbb39..80594fa1 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/command/VelocityCommand.java +++ b/velocity/src/main/java/net/william278/huskchat/command/VelocityCommand.java @@ -17,14 +17,13 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.command; +package net.william278.huskchat.command; import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.proxy.Player; -import net.william278.huskchat.command.*; -import net.william278.huskchat.player.ConsolePlayer; -import net.william278.huskchat.velocity.VelocityHuskChat; -import net.william278.huskchat.velocity.player.VelocityPlayer; +import net.william278.huskchat.VelocityHuskChat; +import net.william278.huskchat.user.ConsoleUser; +import net.william278.huskchat.user.VelocityUser; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -52,16 +51,16 @@ public VelocityCommand(@NotNull CommandBase command, @NotNull VelocityHuskChat p @Override public void execute(@NotNull Invocation invocation) { if (invocation.source() instanceof Player player) { - command.onExecute(VelocityPlayer.adapt(player), invocation.arguments()); + command.onExecute(VelocityUser.adapt(player, plugin), invocation.arguments()); } else { - command.onExecute(ConsolePlayer.create(plugin), invocation.arguments()); + command.onExecute(ConsoleUser.wrap(plugin), invocation.arguments()); } } @Override public List suggest(@NotNull Invocation invocation) { if (invocation.source() instanceof Player player) { - return command.onTabComplete(VelocityPlayer.adapt(player), invocation.arguments()); + return command.onTabComplete(VelocityUser.adapt(player, plugin), invocation.arguments()); } return List.of(); } @@ -74,17 +73,17 @@ public boolean hasPermission(@NotNull Invocation invocation) { public enum Type { HUSKCHAT((plugin) -> Optional.of(new VelocityCommand(new HuskChatCommand(plugin), plugin))), CHANNEL((plugin) -> Optional.of(new VelocityCommand(new ChannelCommand(plugin), plugin))), - MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new VelocityCommand(new MessageCommand(plugin), plugin)) : Optional.empty()), - REPLY((plugin) -> plugin.getSettings().isDoMessageCommand() + REPLY((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new VelocityCommand(new ReplyCommand(plugin), plugin)) : Optional.empty()), - OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().isDoMessageCommand() + OPT_OUT_MESSAGE((plugin) -> plugin.getSettings().getMessageCommand().isEnabled() ? Optional.of(new VelocityCommand(new OptOutMessageCommand(plugin), plugin)) : Optional.empty()), - BROADCAST((plugin) -> plugin.getSettings().isDoBroadcastCommand() + BROADCAST((plugin) -> plugin.getSettings().getBroadcastCommand().isEnabled() ? Optional.of(new VelocityCommand(new BroadcastCommand(plugin), plugin)) : Optional.empty()), - SOCIAL_SPY((plugin) -> plugin.getSettings().doSocialSpyCommand() + SOCIAL_SPY((plugin) -> plugin.getSettings().getSocialSpy().isEnabled() ? Optional.of(new VelocityCommand(new SocialSpyCommand(plugin), plugin)) : Optional.empty()), - LOCAL_SPY((plugin) -> plugin.getSettings().doLocalSpyCommand() + LOCAL_SPY((plugin) -> plugin.getSettings().getLocalSpy().isEnabled() ? Optional.of(new VelocityCommand(new LocalSpyCommand(plugin), plugin)) : Optional.empty()); private final Function> commandSupplier; @@ -93,18 +92,14 @@ public enum Type { this.commandSupplier = commandSupplier; } - @NotNull - private Optional create(@NotNull VelocityHuskChat plugin) { - return commandSupplier.apply(plugin); + private void register(@NotNull VelocityHuskChat plugin) { + commandSupplier.apply(plugin); } - public static List getCommands(@NotNull VelocityHuskChat plugin) { - return Arrays.stream(values()) - .map(type -> type.create(plugin)) - .filter(Optional::isPresent).map(Optional::get) - .toList(); + public static void registerAll(@NotNull VelocityHuskChat plugin) { + Arrays.stream(values()).forEach(type -> type.register(plugin)); } } - + } \ No newline at end of file diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/event/BroadcastMessageEvent.java b/velocity/src/main/java/net/william278/huskchat/event/VelocityBroadcastMessageEvent.java similarity index 73% rename from velocity/src/main/java/net/william278/huskchat/velocity/event/BroadcastMessageEvent.java rename to velocity/src/main/java/net/william278/huskchat/event/VelocityBroadcastMessageEvent.java index 5e1c8f80..6be51ec5 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/event/BroadcastMessageEvent.java +++ b/velocity/src/main/java/net/william278/huskchat/event/VelocityBroadcastMessageEvent.java @@ -17,24 +17,23 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public class BroadcastMessageEvent extends VelocityEvent implements IBroadcastMessageEvent { - private Player sender; +public class VelocityBroadcastMessageEvent extends VelocityEvent implements BroadcastMessageEvent { + private OnlineUser sender; private String message; - public BroadcastMessageEvent(Player sender, String message) { + public VelocityBroadcastMessageEvent(OnlineUser sender, String message) { this.sender = sender; this.message = message; } @NotNull @Override - public Player getSender() { + public OnlineUser getSender() { return sender; } @@ -45,7 +44,7 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/event/ChatMessageEvent.java b/velocity/src/main/java/net/william278/huskchat/event/VelocityChatMessageEvent.java similarity index 77% rename from velocity/src/main/java/net/william278/huskchat/velocity/event/ChatMessageEvent.java rename to velocity/src/main/java/net/william278/huskchat/event/VelocityChatMessageEvent.java index 1aac80a3..cefe8c6c 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/event/ChatMessageEvent.java +++ b/velocity/src/main/java/net/william278/huskchat/event/VelocityChatMessageEvent.java @@ -17,18 +17,17 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; -public class ChatMessageEvent extends VelocityEvent implements IChatMessageEvent { - private Player sender; +public class VelocityChatMessageEvent extends VelocityEvent implements ChatMessageEvent { + private OnlineUser sender; private String message; private String channelId; - public ChatMessageEvent(Player sender, String message, String channelId) { + public VelocityChatMessageEvent(OnlineUser sender, String message, String channelId) { this.sender = sender; this.message = message; this.channelId = channelId; @@ -36,7 +35,7 @@ public ChatMessageEvent(Player sender, String message, String channelId) { @Override @NotNull - public Player getSender() { + public OnlineUser getSender() { return sender; } @@ -53,7 +52,7 @@ public String getChannelId() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEvent.java b/velocity/src/main/java/net/william278/huskchat/event/VelocityEvent.java similarity index 74% rename from velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEvent.java rename to velocity/src/main/java/net/william278/huskchat/event/VelocityEvent.java index fed59e68..1b4f987f 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEvent.java +++ b/velocity/src/main/java/net/william278/huskchat/event/VelocityEvent.java @@ -17,20 +17,15 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.EventBase; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter public class VelocityEvent implements EventBase { - private boolean cancelled = false; - @Override - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } + private boolean cancelled = false; - @Override - public boolean isCancelled() { - return cancelled; - } } diff --git a/velocity/src/main/java/net/william278/huskchat/event/VelocityEventProvider.java b/velocity/src/main/java/net/william278/huskchat/event/VelocityEventProvider.java new file mode 100644 index 00000000..6561244b --- /dev/null +++ b/velocity/src/main/java/net/william278/huskchat/event/VelocityEventProvider.java @@ -0,0 +1,54 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.event; + +import com.velocitypowered.api.proxy.ProxyServer; +import net.william278.huskchat.user.OnlineUser; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public interface VelocityEventProvider extends EventProvider { + + @Override + default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, + @NotNull String message, + @NotNull String channelId) { + return getProxyServer().getEventManager().fire(new VelocityChatMessageEvent(player, message, channelId)); + } + + @Override + default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, + @NotNull List receivers, + @NotNull String message) { + return getProxyServer().getEventManager().fire(new VelocityPrivateMessageEvent(sender, receivers, message)); + } + + @Override + default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, + @NotNull String message) { + return getProxyServer().getEventManager().fire(new VelocityBroadcastMessageEvent(sender, message)); + } + + @NotNull + ProxyServer getProxyServer(); + +} diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/event/PrivateMessageEvent.java b/velocity/src/main/java/net/william278/huskchat/event/VelocityPrivateMessageEvent.java similarity index 68% rename from velocity/src/main/java/net/william278/huskchat/velocity/event/PrivateMessageEvent.java rename to velocity/src/main/java/net/william278/huskchat/event/VelocityPrivateMessageEvent.java index ebd73748..81aa6596 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/event/PrivateMessageEvent.java +++ b/velocity/src/main/java/net/william278/huskchat/event/VelocityPrivateMessageEvent.java @@ -17,20 +17,19 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.event; +package net.william278.huskchat.event; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; +import net.william278.huskchat.user.OnlineUser; import org.jetbrains.annotations.NotNull; import java.util.List; -public class PrivateMessageEvent extends VelocityEvent implements IPrivateMessageEvent { - private Player sender; - private List recipients; +public class VelocityPrivateMessageEvent extends VelocityEvent implements PrivateMessageEvent { + private OnlineUser sender; + private List recipients; private String message; - public PrivateMessageEvent(@NotNull Player sender, @NotNull List recipients, @NotNull String message) { + public VelocityPrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List recipients, @NotNull String message) { this.sender = sender; this.recipients = recipients; this.message = message; @@ -38,13 +37,13 @@ public PrivateMessageEvent(@NotNull Player sender, @NotNull List recipie @Override @NotNull - public Player getSender() { + public OnlineUser getSender() { return sender; } @NotNull @Override - public List getRecipients() { + public List getRecipients() { return recipients; } @@ -55,12 +54,12 @@ public String getMessage() { } @Override - public void setSender(@NotNull Player sender) { + public void setSender(@NotNull OnlineUser sender) { this.sender = sender; } @Override - public void setRecipients(@NotNull List recipients) { + public void setRecipients(@NotNull List recipients) { this.recipients = recipients; } diff --git a/velocity/src/main/java/net/william278/huskchat/listener/VelocityChatListener.java b/velocity/src/main/java/net/william278/huskchat/listener/VelocityChatListener.java new file mode 100644 index 00000000..58d12f96 --- /dev/null +++ b/velocity/src/main/java/net/william278/huskchat/listener/VelocityChatListener.java @@ -0,0 +1,50 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.listener; + +import com.velocitypowered.api.event.player.PlayerChatEvent; +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.message.ChatMessage; +import net.william278.huskchat.user.VelocityUser; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public interface VelocityChatListener { + + default boolean handlePlayerChat(PlayerChatEvent e) { + final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin()); + final Optional channel = plugin().getChannels().getChannel( + plugin().getUserCache().getPlayerChannel(player.getUuid()) + ); + if (channel.isEmpty()) { + plugin().getLocales().sendMessage(player, "error_no_channel"); + return false; + } + + // Send the chat message, determine if the event should be canceled + return !new ChatMessage(channel.get(), player, e.getMessage(), plugin()).dispatch(); + } + + @NotNull + HuskChat plugin(); + +} diff --git a/velocity/src/main/java/net/william278/huskchat/listener/VelocityEventChatListener.java b/velocity/src/main/java/net/william278/huskchat/listener/VelocityEventChatListener.java new file mode 100644 index 00000000..290d92f8 --- /dev/null +++ b/velocity/src/main/java/net/william278/huskchat/listener/VelocityEventChatListener.java @@ -0,0 +1,46 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.listener; + +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.player.PlayerChatEvent; +import net.william278.huskchat.HuskChat; +import org.jetbrains.annotations.NotNull; + +public record VelocityEventChatListener(@NotNull HuskChat plugin) implements VelocityChatListener { + + @Subscribe(order = PostOrder.LATE) + public void onPlayerChat(PlayerChatEvent e) { + if (!e.getResult().isAllowed()) { + return; + } + if (!this.handlePlayerChat(e)) { + e.setResult(PlayerChatEvent.ChatResult.denied()); + } + } + + @Override + @NotNull + public HuskChat plugin() { + return plugin; + } + +} diff --git a/velocity/src/main/java/net/william278/huskchat/listener/VelocityPacketChatListener.java b/velocity/src/main/java/net/william278/huskchat/listener/VelocityPacketChatListener.java new file mode 100644 index 00000000..da7de492 --- /dev/null +++ b/velocity/src/main/java/net/william278/huskchat/listener/VelocityPacketChatListener.java @@ -0,0 +1,156 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * 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 + * + * http://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. + */ + +package net.william278.huskchat.listener; + +import com.google.common.collect.Sets; +import com.velocitypowered.api.event.AwaitingEventExecutor; +import com.velocitypowered.api.event.EventTask; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.event.player.PlayerChatEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.network.Connections; +import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket; +import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerChatPacket; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.william278.desertwell.util.ThrowingConsumer; +import net.william278.huskchat.HuskChat; +import net.william278.huskchat.VelocityHuskChat; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class VelocityPacketChatListener { + private static final String KEY = "huskchat"; + + private final VelocityHuskChat plugin; + @Getter + private final Set huskChatEntries; + + public VelocityPacketChatListener(@NotNull VelocityHuskChat plugin) { + this.plugin = plugin; + this.huskChatEntries = Sets.newConcurrentHashSet(); + } + + public void register() { + this.loadPlayers(); + this.loadListeners(); + } + + private void loadPlayers() { + plugin.getServer().getAllPlayers().forEach(this::injectPlayer); + } + + private void loadListeners() { + plugin.getServer().getEventManager().register(plugin, PostLoginEvent.class, + (AwaitingEventExecutor) postLoginEvent -> EventTask.withContinuation(continuation -> { + injectPlayer(postLoginEvent.getPlayer()); + continuation.resume(); + })); + + plugin.getServer().getEventManager().register(plugin, DisconnectEvent.class, + (AwaitingEventExecutor) disconnectEvent -> + disconnectEvent.getLoginStatus() == DisconnectEvent.LoginStatus.CONFLICTING_LOGIN + ? null + : EventTask.async(() -> removePlayer(disconnectEvent.getPlayer()))); + } + + public void injectPlayer(@NotNull Player player) { + final PlayerChannelHandler handler = new PlayerChannelHandler(plugin, player); + final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player; + removePlayer(player); + connectedPlayer.getConnection() + .getChannel() + .pipeline() + .addBefore(Connections.HANDLER, KEY, handler); + } + + public void removePlayer(@NotNull Player player) { + final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player; + final Channel channel = connectedPlayer.getConnection().getChannel(); + if (channel.pipeline().get(KEY) != null) { + channel.pipeline().remove(KEY); + } + } + + @RequiredArgsConstructor + public static class PlayerChannelHandler extends ChannelDuplexHandler implements VelocityChatListener { + + private final VelocityHuskChat plugin; + private final Player player; + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + final Optional message = this.extractChatMessage(msg); + if (message.isEmpty()) { + super.write(ctx, msg, promise); + return; + } + this.handleChat(message.get(), (passthrough) -> super.write(ctx, msg, promise)); + } + + @NotNull + private Optional extractChatMessage(Object msg) { + if (msg instanceof final SessionPlayerChatPacket session) { + // Handle session chat (1.19.4+) + return Optional.of(session.getMessage()); + } else if (msg instanceof final KeyedPlayerChatPacket keyed) { + // Handle keyed chat (1.19.2-4) + return Optional.of(keyed.getMessage()); + } else if (msg instanceof final LegacyChatPacket legacy) { + // Handle legacy chat (pre-1.19.1) + return Optional.of(legacy.getMessage()); + } + return Optional.empty(); + } + + private void handleChat(@NotNull String message, @NotNull ThrowingConsumer ifAllowed) { + this.dispatchEvent(message) + .thenApply(event -> event.getResult().isAllowed() && handlePlayerChat(event)) + .thenAccept(allowed -> { + if (allowed) { + ifAllowed.accept(null); + } + }); + } + + @NotNull + private CompletableFuture dispatchEvent(@NotNull String message) { + return plugin.getServer().getEventManager().fire(new PlayerChatEvent(player, message)); + } + + @Override + @NotNull + public HuskChat plugin() { + return plugin; + } + } + +} diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/listener/VelocityListener.java b/velocity/src/main/java/net/william278/huskchat/listener/VelocityPlayerListener.java similarity index 54% rename from velocity/src/main/java/net/william278/huskchat/velocity/listener/VelocityListener.java rename to velocity/src/main/java/net/william278/huskchat/listener/VelocityPlayerListener.java index 372291d9..ad6da287 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/listener/VelocityListener.java +++ b/velocity/src/main/java/net/william278/huskchat/listener/VelocityPlayerListener.java @@ -17,58 +17,37 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.listener; +package net.william278.huskchat.listener; -import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.PostLoginEvent; -import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; import net.william278.huskchat.HuskChat; -import net.william278.huskchat.listener.PlayerListener; -import net.william278.huskchat.message.ChatMessage; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.velocity.player.VelocityPlayer; +import net.william278.huskchat.user.VelocityUser; import org.jetbrains.annotations.NotNull; -public class VelocityListener extends PlayerListener { +public class VelocityPlayerListener extends PlayerListener { - public VelocityListener(@NotNull HuskChat plugin) { + public VelocityPlayerListener(@NotNull HuskChat plugin) { super(plugin); } - @Subscribe(order = PostOrder.LATE) - public void onPlayerChat(PlayerChatEvent e) { - if (!e.getResult().isAllowed()) { - return; - } - - final Player player = VelocityPlayer.adapt(e.getPlayer()); - final boolean shouldCancel = new ChatMessage(plugin.getPlayerCache().getPlayerChannel(player.getUuid()), - player, e.getMessage(), plugin) - .dispatch(); - - if (shouldCancel) { - e.setResult(PlayerChatEvent.ChatResult.denied()); - } - } - @Subscribe public void onPlayerChangeServer(ServerConnectedEvent e) { final String server = e.getServer().getServerInfo().getName(); - final VelocityPlayer player = VelocityPlayer.adapt(e.getPlayer()); + final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin); handlePlayerSwitchServer(player, server); } @Subscribe public void onPlayerJoinNetwork(PostLoginEvent e) { - handlePlayerJoin(VelocityPlayer.adapt(e.getPlayer())); + handlePlayerJoin(VelocityUser.adapt(e.getPlayer(), plugin)); } @Subscribe public void onPlayerQuitNetwork(DisconnectEvent e) { - handlePlayerQuit(VelocityPlayer.adapt(e.getPlayer())); + handlePlayerQuit(VelocityUser.adapt(e.getPlayer(), plugin)); } } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/player/VelocityPlayer.java b/velocity/src/main/java/net/william278/huskchat/user/VelocityUser.java similarity index 50% rename from velocity/src/main/java/net/william278/huskchat/velocity/player/VelocityPlayer.java rename to velocity/src/main/java/net/william278/huskchat/user/VelocityUser.java index b7e29a95..03aee3b9 100644 --- a/velocity/src/main/java/net/william278/huskchat/velocity/player/VelocityPlayer.java +++ b/velocity/src/main/java/net/william278/huskchat/user/VelocityUser.java @@ -17,39 +17,31 @@ * limitations under the License. */ -package net.william278.huskchat.velocity.player; +package net.william278.huskchat.user; +import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; import net.kyori.adventure.audience.Audience; -import net.william278.huskchat.player.Player; -import net.william278.huskchat.velocity.VelocityHuskChat; +import net.william278.huskchat.HuskChat; import org.jetbrains.annotations.NotNull; import java.util.Optional; -import java.util.UUID; /** - * Velocity implementation of a cross-platform {@link Player} + * Velocity implementation of a cross-platform {@link OnlineUser} */ -public class VelocityPlayer implements Player { +public class VelocityUser extends OnlineUser { - private static final VelocityHuskChat plugin = VelocityHuskChat.getInstance(); private final com.velocitypowered.api.proxy.Player player; - private VelocityPlayer(com.velocitypowered.api.proxy.Player player) { + private VelocityUser(@NotNull Player player, @NotNull HuskChat plugin) { + super(player.getUsername(), player.getUniqueId(), plugin); this.player = player; } - @Override - @NotNull - public String getName() { - return player.getUsername(); - } - @NotNull - @Override - public UUID getUuid() { - return player.getUniqueId(); + public static VelocityUser adapt(@NotNull Player player, @NotNull HuskChat plugin) { + return new VelocityUser(player, plugin); } @Override @@ -69,11 +61,7 @@ public String getServerName() { @Override public int getPlayersOnServer() { - final Optional connection = player.getCurrentServer(); - if (connection.isPresent()) { - return connection.get().getServer().getPlayersConnected().size(); - } - return 0; + return player.getCurrentServer().map(conn -> conn.getServer().getPlayersConnected().size()).orElse(0); } @Override @@ -87,24 +75,9 @@ public Audience getAudience() { return player; } - /** - * Adapts a cross-platform {@link Player} to a Velocity {@link com.velocitypowered.api.proxy.Player} object - * - * @param player {@link Player} to adapt - * @return The {@link com.velocitypowered.api.proxy.Player} object, {@code null} if they are offline - */ - public static Optional toVelocity(@NotNull Player player) { - return plugin.getProxyServer().getPlayer(player.getUuid()); - } - - /** - * Adapts a Velocity {@link com.velocitypowered.api.proxy.Player} to a cross-platform {@link Player} object - * - * @param player {@link com.velocitypowered.api.proxy.Player} to adapt - * @return The {@link Player} object - */ @NotNull - public static VelocityPlayer adapt(com.velocitypowered.api.proxy.Player player) { - return new VelocityPlayer(player); + public Player getPlayer() { + return player; } + } diff --git a/velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEventDispatcher.java b/velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEventDispatcher.java deleted file mode 100644 index d42a15fd..00000000 --- a/velocity/src/main/java/net/william278/huskchat/velocity/event/VelocityEventDispatcher.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of HuskChat, licensed under the Apache License 2.0. - * - * Copyright (c) William278 - * Copyright (c) contributors - * - * 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 - * - * http://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. - */ - -package net.william278.huskchat.velocity.event; - -import com.velocitypowered.api.proxy.ProxyServer; -import net.william278.huskchat.event.EventDispatcher; -import net.william278.huskchat.event.IBroadcastMessageEvent; -import net.william278.huskchat.event.IChatMessageEvent; -import net.william278.huskchat.event.IPrivateMessageEvent; -import net.william278.huskchat.player.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class VelocityEventDispatcher implements EventDispatcher { - private final ProxyServer server; - - public VelocityEventDispatcher(ProxyServer server) { - this.server = server; - } - - @Override - public CompletableFuture dispatchChatMessageEvent(@NotNull Player player, @NotNull String message, @NotNull String channelId) { - return server.getEventManager().fire(new ChatMessageEvent(player, message, channelId)); - } - - @Override - public CompletableFuture dispatchPrivateMessageEvent(@NotNull Player sender, @NotNull List receivers, @NotNull String message) { - return server.getEventManager().fire(new PrivateMessageEvent(sender, receivers, message)); - } - - @Override - public CompletableFuture dispatchBroadcastMessageEvent(@NotNull Player sender, @NotNull String message) { - return server.getEventManager().fire(new BroadcastMessageEvent(sender, message)); - } -} diff --git a/velocity/src/main/resources/velocity-metadata.yml b/velocity/src/main/resources/velocity-metadata.yml new file mode 100644 index 00000000..9fa88f5f --- /dev/null +++ b/velocity/src/main/resources/velocity-metadata.yml @@ -0,0 +1,2 @@ +velocity_api_version: '${velocity_api_version}' +velocity_minimum_build: ${velocity_minimum_build} \ No newline at end of file diff --git a/velocity/src/main/resources/velocity-plugin.json b/velocity/src/main/resources/velocity-plugin.json index 6bce3b59..33fd9e69 100644 --- a/velocity/src/main/resources/velocity-plugin.json +++ b/velocity/src/main/resources/velocity-plugin.json @@ -29,5 +29,5 @@ "optional": true } ], - "main": "net.william278.huskchat.velocity.VelocityHuskChat" + "main": "net.william278.huskchat.VelocityHuskChat" } \ No newline at end of file