├── .github └── workflows │ └── codeql.yml ├── .gitignore ├── .scripts └── ensure-java-17 ├── LICENSE ├── README.md ├── build.gradle ├── docs ├── allclasses-index.html ├── allpackages-index.html ├── constant-values.html ├── copy.svg ├── dev │ └── unnm3d │ │ └── redischat │ │ ├── Permissions.html │ │ ├── RedisChat.html │ │ ├── UpdateCheck.html │ │ ├── api │ │ ├── DataManager.html │ │ ├── RedisChatAPI.html │ │ ├── TagResolverIntegration.html │ │ ├── VanishIntegration.html │ │ ├── events │ │ │ ├── AsyncRedisChatMessageEvent.html │ │ │ ├── ChannelGuiPopulateEvent.html │ │ │ ├── FilterEvent.html │ │ │ ├── MailDeleteEvent.html │ │ │ ├── MailEditorEvent.MailEditorState.html │ │ │ ├── MailEditorEvent.html │ │ │ ├── MailEvent.html │ │ │ ├── MailReadStatusChangeEvent.html │ │ │ ├── MailReceivedEvent.html │ │ │ ├── MailSendEvent.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── channels │ │ ├── ChannelCommand.html │ │ ├── ChannelManager.html │ │ ├── gui │ │ │ ├── ChannelGUI.html │ │ │ ├── GlobalChannel.html │ │ │ ├── PlayerChannel.Status.html │ │ │ ├── PlayerChannel.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── chat │ │ ├── ChatColorGUI.html │ │ ├── ChatFormat.html │ │ ├── ChatListener.html │ │ ├── ChatListenerWithPriority.html │ │ ├── ComponentProvider.html │ │ ├── JoinQuitManager.html │ │ ├── KnownChatEntities.html │ │ ├── PlaceholderManager.html │ │ ├── RedisChatPAPI.html │ │ ├── filters │ │ │ ├── AbstractFilter.Direction.html │ │ │ ├── AbstractFilter.html │ │ │ ├── DefaultSettings.html │ │ │ ├── FilterManager.html │ │ │ ├── FilterResult.html │ │ │ ├── incoming │ │ │ │ ├── IgnorePlayerFilter.IgnorePlayerFilterProperties.html │ │ │ │ ├── IgnorePlayerFilter.html │ │ │ │ ├── PermissionFilter.html │ │ │ │ ├── package-summary.html │ │ │ │ └── package-tree.html │ │ │ ├── outgoing │ │ │ │ ├── CapsFilter.CapsFilterProperties.html │ │ │ │ ├── CapsFilter.html │ │ │ │ ├── DuplicateFilter.DuplicateFilterProperties.html │ │ │ │ ├── DuplicateFilter.html │ │ │ │ ├── IgnoreFilter.html │ │ │ │ ├── MutedChannelFilter.html │ │ │ │ ├── SpamFilter.html │ │ │ │ ├── TagFilter.html │ │ │ │ ├── WordBlacklistFilter.WordBlacklistFilterProperties.html │ │ │ │ ├── WordBlacklistFilter.html │ │ │ │ ├── package-summary.html │ │ │ │ └── package-tree.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── objects │ │ │ ├── AudienceType.html │ │ │ ├── Channel.ChannelBuilder.html │ │ │ ├── Channel.html │ │ │ ├── ChannelAudience.ChannelAudienceBuilder.html │ │ │ ├── ChannelAudience.html │ │ │ ├── ChatMessage.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── commands │ │ ├── AnnounceCommand.html │ │ ├── BroadcastCommand.html │ │ ├── ChatAsCommand.html │ │ ├── ChatColorCommand.html │ │ ├── ClearChatCommand.html │ │ ├── IgnoreCommand.html │ │ ├── IgnoreWhitelistCommand.html │ │ ├── InvShareCommand.InventoryType.html │ │ ├── InvShareCommand.html │ │ ├── MainCommand.html │ │ ├── MsgCommand.html │ │ ├── PlayerListManager.html │ │ ├── ReplyCommand.html │ │ ├── SetItemCommand.html │ │ ├── SetPlaceholderCommand.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── datamanagers │ │ ├── DataKey.html │ │ ├── RedisDataManager.html │ │ ├── package-summary.html │ │ ├── package-tree.html │ │ ├── redistools │ │ │ ├── RedisAbstract.html │ │ │ ├── RoundRobinConnectionPool.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ └── sqlmanagers │ │ │ ├── MySQLDataManager.html │ │ │ ├── PluginMessageManager.html │ │ │ ├── SQLDataManager.html │ │ │ ├── SQLiteDataManager.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── discord │ │ ├── DiscordWebhook.html │ │ ├── IDiscordHook.html │ │ ├── SpicordHook.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── integrations │ │ ├── OraxenTagResolver.html │ │ ├── PremiumVanishIntegration.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── mail │ │ ├── Mail.MailCategory.html │ │ ├── Mail.html │ │ ├── MailCommand.html │ │ ├── MailGUIManager.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── moderation │ │ ├── MuteCommand.html │ │ ├── MuteManager.html │ │ ├── SpyChatCommand.html │ │ ├── SpyManager.html │ │ ├── StaffChatCommand.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── package-summary.html │ │ ├── package-tree.html │ │ ├── permission │ │ ├── LuckPermsProvider.html │ │ ├── PermissionProvider.html │ │ ├── VaultPermissionProvider.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── settings │ │ ├── Config.Announcement.html │ │ ├── Config.DataType.html │ │ ├── Config.Mysql.html │ │ ├── Config.RedisSettings.html │ │ ├── Config.SpicordSettings.html │ │ ├── Config.html │ │ ├── ConfigValidator.html │ │ ├── FiltersConfig.FilterSettings.html │ │ ├── FiltersConfig.html │ │ ├── GuiSettings.html │ │ ├── Messages.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── task │ │ ├── AnnouncerManager.html │ │ ├── AnnouncerTask.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ └── utils │ │ ├── AdventureWebuiEditorAPI.html │ │ ├── ItemNameProvider.html │ │ ├── Metrics.AdvancedBarChart.html │ │ ├── Metrics.AdvancedPie.html │ │ ├── Metrics.CustomChart.html │ │ ├── Metrics.DrilldownPie.html │ │ ├── Metrics.JsonObjectBuilder.JsonObject.html │ │ ├── Metrics.JsonObjectBuilder.html │ │ ├── Metrics.MetricsBase.html │ │ ├── Metrics.MultiLineChart.html │ │ ├── Metrics.SimpleBarChart.html │ │ ├── Metrics.SimplePie.html │ │ ├── Metrics.SingleLineChart.html │ │ ├── Metrics.html │ │ ├── package-summary.html │ │ └── package-tree.html ├── element-list ├── help-doc.html ├── index-files │ ├── index-1.html │ ├── index-10.html │ ├── index-11.html │ ├── index-12.html │ ├── index-13.html │ ├── index-14.html │ ├── index-15.html │ ├── index-16.html │ ├── index-17.html │ ├── index-18.html │ ├── index-19.html │ ├── index-2.html │ ├── index-20.html │ ├── index-21.html │ ├── index-22.html │ ├── index-23.html │ ├── index-3.html │ ├── index-4.html │ ├── index-5.html │ ├── index-6.html │ ├── index-7.html │ ├── index-8.html │ └── index-9.html ├── index.html ├── legal │ ├── COPYRIGHT │ ├── LICENSE │ ├── jquery.md │ └── jqueryUI.md ├── link.svg ├── member-search-index.js ├── module-search-index.js ├── overview-summary.html ├── overview-tree.html ├── package-search-index.js ├── resources │ ├── glass.png │ └── x.png ├── script-dir │ ├── jquery-3.6.1.min.js │ ├── jquery-ui.min.css │ └── jquery-ui.min.js ├── script.js ├── search-page.js ├── search.html ├── search.js ├── stylesheet.css ├── tag-search-index.js └── type-search-index.js ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── jitpack.yml ├── settings.gradle └── src ├── main ├── java │ └── dev │ │ └── unnm3d │ │ └── redischat │ │ ├── Permissions.java │ │ ├── RedisChat.java │ │ ├── UpdateCheck.java │ │ ├── api │ │ ├── DataManager.java │ │ ├── RedisChatAPI.java │ │ ├── TagResolverIntegration.java │ │ ├── VanishIntegration.java │ │ ├── events │ │ │ ├── AsyncRedisChatMessageEvent.java │ │ │ ├── ChannelGuiPopulateEvent.java │ │ │ ├── FilterEvent.java │ │ │ ├── MailDeleteEvent.java │ │ │ ├── MailEditorEvent.java │ │ │ ├── MailEvent.java │ │ │ ├── MailReadStatusChangeEvent.java │ │ │ ├── MailReceivedEvent.java │ │ │ └── MailSendEvent.java │ │ └── objects │ │ │ ├── AudienceType.java │ │ │ ├── Channel.java │ │ │ ├── ChannelAudience.java │ │ │ ├── ChatMessage.java │ │ │ └── KnownChatEntities.java │ │ ├── channels │ │ ├── ChannelCommand.java │ │ ├── ChannelManager.java │ │ └── gui │ │ │ ├── ChannelGUI.java │ │ │ ├── GlobalChannel.java │ │ │ └── PlayerChannel.java │ │ ├── chat │ │ ├── ChatColorGUI.java │ │ ├── ChatFormat.java │ │ ├── ComponentProvider.java │ │ ├── PlaceholderManager.java │ │ ├── RedisChatPAPI.java │ │ ├── filters │ │ │ ├── AbstractFilter.java │ │ │ ├── DefaultSettings.java │ │ │ ├── FilterManager.java │ │ │ ├── FilterResult.java │ │ │ ├── incoming │ │ │ │ ├── IgnorePlayerFilter.java │ │ │ │ └── PermissionFilter.java │ │ │ └── outgoing │ │ │ │ ├── CapsFilter.java │ │ │ │ ├── DuplicateFilter.java │ │ │ │ ├── IgnoreFilter.java │ │ │ │ ├── MutedChannelFilter.java │ │ │ │ ├── SpamFilter.java │ │ │ │ ├── TagFilter.java │ │ │ │ └── WordBlacklistFilter.java │ │ └── listeners │ │ │ ├── ChatListenerWithPriority.java │ │ │ ├── JoinQuitManager.java │ │ │ └── UtilsListener.java │ │ ├── commands │ │ ├── AnnounceCommand.java │ │ ├── BroadcastCommand.java │ │ ├── ChatAsCommand.java │ │ ├── ChatColorCommand.java │ │ ├── ClearChatCommand.java │ │ ├── IgnoreCommand.java │ │ ├── IgnoreWhitelistCommand.java │ │ ├── InvShareCommand.java │ │ ├── MainCommand.java │ │ ├── PlayerListManager.java │ │ ├── RedisChatCommand.java │ │ ├── SetItemCommand.java │ │ ├── SetPlaceholderCommand.java │ │ ├── TalkOnCommand.java │ │ ├── UniformMsgCommand.java │ │ └── UniformReplyCommand.java │ │ ├── datamanagers │ │ ├── DataKey.java │ │ ├── RedisDataManager.java │ │ ├── redistools │ │ │ ├── RedisAbstract.java │ │ │ └── RoundRobinConnectionPool.java │ │ └── sqlmanagers │ │ │ ├── MySQLDataManager.java │ │ │ ├── PluginMessageManager.java │ │ │ ├── SQLDataManager.java │ │ │ └── SQLiteDataManager.java │ │ ├── discord │ │ ├── DiscordWebhook.java │ │ ├── IDiscordHook.java │ │ └── SpicordHook.java │ │ ├── integrations │ │ ├── OraxenTagResolver.java │ │ ├── PremiumVanishIntegration.java │ │ └── SuperVanishIntegration.java │ │ ├── mail │ │ ├── Mail.java │ │ ├── MailCommand.java │ │ └── MailGUIManager.java │ │ ├── moderation │ │ ├── MuteCommand.java │ │ ├── MuteManager.java │ │ ├── SpyChatCommand.java │ │ ├── SpyManager.java │ │ └── StaffChatCommand.java │ │ ├── permission │ │ ├── LuckPermsProvider.java │ │ ├── PermissionProvider.java │ │ └── VaultPermissionProvider.java │ │ ├── settings │ │ ├── Config.java │ │ ├── ConfigValidator.java │ │ ├── FiltersConfig.java │ │ ├── GuiSettings.java │ │ └── Messages.java │ │ ├── task │ │ ├── AnnouncerManager.java │ │ └── AnnouncerTask.java │ │ └── utils │ │ ├── AdventureWebuiEditorAPI.java │ │ ├── ItemNameProvider.java │ │ └── Metrics.java └── resources │ ├── plugin.yml │ └── webhook_embed.json └── test └── java └── dev └── unnm3d └── redischat └── JsonTests.java /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '30 2 * * 5' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # required to fetch internal or private CodeQL packs 37 | packages: read 38 | 39 | # only required for workflows in private repositories 40 | actions: read 41 | contents: read 42 | 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - language: java-kotlin 48 | build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too. 49 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 50 | # Use `c-cpp` to analyze code written in C, C++ or both 51 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 52 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 53 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 54 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 55 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 56 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v4 60 | 61 | # Initializes the CodeQL tools for scanning. 62 | - name: Initialize CodeQL 63 | uses: github/codeql-action/init@v3 64 | with: 65 | languages: ${{ matrix.language }} 66 | build-mode: ${{ matrix.build-mode }} 67 | # If you wish to specify custom queries, you can do so here or in a config file. 68 | # By default, queries listed here will override any specified in a config file. 69 | # Prefix the list here with "+" to use these queries and those in the config file. 70 | 71 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 72 | # queries: security-extended,security-and-quality 73 | 74 | # If the analyze step fails for one of the languages you are analyzing with 75 | # "We were unable to automatically build your code", modify the matrix above 76 | # to set the build mode to "manual" for that language. Then modify this step 77 | # to build your code. 78 | # ℹ️ Command-line programs to run using the OS shell. 79 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 80 | - if: matrix.build-mode == 'manual' 81 | shell: bash 82 | run: | 83 | echo 'If you are using a "manual" build mode for one or more of the' \ 84 | 'languages you are analyzing, replace this with the commands to build' \ 85 | 'your code, for example:' 86 | echo ' make bootstrap' 87 | echo ' make release' 88 | exit 1 89 | 90 | - name: Perform CodeQL Analysis 91 | uses: github/codeql-action/analyze@v3 92 | with: 93 | category: "/language:${{matrix.language}}" 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | 11 | # Compiled class file 12 | *.class 13 | 14 | # Log file 15 | *.log 16 | 17 | # BlueJ files 18 | *.ctxt 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.nar 24 | *.ear 25 | *.zip 26 | *.tar.gz 27 | *.rar 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | 32 | *~ 33 | 34 | # temporary files which can be created if a process still has a handle open of a deleted file 35 | .fuse_hidden* 36 | 37 | # KDE directory preferences 38 | .directory 39 | 40 | # Linux trash folder which might appear on any partition or disk 41 | .Trash-* 42 | 43 | # .nfs files are created when an open file is removed but is still being accessed 44 | .nfs* 45 | 46 | # General 47 | .DS_Store 48 | .AppleDouble 49 | .LSOverride 50 | 51 | # Icon must end with two \r 52 | Icon 53 | 54 | # Thumbnails 55 | ._* 56 | 57 | # Files that might appear in the root of a volume 58 | .DocumentRevisions-V100 59 | .fseventsd 60 | .Spotlight-V100 61 | .TemporaryItems 62 | .Trashes 63 | .VolumeIcon.icns 64 | .com.apple.timemachine.donotpresent 65 | 66 | # Directories potentially created on remote AFP share 67 | .AppleDB 68 | .AppleDesktop 69 | Network Trash Folder 70 | Temporary Items 71 | .apdisk 72 | 73 | # Windows thumbnail cache files 74 | Thumbs.db 75 | Thumbs.db:encryptable 76 | ehthumbs.db 77 | ehthumbs_vista.db 78 | 79 | # Dump file 80 | *.stackdump 81 | 82 | # Folder config file 83 | [Dd]esktop.ini 84 | 85 | # Recycle Bin used on file shares 86 | $RECYCLE.BIN/ 87 | 88 | # Windows Installer files 89 | *.cab 90 | *.msi 91 | *.msix 92 | *.msm 93 | *.msp 94 | 95 | # Windows shortcuts 96 | *.lnk 97 | 98 | target/ 99 | 100 | pom.xml.tag 101 | pom.xml.releaseBackup 102 | pom.xml.versionsBackup 103 | pom.xml.next 104 | 105 | release.properties 106 | dependency-reduced-pom.xml 107 | buildNumber.properties 108 | .mvn/timing.properties 109 | .mvn/wrapper/maven-wrapper.jar 110 | .flattened-pom.xml 111 | 112 | # Common working directory 113 | run/ 114 | src/main/java/dev/unnm3d/redischat/utils/Metrics.java -------------------------------------------------------------------------------- /.scripts/ensure-java-17: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JV=$(java -version 2>&1 >/dev/null | head -1) 4 | echo "$JV" | sed -E 's/^.*version "([^".]*)\.[^"]*".*$/\1/' 5 | 6 | if [ "$JV" != 17 ]; then 7 | case "$1" in 8 | install) 9 | echo "installing sdkman..." 10 | curl -s "https://get.sdkman.io" | bash 11 | source ~/.sdkman/bin/sdkman-init.sh 12 | sdk install java 17.0.3-open 13 | ;; 14 | use) 15 | echo "must source ~/.sdkman/bin/sdkman-init.sh" 16 | exit 1 17 | ;; 18 | esac 19 | fi 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is owned and maintained by "Emibergo02" and is distributed with "All Rights Reserved". 2 | Emiliano Bergonzani reserves the right to change these terms at any time, you have to comply with the most recent version. 3 | I do NOT provide support to servers involved with piracy in any form, or owners who have downloaded ANY of our plugins from an unofficial/illegal source. 4 | 5 | Things can you CANNOT do: 6 | - Issue a refund on PayPal without our explicit permission, as this is a digital good. 7 | - Redistribute, sell or give an official/modified version of the plugin (with or without any type of counterpart) to anyone else. 8 | - Modify and compile the project source code to bypass an anti-piracy protection. 9 | - Download, compile, decompile or use the plugin on any server without purchasing a license. 10 | 11 | Things can you CAN do when purchasing the plugin: 12 | - Download and decompile the plugin file. 13 | - Fork and modify the project source code to meet your production server's needs. 14 | - Use it on ONE production server or network (= proxy-connected servers) and one private test server at a time. 15 | 16 | You may propose a merge request, under the terms that you grant full rights to us using any pushed code. 17 | 18 | If you are a developer and have not purchased a license, you have the permission to download, fork, edit and compile the project source code, and sell code modifications to your client ONLY IF they have already purchased a license. This only applies to one-time comission works and does NOT apply to public sales. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RedisChat 2 | [![CodeFactor](https://www.codefactor.io/repository/github/emibergo02/redischat/badge)](https://www.codefactor.io/repository/github/emibergo02/redischat) 3 | 4 | multi-server chat made for large scalable networks 5 | 6 | README coming soon 7 | More info on [the Gitbook page](https://emibergo.gitbook.io/redischat/) 8 | 9 | [Javadoc here](https://emibergo02.github.io/RedisChat/) 10 | -------------------------------------------------------------------------------- /docs/constant-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Constant Field Values 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Constant Field Values

52 |
53 |
54 |

Contents

55 | 58 |
59 |
60 |

dev.unnm3d.*

61 |
    62 |
  • 63 |
    dev.unnm3d.redischat.utils.Metrics.MetricsBase
    64 |
    65 |
    Modifier and Type
    66 |
    Constant Field
    67 |
    Value
    68 |
    public static final String
    69 | 70 |
    "3.0.0"
    71 |
    72 |
  • 73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27 | 28 | 29 | 31 | 33 | 34 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/api/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.api Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.api

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 66 |
67 |
68 |

Interface Hierarchy

69 | 74 |
75 |
76 |
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/channels/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.channels Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.channels

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 71 |
72 |
73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/chat/filters/incoming/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.chat.filters.incoming Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.chat.filters.incoming

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 76 |
77 |
78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/datamanagers/redistools/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.datamanagers.redistools Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.datamanagers.redistools

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 67 |
68 |
69 |
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/datamanagers/sqlmanagers/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.datamanagers.sqlmanagers Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.datamanagers.sqlmanagers

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 75 |
76 |
77 |
78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/discord/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.discord Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.discord

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 |
      62 |
    • dev.unnm3d.redischat.discord.DiscordWebhook (implements dev.unnm3d.redischat.discord.IDiscordHook)
    • 63 |
    • org.spicord.api.addon.SimpleAddon 64 | 67 |
    • 68 |
    69 |
  • 70 |
71 |
72 |
73 |

Interface Hierarchy

74 | 77 |
78 |
79 |
80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/integrations/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.integrations Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.integrations

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 67 |
68 |
69 |
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/moderation/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.moderation Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.moderation

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 | 68 |
  • 69 |
70 |
71 |
72 |
73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/permission/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.permission Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.permission

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 | 67 |
68 |
69 |

Interface Hierarchy

70 | 73 |
74 |
75 |
76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/dev/unnm3d/redischat/task/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev.unnm3d.redischat.task Class Hierarchy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 48 |
49 |
50 |
51 |

Hierarchy For Package dev.unnm3d.redischat.task

52 |
53 | Package Hierarchies: 54 | 57 |
58 |

Class Hierarchy

59 |
    60 |
  • java.lang.Object 61 |
      62 |
    • dev.unnm3d.redischat.task.AnnouncerManager
    • 63 |
    • com.github.Anon8281.universalScheduler.UniversalRunnable (implements java.lang.Runnable) 64 | 67 |
    • 68 |
    69 |
  • 70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /docs/element-list: -------------------------------------------------------------------------------- 1 | dev.unnm3d.redischat 2 | dev.unnm3d.redischat.api 3 | dev.unnm3d.redischat.api.events 4 | dev.unnm3d.redischat.channels 5 | dev.unnm3d.redischat.channels.gui 6 | dev.unnm3d.redischat.chat 7 | dev.unnm3d.redischat.chat.filters 8 | dev.unnm3d.redischat.chat.filters.incoming 9 | dev.unnm3d.redischat.chat.filters.outgoing 10 | dev.unnm3d.redischat.chat.objects 11 | dev.unnm3d.redischat.commands 12 | dev.unnm3d.redischat.datamanagers 13 | dev.unnm3d.redischat.datamanagers.redistools 14 | dev.unnm3d.redischat.datamanagers.sqlmanagers 15 | dev.unnm3d.redischat.discord 16 | dev.unnm3d.redischat.integrations 17 | dev.unnm3d.redischat.mail 18 | dev.unnm3d.redischat.moderation 19 | dev.unnm3d.redischat.permission 20 | dev.unnm3d.redischat.settings 21 | dev.unnm3d.redischat.task 22 | dev.unnm3d.redischat.utils 23 | -------------------------------------------------------------------------------- /docs/legal/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Please see ..\java.base\COPYRIGHT 2 | -------------------------------------------------------------------------------- /docs/legal/LICENSE: -------------------------------------------------------------------------------- 1 | Please see ..\java.base\LICENSE 2 | -------------------------------------------------------------------------------- /docs/legal/jquery.md: -------------------------------------------------------------------------------- 1 | ## jQuery v3.6.1 2 | 3 | ### jQuery License 4 | ``` 5 | jQuery v 3.6.1 6 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/ 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | ****************************************** 28 | 29 | The jQuery JavaScript Library v3.6.1 also includes Sizzle.js 30 | 31 | Sizzle.js includes the following license: 32 | 33 | Copyright JS Foundation and other contributors, https://js.foundation/ 34 | 35 | This software consists of voluntary contributions made by many 36 | individuals. For exact contribution history, see the revision history 37 | available at https://github.com/jquery/sizzle 38 | 39 | The following license applies to all parts of this software except as 40 | documented below: 41 | 42 | ==== 43 | 44 | Permission is hereby granted, free of charge, to any person obtaining 45 | a copy of this software and associated documentation files (the 46 | "Software"), to deal in the Software without restriction, including 47 | without limitation the rights to use, copy, modify, merge, publish, 48 | distribute, sublicense, and/or sell copies of the Software, and to 49 | permit persons to whom the Software is furnished to do so, subject to 50 | the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be 53 | included in all copies or substantial portions of the Software. 54 | 55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 56 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 57 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 58 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 59 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 60 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 61 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 62 | 63 | ==== 64 | 65 | All files located in the node_modules and external directories are 66 | externally maintained libraries used by this software which have their 67 | own licenses; we recommend you read them, as their terms may differ from 68 | the terms above. 69 | 70 | ********************* 71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/legal/jqueryUI.md: -------------------------------------------------------------------------------- 1 | ## jQuery UI v1.13.2 2 | 3 | ### jQuery UI License 4 | ``` 5 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 6 | 7 | This software consists of voluntary contributions made by many 8 | individuals. For exact contribution history, see the revision history 9 | available at https://github.com/jquery/jquery-ui 10 | 11 | The following license applies to all parts of this software except as 12 | documented below: 13 | 14 | ==== 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of this software and associated documentation files (the 18 | "Software"), to deal in the Software without restriction, including 19 | without limitation the rights to use, copy, modify, merge, publish, 20 | distribute, sublicense, and/or sell copies of the Software, and to 21 | permit persons to whom the Software is furnished to do so, subject to 22 | the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be 25 | included in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 31 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 32 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | ==== 36 | 37 | Copyright and related rights for sample code are waived via CC0. Sample 38 | code is defined as all source code contained within the demos directory. 39 | 40 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 41 | 42 | ==== 43 | 44 | All files located in the node_modules and external directories are 45 | externally maintained libraries used by this software which have their 46 | own licenses; we recommend you read them, as their terms may differ from 47 | the terms above. 48 | 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/module-search-index.js: -------------------------------------------------------------------------------- 1 | moduleSearchIndex = [];updateSearchResults(); -------------------------------------------------------------------------------- /docs/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Generated Documentation (Untitled) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 |
20 | 23 |

index.html

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/package-search-index.js: -------------------------------------------------------------------------------- 1 | packageSearchIndex = [{"l":"All Packages","u":"allpackages-index.html"},{"l":"dev.unnm3d.redischat"},{"l":"dev.unnm3d.redischat.api"},{"l":"dev.unnm3d.redischat.api.events"},{"l":"dev.unnm3d.redischat.channels"},{"l":"dev.unnm3d.redischat.channels.gui"},{"l":"dev.unnm3d.redischat.chat"},{"l":"dev.unnm3d.redischat.chat.filters"},{"l":"dev.unnm3d.redischat.chat.filters.incoming"},{"l":"dev.unnm3d.redischat.chat.filters.outgoing"},{"l":"dev.unnm3d.redischat.chat.objects"},{"l":"dev.unnm3d.redischat.commands"},{"l":"dev.unnm3d.redischat.datamanagers"},{"l":"dev.unnm3d.redischat.datamanagers.redistools"},{"l":"dev.unnm3d.redischat.datamanagers.sqlmanagers"},{"l":"dev.unnm3d.redischat.discord"},{"l":"dev.unnm3d.redischat.integrations"},{"l":"dev.unnm3d.redischat.mail"},{"l":"dev.unnm3d.redischat.moderation"},{"l":"dev.unnm3d.redischat.permission"},{"l":"dev.unnm3d.redischat.settings"},{"l":"dev.unnm3d.redischat.task"},{"l":"dev.unnm3d.redischat.utils"}];updateSearchResults(); -------------------------------------------------------------------------------- /docs/resources/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emibergo02/RedisChat/3d3a32af6fd827a085ae903228300ef9c9df28f4/docs/resources/glass.png -------------------------------------------------------------------------------- /docs/resources/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emibergo02/RedisChat/3d3a32af6fd827a085ae903228300ef9c9df28f4/docs/resources/x.png -------------------------------------------------------------------------------- /docs/script-dir/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.13.2 - 2023-02-27 2 | * http://jqueryui.com 3 | * Includes: core.css, autocomplete.css, menu.css 4 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;-ms-filter:"alpha(opacity=0)"}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0} -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Search 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 |
24 | 44 |
45 |
46 |

Search

47 |
48 | 49 | 50 |
51 | Additional resources 52 |
53 |
54 |
55 |

The help page provides an introduction to the scope and syntax of JavaDoc search.

56 |

You can use the <ctrl> or <cmd> keys in combination with the left and right arrow keys to switch between result tabs in this page.

57 |

The URL template below may be used to configure this page as a search engine in browsers that support this feature. It has been tested to work in Google Chrome and Mozilla Firefox. Note that other browsers may not support this feature or require a different URL format.

58 | link 59 |

60 | 61 |

62 |
63 |

Loading search index...

64 | 68 |
69 |
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/tag-search-index.js: -------------------------------------------------------------------------------- 1 | tagSearchIndex = [{"l":"Constant Field Values","h":"","u":"constant-values.html"}];updateSearchResults(); -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emibergo02/RedisChat/3d3a32af6fd827a085ae903228300ef9c9df28f4/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 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | # JitPack configuration, ensuring the JDK is set to v17 2 | jdk: 3 | - 'openjdk17' 4 | before_install: 5 | - 'git clone https://github.com/Emibergo02/RedisChat.git' 6 | - 'chmod +x gradlew' 7 | - 'chmod +x ./.scripts/ensure-java-17' 8 | - 'bash ./.scripts/ensure-java-17 install' 9 | install: 10 | - 'if ! ./.scripts/ensure-java-17 use; then source ~/.sdkman/bin/sdkman-init.sh; fi' 11 | - 'java -version' 12 | - './gradlew publishToMavenLocal' 13 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RedisChat' 2 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/Permissions.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum Permissions { 7 | MESSAGE("redischat.message"), 8 | MAIL_WRITE("redischat.mail.write"), 9 | MAIL_WRITE_PUBLIC("redischat.mail.writepublic"), 10 | MAIL_READ("redischat.mail"), 11 | MAIL_DELETE("redischat.mail.delete"), 12 | MAIL_PUBLIC_DELETE("redischat.mail.deletepublic"), 13 | MAIL_UNREAD("redischat.mail.unread"), 14 | IGNORE("redischat.ignore"), 15 | IGNORE_WHITELIST("redischat.ignore_whitelist"), 16 | BYPASS_FILTER_PREFIX("redischat.bypassfilter."), 17 | USE_FORMATTING("redischat.useformatting"), 18 | USE_DANGEROUS("redischat.usedangeroustags"), 19 | USE_ITEM("redischat.showitem"), 20 | USE_INVENTORY("redischat.showinv"), 21 | USE_ENDERCHEST("redischat.showenderchest"), 22 | USE_EMOJI_PLACEHOLDERS("redischat.use_emoji"), 23 | BROADCAST("redischat.broadcast"), 24 | ANNOUNCER("redischat.announcer"), 25 | CLEARCHAT("redischat.clearchat"), 26 | ADMIN("redischat.admin"), 27 | ADMIN_STAFF_CHAT("redischat.staffchat"), 28 | BYPASS_RATE_LIMIT("redischat.bypass_rate_limit"), 29 | CHANNEL_PREFIX("redischat.channel."), 30 | CHANNEL_PUBLIC("redischat.channel.public"), 31 | CHANNEL_CREATE("redischat.createchannel"), 32 | CHANNEL_INFO("redischat.infochannel"), 33 | CHANNEL_GUI("redischat.channelgui"), 34 | CHANNEL_CHANGE_DISPLAYNAME("redischat.changedisplayname"), 35 | CHANNEL_DELETE("redischat.deletechannel"), 36 | CHANNEL_TOGGLE_PLAYER("redischat.playerchannel"), 37 | CHANNEL_LIST("redischat.listchannel"), 38 | CHANNEL_MUTE("redischat.mutechannel"), 39 | CHANNEL_HIDE_PREFIX("redischat.hidechannel."), 40 | CHANNEL_SHOW_PREFIX("redischat.showchannel."), 41 | JOIN_QUIT("redischat.joinquit"), 42 | CHAT_COLOR("redischat.chatcolor"), 43 | SET_PLACEHOLDER("redischat.setchatplaceholder"), 44 | SPY_OTHERS("redischat.spycommand.other"), 45 | ; 46 | 47 | private final String permission; 48 | 49 | Permissions(String permission) { 50 | this.permission = permission; 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/UpdateCheck.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonParser; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.net.URL; 9 | import java.util.Scanner; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.function.Consumer; 12 | import java.util.logging.Logger; 13 | 14 | public class UpdateCheck { 15 | 16 | public final Logger logger; 17 | 18 | public UpdateCheck(RedisChat plugin) { 19 | this.logger = plugin.getLogger(); 20 | } 21 | 22 | public void getVersion(final Consumer consumer) { 23 | CompletableFuture.runAsync(() -> { 24 | try (InputStream inputStream = new URL("https://api.spiget.org/v2/resources/111015/versions/latest") 25 | .openStream(); Scanner scanner = new Scanner(inputStream)) { 26 | if (scanner.hasNext()) { 27 | JsonElement element = JsonParser.parseString(scanner.next()).getAsJsonObject().get("name"); 28 | if (element != null) 29 | consumer.accept(element.getAsString()); 30 | } 31 | } catch (IOException exception) { 32 | logger.severe("Unable to check for updates: " + exception.getMessage()); 33 | } 34 | }, RedisChat.getInstance().getExecutorService()); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/TagResolverIntegration.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public interface TagResolverIntegration { 6 | 7 | /** 8 | * Parse tags/placeholders of a message 9 | * To be used for MiniMessage or other tag/placeholders systems 10 | * 11 | * @param message The message to resolve 12 | * @return The resolved message 13 | */ 14 | default @NotNull String parseTags(String message) { 15 | return message; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/VanishIntegration.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.bukkit.entity.Player; 5 | 6 | public interface VanishIntegration { 7 | 8 | /** 9 | * Check if a player can see another player 10 | * 11 | * @param playerName The playerName 12 | * @param viewer The player who is viewing the possible vanished player 13 | * @return true if the viewer can see the player 14 | */ 15 | boolean canSee(CommandSender viewer, String playerName); 16 | 17 | /** 18 | * Check if a player is vanished 19 | * Default implementation checks for the PremiumVanish metadata 20 | * 21 | * @param player The player 22 | * @return true if the player is vanished 23 | */ 24 | boolean isVanished(Player player); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/AsyncRedisChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.api.objects.ChannelAudience; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.kyori.adventure.text.Component; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.event.Cancellable; 9 | import org.bukkit.event.Event; 10 | import org.bukkit.event.HandlerList; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class AsyncRedisChatMessageEvent extends Event implements Cancellable { 14 | 15 | private static final HandlerList HANDLERS = new HandlerList(); 16 | @Getter 17 | private final CommandSender sender; 18 | @Setter 19 | @Getter 20 | private ChannelAudience audience; 21 | @Getter 22 | private final Component format; 23 | @Getter 24 | private final Component content; 25 | private boolean cancelled; 26 | 27 | /** 28 | * Creates a RedisChatMessageEvent 29 | * 30 | * @param sender The sender of the message 31 | * @param audience The channel of the message 32 | * @param format The format of the message 33 | * @param content The message content 34 | */ 35 | public AsyncRedisChatMessageEvent(CommandSender sender, ChannelAudience audience, Component format, Component content) { 36 | super(true); 37 | this.sender = sender; 38 | this.audience = audience; 39 | this.format = format; 40 | this.content = content; 41 | this.cancelled = false; 42 | } 43 | 44 | @Override 45 | public boolean isCancelled() { 46 | return cancelled; 47 | } 48 | 49 | @Override 50 | public void setCancelled(boolean cancel) { 51 | this.cancelled = cancel; 52 | } 53 | 54 | @Override 55 | public @NotNull HandlerList getHandlers() { 56 | return HANDLERS; 57 | } 58 | 59 | public static HandlerList getHandlerList() { 60 | return HANDLERS; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/ChannelGuiPopulateEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.channels.gui.PlayerChannel; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.Event; 8 | import org.bukkit.event.HandlerList; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.List; 12 | 13 | @Getter 14 | public class ChannelGuiPopulateEvent extends Event { 15 | private static final HandlerList HANDLERS = new HandlerList(); 16 | private final Player player; 17 | @Setter 18 | private List channelItems; 19 | 20 | /** 21 | * Event that is called before the channel GUI is populated 22 | * @param player The player that is opening the GUI 23 | * @param channelItems The list of channels that will be displayed in the GUI 24 | */ 25 | public ChannelGuiPopulateEvent(Player player, List channelItems) { 26 | super(true); 27 | this.player = player; 28 | this.channelItems = channelItems; 29 | } 30 | 31 | @Override 32 | public @NotNull HandlerList getHandlers() { 33 | return HANDLERS; 34 | } 35 | 36 | public static HandlerList getHandlerList() { 37 | return HANDLERS; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/FilterEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 4 | import dev.unnm3d.redischat.chat.filters.FilterResult; 5 | import dev.unnm3d.redischat.settings.FiltersConfig; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.event.Event; 10 | import org.bukkit.event.HandlerList; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | @Getter 14 | public class FilterEvent extends Event { 15 | private static final HandlerList HANDLERS = new HandlerList(); 16 | private final AbstractFilter filter; 17 | @Setter 18 | private FilterResult result; 19 | 20 | /** 21 | * Event that is called when a filter is applied to a message 22 | * @param filter The filter that is being applied 23 | * @param result The result of the filter 24 | */ 25 | public FilterEvent(AbstractFilter filter, FilterResult result) { 26 | super(!Bukkit.isPrimaryThread()); 27 | this.filter = filter; 28 | this.result = result; 29 | } 30 | 31 | @Override 32 | public @NotNull HandlerList getHandlers() { 33 | return HANDLERS; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailDeleteEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | import lombok.Getter; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.Cancellable; 7 | 8 | 9 | public class MailDeleteEvent extends MailEvent implements Cancellable { 10 | @Getter 11 | private final Player deleter; 12 | private boolean cancelled = false; 13 | 14 | /** 15 | * Event that is called when a mail is deleted 16 | * @param mail The mail that is being deleted 17 | * @param deleter The player that is deleting the mail 18 | */ 19 | public MailDeleteEvent(Mail mail, Player deleter) { 20 | super(mail); 21 | this.deleter = deleter; 22 | } 23 | 24 | @Override 25 | public boolean isCancelled() { 26 | return cancelled; 27 | } 28 | 29 | @Override 30 | public void setCancelled(boolean cancelled) { 31 | this.cancelled = cancelled; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailEditorEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class MailEditorEvent extends MailEvent { 8 | private final MailEditorState state; 9 | 10 | /** 11 | * Event that is called when a mail is being created via the web editor 12 | * The event is called when the editor is opened and closed 13 | * 14 | * @param mail The mail that is being created 15 | * @param state The state of the editor 16 | */ 17 | public MailEditorEvent(Mail mail, MailEditorState state) { 18 | super(mail); 19 | this.state = state; 20 | } 21 | 22 | public enum MailEditorState { 23 | STARTED, 24 | COMPLETED 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.event.Event; 8 | import org.bukkit.event.HandlerList; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | @Setter 12 | @Getter 13 | public abstract class MailEvent extends Event { 14 | private static final HandlerList HANDLERS = new HandlerList(); 15 | 16 | private Mail mail; 17 | 18 | public MailEvent(Mail mail) { 19 | super(!Bukkit.isPrimaryThread()); 20 | this.mail = mail; 21 | } 22 | 23 | public static HandlerList getHandlerList() { 24 | return HANDLERS; 25 | } 26 | 27 | @Override 28 | public @NotNull HandlerList getHandlers() { 29 | return HANDLERS; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailReadStatusChangeEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | import lombok.Getter; 5 | import org.bukkit.entity.Player; 6 | 7 | @Getter 8 | public class MailReadStatusChangeEvent extends MailEvent { 9 | private final Player player; 10 | 11 | /** 12 | * Event that is called when a mail is read by a player 13 | * @param mail The mail that is being read 14 | * @param player The player that is reading the mail 15 | */ 16 | public MailReadStatusChangeEvent(Mail mail, Player player) { 17 | super(mail); 18 | this.player = player; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailReceivedEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | 5 | public class MailReceivedEvent extends MailEvent { 6 | 7 | /** 8 | * Event that is called when a mail is received by the server 9 | * @param mail The mail that is being received 10 | */ 11 | public MailReceivedEvent(Mail mail) { 12 | super(mail); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/events/MailSendEvent.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.events; 2 | 3 | import dev.unnm3d.redischat.mail.Mail; 4 | import org.bukkit.event.Cancellable; 5 | 6 | public class MailSendEvent extends MailEvent implements Cancellable { 7 | private boolean cancelled = false; 8 | 9 | /** 10 | * Event that is called when a mail is sent 11 | * @param mail The mail that is being sent 12 | */ 13 | public MailSendEvent(Mail mail) { 14 | super(mail); 15 | } 16 | 17 | @Override 18 | public boolean isCancelled() { 19 | return cancelled; 20 | } 21 | 22 | @Override 23 | public void setCancelled(boolean cancelled) { 24 | this.cancelled = cancelled; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/objects/AudienceType.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.objects; 2 | 3 | public enum AudienceType { 4 | /** 5 | * Represents a player in the game. 6 | */ 7 | PLAYER, 8 | 9 | /** 10 | * Represents a chat channel. 11 | */ 12 | CHANNEL, 13 | 14 | /** 15 | * Represents a user/channel on Discord. 16 | */ 17 | DISCORD 18 | } -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/objects/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.objects; 2 | 3 | 4 | import com.google.common.base.Strings; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | @Getter 13 | @EqualsAndHashCode 14 | @ToString 15 | public class ChatMessage { 16 | private final ChannelAudience sender; 17 | private final long timestamp; 18 | @Setter 19 | private String format; 20 | @Setter 21 | private String content; 22 | private final ChannelAudience receiver; 23 | 24 | 25 | /** 26 | * Creates a ChatMessageInfo as "Server" 27 | * 28 | * @param content The message content 29 | */ 30 | public ChatMessage(String content) { 31 | this(new ChannelAudience(), System.currentTimeMillis(), "{message}", content, ChannelAudience.publicChannelAudience()); 32 | } 33 | 34 | /** 35 | * Creates a ChatMessageInfo as "Server" 36 | * 37 | * @param content The message content 38 | */ 39 | public ChatMessage(String content, String permissionToSee) { 40 | this(new ChannelAudience(), System.currentTimeMillis(), "{message}", content, ChannelAudience.publicChannelAudience(permissionToSee)); 41 | } 42 | 43 | /** 44 | * Used for deserialization 45 | * 46 | * @param sender The sender of the message 47 | * @param format The formatting of the message 48 | * @param content The message content 49 | * @param receiver The receiver of the message 50 | */ 51 | public ChatMessage(@NotNull ChannelAudience sender, long timestamp, @Nullable String format, @Nullable String content, @NotNull ChannelAudience receiver) { 52 | this.sender = sender; 53 | this.timestamp = timestamp; 54 | this.format = Strings.nullToEmpty(format); 55 | this.content = Strings.nullToEmpty(content); 56 | this.receiver = receiver; 57 | } 58 | 59 | 60 | /** 61 | * Creates a ChatMessageInfo from a sender, formatting, message and receiver 62 | * 63 | * @param sender The sender of the message 64 | * @param format The formatting of the message 65 | * @param content The message content 66 | * @param receiver The receiver of the message 67 | */ 68 | public ChatMessage(@NotNull ChannelAudience sender, @Nullable String format, @Nullable String content, @NotNull ChannelAudience receiver) { 69 | this(sender, System.currentTimeMillis(), format, content, receiver); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/api/objects/KnownChatEntities.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.api.objects; 2 | 3 | public enum KnownChatEntities { 4 | GENERAL_CHANNEL("public"), 5 | VOID_CHAT("void"), 6 | SERVER_SENDER("*Server*"), 7 | CHANNEL_PREFIX("#"), 8 | DISCORD_PREFIX("@"), 9 | PERMISSION_PREFIX("~"), 10 | STAFFCHAT_CHANNEL_NAME("staffchat"), 11 | ALL_PLAYERS("-ALL-"), 12 | ; 13 | 14 | private final String keyName; 15 | 16 | /** 17 | * @param keyName the name of the key 18 | */ 19 | KnownChatEntities(final String keyName) { 20 | this.keyName = keyName; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return keyName; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/channels/gui/ChannelGUI.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.channels.gui; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.events.ChannelGuiPopulateEvent; 6 | import dev.unnm3d.redischat.api.objects.KnownChatEntities; 7 | import lombok.AllArgsConstructor; 8 | import org.bukkit.entity.Player; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | import xyz.xenondevs.invui.gui.Gui; 12 | import xyz.xenondevs.invui.gui.PagedGui; 13 | import xyz.xenondevs.invui.gui.structure.Markers; 14 | import xyz.xenondevs.invui.item.Item; 15 | import xyz.xenondevs.invui.item.ItemProvider; 16 | import xyz.xenondevs.invui.item.builder.ItemBuilder; 17 | import xyz.xenondevs.invui.item.impl.controlitem.PageItem; 18 | 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | @AllArgsConstructor 23 | public class ChannelGUI { 24 | private final RedisChat plugin; 25 | 26 | 27 | public Gui getChannelsGUI(@NotNull Player player, @Nullable String activeChannelName) { 28 | 29 | final List items = plugin.getChannelManager().getAllChannels().stream() 30 | .filter(channel -> channel.isShownByDefault() || player.hasPermission(Permissions.CHANNEL_SHOW_PREFIX.getPermission() + channel.getName())) 31 | .map(channel -> new PlayerChannel(channel, player, channel.getName().equals(activeChannelName))) 32 | .filter(playerChannel -> !playerChannel.isHidden()) 33 | .collect(Collectors.toList()); 34 | 35 | final ChannelGuiPopulateEvent populateEvent = new ChannelGuiPopulateEvent(player, items); 36 | plugin.getServer().getPluginManager().callEvent(populateEvent); 37 | 38 | return PagedGui.items() 39 | .setStructure( 40 | plugin.guiSettings.channelGUIStructure.toArray(new String[0])) 41 | .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) // where paged items should be put 42 | .addIngredient('<', new PageItem(false) { 43 | @Override 44 | public ItemProvider getItemProvider(PagedGui gui) { 45 | return new ItemBuilder(plugin.guiSettings.backButton); 46 | } 47 | }) 48 | .addIngredient('>', new PageItem(true) { 49 | @Override 50 | public ItemProvider getItemProvider(PagedGui gui) { 51 | return new ItemBuilder(plugin.guiSettings.forwardButton); 52 | } 53 | }) 54 | .addIngredient('G', new GlobalChannel( 55 | plugin.getChannelManager().getPublicChannel(null), 56 | player, 57 | KnownChatEntities.GENERAL_CHANNEL.toString().equals(activeChannelName))) 58 | .setContent(populateEvent.getChannelItems().stream().map(Item.class::cast).toList()) 59 | .build(); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/channels/gui/GlobalChannel.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.channels.gui; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import dev.unnm3d.redischat.api.objects.Channel; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.inventory.ItemStack; 7 | import org.bukkit.inventory.meta.ItemMeta; 8 | import xyz.xenondevs.invui.item.ItemProvider; 9 | import xyz.xenondevs.invui.item.builder.ItemBuilder; 10 | 11 | public class GlobalChannel extends PlayerChannel { 12 | public GlobalChannel(Channel channel, Player player, boolean isActive) { 13 | super(channel, player, isActive); 14 | } 15 | 16 | @Override 17 | public ItemProvider getItemProvider() { 18 | ItemStack item; 19 | if (status == Status.MUTED) { 20 | item = RedisChat.getInstance().guiSettings.mutedGlobal; 21 | } else if (status == Status.LISTENING) { 22 | item = RedisChat.getInstance().guiSettings.activeGlobal; 23 | } else { 24 | item = RedisChat.getInstance().guiSettings.idleGlobal; 25 | } 26 | 27 | final ItemMeta im = item.getItemMeta(); 28 | if (im != null) 29 | im.setDisplayName("§r" + RedisChat.getInstance().getComponentProvider() 30 | .replaceAmpersandCodesWithSection(channel.getDisplayName())); 31 | item.setItemMeta(im); 32 | return new ItemBuilder(item); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/ChatColorGUI.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.inventory.ClickType; 7 | import org.bukkit.event.inventory.InventoryClickEvent; 8 | import xyz.xenondevs.invui.gui.AbstractGui; 9 | 10 | public class ChatColorGUI extends AbstractGui { 11 | private final RedisChat plugin; 12 | 13 | public ChatColorGUI(RedisChat plugin) { 14 | super(9, plugin.guiSettings.chatColorGUIStructure.size()); 15 | this.plugin = plugin; 16 | this.applyStructure(plugin.guiSettings.getChatColorGUIStructure()); 17 | } 18 | 19 | @Override 20 | public void handleClick(int slotNumber, Player player, ClickType clickType, InventoryClickEvent event) { 21 | super.handleClick(slotNumber, player, clickType, event); 22 | player.closeInventory(); 23 | 24 | StringBuilder sb = new StringBuilder(); 25 | plugin.guiSettings.chatColorGUIStructure.forEach(row -> sb.append(row.replace(" ", ""))); 26 | 27 | char slotChar = sb.toString().charAt(slotNumber); 28 | final ChatColor color = ChatColor.getByChar(slotChar); 29 | if (color == null) { 30 | plugin.messages.sendMessage(player, plugin.messages.invalid_color); 31 | return; 32 | } 33 | if (color == ChatColor.RESET) { 34 | plugin.getPlaceholderManager().removePlayerPlaceholder(player.getName(), "chat_color"); 35 | } else { 36 | plugin.getPlaceholderManager().addPlayerPlaceholder(player.getName(), "chat_color", "<" + color.name().toLowerCase() + ">"); 37 | } 38 | 39 | plugin.messages.sendMessage(player, plugin.messages.color_set); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/ChatFormat.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public record ChatFormat( 6 | @NotNull String permission, 7 | @NotNull String format, 8 | @NotNull String private_format, 9 | @NotNull String receive_private_format, 10 | @NotNull String mention_format, 11 | @NotNull String link_format, 12 | @NotNull String join_format, 13 | @NotNull String quit_format) { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/PlaceholderManager.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import org.bukkit.event.EventHandler; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.player.PlayerLoginEvent; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public class PlaceholderManager implements Listener { 12 | 13 | private final RedisChat plugin; 14 | private final ConcurrentHashMap> playerPlaceholders; 15 | 16 | public PlaceholderManager(RedisChat plugin) { 17 | this.plugin = plugin; 18 | this.playerPlaceholders = new ConcurrentHashMap<>(); 19 | plugin.getServer().getPluginManager().registerEvents(this, plugin); 20 | } 21 | 22 | @EventHandler 23 | public void onPlayerJoin(PlayerLoginEvent event) { 24 | updatePlayerPlaceholderCache(event.getPlayer().getName()); 25 | } 26 | 27 | public void updatePlayerPlaceholderCache(String playerName) { 28 | this.plugin.getDataManager().getPlayerPlaceholders(playerName) 29 | .thenAccept(redisPlaceholders -> { 30 | if (redisPlaceholders == null) return; 31 | playerPlaceholders.put(playerName, new ConcurrentHashMap<>(redisPlaceholders)); 32 | }); 33 | } 34 | 35 | public void updatePlayerPlaceholders(String serializedPlaceholders) { 36 | final String playerName = serializedPlaceholders.substring(0,serializedPlaceholders.indexOf("§;")); 37 | final String placeholders = serializedPlaceholders.substring(serializedPlaceholders.indexOf("§;") + 2); 38 | playerPlaceholders.put(playerName, new ConcurrentHashMap<>(plugin.getDataManager().deserializePlayerPlaceholders(placeholders))); 39 | } 40 | 41 | public void addPlayerPlaceholder(String playerName, String placeholder, String value) { 42 | playerPlaceholders.computeIfAbsent(playerName, k -> new ConcurrentHashMap<>()).put(placeholder, value); 43 | this.plugin.getDataManager().setPlayerPlaceholders(playerName, playerPlaceholders.get(playerName)); 44 | } 45 | 46 | public void removePlayerPlaceholder(String playerName, String placeholder) { 47 | if (!playerPlaceholders.containsKey(playerName)) return; 48 | if (playerPlaceholders.get(playerName).remove(placeholder) == null) return; 49 | this.plugin.getDataManager().setPlayerPlaceholders(playerName, playerPlaceholders.get(playerName)); 50 | } 51 | 52 | public String getPlaceholder(@NotNull String playerName, @NotNull String placeholder) { 53 | return playerPlaceholders.getOrDefault(playerName, new ConcurrentHashMap<>()).getOrDefault(placeholder, ""); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/RedisChatPAPI.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import lombok.AllArgsConstructor; 5 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 6 | import org.bukkit.OfflinePlayer; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | @AllArgsConstructor 10 | public class RedisChatPAPI extends PlaceholderExpansion { 11 | private final RedisChat plugin; 12 | 13 | @Override 14 | public @NotNull String getIdentifier() { 15 | return "redischat"; 16 | } 17 | 18 | @Override 19 | public @NotNull String getAuthor() { 20 | return "Unnm3d"; 21 | } 22 | 23 | @Override 24 | public @NotNull String getVersion() { 25 | return "1.1.0"; 26 | } 27 | 28 | @Override 29 | public boolean persist() { 30 | return true; 31 | } 32 | 33 | 34 | @Override 35 | public String onRequest(OfflinePlayer player, @NotNull String params) { 36 | if (params.equalsIgnoreCase("active_channel")) { 37 | if (player.getName() == null) return plugin.getChannelManager().getPublicChannel(null).getName(); 38 | return plugin.getChannelManager().getActiveChannel(player.getName()); 39 | } 40 | 41 | if (params.equalsIgnoreCase("notify_status")) { 42 | try { 43 | if (plugin.getMailGUIManager().getPublicMails(player.getName()).get() 44 | .stream().anyMatch(mail -> !mail.isRead())) { 45 | return "2"; 46 | } 47 | if (plugin.getMailGUIManager().getPrivateMails(player.getName()).get() 48 | .stream().anyMatch(mail -> !mail.isRead())) { 49 | return "1"; 50 | } 51 | } catch (Exception e) { 52 | plugin.getLogger().severe("Error getting mail status: " + e); 53 | } 54 | return "0"; 55 | } 56 | 57 | if (params.equalsIgnoreCase("ignoring_all")) { 58 | if (player.getName() == null) return "false"; 59 | return plugin.getChannelManager().getMuteManager().getIgnoreList(player.getName()).contains("all") ? "true" : "false"; 60 | } 61 | if (player != null && player.getName() != null) { 62 | return plugin.getPlaceholderManager().getPlaceholder(player.getName(), params); 63 | } 64 | return null; // Placeholder is unknown by the Expansion 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/AbstractFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters; 2 | 3 | import dev.unnm3d.redischat.api.objects.ChatMessage; 4 | import dev.unnm3d.redischat.settings.FiltersConfig; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.bukkit.command.CommandSender; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.function.BiFunction; 12 | 13 | @Getter 14 | @AllArgsConstructor 15 | public abstract class AbstractFilter implements BiFunction { 16 | 17 | protected final String name; 18 | protected final Direction direction; 19 | @Setter 20 | protected T filterSettings; 21 | 22 | @Override 23 | public FilterResult apply(CommandSender player, ChatMessage chatMessage) { 24 | return applyWithPrevious(player, chatMessage); 25 | } 26 | 27 | public abstract FilterResult applyWithPrevious(CommandSender receiver, @NotNull ChatMessage message, ChatMessage... previousMessages); 28 | 29 | 30 | public enum Direction { 31 | INCOMING, 32 | OUTGOING, 33 | BOTH 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/DefaultSettings.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters; 2 | 3 | import dev.unnm3d.redischat.chat.filters.incoming.IgnorePlayerFilter; 4 | import dev.unnm3d.redischat.chat.filters.outgoing.CapsFilter; 5 | import dev.unnm3d.redischat.chat.filters.outgoing.DuplicateFilter; 6 | import dev.unnm3d.redischat.chat.filters.outgoing.IgnoreFilter; 7 | import dev.unnm3d.redischat.chat.filters.outgoing.WordBlacklistFilter; 8 | import dev.unnm3d.redischat.api.objects.AudienceType; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import lombok.Getter; 11 | 12 | import java.util.Set; 13 | 14 | @Getter 15 | public enum DefaultSettings { 16 | 17 | IGNORE_PLAYER(new IgnorePlayerFilter.IgnorePlayerFilterProperties()), 18 | PERMISSION(new FiltersConfig.FilterSettings(true, 2, Set.of(AudienceType.CHANNEL), Set.of())), 19 | DISCORD(new FiltersConfig.FilterSettings(true, 1, Set.of(AudienceType.DISCORD), Set.of())), 20 | PRIVATE_OUT(new FiltersConfig.FilterSettings(true, 3, Set.of(AudienceType.PLAYER), Set.of())), 21 | CAPS(new CapsFilter.CapsFilterProperties()), 22 | DUPLICATE(new DuplicateFilter.DuplicateFilterProperties()), 23 | IGNORE(new IgnoreFilter.IgnoreFilterProperties()), 24 | MUTED_CHANNEL(new FiltersConfig.FilterSettings(true, 5, Set.of(AudienceType.CHANNEL), Set.of())), 25 | TAGS(new FiltersConfig.FilterSettings(true, 9, Set.of(), Set.of())), 26 | WORDS(new WordBlacklistFilter.WordBlacklistFilterProperties()), 27 | SPAM(new FiltersConfig.FilterSettings(true, 1, Set.of(), Set.of())); 28 | 29 | private final FiltersConfig.FilterSettings filterSettings; 30 | 31 | DefaultSettings(FiltersConfig.FilterSettings filterSettings) { 32 | this.filterSettings = filterSettings; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/FilterResult.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters; 2 | 3 | import dev.unnm3d.redischat.api.objects.ChatMessage; 4 | import net.kyori.adventure.text.Component; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Optional; 8 | 9 | /** 10 | * The result of a filter 11 | * 12 | * @param message The resulting message (it can be the same as the filter input message or a modified version) 13 | * @param filtered Whether the message was filtered (if true, the message will not be sent) 14 | */ 15 | public record FilterResult(@NotNull ChatMessage message, boolean filtered, @NotNull Optional filteredReason) { 16 | 17 | public FilterResult(ChatMessage message, boolean filtered) { 18 | this(message, filtered, Optional.empty()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/incoming/IgnorePlayerFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.incoming; 2 | 3 | import de.exlll.configlib.Comment; 4 | import de.exlll.configlib.Configuration; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import dev.unnm3d.redischat.api.objects.AudienceType; 7 | import dev.unnm3d.redischat.api.objects.ChatMessage; 8 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 9 | import dev.unnm3d.redischat.chat.filters.FilterResult; 10 | import dev.unnm3d.redischat.settings.FiltersConfig; 11 | import lombok.Getter; 12 | import net.kyori.adventure.text.minimessage.MiniMessage; 13 | import org.bukkit.command.CommandSender; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.Arrays; 17 | import java.util.Optional; 18 | import java.util.Set; 19 | 20 | public class IgnorePlayerFilter extends AbstractFilter { 21 | 22 | public IgnorePlayerFilter(IgnorePlayerFilterProperties filterSettings) { 23 | super("ignore_player", Direction.INCOMING, filterSettings); 24 | } 25 | 26 | @Override 27 | public FilterResult applyWithPrevious(CommandSender receiver, @NotNull ChatMessage chatMessage, ChatMessage... previousMessages) { 28 | boolean isIgnored = RedisChat.getInstance().getChannelManager().getMuteManager() 29 | .isPlayerIgnored(receiver.getName(), chatMessage.getSender().getName()); 30 | 31 | 32 | if (isIgnored && (chatMessage.getReceiver().isPlayer() || filterSettings.ignoreChannelMessages)) { 33 | 34 | if (filterSettings.sendWarnWhenIgnoring && 35 | Arrays.stream(previousMessages) 36 | .filter(message -> message.getSender().getName().equals(chatMessage.getSender().getName())) 37 | .filter(message -> message.getReceiver().getName().equals(chatMessage.getReceiver().getName())) 38 | .findAny() 39 | .isEmpty()) { 40 | 41 | return new FilterResult(chatMessage, true, 42 | Optional.of(MiniMessage.miniMessage().deserialize(RedisChat.getInstance().messages.ignoredMessageReceiver 43 | .replace("%player%", chatMessage.getSender().getName())))); 44 | } 45 | 46 | return new FilterResult(chatMessage, true); 47 | } 48 | 49 | return new FilterResult(chatMessage, false); 50 | } 51 | 52 | @Configuration 53 | @Getter 54 | public static class IgnorePlayerFilterProperties extends FiltersConfig.FilterSettings { 55 | @Comment("If true, send a ignored_player_receiver message when ignoring a player") 56 | private boolean sendWarnWhenIgnoring; 57 | @Comment("If true, sendWarnWhenIgnoring will work on channel messages too") 58 | private boolean ignoreChannelMessages; 59 | 60 | public IgnorePlayerFilterProperties() { 61 | super(true, 4, Set.of(AudienceType.PLAYER, AudienceType.CHANNEL), Set.of()); 62 | this.ignoreChannelMessages = true; 63 | this.sendWarnWhenIgnoring = true; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/incoming/PermissionFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.incoming; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 6 | import dev.unnm3d.redischat.chat.filters.FilterResult; 7 | import dev.unnm3d.redischat.api.objects.Channel; 8 | import dev.unnm3d.redischat.api.objects.ChatMessage; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import org.bukkit.command.CommandSender; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class PermissionFilter extends AbstractFilter { 14 | private final RedisChat plugin; 15 | 16 | public PermissionFilter(RedisChat plugin, FiltersConfig.FilterSettings filterSettings) { 17 | super("permission", Direction.INCOMING, filterSettings); 18 | this.plugin = plugin; 19 | } 20 | 21 | @Override 22 | public FilterResult applyWithPrevious(CommandSender receiver, @NotNull ChatMessage chatMessage, ChatMessage... previousMessages) { 23 | if (!plugin.getChannelManager().getRegisteredChannel(chatMessage.getReceiver().getName()) 24 | .map(Channel::isPermissionEnabled).orElse(true)) { 25 | return new FilterResult(chatMessage, false); 26 | } 27 | 28 | for (String permission : chatMessage.getReceiver().getPermissions()) { 29 | if (!receiver.hasPermission(permission)) { 30 | return new FilterResult(chatMessage, true); 31 | } 32 | } 33 | 34 | //Default read permission check 35 | final String permission = Permissions.CHANNEL_PREFIX.getPermission() + chatMessage.getReceiver().getName(); 36 | if (!(receiver.hasPermission(permission) || receiver.hasPermission(permission + ".read"))) { 37 | return new FilterResult(chatMessage, true); 38 | } 39 | 40 | return new FilterResult(chatMessage, false); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/CapsFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import de.exlll.configlib.Comment; 4 | import de.exlll.configlib.Configuration; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 7 | import dev.unnm3d.redischat.chat.filters.FilterResult; 8 | import dev.unnm3d.redischat.api.objects.ChatMessage; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import lombok.Getter; 11 | import org.bukkit.command.CommandSender; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Optional; 15 | import java.util.Set; 16 | 17 | 18 | public class CapsFilter extends AbstractFilter { 19 | private final RedisChat plugin; 20 | 21 | public CapsFilter(RedisChat plugin) { 22 | super("caps", Direction.OUTGOING, plugin.filterSettings.caps); 23 | this.plugin = plugin; 24 | } 25 | 26 | 27 | /** 28 | * Transform uppercase messages into lowercase 29 | * 30 | * @param message The message to transform 31 | * @return The transformed message 32 | */ 33 | public boolean antiCaps(@NotNull String message) { 34 | int capsCount = 0; 35 | for (char c : message.toCharArray()) 36 | if (Character.isUpperCase(c)) 37 | capsCount++; 38 | return capsCount > message.length() * (filterSettings.percentageCaps / 100.0); 39 | } 40 | 41 | @Override 42 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 43 | 44 | //Remove usernames from the message so players can send UPPERCASE USERNAMES 45 | String mentionRemoved = message.getContent(); 46 | for (String playerName : plugin.getPlayerListManager().getPlayerList(sender)) { 47 | mentionRemoved = mentionRemoved.replace(playerName, ""); 48 | } 49 | 50 | //Then check for the presence of caps 51 | if (antiCaps(mentionRemoved)) { 52 | message.setContent(message.getContent().toLowerCase()); 53 | return new FilterResult(message, filterSettings.shouldBlock, Optional.of( 54 | RedisChat.getInstance().getComponentProvider().parse(sender, 55 | RedisChat.getInstance().messages.caps, 56 | true, 57 | false, 58 | false) 59 | )); 60 | } 61 | 62 | return new FilterResult(message, false, Optional.empty()); 63 | } 64 | 65 | 66 | @Configuration 67 | @Getter 68 | public static class CapsFilterProperties extends FiltersConfig.FilterSettings { 69 | private int percentageCaps; 70 | @Comment("Should the message be blocked") 71 | private boolean shouldBlock; 72 | 73 | public CapsFilterProperties() { 74 | super(true, 8, Set.of(), Set.of()); 75 | this.percentageCaps = 50; 76 | this.shouldBlock = false; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/DuplicateFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import de.exlll.configlib.Comment; 4 | import de.exlll.configlib.Configuration; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 7 | import dev.unnm3d.redischat.chat.filters.FilterResult; 8 | import dev.unnm3d.redischat.api.objects.ChatMessage; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import lombok.Getter; 11 | import org.bukkit.command.CommandSender; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Arrays; 15 | import java.util.Comparator; 16 | import java.util.Optional; 17 | import java.util.Set; 18 | 19 | 20 | public class DuplicateFilter extends AbstractFilter { 21 | 22 | public DuplicateFilter(DuplicateFilterProperties filterSettings) { 23 | super("duplicate", Direction.OUTGOING, filterSettings); 24 | } 25 | 26 | @Override 27 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 28 | //Skip filter if no previous messages 29 | if(previousMessages.length == 0) return new FilterResult(message, false, Optional.empty()); 30 | 31 | //Reverse the array to check the last messages first 32 | int checkedMessages = 0; 33 | for (ChatMessage chatMessage : 34 | Arrays.stream(previousMessages) 35 | .sorted(Comparator.comparingLong(ChatMessage::getTimestamp).reversed()) 36 | .toList()) { 37 | float similarityPercentage = levenshteinScore(message.getContent(), chatMessage.getContent()) * 100; 38 | 39 | //If the similarity is lower than the percentage, skip filter 40 | if (similarityPercentage < filterSettings.similarityPercentage) { 41 | return new FilterResult(message, false, Optional.empty()); 42 | } 43 | if (++checkedMessages == filterSettings.messagesToCheck) break; 44 | } 45 | 46 | return new FilterResult(message, true, 47 | Optional.of(RedisChat.getInstance().getComponentProvider() 48 | .parse(sender, RedisChat.getInstance().messages.duplicate_message, true, 49 | false, false))); 50 | } 51 | 52 | private float levenshteinScore(String first, String second) { 53 | int maxLength = Math.max(first.length(), second.length()); 54 | //Can't divide by 0 55 | if (maxLength == 0) return 1.0f; 56 | return ((float) (maxLength - computeEditDistance(first, second))) / (float) maxLength; 57 | } 58 | 59 | private int computeEditDistance(String first, String second) { 60 | first = first.toLowerCase(); 61 | second = second.toLowerCase(); 62 | 63 | int[] costs = new int[second.length() + 1]; 64 | for (int i = 0; i <= first.length(); i++) { 65 | int previousValue = i; 66 | for (int j = 0; j <= second.length(); j++) { 67 | if (i == 0) { 68 | costs[j] = j; 69 | } else if (j > 0) { 70 | int useValue = costs[j - 1]; 71 | if (first.charAt(i - 1) != second.charAt(j - 1)) { 72 | useValue = Math.min(Math.min(useValue, previousValue), costs[j]) + 1; 73 | } 74 | costs[j - 1] = previousValue; 75 | previousValue = useValue; 76 | 77 | } 78 | } 79 | if (i > 0) { 80 | costs[second.length()] = previousValue; 81 | } 82 | } 83 | return costs[second.length()]; 84 | } 85 | 86 | 87 | @Configuration 88 | @Getter 89 | public static class DuplicateFilterProperties extends FiltersConfig.FilterSettings { 90 | private int similarityPercentage; 91 | @Comment("How many messages to check for duplicates (from the last one)") 92 | private int messagesToCheck; 93 | 94 | public DuplicateFilterProperties() { 95 | super(true, 8, Set.of(), Set.of()); 96 | this.similarityPercentage = 60; 97 | this.messagesToCheck = 2; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/IgnoreFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import de.exlll.configlib.Configuration; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.AudienceType; 6 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 7 | import dev.unnm3d.redischat.chat.filters.FilterResult; 8 | import dev.unnm3d.redischat.api.objects.ChatMessage; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import lombok.Getter; 11 | import org.bukkit.command.CommandSender; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Optional; 15 | import java.util.Set; 16 | 17 | public class IgnoreFilter extends AbstractFilter { 18 | 19 | private final RedisChat plugin; 20 | 21 | public IgnoreFilter(RedisChat plugin, IgnoreFilter.IgnoreFilterProperties filterSettings) { 22 | super("ignore", Direction.OUTGOING, filterSettings); 23 | this.plugin = plugin; 24 | } 25 | 26 | 27 | @Override 28 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 29 | if (plugin.getChannelManager().getMuteManager().isPlayerIgnored(sender.getName(), message.getReceiver().getName())) { 30 | return new FilterResult(message, true, Optional.of( 31 | plugin.getComponentProvider().parse(sender, plugin.messages.ignoredMessageSender, 32 | true, false, false))); 33 | } 34 | if (plugin.getChannelManager().getMuteManager().isPlayerIgnored(message.getReceiver().getName(), sender.getName())) { 35 | return new FilterResult(message, true, Optional.of( 36 | plugin.getComponentProvider().parse(sender, plugin.messages.receiverIgnoringSender, 37 | true, false, false))); 38 | } 39 | 40 | return new FilterResult(message, false, Optional.empty()); 41 | } 42 | 43 | @Configuration 44 | @Getter 45 | public static class IgnoreFilterProperties extends FiltersConfig.FilterSettings { 46 | private boolean sendWarnWhenIgnored; 47 | private boolean sendWarnWhenIgnoring; 48 | 49 | public IgnoreFilterProperties() { 50 | super(true, 4, Set.of(AudienceType.PLAYER), Set.of()); 51 | this.sendWarnWhenIgnored = false; 52 | this.sendWarnWhenIgnoring = true; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/MutedChannelFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 6 | import dev.unnm3d.redischat.chat.filters.FilterResult; 7 | import dev.unnm3d.redischat.api.objects.Channel; 8 | import dev.unnm3d.redischat.api.objects.ChatMessage; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import org.bukkit.command.CommandSender; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Optional; 14 | 15 | 16 | public class MutedChannelFilter extends AbstractFilter { 17 | 18 | private final RedisChat plugin; 19 | 20 | public MutedChannelFilter(RedisChat plugin, FiltersConfig.FilterSettings filterSettings) { 21 | super("muted_channel", Direction.OUTGOING, filterSettings); 22 | this.plugin = plugin; 23 | } 24 | 25 | @Override 26 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 27 | 28 | if (plugin.getChannelManager().getMuteManager().isMutedOnChannel(sender.getName(), message.getReceiver().getName())) { 29 | return new FilterResult(message, true, Optional.of( 30 | plugin.getComponentProvider().parse(sender, 31 | plugin.messages.muted_on_channel.replace("%channel%", message.getReceiver().getName()), 32 | true, 33 | false, 34 | false) 35 | )); 36 | } 37 | 38 | if (!plugin.getChannelManager().getRegisteredChannel(message.getReceiver().getName()) 39 | .map(Channel::isPermissionEnabled).orElse(true)) { 40 | return new FilterResult(message, false); 41 | } 42 | 43 | final String permission = Permissions.CHANNEL_PREFIX.getPermission() + message.getReceiver().getName(); 44 | if (!(sender.hasPermission(permission) || sender.hasPermission(permission + ".write"))) { 45 | return new FilterResult(message, true, Optional.of( 46 | plugin.getComponentProvider().parse(sender, 47 | plugin.messages.channelNoPermission.replace("%channel%", message.getReceiver().getName()), 48 | true, 49 | false, 50 | false) 51 | )); 52 | } 53 | 54 | return new FilterResult(message, false, Optional.empty()); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/SpamFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.Channel; 6 | import dev.unnm3d.redischat.api.objects.ChatMessage; 7 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 8 | import dev.unnm3d.redischat.chat.filters.FilterResult; 9 | import dev.unnm3d.redischat.settings.FiltersConfig; 10 | import org.bukkit.command.CommandSender; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Optional; 14 | 15 | 16 | public class SpamFilter extends AbstractFilter { 17 | private final RedisChat plugin; 18 | 19 | public SpamFilter(RedisChat plugin, FiltersConfig.FilterSettings filterSettings) { 20 | super("spam", Direction.OUTGOING, filterSettings); 21 | this.plugin = plugin; 22 | } 23 | 24 | @Override 25 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 26 | if (!message.getReceiver().isChannel()) return new FilterResult(message, false, Optional.empty()); 27 | 28 | if (!sender.hasPermission(Permissions.BYPASS_RATE_LIMIT.getPermission())) { 29 | final Channel channel = plugin.getChannelManager().getChannel(message.getReceiver().getName(), null) 30 | .orElse(plugin.getChannelManager().getPublicChannel(null)); 31 | if (channel != null && plugin.getDataManager().isRateLimited(sender.getName(), channel)) { 32 | return new FilterResult(message, true, Optional.of( 33 | plugin.getComponentProvider().parse(sender, 34 | plugin.messages.rate_limited, 35 | true, 36 | false, 37 | false) 38 | )); 39 | } 40 | } 41 | 42 | return new FilterResult(message, false, Optional.empty()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/TagFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.ChatMessage; 6 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 7 | import dev.unnm3d.redischat.chat.filters.FilterResult; 8 | import dev.unnm3d.redischat.settings.FiltersConfig; 9 | import me.clip.placeholderapi.PlaceholderAPI; 10 | import net.kyori.adventure.text.minimessage.MiniMessage; 11 | import org.bukkit.command.CommandSender; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Optional; 15 | import java.util.regex.Matcher; 16 | 17 | 18 | public class TagFilter extends AbstractFilter { 19 | 20 | private final RedisChat plugin; 21 | 22 | public TagFilter(RedisChat plugin, FiltersConfig.FilterSettings filterSettings) { 23 | super("tag", Direction.OUTGOING, filterSettings); 24 | this.plugin = plugin; 25 | } 26 | 27 | @Override 28 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage chatMessage, ChatMessage... previousMessages) { 29 | if (!sender.hasPermission(Permissions.USE_DANGEROUS.getPermission())) { 30 | chatMessage.setContent(chatMessage.getContent() 31 | .replace("run_command", "copy_to_clipboard") 32 | .replace("suggest_command", "copy_to_clipboard")); 33 | } 34 | 35 | if (sender.hasPermission(Permissions.USE_FORMATTING.getPermission())) 36 | return new FilterResult(chatMessage, false); 37 | 38 | final Matcher m = PlaceholderAPI.getPlaceholderPattern().matcher(chatMessage.getContent()); 39 | if (m.find()) { 40 | return new FilterResult(chatMessage, true, Optional.of( 41 | plugin.getComponentProvider().parse(sender, 42 | plugin.messages.messageContainsBadWords 43 | .replace("%words%", m.group()), 44 | true, 45 | false, 46 | false) 47 | )); 48 | } 49 | 50 | chatMessage.setContent(plugin.getComponentProvider().purgeTags(chatMessage.getContent())); 51 | 52 | if (chatMessage.getContent().trim().isEmpty()) { 53 | return new FilterResult(chatMessage, true, Optional.of( 54 | MiniMessage.miniMessage().deserialize(plugin.messages.empty_message) 55 | )); 56 | } 57 | return new FilterResult(chatMessage, false); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/filters/outgoing/WordBlacklistFilter.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.filters.outgoing; 2 | 3 | import de.exlll.configlib.Configuration; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.ChatMessage; 6 | import dev.unnm3d.redischat.chat.filters.AbstractFilter; 7 | import dev.unnm3d.redischat.chat.filters.FilterResult; 8 | import dev.unnm3d.redischat.settings.FiltersConfig; 9 | import lombok.Getter; 10 | import org.bukkit.command.CommandSender; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.HashSet; 14 | import java.util.Optional; 15 | import java.util.Set; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | 20 | public class WordBlacklistFilter extends AbstractFilter { 21 | private final RedisChat plugin; 22 | 23 | public WordBlacklistFilter(RedisChat plugin, WordBlacklistFilterProperties filterSettings) { 24 | super("word_blacklist", Direction.OUTGOING, filterSettings); 25 | this.plugin = plugin; 26 | } 27 | 28 | @Override 29 | public FilterResult applyWithPrevious(CommandSender sender, @NotNull ChatMessage message, ChatMessage... previousMessages) { 30 | String sanitized = message.getContent(); 31 | 32 | Set forbiddenWords = new HashSet<>(); 33 | //Check for forbidden words, wrapping every blacklisted word in a regex group 34 | for (Pattern pattern : plugin.config.regex_blacklist.stream() 35 | .map(regex -> Pattern.compile("(" + regex + ")")) 36 | .toList()) { 37 | final StringBuilder sanitizedFinal = new StringBuilder(); 38 | final Matcher m = pattern.matcher(sanitized); 39 | while (m.find()) { 40 | forbiddenWords.add(m.group().trim()); 41 | m.appendReplacement(sanitizedFinal, filterSettings.replacement); 42 | } 43 | m.appendTail(sanitizedFinal); 44 | sanitized = sanitizedFinal.toString(); 45 | } 46 | 47 | if (filterSettings.blockCensoredMessage && !forbiddenWords.isEmpty()) { 48 | return new FilterResult(message, true, Optional.of( 49 | plugin.getComponentProvider().parse(sender, 50 | plugin.messages.messageContainsBadWords 51 | .replace("%words%", String.join(",", forbiddenWords)), 52 | true, 53 | false, 54 | false) 55 | )); 56 | } 57 | //Set the sanitized message 58 | message.setContent(sanitized); 59 | return new FilterResult(message, false, Optional.empty()); 60 | } 61 | 62 | 63 | @Configuration 64 | @Getter 65 | public static class WordBlacklistFilterProperties extends FiltersConfig.FilterSettings { 66 | private boolean blockCensoredMessage; 67 | private String replacement; 68 | 69 | public WordBlacklistFilterProperties() { 70 | super(true, 1, Set.of(), Set.of()); 71 | this.blockCensoredMessage = true; 72 | this.replacement = "*****"; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/listeners/ChatListenerWithPriority.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.listeners; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import lombok.Getter; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.EventPriority; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.player.AsyncPlayerChatEvent; 9 | 10 | @Getter 11 | public enum ChatListenerWithPriority { 12 | LOWEST(new ChatListener() { 13 | @EventHandler(priority = EventPriority.LOWEST) 14 | public void onChat(AsyncPlayerChatEvent event) { 15 | listenChat(event); 16 | } 17 | }), 18 | LOW(new ChatListener() { 19 | @EventHandler(priority = EventPriority.LOW) 20 | public void onChat(AsyncPlayerChatEvent event) { 21 | listenChat(event); 22 | } 23 | }), 24 | NORMAL(new ChatListener() { 25 | @EventHandler(priority = EventPriority.NORMAL) 26 | public void onChat(AsyncPlayerChatEvent event) { 27 | listenChat(event); 28 | } 29 | }), 30 | HIGH(new ChatListener() { 31 | @EventHandler(priority = EventPriority.HIGH) 32 | public void onChat(AsyncPlayerChatEvent event) { 33 | listenChat(event); 34 | } 35 | }), 36 | HIGHEST(new ChatListener() { 37 | @EventHandler(priority = EventPriority.HIGHEST) 38 | public void onChat(AsyncPlayerChatEvent event) { 39 | listenChat(event); 40 | } 41 | }), 42 | MONITOR(new ChatListener() { 43 | @EventHandler(priority = EventPriority.MONITOR) 44 | public void onChat(AsyncPlayerChatEvent event) { 45 | listenChat(event); 46 | } 47 | }); 48 | 49 | 50 | private final Listener listener; 51 | 52 | ChatListenerWithPriority(Listener listener) { 53 | this.listener = listener; 54 | } 55 | 56 | 57 | private abstract static class ChatListener implements Listener { 58 | private final RedisChat plugin = RedisChat.getInstance(); 59 | 60 | public void listenChat(AsyncPlayerChatEvent event) { 61 | if (event.isCancelled()) return; 62 | event.setCancelled(true); 63 | plugin.getChannelManager().outgoingMessage(event.getPlayer(), event.getMessage()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/chat/listeners/UtilsListener.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.chat.listeners; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.mail.Mail; 6 | import lombok.AllArgsConstructor; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerJoinEvent; 11 | 12 | @AllArgsConstructor 13 | public class UtilsListener implements Listener { 14 | private final RedisChat plugin; 15 | 16 | @EventHandler(priority = EventPriority.HIGHEST) 17 | public void onJoinSpy(PlayerJoinEvent event) { 18 | plugin.getSpyManager().onJoin(event.getPlayer()); 19 | } 20 | 21 | @EventHandler(priority = EventPriority.LOWEST) 22 | public void onJoinOthers(PlayerJoinEvent event) { 23 | plugin.getDataManager().getActivePlayerChannel(event.getPlayer().getName()) 24 | .thenAccept(channelName -> 25 | plugin.getChannelManager().updateActiveChannel(event.getPlayer().getName(), channelName)); 26 | //Remove chat color placeholder if player doesn't have permission to edit it 27 | final String activeChatColor = plugin.getPlaceholderManager().getPlaceholder(event.getPlayer().getName(), "chat_color") 28 | .replace("<", "").replace(">", ""); 29 | if (!activeChatColor.isEmpty() && 30 | !event.getPlayer().hasPermission(Permissions.CHAT_COLOR.getPermission() + "." + activeChatColor)) { 31 | plugin.getPlaceholderManager().removePlayerPlaceholder(event.getPlayer().getName(), "chat_color"); 32 | } 33 | 34 | if (!plugin.config.enableMails) return; 35 | if (!plugin.config.remindMailOnJoin) return; 36 | if (plugin.getPlayerListManager().getPlayerList(event.getPlayer()).contains(event.getPlayer().getName())) 37 | return; 38 | 39 | //Send mail reminder 40 | plugin.getMailGUIManager().getPublicMails(event.getPlayer().getName()) 41 | .thenAccept(mails -> { 42 | if (mails.isEmpty()) return; 43 | for (Mail mail : mails) { 44 | if (mail.isRead()) continue; 45 | plugin.messages.sendMessage(event.getPlayer(), plugin.messages.mailReceived 46 | .replace("%sender%", mail.getSender()) 47 | .replace("%title%", mail.getTitle())); 48 | } 49 | }); 50 | 51 | //Send mail reminder 52 | plugin.getMailGUIManager().getPrivateMails(event.getPlayer().getName()) 53 | .thenAccept(mails -> { 54 | if (mails.isEmpty()) return; 55 | for (Mail mail : mails) { 56 | if (mail.isRead()) continue; 57 | plugin.messages.sendMessage(event.getPlayer(), plugin.messages.mailReceived 58 | .replace("%sender%", mail.getSender()) 59 | .replace("%title%", mail.getTitle())); 60 | } 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/AnnounceCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import com.google.common.base.Strings; 4 | import dev.jorel.commandapi.CommandAPICommand; 5 | import dev.jorel.commandapi.arguments.ArgumentSuggestions; 6 | import dev.jorel.commandapi.arguments.MultiLiteralArgument; 7 | import dev.jorel.commandapi.arguments.StringArgument; 8 | import dev.unnm3d.redischat.Permissions; 9 | import dev.unnm3d.redischat.RedisChat; 10 | import dev.unnm3d.redischat.settings.Config; 11 | import dev.unnm3d.redischat.task.AnnouncerManager; 12 | import dev.unnm3d.redischat.task.AnnouncerTask; 13 | import lombok.AllArgsConstructor; 14 | 15 | @AllArgsConstructor 16 | public class AnnounceCommand { 17 | private final RedisChat plugin; 18 | private final AnnouncerManager announcerManager; 19 | 20 | public CommandAPICommand getCommand() { 21 | 22 | return new CommandAPICommand("announcer") 23 | .withAliases(plugin.config.getCommandAliases("announcer")) 24 | .withPermission(Permissions.ANNOUNCER.getPermission()) 25 | .withArguments(new MultiLiteralArgument("action", "stop", "start")) 26 | .withArguments(new StringArgument("announcementName") 27 | .replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> 28 | plugin.config.announcer.stream() 29 | .map(Config.Announcement::announcementName) 30 | .filter(announceName -> announceName.startsWith(commandSenderSuggestionInfo.currentArg())) 31 | .toArray(String[]::new)) 32 | )) 33 | .executes((sender, args) -> { 34 | final String announceName = Strings.nullToEmpty((String) args.get(1)); 35 | switch (Strings.nullToEmpty((String) args.get(0))) { 36 | case "stop" -> { 37 | if (announcerManager.cancelAnnounce(announceName) == null) { 38 | plugin.messages.sendMessage(sender, plugin.messages.announce_not_found.replace("%name%", announceName)); 39 | return; 40 | } 41 | plugin.messages.sendMessage(sender, plugin.messages.action_completed_successfully); 42 | } 43 | case "start" -> { 44 | final AnnouncerTask at = announcerManager.startAnnounce(announceName); 45 | if (at == null) { 46 | plugin.messages.sendMessage(sender, plugin.messages.announce_not_found.replace("%name%", announceName)); 47 | return; 48 | } 49 | plugin.messages.sendMessage(sender, plugin.messages.action_completed_successfully); 50 | at.run(); 51 | } 52 | default -> plugin.messages.sendMessage(sender, plugin.messages.missing_arguments); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/BroadcastCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import com.github.Anon8281.universalScheduler.UniversalRunnable; 4 | import dev.jorel.commandapi.CommandAPICommand; 5 | import dev.jorel.commandapi.arguments.ArgumentSuggestions; 6 | import dev.jorel.commandapi.arguments.GreedyStringArgument; 7 | import dev.jorel.commandapi.arguments.StringArgument; 8 | import dev.unnm3d.redischat.RedisChat; 9 | import dev.unnm3d.redischat.api.objects.Channel; 10 | import lombok.AllArgsConstructor; 11 | import net.kyori.adventure.text.Component; 12 | import net.kyori.adventure.text.minimessage.MiniMessage; 13 | 14 | import java.util.Optional; 15 | 16 | @AllArgsConstructor 17 | public class BroadcastCommand { 18 | private final RedisChat plugin; 19 | 20 | 21 | public CommandAPICommand getBroadcastCommand() { 22 | return new CommandAPICommand("rbroadcast") 23 | .withAliases(plugin.config.getCommandAliases("rbroadcast")) 24 | .withPermission("redischat.broadcast") 25 | .withArguments(new StringArgument("channel") 26 | .replaceSuggestions(ArgumentSuggestions.strings(getChannelsWithPublic()))) 27 | .withArguments(new GreedyStringArgument("message")) 28 | .executes((sender, args) -> { 29 | final Optional channel = plugin.getChannelManager().getChannel((String) args.get(0), sender); 30 | final String message = (String) args.get(1); 31 | if (message == null) return; 32 | if (message.isEmpty()) return; 33 | if (channel.isEmpty()) { 34 | plugin.messages.sendMessage(sender, plugin.messages.channelNotFound); 35 | return; 36 | } 37 | new UniversalRunnable() { 38 | @Override 39 | public void run() { 40 | final Component component = plugin.getComponentProvider().parse(null, 41 | plugin.config.broadcast_format.replace("{message}", message), 42 | true, false, false); 43 | plugin.getChannelManager().broadcastMessage(channel.get(), MiniMessage.miniMessage().serialize(component)); 44 | } 45 | }.runTaskAsynchronously(plugin); 46 | }); 47 | } 48 | 49 | public CommandAPICommand getBroadcastRawCommand() { 50 | return new CommandAPICommand("rbroadcastraw") 51 | .withAliases(plugin.config.getCommandAliases("rbroadcastraw")) 52 | .withPermission("redischat.broadcastraw") 53 | .withArguments(new StringArgument("channel") 54 | .replaceSuggestions(ArgumentSuggestions.strings(getChannelsWithPublic()))) 55 | .withArguments(new GreedyStringArgument("message")) 56 | .executes((sender, args) -> { 57 | final Optional channel = plugin.getChannelManager().getChannel((String) args.get(0), sender); 58 | final String message = (String) args.get(1); 59 | if (message == null) return; 60 | if (message.isEmpty()) return; 61 | if (channel.isEmpty()) { 62 | plugin.messages.sendMessage(sender, plugin.messages.channelNotFound); 63 | return; 64 | } 65 | new UniversalRunnable() { 66 | @Override 67 | public void run() { 68 | final Component component = plugin.getComponentProvider().parse(null, 69 | message, 70 | true, false, false); 71 | 72 | plugin.getChannelManager().broadcastMessage(channel.get(), MiniMessage.miniMessage().serialize(component)); 73 | } 74 | }.runTaskAsynchronously(plugin); 75 | }); 76 | } 77 | 78 | private String[] getChannelsWithPublic() { 79 | final String[] array = new String[plugin.getChannelManager().getRegisteredChannels().size() + 1]; 80 | array[0] = "public"; 81 | int index = 1; 82 | 83 | for (String s : plugin.getChannelManager().getRegisteredChannels().keySet()) { 84 | array[index] = s; 85 | index++; 86 | } 87 | return array; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/ChatAsCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.jorel.commandapi.CommandAPICommand; 4 | import dev.jorel.commandapi.arguments.GreedyStringArgument; 5 | import dev.jorel.commandapi.arguments.PlayerArgument; 6 | import dev.jorel.commandapi.arguments.StringArgument; 7 | import dev.unnm3d.redischat.RedisChat; 8 | import lombok.AllArgsConstructor; 9 | import org.bukkit.entity.Player; 10 | 11 | @AllArgsConstructor 12 | public class ChatAsCommand { 13 | private RedisChat plugin; 14 | 15 | public CommandAPICommand getCommand() { 16 | return new CommandAPICommand("chatas") 17 | .withPermission("redischat.chatas") 18 | .withArguments( 19 | new PlayerArgument("player"), 20 | new StringArgument("channel"), 21 | new GreedyStringArgument("message")) 22 | .executes((sender, args) -> { 23 | final Player target = (Player) args.get(0); 24 | if (target == null) return; 25 | final String message = (String) args.get(2); 26 | if (message == null) return; 27 | 28 | RedisChat.getScheduler().runTaskAsynchronously(() -> 29 | plugin.getChannelManager().outgoingMessage(target, message)); 30 | 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/ChatColorCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.chat.ChatColorGUI; 6 | import lombok.AllArgsConstructor; 7 | import net.kyori.adventure.text.format.NamedTextColor; 8 | import org.bukkit.command.Command; 9 | import org.bukkit.command.CommandExecutor; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.command.TabCompleter; 12 | import org.bukkit.entity.Player; 13 | import org.jetbrains.annotations.NotNull; 14 | import xyz.xenondevs.invui.window.Window; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | @AllArgsConstructor 21 | public class ChatColorCommand implements CommandExecutor, TabCompleter { 22 | private final RedisChat plugin; 23 | 24 | @Override 25 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 26 | if (!(sender instanceof Player player)) return true; 27 | if (plugin.config.enableChatColorGUI) { 28 | Window.single() 29 | .setTitle(plugin.guiSettings.chatColorGUITitle) 30 | .setGui(new ChatColorGUI(plugin)) 31 | .open(player); 32 | return true; 33 | } 34 | 35 | if (args.length == 0) { 36 | plugin.messages.sendMessage(player, plugin.messages.missing_arguments); 37 | return true; 38 | } 39 | if(args[0].equalsIgnoreCase("reset")){ 40 | plugin.getPlaceholderManager().removePlayerPlaceholder(player.getName(), "chat_color"); 41 | plugin.messages.sendMessage(player, plugin.messages.color_set); 42 | return true; 43 | } 44 | if (!args[0].matches("#[0-9A-Fa-f]{6}")) { 45 | if (!getAvailableColors().contains(args[0])) { 46 | plugin.messages.sendMessage(player, plugin.messages.invalid_color); 47 | return true; 48 | } else if (!player.hasPermission(Permissions.CHAT_COLOR.getPermission() + "." + args[0].toLowerCase())) { 49 | plugin.messages.sendMessage(player, plugin.messages.noPermission); 50 | return true; 51 | } 52 | } else if (!player.hasPermission(Permissions.CHAT_COLOR.getPermission() + ".hex")) { 53 | plugin.messages.sendMessage(player, plugin.messages.noPermission); 54 | return true; 55 | } 56 | 57 | plugin.getPlaceholderManager().addPlayerPlaceholder(player.getName(), "chat_color", "<" + args[0] + ">"); 58 | plugin.messages.sendMessage(player, plugin.messages.color_set); 59 | return true; 60 | } 61 | 62 | @Override 63 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { 64 | if (!sender.hasPermission(Permissions.CHAT_COLOR.getPermission()) || plugin.config.enableChatColorGUI) 65 | return List.of(); 66 | final List colors = new ArrayList<>(getAvailableColors()); 67 | colors.add("#RRGGBB"); 68 | colors.add("reset"); 69 | return colors.stream().filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase())).toList(); 70 | } 71 | 72 | private Set getAvailableColors() { 73 | return NamedTextColor.NAMES.keys(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/ClearChatCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import com.github.Anon8281.universalScheduler.UniversalRunnable; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.ChatMessage; 6 | import lombok.AllArgsConstructor; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandExecutor; 9 | import org.bukkit.command.CommandSender; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | @AllArgsConstructor 13 | public class ClearChatCommand implements CommandExecutor { 14 | private final RedisChat plugin; 15 | 16 | @Override 17 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 18 | new UniversalRunnable() { 19 | @Override 20 | public void run() { 21 | plugin.getDataManager().sendChatMessage( 22 | new ChatMessage(RedisChat.getInstance().config.clear_chat_message)); 23 | } 24 | }.runTaskAsynchronously(plugin); 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/IgnoreCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import lombok.AllArgsConstructor; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.command.TabCompleter; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | @AllArgsConstructor 18 | public class IgnoreCommand implements CommandExecutor, TabCompleter { 19 | private final RedisChat plugin; 20 | 21 | @Override 22 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 23 | if (!(sender instanceof Player)) return true; 24 | if (args.length == 0) { 25 | plugin.messages.sendMessage(sender, plugin.messages.missing_arguments); 26 | return true; 27 | } 28 | 29 | if (label.equalsIgnoreCase("ignore") && plugin.getChannelManager().getMuteManager() 30 | .isWhitelistEnabledPlayer(sender.getName())) { 31 | plugin.messages.sendMessage(sender, plugin.messages.ignore_whitelist_enabled); 32 | return true; 33 | } else if (label.equalsIgnoreCase("allowmsg") && !plugin.getChannelManager().getMuteManager() 34 | .isWhitelistEnabledPlayer(sender.getName())) { 35 | plugin.messages.sendMessage(sender, plugin.messages.ignore_whitelist_disabled); 36 | return true; 37 | } 38 | 39 | if (args[0].equalsIgnoreCase(sender.getName())) { 40 | plugin.getComponentProvider().sendMessage(sender, plugin.getComponentProvider().parse(sender, plugin.messages.cannot_ignore_yourself, 41 | true, false, false)); 42 | return true; 43 | } 44 | 45 | if (args[0].equalsIgnoreCase("list")) { 46 | final String stringList = String.join(", ", plugin.getChannelManager().getMuteManager().getIgnoreList(sender.getName())); 47 | plugin.getComponentProvider().sendMessage(sender, 48 | plugin.getComponentProvider().parse(null, 49 | plugin.messages.ignoring_list.replace("%list%", stringList), true, false, false)); 50 | 51 | return true; 52 | } 53 | 54 | //Apply ignore 55 | final String message = plugin.getChannelManager().getMuteManager().toggleIgnorePlayer(sender.getName(), args[0]) ? 56 | plugin.messages.ignoring_player.replace("%player%", args[0]) : 57 | plugin.messages.not_ignoring_player.replace("%player%", args[0]); 58 | 59 | plugin.getComponentProvider().sendMessage(sender, plugin.getComponentProvider().parse(null, message, true, false, false)); 60 | 61 | return true; 62 | } 63 | 64 | @Nullable 65 | @Override 66 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { 67 | if (!sender.hasPermission(Permissions.IGNORE.getPermission()) || args.length > 1) { 68 | return List.of(); 69 | } 70 | 71 | List suggestions = new ArrayList<>(); 72 | suggestions.add("list"); 73 | 74 | if (!plugin.config.allPlayersString.isEmpty()) { 75 | suggestions.add(plugin.config.allPlayersString); 76 | } 77 | 78 | String input = args.length > 0 ? args[0].toLowerCase() : ""; 79 | suggestions.addAll(plugin.getPlayerListManager().getPlayerList(sender) 80 | .stream() 81 | .filter(name -> name.toLowerCase().startsWith(input)) 82 | .toList()); 83 | 84 | return suggestions; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/IgnoreWhitelistCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.jorel.commandapi.CommandAPICommand; 4 | import dev.jorel.commandapi.arguments.PlayerArgument; 5 | import dev.unnm3d.redischat.Permissions; 6 | import dev.unnm3d.redischat.RedisChat; 7 | import lombok.AllArgsConstructor; 8 | import org.bukkit.entity.Player; 9 | 10 | 11 | @AllArgsConstructor 12 | public class IgnoreWhitelistCommand { 13 | private final RedisChat plugin; 14 | 15 | public CommandAPICommand getCommand() { 16 | return new CommandAPICommand("ignorewhitelist") 17 | .withPermission(Permissions.IGNORE_WHITELIST.getPermission()) 18 | .withOptionalArguments(new PlayerArgument("player")) 19 | .executesPlayer((sender, args) -> { 20 | //Get the target player name or sender name 21 | final String targetName = args.getOptional("player") 22 | .map(o -> ((Player) o).getName()) 23 | .orElseGet(sender::getName); 24 | if (!sender.hasPermission(Permissions.IGNORE_WHITELIST.getPermission() + ".other") && 25 | !sender.getName().equals(targetName)) { 26 | plugin.messages.sendMessage(sender, plugin.messages.noPermission); 27 | return; 28 | } 29 | if (plugin.getChannelManager().getMuteManager().isWhitelistEnabledPlayer(targetName)) { 30 | plugin.getChannelManager().getMuteManager().setWhitelistEnabledPlayer(targetName, false); 31 | plugin.messages.sendMessage(sender, plugin.messages.ignore_whitelist_disabled); 32 | } else { 33 | plugin.getChannelManager().getMuteManager().setWhitelistEnabledPlayer(targetName, true); 34 | plugin.messages.sendMessage(sender, plugin.messages.ignore_whitelist_enabled); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/RedisChatCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import net.william278.uniform.paper.LegacyPaperCommand; 4 | 5 | public interface RedisChatCommand { 6 | LegacyPaperCommand getCommand(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/SetItemCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import lombok.AllArgsConstructor; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.command.TabCompleter; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.ItemStack; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.lang.reflect.Field; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | 18 | @AllArgsConstructor 19 | public class SetItemCommand implements CommandExecutor, TabCompleter { 20 | private RedisChat plugin; 21 | 22 | @Override 23 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 24 | 25 | if (!(sender instanceof Player player)) { 26 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.noConsole); 27 | return true; 28 | } 29 | 30 | if (args.length == 0) { 31 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.missing_arguments); 32 | return true; 33 | } 34 | plugin.guiSettings.setIngredient(args[0], player.getInventory().getItemInMainHand()); 35 | plugin.saveGuiSettings(); 36 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.itemSet); 37 | 38 | return true; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { 44 | if (args.length == 1) { 45 | return Arrays.stream(plugin.guiSettings.getClass().getDeclaredFields()) 46 | .filter(field -> field.getType().equals(ItemStack.class)) 47 | .map(Field::getName) 48 | .filter(name -> name.startsWith(args[0])) 49 | .toList(); 50 | } 51 | return List.of(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/SetPlaceholderCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import lombok.AllArgsConstructor; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.command.TabCompleter; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.List; 13 | 14 | @AllArgsConstructor 15 | public class SetPlaceholderCommand implements CommandExecutor, TabCompleter { 16 | private final RedisChat plugin; 17 | 18 | @Override 19 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 20 | if (args.length < 3) { 21 | plugin.messages.sendMessage(sender, plugin.messages.missing_arguments); 22 | return true; 23 | } 24 | if (args[2].equals("delete")) { 25 | plugin.getPlaceholderManager().removePlayerPlaceholder(args[0], args[1]); 26 | plugin.messages.sendMessage(sender, plugin.messages.placeholder_deleted 27 | .replace("%placeholder%", args[1]) 28 | .replace("%player%", args[0])); 29 | return true; 30 | } 31 | 32 | plugin.getPlaceholderManager().addPlayerPlaceholder(args[0], args[1], args[2]); 33 | 34 | plugin.messages.sendMessage(sender, plugin.messages.placeholder_set 35 | .replace("%player%", args[0]) 36 | .replace("%placeholder%", args[1]) 37 | .replace("%value%", args[2].replace("<", "\\<")) 38 | ); 39 | return true; 40 | } 41 | 42 | @Nullable 43 | @Override 44 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { 45 | if (args.length == 1) { 46 | return plugin.getPlayerListManager().getPlayerList(sender) 47 | .stream().filter(s -> 48 | s.toLowerCase().startsWith(args[args.length - 1].toLowerCase()) 49 | ).toList(); 50 | } else if (args.length == 2 && args[args.length - 1].isEmpty()) { 51 | return List.of("chat_color", "placeholder"); 52 | } else if (args.length == 3 && args[args.length - 1].isEmpty()) { 53 | return List.of("value"); 54 | } 55 | return List.of(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/TalkOnCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import dev.unnm3d.redischat.api.objects.ChannelAudience; 6 | import lombok.AllArgsConstructor; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandExecutor; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.command.TabCompleter; 11 | import org.bukkit.entity.Player; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | @AllArgsConstructor 20 | public class TalkOnCommand implements CommandExecutor, TabCompleter { 21 | private RedisChat plugin; 22 | 23 | @Override 24 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 25 | 26 | if (!(sender instanceof Player)) { 27 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.noConsole); 28 | return true; 29 | } 30 | 31 | if (args.length == 0) { 32 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.missing_arguments); 33 | return true; 34 | } 35 | 36 | if (getAvailableChannelNames(sender).noneMatch(name -> name.equalsIgnoreCase(args[0])) && !args[0].equalsIgnoreCase("public")) { 37 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.channelNotFound); 38 | return true; 39 | } 40 | 41 | plugin.getChannelManager().setActiveChannel(sender.getName(), args[0].equalsIgnoreCase("public") ? null : args[0]); 42 | plugin.messages.sendMessage(sender, plugin.messages.channelTalk 43 | .replace("%channel%", args[0]) 44 | ); 45 | 46 | return true; 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { 52 | if (args.length == 1) { 53 | return getAvailableChannelNames(sender) 54 | .filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase())) 55 | .collect(Collectors.collectingAndThen(Collectors.toList(), list -> { 56 | if ("public".startsWith(args[0].toLowerCase())) list.add("public"); 57 | return list; 58 | })); 59 | } 60 | return List.of(); 61 | } 62 | 63 | public Stream getAvailableChannelNames(CommandSender sender) { 64 | return plugin.getChannelManager().getAllChannels().stream() 65 | .filter(channel -> { 66 | if (channel.isShownByDefault()) { 67 | return sender.isOp() || !sender.hasPermission(Permissions.CHANNEL_HIDE_PREFIX.getPermission() + channel.getName()); 68 | } 69 | return sender.hasPermission(Permissions.CHANNEL_SHOW_PREFIX.getPermission() + channel.getName()); 70 | 71 | }) 72 | .map(ChannelAudience::getName); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/UniformMsgCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import dev.unnm3d.redischat.Permissions; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import lombok.AllArgsConstructor; 7 | import net.william278.uniform.Permission; 8 | import net.william278.uniform.paper.LegacyPaperCommand; 9 | 10 | import java.util.List; 11 | 12 | @AllArgsConstructor 13 | public class UniformMsgCommand implements RedisChatCommand { 14 | 15 | @Override 16 | public LegacyPaperCommand getCommand() { 17 | RedisChat plugin = RedisChat.getInstance(); 18 | return LegacyPaperCommand.builder("msg") 19 | .setDescription("Send a message to another player") 20 | .setAliases(plugin.config.commandAliases.getOrDefault("msg", List.of())) 21 | .setPermission(new Permission(Permissions.MESSAGE.getPermission(), Permission.Default.TRUE)) 22 | .addArgument("target", StringArgumentType.word(), (context, builder) -> { 23 | RedisChat.getInstance().getPlayerListManager().getPlayerList(context.getSource()) 24 | .stream().filter(playerName -> playerName.toLowerCase().startsWith(builder.getRemainingLowerCase())) 25 | .forEach(builder::suggest); 26 | return builder.buildFuture(); 27 | }) 28 | .execute(commandContext -> RedisChat.getScheduler().runTaskAsynchronously(() -> { 29 | String target = commandContext.getArgument("target", String.class); 30 | String content = commandContext.getArgument("content", String.class); 31 | if (content.isEmpty()) { 32 | plugin.messages.sendMessage(commandContext.getSource(), plugin.messages.missing_arguments); 33 | return; 34 | } 35 | if (target.isEmpty()) { 36 | plugin.messages.sendMessage(commandContext.getSource(), plugin.messages.missing_arguments); 37 | return; 38 | } 39 | 40 | if (!plugin.config.debug) 41 | if (target.equalsIgnoreCase(commandContext.getSource().getName())) { 42 | plugin.messages.sendMessage(commandContext.getSource(), plugin.messages.cannot_message_yourself); 43 | return; 44 | } 45 | 46 | if (!plugin.getPlayerListManager().getPlayerList(commandContext.getSource()).contains(target)) { 47 | plugin.messages.sendMessage(commandContext.getSource(), 48 | plugin.messages.player_not_online.replace("%player%", target)); 49 | return; 50 | } 51 | 52 | plugin.getChannelManager().outgoingPrivateMessage(commandContext.getSource(), target, content); 53 | 54 | //Set reply name for /reply 55 | plugin.getDataManager().setReplyName(target, commandContext.getSource().getName()); 56 | 57 | }), "target") 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/commands/UniformReplyCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.commands; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import dev.unnm3d.redischat.Permissions; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import lombok.AllArgsConstructor; 7 | import net.william278.uniform.BaseCommand; 8 | import net.william278.uniform.Permission; 9 | import net.william278.uniform.paper.LegacyPaperCommand; 10 | import org.bukkit.command.CommandSender; 11 | 12 | import java.util.List; 13 | 14 | @AllArgsConstructor 15 | public class UniformReplyCommand implements RedisChatCommand { 16 | 17 | @Override 18 | public LegacyPaperCommand getCommand() { 19 | RedisChat plugin = RedisChat.getInstance(); 20 | return LegacyPaperCommand.builder("reply") 21 | .setAliases(plugin.config.commandAliases.getOrDefault("reply", List.of())) 22 | .setPermission(new Permission(Permissions.MESSAGE.getPermission(), Permission.Default.TRUE)) 23 | .setDescription("Send a message to another player") 24 | .addArgument("content", StringArgumentType.greedyString(), 25 | (commandContext, builder) -> { 26 | if (builder.getRemaining().isEmpty()) { 27 | builder.suggest(plugin.messages.replySuggestion); 28 | } 29 | return builder.buildFuture(); 30 | }) 31 | .execute(commandContext -> RedisChat.getScheduler().runTaskAsynchronously(() -> { 32 | CommandSender sender = commandContext.getSource(); 33 | plugin.getDataManager().getReplyName(sender.getName()) 34 | .thenAcceptAsync(receiverOpt -> { 35 | if (receiverOpt.isEmpty()) { 36 | plugin.getComponentProvider().sendMessage(sender, plugin.messages.no_reply_found); 37 | return; 38 | } 39 | 40 | String receiverName = receiverOpt.get(); 41 | if (!plugin.getPlayerListManager().getPlayerList(sender).contains(receiverName)) { 42 | plugin.getComponentProvider().sendMessage(sender, 43 | plugin.messages.reply_not_online.replace("%player%", receiverName)); 44 | return; 45 | } 46 | 47 | String content = commandContext.getArgument("content", String.class); 48 | plugin.getChannelManager().outgoingPrivateMessage(sender, receiverName, content); 49 | 50 | plugin.getDataManager().setReplyName(receiverName, sender.getName()); 51 | 52 | }, plugin.getExecutorService()); 53 | }), "content") 54 | .build(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/datamanagers/DataKey.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.datamanagers; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | 5 | public enum DataKey { 6 | CHAT_CHANNEL("rchat:chat"), 7 | GLOBAL_CHANNEL("rchat:g_chat"), 8 | CHANNEL_UPDATE("rchat:ch_update"), 9 | REJOIN_CHANNEL("rchat:rejoin"), 10 | PLAYERLIST("rchat:playerlist"), 11 | MAIL_UPDATE_CHANNEL("rchat:mail_update"), 12 | PLAYER_ACTIVE_CHANNEL_PREFIX("rchat:ach_"), 13 | PLAYER_ACTIVE_CHANNEL_UPDATE("rchat:achup"), 14 | CHANNELS("rchat:ch"), 15 | ACTIVE_CHANNEL_ID("!activech"), 16 | IGNORE_PREFIX("rchat:ignore_"), 17 | MUTED_ENTITIES("rchat:muted"), 18 | RATE_LIMIT_PREFIX("rchat:ratelimit_"), 19 | REPLY("rchat:reply"), 20 | PLAYER_PLACEHOLDERS("rchat:p_ph"), 21 | INVSHARE_ITEM("rchat:item"), 22 | INVSHARE_INVENTORY("rchat:inventory"), 23 | INVSHARE_ENDERCHEST("rchat:enderchest"), 24 | SPYING_LIST("rchat:spying"), 25 | PRIVATE_MAIL_PREFIX("rmail:"), 26 | PUBLIC_MAIL("rmail:public"), 27 | MUTED_UPDATE("rchat:m_update"), 28 | WHITELIST_ENABLED_PLAYERS("rchat:wl_enabled"), 29 | WHITELIST_ENABLED_UPDATE("rchat:wl_enabled_update"), 30 | READ_MAIL_MAP("rchat:read_mails:"), 31 | DELETE_TAG("§del§"), 32 | ; 33 | 34 | private final String keyName; 35 | 36 | 37 | /** 38 | * @param keyName the name of the key 39 | */ 40 | DataKey(final String keyName) { 41 | this.keyName = keyName; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return RedisChat.getInstance().config.clusterId + keyName; 47 | } 48 | 49 | public String withoutCluster() { 50 | return keyName; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/datamanagers/redistools/RedisAbstract.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.datamanagers.redistools; 2 | 3 | import io.lettuce.core.RedisClient; 4 | import io.lettuce.core.TransactionResult; 5 | import io.lettuce.core.api.StatefulRedisConnection; 6 | import io.lettuce.core.api.async.RedisAsyncCommands; 7 | import io.lettuce.core.api.sync.RedisCommands; 8 | import io.lettuce.core.pubsub.RedisPubSubListener; 9 | import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; 10 | import org.bukkit.Bukkit; 11 | 12 | import java.util.List; 13 | import java.util.Optional; 14 | import java.util.concurrent.CompletionStage; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | import java.util.function.Consumer; 17 | import java.util.function.Function; 18 | 19 | 20 | public abstract class RedisAbstract { 21 | private final RoundRobinConnectionPool roundRobinConnectionPool; 22 | private final ConcurrentHashMap> pubSubConnections; 23 | protected RedisClient lettuceRedisClient; 24 | 25 | public RedisAbstract(RedisClient lettuceRedisClient, int poolSize) { 26 | this.lettuceRedisClient = lettuceRedisClient; 27 | this.roundRobinConnectionPool = new RoundRobinConnectionPool<>(lettuceRedisClient::connect, poolSize); 28 | this.pubSubConnections = new ConcurrentHashMap<>(); 29 | } 30 | 31 | public abstract void receiveMessage(String channel, String message); 32 | 33 | protected void registerSub(String... listenedChannels) { 34 | if (listenedChannels.length == 0) { 35 | return; 36 | } 37 | final StatefulRedisPubSubConnection pubSubConnection = lettuceRedisClient.connectPubSub(); 38 | pubSubConnections.put(listenedChannels, pubSubConnection); 39 | pubSubConnection.addListener(new RedisPubSubListener<>() { 40 | @Override 41 | public void message(String channel, String message) { 42 | receiveMessage(channel, message); 43 | } 44 | 45 | @Override 46 | public void message(String pattern, String channel, String message) { 47 | } 48 | 49 | @Override 50 | public void subscribed(String channel, long count) { 51 | } 52 | 53 | @Override 54 | public void psubscribed(String pattern, long count) { 55 | } 56 | 57 | @Override 58 | public void unsubscribed(String channel, long count) { 59 | } 60 | 61 | @Override 62 | public void punsubscribed(String pattern, long count) { 63 | } 64 | }); 65 | pubSubConnection.async().subscribe(listenedChannels) 66 | .exceptionally(throwable -> { 67 | throwable.printStackTrace(); 68 | return null; 69 | }); 70 | } 71 | 72 | public CompletionStage getConnectionAsync(Function, CompletionStage> redisCallBack) { 73 | return redisCallBack.apply(roundRobinConnectionPool.get().async()); 74 | } 75 | 76 | public CompletionStage getConnectionPipeline(Function, CompletionStage> redisCallBack) { 77 | StatefulRedisConnection connection = roundRobinConnectionPool.get(); 78 | connection.setAutoFlushCommands(false); 79 | CompletionStage completionStage = redisCallBack.apply(connection.async()); 80 | connection.flushCommands(); 81 | connection.setAutoFlushCommands(true); 82 | return completionStage; 83 | } 84 | 85 | public Optional> executeTransaction(Consumer> redisCommandsConsumer) { 86 | final RedisCommands syncCommands = roundRobinConnectionPool.get().sync(); 87 | syncCommands.multi(); 88 | redisCommandsConsumer.accept(syncCommands); 89 | final TransactionResult transactionResult = syncCommands.exec(); 90 | return Optional.ofNullable(transactionResult.wasDiscarded() ? null : transactionResult.stream().toList()); 91 | } 92 | 93 | public void close() { 94 | pubSubConnections.values().forEach(StatefulRedisPubSubConnection::close); 95 | Bukkit.getLogger().info("Closing pubsub connection"); 96 | lettuceRedisClient.shutdown(); 97 | Bukkit.getLogger().info("Lettuce shutdown connection"); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/datamanagers/redistools/RoundRobinConnectionPool.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.datamanagers.redistools; 2 | 3 | 4 | import io.lettuce.core.api.StatefulRedisConnection; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | import java.util.function.Supplier; 8 | 9 | public class RoundRobinConnectionPool { 10 | private final AtomicInteger next = new AtomicInteger(0); 11 | private final StatefulRedisConnection[] elements; 12 | private final Supplier> statefulRedisConnectionSupplier; 13 | 14 | @SuppressWarnings("unchecked") 15 | public RoundRobinConnectionPool(Supplier> statefulRedisConnectionSupplier, int poolSize) { 16 | this.statefulRedisConnectionSupplier = statefulRedisConnectionSupplier; 17 | this.elements = new StatefulRedisConnection[poolSize]; 18 | for (int i = 0; i < poolSize; i++) { 19 | elements[i] = statefulRedisConnectionSupplier.get(); 20 | } 21 | } 22 | 23 | public StatefulRedisConnection get() { 24 | int index = next.getAndIncrement() % elements.length; 25 | StatefulRedisConnection connection = elements[index]; 26 | if (connection != null) 27 | if (connection.isOpen()) 28 | if (connection.isMulti()) { 29 | return get(); 30 | } else 31 | return connection; 32 | 33 | connection = statefulRedisConnectionSupplier.get(); 34 | elements[index] = connection; 35 | return connection; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/datamanagers/sqlmanagers/MySQLDataManager.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.datamanagers.sqlmanagers; 2 | 3 | import com.zaxxer.hikari.HikariDataSource; 4 | import dev.unnm3d.redischat.RedisChat; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.Map; 10 | import java.util.Properties; 11 | 12 | public class MySQLDataManager extends SQLDataManager { 13 | private HikariDataSource dataSource; 14 | 15 | public MySQLDataManager(RedisChat plugin) { 16 | super(plugin); 17 | initialize(); 18 | } 19 | 20 | @Override 21 | protected void initialize() throws IllegalStateException { 22 | // Initialize the Hikari pooled connection 23 | dataSource = new HikariDataSource(); 24 | 25 | dataSource.setDriverClassName(plugin.config.mysql.driverClass()); 26 | dataSource.setJdbcUrl("jdbc:" + (plugin.config.mysql.driverClass().contains("mariadb") ? "mariadb" : "mysql") 27 | + "://" 28 | + plugin.config.mysql.host() 29 | + ":" 30 | + plugin.config.mysql.port() 31 | + "/" 32 | + plugin.config.mysql.database() 33 | + plugin.config.mysql.connectionParameters() 34 | ); 35 | 36 | 37 | // Authenticate with the database 38 | dataSource.setUsername(plugin.config.mysql.username()); 39 | dataSource.setPassword(plugin.config.mysql.password()); 40 | 41 | // Set connection pool options 42 | dataSource.setMaximumPoolSize(plugin.config.mysql.poolSize()); 43 | dataSource.setMinimumIdle(plugin.config.mysql.poolIdle()); 44 | dataSource.setConnectionTimeout(plugin.config.mysql.poolTimeout()); 45 | dataSource.setMaxLifetime(plugin.config.mysql.poolLifetime()); 46 | dataSource.setKeepaliveTime(plugin.config.mysql.poolKeepAlive()); 47 | 48 | // Set additional connection pool properties 49 | final Properties properties = new Properties(); 50 | properties.putAll( 51 | Map.of("cachePrepStmts", "true", 52 | "prepStmtCacheSize", "250", 53 | "prepStmtCacheSqlLimit", "2048", 54 | "useServerPrepStmts", "true", 55 | "useLocalSessionState", "true", 56 | "useLocalTransactionState", "true" 57 | )); 58 | properties.putAll( 59 | Map.of( 60 | "rewriteBatchedStatements", "true", 61 | "cacheResultSetMetadata", "true", 62 | "cacheServerConfiguration", "true", 63 | "elideSetAutoCommits", "true", 64 | "maintainTimeStats", "false") 65 | ); 66 | dataSource.setDataSourceProperties(properties); 67 | 68 | // Prepare database schema; make tables if they don't exist 69 | try (Connection connection = dataSource.getConnection()) { 70 | try (Statement statement = connection.createStatement()) { 71 | for (String tableCreationStatement : getSQLSchema()) { 72 | statement.execute(tableCreationStatement); 73 | } 74 | } catch (SQLException e) { 75 | throw new IllegalStateException("Failed to create database tables. Make sure you're running MySQL v8.0+" 76 | + "and that your connecting user account has privileges to create tables.", e); 77 | } 78 | } catch (SQLException e) { 79 | throw new IllegalStateException("Failed to establish a connection to the MySQL database. " 80 | + "Please check the supplied database credentials in the config file", e); 81 | } 82 | } 83 | 84 | @Override 85 | protected Connection getConnection() throws SQLException { 86 | return dataSource.getConnection(); 87 | } 88 | 89 | @Override 90 | public void close() { 91 | dataSource.close(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/discord/DiscordWebhook.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.discord; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import dev.unnm3d.redischat.api.objects.Channel; 5 | import dev.unnm3d.redischat.api.objects.ChatMessage; 6 | import net.kyori.adventure.text.minimessage.MiniMessage; 7 | 8 | import javax.net.ssl.HttpsURLConnection; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.net.URL; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.Optional; 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | public class DiscordWebhook implements IDiscordHook { 17 | private final RedisChat plugin; 18 | 19 | 20 | public DiscordWebhook(RedisChat plugin) { 21 | this.plugin = plugin; 22 | } 23 | 24 | @Override 25 | public void sendDiscordMessage(ChatMessage message) { 26 | if (message.getReceiver().isChannel()) { 27 | final Optional channel = plugin.getChannelManager().getChannel(message.getReceiver().getName(), null); 28 | if(channel.isEmpty()) { 29 | plugin.getLogger().warning("Channel not found: " + message.getReceiver().getName()); 30 | return; 31 | } 32 | 33 | if (channel.get().getDiscordWebhook() == null || channel.get().getDiscordWebhook().isEmpty() || message.getSender().isDiscord()) 34 | return; 35 | 36 | CompletableFuture.runAsync(() -> { 37 | try { 38 | final HttpsURLConnection connection = (HttpsURLConnection) new URL(channel.get().getDiscordWebhook()).openConnection(); 39 | connection.setRequestMethod("POST"); 40 | connection.setRequestProperty("Content-Type", "application/json"); 41 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11"); 42 | connection.setDoOutput(true); 43 | try (final OutputStream outputStream = connection.getOutputStream()) { 44 | final String avatarUrl = !message.getSender().isServer() ? 45 | plugin.getServer().getOnlinePlayers().stream() 46 | .filter(p -> p.getName().equals(message.getSender().getName())) 47 | .findFirst() 48 | .map(p -> "https://minotar.net/helm/" + p.getUniqueId().toString().replace("-", "") + ".png") 49 | .orElse("") : 50 | ""; 51 | 52 | outputStream.write((String.format(""" 53 | {"username": "%s", 54 | "avatar_url": "%s", 55 | "content": "%s", 56 | "embeds": [] 57 | } 58 | """, 59 | message.getSender().isServer() ? "Server" : message.getSender().getName(), 60 | avatarUrl, 61 | MiniMessage.miniMessage().stripTags(message.getContent()) 62 | ).getBytes(StandardCharsets.UTF_8))); 63 | } 64 | connection.getInputStream(); 65 | } catch (final IOException e) { 66 | throw new RuntimeException(e); 67 | } 68 | }, plugin.getExecutorService()).exceptionally((e) -> { 69 | plugin.getLogger().warning("Unable to send message to Discord channel " + channel.get().getName() + ": " + e.getMessage()); 70 | return null; 71 | }); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/discord/IDiscordHook.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.discord; 2 | 3 | import dev.unnm3d.redischat.api.objects.ChatMessage; 4 | 5 | public interface IDiscordHook { 6 | 7 | default void sendDiscordMessage(ChatMessage message) { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/integrations/OraxenTagResolver.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.integrations; 2 | 3 | import dev.unnm3d.redischat.api.TagResolverIntegration; 4 | import io.th0rgal.oraxen.utils.AdventureUtils; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class OraxenTagResolver implements TagResolverIntegration { 8 | @Override 9 | public @NotNull String parseTags(String message) { 10 | return AdventureUtils.parseMiniMessage(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/integrations/PremiumVanishIntegration.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.integrations; 2 | 3 | import de.myzelyam.api.vanish.PlayerVanishStateChangeEvent; 4 | import de.myzelyam.api.vanish.VanishAPI; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import dev.unnm3d.redischat.api.VanishIntegration; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.Listener; 11 | 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | public class PremiumVanishIntegration implements VanishIntegration { 16 | protected final Set vanishedPlayers = new HashSet<>(); 17 | 18 | public PremiumVanishIntegration(RedisChat plugin) { 19 | plugin.getServer().getPluginManager().registerEvents(new VanishListener(), plugin); 20 | } 21 | 22 | @Override 23 | public boolean canSee(CommandSender viewer, String playerName) { 24 | if (viewer.hasPermission("pv.see")) return true; 25 | return !vanishedPlayers.contains(playerName); 26 | } 27 | 28 | @Override 29 | public boolean isVanished(Player player) { 30 | return VanishAPI.isInvisible(player); 31 | } 32 | 33 | private class VanishListener implements Listener { 34 | @EventHandler 35 | public void onVanish(PlayerVanishStateChangeEvent event) { 36 | if (event.isVanishing()) { 37 | vanishedPlayers.add(event.getName()); 38 | } else { 39 | vanishedPlayers.remove(event.getName()); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/integrations/SuperVanishIntegration.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.integrations; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import org.bukkit.command.CommandSender; 5 | 6 | public class SuperVanishIntegration extends PremiumVanishIntegration { 7 | 8 | public SuperVanishIntegration(RedisChat plugin) { 9 | super(plugin); 10 | } 11 | 12 | @Override 13 | public boolean canSee(CommandSender viewer, String playerName) { 14 | if (viewer.hasPermission("sv.see")) return true; 15 | return !vanishedPlayers.contains(playerName); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/moderation/MuteCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.moderation; 2 | 3 | import dev.jorel.commandapi.CommandAPICommand; 4 | import dev.jorel.commandapi.arguments.Argument; 5 | import dev.jorel.commandapi.arguments.ArgumentSuggestions; 6 | import dev.jorel.commandapi.arguments.StringArgument; 7 | import dev.unnm3d.redischat.Permissions; 8 | import dev.unnm3d.redischat.RedisChat; 9 | import dev.unnm3d.redischat.api.objects.KnownChatEntities; 10 | import lombok.AllArgsConstructor; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | @AllArgsConstructor 17 | public class MuteCommand { 18 | 19 | private RedisChat plugin; 20 | 21 | 22 | public CommandAPICommand getMuteCommand() { 23 | return new CommandAPICommand("rmutechat") 24 | .withPermission(Permissions.CHANNEL_MUTE.getPermission()) 25 | .withAliases(plugin.config.getCommandAliases("rmutechat")) 26 | .withArguments(getPlayerArgument()) 27 | .withOptionalArguments(getChannelArgument()) 28 | .executes((sender, args) -> { 29 | final String playerName = (String) args.get("player"); 30 | if (playerName == null) { 31 | plugin.messages.sendMessage(sender, plugin.messages.missing_arguments); 32 | return; 33 | } 34 | final String channelName = (String) args.getOptional("channel").orElse(KnownChatEntities.GENERAL_CHANNEL.toString()); 35 | 36 | plugin.getChannelManager().getMuteManager().toggleMuteOnChannel(playerName, channelName, true); 37 | plugin.messages.sendMessage(sender, plugin.messages.muted_player 38 | .replace("%player%", playerName) 39 | .replace("%channel%", channelName) 40 | ); 41 | }); 42 | } 43 | 44 | public CommandAPICommand getUnMuteCommand() { 45 | return new CommandAPICommand("runmutechat") 46 | .withPermission(Permissions.CHANNEL_MUTE.getPermission()) 47 | .withAliases(plugin.config.getCommandAliases("runmutechat")) 48 | .withArguments(getPlayerArgument()) 49 | .withOptionalArguments(getChannelArgument()) 50 | .executes((sender, args) -> { 51 | final String playerName = (String) args.get(0); 52 | if (playerName == null) { 53 | plugin.messages.sendMessage(sender, plugin.messages.missing_arguments); 54 | return; 55 | } 56 | String channelName = (String) args.getOptional("channel").orElse(KnownChatEntities.GENERAL_CHANNEL.toString()); 57 | 58 | plugin.getChannelManager().getMuteManager().toggleMuteOnChannel(playerName, channelName, false); 59 | plugin.messages.sendMessage(sender, plugin.messages.unmuted_player 60 | .replace("%player%", playerName) 61 | .replace("%channel%", channelName) 62 | ); 63 | }); 64 | } 65 | 66 | private Argument getPlayerArgument() { 67 | return new StringArgument("player") 68 | .replaceSuggestions(ArgumentSuggestions.stringCollection(functi1 -> { 69 | final List suggestions = plugin.getPlayerListManager().getPlayerList(functi1.sender()) 70 | .stream().filter(s -> s.toLowerCase().startsWith(functi1.currentArg().toLowerCase())) 71 | .collect(Collectors.toCollection(ArrayList::new)); 72 | if (!plugin.config.allPlayersString.isEmpty()) suggestions.add(plugin.config.allPlayersString); 73 | return suggestions; 74 | })); 75 | } 76 | 77 | private Argument getChannelArgument() { 78 | return new StringArgument("channel") 79 | .replaceSuggestions(ArgumentSuggestions.stringCollection(functi1 -> { 80 | final List suggestions = plugin.getChannelManager().getRegisteredChannels().keySet() 81 | .stream().filter(s -> s.toLowerCase().startsWith(functi1.currentArg().toLowerCase())) 82 | .collect(Collectors.toCollection(ArrayList::new)); 83 | suggestions.add(KnownChatEntities.GENERAL_CHANNEL.toString()); 84 | return suggestions; 85 | })); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/moderation/SpyChatCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.moderation; 2 | 3 | import dev.unnm3d.redischat.Permissions; 4 | import dev.unnm3d.redischat.RedisChat; 5 | import lombok.AllArgsConstructor; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | @AllArgsConstructor 13 | public class SpyChatCommand implements CommandExecutor { 14 | private final RedisChat plugin; 15 | 16 | @Override 17 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 18 | 19 | if (args.length == 0 && !(sender instanceof Player)) { 20 | plugin.messages.sendMessage(sender, plugin.messages.player_not_online.replace("%player%", "CONSOLE")); 21 | return true; 22 | } 23 | 24 | String playerName = args.length == 0 ? sender.getName() : args[0]; 25 | if (args.length > 0 && !sender.hasPermission(Permissions.SPY_OTHERS.getPermission())) { 26 | plugin.messages.sendMessage(sender, plugin.messages.noPermission); 27 | return true; 28 | } 29 | 30 | if (plugin.getSpyManager().toggleSpying(playerName)) { 31 | plugin.getComponentProvider().sendMessage(sender, plugin.getComponentProvider().parse(null, plugin.messages.spychat_enabled.replace("%player%", playerName), true, false, false)); 32 | } else { 33 | plugin.getComponentProvider().sendMessage(sender, plugin.getComponentProvider().parse(null, plugin.messages.spychat_disabled.replace("%player%", playerName), true, false, false)); 34 | } 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/moderation/SpyManager.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.moderation; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import org.bukkit.entity.Player; 5 | 6 | import java.util.HashSet; 7 | import java.util.Optional; 8 | import java.util.Set; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.ConcurrentMap; 11 | 12 | public class SpyManager { 13 | private final RedisChat plugin; 14 | private final Set isSpyingNames; 15 | 16 | public SpyManager(RedisChat plugin) { 17 | this.plugin = plugin; 18 | this.isSpyingNames = ConcurrentHashMap.newKeySet(); 19 | } 20 | 21 | public void onJoin(Player player) { 22 | plugin.getDataManager().isSpying(player.getName()).thenAccept(isSpying -> { 23 | if (isSpying) { 24 | this.isSpyingNames.add(player.getName()); 25 | } else { 26 | this.isSpyingNames.remove(player.getName()); 27 | } 28 | }); 29 | } 30 | 31 | public boolean toggleSpying(String playerName) { 32 | if (isSpyingNames.contains(playerName)) { 33 | isSpyingNames.remove(playerName); 34 | plugin.getDataManager().setSpying(playerName, false); 35 | return false; 36 | } else { 37 | isSpyingNames.add(playerName); 38 | plugin.getDataManager().setSpying(playerName, true); 39 | return true; 40 | } 41 | } 42 | 43 | public boolean isSpying(String playerName) { 44 | return isSpyingNames.contains(playerName); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/moderation/StaffChatCommand.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.moderation; 2 | 3 | import com.mojang.brigadier.arguments.StringArgumentType; 4 | import dev.jorel.commandapi.CommandAPICommand; 5 | import dev.jorel.commandapi.arguments.GreedyStringArgument; 6 | import dev.unnm3d.redischat.Permissions; 7 | import dev.unnm3d.redischat.RedisChat; 8 | import dev.unnm3d.redischat.commands.RedisChatCommand; 9 | import lombok.AllArgsConstructor; 10 | import net.william278.uniform.BaseCommand; 11 | import net.william278.uniform.Permission; 12 | import net.william278.uniform.paper.LegacyPaperCommand; 13 | import org.bukkit.command.CommandSender; 14 | 15 | import java.util.List; 16 | 17 | @AllArgsConstructor 18 | public class StaffChatCommand implements RedisChatCommand { 19 | 20 | @Override 21 | public LegacyPaperCommand getCommand() { 22 | return LegacyPaperCommand.builder("staffchat") 23 | .addPermissions(new Permission(Permissions.ADMIN_STAFF_CHAT.getPermission(), Permission.Default.IF_OP)) 24 | .setAliases(RedisChat.getInstance().config.commandAliases.getOrDefault("staffchat", List.of())) 25 | .addArgument("content", StringArgumentType.greedyString(), 26 | (commandContext, builder) -> { 27 | if (builder.getRemaining().isEmpty()) { 28 | builder.suggest(RedisChat.getInstance().messages.replySuggestion); 29 | } 30 | return builder.buildFuture(); 31 | }) 32 | .execute(commandContext -> { 33 | String message = commandContext.getArgument("content", String.class); 34 | if (message == null) return; 35 | if (message.isEmpty()) { 36 | RedisChat.getInstance().messages.sendMessage(commandContext.getSource(), RedisChat.getInstance().messages.missing_arguments); 37 | return; 38 | } 39 | RedisChat.getScheduler().runTaskAsynchronously(() -> 40 | RedisChat.getInstance().getChannelManager().outgoingMessage(commandContext.getSource(), RedisChat.getInstance().getChannelManager().getStaffChatChannel(), message)); 41 | }, "content").build(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/permission/LuckPermsProvider.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.permission; 2 | 3 | import net.luckperms.api.LuckPerms; 4 | import net.luckperms.api.node.Node; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.OfflinePlayer; 7 | import org.bukkit.plugin.RegisteredServiceProvider; 8 | 9 | public class LuckPermsProvider implements PermissionProvider { 10 | private LuckPerms perms; 11 | 12 | public LuckPermsProvider() { 13 | RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(LuckPerms.class); 14 | if (rsp != null) 15 | perms = rsp.getProvider(); 16 | } 17 | 18 | @Override 19 | public void setPermission(OfflinePlayer player, String permission) { 20 | if (perms == null) return; 21 | perms.getUserManager().modifyUser(player.getUniqueId(), user -> 22 | user.data().add( 23 | Node.builder(permission) 24 | .context(perms.getContextManager().getStaticContext()) 25 | .value(true) 26 | .build())); 27 | } 28 | 29 | @Override 30 | public void unsetPermission(OfflinePlayer player, String permission) { 31 | if (perms == null) return; 32 | perms.getUserManager().modifyUser(player.getUniqueId(), user -> 33 | user.data().add( 34 | Node.builder(permission) 35 | .context(perms.getContextManager().getStaticContext()) 36 | .value(false) 37 | .build())); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/permission/PermissionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.permission; 2 | 3 | import org.bukkit.OfflinePlayer; 4 | 5 | public interface PermissionProvider { 6 | 7 | default void setPermission(OfflinePlayer player, String permission) { 8 | } 9 | 10 | default void unsetPermission(OfflinePlayer player, String permission) { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/permission/VaultPermissionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.permission; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import net.milkbowl.vault.permission.Permission; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.OfflinePlayer; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.plugin.RegisteredServiceProvider; 9 | 10 | public class VaultPermissionProvider implements PermissionProvider { 11 | private Permission perms; 12 | 13 | public VaultPermissionProvider() { 14 | RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Permission.class); 15 | if (rsp != null) 16 | perms = rsp.getProvider(); 17 | } 18 | 19 | @Override 20 | public void setPermission(OfflinePlayer player, String permission) { 21 | if (!(player instanceof Player onlinePlayer)) { 22 | RedisChat.getInstance().getLogger().warning("Player " + player.getName() + " is not online, can't set permission " + permission); 23 | return; 24 | } 25 | 26 | if (perms != null) 27 | perms.playerAdd(onlinePlayer, permission); 28 | } 29 | 30 | @Override 31 | public void unsetPermission(OfflinePlayer player, String permission) { 32 | if (!(player instanceof Player onlinePlayer)) { 33 | RedisChat.getInstance().getLogger().warning("Player " + player.getName() + " is not online, can't unset permission " + permission); 34 | return; 35 | } 36 | 37 | if (perms != null) 38 | perms.playerRemove(onlinePlayer, permission); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/settings/ConfigValidator.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.settings; 2 | 3 | public interface ConfigValidator { 4 | boolean validateConfig(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/settings/FiltersConfig.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.settings; 2 | 3 | import de.exlll.configlib.*; 4 | import dev.unnm3d.redischat.chat.filters.DefaultSettings; 5 | import dev.unnm3d.redischat.chat.filters.incoming.IgnorePlayerFilter; 6 | import dev.unnm3d.redischat.chat.filters.outgoing.*; 7 | import dev.unnm3d.redischat.api.objects.AudienceType; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Getter; 10 | 11 | import java.util.*; 12 | 13 | 14 | @Configuration 15 | public final class FiltersConfig { 16 | 17 | //INCOMING 18 | @Comment({"Filters incoming private messages: Checks if the player is ignoring the sender", 19 | "If the player is ignoring the sender, it may send a warning message"}) 20 | public IgnorePlayerFilter.IgnorePlayerFilterProperties ignorePlayer = (IgnorePlayerFilter.IgnorePlayerFilterProperties) DefaultSettings.IGNORE_PLAYER.getFilterSettings(); 21 | @Comment({"Filters incoming channel messages: Checks if the player has all the permissions", 22 | "to see the message (if the channel is permission-enabled)"}) 23 | public FilterSettings permission = DefaultSettings.PERMISSION.getFilterSettings(); 24 | 25 | //OUTGOING 26 | public CapsFilter.CapsFilterProperties caps = (CapsFilter.CapsFilterProperties) DefaultSettings.CAPS.getFilterSettings(); 27 | public FilterSettings spam = DefaultSettings.SPAM.getFilterSettings(); 28 | public DuplicateFilter.DuplicateFilterProperties duplicate = (DuplicateFilter.DuplicateFilterProperties) DefaultSettings.DUPLICATE.getFilterSettings(); 29 | @Comment({"Filters outgoing private messages: If a player is ignoring the receiver", 30 | "or the receiver is ignoring the sender, the message will not be sent"}) 31 | public IgnoreFilter.IgnoreFilterProperties ignore = (IgnoreFilter.IgnoreFilterProperties) DefaultSettings.IGNORE.getFilterSettings(); 32 | @Comment({"Filters outgoing channel messages: If a player is muted on that channel", 33 | "or does not have permission to write, the message will not be sent"}) 34 | public FilterSettings mutedChannel = DefaultSettings.MUTED_CHANNEL.getFilterSettings(); 35 | @Comment({"Filters outgoing channel messages: Replace dangerous tags with safer ones", 36 | "and checks if the player has permission to use them"}) 37 | public FilterSettings tags = DefaultSettings.TAGS.getFilterSettings(); 38 | public WordBlacklistFilter.WordBlacklistFilterProperties words = (WordBlacklistFilter.WordBlacklistFilterProperties) DefaultSettings.WORDS.getFilterSettings(); 39 | 40 | 41 | @Configuration 42 | @AllArgsConstructor 43 | @Getter 44 | public static class FilterSettings { 45 | protected boolean enabled; 46 | protected int priority; 47 | protected Set audienceWhitelist; 48 | protected Set channelWhitelist; 49 | 50 | public FilterSettings() { 51 | this(false, 1, Set.of(), Set.of()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/task/AnnouncerManager.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.task; 2 | 3 | import dev.unnm3d.redischat.RedisChat; 4 | import dev.unnm3d.redischat.settings.Config; 5 | 6 | import java.util.HashMap; 7 | 8 | 9 | public class AnnouncerManager { 10 | private final HashMap announcerTasks; 11 | private final RedisChat plugin; 12 | 13 | public AnnouncerManager(RedisChat plugin) { 14 | this.announcerTasks = new HashMap<>(); 15 | this.plugin = plugin; 16 | reload(); 17 | } 18 | 19 | public void reload() { 20 | cancelAll(); 21 | announcerTasks.clear(); 22 | int fullInterval = plugin.config.announcer.stream().mapToInt(Config.Announcement::delay).sum(); 23 | int previousDelay = 0; 24 | for (Config.Announcement announce : plugin.config.announcer) { 25 | final AnnouncerTask at = new AnnouncerTask(plugin, 26 | announce.message(), 27 | announce.channelName() == null || announce.channelName().isEmpty() ? "public" : announce.channelName(), 28 | previousDelay + announce.delay(), 29 | fullInterval); 30 | previousDelay += announce.delay(); 31 | announcerTasks.put(announce.announcementName(), at); 32 | at.start(); 33 | } 34 | } 35 | 36 | public void cancelAll() { 37 | announcerTasks.values().forEach(at->{ 38 | try { 39 | at.cancel(); 40 | } catch (IllegalStateException ignored) { 41 | } 42 | }); 43 | } 44 | 45 | public AnnouncerTask cancelAnnounce(String name) { 46 | AnnouncerTask at = announcerTasks.remove(name); 47 | if (at == null) return null; 48 | 49 | try { 50 | at.cancel(); 51 | } catch (IllegalStateException ignored) { 52 | } 53 | 54 | at = new AnnouncerTask(plugin, at.getMessage(), at.getChannelName(), at.getDelay(), at.getInterval()); 55 | announcerTasks.put(name, at); 56 | return at; 57 | } 58 | 59 | public AnnouncerTask startAnnounce(String name) { 60 | AnnouncerTask at = announcerTasks.get(name); 61 | if (at != null) { 62 | at.start(); 63 | } 64 | 65 | return at; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/task/AnnouncerTask.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.task; 2 | 3 | import com.github.Anon8281.universalScheduler.UniversalRunnable; 4 | import com.google.common.base.Strings; 5 | import dev.unnm3d.redischat.RedisChat; 6 | import dev.unnm3d.redischat.api.objects.ChannelAudience; 7 | import dev.unnm3d.redischat.api.objects.ChatMessage; 8 | import lombok.Getter; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | 12 | public class AnnouncerTask extends UniversalRunnable { 13 | 14 | @Getter 15 | private final RedisChat plugin; 16 | private final String message; 17 | @Getter 18 | private final String channelName; 19 | @Getter 20 | private final int delay; 21 | @Getter 22 | private final int interval; 23 | 24 | public AnnouncerTask(RedisChat plugin, String message, String channelName, int delay, int interval) { 25 | this.plugin = plugin; 26 | this.message = message; 27 | this.channelName = channelName; 28 | this.delay = delay; 29 | this.interval = interval; 30 | } 31 | 32 | public void start() { 33 | try { 34 | runTaskTimerAsynchronously(plugin, delay * 20L, interval * 20L); 35 | } catch (IllegalStateException alreadyStarted) { 36 | getPlugin().getLogger().warning("AnnounceTask already started"); 37 | } 38 | } 39 | 40 | @Override 41 | public void run() { 42 | plugin.getDataManager().sendChatMessage( 43 | new ChatMessage( 44 | new ChannelAudience(), 45 | "{message}", 46 | getMessage(), 47 | new ChannelAudience(channelName) 48 | )); 49 | } 50 | 51 | public @NotNull String getMessage() { 52 | return Strings.nullToEmpty(message); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/unnm3d/redischat/utils/ItemNameProvider.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat.utils; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.inventory.meta.ItemMeta; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.lang.reflect.Method; 8 | 9 | 10 | public class ItemNameProvider { 11 | private Method getItemNameMethod; 12 | private Method hasItemNameMethod; 13 | private final boolean useItemName; 14 | 15 | public ItemNameProvider(boolean useItemName) { 16 | try { 17 | this.getItemNameMethod = ItemMeta.class.getDeclaredMethod("getItemName"); 18 | this.hasItemNameMethod = ItemMeta.class.getDeclaredMethod("hasItemName"); 19 | } catch (NoSuchMethodException ignored) { 20 | this.useItemName = false; 21 | Bukkit.getLogger().warning("Failed to find ItemMeta#getItemName() method. Falling back to display name."); 22 | return; 23 | } 24 | this.useItemName = useItemName; 25 | } 26 | 27 | public String getItemName(ItemMeta itemMeta) { 28 | if (getItemNameMethod == null || !useItemName) 29 | return itemMeta.getDisplayName(); 30 | try { 31 | return (String) getItemNameMethod.invoke(itemMeta); 32 | } catch (IllegalAccessException | InvocationTargetException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | public boolean hasItemName(ItemMeta itemMeta) { 38 | if (hasItemNameMethod == null || !useItemName) 39 | return itemMeta.hasDisplayName(); 40 | try { 41 | return (boolean) hasItemNameMethod.invoke(itemMeta); 42 | } catch (IllegalAccessException | InvocationTargetException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/webhook_embed.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "{SENDER_USERNAME} • {SENDER_CHANNEL}", 3 | "description": "{CHAT_MESSAGE}", 4 | "color": 64410, 5 | "footer": { 6 | "text": "{SENDER_USERNAME} • {SENDER_CHANNEL}", 7 | "icon_url": "https://crafatar.com/avatars/{SENDER_UUID}?size=64" 8 | }, 9 | "timestamp": "{CURRENT_TIMESTAMP}" 10 | } -------------------------------------------------------------------------------- /src/test/java/dev/unnm3d/redischat/JsonTests.java: -------------------------------------------------------------------------------- 1 | package dev.unnm3d.redischat; 2 | 3 | 4 | import com.google.gson.Gson; 5 | import dev.unnm3d.redischat.api.objects.KnownChatEntities; 6 | import dev.unnm3d.redischat.api.objects.AudienceType; 7 | import dev.unnm3d.redischat.api.objects.ChannelAudience; 8 | import dev.unnm3d.redischat.api.objects.Channel; 9 | import dev.unnm3d.redischat.api.objects.ChatMessage; 10 | import org.junit.jupiter.api.DisplayName; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 15 | 16 | public class JsonTests { 17 | private static final Gson gson = new Gson(); 18 | 19 | @Test 20 | @DisplayName("Channel serialization") 21 | public void testChannelSerialization() { 22 | Channel nc = Channel.builder("name") 23 | .format("fabrizio {message}") 24 | .rateLimit(10) 25 | .rateLimitPeriod(1000) 26 | .discordWebhook("webhook") 27 | .proximityDistance(50) 28 | .filtered(false) 29 | .permission("perm1") 30 | .notificationSound("sound") 31 | .build(); 32 | Channel nc2 = gson.fromJson(gson.toJson(nc), Channel.class); 33 | assertEquals(nc, nc2); 34 | System.out.println(nc.getProximityDistance()); 35 | System.out.println(nc2.getProximityDistance()); 36 | } 37 | 38 | @Test 39 | @DisplayName("Channel serialization") 40 | public void testChannelAudienceSerialization() { 41 | ChannelAudience nc = ChannelAudience.builder("name") 42 | .proximityDistance(-1) 43 | .permission("perm1") 44 | .type(AudienceType.PLAYER) 45 | .permission("perm2") 46 | .build(); 47 | ChannelAudience nc2 = gson.fromJson(gson.toJson(nc), ChannelAudience.class); 48 | assertEquals(nc, nc2); 49 | System.out.println(nc); 50 | System.out.println(nc2); 51 | } 52 | 53 | @Test 54 | @DisplayName("ChatMessage serialization") 55 | public void testChatMessageSerialization() { 56 | ChannelAudience receiver = ChannelAudience.builder("name") 57 | .proximityDistance(-1) 58 | .permission("perm1") 59 | .type(AudienceType.PLAYER) 60 | .permission("perm2") 61 | .build(); 62 | ChatMessage ncm = new ChatMessage( 63 | new ChannelAudience(AudienceType.PLAYER, KnownChatEntities.SERVER_SENDER.toString()), 64 | "{message}", 65 | "Hello World", 66 | receiver 67 | ); 68 | ChatMessage ncm2 = gson.fromJson(gson.toJson(ncm), ChatMessage.class); 69 | assertEquals(ncm, ncm2); 70 | 71 | //Change a superclass field 72 | ncm2.getReceiver().setProximityDistance(10); 73 | assertNotEquals(ncm, ncm2); 74 | } 75 | 76 | 77 | } 78 | --------------------------------------------------------------------------------