├── .github ├── dependabot.yml ├── funding.yml └── workflows │ ├── ci.yml │ ├── pr_tests.yml │ ├── release.yml │ └── update_docs.yml ├── .gitignore ├── HEADER ├── LICENSE ├── README.md ├── build.gradle ├── bukkit ├── build.gradle └── src │ └── main │ ├── java │ └── net │ │ └── william278 │ │ └── huskchat │ │ ├── BukkitHuskChat.java │ │ ├── api │ │ └── BukkitHuskChatAPI.java │ │ ├── command │ │ └── BukkitCommand.java │ │ ├── event │ │ ├── BukkitBroadcastMessageEvent.java │ │ ├── BukkitChatMessageEvent.java │ │ ├── BukkitEvent.java │ │ ├── BukkitEventProvider.java │ │ └── BukkitPrivateMessageEvent.java │ │ ├── listener │ │ └── BukkitListener.java │ │ ├── placeholders │ │ └── BukkitPlaceholderAPIReplacer.java │ │ └── user │ │ └── BukkitUser.java │ └── resources │ └── plugin.yml ├── bungee ├── build.gradle └── src │ └── main │ ├── java │ └── net │ │ └── william278 │ │ └── huskchat │ │ ├── BungeeHuskChat.java │ │ ├── api │ │ └── BungeeHuskChatAPI.java │ │ ├── command │ │ └── BungeeCommand.java │ │ ├── event │ │ ├── BungeeBroadcastMessageEvent.java │ │ ├── BungeeChatMessageEvent.java │ │ ├── BungeeEvent.java │ │ ├── BungeeEventProvider.java │ │ └── BungeePrivateMessageEvent.java │ │ ├── getter │ │ └── BungeePermsDataGetter.java │ │ ├── listener │ │ └── BungeeListener.java │ │ └── user │ │ └── BungeeUser.java │ └── resources │ └── bungee.yml ├── common ├── build.gradle └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── william278 │ │ │ └── huskchat │ │ │ ├── HuskChat.java │ │ │ ├── api │ │ │ └── HuskChatAPI.java │ │ │ ├── channel │ │ │ └── Channel.java │ │ │ ├── command │ │ │ ├── BroadcastCommand.java │ │ │ ├── ChannelCommand.java │ │ │ ├── CommandBase.java │ │ │ ├── HuskChatCommand.java │ │ │ ├── LocalSpyCommand.java │ │ │ ├── MessageCommand.java │ │ │ ├── OptOutMessageCommand.java │ │ │ ├── ReplyCommand.java │ │ │ ├── ShortcutCommand.java │ │ │ └── SocialSpyCommand.java │ │ │ ├── config │ │ │ ├── Channels.java │ │ │ ├── ConfigProvider.java │ │ │ ├── Filters.java │ │ │ ├── Locales.java │ │ │ └── Settings.java │ │ │ ├── discord │ │ │ ├── DiscordHook.java │ │ │ ├── SpicordHook.java │ │ │ └── WebHook.java │ │ │ ├── event │ │ │ ├── BroadcastMessageEvent.java │ │ │ ├── ChatMessageEvent.java │ │ │ ├── EventBase.java │ │ │ ├── EventProvider.java │ │ │ └── PrivateMessageEvent.java │ │ │ ├── filter │ │ │ ├── AdvertisingFilterer.java │ │ │ ├── AsciiFilter.java │ │ │ ├── CapsFilter.java │ │ │ ├── ChatFilter.java │ │ │ ├── EmojiReplacer.java │ │ │ ├── FilterProvider.java │ │ │ ├── ProfanityFilterer.java │ │ │ ├── RegexFilter.java │ │ │ ├── RepeatFilter.java │ │ │ └── SpamFilter.java │ │ │ ├── getter │ │ │ ├── DataGetter.java │ │ │ ├── DefaultDataGetter.java │ │ │ └── LuckPermsDataGetter.java │ │ │ ├── listener │ │ │ └── PlayerListener.java │ │ │ ├── message │ │ │ ├── BroadcastMessage.java │ │ │ ├── ChatMessage.java │ │ │ └── PrivateMessage.java │ │ │ ├── placeholders │ │ │ ├── DefaultReplacer.java │ │ │ ├── PAPIProxyBridgeReplacer.java │ │ │ └── PlaceholderReplacer.java │ │ │ ├── user │ │ │ ├── ConsoleUser.java │ │ │ ├── OnlineUser.java │ │ │ ├── User.java │ │ │ └── UserCache.java │ │ │ └── util │ │ │ └── AudiencesProvider.java │ └── resources │ │ ├── discord │ │ ├── embedded_message.json │ │ └── inline_message.json │ │ └── locales │ │ ├── bg-bg.yml │ │ ├── de-de.yml │ │ ├── en-gb.yml │ │ ├── es-mx.yml │ │ ├── fr-fr.yml │ │ ├── hu-hu.yml │ │ ├── id-id.yml │ │ ├── it-it.yml │ │ ├── pt-br.yml │ │ ├── ru-ru.yml │ │ ├── zh-cn.yml │ │ └── zh-tw.yml │ └── test │ └── java │ └── net │ └── william278 │ └── huskchat │ ├── channel │ └── ChannelTests.java │ ├── filter │ ├── AdvertisingFilterTests.java │ ├── AsciiFilterTests.java │ ├── CapsFilterTests.java │ └── ProfanityFilterTests.java │ └── user │ └── TestOnlineUser.java ├── docs ├── Backend-Chat-Entry.md ├── Channels.md ├── Commands.md ├── Config-Files.md ├── Discord-Hook.md ├── Filters-and-Replacers.md ├── Formatting.md ├── Group-Messages.md ├── Home.md ├── Join-and-Quit-Messages.md ├── Setup.md ├── Social-and-Local-Spy.md ├── Translations.md ├── _Footer.md └── _Sidebar.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── banner.png └── spicord-bot-intents.png ├── paper ├── build.gradle └── src │ └── main │ ├── java │ └── net │ │ └── william278 │ │ └── huskchat │ │ └── PaperHuskChat.java │ └── resources │ └── paper-plugin.yml ├── plugin └── build.gradle ├── settings.gradle └── velocity ├── build.gradle └── src └── main ├── java └── net │ └── william278 │ └── huskchat │ ├── VelocityHuskChat.java │ ├── api │ └── VelocityHuskChatAPI.java │ ├── command │ └── VelocityCommand.java │ ├── event │ ├── VelocityBroadcastMessageEvent.java │ ├── VelocityChatMessageEvent.java │ ├── VelocityEvent.java │ ├── VelocityEventProvider.java │ └── VelocityPrivateMessageEvent.java │ ├── listener │ ├── VelocityChatListener.java │ ├── VelocityEventChatListener.java │ ├── VelocityPacketChatListener.java │ └── VelocityPlayerListener.java │ └── user │ └── VelocityUser.java └── resources ├── velocity-metadata.yml └── velocity-plugin.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration file for GitHub 2 | 3 | version: 2 4 | updates: 5 | # CI workflow action updates 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | commit-message: 11 | prefix: "ci" 12 | 13 | # Gradle package updates 14 | - package-ecosystem: "gradle" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | commit-message: 19 | prefix: "deps" 20 | ignore: 21 | - dependency-name: 'org.spigotmc:spigot-api' 22 | - dependency-name: 'org.papermc:paper-api' 23 | - dependency-name: 'net.md-5:bungeecord-api' 24 | - dependency-name: 'net.dv8tion:JDA' -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # Funding metadata for GitHub 2 | 3 | github: WiIIiam278 4 | custom: https://buymeacoff.ee/william278 -------------------------------------------------------------------------------- /.github/workflows/pr_tests.yml: -------------------------------------------------------------------------------- 1 | # Carry out tests on pull requests 2 | name: PR Tests 3 | 4 | on: 5 | pull_request: 6 | branches: [ 'master' ] 7 | 8 | permissions: 9 | contents: read 10 | 11 | env: 12 | LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.10.8/x64/lib/python3.10/site-packages/jep/ 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up JDK 17 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: '17' 23 | distribution: 'temurin' 24 | - name: Install Python 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: '3.10.8' 28 | - name: Install Python dependencies 29 | shell: bash 30 | run: | 31 | python -m pip install --upgrade pip setuptools wheel 32 | python -m pip install jep 33 | python -m pip install alt-profanity-check 34 | - name: Test Pull Request 35 | uses: gradle/gradle-build-action@v3 36 | with: 37 | arguments: build test -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Publishes a release to Modrinth and CurseForge when a release is published on GitHub. 2 | name: Release Test & Publish 3 | 4 | on: 5 | release: 6 | types: [ published ] 7 | 8 | permissions: 9 | contents: read 10 | checks: write 11 | 12 | env: 13 | LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.10.8/x64/lib/python3.10/site-packages/jep/ 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: 'Checkout for CI 🛎️' 20 | uses: actions/checkout@v4 21 | - name: 'Set up JDK 17 📦' 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: '17' 25 | distribution: 'temurin' 26 | - name: 'Install Python 📦' 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.10.8' 30 | - name: Install Python dependencies 31 | shell: bash 32 | run: | 33 | python -m pip install --upgrade pip setuptools wheel 34 | python -m pip install jep 35 | python -m pip install alt-profanity-check 36 | - name: 'Build with Gradle 🏗️' 37 | uses: gradle/gradle-build-action@v3 38 | with: 39 | arguments: build test publish 40 | env: 41 | RELEASES_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 42 | RELEASES_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 43 | - name: 'Publish Test Report 📊' 44 | uses: mikepenz/action-junit-report@v4 45 | if: success() || failure() # always run even if the previous step fails 46 | with: 47 | report_paths: '**/build/test-results/test/TEST-*.xml' 48 | - name: 'Publish to William278.net 🚀' 49 | uses: WiIIiam278/bones-publish-action@v1 50 | with: 51 | api-key: ${{ secrets.BONES_API_KEY }} 52 | project: 'huskchat' 53 | channel: 'release' 54 | version: ${{ github.event.release.tag_name }} 55 | changelog: ${{ github.event.release.body }} 56 | distro-names: | 57 | bungee-velocity-paper 58 | distro-groups: | 59 | bungee-velocity-paper 60 | distro-descriptions: | 61 | Bungee, Velocity & Paper 62 | files: | 63 | target/HuskChat-Plugin-${{ github.event.release.tag_name }}.jar 64 | - name: 'Publish to Modrinth & Hangar 🚰' 65 | uses: WiIIiam278/mc-publish@hangar 66 | with: 67 | modrinth-id: SSXTD3me 68 | modrinth-featured: false 69 | modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 70 | hangar-id: William278/HuskChat 71 | hangar-token: ${{ secrets.HANGAR_API_KEY }} 72 | files-primary: target/HuskChat-Plugin-${{ github.event.release.tag_name }}.jar 73 | name: HuskChat v${{ github.event.release.tag_name }} 74 | version: ${{ github.event.release.tag_name }} 75 | changelog: ${{ github.event.release.body }} 76 | hangar-version-type: Release 77 | modrinth-version-type: release 78 | game-versions: | 79 | 1.17.1 80 | 1.18 81 | 1.18.1 82 | 1.18.2 83 | 1.19 84 | 1.19.1 85 | 1.19.2 86 | 1.19.3 87 | 1.19.4 88 | 1.20 89 | 1.20.1 90 | 1.20.2 91 | 1.20.3 92 | 1.20.4 93 | modrinth-dependencies: | 94 | luckperms | suggests | * 95 | papiproxybridge | suggests | * 96 | placeholderapi | suggests | * 97 | modrinth-loaders: | 98 | bungeecord 99 | velocity 100 | spigot 101 | paper 102 | folia 103 | hangar-loaders: | 104 | velocity 105 | java: 17 -------------------------------------------------------------------------------- /.github/workflows/update_docs.yml: -------------------------------------------------------------------------------- 1 | # Update the GitHub Wiki documentation when a push is made to docs/ 2 | name: Update Docs 3 | 4 | on: 5 | push: 6 | branches: [ 'master' ] 7 | paths: 8 | - 'docs/**' 9 | - 'workflows/**' 10 | tags-ignore: 11 | - '*' 12 | 13 | permissions: 14 | contents: write 15 | 16 | jobs: 17 | deploy-wiki: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: 'Checkout for CI 🛎️' 21 | uses: actions/checkout@v4 22 | - name: 'Push Docs to Github Wiki 📄️' 23 | uses: Andrew-Chen-Wang/github-wiki-action@v4 24 | with: 25 | path: 'docs' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | run/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### IntelliJ IDEA ### 9 | .idea/modules.xml 10 | .idea/jarRepositories.xml 11 | .idea/compiler.xml 12 | .idea/libraries/ 13 | *.iws 14 | *.iml 15 | *.ipr 16 | out/ 17 | !**/src/main/**/out/ 18 | !**/src/test/**/out/ 19 | /.idea/ 20 | /target/ 21 | 22 | ### Eclipse ### 23 | .apt_generated 24 | .classpath 25 | .factorypath 26 | .project 27 | .settings 28 | .springBeans 29 | .sts4-cache 30 | bin/ 31 | !**/src/main/**/bin/ 32 | !**/src/test/**/bin/ 33 | 34 | ### NetBeans ### 35 | /nbproject/private/ 36 | /nbbuild/ 37 | /dist/ 38 | /nbdist/ 39 | /.nb-gradle/ 40 | 41 | ### VS Code ### 42 | .vscode/ 43 | 44 | ### Mac OS ### 45 | .DS_Store -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | This file is part of HuskChat, licensed under the Apache License 2.0. 2 | 3 | Copyright (c) William278 4 | Copyright (c) contributors 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. -------------------------------------------------------------------------------- /bukkit/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(path: ':common') 3 | 4 | implementation 'org.bstats:bstats-bukkit:3.0.2' 5 | implementation 'net.kyori:adventure-platform-bukkit:4.3.4' 6 | implementation 'space.arim.morepaperlib:morepaperlib:0.4.4' 7 | 8 | compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT' 9 | compileOnly 'commons-io:commons-io:2.16.1' 10 | compileOnly 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT' 11 | compileOnly 'me.clip:placeholderapi:2.11.6' 12 | compileOnly 'org.jetbrains:annotations:24.1.0' 13 | compileOnly 'org.projectlombok:lombok:1.18.34' 14 | 15 | annotationProcessor 'org.projectlombok:lombok:1.18.34' 16 | } 17 | 18 | shadowJar { 19 | relocate 'net.william278.profanitycheckerapi', 'net.william278.huskchat.libraries.profanitycheckerapi' 20 | relocate 'net.william278.desertwell', 'net.william278.huskchat.libraries.desertwell' 21 | relocate 'de.themoep', 'net.william278.huskchat.libraries' 22 | relocate 'dev.vankka', 'net.william278.huskchat.libraries' 23 | relocate 'de.exlll', 'net.william278.huskchat.libraries' 24 | relocate 'org.snakeyaml.engine', 'net.william278.huskchat.libraries.snakeyaml.engine' 25 | relocate 'org.json', 'net.william278.huskchat.libraries.json' 26 | 27 | relocate 'org.apache', 'net.william278.huskchat.libraries' 28 | relocate 'org.jetbrains', 'net.william278.huskchat.libraries' 29 | relocate 'org.intellij', 'net.william278.huskchat.libraries' 30 | relocate 'org.bstats', 'net.william278.huskchat.libraries.bstats' 31 | relocate 'space.arim', 'net.william278.huskchat.libraries.morepaperlib' 32 | 33 | dependencies { 34 | //noinspection GroovyAssignabilityCheck 35 | exclude dependency(':slf4j-api') 36 | } 37 | 38 | minimize() 39 | } -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/api/BukkitHuskChatAPI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.api; 21 | 22 | import net.william278.huskchat.BukkitHuskChat; 23 | import net.william278.huskchat.HuskChat; 24 | import net.william278.huskchat.user.BukkitUser; 25 | import org.bukkit.entity.Player; 26 | import org.jetbrains.annotations.ApiStatus; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | @SuppressWarnings("unused") 30 | public class BukkitHuskChatAPI extends HuskChatAPI { 31 | 32 | private BukkitHuskChatAPI(@NotNull HuskChat plugin) { 33 | super(plugin); 34 | } 35 | 36 | @NotNull 37 | public static BukkitHuskChatAPI getInstance() { 38 | return (BukkitHuskChatAPI) instance; 39 | } 40 | 41 | /** 42 | * @hidden 43 | */ 44 | @ApiStatus.Internal 45 | public static void register(@NotNull BukkitHuskChat plugin) { 46 | HuskChatAPI.instance = new BukkitHuskChatAPI(plugin); 47 | } 48 | 49 | /** 50 | * Adapts a platform-specific Player object to a cross-platform Player object 51 | * 52 | * @param player Must be a platform-specific Player object, e.g. a Velocity Player 53 | * @return {@link BukkitUser} 54 | */ 55 | @NotNull 56 | public BukkitUser adaptPlayer(@NotNull Player player) { 57 | return BukkitUser.adapt(player, plugin); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/event/BukkitBroadcastMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.bukkit.event.HandlerList; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | public class BukkitBroadcastMessageEvent extends BukkitEvent implements BroadcastMessageEvent { 27 | 28 | private static final HandlerList HANDLERS = new HandlerList(); 29 | private String message; 30 | 31 | public BukkitBroadcastMessageEvent(@NotNull OnlineUser player, @NotNull String message) { 32 | super(player); 33 | this.message = message; 34 | } 35 | 36 | @NotNull 37 | public static HandlerList getHandlerList() { 38 | return HANDLERS; 39 | } 40 | 41 | @NotNull 42 | @Override 43 | public HandlerList getHandlers() { 44 | return getHandlerList(); 45 | } 46 | 47 | @NotNull 48 | @Override 49 | public OnlineUser getSender() { 50 | return player; 51 | } 52 | 53 | @NotNull 54 | @Override 55 | public String getMessage() { 56 | return message; 57 | } 58 | 59 | @Override 60 | public void setSender(@NotNull OnlineUser sender) { 61 | this.player = sender; 62 | } 63 | 64 | @Override 65 | public void setMessage(@NotNull String message) { 66 | this.message = message; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/event/BukkitChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.bukkit.event.HandlerList; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | public class BukkitChatMessageEvent extends BukkitEvent implements ChatMessageEvent { 27 | 28 | private static final HandlerList HANDLERS = new HandlerList(); 29 | private String message; 30 | private String channelId; 31 | 32 | protected BukkitChatMessageEvent(@NotNull OnlineUser player, 33 | @NotNull String message, 34 | @NotNull String channelId) { 35 | super(player); 36 | this.message = message; 37 | this.channelId = channelId; 38 | } 39 | 40 | @NotNull 41 | public static HandlerList getHandlerList() { 42 | return HANDLERS; 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public HandlerList getHandlers() { 48 | return getHandlerList(); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public OnlineUser getSender() { 54 | return player; 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public String getMessage() { 60 | return message; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public String getChannelId() { 66 | return channelId; 67 | } 68 | 69 | @Override 70 | public void setSender(@NotNull OnlineUser sender) { 71 | this.player = sender; 72 | } 73 | 74 | @Override 75 | public void setMessage(@NotNull String message) { 76 | this.message = message; 77 | } 78 | 79 | @Override 80 | public void setChannelId(@NotNull String channelId) { 81 | this.channelId = channelId; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/event/BukkitEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.bukkit.event.Cancellable; 24 | import org.bukkit.event.Event; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | public abstract class BukkitEvent extends Event implements Cancellable { 28 | 29 | private boolean cancelled; 30 | protected OnlineUser player; 31 | 32 | protected BukkitEvent(@NotNull OnlineUser player) { 33 | this.player = player; 34 | } 35 | 36 | @Override 37 | public boolean isCancelled() { 38 | return cancelled; 39 | } 40 | 41 | @Override 42 | public void setCancelled(boolean cancel) { 43 | this.cancelled = cancel; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/event/BukkitEventProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.BukkitHuskChat; 23 | import net.william278.huskchat.user.OnlineUser; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public interface BukkitEventProvider extends EventProvider { 30 | 31 | @Override 32 | default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, 33 | @NotNull String message, 34 | @NotNull String channelId) { 35 | final CompletableFuture completableFuture = new CompletableFuture<>(); 36 | final BukkitChatMessageEvent event = new BukkitChatMessageEvent(player, message, channelId); 37 | getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { 38 | getPlugin().getServer().getPluginManager().callEvent(event); 39 | completableFuture.complete(event); 40 | }); 41 | return completableFuture; 42 | } 43 | 44 | @Override 45 | default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, 46 | @NotNull List receivers, 47 | @NotNull String message) { 48 | final CompletableFuture completableFuture = new CompletableFuture<>(); 49 | final BukkitPrivateMessageEvent event = new BukkitPrivateMessageEvent(sender, receivers, message); 50 | getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { 51 | getPlugin().getServer().getPluginManager().callEvent(event); 52 | completableFuture.complete(event); 53 | }); 54 | return completableFuture; 55 | } 56 | 57 | @Override 58 | default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, 59 | @NotNull String message) { 60 | final CompletableFuture completableFuture = new CompletableFuture<>(); 61 | final BukkitBroadcastMessageEvent event = new BukkitBroadcastMessageEvent(sender, message); 62 | getPlugin().getServer().getScheduler().runTask(getPlugin(), () -> { 63 | getPlugin().getServer().getPluginManager().callEvent(event); 64 | completableFuture.complete(event); 65 | }); 66 | return completableFuture; 67 | } 68 | 69 | BukkitHuskChat getPlugin(); 70 | 71 | } 72 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/event/BukkitPrivateMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.bukkit.event.HandlerList; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | 28 | public class BukkitPrivateMessageEvent extends BukkitEvent implements PrivateMessageEvent { 29 | 30 | private static final HandlerList HANDLERS = new HandlerList(); 31 | private List recipients; 32 | private String message; 33 | 34 | public BukkitPrivateMessageEvent(@NotNull OnlineUser player, @NotNull List recipients, @NotNull String message) { 35 | super(player); 36 | this.recipients = recipients; 37 | this.message = message; 38 | } 39 | 40 | @NotNull 41 | public static HandlerList getHandlerList() { 42 | return HANDLERS; 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public HandlerList getHandlers() { 48 | return getHandlerList(); 49 | } 50 | 51 | @Override 52 | @NotNull 53 | public OnlineUser getSender() { 54 | return player; 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public List getRecipients() { 60 | return recipients; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public String getMessage() { 66 | return message; 67 | } 68 | 69 | @Override 70 | public void setSender(@NotNull OnlineUser sender) { 71 | this.player = sender; 72 | } 73 | 74 | @Override 75 | public void setRecipients(@NotNull List recipients) { 76 | this.recipients = recipients; 77 | } 78 | 79 | @Override 80 | public void setMessage(@NotNull String message) { 81 | this.message = message; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/listener/BukkitListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.channel.Channel; 24 | import net.william278.huskchat.message.ChatMessage; 25 | import net.william278.huskchat.user.BukkitUser; 26 | import org.bukkit.event.EventHandler; 27 | import org.bukkit.event.EventPriority; 28 | import org.bukkit.event.Listener; 29 | import org.bukkit.event.player.AsyncPlayerChatEvent; 30 | import org.bukkit.event.player.PlayerJoinEvent; 31 | import org.bukkit.event.player.PlayerQuitEvent; 32 | import org.jetbrains.annotations.NotNull; 33 | 34 | import java.util.Optional; 35 | 36 | public class BukkitListener extends PlayerListener implements Listener { 37 | 38 | public BukkitListener(@NotNull HuskChat plugin) { 39 | super(plugin); 40 | } 41 | 42 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) 43 | public void onPlayerChat(AsyncPlayerChatEvent e) { 44 | // Verify they are in a channel 45 | final BukkitUser player = BukkitUser.adapt(e.getPlayer(), plugin); 46 | final Optional channel = plugin.getUserCache().getPlayerChannel(player.getUuid()) 47 | .flatMap(channelId -> plugin.getChannels().getChannel(channelId)); 48 | if (channel.isEmpty()) { 49 | plugin.getLocales().sendMessage(player, "error_no_channel"); 50 | return; 51 | } 52 | 53 | // Send the chat message, determine if the event should be canceled 54 | if (new ChatMessage(channel.get(), player, e.getMessage(), plugin).dispatch()) { 55 | e.setCancelled(true); 56 | } 57 | } 58 | 59 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) 60 | public void onPlayerJoin(PlayerJoinEvent e) { 61 | final BukkitUser player = BukkitUser.adapt(e.getPlayer(), plugin); 62 | super.handlePlayerSwitchServer(player, player.getServerName()); 63 | if (plugin.getSettings().getJoinAndQuitMessages().getJoin().isEnabled() 64 | || !plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope().isPassThrough()) { 65 | e.setJoinMessage(null); 66 | } 67 | super.handlePlayerJoin(player); 68 | } 69 | 70 | @EventHandler 71 | public void onPlayerQuit(PlayerQuitEvent e) { 72 | if (plugin.getSettings().getJoinAndQuitMessages().getQuit().isEnabled() 73 | || !plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope().isPassThrough()) { 74 | e.setQuitMessage(null); 75 | } 76 | super.handlePlayerQuit(BukkitUser.adapt(e.getPlayer(), plugin)); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/placeholders/BukkitPlaceholderAPIReplacer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.placeholders; 21 | 22 | import me.clip.placeholderapi.PlaceholderAPI; 23 | import net.william278.huskchat.user.BukkitUser; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public class BukkitPlaceholderAPIReplacer implements PlaceholderReplacer { 30 | 31 | @Override 32 | public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player) { 33 | return CompletableFuture.completedFuture(PlaceholderAPI.setPlaceholders( 34 | ((BukkitUser) player).getPlayer(), 35 | message 36 | )); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /bukkit/src/main/java/net/william278/huskchat/user/BukkitUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import org.bukkit.entity.Player; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | 27 | public class BukkitUser extends OnlineUser { 28 | private final Player player; 29 | 30 | private BukkitUser(@NotNull Player player, @NotNull HuskChat plugin) { 31 | super(player.getName(), player.getUniqueId(), plugin); 32 | this.player = player; 33 | } 34 | 35 | @NotNull 36 | public static BukkitUser adapt(@NotNull Player player, @NotNull HuskChat plugin) { 37 | return new BukkitUser(player, plugin); 38 | } 39 | 40 | @Override 41 | public int getPing() { 42 | return player.getPing(); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public String getServerName() { 48 | return "server"; 49 | } 50 | 51 | @Override 52 | public int getPlayersOnServer() { 53 | return player.getServer().getOnlinePlayers().size(); 54 | } 55 | 56 | @Override 57 | public boolean hasPermission(@Nullable String node, boolean allowByDefault) { 58 | if (node != null && player.isPermissionSet(node)) { 59 | return player.hasPermission(node); 60 | } else { 61 | return allowByDefault || player.isOp(); 62 | } 63 | } 64 | 65 | @NotNull 66 | public Player getPlayer() { 67 | return player; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /bukkit/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: 'HuskChat' 2 | version: '${version}' 3 | description: '${description}' 4 | author: 'William278' 5 | main: 'net.william278.huskchat.BukkitHuskChat' 6 | api-version: 1.17 7 | folia-supported: true 8 | softdepend: 9 | - 'LuckPerms' 10 | - 'PlaceholderAPI' 11 | - 'Spicord' -------------------------------------------------------------------------------- /bungee/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(path: ':common') 3 | 4 | implementation 'org.bstats:bstats-bungeecord:3.0.2' 5 | implementation 'net.kyori:adventure-platform-bungeecord:4.3.2' 6 | 7 | compileOnly 'net.md-5:bungeecord-api:1.20-R0.1-SNAPSHOT' 8 | compileOnly 'commons-io:commons-io:2.16.1' 9 | compileOnly 'net.alpenblock:BungeePerms:4.0-dev-143' 10 | compileOnly 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT' 11 | compileOnly 'org.jetbrains:annotations:24.1.0' 12 | compileOnly 'org.projectlombok:lombok:1.18.34' 13 | 14 | annotationProcessor 'org.projectlombok:lombok:1.18.34' 15 | } 16 | 17 | shadowJar { 18 | relocate 'net.william278.profanitycheckerapi', 'net.william278.huskchat.libraries.profanitycheckerapi' 19 | relocate 'net.william278.desertwell', 'net.william278.huskchat.libraries.desertwell' 20 | relocate 'de.themoep', 'net.william278.huskchat.libraries' 21 | relocate 'dev.vankka', 'net.william278.huskchat.libraries' 22 | relocate 'de.exlll', 'net.william278.huskchat.libraries' 23 | relocate 'org.snakeyaml.engine', 'net.william278.huskchat.libraries.snakeyaml.engine' 24 | relocate 'org.json', 'net.william278.huskchat.libraries.json' 25 | 26 | relocate 'org.apache', 'net.william278.huskchat.libraries' 27 | relocate 'org.jetbrains', 'net.william278.huskchat.libraries' 28 | relocate 'org.intellij', 'net.william278.huskchat.libraries' 29 | relocate 'org.bstats', 'net.william278.huskchat.libraries.bstats' 30 | 31 | dependencies { 32 | //noinspection GroovyAssignabilityCheck 33 | exclude dependency(':slf4j-api') 34 | } 35 | 36 | minimize() 37 | } -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/api/BungeeHuskChatAPI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.api; 21 | 22 | import net.md_5.bungee.api.connection.ProxiedPlayer; 23 | import net.william278.huskchat.BungeeHuskChat; 24 | import net.william278.huskchat.HuskChat; 25 | import net.william278.huskchat.user.BungeeUser; 26 | import org.jetbrains.annotations.ApiStatus; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | @SuppressWarnings("unused") 30 | public class BungeeHuskChatAPI extends HuskChatAPI { 31 | 32 | private BungeeHuskChatAPI(@NotNull HuskChat plugin) { 33 | super(plugin); 34 | } 35 | 36 | @NotNull 37 | public static BungeeHuskChatAPI getInstance() { 38 | return (BungeeHuskChatAPI) instance; 39 | } 40 | 41 | /** 42 | * @hidden 43 | */ 44 | @ApiStatus.Internal 45 | public static void register(@NotNull BungeeHuskChat plugin) { 46 | HuskChatAPI.instance = new BungeeHuskChatAPI(plugin); 47 | } 48 | 49 | /** 50 | * Adapts a platform-specific Player object to a cross-platform Player object 51 | * 52 | * @param player Must be a platform-specific Player object, e.g. a Velocity Player 53 | * @return {@link BungeeUser} 54 | */ 55 | @NotNull 56 | public BungeeUser adaptPlayer(@NotNull ProxiedPlayer player) { 57 | return BungeeUser.adapt(player, plugin); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/event/BungeeBroadcastMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public class BungeeBroadcastMessageEvent extends BungeeEvent implements BroadcastMessageEvent { 26 | private OnlineUser sender; 27 | private String message; 28 | 29 | public BungeeBroadcastMessageEvent(OnlineUser sender, String message) { 30 | this.sender = sender; 31 | this.message = message; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public OnlineUser getSender() { 37 | return sender; 38 | } 39 | 40 | @Override 41 | @NotNull 42 | public String getMessage() { 43 | return message; 44 | } 45 | 46 | @Override 47 | public void setSender(@NotNull OnlineUser sender) { 48 | this.sender = sender; 49 | } 50 | 51 | @Override 52 | public void setMessage(@NotNull String message) { 53 | this.message = message; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/event/BungeeChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public class BungeeChatMessageEvent extends BungeeEvent implements ChatMessageEvent { 26 | private OnlineUser sender; 27 | private String message; 28 | private String channelId; 29 | 30 | public BungeeChatMessageEvent(OnlineUser sender, String message, String channelId) { 31 | this.sender = sender; 32 | this.message = message; 33 | this.channelId = channelId; 34 | } 35 | 36 | @Override 37 | @NotNull 38 | public OnlineUser getSender() { 39 | return sender; 40 | } 41 | 42 | @Override 43 | @NotNull 44 | public String getMessage() { 45 | return message; 46 | } 47 | 48 | @Override 49 | @NotNull 50 | public String getChannelId() { 51 | return channelId; 52 | } 53 | 54 | @Override 55 | public void setSender(@NotNull OnlineUser sender) { 56 | this.sender = sender; 57 | } 58 | 59 | @Override 60 | public void setMessage(@NotNull String message) { 61 | this.message = message; 62 | } 63 | 64 | @Override 65 | public void setChannelId(@NotNull String channelId) { 66 | this.channelId = channelId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/event/BungeeEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import lombok.Getter; 23 | import lombok.Setter; 24 | import net.md_5.bungee.api.plugin.Cancellable; 25 | import net.md_5.bungee.api.plugin.Event; 26 | 27 | @Setter 28 | @Getter 29 | public class BungeeEvent extends Event implements Cancellable { 30 | 31 | private boolean cancelled = false; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/event/BungeeEventProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.md_5.bungee.api.ProxyServer; 23 | import net.william278.huskchat.user.OnlineUser; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public interface BungeeEventProvider extends EventProvider { 30 | 31 | // To keep compatibility with the Velocity implementation, the Bungee events also return CompletableFuture 32 | @Override 33 | default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser sender, @NotNull String message, @NotNull String channelId) { 34 | final CompletableFuture completableFuture = new CompletableFuture<>(); 35 | completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeeChatMessageEvent(sender, message, channelId))); 36 | return completableFuture; 37 | } 38 | 39 | @Override 40 | default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List receivers, @NotNull String message) { 41 | final CompletableFuture completableFuture = new CompletableFuture<>(); 42 | completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeePrivateMessageEvent(sender, receivers, message))); 43 | return completableFuture; 44 | } 45 | 46 | @Override 47 | default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, @NotNull String message) { 48 | final CompletableFuture completableFuture = new CompletableFuture<>(); 49 | completableFuture.complete(getProxy().getPluginManager().callEvent(new BungeeBroadcastMessageEvent(sender, message))); 50 | return completableFuture; 51 | } 52 | 53 | ProxyServer getProxy(); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/event/BungeePrivateMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.List; 26 | 27 | public class BungeePrivateMessageEvent extends BungeeEvent implements PrivateMessageEvent { 28 | private OnlineUser sender; 29 | private List recipients; 30 | private String message; 31 | 32 | public BungeePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List recipients, @NotNull String message) { 33 | this.sender = sender; 34 | this.recipients = recipients; 35 | this.message = message; 36 | } 37 | 38 | @Override 39 | @NotNull 40 | public OnlineUser getSender() { 41 | return sender; 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public List getRecipients() { 47 | return recipients; 48 | } 49 | 50 | @NotNull 51 | @Override 52 | public String getMessage() { 53 | return message; 54 | } 55 | 56 | @Override 57 | public void setSender(@NotNull OnlineUser sender) { 58 | this.sender = sender; 59 | } 60 | 61 | @Override 62 | public void setRecipients(@NotNull List recipients) { 63 | this.recipients = recipients; 64 | } 65 | 66 | @Override 67 | public void setMessage(@NotNull String message) { 68 | this.message = message; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/listener/BungeeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import net.md_5.bungee.api.connection.ProxiedPlayer; 23 | import net.md_5.bungee.api.event.ChatEvent; 24 | import net.md_5.bungee.api.event.PlayerDisconnectEvent; 25 | import net.md_5.bungee.api.event.PostLoginEvent; 26 | import net.md_5.bungee.api.event.ServerSwitchEvent; 27 | import net.md_5.bungee.api.plugin.Listener; 28 | import net.md_5.bungee.event.EventHandler; 29 | import net.md_5.bungee.event.EventPriority; 30 | import net.william278.huskchat.HuskChat; 31 | import net.william278.huskchat.channel.Channel; 32 | import net.william278.huskchat.message.ChatMessage; 33 | import net.william278.huskchat.user.BungeeUser; 34 | import org.jetbrains.annotations.NotNull; 35 | 36 | import java.util.Optional; 37 | 38 | public class BungeeListener extends PlayerListener implements Listener { 39 | 40 | public BungeeListener(@NotNull HuskChat plugin) { 41 | super(plugin); 42 | } 43 | 44 | @EventHandler(priority = EventPriority.HIGH) 45 | public void onPlayerChat(ChatEvent e) { 46 | if (e.isCommand() || e.isProxyCommand() || e.isCancelled()) { 47 | return; 48 | } 49 | 50 | // Verify they are in a channel 51 | final BungeeUser player = BungeeUser.adapt((ProxiedPlayer) e.getSender(), plugin); 52 | final Optional channel = plugin.getUserCache().getPlayerChannel(player.getUuid()) 53 | .flatMap(channelId -> plugin.getChannels().getChannel(channelId)); 54 | if (channel.isEmpty()) { 55 | plugin.getLocales().sendMessage(player, "error_no_channel"); 56 | return; 57 | } 58 | 59 | // Send the chat message, determine if the event should be canceled 60 | if (new ChatMessage(channel.get(), player, e.getMessage(), plugin).dispatch()) { 61 | e.setCancelled(true); 62 | } 63 | } 64 | 65 | @EventHandler(priority = EventPriority.HIGH) 66 | public void onPlayerChangeServer(ServerSwitchEvent e) { 67 | final BungeeUser player = BungeeUser.adapt(e.getPlayer(), plugin); 68 | this.handlePlayerSwitchServer(player, player.getServerName()); 69 | } 70 | 71 | @EventHandler 72 | public void onPlayerJoinNetwork(PostLoginEvent e) { 73 | super.handlePlayerJoin(BungeeUser.adapt(e.getPlayer(), plugin)); 74 | } 75 | 76 | @EventHandler 77 | public void onPlayerQuitNetwork(PlayerDisconnectEvent e) { 78 | super.handlePlayerQuit(BungeeUser.adapt(e.getPlayer(), plugin)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /bungee/src/main/java/net/william278/huskchat/user/BungeeUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import net.md_5.bungee.api.connection.ProxiedPlayer; 23 | import net.md_5.bungee.api.connection.Server; 24 | import net.william278.huskchat.HuskChat; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.Optional; 29 | 30 | /** 31 | * Bungee implementation of a cross-platform {@link OnlineUser} 32 | */ 33 | public class BungeeUser extends OnlineUser { 34 | private final ProxiedPlayer player; 35 | 36 | private BungeeUser(@NotNull ProxiedPlayer player, @NotNull HuskChat plugin) { 37 | super(player.getName(), player.getUniqueId(), plugin); 38 | this.player = player; 39 | } 40 | 41 | /** 42 | * Adapts a bungee {@link ProxiedPlayer} to a cross-platform {@link OnlineUser} object 43 | * 44 | * @param player {@link ProxiedPlayer} to adapt 45 | * @param plugin the plugin instance 46 | * @return The {@link OnlineUser} object 47 | */ 48 | @NotNull 49 | public static BungeeUser adapt(@NotNull ProxiedPlayer player, @NotNull HuskChat plugin) { 50 | return new BungeeUser(player, plugin); 51 | } 52 | 53 | @Override 54 | public int getPing() { 55 | return player.getPing(); 56 | } 57 | 58 | @Override 59 | @NotNull 60 | public String getServerName() { 61 | return getConnectedTo().map(s -> s.getInfo().getName()).orElse("unknown"); 62 | } 63 | 64 | @Override 65 | public int getPlayersOnServer() { 66 | return getConnectedTo().map(s -> s.getInfo().getPlayers().size()).orElse(0); 67 | } 68 | 69 | private Optional getConnectedTo() { 70 | return Optional.ofNullable(player.getServer()); 71 | } 72 | 73 | @Override 74 | public boolean hasPermission(@Nullable String node, boolean allowByDefault) { 75 | if (node != null && player.getPermissions().contains(node)) { 76 | return player.hasPermission(node); 77 | } else { 78 | return allowByDefault; 79 | } 80 | } 81 | 82 | @NotNull 83 | public ProxiedPlayer getPlayer() { 84 | return player; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /bungee/src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: 'HuskChat' 2 | version: '${version}' 3 | description: '${description}' 4 | author: 'William278' 5 | main: 'net.william278.huskchat.BungeeHuskChat' 6 | softDepends: 7 | - 'LuckPerms' 8 | - 'PAPIProxyBridge' 9 | - 'Spicord' -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | api 'commons-io:commons-io:2.16.1' 7 | api 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT' 8 | api 'net.william278:profanitycheckerapi:3.0' 9 | api 'net.william278:desertwell:2.0.4' 10 | api 'dev.vankka:mcdiscordreserializer:4.3.0' 11 | api 'de.exlll:configlib-yaml:4.5.0' 12 | 13 | compileOnly 'net.kyori:adventure-api:4.17.0' 14 | compileOnly 'net.kyori:adventure-platform-api:4.3.2' 15 | compileOnly 'net.william278:papiproxybridge:1.5' 16 | compileOnly 'net.luckperms:api:5.4' 17 | compileOnly 'com.github.Spicord.Spicord:spicord-common:v5-SNAPSHOT' 18 | compileOnly 'net.dv8tion:JDA:5.0.0-beta.4' 19 | compileOnly 'org.jetbrains:annotations:24.1.0' 20 | compileOnly 'org.projectlombok:lombok:1.18.34' 21 | 22 | testImplementation 'net.william278:profanitycheckerapi:3.0' 23 | testImplementation 'net.kyori:adventure-api:4.17.0' 24 | 25 | annotationProcessor 'org.projectlombok:lombok:1.18.34' 26 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/BroadcastCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.message.BroadcastMessage; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.StringJoiner; 28 | 29 | public class BroadcastCommand extends CommandBase { 30 | 31 | public BroadcastCommand(@NotNull HuskChat plugin) { 32 | super(plugin.getSettings().getBroadcastCommand().getBroadcastAliases(), "", plugin); 33 | this.operatorOnly = true; 34 | } 35 | 36 | @Override 37 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 38 | if (args.length >= 1) { 39 | StringJoiner message = new StringJoiner(" "); 40 | for (String argument : args) { 41 | message.add(argument); 42 | } 43 | new BroadcastMessage(player, message.toString(), plugin).dispatch(); 44 | } else { 45 | plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/ChannelCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.channel.Channel; 24 | import net.william278.huskchat.user.ConsoleUser; 25 | import net.william278.huskchat.user.OnlineUser; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Unmodifiable; 28 | 29 | import java.util.List; 30 | import java.util.Set; 31 | import java.util.stream.Collectors; 32 | 33 | public class ChannelCommand extends CommandBase { 34 | 35 | public ChannelCommand(@NotNull HuskChat plugin) { 36 | super(plugin.getChannels().getChannelCommandAliases(), "", plugin); 37 | } 38 | 39 | @Override 40 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 41 | if (player instanceof ConsoleUser) { 42 | plugin.getLocales().sendMessage(player, "error_in_game_only"); 43 | return; 44 | } 45 | if (args.length == 1) { 46 | plugin.editUserCache(c -> c.switchPlayerChannel(player, args[0], plugin)); 47 | } else { 48 | plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); 49 | } 50 | } 51 | 52 | @Override 53 | @NotNull 54 | public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { 55 | if (args.length <= 1) { 56 | return getUsableChannels(player).stream() 57 | .filter(val -> val.toLowerCase().startsWith((args.length == 1) ? args[0].toLowerCase() : "")) 58 | .sorted().toList(); 59 | } 60 | return List.of(); 61 | } 62 | 63 | @NotNull 64 | @Unmodifiable 65 | public Set getUsableChannels(@NotNull OnlineUser player) { 66 | return plugin.getChannels().getChannels().stream() 67 | .filter(c -> c.canUserSend(player)) 68 | .map(Channel::getId) 69 | .collect(Collectors.toSet()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/CommandBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import lombok.Getter; 23 | import net.william278.huskchat.HuskChat; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | import java.util.List; 29 | import java.util.Locale; 30 | 31 | /** 32 | * Abstract, cross-platform representation of a plugin command 33 | */ 34 | public abstract class CommandBase { 35 | 36 | protected final HuskChat plugin; 37 | protected final List aliases; 38 | protected final String usage; 39 | @Getter 40 | protected boolean operatorOnly = false; 41 | 42 | public CommandBase(@NotNull List aliases, @NotNull String usage, @NotNull HuskChat plugin) { 43 | if (aliases.isEmpty()) { 44 | throw new IllegalArgumentException("Command must have at least one alias"); 45 | } 46 | this.aliases = aliases.stream().map(s -> s.toLowerCase(Locale.ENGLISH)).toList(); 47 | this.usage = usage; 48 | this.plugin = plugin; 49 | } 50 | 51 | /** 52 | * Fires when the command is executed 53 | * 54 | * @param player {@link OnlineUser} executing the command 55 | * @param args Command arguments 56 | */ 57 | public abstract void onExecute(@NotNull OnlineUser player, @NotNull String[] args); 58 | 59 | /** 60 | * What should be returned when the player attempts to TAB complete the command 61 | * 62 | * @param player {@link OnlineUser} doing the TAB completion 63 | * @param args Current command arguments 64 | * @return List of String arguments to offer TAB suggestions 65 | */ 66 | @NotNull 67 | public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { 68 | return List.of(); 69 | } 70 | 71 | /** 72 | * Get the primary command alias 73 | */ 74 | @NotNull 75 | public String getName() { 76 | return aliases.get(0); 77 | } 78 | 79 | /** 80 | * Command aliases 81 | */ 82 | @NotNull 83 | public List getAliases() { 84 | return aliases.subList(1, aliases.size()); 85 | } 86 | 87 | @NotNull 88 | public String getUsage() { 89 | return "/" + getName() + " " + usage; 90 | } 91 | 92 | /** 93 | * Command permission node 94 | */ 95 | @Nullable 96 | public String getPermission(@NotNull String... children) { 97 | return String.join(".", 98 | "huskchat", "command", 99 | children.length == 0 ? getName() : getName() + 100 | "." + String.join(".", children) 101 | ); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/LocalSpyCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.user.ConsoleUser; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import net.william278.huskchat.user.UserCache; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.util.Arrays; 29 | import java.util.List; 30 | import java.util.Optional; 31 | 32 | public class LocalSpyCommand extends CommandBase { 33 | 34 | public LocalSpyCommand(@NotNull HuskChat plugin) { 35 | super(plugin.getSettings().getLocalSpy().getLocalspyAliases(), "[color]", plugin); 36 | this.operatorOnly = true; 37 | } 38 | 39 | @Override 40 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 41 | if (player instanceof ConsoleUser) { 42 | plugin.getLocales().sendMessage(player, "error_in_game_only"); 43 | return; 44 | } 45 | 46 | // Set with color 47 | if (args.length == 1) { 48 | final Optional selectedColor = UserCache.SpyColor.getColor(args[0]); 49 | if (selectedColor.isEmpty()) { 50 | plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); 51 | return; 52 | } 53 | 54 | final UserCache.SpyColor color = selectedColor.get(); 55 | plugin.editUserCache(c -> c.setLocalSpy(player, color)); 56 | plugin.getLocales().sendMessage(player, "local_spy_toggled_on_color", 57 | color.colorCode, color.name().toLowerCase().replaceAll("_", " ")); 58 | return; 59 | } 60 | 61 | // Toggle without specifying color 62 | if (!plugin.getUserCache().isLocalSpying(player)) { 63 | plugin.editUserCache(c -> c.setLocalSpy(player)); 64 | plugin.getLocales().sendMessage(player, "local_spy_toggled_on"); 65 | return; 66 | } 67 | plugin.editUserCache(c -> c.removeLocalSpy(player)); 68 | plugin.getLocales().sendMessage(player, "local_spy_toggled_off"); 69 | } 70 | 71 | @Override 72 | @NotNull 73 | public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { 74 | return Arrays.stream(UserCache.SpyColor.values()) 75 | .map(UserCache.SpyColor::name).map(String::toLowerCase) 76 | .toList(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/OptOutMessageCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.user.OnlineUser; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | import java.util.UUID; 28 | import java.util.stream.Collectors; 29 | 30 | public class OptOutMessageCommand extends CommandBase { 31 | 32 | public OptOutMessageCommand(@NotNull HuskChat plugin) { 33 | super(List.of("optoutmsg"), "", plugin); 34 | } 35 | 36 | @Override 37 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 38 | plugin.getUserCache().getLastMessengers(player.getUuid()).ifPresentOrElse(lastMessengers -> { 39 | if (lastMessengers.size() <= 1) { 40 | plugin.getLocales().sendMessage(player, "error_last_message_not_group"); 41 | return; 42 | } 43 | 44 | for (UUID uuid : lastMessengers) { 45 | plugin.getUserCache().getLastMessengers(uuid).ifPresent(last -> last.remove(player.getUuid())); 46 | } 47 | 48 | String playerList = lastMessengers.stream().flatMap(u -> plugin.getPlayer(u).stream()) 49 | .map(OnlineUser::getName).collect(Collectors.joining(", ")); 50 | StringBuilder builder = new StringBuilder(); 51 | int lastComma = playerList.lastIndexOf(','); 52 | builder.append(playerList, 0, lastComma); 53 | builder.append(" ").append(plugin.getLocales().getRawLocale("list_conjunction")); 54 | builder.append(playerList.substring(lastComma + 1)); 55 | 56 | plugin.getLocales().sendMessage(player, "removed_from_group_message", builder.toString()); 57 | lastMessengers.clear(); 58 | }, () -> plugin.getLocales().sendMessage(player, "error_no_messages_opt_out")); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/ReplyCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.message.PrivateMessage; 24 | import net.william278.huskchat.user.ConsoleUser; 25 | import net.william278.huskchat.user.OnlineUser; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | import java.util.*; 30 | 31 | public class ReplyCommand extends CommandBase { 32 | 33 | public ReplyCommand(@NotNull HuskChat plugin) { 34 | super(plugin.getSettings().getMessageCommand().getReplyAliases(), "", plugin); 35 | } 36 | 37 | @Override 38 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 39 | if (args.length >= 1) { 40 | final Optional> lastMessengers = plugin.getUserCache().getLastMessengers(player.getUuid()); 41 | if (lastMessengers.isEmpty()) { 42 | plugin.getLocales().sendMessage(player, "error_reply_no_messages"); 43 | return; 44 | } 45 | 46 | final ArrayList lastPlayers = new ArrayList<>(); 47 | for (UUID lastMessenger : lastMessengers.get()) { 48 | if (ConsoleUser.isConsolePlayer(lastMessenger)) { 49 | lastPlayers.add(ConsoleUser.wrap(plugin).getName()); 50 | } else { 51 | plugin.getPlayer(lastMessenger).ifPresent(online -> lastPlayers.add(online.getName())); 52 | } 53 | } 54 | 55 | if (lastPlayers.isEmpty()) { 56 | if (lastMessengers.get().size() > 1) { 57 | plugin.getLocales().sendMessage(player, "error_reply_none_online"); 58 | } else { 59 | plugin.getLocales().sendMessage(player, "error_reply_not_online"); 60 | } 61 | return; 62 | } 63 | 64 | final StringJoiner message = new StringJoiner(" "); 65 | for (String arg : args) { 66 | message.add(arg); 67 | } 68 | 69 | final String messageToSend = message.toString(); 70 | new PrivateMessage(player, lastPlayers, messageToSend, plugin).dispatch(); 71 | } else { 72 | plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); 73 | } 74 | } 75 | 76 | @Override 77 | @Nullable 78 | public String getPermission(@NotNull String... args) { 79 | return String.join(".", 80 | "huskchat", "command", "msg", "reply", 81 | String.join(".", args) 82 | ); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/ShortcutCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.channel.Channel; 24 | import net.william278.huskchat.message.ChatMessage; 25 | import net.william278.huskchat.user.ConsoleUser; 26 | import net.william278.huskchat.user.OnlineUser; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import java.util.List; 31 | import java.util.Optional; 32 | import java.util.StringJoiner; 33 | 34 | public class ShortcutCommand extends CommandBase { 35 | private final String channelId; 36 | 37 | public ShortcutCommand(@NotNull String command, @NotNull String channelId, @NotNull HuskChat plugin) { 38 | super(List.of(command), "[message]", plugin); 39 | this.channelId = channelId; 40 | } 41 | 42 | @Override 43 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 44 | if (args.length == 0) { 45 | // Console can't chat in the same way as players can, it can only use commands. 46 | // So no need to allow it to switch channels. 47 | if (player instanceof ConsoleUser) { 48 | plugin.getLocales().sendMessage(player, "error_console_switch_channels"); 49 | return; 50 | } 51 | plugin.editUserCache(c -> c.switchPlayerChannel(player, channelId, plugin)); 52 | } else { 53 | StringJoiner message = new StringJoiner(" "); 54 | for (String arg : args) { 55 | message.add(arg); 56 | } 57 | 58 | final Optional optionalChannel = plugin.getChannels().getChannel(channelId); 59 | if (optionalChannel.isEmpty()) { 60 | plugin.getLocales().sendMessage(player, "error_no_channel"); 61 | return; 62 | } 63 | 64 | final Channel channel = optionalChannel.get(); 65 | if (channel.getBroadcastScope().isPassThrough()) { 66 | plugin.getLocales().sendMessage(player, "error_passthrough_shortcut_command"); 67 | return; 68 | } 69 | new ChatMessage(channel, player, message.toString(), plugin).dispatch(); 70 | } 71 | } 72 | 73 | @Override 74 | @Nullable 75 | public String getPermission(@NotNull String... args) { 76 | return plugin.getChannels().getChannel(channelId) 77 | .map(Channel::getPermissions) 78 | .flatMap(Channel.ChannelPermissions::getSend) 79 | .orElse(null); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/command/SocialSpyCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.command; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.user.ConsoleUser; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import net.william278.huskchat.user.UserCache; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.util.Arrays; 29 | import java.util.List; 30 | import java.util.Optional; 31 | 32 | public class SocialSpyCommand extends CommandBase { 33 | 34 | public SocialSpyCommand(@NotNull HuskChat plugin) { 35 | super(plugin.getSettings().getSocialSpy().getSocialspyAliases(), "[color]", plugin); 36 | this.operatorOnly = true; 37 | } 38 | 39 | @Override 40 | public void onExecute(@NotNull OnlineUser player, @NotNull String[] args) { 41 | if (player instanceof ConsoleUser) { 42 | plugin.getLocales().sendMessage(player, "error_in_game_only"); 43 | return; 44 | } 45 | 46 | // Set with color 47 | if (args.length == 1) { 48 | final Optional selectedColor = UserCache.SpyColor.getColor(args[0]); 49 | if (selectedColor.isEmpty()) { 50 | plugin.getLocales().sendMessage(player, "error_invalid_syntax", getUsage()); 51 | return; 52 | } 53 | 54 | final UserCache.SpyColor color = selectedColor.get(); 55 | plugin.editUserCache(c -> c.setSocialSpy(player, color)); 56 | plugin.getLocales().sendMessage(player, "social_spy_toggled_on_color", 57 | color.colorCode, color.name().toLowerCase().replaceAll("_", " ")); 58 | return; 59 | } 60 | 61 | // Toggle without specifying color 62 | if (!plugin.getUserCache().isSocialSpying(player)) { 63 | plugin.editUserCache(c -> c.setSocialSpy(player)); 64 | plugin.getLocales().sendMessage(player, "social_spy_toggled_on"); 65 | return; 66 | } 67 | plugin.editUserCache(c -> c.removeSocialSpy(player)); 68 | plugin.getLocales().sendMessage(player, "social_spy_toggled_off"); 69 | } 70 | 71 | @Override 72 | @NotNull 73 | public List onTabComplete(@NotNull OnlineUser player, @NotNull String[] args) { 74 | return Arrays.stream(UserCache.SpyColor.values()) 75 | .map(UserCache.SpyColor::name).map(String::toLowerCase) 76 | .toList(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/config/Filters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.config; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.AccessLevel; 24 | import lombok.Getter; 25 | import lombok.NoArgsConstructor; 26 | import net.william278.huskchat.filter.ChatFilter; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | /** 33 | * Class for loading and storing Chat Filters 34 | */ 35 | @SuppressWarnings("FieldMayBeFinal") 36 | @Getter 37 | @Configuration 38 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 39 | public class Filters { 40 | 41 | static final String CONFIG_HEADER = """ 42 | ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 43 | ┃ HuskChat - Filters ┃ 44 | ┃ Developed by William278 ┃ 45 | ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 46 | ┣╸ Information: https://william278.net/project/huskchat/ 47 | ┗╸ Channels Help: https://william278.net/docs/huskchat/filters-and-replacers/"""; 48 | 49 | private Map filters = new HashMap<>(Map.of( 50 | ChatFilter.Type.ADVERTISING, ChatFilter.Type.ADVERTISING.getDefaultSettings(), 51 | ChatFilter.Type.CAPS, ChatFilter.Type.CAPS.getDefaultSettings(), 52 | ChatFilter.Type.REPEAT, ChatFilter.Type.REPEAT.getDefaultSettings(), 53 | ChatFilter.Type.SPAM, ChatFilter.Type.SPAM.getDefaultSettings(), 54 | ChatFilter.Type.PROFANITY, ChatFilter.Type.PROFANITY.getDefaultSettings(), 55 | ChatFilter.Type.ASCII, ChatFilter.Type.ASCII.getDefaultSettings(), 56 | ChatFilter.Type.REGEX, ChatFilter.Type.REGEX.getDefaultSettings() 57 | )); 58 | 59 | private Map replacers = new HashMap<>(Map.of( 60 | ChatFilter.Type.EMOJI, ChatFilter.Type.EMOJI.getDefaultSettings() 61 | )); 62 | 63 | public boolean isFilterEnabled(@NotNull ChatFilter.Type type) { 64 | return filters.get(type).isEnabled(); 65 | } 66 | 67 | public boolean isReplacerEnabled(@NotNull ChatFilter.Type type) { 68 | return replacers.get(type).isEnabled(); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/discord/DiscordHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.discord; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.message.ChatMessage; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.io.IOException; 27 | import java.nio.charset.StandardCharsets; 28 | import java.time.ZonedDateTime; 29 | import java.time.format.DateTimeFormatter; 30 | import java.util.Locale; 31 | 32 | public interface DiscordHook { 33 | 34 | void postMessage(@NotNull ChatMessage message); 35 | 36 | /** 37 | * Get the discord chat message json for the given message. 38 | * 39 | * @param plugin The discord message format to use 40 | * @param message The message to format 41 | * @return the json message as a byte array 42 | */ 43 | static byte[] getDiscordMessageJson(@NotNull HuskChat plugin, @NotNull ChatMessage message) { 44 | return plugin.getSettings().getDiscord().getFormatStyle() 45 | .getPostMessageFormat(plugin) 46 | .replace("{SENDER_UUID}", message.getSender().getUuid().toString()) 47 | .replace("{SENDER_CHANNEL}", message.getChannel().getId()) 48 | .replace("{CURRENT_TIMESTAMP}", ZonedDateTime.now() 49 | .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)) 50 | .replace("{SENDER_USERNAME}", message.getSender().getName()) 51 | .replace("{CHAT_MESSAGE}", message.getMessage() 52 | .replace("\\", "\\\\") 53 | .replace("\"", "\\\"")) 54 | .getBytes(StandardCharsets.UTF_8); 55 | } 56 | 57 | /** 58 | * Message format definitions for Discord webhooks 59 | */ 60 | enum Format { 61 | 62 | EMBEDDED, 63 | INLINE; 64 | 65 | private String format; 66 | 67 | @NotNull 68 | private String getPostMessageFormat(@NotNull HuskChat plugin) { 69 | if (this.format != null) { 70 | return this.format; 71 | } 72 | try { 73 | return this.format = new String(plugin.getResource(String.format( 74 | "discord/%s_message.json", name().toLowerCase(Locale.ENGLISH) 75 | )).readAllBytes(), StandardCharsets.UTF_8); 76 | } catch (IOException e) { 77 | throw new IllegalStateException("Unable to load \"" + name() + "\" Discord message format", e); 78 | } 79 | } 80 | 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/discord/WebHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.discord; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.message.ChatMessage; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.io.OutputStream; 27 | import java.net.HttpURLConnection; 28 | import java.net.URL; 29 | import java.util.Map; 30 | import java.util.Optional; 31 | import java.util.concurrent.CompletableFuture; 32 | import java.util.logging.Level; 33 | 34 | /** 35 | * Represents a discord webhook 36 | */ 37 | public class WebHook implements DiscordHook { 38 | 39 | private final HuskChat plugin; 40 | 41 | // Get the webhook URL for a channel by its ID 42 | private Optional getWebhookUrl(@NotNull String channelId) { 43 | final Map urls = plugin.getSettings().getDiscord().getChannelWebhooks(); 44 | if (urls.containsKey(channelId)) { 45 | return Optional.of(urls.get(channelId)); 46 | } 47 | return Optional.empty(); 48 | } 49 | 50 | public WebHook(@NotNull HuskChat plugin) { 51 | this.plugin = plugin; 52 | } 53 | 54 | /** 55 | * Dispatch a {@link ChatMessage} to a discord webhook 56 | * 57 | * @param message The message to dispatch 58 | */ 59 | @Override 60 | public void postMessage(@NotNull ChatMessage message) { 61 | CompletableFuture.runAsync(() -> getWebhookUrl(message.getChannel().getId()).ifPresent(webhookUrl -> { 62 | try { 63 | final HttpURLConnection webhookConnection = (HttpURLConnection) webhookUrl.openConnection(); 64 | webhookConnection.setRequestMethod("POST"); 65 | webhookConnection.setDoOutput(true); 66 | 67 | final byte[] jsonMessage = DiscordHook.getDiscordMessageJson(plugin, message); 68 | final int messageLength = jsonMessage.length; 69 | webhookConnection.setFixedLengthStreamingMode(messageLength); 70 | webhookConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); 71 | webhookConnection.connect(); 72 | try (OutputStream messageOutputStream = webhookConnection.getOutputStream()) { 73 | messageOutputStream.write(jsonMessage); 74 | } 75 | } catch (Throwable e) { 76 | plugin.log(Level.WARNING, "Unable to send message to Discord webhook", e); 77 | } 78 | })); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/event/BroadcastMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public interface BroadcastMessageEvent extends EventBase { 26 | 27 | @NotNull 28 | OnlineUser getSender(); 29 | @NotNull 30 | String getMessage(); 31 | 32 | void setSender(@NotNull OnlineUser sender); 33 | void setMessage(@NotNull String message); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/event/ChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public interface ChatMessageEvent extends EventBase { 26 | 27 | @NotNull 28 | OnlineUser getSender(); 29 | @NotNull 30 | String getMessage(); 31 | @NotNull 32 | String getChannelId(); 33 | 34 | void setSender(@NotNull OnlineUser sender); 35 | void setMessage(@NotNull String message); 36 | @SuppressWarnings("unused") 37 | void setChannelId(@NotNull String channelId); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/event/EventBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | public interface EventBase { 23 | 24 | @SuppressWarnings("unused") 25 | void setCancelled(boolean cancelled); 26 | boolean isCancelled(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/event/EventProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.List; 26 | import java.util.concurrent.CompletableFuture; 27 | 28 | public interface EventProvider { 29 | 30 | CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, @NotNull String message, @NotNull String channelId); 31 | 32 | CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List receivers, @NotNull String message); 33 | 34 | CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, @NotNull String message); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/event/PrivateMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.List; 26 | 27 | public interface PrivateMessageEvent extends EventBase { 28 | 29 | @NotNull 30 | OnlineUser getSender(); 31 | @NotNull 32 | List getRecipients(); 33 | @NotNull 34 | String getMessage(); 35 | 36 | void setSender(@NotNull OnlineUser sender); 37 | @SuppressWarnings("unused") 38 | void setRecipients(@NotNull List Recipients); 39 | void setMessage(@NotNull String message); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/AsciiFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.regex.Pattern; 26 | 27 | /** 28 | * A {@link ChatFilter} that filters against unicode (non-ASCII) characters 29 | */ 30 | public class AsciiFilter extends ChatFilter { 31 | 32 | /** 33 | * Regex pattern matching only ascii characters 34 | */ 35 | private final Pattern asciiPattern = Pattern.compile("^[\\u0000-\\u007F]*$"); 36 | 37 | public AsciiFilter(@NotNull FilterSettings settings) { 38 | super(settings); 39 | } 40 | 41 | @NotNull 42 | public static FilterSettings getDefaultSettings() { 43 | return new FilterSettings(); 44 | } 45 | 46 | @Override 47 | public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { 48 | return asciiPattern.matcher(message).matches(); 49 | } 50 | 51 | @Override 52 | @NotNull 53 | public String getDisallowedLocale() { 54 | return "error_chat_filter_ascii"; 55 | } 56 | 57 | @Override 58 | @NotNull 59 | public String getIgnorePermission() { 60 | return "huskchat.ignore_filters.ascii"; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/CapsFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.AccessLevel; 24 | import lombok.AllArgsConstructor; 25 | import lombok.Getter; 26 | import lombok.NoArgsConstructor; 27 | import net.william278.huskchat.user.OnlineUser; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | /** 31 | * A {@link ChatFilter} that filters against players using too many caps in their message 32 | */ 33 | public class CapsFilter extends ChatFilter { 34 | 35 | public CapsFilter(FilterSettings settings) { 36 | super(settings); 37 | } 38 | 39 | @NotNull 40 | public static FilterSettings getDefaultSettings() { 41 | return new CapsFilterSettings(); 42 | } 43 | 44 | @Override 45 | public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { 46 | double messageLength = message.length(); 47 | if (messageLength <= 5) { 48 | return true; 49 | } 50 | int capsLetters = 0; 51 | for (char messageChar : message.toCharArray()) { 52 | if (Character.isUpperCase(messageChar)) { 53 | capsLetters++; 54 | } 55 | } 56 | double capsProportion = (double) capsLetters / messageLength; 57 | return !(capsProportion > ((CapsFilterSettings) settings).getMaxCapsPercentage()); 58 | } 59 | 60 | @Override 61 | @NotNull 62 | public String getDisallowedLocale() { 63 | return "error_chat_filter_caps"; 64 | } 65 | 66 | @Override 67 | @NotNull 68 | public String getIgnorePermission() { 69 | return "huskchat.ignore_filters.caps"; 70 | } 71 | 72 | 73 | @Getter 74 | @Configuration 75 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 76 | @AllArgsConstructor(access = AccessLevel.PACKAGE) 77 | public static class CapsFilterSettings extends FilterSettings { 78 | public double maxCapsPercentage = 0.4d; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/EmojiReplacer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.AccessLevel; 24 | import lombok.Getter; 25 | import lombok.NoArgsConstructor; 26 | import net.william278.huskchat.user.OnlineUser; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.HashMap; 30 | import java.util.Locale; 31 | import java.util.Map; 32 | import java.util.StringJoiner; 33 | 34 | /** 35 | * A {@link ReplacerFilter} that replaces chat emoji with the character emote 36 | */ 37 | public class EmojiReplacer extends ChatFilter.ReplacerFilter { 38 | 39 | public EmojiReplacer(@NotNull FilterSettings settings) { 40 | super(settings); 41 | } 42 | 43 | @Override 44 | @NotNull 45 | public String replace(@NotNull String message) { 46 | String[] words = message.split(" "); 47 | StringJoiner replacedMessage = new StringJoiner(" "); 48 | final EmojiReplacerSettings settings = (EmojiReplacerSettings) this.settings; 49 | for (String word : words) { 50 | for (String emoteFormat : settings.getEmoji().keySet()) { 51 | if (!settings.isCaseInsensitive()) { 52 | if (word.equals(emoteFormat)) { 53 | word = settings.getEmoji().get(emoteFormat); 54 | break; 55 | } 56 | } else { 57 | if (word.toLowerCase(Locale.ROOT).equals(emoteFormat)) { 58 | word = settings.getEmoji().get(emoteFormat); 59 | break; 60 | } 61 | } 62 | } 63 | replacedMessage.add(word); 64 | } 65 | return replacedMessage.toString(); 66 | } 67 | 68 | @NotNull 69 | public static FilterSettings getDefaultSettings() { 70 | return new EmojiReplacerSettings(); 71 | } 72 | 73 | @Override 74 | public boolean isAllowed(@NotNull OnlineUser sender, @NotNull String message) { 75 | return true; 76 | } 77 | 78 | @Override 79 | @NotNull 80 | public String getDisallowedLocale() { 81 | throw new UnsupportedOperationException("EmojiReplacer does not support failure messages"); 82 | } 83 | 84 | @Override 85 | @NotNull 86 | public String getIgnorePermission() { 87 | return "huskchat.ignore_filters.emoji_replacer"; 88 | } 89 | 90 | @Getter 91 | @Configuration 92 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 93 | public static class EmojiReplacerSettings extends FilterSettings { 94 | private boolean caseInsensitive = false; 95 | private Map emoji = new HashMap<>(Map.of( 96 | ":)", "☺", 97 | ":smile:", "☺", 98 | ":-)", "☺", 99 | ":(", "☹", 100 | ":frown:", "☹", 101 | ":-(", "☹", 102 | ":fire:", "🔥", 103 | ":heart:", "❤", 104 | "<3", "❤", 105 | ":star:", "⭐" 106 | )); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/FilterProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.channel.Channel; 24 | import net.william278.huskchat.config.Filters; 25 | import net.william278.huskchat.user.OnlineUser; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.util.List; 29 | import java.util.Optional; 30 | import java.util.logging.Level; 31 | 32 | public interface FilterProvider { 33 | 34 | List getFiltersAndReplacers(); 35 | 36 | default void loadFilters() { 37 | final Filters settings = getPlugin().getFilterSettings(); 38 | settings.getFilters().entrySet().stream() 39 | .filter(entry -> entry.getValue().isEnabled()) 40 | .forEach(entry -> { 41 | final ChatFilter.Type type = entry.getKey(); 42 | final ChatFilter.FilterSettings filterSettings = entry.getValue(); 43 | getFiltersAndReplacers().add(type.getCreator().apply(filterSettings)); 44 | getPlugin().log(Level.INFO, "Loaded %s filter".formatted(type.name())); 45 | }); 46 | } 47 | 48 | default Optional filter(@NotNull OnlineUser sender, @NotNull String message, 49 | @NotNull List filters) { 50 | boolean bypass = sender.hasPermission("huskchat.bypass_filters", false); 51 | final StringBuilder filtered = new StringBuilder(message); 52 | for (ChatFilter filter : filters) { 53 | if (sender.hasPermission(filter.getIgnorePermission(), false)) { 54 | continue; 55 | } 56 | if (filter instanceof ChatFilter.ReplacerFilter replacer) { 57 | filtered.replace(0, filtered.length(), replacer.replace(filtered.toString())); 58 | } 59 | if (!bypass && !filter.isAllowed(sender, message)) { 60 | getPlugin().getLocales().sendMessage(sender, filter.getDisallowedLocale()); 61 | return Optional.empty(); 62 | } 63 | } 64 | return Optional.of(filtered.toString()); 65 | } 66 | 67 | default List getChannelFilters(@NotNull Channel channel) { 68 | return getFiltersAndReplacers().stream() 69 | .filter(filter -> filter.getSettings().getChannels().contains(channel.getId())) 70 | .toList(); 71 | } 72 | 73 | default List getMessageFilters() { 74 | return getFiltersAndReplacers().stream() 75 | .filter(filter -> filter.getSettings().isPrivateMessages()) 76 | .toList(); 77 | } 78 | 79 | default List getBroadcastFilters() { 80 | return getFiltersAndReplacers().stream() 81 | .filter(filter -> filter.getSettings().isBroadcastMessages()) 82 | .toList(); 83 | } 84 | 85 | 86 | @NotNull 87 | HuskChat getPlugin(); 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/RegexFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.Getter; 24 | import net.william278.huskchat.user.OnlineUser; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | public class RegexFilter extends ChatFilter { 31 | 32 | public RegexFilter(@NotNull FilterSettings settings) { 33 | super(settings); 34 | } 35 | 36 | @Override 37 | public boolean isAllowed(@NotNull OnlineUser sender, @NotNull String message) { 38 | if (!settings.isEnabled()) { 39 | return true; 40 | } 41 | for (String pattern : ((RegexFilterSettings) settings).getPatterns()) { 42 | if (message.matches(pattern)) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | @Override 50 | @NotNull 51 | public String getDisallowedLocale() { 52 | return "error_chat_filter_regex"; 53 | } 54 | 55 | @Override 56 | @NotNull 57 | public String getIgnorePermission() { 58 | return "huskchat.ignore_filters.regex"; 59 | } 60 | 61 | @NotNull 62 | public static FilterSettings getDefaultSettings() { 63 | return new RegexFilterSettings(); 64 | } 65 | 66 | @Getter 67 | @Configuration 68 | public static class RegexFilterSettings extends FilterSettings { 69 | private List patterns = new ArrayList<>(); 70 | 71 | private RegexFilterSettings() { 72 | this.enabled = false; 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/RepeatFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.AccessLevel; 24 | import lombok.Getter; 25 | import lombok.NoArgsConstructor; 26 | import net.william278.huskchat.user.OnlineUser; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.HashMap; 30 | import java.util.LinkedList; 31 | import java.util.UUID; 32 | 33 | /** 34 | * A {@link ChatFilter} that filters against users sending the same message too many times 35 | */ 36 | public class RepeatFilter extends ChatFilter { 37 | 38 | /** 39 | * Map of user {@link UUID}s to a queue recording the previous messages the user has sent 40 | */ 41 | private final HashMap> userMessageQueues; 42 | 43 | public RepeatFilter(@NotNull FilterSettings settings) { 44 | super(settings); 45 | this.userMessageQueues = new HashMap<>(); 46 | } 47 | 48 | @NotNull 49 | public static FilterSettings getDefaultSettings() { 50 | return new RepeatFilterSettings(); 51 | } 52 | 53 | @Override 54 | public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { 55 | if (!userMessageQueues.containsKey(player.getUuid())) { 56 | userMessageQueues.put(player.getUuid(), new LinkedList<>()); 57 | } 58 | if (!userMessageQueues.get(player.getUuid()).isEmpty()) { 59 | for (String previousMessage : userMessageQueues.get(player.getUuid())) { 60 | if (message.equalsIgnoreCase(previousMessage)) { 61 | return false; 62 | } 63 | } 64 | if (userMessageQueues.get(player.getUuid()).size() + 1 > 65 | ((RepeatFilterSettings) settings).getPreviousMessagesToCheck()) { 66 | userMessageQueues.get(player.getUuid()).removeLast(); 67 | } 68 | } 69 | userMessageQueues.get(player.getUuid()).addFirst(message); 70 | return true; 71 | } 72 | 73 | @Override 74 | @NotNull 75 | public String getDisallowedLocale() { 76 | return "error_chat_filter_repeat"; 77 | } 78 | 79 | @Override 80 | @NotNull 81 | public String getIgnorePermission() { 82 | return "huskchat.ignore_filters.spam"; 83 | } 84 | 85 | 86 | @Getter 87 | @Configuration 88 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 89 | public static class RepeatFilterSettings extends FilterSettings { 90 | public int previousMessagesToCheck = 5; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/filter/SpamFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import de.exlll.configlib.Configuration; 23 | import lombok.AccessLevel; 24 | import lombok.Getter; 25 | import lombok.NoArgsConstructor; 26 | import net.william278.huskchat.user.OnlineUser; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.time.Instant; 30 | import java.util.HashMap; 31 | import java.util.LinkedList; 32 | import java.util.UUID; 33 | 34 | /** 35 | * A {@link ChatFilter} that filters against users sending too many messages into the chat 36 | */ 37 | public class SpamFilter extends ChatFilter { 38 | 39 | /** 40 | * Map of user {@link UUID}s to a queue recording the timestamps of when that user 41 | * last sent a message, used to calculate if the user is sending messages too quickly. 42 | */ 43 | private final HashMap> userMessageQueues; 44 | 45 | public SpamFilter(@NotNull FilterSettings settings) { 46 | super(settings); 47 | this.userMessageQueues = new HashMap<>(); 48 | } 49 | 50 | @NotNull 51 | public static FilterSettings getDefaultSettings() { 52 | return new SpamFilterSettings(); 53 | } 54 | 55 | @Override 56 | public boolean isAllowed(@NotNull OnlineUser player, @NotNull String message) { 57 | if (!userMessageQueues.containsKey(player.getUuid())) { 58 | userMessageQueues.put(player.getUuid(), new LinkedList<>()); 59 | } 60 | final long currentTimestamp = Instant.now().getEpochSecond(); 61 | if (!userMessageQueues.get(player.getUuid()).isEmpty()) { 62 | final SpamFilterSettings spam = (SpamFilterSettings) settings; 63 | if (currentTimestamp > userMessageQueues.get(player.getUuid()).getLast() + spam.getPeriodSeconds()) { 64 | userMessageQueues.get(player.getUuid()).removeLast(); 65 | } 66 | if (userMessageQueues.get(player.getUuid()).size() > spam.getMessagesPerPeriod()) { 67 | return false; 68 | } 69 | } 70 | userMessageQueues.get(player.getUuid()).addFirst(currentTimestamp); 71 | return true; 72 | } 73 | 74 | @Override 75 | @NotNull 76 | public String getDisallowedLocale() { 77 | return "error_chat_filter_spam"; 78 | } 79 | 80 | @Override 81 | @NotNull 82 | public String getIgnorePermission() { 83 | return "huskchat.ignore_filters.spam"; 84 | } 85 | 86 | 87 | @Getter 88 | @Configuration 89 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 90 | public static class SpamFilterSettings extends FilterSettings { 91 | public int periodSeconds = 4; 92 | public int messagesPerPeriod = 3; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/getter/DataGetter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.getter; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.Optional; 26 | 27 | /** 28 | * Used for fetching data about users from third party plugins 29 | */ 30 | public abstract class DataGetter { 31 | 32 | public abstract String getPlayerFullName(@NotNull OnlineUser player); 33 | 34 | public abstract String getPlayerName(@NotNull OnlineUser player); 35 | 36 | public abstract Optional getPlayerPrefix(@NotNull OnlineUser player); 37 | 38 | public abstract Optional getPlayerSuffix(@NotNull OnlineUser player); 39 | 40 | public abstract Optional getPlayerGroupName(@NotNull OnlineUser player); 41 | 42 | public abstract Optional getPlayerGroupDisplayName(@NotNull OnlineUser player); 43 | 44 | public abstract Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/getter/DefaultDataGetter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.getter; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.Optional; 26 | 27 | /** 28 | * The default Data Getter if LuckPerms is not installed 29 | */ 30 | public class DefaultDataGetter extends DataGetter { 31 | 32 | public DefaultDataGetter() { 33 | super(); 34 | } 35 | 36 | @Override 37 | public String getPlayerFullName(@NotNull OnlineUser player) { 38 | return player.getName(); 39 | } 40 | 41 | @Override 42 | public String getPlayerName(@NotNull OnlineUser player) { 43 | return player.getName(); 44 | } 45 | 46 | @Override 47 | public Optional getPlayerPrefix(@NotNull OnlineUser player) { 48 | return Optional.empty(); 49 | } 50 | 51 | @Override 52 | public Optional getPlayerSuffix(@NotNull OnlineUser player) { 53 | return Optional.empty(); 54 | } 55 | 56 | @Override 57 | public Optional getPlayerGroupName(@NotNull OnlineUser player) { 58 | return Optional.empty(); 59 | } 60 | 61 | @Override 62 | public Optional getPlayerGroupDisplayName(@NotNull OnlineUser player) { 63 | return Optional.empty(); 64 | } 65 | 66 | @Override 67 | public Optional getTextFromNode(@NotNull OnlineUser player, @NotNull String nodePrefix) { 68 | return Optional.empty(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/listener/PlayerListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import lombok.AllArgsConstructor; 23 | import net.william278.huskchat.HuskChat; 24 | import net.william278.huskchat.channel.Channel; 25 | import net.william278.huskchat.user.OnlineUser; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.util.Map; 29 | import java.util.Optional; 30 | 31 | @AllArgsConstructor 32 | public abstract class PlayerListener { 33 | 34 | protected final HuskChat plugin; 35 | 36 | // Handle server switches 37 | public final void handlePlayerSwitchServer(@NotNull OnlineUser player, @NotNull String newServer) { 38 | // Switch to the default channel for the server if there is one 39 | final Optional defaultChannel = plugin.getChannels().getServerDefaultChannel(newServer); 40 | if (defaultChannel.isPresent()) { 41 | plugin.editUserCache(c -> c.switchPlayerChannel(player, defaultChannel.get(), plugin)); 42 | return; 43 | } 44 | 45 | // Switch channels to the default one if they don't have one 46 | final Optional currentChannel = plugin.getUserCache().getPlayerChannel(player.getUuid()); 47 | if (currentChannel.isEmpty()) { 48 | plugin.editUserCache(c -> c.switchPlayerChannel(player, plugin.getChannels().getDefaultChannel(), plugin)); 49 | return; 50 | } 51 | 52 | // Switch the player's channel away if their current channel is now restricted 53 | plugin.getChannels().getChannels().stream() 54 | .filter(channel -> channel.getId().equalsIgnoreCase(currentChannel.get())) 55 | .findFirst().filter(channel -> channel.isServerRestricted(newServer)) 56 | .ifPresent(restricted -> plugin.editUserCache(c -> c 57 | .switchPlayerChannel(player, plugin.getChannels().getDefaultChannel(), plugin))); 58 | } 59 | 60 | // Handle player joins 61 | public final void handlePlayerJoin(@NotNull OnlineUser player) { 62 | handlePlayerSwitchServer(player, player.getServerName()); 63 | if (plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { 64 | return; 65 | } 66 | if (plugin.getSettings().getJoinAndQuitMessages().getJoin().isEnabled()) { 67 | plugin.getLocales().sendJoinMessage(player, plugin); 68 | } 69 | } 70 | 71 | // Handle player quits 72 | public final void handlePlayerQuit(@NotNull OnlineUser player) { 73 | if (plugin.getSettings().getJoinAndQuitMessages().getBroadcastScope() == Channel.BroadcastScope.PASSTHROUGH) { 74 | return; 75 | } 76 | if (plugin.getSettings().getJoinAndQuitMessages().getQuit().isEnabled()) { 77 | plugin.getLocales().sendQuitMessage(player, plugin); 78 | } 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/message/BroadcastMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.message; 21 | 22 | import de.themoep.minedown.adventure.MineDown; 23 | import de.themoep.minedown.adventure.MineDownParser; 24 | import net.kyori.adventure.text.Component; 25 | import net.kyori.adventure.text.TextComponent; 26 | import net.william278.huskchat.HuskChat; 27 | import net.william278.huskchat.config.Settings; 28 | import net.william278.huskchat.user.OnlineUser; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | import java.util.Optional; 32 | import java.util.logging.Level; 33 | 34 | /** 35 | * Represents a broadcast message to be sent to everyone 36 | */ 37 | public class BroadcastMessage { 38 | 39 | private final Settings.BroadcastSettings settings; 40 | private final OnlineUser sender; 41 | private final HuskChat plugin; 42 | private String message; 43 | 44 | public BroadcastMessage(@NotNull OnlineUser sender, @NotNull String message, @NotNull HuskChat plugin) { 45 | this.sender = sender; 46 | this.plugin = plugin; 47 | this.settings = plugin.getSettings().getBroadcastCommand(); 48 | this.message = message; 49 | } 50 | 51 | /** 52 | * Dispatch the broadcast message to be sent 53 | */ 54 | public void dispatch() { 55 | plugin.fireBroadcastMessageEvent(sender, message).thenAccept(event -> { 56 | if (event.isCancelled()) { 57 | return; 58 | } 59 | message = event.getMessage(); 60 | 61 | // If the message is to be filtered, then perform filter checks (unless they have the bypass permission) 62 | final Optional filtered = plugin.filter(sender, message, plugin.getBroadcastFilters()); 63 | if (filtered.isEmpty()) { 64 | return; 65 | } 66 | message = filtered.get(); 67 | 68 | // Send the broadcast 69 | plugin.getOnlinePlayers().forEach(this::sendMessage); 70 | 71 | // Log to console 72 | if (settings.isLogToConsole()) { 73 | plugin.log(Level.INFO, settings.getLogFormat() + message); 74 | } 75 | }); 76 | } 77 | 78 | public void sendMessage(@NotNull OnlineUser player) { 79 | final TextComponent.Builder componentBuilder = Component.text(); 80 | componentBuilder.append(new MineDown(plugin.getSettings().getBroadcastCommand().getFormat()).toComponent()); 81 | componentBuilder.append(new MineDown(message).disable(MineDownParser.Option.ADVANCED_FORMATTING).toComponent()); 82 | player.sendMessage(componentBuilder.build()); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/placeholders/PAPIProxyBridgeReplacer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.placeholders; 21 | 22 | import net.william278.huskchat.HuskChat; 23 | import net.william278.huskchat.user.OnlineUser; 24 | import net.william278.papiproxybridge.api.PlaceholderAPI; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public class PAPIProxyBridgeReplacer implements PlaceholderReplacer { 30 | 31 | private final PlaceholderAPI instance; 32 | 33 | public PAPIProxyBridgeReplacer(@NotNull HuskChat plugin) { 34 | this.instance = PlaceholderAPI.createInstance(); 35 | instance.setCacheExpiry(plugin.getSettings().getPlaceholder().getCacheTime()); 36 | } 37 | 38 | @Override 39 | public CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player) { 40 | return instance.formatPlaceholders(message, player.getUuid()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/placeholders/PlaceholderReplacer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.placeholders; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.concurrent.CompletableFuture; 26 | 27 | public interface PlaceholderReplacer { 28 | 29 | CompletableFuture formatPlaceholders(@NotNull String message, @NotNull OnlineUser player); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/user/ConsoleUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import net.kyori.adventure.audience.Audience; 23 | import net.william278.huskchat.HuskChat; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | 27 | import java.util.UUID; 28 | 29 | public class ConsoleUser extends OnlineUser { 30 | 31 | private static final UUID CONSOLE_UUID = new UUID(0, 0); 32 | private static final String CONSOLE_USERNAME = "[CONSOLE]"; 33 | 34 | private ConsoleUser(@NotNull HuskChat plugin) { 35 | super(CONSOLE_USERNAME, CONSOLE_UUID, plugin); 36 | } 37 | 38 | @Override 39 | public int getPing() { 40 | return 0; 41 | } 42 | 43 | @Override 44 | @NotNull 45 | public String getServerName() { 46 | return plugin.getPlatform(); 47 | } 48 | 49 | @Override 50 | public int getPlayersOnServer() { 51 | return plugin.getOnlinePlayers().size(); 52 | } 53 | 54 | @Override 55 | public boolean hasPermission(@Nullable String node, boolean allowByDefault) { 56 | return true; 57 | } 58 | 59 | @NotNull 60 | @Override 61 | public Audience getAudience() { 62 | return plugin.getConsole(); 63 | } 64 | 65 | /** 66 | * Adapt the proxy console player into a cross-platform one 67 | * 68 | * @param plugin The implementing HuskChat plugin 69 | * @return The ConsolePlayer 70 | */ 71 | @NotNull 72 | public static ConsoleUser wrap(@NotNull HuskChat plugin) { 73 | return new ConsoleUser(plugin); 74 | } 75 | 76 | /** 77 | * Returns true if the UUID is that of the console player 78 | * 79 | * @param uuid UUID to check 80 | * @return {@code true} if the UUID is the console 81 | */ 82 | public static boolean isConsolePlayer(@NotNull UUID uuid) { 83 | return uuid.equals(CONSOLE_UUID); 84 | } 85 | 86 | /** 87 | * Returns true if the username is that of the console player 88 | * 89 | * @param username username to check 90 | * @return {@code true} if the username is the console 91 | */ 92 | public static boolean isConsolePlayer(@NotNull String username) { 93 | return username.equalsIgnoreCase(CONSOLE_USERNAME); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/user/OnlineUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import de.themoep.minedown.adventure.MineDown; 23 | import net.kyori.adventure.audience.Audience; 24 | import net.kyori.adventure.text.Component; 25 | import net.william278.huskchat.HuskChat; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | import org.jetbrains.annotations.TestOnly; 29 | 30 | import java.util.UUID; 31 | 32 | /** 33 | * Abstract cross-platform Player object 34 | */ 35 | public abstract class OnlineUser extends User { 36 | 37 | protected HuskChat plugin; 38 | 39 | protected OnlineUser(@NotNull String username, @NotNull UUID uuid, @NotNull HuskChat plugin) { 40 | super(username, uuid); 41 | this.plugin = plugin; 42 | } 43 | 44 | @TestOnly 45 | protected OnlineUser(@NotNull String username, @NotNull UUID uuid) { 46 | super(username, uuid); 47 | } 48 | 49 | /** 50 | * Return the player's ping 51 | * 52 | * @return the player's ping 53 | */ 54 | public abstract int getPing(); 55 | 56 | /** 57 | * Return the name of the server the player is connected to 58 | * 59 | * @return player's server name 60 | */ 61 | @NotNull 62 | public abstract String getServerName(); 63 | 64 | /** 65 | * Return the number of people on that player's server 66 | * 67 | * @return player count on the player's server 68 | */ 69 | public abstract int getPlayersOnServer(); 70 | 71 | /** 72 | * Check if the player has a permission 73 | * 74 | * @param permission the permission to check 75 | * @param allowByDefault whether to allow the permission by default if it is not set 76 | * @return whether the player has the permission 77 | */ 78 | public abstract boolean hasPermission(@Nullable String permission, boolean allowByDefault); 79 | 80 | @NotNull 81 | public Audience getAudience() { 82 | return plugin.getAudience(getUuid()); 83 | } 84 | 85 | public void sendMessage(@NotNull Component message) { 86 | getAudience().sendMessage(message); 87 | } 88 | 89 | public void sendMessage(@NotNull MineDown mineDown) { 90 | sendMessage(mineDown.toComponent()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/user/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import lombok.AccessLevel; 23 | import lombok.AllArgsConstructor; 24 | import lombok.Getter; 25 | import lombok.NoArgsConstructor; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.util.UUID; 29 | 30 | @Getter 31 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 32 | @AllArgsConstructor(access = AccessLevel.PROTECTED) 33 | public class User implements Comparable { 34 | 35 | @NotNull 36 | private String name; 37 | 38 | @NotNull 39 | private UUID uuid; 40 | 41 | @NotNull 42 | public static User of(@NotNull UUID uuid, @NotNull String name) { 43 | return new User(name, uuid); 44 | } 45 | 46 | @Override 47 | public boolean equals(Object obj) { 48 | if (!(obj instanceof User user)) { 49 | return false; 50 | } 51 | return user.getUuid().equals(uuid); 52 | } 53 | 54 | @Override 55 | public int compareTo(@NotNull User o) { 56 | return name.compareTo(o.getName()); 57 | } 58 | } -------------------------------------------------------------------------------- /common/src/main/java/net/william278/huskchat/util/AudiencesProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.util; 21 | 22 | 23 | import net.kyori.adventure.audience.Audience; 24 | import net.kyori.adventure.audience.Audiences; 25 | import net.william278.huskchat.HuskChat; 26 | import net.william278.huskchat.user.ConsoleUser; 27 | import net.william278.huskchat.user.OnlineUser; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | import java.util.UUID; 31 | 32 | /** 33 | * Interface for providing the {@link ConsoleUser} and {@link Audiences} instances 34 | * 35 | * @since 3.0 36 | */ 37 | public interface AudiencesProvider { 38 | 39 | 40 | /** 41 | * Get the {@link Audience} instance for the given {@link UUID} 42 | * 43 | * @param user the {@link OnlineUser} to get the {@link UUID} for 44 | * @return the {@link Audience} instance 45 | */ 46 | @NotNull 47 | Audience getAudience(@NotNull UUID user); 48 | 49 | @NotNull 50 | Audience getConsole(); 51 | 52 | /** 53 | * Get the {@link ConsoleUser} instance 54 | * 55 | * @return the {@link ConsoleUser} instance 56 | * @since 3.0 57 | */ 58 | @NotNull 59 | default ConsoleUser getConsoleUser() { 60 | return ConsoleUser.wrap(getPlugin()); 61 | } 62 | 63 | @NotNull 64 | HuskChat getPlugin(); 65 | 66 | } -------------------------------------------------------------------------------- /common/src/main/resources/discord/embedded_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "avatar_url": "https://william278.net/images/icons/huskchat.png", 3 | "username": "HuskChat", 4 | "content": null, 5 | "allowed_mentions": { "parse": [] }, 6 | "embeds": [ 7 | { 8 | "description": "{CHAT_MESSAGE}", 9 | "color": 64410, 10 | "footer": { 11 | "text": "{SENDER_USERNAME} • {SENDER_CHANNEL}", 12 | "icon_url": "https://minotar.net/avatar/{SENDER_UUID}/64" 13 | }, 14 | "timestamp": "{CURRENT_TIMESTAMP}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /common/src/main/resources/discord/inline_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "avatar_url": "https://minotar.net/avatar/{SENDER_UUID}/128", 3 | "username": "[{SENDER_CHANNEL}] {SENDER_USERNAME}", 4 | "content": "{CHAT_MESSAGE}", 5 | "allowed_mentions": { "parse": [] } 6 | } -------------------------------------------------------------------------------- /common/src/main/resources/locales/zh-cn.yml: -------------------------------------------------------------------------------- 1 | locales: 2 | error_no_permission: '[错误:](#ff3300) [你没有执行该指令的权限。](#ff7e5e)' 3 | error_invalid_syntax: '[错误:](#ff3300) [语法不正确。用法: %1%](#ff7e5e)' 4 | channel_switched: '[你已切换到](#00fb9a) [%1%](#00fb9a bold) [频道!](#00fb9a)' 5 | error_no_permission_send: '[错误:](#ff3300) [你没有在 %1% 频道中说话的权限。](#ff7e5e)' 6 | error_invalid_channel: '[错误:](#ff3300) [请指定一个有效的聊天频道。](#ff7e5e)' 7 | error_invalid_channel_command: '[错误:](#ff3300) [频道无效](#ff7e5e)' 8 | error_no_channel: '[错误:](#ff3300) [你正在一个无效的频道中说话。](#ff7e5e) [使用 /channel 切换到一个有效的频道。](#ff7e5e show_text=&#ff7e5e&点击这里获得命令建议 suggest_command=/channel )' 9 | error_player_not_found: '[错误:](#ff3300) [找不到指定玩家;他们在线吗?](#ff7e5e)' 10 | error_cannot_message_self: '[错误:](#ff3300) [你不能给自己发消息!](#ff7e5e)' 11 | error_reply_no_messages: '[错误:](#ff3300) [没有人给你发可以回复的消息!](#ff7e5e)' 12 | error_reply_not_online: '[错误:](#ff3300) [你最后发消息的人已经不在线了。](#ff7e5e)' 13 | error_message_restricted_server: '[错误:](#ff3300) [你不能给这个服务器的玩家发送信息。](#ff7e5e)' 14 | error_message_recipient_restricted_server: '[错误:](#ff3300) [该玩家在一个不能接收信息的服务器上。](#ff7e5e)' 15 | error_channel_restricted_server: '[错误:](#ff3300) [你不能在这个服务器的 %1% 频道中说话](#ff7e5e)' 16 | social_spy_toggled_on: '[你正在窥探私聊。](#00fb9a)' 17 | social_spy_toggled_on_color: '[你正在窥探私聊于](#00fb9a) %1%%2%' 18 | social_spy_toggled_off: '[你不再窥探私聊了。](#00fb9a)' 19 | local_spy_toggled_on: '[你正在窥探其他服务器的本地聊天频道。](#00fb9a)' 20 | local_spy_toggled_on_color: '[你正在窥探本地聊天于](#00fb9a) %1%%2%' 21 | local_spy_toggled_off: '[你不再窥探其他服务器的本地聊天频道了。](#00fb9a)' 22 | error_chat_filter_advertising: '[你不能发广告或链接。](#ff7e5e)' 23 | error_chat_filter_profanity: '[不能说脏话哦](#ff7e5e)' 24 | error_chat_filter_caps: '[请注意你在聊天中的大写字母。](#ff7e5e)' 25 | error_chat_filter_spam: '[请稍等!你发送消息的速度太快了。](#ff7e5e)' 26 | error_chat_filter_ascii: '[你不能再聊天中使用特殊字符。](#ff7e5e)' 27 | error_chat_filter_repeat: '[你刚刚才发过这条信息](#ff7e5e)' 28 | error_chat_filter_regex: '[你的消息包含被屏蔽的文字。](#ff7e5e)' 29 | error_in_game_only: '错误: 该命令只能在游戏中使用.' 30 | error_console_local_scope: '错误: 不支持从控制台发送消息到本地频道。' 31 | error_console_switch_channels: '错误: 你不能在控制台切换到另一个频道,请改用快捷命令' 32 | error_group_messages_disabled: '[错误:](#ff3300) [你不能向多个人发送消息](#ff7e5e)' 33 | error_group_messages_max: '[错误:](#ff3300) [你一次最多只能向 %1% 个人发送消息](#ff7e5e)' 34 | error_players_not_found: '[错误:](#ff3300) [找不到任何指定玩家;他们在线吗?](#ff7e5e)' 35 | error_reply_none_online: '[错误:](#ff3300) [你最后发消息的那组人中没人还在线。](#ff7e5e)' 36 | error_last_message_not_group: '[错误:](#ff3300) [你收到的最后一条信息不是群组信息。](#ff7e5e)' 37 | error_no_messages_opt_out: '[错误:](#ff3300) [你还没有发送或接收群组信息。](#ff7e5e)' 38 | removed_from_group_message: '[你被从群组信息中移除:](#00fb9a) %1%' 39 | list_conjunction: '和' 40 | error_passthrough_shortcut_command: '[错误:](#ff3300) [不支持使用快捷命令向 passthrough 频道发送消息。请先切换到该频道。](#ff7e5e)' 41 | up_to_date: '[HuskChat](#00fb9a bold) [| 你正在使用最新版本的HuskChat (v%1%).](#00fb9a)' 42 | update_available: '[HuskChat](#ff7e5e bold) [| 一个新版本的HuskChat已经可以更新: v%1% (当前: v%2%).](#ff7e5e)' 43 | -------------------------------------------------------------------------------- /common/src/main/resources/locales/zh-tw.yml: -------------------------------------------------------------------------------- 1 | locales: 2 | error_no_permission: '[錯誤:](#ff3300) [你沒有執行該指令的權限。 ](#ff7e5e)' 3 | error_invalid_syntax: '[錯誤:](#ff3300) [語法錯誤。使用方法:%1% ](#ff7e5e)' 4 | channel_switched: '[你現在正在使用](#00fb9a) [%1% ](#00fb9a bold) [聊天! ](#00fb9a)' 5 | error_no_permission_send: '[錯誤:](#ff3300) [你沒有在 %1% 聊天中發言的權限。 ](#ff7e5e)' 6 | error_invalid_channel: '[錯誤:](#ff3300) [請指定有效的聊天頻道。 ](#ff7e5e)' 7 | error_invalid_channel_command: '[錯誤:](#ff3300) [該頻道無效。 ](#ff7e5e)' 8 | error_no_channel: '[錯誤:](#ff3300) [你正在嘗試使用無效的聊天頻道。使用](#ff7e5e) [/channel ](#ff7e5e italic show_text=&#ff7e5e&點選此處以建議指令 suggest_command=/channel ) [切換至有效的頻道。 ](#ff7e5e)' 9 | error_player_not_found: '[錯誤:](#ff3300) [找不到指定的玩家;他們在線嗎? ](#ff7e5e)' 10 | error_cannot_message_self: '[錯誤:](#ff3300) [你不能傳訊息給自己! ](#ff7e5e)' 11 | error_reply_no_messages: '[錯誤:](#ff3300) [沒有人傳訊息給你,無法回覆! ](#ff7e5e)' 12 | error_reply_not_online: '[錯誤:](#ff3300) [你最後傳訊息給的那個人已經不在線了。 ](#ff7e5e)' 13 | error_message_restricted_server: '[錯誤:](#ff3300) [你不能從這個伺服器傳訊息給玩家。 ](#ff7e5e)' 14 | error_message_recipient_restricted_server: '[錯誤:](#ff3300) [該玩家所在的伺服器無法接收訊息。 ](#ff7e5e)' 15 | error_channel_restricted_server: '[錯誤:](#ff3300) [你不能從這個伺服器在 %1% 聊天中發言。 ](#ff7e5e)' 16 | social_spy_toggled_on: '[你現在正在監聽私訊。 ](#00fb9a)' 17 | social_spy_toggled_on_color: '[你現在正在監聽](#00fb9a) [ %1%%2% ](#00fb9a) [的私訊。 ]' 18 | social_spy_toggled_off: '[你不再監聽私訊。 ](#00fb9a)' 19 | local_spy_toggled_on: '[你現在正在監聽其他伺服器的本地聊天頻道。 ](#00fb9a)' 20 | local_spy_toggled_on_color: '[你現在正在監聽](#00fb9a) [ %1%%2% ](#00fb9a) [的本地聊天。 ]' 21 | local_spy_toggled_off: '[你不再監聽其他伺服器的本地聊天頻道。 ](#00fb9a)' 22 | error_chat_filter_advertising: '[你不能在聊天中做廣告或發布連結。 ](#ff7e5e)' 23 | error_chat_filter_profanity: '[你不能在聊天中使用髒話。 ](#ff7e5e)' 24 | error_chat_filter_caps: '[請注意你的大寫字母在聊天中的使用。 ](#ff7e5e)' 25 | error_chat_filter_spam: '[請稍候!你發送訊息的頻率過高。 ](#ff7e5e)' 26 | error_chat_filter_ascii: '[你不能在聊天中使用特殊字元。 ](#ff7e5e)' 27 | error_chat_filter_repeat: '[你最近才發送過這則訊息! ](#ff7e5e)' 28 | error_chat_filter_regex: '[你的訊息包含被封鎖的文字。 ](#ff7e5e)' 29 | error_in_game_only: '錯誤:該指令只能在遊戲中使用。' 30 | error_console_local_scope: '錯誤:從主控台發送訊息到具有本地範圍的頻道不受支援。' 31 | error_console_switch_channels: '錯誤:你不能從主控台切換到其他頻道。請改用快速指令。' 32 | error_group_messages_disabled: '[錯誤:](#ff3300) [你不能傳訊息給多個人。 ](#ff7e5e)' 33 | error_group_messages_max: '[錯誤:](#ff3300) [你一次最多只能傳訊息給 %1% 個人。 ](#ff7e5e)' 34 | error_players_not_found: '[錯誤:](#ff3300) [找不到指定的玩家;他們在線嗎? ](#ff7e5e)' 35 | error_reply_none_online: '[錯誤:](#ff3300) [你最後傳訊息給的那群人中,沒有人還在線。 ](#ff7e5e)' 36 | error_last_message_not_group: '[錯誤:](#ff3300) [你最後收到的訊息不是群組訊息。 ](#ff7e5e)' 37 | error_no_messages_opt_out: '[錯誤:](#ff3300) [你尚未傳送或接收過群組訊息。 ](#ff7e5e)' 38 | removed_from_group_message: '[從以下群組訊息中移除你:](#00fb9a) %1%' 39 | list_conjunction: '和' 40 | error_passthrough_shortcut_command: '[錯誤:](#ff3300) [使用快速指令傳送訊息到穿透頻道不受支援。請先切換到該頻道。 ](#ff7e5e)' 41 | up_to_date: '[HuskChat](#00fb9a bold) [ | 你正在執行最新版本的 HuskChat (v%1%)。 ](#00fb9a)' 42 | update_available: '[HuskChat](#ff7e5e bold) [ | 有新的 HuskChat 版本可供使用:v%1% (正在執行:v%2%)。 ](#ff7e5e)' -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/channel/ChannelTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.channel; 21 | 22 | import java.util.List; 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | 26 | public class ChannelTests { 27 | Channel plainTextChannel = Channel 28 | .builder().id("plaintext").restrictedServers(List.of("plain", "text", "restrictions")).build(); 29 | Channel regexTextChannel = Channel 30 | .builder().id("regex").restrictedServers(List.of(".*regex.*", "matcher.*", ".*channel")).build(); 31 | 32 | @Test 33 | public void testPlaintextUnrestrictedServer() { 34 | Assertions.assertFalse(plainTextChannel.isServerRestricted("nota")); 35 | Assertions.assertFalse(plainTextChannel.isServerRestricted("plaintext")); 36 | Assertions.assertFalse(plainTextChannel.isServerRestricted("restricted server")); 37 | } 38 | 39 | @Test 40 | public void testPlaintextRestrictedServer() { 41 | Assertions.assertTrue(plainTextChannel.isServerRestricted("plain")); 42 | Assertions.assertTrue(plainTextChannel.isServerRestricted("text")); 43 | Assertions.assertTrue(plainTextChannel.isServerRestricted("restrictions")); 44 | } 45 | 46 | @Test 47 | public void testPlaintextRestrictedServerIgnoreCase() { 48 | Assertions.assertTrue(plainTextChannel.isServerRestricted("PLAIN")); 49 | Assertions.assertTrue(plainTextChannel.isServerRestricted("tExT")); 50 | Assertions.assertTrue(plainTextChannel.isServerRestricted("resTriCTioNs")); 51 | } 52 | 53 | @Test 54 | public void testRegexUnrestrictedServer() { 55 | Assertions.assertFalse(regexTextChannel.isServerRestricted("does")); 56 | Assertions.assertFalse(regexTextChannel.isServerRestricted("not")); 57 | Assertions.assertFalse(regexTextChannel.isServerRestricted("match")); 58 | } 59 | 60 | @Test 61 | public void testRegexRestrictedServer() { 62 | Assertions.assertTrue(regexTextChannel.isServerRestricted("xxx-regex-1234")); 63 | Assertions.assertTrue(regexTextChannel.isServerRestricted("matcher-funtime")); 64 | Assertions.assertTrue(regexTextChannel.isServerRestricted("super-channel")); 65 | } 66 | 67 | @Test 68 | public void testRegexRestrictedServerIgnoreCase() { 69 | Assertions.assertTrue(regexTextChannel.isServerRestricted("xXx-REGEX-1234")); 70 | Assertions.assertTrue(regexTextChannel.isServerRestricted("maTCher-funtime")); 71 | Assertions.assertTrue(regexTextChannel.isServerRestricted("sUPEr-chANnel")); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/filter/AdvertisingFilterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.user.TestOnlineUser; 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | 26 | public class AdvertisingFilterTests { 27 | 28 | AdvertisingFilterer filterer = new AdvertisingFilterer(new ChatFilter.FilterSettings()); 29 | 30 | @Test 31 | public void testSentence() { 32 | Assertions.assertTrue(filterer.isAllowed(new TestOnlineUser(), "This is an example sentence!")); 33 | } 34 | 35 | @Test 36 | public void testFullUrl() { 37 | Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "https://william278.net")); 38 | } 39 | 40 | @Test 41 | public void testPartialUrl() { 42 | Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "william278.net")); 43 | } 44 | 45 | @Test 46 | public void testSubDomainUrl() { 47 | Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "example.william278.net")); 48 | } 49 | 50 | @Test 51 | public void testSubDomainUrlWithPort() { 52 | Assertions.assertFalse(filterer.isAllowed(new TestOnlineUser(), "william278.net:25565")); 53 | } 54 | 55 | @Test 56 | public void testTopLevelDomain() { 57 | Assertions.assertTrue(filterer.isAllowed(new TestOnlineUser(), ".net")); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/filter/AsciiFilterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.user.TestOnlineUser; 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | 26 | public class AsciiFilterTests { 27 | 28 | AsciiFilter asciiFilter = new AsciiFilter(new ChatFilter.FilterSettings()); 29 | 30 | @Test 31 | public void testAsciiSentence() { 32 | Assertions.assertTrue(asciiFilter.isAllowed(new TestOnlineUser(), "This is a test sentence")); 33 | } 34 | 35 | @Test 36 | public void testAsciiSentenceWithMathematicalSymbolsAndPunctuation() { 37 | Assertions.assertTrue(asciiFilter.isAllowed(new TestOnlineUser(), "This is a (test) sentence with [mathematical symbols], like + - = != etc :-)")); 38 | } 39 | 40 | @Test 41 | public void testUnicodeSentence() { 42 | Assertions.assertFalse(asciiFilter.isAllowed(new TestOnlineUser(), "• This is a test sentence with ♣ UNICODE ♣ characters •")); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/filter/CapsFilterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.user.TestOnlineUser; 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | 26 | public class CapsFilterTests { 27 | 28 | CapsFilter capsFilter50Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(0.5)); 29 | CapsFilter capsFilter25Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(0.25)); 30 | CapsFilter capsFilter100Percent = new CapsFilter(new CapsFilter.CapsFilterSettings(1.0)); 31 | 32 | @Test 33 | public void testCapsFilter_50PercentCaps_FullCaps() { 34 | Assertions.assertFalse(capsFilter50Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); 35 | } 36 | 37 | @Test 38 | public void testCapsFilter_50PercentCaps_LowerCase() { 39 | Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestOnlineUser(), "this is a test message")); 40 | } 41 | 42 | @Test 43 | public void testCapsFilter_50PercentCaps_HalfCaps() { 44 | Assertions.assertTrue(capsFilter50Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); 45 | } 46 | 47 | @Test 48 | public void testCapsFilter_25PercentCaps_FullCaps() { 49 | Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); 50 | } 51 | 52 | @Test 53 | public void testCapsFilter_25PercentCaps_LowerCase() { 54 | Assertions.assertTrue(capsFilter25Percent.isAllowed(new TestOnlineUser(), "this is a test message")); 55 | } 56 | 57 | @Test 58 | public void testCapsFilter_25PercentCaps_HalfCaps() { 59 | Assertions.assertFalse(capsFilter25Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); 60 | } 61 | 62 | @Test 63 | public void testCapsFilter_100PercentCaps_FullCaps() { 64 | Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "THIS IS A TEST MESSAGE")); 65 | } 66 | 67 | @Test 68 | public void testCapsFilter_100PercentCaps_LowerCase() { 69 | Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "this is a test message")); 70 | } 71 | 72 | @Test 73 | public void testCapsFilter_100PercentCaps_HalfCaps() { 74 | Assertions.assertTrue(capsFilter100Percent.isAllowed(new TestOnlineUser(), "this is a TEST MESSAGE")); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/filter/ProfanityFilterTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.filter; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import net.william278.huskchat.user.TestOnlineUser; 24 | import org.junit.jupiter.api.Assertions; 25 | import org.junit.jupiter.api.Test; 26 | 27 | public class ProfanityFilterTests { 28 | 29 | private final ProfanityFilterer filterer = new ProfanityFilterer( 30 | new ProfanityFilterer.ProfanityFilterSettings( 31 | "", ProfanityFilterer.ProfanityFilterMode.TOLERANCE, 0.8d 32 | ) 33 | ); 34 | 35 | @Test 36 | public void givenSentenceContainingProfanity_testIsProfane() { 37 | final OnlineUser dummyPlayer = new TestOnlineUser(); 38 | Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "This is a fucking test sentence")); 39 | Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "Shit")); 40 | } 41 | 42 | @Test 43 | public void givenNormalSentences_testIsNotProfane() { 44 | final OnlineUser dummyPlayer = new TestOnlineUser(); 45 | Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "AHOJ")); 46 | Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "Hello")); 47 | } 48 | 49 | @Test 50 | public void givenObfuscatedProfanity_testIsProfane() { 51 | final OnlineUser dummyPlayer = new TestOnlineUser(); 52 | Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "You're a fuck1ng idiot")); 53 | Assertions.assertFalse(filterer.isAllowed(dummyPlayer, "Shut the h3ll up")); 54 | } 55 | 56 | @Test 57 | public void givenScunthorpe_testIsNotProfane() { 58 | final OnlineUser dummyPlayer = new TestOnlineUser(); 59 | Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "Scunthorpe")); 60 | } 61 | 62 | @Test 63 | public void givenLeetSpeak_testIsNotProfane() { 64 | final OnlineUser dummyPlayer = new TestOnlineUser(); 65 | Assertions.assertTrue(filterer.isAllowed(dummyPlayer, "1337")); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/src/test/java/net/william278/huskchat/user/TestOnlineUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import net.kyori.adventure.audience.Audience; 23 | import org.jetbrains.annotations.NotNull; 24 | import org.jetbrains.annotations.Nullable; 25 | 26 | import java.util.UUID; 27 | 28 | public class TestOnlineUser extends OnlineUser { 29 | 30 | private final static int TEST_PLAYER_PING = 5; 31 | private final static String TEST_PLAYER_SERVER = "test"; 32 | private final static int TEST_PLAYER_SERVER_PLAYER_COUNT = 1; 33 | 34 | public TestOnlineUser() { 35 | super(UUID.randomUUID().toString().split("-")[0], UUID.randomUUID()); 36 | } 37 | 38 | @Override 39 | public int getPing() { 40 | return TEST_PLAYER_PING; 41 | } 42 | 43 | @Override 44 | @NotNull 45 | public String getServerName() { 46 | return TEST_PLAYER_SERVER; 47 | } 48 | 49 | @Override 50 | public int getPlayersOnServer() { 51 | return TEST_PLAYER_SERVER_PLAYER_COUNT; 52 | } 53 | 54 | @Override 55 | public boolean hasPermission(@Nullable String permission, boolean allowByDefault) { 56 | return true; 57 | } 58 | 59 | @NotNull 60 | @Override 61 | public Audience getAudience() { 62 | return Audience.empty(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/Backend-Chat-Entry.md: -------------------------------------------------------------------------------- 1 | > **Note:** The information of this page does not apply to those running HuskChat on a single-server Spigot setup. 2 | 3 | This page provides information on using HuskChat on the proxy with backend plugins or mods that rely on using chat for data entry. 4 | 5 | ## Background 6 | Plugins that rely on users entering things in chat to set names are not using the chat API in the way it was intended. Data entry should be done through commands, not through chat. Especially with the arrival of Minecraft 1.19.1, plugins should move away from relying on this (even if it is convenient). 7 | 8 | Anvil menus, command entry, sign menus, and other methods of data entry should be used. Some plugins, like *QuickShopReremake* provide command alternatives (`/i `) for data entry for this reason exactly. 9 | 10 | ## Things to know 11 | * HuskChat is a proxy plugin. It sits on your proxy (Bungeecord, Waterfall, Velocity, etc) - *not* your "backend" (Spigot, Fabric, etc.) server. This means that when someone types something into chat, HuskChat processes it on behalf of the server the player is connected to; HuskChat on your proxy won't let that message be passed on to the backend server. This means that plugins that rely on chat input won't work. 12 | * However, there is a workaround. HuskChat lets you set the "broadcast scope" of a channel, which affects who sees the chat message sent by players. HuskChat provides three special scopes, ideal for handling situations such as this—`passthrough`, `local_passthrough` and `global_passthrough`. 13 | 1. Create a channel with a `passthrough` scope. 14 | 2. Define a convenient shortcut command for this channel (`/i`, for instance) 15 | 3. Install [CancelChat](https://github.com/WiIIiam278/CancelChat/releases) (Spigot) or [CancelChat Fabric](https://modrinth.com/mod/cancelchat-fabric) - (it's super light weight, don't worry) on your backend. If you don't do this, then chat messages will be broadcast to players as your backend will process and dispatch the message as it normally would. 16 | 4. Save your config and restart the server. When prompted to enter data, switch to the channel using `/i` and enter the data. Players can then change their channel back as they normally would. 17 | 5. See also: [Channel Specification](https://william278.net/docs/huskchat/Channels) -------------------------------------------------------------------------------- /docs/Commands.md: -------------------------------------------------------------------------------- 1 | HuskChat provides a number of commands, for switching channels, sending broadcasts and messaging players and groups of players. Channels can also be restricted behind send and receive permission nodes. 2 | 3 | ## List of Commands 4 | | Command | Usage | Aliases | Description | Permission | 5 | |-------------------|------------------------------|----------------------------------------------------|------------------------------------------------------------------|-------------------------------------------------------------| 6 | | `/channel` | `/channel [message]` | `/c` | Send a message or switch to a chat channel | `huskchat.command.channel` | 7 | | `/huskchat` | `/huskchat ` | N/A | View plugin information and reload | `huskchat.command.huskchat` | 8 | | `/msg` | `/msg ` | `/m`, `/tell`, `/w`, `/whisper`, `/message`, `/pm` | Send a private message to a player | `huskchat.command.msg` | 9 | | `/reply` | `/reply ` | `/r` | Quickly reply to a private message | `huskchat.command.msg.reply` | 10 | | `/socialspy` | `/socialspy [color]` | `/ss` | Lets you view other users' private messages | `huskchat.command.socialspy` | 11 | | `/localspy` | `/localspy [color]` | `/ls` | Lets you view messages sent in other local chat channels† | `huskchat.command.localspy` | 12 | | `/broadcast` | `/broadcast ` | `/alert` | Lets you send a broadcast across the server | `huskchat.command.broadcast` | 13 | | `/optoutmsg` | `/optoutmsg` | N/A | Lets you "opt-out" of a group private message you are in | `huskchat.command.optoutmsg` | 14 | | Shortcut commands | `/ ` | N/A | Quickly send a message in or switch to a chat channel | Channel send permission, e.g. `huskchat.channel.staff.send` | 15 | 16 | † `/localspy` is not available on single-server Spigot setups. 17 | 18 | ## Channel send and receive permissions 19 | Channels also have their own permission to send and receive to. 20 | 21 | You can configure these in the channel config file, but by default they are `huskchat.channel..receive`. Channels without permissions set do not require the permission node to talk in. 22 | 23 | ## Chat formatting permissions 24 | Formatting messages also has its own permission to allow users to use minedown. 25 | 26 | You can apply the node, `huskchat.formatted_chat`, to allow players to format there messages with any Minedown formatting. Any users attempting to use formatting without the node will simply send the format in chat as a normal message, having the format in the configuration file be used instead. -------------------------------------------------------------------------------- /docs/Discord-Hook.md: -------------------------------------------------------------------------------- 1 | HuskChat supports displaying messages on Discord, either through webhooks for one-way communication or by using Spicord for two-way communication (some setup required). 2 | 3 | To enable Discord support, set `enabled` to `true` in the `discord:` section of [`config.yml`](config-files). You can then configure the bot and channel webhooks. 4 | 5 | ## Webhooks 6 | Webhooks are a simple way to send messages to a Discord channel. You can create a webhook for a channel by going to the channel settings, then Integrations, then Webhooks. You can then create a webhook and copy the URL. In the HuskChat config, you can then add the webhook URL to the `channel_webhooks` section of the config. 7 | 8 | ## Spicord 9 | Spicord is a plugin that allows for two-way communication between Discord and Minecraft. You can find more information about Spicord [here](https://www.spigotmc.org/resources/spicord.64918/). Spicord can be installed on BungeeCord, Velocity, or Paper. 10 | 11 | > **Why not support DiscordSRV?** DiscordSRV does not support Velocity/Bungee, and Spicord does :-) 12 | 13 | ### Installing Spicord 14 | 1. Download the Spicord plugin and place it in the plugins folder of your server alongside HuskChat. 15 | 2. Start the server. 16 | 3. Open the config.toml file located in the `plugins/Spicord` directory using a text editor. 17 | * Insert your bot token in the designated field (see below how to get a token) 18 | * Change the value of the enabled option to true 19 | * Add `huskchat` to the `addons` section of your bot 20 | 4. Restart your server. 21 | 22 | Your Spicord `config.toml` file should contain a bot like this: 23 | ```toml 24 | name = "Server Chat" 25 | enabled = true 26 | token = "[YOUR TOKEN]" 27 | command_support = true 28 | command_prefix = "-" 29 | addons = [ 30 | "spicord::info", 31 | "spicord::plugins", 32 | "spicord::players", 33 | "huskchat" 34 | ] 35 | ``` 36 | 37 | ### Creating a bot 38 | Here's how to create a bot and add it to your Discord server (Taken from [Spicord's documentation](https://github.com/Spicord/Spicord/blob/v5/tutorial/CREATE-A-BOT.md)): 39 | 40 | 1. Log-in into the [Discord Developer Portal](https://discord.com/developers/applications) 41 | 2. Click **New Application** and choose a name for your bot 42 | 3. You will see the information of your application and you will need to copy the numbers below **Client ID**, you will need it to invite your bot to your server 43 | 4. Switch to the **Bot** tab located at the left of the page, and then click **Add Bot > Yes, do it!** 44 | 5. You will see your bot information and there you can change its profile picture and name 45 | 6. Click the **Copy** button below the **Token** section, you will need to put it in the Spicord configuration 46 | 7. To invite your bot, go to `https://discord.com/oauth2/authorize?scope=bot&permissions=8&client_id=YOUR_ID` but before replace `YOUR_ID` with the ID you copied in the 3rd step, this will generate the invite url for your bot and redirect you to it, the generated url will make your bot have Administrator permission 47 | 48 | Note that you are required to enable the following gateway intents for your bot on the developer panel, otherwise it won't boot up: 49 | ![Gateway intents](https://raw.githubusercontent.com/WiIIiam278/HuskChat/master/images/spicord-bot-intents.png) 50 | 51 | ### Configuring HuskChat 52 | Once you have created your bot and invited it to your server, you can configure HuskChat to use it. In the `discord:` section of the config, set `enabled` to `true` and `spicord.enabled` to `true`. You can then configure the bot and channel IDs. 53 | 54 | To get the ID of a channel, ensure Developer Mode is enabled in your Discord settings, then right click a channel and select "Copy ID". All you need to do then is paste the ID into the config and map them to the corresponding in-game channel! 55 | 56 | Restart your server, and enjoy! 57 | -------------------------------------------------------------------------------- /docs/Formatting.md: -------------------------------------------------------------------------------- 1 | The channel `format` defines how messages sent on that channel will be formatted. The content of the message sent by the player itself is always appended after the formatting. Note that any uncleared formatting will persist and apply to the message contents. The format of private messages, group private messages and broadcasts can also be customized. 2 | 3 | ## MineDown 4 | You can make use of [MineDown formatting](https://github.com/Phoenix616/MineDown) to use modern (1.16+) hexadecimal colors and easily 5 | make use of advanced text effects such as gradients. You can embed this formatting within the prefix and suffix contents of LuckPerms groups as well. 6 | 7 | ## Placeholders 8 | Within channel formats you can make use of the following placeholders, which will be replaced with the formatted text. 9 | 10 | ### Regular placeholders 11 | * `%name%` - Username 12 | * `%full_name%` - Role prefix, player username & role suffix 13 | * `%prefix%` - Role prefix 14 | * `%suffix%` - Role suffix 15 | * `%role%` - User's (primary) group name 16 | * `%role_display_name%` - User's (primary) group display name 17 | * `%ping%` - User's ping 18 | * `%uuid%` - User's UUID 19 | * `%server%` - Server the user is on (on single-server setups, this is always `server`) 20 | * `%local_players_online%` - Number of players on the server the user is on 21 | 22 | ### Time placeholders 23 | These display the current system time. 24 | * `%timestamp%` - yyyy/MM/dd HH:mm:ss 25 | * `%current_time%` - HH:mm:ss 26 | * `%current_time_short%` - HH:mm 27 | * `%current_date%` - yyyy/MM/dd 28 | * `%current_date_uk%` - dd/MM/yyyy 29 | * `%current_date_day%` - dd 30 | * `%current_date_month%` - MM 31 | * `%current_date_year%` - yyyy 32 | 33 | ### PlaceholderAPI support 34 | By installing [PAPIProxyBridge](https://modrinth.com/plugin/papiproxybridge) on both your proxy (Bungee or Velocity) and backend (Paper or Fabric) servers, you can use PlaceholderAPI placeholders in channel and message formats. 35 | 36 | ### Private message placeholders 37 | In private messages, the placeholders are applied to the sender of the message for inbound messages, and the receiver for outbound messages. You can use all placeholders above after the `sender_` or `receiver_` prefix. (e.g. `%sender_(placeholder)%` and `%receiver_(placeholder)%`). 38 | 39 | There are additional placeholders for group private messages: 40 | * `%group_amount%` (number of members in the group private message) 41 | * `%group_amount_subscript%` (number of members in the group private message, in subscript font) 42 | * `%group_members_comma_separated%` (comma separated list of members in the group private message) 43 | * `%group_members%` (newline separated list of members in the group private message) 44 | 45 | The social spy message formatting lets you format both the message sender and receiver with the same placeholders listed above. The sender and receiver are disambiguated with prefixes, so you can use both; i.e. `%sender_(placeholder)%` and `%receiver_(placeholder)%`. -------------------------------------------------------------------------------- /docs/Group-Messages.md: -------------------------------------------------------------------------------- 1 | Group Messages are a unique feature of HuskChat that lets you send messages to a group of players, instead of just a single player. This is really useful for parties of players working together on group projects. 2 | 3 | ## Configuring Group Messages 4 | To enable Group Messages, ensure the `group_messages` feature is enabled in the `message_command` section of the config.yml file. You can also set a maximum number of players that can be included in a group message, to prevent players from messaging the entire server! 5 | 6 | 7 |
8 | Config.yml 9 | 10 | ```yaml 11 | group_messages: # Whether to allow sending and replying to a message in a group (/msg User1,User2 ) 12 | enabled: true 13 | max_size: 10 14 | ``` 15 |
16 | 17 | ## Sending and replying in a group 18 | To send a group message, simply use the `/msg` command with a list of players to send to, separated by commas. For example, `/msg User1,User2,User3 Hello!` will send the message `Hello!` to `User1`, `User2` and `User3`. The message sent in chat will have a small (`[+N]`) postfixed to the recipient name to indicate how many players are in the group. Hover over the name to see the full list of players in the group, and click the name to paste the full `/msg` command into your chat window. 19 | 20 | The `/r` (reply) command also supports replying to a group message. If you receive a group message from `User1`, `User2` and `User3`, you can reply to all of them by using `/r Hello!`. 21 | 22 | If you're stuck in a long or spammy chain of /r messages, you can use `/optoutmsg` to silently leave the conversation. -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | # [![HuskChat banner](https://raw.githubusercontent.com/WiIIiam278/HuskChat/master/images/banner.png)](https://github.com/WiIIiam278/HuskChat) 2 | Welcome! This is the plugin documentation for HuskChat—the no-frills chat plugin for Minecraft: Java Edition servers and networks. Please click through to the topic you'd like to read about. 3 | 4 | ## Guides 5 | * 📚 [[Setup]] 6 | * 📝 [[Config Files]] 7 | * 🖥️ [[Commands]] 8 | 9 | ## Documentation 10 | * 📺 [[Channels]] 11 | * ✍️ [[Formatting]] 12 | * ✉️ [[Group Messages]] 13 | * 🤫 [[Filters and Replacers]] 14 | * 🔎 [[Social and Local Spy]] 15 | * ⚙️ [[Backend Chat Entry]] 16 | * 📜 [[Join and Quit Messages]] 17 | * 🔵 [[Discord Hook]] 18 | 19 | ## Links 20 | * 💻 [GitHub](https://github.com/WiIIiam278/HuskChat) 21 | * 📂 [Downloads](https://www.spigotmc.org/resources/huskchat.94496/) 22 | * 🚰 [Spigot](https://www.spigotmc.org/resources/huskchat.94496/) 23 | * 🛒 [Polymart](https://polymart.org/resource/huskchat.1217) 24 | * 🔧 [Modrinth](https://modrinth.com/plugin/huskchat) 25 | * 🛫 [Hangar](https://hangar.papermc.io/William278/HuskChat) 26 | * 💬 [Discord Support](https://discord.gg/tVYhJfyDWG) -------------------------------------------------------------------------------- /docs/Join-and-Quit-Messages.md: -------------------------------------------------------------------------------- 1 | HuskChat supports displaying special **join and quit messages** whenever a player joins/leaves your network (or single-server setup when installing the plugin standalone). 2 | 3 | ## Usage 4 | To enable this feature, set `join_and_quit_messages.join.enabled` and/or `join_and_quit_messages.quit.enabled` to `true` in your `config.yml` file. You can then modify the `format` of either, which accepts placeholders and standard MineDown formatting. 5 | 6 | ### Broadcast scopes 7 | You can set the `broadcast_scope` of join and quit messages in a similar fashion to how you can do this for [[Channels]]. See [Broadcast Scopes](channels#channel-scope) for more details on the available scopes. 8 | 9 | Note that global, local and regular PASSTHROUGH scopes are only effective when running the plugin on a standalone Spigot/Paper server; when running HuskChat on a proxy (Velocity/Bungee) server, the _regular join/leave message won't be cancelled_. This is because the join/leave message is handled on the backend. 10 | 11 |
12 | Example config.yml 13 | 14 | ```yaml 15 | # Options for customizing player join and quit messages 16 | join_and_quit_messages: 17 | join: 18 | enabled: false 19 | # Use the huskchat.join_message.[text] permission to override this per-group if needed 20 | format: '&e%name% joined the network' 21 | quit: 22 | enabled: false 23 | # Use the huskchat.quit_message.[text] permission to override this per-group if needed 24 | format: '&e%name% left the network' 25 | broadcast_scope: GLOBAL # Note that on Velocity/Bungee, PASSTHROUGH modes won't cancel local join/quit messages 26 | ``` 27 |
28 | 29 | ## Permission-based formats 30 | You can set specific join/quit messages for specific groups by using the `huskchat.join_message.[text]` and `huskchat.quit_message.[text]` permissions. For example, if you wanted to set a special join message for players with the `vip` group, you could give them the `huskchat.join_message.&a%name% has arrived with style!` permission node to display a different join message. -------------------------------------------------------------------------------- /docs/Setup.md: -------------------------------------------------------------------------------- 1 | This will walk you through installing HuskChat on your Velocity or BungeeCord/Waterfall-based proxy server. 2 | 3 | ## Requirements 4 | * Java 17+ 5 | * A Spigot (1.17.1+) Minecraft server (for single-server setups) OR a Velocity (recommended) or BungeeCord/Waterfall-based proxy server 6 | 7 | > **Warning:** The Spigot plugin "NoChatReports" will not work with HuskChat. Use FreedomChat instead if you wish to eliminate chat report warnings. 8 | 9 | ## Single-server Setup Instructions 10 | 1. Turn off your Spigot server 11 | 2. Download the [latest version of HuskChat](https://github.com/WiIIiam278/HuskChat/releases/latest). 12 | 3. Add the jar file to your Spigot server's `~/plugins` folder 13 | 4. Start your Spigot server. Let HuskChat generate its [[config files]], then stop the server again. 14 | 5. Modify your HuskChat [[config files]] as needed, then start your Spigot server again. 15 | 16 | ## Multi-server Setup Instructions 17 | ### Velocity Installation 18 | 1. Turn off your Velocity proxy 19 | 2. Download the [latest version of HuskChat](https://github.com/WiIIiam278/HuskChat/releases/latest). 20 | 3. Add the jar file to your Velocity proxy server's `~/plugins` folder. 21 | 4. Start your Velocity proxy. Let HuskChat generate its [[config files]], then stop the proxy again. 22 | 5. Modify your HuskChat config files as needed, then start your Velocity proxy again. 23 | 24 | ### BungeeCord/Waterfall Installation 25 | > **Warning:** Waterfall is deprecated. Velocity is recommended as the next-generation proxy server software. You may experience issues when using HuskChat with Modern (post-1.19) Minecraft clients on your network. 26 | 27 | 1. Turn off your BungeeCord/Waterfall proxy 28 | 2. Download the [latest version of HuskChat](https://github.com/WiIIiam278/HuskChat/releases/latest). 29 | 3. Add the jar file to your BungeeCord/Waterfall proxy server's `~/plugins` folder 30 | 4. Start your BungeeCord/Waterfall proxy. Let HuskChat generate its config files, then stop the proxy again. 31 | 5. Modify your HuskChat [[config files]] as needed, then start your BungeeCord/Waterfall proxy again. 32 | 33 | ## Next Steps 34 | * [[Channels]] 35 | * [[Formatting]] 36 | * [[Commands]] 37 | -------------------------------------------------------------------------------- /docs/Social-and-Local-Spy.md: -------------------------------------------------------------------------------- 1 | HuskChat provides `/socialspy` and `/localspy` commands. Players with permission can toggle `socialspy` to see the 2 | private messages of users. 3 | 4 | On servers running HuskChat on proxy-server setups, the `/localspy` command is also available. This allows players with permission to view messages sent in Local chat channels on other servers, including ones that passthrough messages locally. 5 | 6 | ## Bypassing Social Spy 7 | If you have the `huskchat.command.socialspy.bypass` permission, you can bypass having your messages be spied on. Note though that other players with the `huskchat.command.socialspy.bypass` permission bypass the bypass and will be able to see your private messages. -------------------------------------------------------------------------------- /docs/Translations.md: -------------------------------------------------------------------------------- 1 | HuskChat supports a number of community-sourced translations of the plugin locales into different languages. The default language is [`en-gb`](https://github.com/WiIIiam278/HuskChat/blob/master/common/src/main/resources/locales/en-gb.yml) (English). The messages file is formatted using [MineDown](https://github.com/Phoenix616/MineDown). 2 | 3 | You can change which preset language option to use by changing the top-level `language` setting in the plugin config.yml file. You must change this to one of the supported language codes. You can [view a list of the supported languages](https://github.com/WiIIiam278/HuskChat/tree/master/common/src/main/resources/locales) by looking at the locales source folder. 4 | 5 | ## Contributing Locales 6 | You can contribute locales by submitting a pull request with a yaml file containing translations of the [default locales](https://github.com/WiIIiam278/HuskChat/blob/master/common/src/main/resources/locales/en-gb.yml) into your language. Here's a few pointers for doing this: 7 | * Do not translate the locale keys themselves (e.g. `channel_switched`) 8 | * Your pull request should be for a file in the [locales folder](https://github.com/WiIIiam278/HuskChat/tree/master/common/src/main/resources/locales) 9 | * Do not translate the [MineDown](https://github.com/Phoenix616/MineDown) syntax itself or commands and their parameters; only the english interface text 10 | * Each locale should be on one line, and the header should be removed. 11 | * Use the correct ISO 639-1 [locale code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language and dialect 12 | * If you are able to, you can add your name to the `AboutMenu` translators credit list yourself, otherwise this can be done for you 13 | 14 | Thank you for your interest in making HuskChat more accessible around the world! 15 | -------------------------------------------------------------------------------- /docs/_Footer.md: -------------------------------------------------------------------------------- 1 | | This documentation is available via [william278.net](https://william278.net/docs/huskchat) | 2 | |--------------------------------------------------------------------------------------------| -------------------------------------------------------------------------------- /docs/_Sidebar.md: -------------------------------------------------------------------------------- 1 | ## Guides 2 | * 📚 [[Setup]] 3 | * 📝 [[Config Files]] 4 | * 🖥️ [[Commands]] 5 | 6 | ## Documentation 7 | * 📺 [[Channels]] 8 | * ✍️ [[Formatting]] 9 | * ✉️ [[Group Messages]] 10 | * 🤫 [[Filters and Replacers]] 11 | * 🔎 [[Social and Local Spy]] 12 | * ⚙️ [[Backend Chat Entry]] 13 | * 📜 [[Join and Quit Messages]] 14 | * 🔵 [[Discord Hook]] 15 | 16 | ## Links 17 | * 💻 [GitHub](https://github.com/WiIIiam278/HuskChat) 18 | * 📂 [Downloads](https://www.spigotmc.org/resources/huskchat.94496/) 19 | * 🚰 [Spigot](https://www.spigotmc.org/resources/huskchat.94496/) 20 | * 🛒 [Polymart](https://polymart.org/resource/huskchat.1217) 21 | * 🔧 [Modrinth](https://modrinth.com/plugin/huskchat) 22 | * 🛫 [Hangar](https://hangar.papermc.io/William278/HuskChat) 23 | * 💬 [Discord Support](https://discord.gg/tVYhJfyDWG) -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs='-Dfile.encoding=UTF-8' 2 | 3 | org.gradle.parallel=true 4 | org.gradle.daemon=true 5 | javaVersion=17 6 | 7 | plugin_version=3.0.4 8 | plugin_archive=huskchat 9 | plugin_description=A simple & customizable no-frills Minecraft chat system 10 | 11 | velocity_api_version=3.3.0 12 | velocity_minimum_build=329 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WiIIiam278/HuskChat/d31180b0c127ace47e33848d82ad1c533fe9de35/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WiIIiam278/HuskChat/d31180b0c127ace47e33848d82ad1c533fe9de35/images/banner.png -------------------------------------------------------------------------------- /images/spicord-bot-intents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WiIIiam278/HuskChat/d31180b0c127ace47e33848d82ad1c533fe9de35/images/spicord-bot-intents.png -------------------------------------------------------------------------------- /paper/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'xyz.jpenilla.run-paper' version '2.3.0' 3 | } 4 | 5 | dependencies { 6 | implementation project(':bukkit') 7 | compileOnly project(':common') 8 | 9 | compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT' 10 | compileOnly 'org.bstats:bstats-bukkit:3.0.2' 11 | compileOnly 'org.jetbrains:annotations:24.1.0' 12 | } 13 | 14 | shadowJar { 15 | relocate 'net.william278.profanitycheckerapi', 'net.william278.huskchat.libraries.profanitycheckerapi' 16 | relocate 'net.william278.desertwell', 'net.william278.huskchat.libraries.desertwell' 17 | relocate 'de.themoep', 'net.william278.huskchat.libraries' 18 | relocate 'dev.vankka', 'net.william278.huskchat.libraries' 19 | relocate 'de.exlll', 'net.william278.huskchat.libraries' 20 | relocate 'org.snakeyaml.engine', 'net.william278.huskchat.libraries.snakeyaml.engine' 21 | relocate 'org.json', 'net.william278.huskchat.libraries.json' 22 | 23 | relocate 'org.apache', 'net.william278.huskchat.libraries' 24 | relocate 'org.jetbrains', 'net.william278.huskchat.libraries' 25 | relocate 'org.intellij', 'net.william278.huskchat.libraries' 26 | relocate 'org.bstats', 'net.william278.huskchat.libraries.bstats' 27 | relocate 'space.arim', 'net.william278.huskchat.libraries.morepaperlib' 28 | 29 | minimize() 30 | } 31 | 32 | tasks { 33 | runServer { 34 | minecraftVersion('1.20.4') 35 | 36 | downloadPlugins { 37 | url("https://download.luckperms.net/1526/bukkit/loader/LuckPerms-Bukkit-5.4.113.jar") 38 | url("https://ci.lucko.me/job/spark/399/artifact/spark-bukkit/build/libs/spark-1.10.58-bukkit.jar") 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /paper/src/main/java/net/william278/huskchat/PaperHuskChat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat; 21 | 22 | import net.kyori.adventure.audience.Audience; 23 | import org.bukkit.entity.Player; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.UUID; 27 | 28 | public class PaperHuskChat extends BukkitHuskChat { 29 | 30 | @NotNull 31 | @Override 32 | public Audience getAudience(@NotNull UUID user) { 33 | final Player player = getServer().getPlayer(user); 34 | return player == null || !player.isOnline() ? Audience.empty() : player; 35 | } 36 | 37 | @Override 38 | @NotNull 39 | public Audience getConsole() { 40 | return getServer().getConsoleSender(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /paper/src/main/resources/paper-plugin.yml: -------------------------------------------------------------------------------- 1 | name: 'HuskChat' 2 | description: '${description}' 3 | author: 'William278' 4 | website: 'https://william278.net/' 5 | main: 'net.william278.huskchat.PaperHuskChat' 6 | version: '${version}' 7 | api-version: '1.19' 8 | folia-supported: true 9 | dependencies: 10 | server: 11 | LuckPerms: 12 | load: BEFORE 13 | join-classpath: true 14 | required: false 15 | PlaceholderAPI: 16 | load: BEFORE 17 | join-classpath: true 18 | required: false 19 | Spicord: 20 | load: BEFORE 21 | join-classpath: true 22 | required: false -------------------------------------------------------------------------------- /plugin/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(path: ':bungee', configuration: 'shadow') 3 | implementation project(path: ':velocity', configuration: 'shadow') 4 | implementation project(path: ':bukkit', configuration: 'shadow') 5 | implementation project(path: ':paper', configuration: 'shadow') 6 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | 7 | rootProject.name = 'HuskChat' 8 | 9 | include( 10 | 'common', 11 | 'bungee', 12 | 'velocity', 13 | 'bukkit', 14 | 'paper', 15 | 'plugin' 16 | ) -------------------------------------------------------------------------------- /velocity/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'xyz.jpenilla.run-velocity' version '2.3.0' 3 | } 4 | 5 | dependencies { 6 | implementation project(path: ':common') 7 | implementation 'org.bstats:bstats-velocity:3.0.2' 8 | 9 | compileOnly "com.velocitypowered:velocity-api:${velocity_api_version}-SNAPSHOT" 10 | compileOnly "com.velocitypowered:velocity-proxy:${velocity_api_version}-SNAPSHOT" 11 | 12 | compileOnly 'io.netty:netty-codec-http:4.1.112.Final' 13 | compileOnly 'it.unimi.dsi:fastutil:8.5.14' 14 | compileOnly 'commons-io:commons-io:2.16.1' 15 | compileOnly 'de.themoep:minedown-adventure:1.7.3-SNAPSHOT' 16 | compileOnly 'org.jetbrains:annotations:24.1.0' 17 | compileOnly 'org.projectlombok:lombok:1.18.34' 18 | compileOnly 'net.kyori:adventure-nbt:4.17.0' 19 | 20 | annotationProcessor 'org.projectlombok:lombok:1.18.34' 21 | } 22 | 23 | shadowJar { 24 | relocate 'net.william278.profanitycheckerapi', 'net.william278.huskchat.libraries.profanitycheckerapi' 25 | relocate 'net.william278.desertwell', 'net.william278.huskchat.libraries.desertwell' 26 | relocate 'de.themoep', 'net.william278.huskchat.libraries' 27 | relocate 'dev.vankka', 'net.william278.huskchat.libraries' 28 | relocate 'de.exlll', 'net.william278.huskchat.libraries' 29 | relocate 'org.snakeyaml.engine', 'net.william278.huskchat.libraries.snakeyaml.engine' 30 | relocate 'org.json', 'net.william278.huskchat.libraries.json' 31 | 32 | relocate 'org.apache', 'net.william278.huskchat.libraries' 33 | relocate 'org.jetbrains', 'net.william278.huskchat.libraries' 34 | relocate 'org.intellij', 'net.william278.huskchat.libraries' 35 | relocate 'org.bstats', 'net.william278.huskchat.libraries.bstats' 36 | 37 | dependencies { 38 | //noinspection GroovyAssignabilityCheck 39 | exclude dependency(':slf4j-api') 40 | } 41 | 42 | minimize() 43 | } 44 | 45 | tasks { 46 | runVelocity { 47 | velocityVersion("${velocity_api_version}-SNAPSHOT") 48 | } 49 | } -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/api/VelocityHuskChatAPI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.api; 21 | 22 | import com.velocitypowered.api.proxy.Player; 23 | import net.william278.huskchat.HuskChat; 24 | import net.william278.huskchat.VelocityHuskChat; 25 | import net.william278.huskchat.user.VelocityUser; 26 | import org.jetbrains.annotations.ApiStatus; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | @SuppressWarnings("unused") 30 | public class VelocityHuskChatAPI extends HuskChatAPI { 31 | 32 | private VelocityHuskChatAPI(@NotNull HuskChat plugin) { 33 | super(plugin); 34 | } 35 | 36 | @NotNull 37 | public static VelocityHuskChatAPI getInstance() { 38 | return (VelocityHuskChatAPI) instance; 39 | } 40 | 41 | /** 42 | * @hidden 43 | */ 44 | @ApiStatus.Internal 45 | public static void register(@NotNull VelocityHuskChat plugin) { 46 | HuskChatAPI.instance = new VelocityHuskChatAPI(plugin); 47 | } 48 | 49 | /** 50 | * Adapts a platform-specific Player object to a cross-platform Player object 51 | * @param player Must be a platform-specific Player object, e.g. a Velocity Player 52 | * @return {@link VelocityUser} 53 | */ 54 | @NotNull 55 | public VelocityUser adaptPlayer(@NotNull Player player) { 56 | return VelocityUser.adapt(player, plugin); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/event/VelocityBroadcastMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public class VelocityBroadcastMessageEvent extends VelocityEvent implements BroadcastMessageEvent { 26 | private OnlineUser sender; 27 | private String message; 28 | 29 | public VelocityBroadcastMessageEvent(OnlineUser sender, String message) { 30 | this.sender = sender; 31 | this.message = message; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public OnlineUser getSender() { 37 | return sender; 38 | } 39 | 40 | @Override 41 | @NotNull 42 | public String getMessage() { 43 | return message; 44 | } 45 | 46 | @Override 47 | public void setSender(@NotNull OnlineUser sender) { 48 | this.sender = sender; 49 | } 50 | 51 | @Override 52 | public void setMessage(@NotNull String message) { 53 | this.message = message; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/event/VelocityChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public class VelocityChatMessageEvent extends VelocityEvent implements ChatMessageEvent { 26 | private OnlineUser sender; 27 | private String message; 28 | private String channelId; 29 | 30 | public VelocityChatMessageEvent(OnlineUser sender, String message, String channelId) { 31 | this.sender = sender; 32 | this.message = message; 33 | this.channelId = channelId; 34 | } 35 | 36 | @Override 37 | @NotNull 38 | public OnlineUser getSender() { 39 | return sender; 40 | } 41 | 42 | @Override 43 | @NotNull 44 | public String getMessage() { 45 | return message; 46 | } 47 | 48 | @Override 49 | @NotNull 50 | public String getChannelId() { 51 | return channelId; 52 | } 53 | 54 | @Override 55 | public void setSender(@NotNull OnlineUser sender) { 56 | this.sender = sender; 57 | } 58 | 59 | @Override 60 | public void setMessage(@NotNull String message) { 61 | this.message = message; 62 | } 63 | 64 | @Override 65 | public void setChannelId(@NotNull String channelId) { 66 | this.channelId = channelId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/event/VelocityEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import lombok.Getter; 23 | import lombok.Setter; 24 | 25 | @Getter 26 | @Setter 27 | public class VelocityEvent implements EventBase { 28 | 29 | private boolean cancelled = false; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/event/VelocityEventProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import com.velocitypowered.api.proxy.ProxyServer; 23 | import net.william278.huskchat.user.OnlineUser; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | import java.util.concurrent.CompletableFuture; 28 | 29 | public interface VelocityEventProvider extends EventProvider { 30 | 31 | @Override 32 | default CompletableFuture fireChatMessageEvent(@NotNull OnlineUser player, 33 | @NotNull String message, 34 | @NotNull String channelId) { 35 | return getProxyServer().getEventManager().fire(new VelocityChatMessageEvent(player, message, channelId)); 36 | } 37 | 38 | @Override 39 | default CompletableFuture firePrivateMessageEvent(@NotNull OnlineUser sender, 40 | @NotNull List receivers, 41 | @NotNull String message) { 42 | return getProxyServer().getEventManager().fire(new VelocityPrivateMessageEvent(sender, receivers, message)); 43 | } 44 | 45 | @Override 46 | default CompletableFuture fireBroadcastMessageEvent(@NotNull OnlineUser sender, 47 | @NotNull String message) { 48 | return getProxyServer().getEventManager().fire(new VelocityBroadcastMessageEvent(sender, message)); 49 | } 50 | 51 | @NotNull 52 | ProxyServer getProxyServer(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/event/VelocityPrivateMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.event; 21 | 22 | import net.william278.huskchat.user.OnlineUser; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.util.List; 26 | 27 | public class VelocityPrivateMessageEvent extends VelocityEvent implements PrivateMessageEvent { 28 | private OnlineUser sender; 29 | private List recipients; 30 | private String message; 31 | 32 | public VelocityPrivateMessageEvent(@NotNull OnlineUser sender, @NotNull List recipients, @NotNull String message) { 33 | this.sender = sender; 34 | this.recipients = recipients; 35 | this.message = message; 36 | } 37 | 38 | @Override 39 | @NotNull 40 | public OnlineUser getSender() { 41 | return sender; 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public List getRecipients() { 47 | return recipients; 48 | } 49 | 50 | @NotNull 51 | @Override 52 | public String getMessage() { 53 | return message; 54 | } 55 | 56 | @Override 57 | public void setSender(@NotNull OnlineUser sender) { 58 | this.sender = sender; 59 | } 60 | 61 | @Override 62 | public void setRecipients(@NotNull List recipients) { 63 | this.recipients = recipients; 64 | } 65 | 66 | @Override 67 | public void setMessage(@NotNull String message) { 68 | this.message = message; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/listener/VelocityChatListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import com.velocitypowered.api.event.player.PlayerChatEvent; 23 | import net.william278.huskchat.HuskChat; 24 | import net.william278.huskchat.channel.Channel; 25 | import net.william278.huskchat.message.ChatMessage; 26 | import net.william278.huskchat.user.VelocityUser; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.Optional; 30 | 31 | public interface VelocityChatListener { 32 | 33 | default boolean handlePlayerChat(PlayerChatEvent e) { 34 | final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin()); 35 | final Optional channel = plugin().getUserCache().getPlayerChannel(player.getUuid()) 36 | .flatMap(channelId -> plugin().getChannels().getChannel(channelId)); 37 | if (channel.isEmpty()) { 38 | plugin().getLocales().sendMessage(player, "error_no_channel"); 39 | return false; 40 | } 41 | 42 | // Send the chat message, determine if the event should be canceled 43 | return !new ChatMessage(channel.get(), player, e.getMessage(), plugin()).dispatch(); 44 | } 45 | 46 | @NotNull 47 | HuskChat plugin(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/listener/VelocityEventChatListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import com.velocitypowered.api.event.PostOrder; 23 | import com.velocitypowered.api.event.Subscribe; 24 | import com.velocitypowered.api.event.player.PlayerChatEvent; 25 | import net.william278.huskchat.HuskChat; 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | public record VelocityEventChatListener(@NotNull HuskChat plugin) implements VelocityChatListener { 29 | 30 | @Subscribe(order = PostOrder.LATE) 31 | public void onPlayerChat(PlayerChatEvent e) { 32 | if (!e.getResult().isAllowed()) { 33 | return; 34 | } 35 | if (!this.handlePlayerChat(e)) { 36 | e.setResult(PlayerChatEvent.ChatResult.denied()); 37 | } 38 | } 39 | 40 | @Override 41 | @NotNull 42 | public HuskChat plugin() { 43 | return plugin; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/listener/VelocityPlayerListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.listener; 21 | 22 | import com.velocitypowered.api.event.Subscribe; 23 | import com.velocitypowered.api.event.connection.DisconnectEvent; 24 | import com.velocitypowered.api.event.player.ServerConnectedEvent; 25 | import net.william278.huskchat.HuskChat; 26 | import net.william278.huskchat.user.VelocityUser; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | public class VelocityPlayerListener extends PlayerListener { 30 | 31 | public VelocityPlayerListener(@NotNull HuskChat plugin) { 32 | super(plugin); 33 | } 34 | 35 | @Subscribe 36 | public void onPlayerChangeServer(ServerConnectedEvent e) { 37 | if (e.getPreviousServer().isEmpty()) { 38 | handlePlayerJoin(VelocityUser.adapt(e.getPlayer(), plugin)); 39 | } 40 | final String server = e.getServer().getServerInfo().getName(); 41 | final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin); 42 | handlePlayerSwitchServer(player, server); 43 | } 44 | 45 | @Subscribe 46 | public void onPlayerQuitNetwork(DisconnectEvent e) { 47 | if (e.getLoginStatus() == DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) { 48 | handlePlayerQuit(VelocityUser.adapt(e.getPlayer(), plugin)); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /velocity/src/main/java/net/william278/huskchat/user/VelocityUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskChat, licensed under the Apache License 2.0. 3 | * 4 | * Copyright (c) William278 5 | * Copyright (c) contributors 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package net.william278.huskchat.user; 21 | 22 | import com.velocitypowered.api.proxy.Player; 23 | import com.velocitypowered.api.proxy.ServerConnection; 24 | import net.kyori.adventure.audience.Audience; 25 | import net.kyori.adventure.util.TriState; 26 | import net.william278.huskchat.HuskChat; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import java.util.Optional; 31 | 32 | /** 33 | * Velocity implementation of a cross-platform {@link OnlineUser} 34 | */ 35 | public class VelocityUser extends OnlineUser { 36 | 37 | private final com.velocitypowered.api.proxy.Player player; 38 | 39 | private VelocityUser(@NotNull Player player, @NotNull HuskChat plugin) { 40 | super(player.getUsername(), player.getUniqueId(), plugin); 41 | this.player = player; 42 | } 43 | 44 | @NotNull 45 | public static VelocityUser adapt(@NotNull Player player, @NotNull HuskChat plugin) { 46 | return new VelocityUser(player, plugin); 47 | } 48 | 49 | @Override 50 | public int getPing() { 51 | return (int) player.getPing(); 52 | } 53 | 54 | @Override 55 | @NotNull 56 | public String getServerName() { 57 | final Optional connection = player.getCurrentServer(); 58 | if (connection.isPresent()) { 59 | return connection.get().getServerInfo().getName(); 60 | } 61 | return ""; 62 | } 63 | 64 | @Override 65 | public int getPlayersOnServer() { 66 | return player.getCurrentServer().map(conn -> conn.getServer().getPlayersConnected().size()).orElse(0); 67 | } 68 | 69 | @Override 70 | public boolean hasPermission(@Nullable String permission, boolean allowByDefault) { 71 | if (permission == null) { 72 | return allowByDefault; 73 | } 74 | final TriState state = player.getPermissionValue(permission).toAdventureTriState(); 75 | if (state == TriState.NOT_SET) { 76 | return allowByDefault; 77 | } 78 | return state == TriState.TRUE; 79 | } 80 | 81 | @NotNull 82 | @Override 83 | public Audience getAudience() { 84 | return player; 85 | } 86 | 87 | @NotNull 88 | public Player getPlayer() { 89 | return player; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /velocity/src/main/resources/velocity-metadata.yml: -------------------------------------------------------------------------------- 1 | velocity_api_version: '${velocity_api_version}' 2 | velocity_minimum_build: ${velocity_minimum_build} -------------------------------------------------------------------------------- /velocity/src/main/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "huskchat", 3 | "name": "HuskChat", 4 | "version": "${version}", 5 | "description": "${description}", 6 | "url": "https://william278.net", 7 | "authors": [ 8 | "William278" 9 | ], 10 | "dependencies": [ 11 | { 12 | "id": "unsignedvelocity", 13 | "optional": true 14 | }, 15 | { 16 | "id": "signedvelocity", 17 | "optional": true 18 | }, 19 | { 20 | "id": "luckperms", 21 | "optional": true 22 | }, 23 | { 24 | "id": "papiproxybridge", 25 | "optional": true 26 | }, 27 | { 28 | "id": "spicord", 29 | "optional": true 30 | } 31 | ], 32 | "main": "net.william278.huskchat.VelocityHuskChat" 33 | } --------------------------------------------------------------------------------