├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .run ├── clean-package.run.xml └── update-license-headers.run.xml ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── rezzedup │ └── discordsrv │ └── staffchat │ ├── ChatService.java │ ├── Data.java │ ├── Debugger.java │ ├── MessageProcessor.java │ ├── Permissions.java │ ├── StaffChatAPI.java │ ├── StaffChatData.java │ ├── StaffChatPlugin.java │ ├── StaffChatProfile.java │ ├── Updater.java │ ├── commands │ ├── ManageStaffChatCommand.java │ ├── StaffChatCommand.java │ ├── ToggleStaffChatCommand.java │ ├── ToggleStaffChatSoundsCommand.java │ └── package-info.java │ ├── config │ ├── Configs.java │ ├── MessagesConfig.java │ ├── StaffChatConfig.java │ └── package-info.java │ ├── events │ ├── AutoStaffChatToggleEvent.java │ ├── ConsoleStaffChatMessageEvent.java │ ├── DiscordStaffChatMessageEvent.java │ ├── PlayerStaffChatMessageEvent.java │ ├── ProfileToggleEvent.java │ ├── ReceivingStaffChatToggleEvent.java │ ├── StaffChatMessageEvent.java │ └── package-info.java │ ├── listeners │ ├── DiscordSrvLoadedLaterListener.java │ ├── DiscordStaffChatListener.java │ ├── JoinNotificationListener.java │ ├── PlayerPrefixedMessageListener.java │ ├── PlayerStaffChatToggleListener.java │ └── package-info.java │ ├── package-info.java │ └── util │ ├── FileIO.java │ ├── MappedPlaceholder.java │ ├── Strings.java │ └── package-info.java └── resources ├── messages.config.header.txt ├── plugin.yml └── staff-chat.config.header.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = tab 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: RezzedUp 2 | custom: ["https://paypal.me/RezzedUp"] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # Don't ignore jars in lib/ 12 | !lib/*.jar 13 | 14 | # IntelliJ 15 | .idea/ 16 | *.iml 17 | *.ipr 18 | out/ 19 | 20 | # Maven 21 | target/ 22 | 23 | # Eclipse 24 | .settings/ 25 | .project 26 | .classpath 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | 31 | # ========================= 32 | # Operating System Files 33 | # ========================= 34 | 35 | # OSX 36 | # ========================= 37 | 38 | .DS_Store 39 | .AppleDouble 40 | .LSOverride 41 | 42 | # Thumbnails 43 | ._* 44 | 45 | # Files that might appear in the root of a volume 46 | .DocumentRevisions-V100 47 | .fseventsd 48 | .Spotlight-V100 49 | .TemporaryItems 50 | .Trashes 51 | .VolumeIcon.icns 52 | 53 | # Directories potentially created on remote AFP share 54 | .AppleDB 55 | .AppleDesktop 56 | Network Trash Folder 57 | Temporary Items 58 | .apdisk 59 | 60 | # Windows 61 | # ========================= 62 | 63 | # Windows image file caches 64 | Thumbs.db 65 | ehthumbs.db 66 | 67 | # Folder config file 68 | Desktop.ini 69 | 70 | # Recycle Bin used on file shares 71 | $RECYCLE.BIN/ 72 | 73 | # Windows Installer files 74 | *.cab 75 | *.msi 76 | *.msm 77 | *.msp 78 | 79 | # Windows shortcuts 80 | *.lnk 81 | -------------------------------------------------------------------------------- /.run/clean-package.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.run/update-license-headers.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2022 RezzedUp and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # ![](https://i.imgur.com/DZwTm1u.png "DiscordSRV-Staff-Chat Logo") 4 | 5 | [![](https://img.shields.io/badge/License-MIT-blue)](./LICENSE "Project License: MIT") 6 | [![](https://img.shields.io/badge/Java-11-orange)](# "Java Version: 11") 7 | [![](https://img.shields.io/github/v/release/DiscordSRV/Staff-Chat.svg?label=Release&color=ok)](https://github.com/DiscordSRV/Staff-Chat/releases/latest "Latest Release") 8 | [![](https://img.shields.io/spiget/downloads/44245?color=yellow&label=Spigot%20Downloads)](https://www.spigotmc.org/resources/discordsrv-staff-chat.44245/ "Spigot Resource Page") 9 | [![](https://img.shields.io/modrinth/dt/uD7Bzf5q?color=%2300af5c&label=Modrinth%20Downloads&logo=modrinth)](https://modrinth.com/plugin/uD7Bzf5q "Modrinth Project Page") 10 | 11 |
12 | 13 | DiscordSRV-Staff-Chat is a staff chat plugin that connects to a Discord channel (via [DiscordSRV](https://github.com/DiscordSRV/DiscordSRV)), allowing in-game staff to communicate with staff on Discord. 14 | 15 | ![](https://i.imgur.com/ssKGDTJ.gif) 16 | 17 | ## Installation 18 | 19 | * Add **DiscordSRV** and **DiscordSRV-Staff-Chat** to your `plugins/` directory. 20 | * Restart the server. 21 | * Add a `staff-chat` channel to **DiscordSRV**'s config. 22 | * Execute: `/discord reload` *or* restart the server again. 23 | 24 | ### Adding a staff-chat channel 25 | 26 | Add a **"staff-chat"** entry to DiscordSRV's config. Note: it's very important that this entry is called `"staff-chat"`. 27 | 28 | ```yaml 29 | # Channel links from game to Discord 30 | # syntax is Channels: {"in-game channel name from Minecraft": "numerical channel ID from Discord", "another in-game channel name from Minecraft": "another numerical channel ID from Discord"} 31 | # 32 | Channels: {"global": "000000000000000000", "staff-chat": "000000000000000000"} 33 | ``` 34 | 35 | Replace all those zeros with your staff chat's channel ID. 36 | 37 | ![](https://i.imgur.com/tXNU6Ei.gif) 38 | 39 | ```yaml 40 | Channels: {"global": "000000000000000000", "staff-chat": "337769984539361281"} 41 | ``` 42 | 43 | 44 | ## Commands & Permissions 45 | 46 | ### /staffchat 47 | 48 | > ![](https://i.imgur.com/ILwkaqa.gif) 49 | > 50 | > **Permission:** `staffchat.access` 51 | > 52 | > **Aliases:** `/adminchat`, `/schat`, `/achat`, `/sc`, `/ac`, and `/a` 53 | > 54 | > **Usage:** 55 | > - `/staffchat` - Toggle automatic staff chat. 56 | > - `/staffchat ` - Send a message to the staff chat. 57 | 58 | ### /leavestaffchat 59 | 60 | > ![](https://i.imgur.com/BO3fgmC.png) 61 | > 62 | > **Permission:** `staffchat.access` 63 | > 64 | > **Aliases:** `/leaveadminchat` 65 | > 66 | > **Usage:** 67 | > - `/leavestaffchat` - Stop receiving staff chat messages. 68 | > - Useful to avoid leaking staff chat messages if a staff member is filming or streaming. 69 | > - This can be disabled in the config if you don't want staff members to turn off their staff chat. 70 | 71 | ### /joinstaffchat 72 | 73 | > ![](https://i.imgur.com/7EriNrS.png) 74 | > 75 | > **Permission:** `staffchat.access` 76 | > 77 | > **Aliases:** `/joinadminchat` 78 | > 79 | > **Usage:** 80 | > - `/joinstaffchat` - Start receiving staff chat messages again if you previously left. 81 | 82 | ### /togglestaffchatsounds 83 | 84 | > ![](https://i.imgur.com/MYbaRtZ.png) 85 | > 86 | > **Permission:** `staffchat.access` 87 | > 88 | > **Aliases:** `/toggleadminchatsounds` 89 | > 90 | > **Usage:** 91 | > - `/togglestaffchatsounds` - Mute or unmute staff chat sounds for yourself. 92 | 93 | ### /managestaffchat 94 | 95 | > **Permission:** `staffchat.manage` 96 | > 97 | > **Aliases:** `/discordsrv-staff-chat`, `/discordsrvstaffchat`, `/discordstaffchat`, `/discordadminchat`, `/manageadminchat` 98 | > 99 | > **Usage:** 100 | > - `/managestaffchat` - Display plugin information and command usage. 101 | > - `/managestaffchat reload` - Reload configs. 102 | > - `/managestaffchat debug` - Enable or disable debugging. 103 | 104 | [![](https://bstats.org/signatures/bukkit/DiscordSRV-Staff-Chat.svg)](https://bstats.org/plugin/bukkit/DiscordSRV-Staff-Chat/11056) 105 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.rezzedup 8 | discordsrv-staff-chat 9 | 1.4.6 10 | 11 | DiscordSRV-Staff-Chat 12 | 2017 13 | https://github.com/DiscordSRV/Staff-Chat 14 | A staff-chat plugin that hooks into DiscordSRV. 15 | 16 | 17 | 11 18 | 11 19 | 20 | yyyy 21 | UTF-8 22 | 23 | com.rezzedup.discordsrv.staffchat.shaded 24 | 25 | 26 | ${project.inceptionYear}-${maven.build.timestamp} 27 | RezzedUp and Contributors 28 | https://github.com/DiscordSRV/Staff-Chat 29 | 30 | 31 | 32 | 33 | spigot-repo 34 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 35 | 36 | 37 | scarsz-nexus 38 | https://nexus.scarsz.me/content/repositories/public/ 39 | 40 | 41 | jitpack.io 42 | https://jitpack.io 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.spigotmc 50 | spigot-api 51 | 1.21.3-R0.1-SNAPSHOT 52 | provided 53 | 54 | 55 | 56 | com.discordsrv 57 | discordsrv 58 | 1.27.0 59 | provided 60 | 61 | 62 | 63 | com.github.placeholderapi 64 | placeholderapi 65 | 2.11.1 66 | provided 67 | 68 | 69 | 70 | community.leaf.configvalues 71 | config-values-bukkit 72 | 0.0.9 73 | 74 | 75 | 76 | community.leaf.eventful 77 | events-bukkit 78 | 0.4.0 79 | 80 | 81 | 82 | community.leaf.tasks 83 | tasks-bukkit 84 | 0.0.2 85 | 86 | 87 | 88 | 89 | com.github.zafarkhaja 90 | java-semver 91 | 0.10.2 92 | 93 | 94 | 95 | org.bstats 96 | bstats-bukkit 97 | 3.0.2 98 | 99 | 100 | 101 | pl.tlinkowski.annotation 102 | pl.tlinkowski.annotation.basic 103 | 0.2.0 104 | 105 | 106 | 107 | 108 | 109 | package 110 | ${project.name}-${project.version} 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-compiler-plugin 116 | 3.8.1 117 | 118 | ${maven.compiler.source} 119 | ${maven.compiler.target} 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-shade-plugin 126 | 3.3.0 127 | 128 | ${project.build.directory}/dependency-reduced-pom.xml 129 | 130 | 131 | 132 | com.github 133 | ${shade.relocation}.com.github 134 | 135 | 136 | community.leaf 137 | ${shade.relocation}.community.leaf 138 | 139 | 140 | com.rezzedup.util 141 | ${shade.relocation}.com.rezzedup.util 142 | 143 | 144 | org.bstats 145 | ${shade.relocation}.org.bstats 146 | 147 | 148 | 149 | 150 | 151 | com.google.code.findbugs:jsr305 152 | org.jetbrains.kotlin:kotlin-annotations-jvm 153 | pl.tlinkowski.annotation:pl.tlinkowski.annotation.basic 154 | 155 | 156 | 157 | 158 | 159 | *:* 160 | 161 | META-INF/** 162 | 163 | 164 | 165 | 166 | 167 | 168 | package 169 | 170 | shade 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | com.mycila 179 | license-maven-plugin 180 | 4.1 181 | 182 | 183 | ${license.header.year} 184 | ${license.header.owner} 185 | ${license.header.url} 186 | 187 | 188 | 189 |
com/mycila/maven/plugin/license/templates/MIT.txt
190 | 191 | *.md 192 | *.txt 193 | *.xml 194 | *.yml 195 | .run/** 196 | examples/** 197 | src/test/resources/** 198 | src/main/resources/** 199 | .editorconfig 200 | 201 |
202 |
203 |
204 | 205 | 206 | 207 | test 208 | 209 | check 210 | 211 | 212 | 213 |
214 |
215 | 216 | 217 | 218 | src/main/resources 219 | true 220 | 221 | 222 | 223 | . 224 | 225 | LICENSE 226 | 227 | 228 | 229 |
230 | 231 |
232 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/ChatService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | public enum ChatService { 26 | MINECRAFT("In-Game"), 27 | DISCORD("Discord"); 28 | 29 | private final String prefix; 30 | 31 | ChatService(String prefix) { 32 | this.prefix = prefix; 33 | } 34 | 35 | public String asPrefixInBrackets(String context) { 36 | return "[" + prefix + ": " + context + "]"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/Data.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 26 | import com.rezzedup.discordsrv.staffchat.events.AutoStaffChatToggleEvent; 27 | import com.rezzedup.discordsrv.staffchat.events.ReceivingStaffChatToggleEvent; 28 | import community.leaf.configvalues.bukkit.YamlValue; 29 | import community.leaf.configvalues.bukkit.data.YamlDataFile; 30 | import community.leaf.configvalues.bukkit.util.Sections; 31 | import community.leaf.tasks.TaskContext; 32 | import org.bukkit.configuration.ConfigurationSection; 33 | import org.bukkit.entity.Player; 34 | import org.bukkit.scheduler.BukkitTask; 35 | import pl.tlinkowski.annotation.basic.NullOr; 36 | 37 | import java.time.Instant; 38 | import java.util.HashMap; 39 | import java.util.Map; 40 | import java.util.Optional; 41 | import java.util.UUID; 42 | 43 | public class Data extends YamlDataFile implements StaffChatData { 44 | private static final String PROFILES_PATH = "staff-chat.profiles"; 45 | 46 | private final Map profilesByUuid = new HashMap<>(); 47 | 48 | private final StaffChatPlugin plugin; 49 | 50 | private @NullOr TaskContext task = null; 51 | 52 | Data(StaffChatPlugin plugin) { 53 | super(plugin.directory().resolve("data"), "staff-chat.data.yml"); 54 | this.plugin = plugin; 55 | 56 | // Load persistent toggles. 57 | if (plugin.config().getOrDefault(StaffChatConfig.PERSIST_TOGGLES)) { 58 | Sections.get(data(), PROFILES_PATH).ifPresent(section -> 59 | { 60 | for (String key : section.getKeys(false)) { 61 | try { 62 | getOrCreateProfile(UUID.fromString(key)); 63 | } catch (IllegalArgumentException ignored) { 64 | } 65 | } 66 | }); 67 | } 68 | 69 | // Start the save task. 70 | task = plugin.async().every(2).minutes().run(() -> { 71 | if (isUpdated()) { 72 | save(); 73 | } 74 | }); 75 | 76 | // Update profiles of all online players when reloaded. 77 | reloadsWith(() -> plugin.getServer().getOnlinePlayers().forEach(this::updateProfile)); 78 | } 79 | 80 | protected void end() { 81 | if (task != null) { 82 | task.cancel(); 83 | } 84 | if (isUpdated()) { 85 | save(); 86 | } 87 | } 88 | 89 | @Override 90 | public StaffChatProfile getOrCreateProfile(UUID uuid) { 91 | return profilesByUuid.computeIfAbsent(uuid, k -> new Profile(plugin, this, k)); 92 | } 93 | 94 | @Override 95 | public Optional getProfile(UUID uuid) { 96 | return Optional.ofNullable(profilesByUuid.get(uuid)); 97 | } 98 | 99 | public void updateProfile(Player player) { 100 | @NullOr Profile profile = profilesByUuid.get(player.getUniqueId()); 101 | 102 | if (Permissions.ACCESS.allows(player)) { 103 | // Ensure that this staff member has an active profile. 104 | if (profile == null) { 105 | profile = (Profile) getOrCreateProfile(player); 106 | } 107 | 108 | // If leaving the staff chat is disabled... 109 | if (!plugin.config().getOrDefault(StaffChatConfig.LEAVING_STAFFCHAT_ENABLED)) { 110 | // ... and this staff member previously left the staff chat ... 111 | if (profile.left != null) { 112 | // Bring them back. 113 | profile.receivesStaffChatMessages(true); 114 | } 115 | } 116 | } else { 117 | // Not a staff member but has a loaded profile... 118 | if (profile != null) { 119 | // Notify that they're no longer talking in staff chat. 120 | if (profile.automaticStaffChat()) { 121 | profile.automaticStaffChat(false); 122 | } 123 | 124 | // No longer staff, delete data. 125 | profile.clearStoredProfileData(); 126 | 127 | // Remove from the map. 128 | profilesByUuid.remove(player.getUniqueId()); 129 | } 130 | } 131 | } 132 | 133 | static class Profile implements StaffChatProfile { 134 | static final YamlValue AUTO_TOGGLE_DATE = YamlValue.ofInstant("toggles.auto").maybe(); 135 | 136 | static final YamlValue LEFT_TOGGLE_DATE = YamlValue.ofInstant("toggles.left").maybe(); 137 | 138 | static final YamlValue MUTED_SOUNDS_TOGGLE = YamlValue.ofBoolean("toggles.muted-sounds").maybe(); 139 | 140 | private final StaffChatPlugin plugin; 141 | private final YamlDataFile yaml; 142 | private final UUID uuid; 143 | 144 | private @NullOr Instant auto; 145 | private @NullOr Instant left; 146 | private boolean mutedSounds = false; 147 | 148 | Profile(StaffChatPlugin plugin, YamlDataFile yaml, UUID uuid) { 149 | this.plugin = plugin; 150 | this.yaml = yaml; 151 | this.uuid = uuid; 152 | 153 | if (plugin.config().getOrDefault(StaffChatConfig.PERSIST_TOGGLES)) { 154 | Sections.get(yaml.data(), path()).ifPresent(section -> 155 | { 156 | auto = AUTO_TOGGLE_DATE.get(section).orElse(null); 157 | left = LEFT_TOGGLE_DATE.get(section).orElse(null); 158 | mutedSounds = MUTED_SOUNDS_TOGGLE.get(section).orElse(false); 159 | }); 160 | } 161 | } 162 | 163 | String path() { 164 | return PROFILES_PATH + "." + uuid; 165 | } 166 | 167 | @Override 168 | public UUID uuid() { 169 | return uuid; 170 | } 171 | 172 | @Override 173 | public Optional sinceEnabledAutoChat() { 174 | return Optional.ofNullable(auto); 175 | } 176 | 177 | @Override 178 | public boolean automaticStaffChat() { 179 | return auto != null; 180 | } 181 | 182 | @Override 183 | public void automaticStaffChat(boolean enabled) { 184 | if (plugin.events().call(new AutoStaffChatToggleEvent(this, enabled)).isCancelled()) { 185 | return; 186 | } 187 | 188 | auto = (enabled) ? Instant.now() : null; 189 | updateStoredProfileData(); 190 | } 191 | 192 | @Override 193 | public Optional sinceLeftStaffChat() { 194 | return Optional.ofNullable(left); 195 | } 196 | 197 | @Override 198 | public boolean receivesStaffChatMessages() { 199 | // hasn't left the staff chat or leaving is disabled outright 200 | return left == null || !plugin.config().getOrDefault(StaffChatConfig.LEAVING_STAFFCHAT_ENABLED); 201 | } 202 | 203 | @Override 204 | public void receivesStaffChatMessages(boolean enabled) { 205 | if (plugin.events().call(new ReceivingStaffChatToggleEvent(this, enabled)).isCancelled()) { 206 | return; 207 | } 208 | 209 | left = (enabled) ? null : Instant.now(); 210 | updateStoredProfileData(); 211 | } 212 | 213 | @Override 214 | public boolean receivesStaffChatSounds() { 215 | return !mutedSounds; 216 | } 217 | 218 | @Override 219 | public void receivesStaffChatSounds(boolean enabled) { 220 | mutedSounds = !enabled; 221 | } 222 | 223 | boolean hasDefaultSettings() { 224 | return auto == null && left == null && !mutedSounds; 225 | } 226 | 227 | void clearStoredProfileData() { 228 | if (!plugin.config().getOrDefault(StaffChatConfig.PERSIST_TOGGLES)) { 229 | return; 230 | } 231 | 232 | yaml.data().set(path(), null); 233 | yaml.updated(true); 234 | } 235 | 236 | void updateStoredProfileData() { 237 | if (!plugin.config().getOrDefault(StaffChatConfig.PERSIST_TOGGLES)) { 238 | return; 239 | } 240 | 241 | if (hasDefaultSettings()) { 242 | clearStoredProfileData(); 243 | return; 244 | } 245 | 246 | ConfigurationSection section = Sections.getOrCreate(yaml.data(), path()); 247 | 248 | AUTO_TOGGLE_DATE.set(section, auto); 249 | LEFT_TOGGLE_DATE.set(section, left); 250 | MUTED_SOUNDS_TOGGLE.set(section, mutedSounds); 251 | 252 | yaml.updated(true); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/Debugger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Message; 26 | import github.scarsz.discordsrv.dependencies.jda.api.entities.User; 27 | import org.bukkit.entity.Player; 28 | import org.bukkit.event.Event; 29 | import org.bukkit.plugin.Plugin; 30 | import pl.tlinkowski.annotation.basic.NullOr; 31 | 32 | import java.io.IOException; 33 | import java.nio.file.Files; 34 | import java.nio.file.Path; 35 | import java.nio.file.StandardOpenOption; 36 | import java.time.LocalDateTime; 37 | import java.time.temporal.ChronoUnit; 38 | import java.util.function.Supplier; 39 | 40 | public class Debugger { 41 | private static String now() { 42 | return LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS).toString(); 43 | } 44 | 45 | private static final DebugLogger DISABLED = message -> { 46 | }; 47 | 48 | private final StaffChatPlugin plugin; 49 | private final Path debugToggleFile; 50 | private final Path debugLogFile; 51 | 52 | private boolean isEnabled; 53 | 54 | public Debugger(StaffChatPlugin plugin) { 55 | this.plugin = plugin; 56 | this.debugToggleFile = plugin.directory().resolve("debugging-is-enabled"); 57 | this.debugLogFile = plugin.directory().resolve("debug.log"); 58 | this.isEnabled = isToggleFilePresent(); 59 | } 60 | 61 | private boolean isToggleFilePresent() { 62 | return Files.isRegularFile(debugToggleFile); 63 | } 64 | 65 | public boolean isEnabled() { 66 | return isEnabled; 67 | } 68 | 69 | public void setEnabled(boolean enabled) { 70 | if (this.isEnabled == enabled) { 71 | return; 72 | } 73 | 74 | this.isEnabled = enabled; 75 | 76 | try { 77 | if (enabled) { 78 | printThenWriteToLogFile("========== Starting Debugger =========="); 79 | if (!isToggleFilePresent()) { 80 | Files.createFile(debugToggleFile); 81 | } 82 | } else { 83 | printThenWriteToLogFile("========== Disabled Debugger =========="); 84 | Files.deleteIfExists(debugToggleFile); 85 | } 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | 91 | public DebugLogger debug(Class clazz) { 92 | return (isEnabled) ? message -> record("[" + clazz.getSimpleName() + "] " + message.get()) : DISABLED; 93 | } 94 | 95 | private void record(String message) { 96 | if (isEnabled) { 97 | printThenWriteToLogFile(message); 98 | } 99 | } 100 | 101 | private void printThenWriteToLogFile(String message) { 102 | plugin.getLogger().info("[Debug] " + message); 103 | 104 | try { 105 | if (!Files.isRegularFile(debugLogFile)) { 106 | Files.createFile(debugLogFile); 107 | } 108 | Files.write(debugLogFile, ("[" + now() + "] " + message + "\n").getBytes(), StandardOpenOption.APPEND); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | public void schedulePluginStatus(Class clazz, String context) { 115 | if (!isEnabled) { 116 | return; 117 | } 118 | 119 | // Log status directly on the next tick. 120 | plugin.sync().run(() -> logPluginStatus(clazz, context + " (Initial)")); 121 | 122 | // Log status 30 seconds after so that DiscordSRV has a chance to connect. 123 | plugin.sync().delay(30).seconds().run(() -> logPluginStatus(clazz, context + " (30 Seconds)")); 124 | } 125 | 126 | private void logPluginStatus(Class clazz, String context) { 127 | debug(clazz).recordDebugLogEntry(() -> 128 | { 129 | @NullOr Plugin discordSrv = plugin.getServer().getPluginManager().getPlugin(StaffChatPlugin.DISCORDSRV); 130 | @NullOr Object channel = plugin.getDiscordChannelOrNull(); 131 | 132 | boolean isDiscordSrvEnabled = discordSrv != null && discordSrv.isEnabled(); 133 | boolean isDiscordSrvHooked = plugin.isDiscordSrvHookEnabled(); 134 | boolean isChannelReady = channel != null; 135 | 136 | return "[Status: " + context + "] " + 137 | "Is DiscordSRV installed and enabled? " + isDiscordSrvEnabled + " :: " + 138 | "Is DiscordSRV hooked? " + isDiscordSrvHooked + " :: " + 139 | "Is " + StaffChatPlugin.CHANNEL + " channel ready? " + isChannelReady + " (" + channel + ")"; 140 | }); 141 | } 142 | 143 | private static String handleContext(@NullOr Object context) { 144 | if (context instanceof Class) { 145 | return ((Class) context).getSimpleName(); 146 | } 147 | if (context instanceof Event) { 148 | return ((Event) context).getEventName(); 149 | } 150 | return String.valueOf(context); 151 | } 152 | 153 | private static String handleException(Throwable exception) { 154 | return exception.getClass().getSimpleName() + ": " + exception.getMessage(); 155 | } 156 | 157 | @FunctionalInterface 158 | public interface DebugLogger { 159 | void recordDebugLogEntry(Supplier message); 160 | 161 | default void log(Supplier message) { 162 | recordDebugLogEntry(message); 163 | } 164 | 165 | default void log(@NullOr Object context, Supplier message) { 166 | recordDebugLogEntry(() -> "[" + handleContext(context) + "] " + message.get()); 167 | } 168 | 169 | default void log(ChatService source, @NullOr Object context, Supplier message) { 170 | recordDebugLogEntry(() -> source.asPrefixInBrackets(handleContext(context)) + " " + message.get()); 171 | } 172 | 173 | default void header(Supplier message) { 174 | recordDebugLogEntry(() -> "---------- " + message.get() + " ----------"); 175 | } 176 | 177 | default void logException(Object context, Throwable exception) { 178 | log(context, () -> { 179 | exception.printStackTrace(); 180 | return handleException(exception); 181 | }); 182 | } 183 | 184 | default T failure(T exception) throws T { 185 | log(() -> handleException(exception)); 186 | throw exception; 187 | } 188 | 189 | default T failure(@NullOr Object context, T exception) throws T { 190 | log(context, () -> handleException(exception)); 191 | throw exception; 192 | } 193 | 194 | default void logConsoleChatMessage(String message) { 195 | log(ChatService.MINECRAFT, "Message", () -> 196 | "from() message(\"" + message + "\")" 197 | ); 198 | } 199 | 200 | default void logPlayerChatMessage(Player author, String message) { 201 | log(ChatService.MINECRAFT, "Message", () -> 202 | "from(" + author.getName() + ") message(\"" + message + "\")" 203 | ); 204 | } 205 | 206 | default void logDiscordChatMessage(User author, Message message) { 207 | log(ChatService.DISCORD, "Message", () -> 208 | "from(" + author.getName() + "#" + author.getDiscriminator() + ") message(\"" + message.getContentStripped() + "\")" 209 | ); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/MessageProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import com.rezzedup.discordsrv.staffchat.config.MessagesConfig; 26 | import com.rezzedup.discordsrv.staffchat.events.ConsoleStaffChatMessageEvent; 27 | import com.rezzedup.discordsrv.staffchat.events.DiscordStaffChatMessageEvent; 28 | import com.rezzedup.discordsrv.staffchat.events.PlayerStaffChatMessageEvent; 29 | import com.rezzedup.discordsrv.staffchat.util.MappedPlaceholder; 30 | import com.rezzedup.discordsrv.staffchat.util.Strings; 31 | import community.leaf.configvalues.bukkit.DefaultYamlValue; 32 | import github.scarsz.discordsrv.DiscordSRV; 33 | import github.scarsz.discordsrv.dependencies.emoji.EmojiParser; 34 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Member; 35 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Message; 36 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Role; 37 | import github.scarsz.discordsrv.dependencies.jda.api.entities.TextChannel; 38 | import github.scarsz.discordsrv.dependencies.jda.api.entities.User; 39 | import github.scarsz.discordsrv.util.DiscordUtil; 40 | import me.clip.placeholderapi.PlaceholderAPI; 41 | import net.md_5.bungee.api.ChatColor; 42 | import org.bukkit.entity.Player; 43 | import pl.tlinkowski.annotation.basic.NullOr; 44 | 45 | import java.awt.*; 46 | import java.util.List; 47 | import java.util.Locale; 48 | import java.util.Objects; 49 | import java.util.function.Consumer; 50 | 51 | public class MessageProcessor { 52 | private final StaffChatPlugin plugin; 53 | 54 | MessageProcessor(StaffChatPlugin plugin) { 55 | this.plugin = plugin; 56 | } 57 | 58 | private void sendFormattedChatMessage(@NullOr Object author, DefaultYamlValue format, MappedPlaceholder placeholders) { 59 | // If the value of %message% doesn't exist for some reason, don't announce. 60 | if (Strings.isEmptyOrNull(placeholders.get("message"))) { 61 | return; 62 | } 63 | 64 | String formatted = plugin.messages().getOrDefault(format); 65 | 66 | if (plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { 67 | // Update format's PAPI placeholders before inserting the message 68 | // (which *could* contain arbitrary placeholders itself, ah placeholder injection). 69 | @NullOr Player player = (author instanceof Player) ? (Player) author : null; 70 | formatted = PlaceholderAPI.setPlaceholders(player, formatted); 71 | } 72 | 73 | String content = Strings.colorful(placeholders.update(formatted)); 74 | 75 | if (author instanceof Player) { 76 | Player player = (Player) author; 77 | StaffChatProfile profile = plugin.data().getOrCreateProfile(player); 78 | 79 | // Author left the staff chat but is sending a message there... 80 | if (!profile.receivesStaffChatMessages()) { 81 | String reminder = Strings.colorful(placeholders.update( 82 | plugin.messages().getOrDefault(MessagesConfig.LEFT_CHAT_NOTIFICATION_REMINDER)) 83 | ); 84 | 85 | player.sendMessage(content); 86 | player.sendMessage(reminder); 87 | 88 | plugin.config().playNotificationSound(player); 89 | } 90 | } 91 | 92 | plugin.onlineStaffChatParticipants().forEach(staff -> { 93 | staff.sendMessage(content); 94 | plugin.config().playMessageSound(staff); 95 | }); 96 | 97 | plugin.getServer().getConsoleSender().sendMessage(content); 98 | } 99 | 100 | private void sendToDiscord(Consumer sender) { 101 | @NullOr TextChannel channel = plugin.getDiscordChannelOrNull(); 102 | 103 | if (channel == null) { 104 | plugin.debug(getClass()).log(ChatService.MINECRAFT, "Message", () -> 105 | "Unable to send message to discord: " + StaffChatPlugin.CHANNEL + " => null" 106 | ); 107 | return; 108 | } 109 | 110 | plugin.debug(getClass()).log(ChatService.MINECRAFT, "Message", () -> 111 | "Sending message to discord channel: " + StaffChatPlugin.CHANNEL + " => " + channel 112 | ); 113 | 114 | sender.accept(channel); 115 | } 116 | 117 | public void processConsoleChat(String message) { 118 | Objects.requireNonNull(message, "message"); 119 | 120 | plugin.debug(getClass()).logConsoleChatMessage(message); 121 | 122 | ConsoleStaffChatMessageEvent event = 123 | plugin.events().call(new ConsoleStaffChatMessageEvent(message)); 124 | 125 | if (event.isCancelled() || event.getText().isEmpty()) { 126 | plugin.debug(getClass()).log(ChatService.MINECRAFT, event, () -> "Cancelled or text is empty"); 127 | return; 128 | } 129 | 130 | MappedPlaceholder placeholders = plugin.messages().placeholders(); 131 | placeholders.map("message", "content", "text").to(event::getText); 132 | 133 | sendFormattedChatMessage(null, MessagesConfig.IN_GAME_CONSOLE_FORMAT, placeholders); 134 | 135 | if (plugin.isDiscordSrvHookEnabled()) { 136 | String discordMessage = placeholders.update( 137 | plugin.messages().getOrDefault(MessagesConfig.DISCORD_CONSOLE_FORMAT) 138 | ); 139 | 140 | sendToDiscord(channel -> DiscordUtil.queueMessage(channel, discordMessage, true)); 141 | } else { 142 | plugin.debug(getClass()).log(ChatService.MINECRAFT, "Message", () -> 143 | "DiscordSRV hook is not enabled, cannot send to discord" 144 | ); 145 | } 146 | } 147 | 148 | public void processPlayerChat(Player author, String message) { 149 | Objects.requireNonNull(author, "author"); 150 | Objects.requireNonNull(message, "message"); 151 | 152 | plugin.debug(getClass()).logPlayerChatMessage(author, message); 153 | 154 | PlayerStaffChatMessageEvent event = 155 | plugin.events().call(new PlayerStaffChatMessageEvent(author, message)); 156 | 157 | if (event.isCancelled() || event.getText().isEmpty()) { 158 | plugin.debug(getClass()).log(ChatService.MINECRAFT, event, () -> "Cancelled or text is empty"); 159 | return; 160 | } 161 | 162 | MappedPlaceholder placeholders = plugin.messages().placeholders(author); 163 | placeholders.map("message", "content", "text").to(event::getText); 164 | 165 | sendFormattedChatMessage(author, MessagesConfig.IN_GAME_PLAYER_FORMAT, placeholders); 166 | 167 | if (plugin.isDiscordSrvHookEnabled()) { 168 | sendToDiscord(channel -> { 169 | // Send to discord off the main thread (just like DiscordSRV does) 170 | plugin.async().run(() -> 171 | DiscordSRV.getPlugin().processChatMessage(author, message, StaffChatPlugin.CHANNEL, false) 172 | ); 173 | }); 174 | } else { 175 | plugin.debug(getClass()).log(ChatService.MINECRAFT, "Message", () -> 176 | "DiscordSRV hook is not enabled, cannot send to discord" 177 | ); 178 | } 179 | } 180 | 181 | public void processDiscordChat(User author, Message message) { 182 | Objects.requireNonNull(author, "author"); 183 | Objects.requireNonNull(message, "message"); 184 | 185 | plugin.debug(getClass()).logDiscordChatMessage(author, message); 186 | 187 | DiscordStaffChatMessageEvent event = 188 | plugin.events().call(new DiscordStaffChatMessageEvent(author, message, message.getContentStripped())); 189 | 190 | if (event.isCancelled() || event.getText().isEmpty()) { 191 | plugin.debug(getClass()).log(ChatService.DISCORD, "Message", () -> "Cancelled or text is empty"); 192 | return; 193 | } 194 | 195 | // Emoji Unicode -> Alias (library included with DiscordSRV) 196 | String text = EmojiParser.parseToAliases(event.getText()); 197 | 198 | MappedPlaceholder placeholders = plugin.messages().placeholders(); 199 | 200 | placeholders.map("message", "content", "text").to(() -> text); 201 | placeholders.map("user", "name", "username", "sender").to(author::getName); 202 | placeholders.map("discriminator", "discrim").to(author::getDiscriminator); 203 | 204 | @NullOr Member member = message.getMember(); 205 | 206 | if (member != null) { 207 | placeholders.map("nickname", "displayname").to(member::getEffectiveName); 208 | 209 | // Simulate placeholders from DiscordSRV: 210 | // https://github.com/DiscordSRV/DiscordSRV/blob/1d08598206b1af5dcc29e411cead8e152e4c3f94/src/main/java/github/scarsz/discordsrv/listeners/DiscordChatListener.java#L293 211 | 212 | DiscordSRV discordSrv = DiscordSRV.getPlugin(); 213 | List selectedRoles = discordSrv.getSelectedRoles(member); 214 | @NullOr Role topRole = (selectedRoles.isEmpty()) ? null : selectedRoles.get(0); 215 | 216 | if (topRole != null) { 217 | placeholders.map("toprole").to(topRole::getName); 218 | placeholders.map("toproleinitial").to(() -> topRole.getName().substring(0, 1)); 219 | 220 | placeholders.map("toprolealias").to(() -> 221 | discordSrv.getRoleAliases().getOrDefault( 222 | topRole.getId(), 223 | discordSrv.getRoleAliases().getOrDefault( 224 | topRole.getName().toLowerCase(Locale.ROOT), 225 | topRole.getName() 226 | ) 227 | ) 228 | ); 229 | 230 | placeholders.map("toprolecolor").to(() -> ChatColor.of(new Color(topRole.getColorRaw()))); 231 | placeholders.map("allroles").to(() -> DiscordUtil.getFormattedRoles(selectedRoles)); 232 | } 233 | } 234 | 235 | sendFormattedChatMessage(author, MessagesConfig.IN_GAME_DISCORD_FORMAT, placeholders); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/Permissions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import org.bukkit.permissions.Permissible; 26 | 27 | public enum Permissions { 28 | MANAGE, 29 | ACCESS; 30 | 31 | public static final String PREFIX = "staffchat"; 32 | 33 | private final String permission; 34 | 35 | Permissions() { 36 | this.permission = PREFIX + "." + name().toLowerCase(); 37 | } 38 | 39 | public String getPermissionNode() { 40 | return this.permission; 41 | } 42 | 43 | public boolean allows(Permissible permissible) { 44 | return permissible.hasPermission(permission); 45 | } 46 | 47 | public boolean denies(Permissible permissible) { 48 | return !allows(permissible); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/StaffChatAPI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Message; 26 | import github.scarsz.discordsrv.dependencies.jda.api.entities.TextChannel; 27 | import github.scarsz.discordsrv.dependencies.jda.api.entities.User; 28 | import org.bukkit.Bukkit; 29 | import org.bukkit.entity.Player; 30 | import pl.tlinkowski.annotation.basic.NullOr; 31 | 32 | import java.util.stream.Stream; 33 | 34 | public interface StaffChatAPI { 35 | StaffChatData data(); 36 | 37 | boolean isDiscordSrvHookEnabled(); 38 | 39 | @NullOr TextChannel getDiscordChannelOrNull(); 40 | 41 | void submitMessageFromConsole(String message); 42 | 43 | void submitMessageFromPlayer(Player author, String message); 44 | 45 | void submitMessageFromDiscord(User author, Message message); 46 | 47 | default Stream onlineStaffChatParticipants() { 48 | return Bukkit.getOnlinePlayers().stream() 49 | .filter(Permissions.ACCESS::allows) 50 | .filter(data()::isReceivingStaffChatMessages); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/StaffChatData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import org.bukkit.entity.Player; 26 | 27 | import java.util.Optional; 28 | import java.util.UUID; 29 | 30 | public interface StaffChatData { 31 | StaffChatProfile getOrCreateProfile(UUID uuid); 32 | 33 | Optional getProfile(UUID uuid); 34 | 35 | default StaffChatProfile getOrCreateProfile(Player player) { 36 | return getOrCreateProfile(player.getUniqueId()); 37 | } 38 | 39 | default Optional getProfile(Player player) { 40 | // If they're a staff member, then they will always have a profile 41 | // otherwise, return the possibly existing profile for non-staff 42 | return (Permissions.ACCESS.allows(player)) 43 | ? Optional.of(getOrCreateProfile(player.getUniqueId())) 44 | : getProfile(player.getUniqueId()); 45 | } 46 | 47 | default boolean isAutomaticStaffChatEnabled(Player player) { 48 | return getProfile(player).filter(StaffChatProfile::automaticStaffChat).isPresent(); 49 | } 50 | 51 | default boolean isReceivingStaffChatMessages(Player player) { 52 | return getProfile(player).filter(StaffChatProfile::receivesStaffChatMessages).isPresent(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/StaffChatPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import com.github.zafarkhaja.semver.Version; 26 | import com.rezzedup.discordsrv.staffchat.commands.ManageStaffChatCommand; 27 | import com.rezzedup.discordsrv.staffchat.commands.StaffChatCommand; 28 | import com.rezzedup.discordsrv.staffchat.commands.ToggleStaffChatCommand; 29 | import com.rezzedup.discordsrv.staffchat.commands.ToggleStaffChatSoundsCommand; 30 | import com.rezzedup.discordsrv.staffchat.config.MessagesConfig; 31 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 32 | import com.rezzedup.discordsrv.staffchat.listeners.DiscordSrvLoadedLaterListener; 33 | import com.rezzedup.discordsrv.staffchat.listeners.DiscordStaffChatListener; 34 | import com.rezzedup.discordsrv.staffchat.listeners.JoinNotificationListener; 35 | import com.rezzedup.discordsrv.staffchat.listeners.PlayerPrefixedMessageListener; 36 | import com.rezzedup.discordsrv.staffchat.listeners.PlayerStaffChatToggleListener; 37 | import com.rezzedup.discordsrv.staffchat.util.FileIO; 38 | import community.leaf.configvalues.bukkit.YamlValue; 39 | import community.leaf.configvalues.bukkit.data.YamlDataFile; 40 | import community.leaf.eventful.bukkit.BukkitEventSource; 41 | import community.leaf.tasks.bukkit.BukkitTaskSource; 42 | import github.scarsz.discordsrv.DiscordSRV; 43 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Message; 44 | import github.scarsz.discordsrv.dependencies.jda.api.entities.TextChannel; 45 | import github.scarsz.discordsrv.dependencies.jda.api.entities.User; 46 | import org.bstats.bukkit.Metrics; 47 | import org.bstats.charts.SimplePie; 48 | import org.bukkit.command.CommandExecutor; 49 | import org.bukkit.command.PluginCommand; 50 | import org.bukkit.command.TabCompleter; 51 | import org.bukkit.entity.Player; 52 | import org.bukkit.plugin.Plugin; 53 | import org.bukkit.plugin.java.JavaPlugin; 54 | import pl.tlinkowski.annotation.basic.NullOr; 55 | 56 | import java.nio.file.Files; 57 | import java.nio.file.Path; 58 | import java.util.List; 59 | 60 | public class StaffChatPlugin extends JavaPlugin implements BukkitTaskSource, BukkitEventSource, StaffChatAPI { 61 | // https://bstats.org/plugin/bukkit/DiscordSRV-Staff-Chat/11056 62 | public static final int BSTATS = 11056; 63 | 64 | public static final String CHANNEL = "staff-chat"; 65 | 66 | public static final String DISCORDSRV = "DiscordSRV"; 67 | 68 | private @NullOr Version version; 69 | private @NullOr Path pluginDirectoryPath; 70 | private @NullOr Path backupsDirectoryPath; 71 | private @NullOr Debugger debugger; 72 | private @NullOr StaffChatConfig config; 73 | private @NullOr MessagesConfig messages; 74 | private @NullOr Data data; 75 | private @NullOr Updater updater; 76 | private @NullOr MessageProcessor processor; 77 | private @NullOr DiscordStaffChatListener discordSrvHook; 78 | 79 | @Override 80 | public void onEnable() { 81 | this.version = Version.valueOf(getDescription().getVersion()); 82 | 83 | this.pluginDirectoryPath = getDataFolder().toPath(); 84 | this.backupsDirectoryPath = pluginDirectoryPath.resolve("backups"); 85 | 86 | this.debugger = new Debugger(this); 87 | 88 | debug(getClass()).header(() -> "Starting Plugin: " + this); 89 | debugger().schedulePluginStatus(getClass(), "Enable"); 90 | 91 | this.config = new StaffChatConfig(this); 92 | this.messages = new MessagesConfig(this); 93 | 94 | loadConfigurationFiles(); 95 | 96 | this.data = new Data(this); 97 | this.updater = new Updater(this); 98 | this.processor = new MessageProcessor(this); 99 | 100 | events().register(new JoinNotificationListener(this)); 101 | events().register(new PlayerPrefixedMessageListener(this)); 102 | events().register(new PlayerStaffChatToggleListener(this)); 103 | 104 | 105 | command("staffchat", new StaffChatCommand(this)); 106 | command("managestaffchat", new ManageStaffChatCommand(this)); 107 | command("togglestaffchatsounds", new ToggleStaffChatSoundsCommand(this)); 108 | 109 | ToggleStaffChatCommand toggle = new ToggleStaffChatCommand(this); 110 | command("leavestaffchat", toggle); 111 | command("joinstaffchat", toggle); 112 | 113 | @NullOr Plugin discordSrv = getServer().getPluginManager().getPlugin(DISCORDSRV); 114 | 115 | if (discordSrv != null) { 116 | debug(getClass()).log("Enable", () -> "DiscordSRV is enabled"); 117 | subscribeToDiscordSrv(discordSrv); 118 | } else { 119 | debug(getClass()).log("Enable", () -> "DiscordSRV is not enabled: continuing without discord support"); 120 | 121 | getLogger().warning("DiscordSRV is not currently enabled (messages will NOT be sent to Discord)."); 122 | getLogger().warning("Staff chat messages will still work in-game, however."); 123 | 124 | // Subscribe to DiscordSRV later because it somehow hasn't enabled yet. 125 | events().register(new DiscordSrvLoadedLaterListener(this)); 126 | } 127 | 128 | startMetrics(); 129 | 130 | // Display toggle message so that auto staff-chat users are aware that their chat is private again. 131 | // Useful when hot loading this plugin on a live server. 132 | onlineStaffChatParticipants() 133 | .filter(data()::isAutomaticStaffChatEnabled) 134 | .forEach(messages()::notifyAutoChatEnabled); 135 | } 136 | 137 | @Override 138 | public void onDisable() { 139 | debug(getClass()).log("Disable", () -> "Disabling plugin..."); 140 | 141 | data().end(); 142 | updater().end(); 143 | 144 | // Display toggle message so that auto staff-chat users are aware that their chat is public again. 145 | // Useful when selectively disabling this plugin on a live server. 146 | onlineStaffChatParticipants() 147 | .filter(data()::isAutomaticStaffChatEnabled) 148 | .forEach(messages()::notifyAutoChatDisabled); 149 | 150 | if (isDiscordSrvHookEnabled()) { 151 | debug(getClass()).log("Disable", () -> "Unsubscribing from DiscordSRV API (hook is enabled)"); 152 | 153 | try { 154 | DiscordSRV.api.unsubscribe(discordSrvHook); 155 | } catch (RuntimeException ignored) { 156 | } // Don't show a user-facing error if DiscordSRV is already unloaded. 157 | } 158 | 159 | debug(getClass()).header(() -> "Disabled Plugin: " + this); 160 | } 161 | 162 | private T initialized(@NullOr T thing) { 163 | if (thing != null) { 164 | return thing; 165 | } 166 | throw new IllegalStateException("Not initialized yet"); 167 | } 168 | 169 | @Override 170 | public Plugin plugin() { 171 | return this; 172 | } 173 | 174 | public Version version() { 175 | return initialized(version); 176 | } 177 | 178 | public Path directory() { 179 | return initialized(pluginDirectoryPath); 180 | } 181 | 182 | public Path backups() { 183 | return initialized(backupsDirectoryPath); 184 | } 185 | 186 | public Debugger debugger() { 187 | return initialized(debugger); 188 | } 189 | 190 | public Debugger.DebugLogger debug(Class clazz) { 191 | return debugger().debug(clazz); 192 | } 193 | 194 | public StaffChatConfig config() { 195 | return initialized(config); 196 | } 197 | 198 | public MessagesConfig messages() { 199 | return initialized(messages); 200 | } 201 | 202 | @Override 203 | public Data data() { 204 | return initialized(data); 205 | } 206 | 207 | public Updater updater() { 208 | return initialized(updater); 209 | } 210 | 211 | @Override 212 | public boolean isDiscordSrvHookEnabled() { 213 | return discordSrvHook != null; 214 | } 215 | 216 | public void subscribeToDiscordSrv(Plugin plugin) { 217 | debug(getClass()).log("Subscribe", () -> "Subscribing to DiscordSRV: " + plugin); 218 | 219 | if (!DISCORDSRV.equals(plugin.getName()) || !(plugin instanceof DiscordSRV)) { 220 | throw debug(getClass()).failure("Subscribe", new IllegalArgumentException("Not DiscordSRV: " + plugin)); 221 | } 222 | 223 | if (isDiscordSrvHookEnabled()) { 224 | throw debug(getClass()).failure("Subscribe", new IllegalStateException( 225 | "Already subscribed to DiscordSRV. Did the server reload? ... If so, don't do that!" 226 | )); 227 | } 228 | 229 | DiscordSRV.api.subscribe(discordSrvHook = new DiscordStaffChatListener(this)); 230 | 231 | getLogger().info("Subscribed to DiscordSRV: messages will be sent to Discord"); 232 | } 233 | 234 | @Override 235 | public @NullOr TextChannel getDiscordChannelOrNull() { 236 | return (isDiscordSrvHookEnabled()) 237 | ? DiscordSRV.getPlugin().getDestinationTextChannelForGameChannelName(CHANNEL) 238 | : null; 239 | } 240 | 241 | private MessageProcessor processor() { 242 | return initialized(processor); 243 | } 244 | 245 | @Override 246 | public void submitMessageFromConsole(String message) { 247 | processor().processConsoleChat(message); 248 | } 249 | 250 | @Override 251 | public void submitMessageFromPlayer(Player author, String message) { 252 | processor().processPlayerChat(author, message); 253 | } 254 | 255 | @Override 256 | public void submitMessageFromDiscord(User author, Message message) { 257 | processor().processDiscordChat(author, message); 258 | } 259 | 260 | // 261 | // 262 | // 263 | 264 | private void loadConfigurationFiles() { 265 | // Explicitly load configs 266 | config().reload(); 267 | messages().reload(); 268 | 269 | // Upgrade & migrate legacy config if it exists 270 | upgradeLegacyConfig(); 271 | } 272 | 273 | private void upgradeLegacyConfig(YamlDataFile file, List> values) { 274 | file.migrateValues(values, getConfig()); 275 | 276 | if (file.isNewlyCreated()) { 277 | file.save(); 278 | } else { 279 | file.backupThenSave(backups(), "migrated"); 280 | } 281 | } 282 | 283 | private void upgradeLegacyConfig() { 284 | Path legacyConfigPath = directory().resolve("config.yml"); 285 | if (!Files.isRegularFile(legacyConfigPath)) { 286 | return; 287 | } 288 | 289 | debug(getClass()).log("Upgrade Legacy Config", () -> 290 | "Found legacy config, upgrading it to new configs..." 291 | ); 292 | 293 | upgradeLegacyConfig(config(), StaffChatConfig.VALUES); 294 | upgradeLegacyConfig(messages(), MessagesConfig.VALUES); 295 | 296 | try { 297 | FileIO.backup(legacyConfigPath, backups().resolve("config.legacy.yml")); 298 | Files.deleteIfExists(legacyConfigPath); 299 | } catch (Exception e) { 300 | e.printStackTrace(); 301 | debug(getClass()).log("Upgrade Legacy Config", () -> 302 | "Failed to backup legacy config: " + e.getMessage() 303 | ); 304 | } 305 | } 306 | 307 | private void command(String name, CommandExecutor executor) { 308 | @NullOr PluginCommand command = getCommand(name); 309 | 310 | if (command == null) { 311 | debug(getClass()).log("Command: Setup", () -> 312 | "Unable to register command /" + name + " because it is not defined in plugin.yml" 313 | ); 314 | return; 315 | } 316 | 317 | command.setExecutor(executor); 318 | debug(getClass()).log("Command: Setup", () -> "Registered command executor for: /" + name); 319 | 320 | if (executor instanceof TabCompleter) { 321 | command.setTabCompleter((TabCompleter) executor); 322 | debug(getClass()).log("Command: Setup", () -> "Registered tab completer for: /" + name); 323 | } 324 | } 325 | 326 | private void startMetrics() { 327 | if (!config().getOrDefault(StaffChatConfig.METRICS_ENABLED)) { 328 | debug(getClass()).log("Metrics", () -> "Aborting: metrics are disabled in the config"); 329 | return; 330 | } 331 | 332 | debug(getClass()).log("Metrics", () -> "Scheduling metrics to start one minute from now"); 333 | 334 | // Start a minute later to get the most accurate data. 335 | sync().delay(1).minutes().run(() -> 336 | { 337 | Metrics metrics = new Metrics(this, BSTATS); 338 | 339 | metrics.addCustomChart(new SimplePie( 340 | "hooked_into_discordsrv", () -> String.valueOf(isDiscordSrvHookEnabled()) 341 | )); 342 | 343 | metrics.addCustomChart(new SimplePie( 344 | "has_valid_staff-chat_channel", () -> String.valueOf(getDiscordChannelOrNull() != null) 345 | )); 346 | 347 | debug(getClass()).log("Metrics", () -> "Started bStats metrics"); 348 | }); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/StaffChatProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import org.bukkit.Bukkit; 26 | import org.bukkit.entity.Player; 27 | 28 | import java.time.Instant; 29 | import java.util.Optional; 30 | import java.util.UUID; 31 | 32 | public interface StaffChatProfile { 33 | UUID uuid(); 34 | 35 | Optional sinceEnabledAutoChat(); 36 | 37 | boolean automaticStaffChat(); 38 | 39 | void automaticStaffChat(boolean enabled); 40 | 41 | Optional sinceLeftStaffChat(); 42 | 43 | boolean receivesStaffChatMessages(); 44 | 45 | void receivesStaffChatMessages(boolean enabled); 46 | 47 | boolean receivesStaffChatSounds(); 48 | 49 | void receivesStaffChatSounds(boolean enabled); 50 | 51 | default void toggleAutomaticStaffChat() { 52 | automaticStaffChat(!automaticStaffChat()); 53 | } 54 | 55 | default Optional toPlayer() { 56 | return Optional.ofNullable(Bukkit.getPlayer(uuid())); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/Updater.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat; 24 | 25 | import com.github.zafarkhaja.semver.Version; 26 | import com.google.gson.JsonObject; 27 | import com.google.gson.JsonParser; 28 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 29 | import community.leaf.tasks.TaskContext; 30 | import net.md_5.bungee.api.ChatColor; 31 | import org.bukkit.command.CommandSender; 32 | import org.bukkit.command.ConsoleCommandSender; 33 | import org.bukkit.entity.Player; 34 | import org.bukkit.scheduler.BukkitTask; 35 | import pl.tlinkowski.annotation.basic.NullOr; 36 | 37 | import java.net.URI; 38 | import java.net.http.HttpClient; 39 | import java.net.http.HttpRequest; 40 | import java.net.http.HttpResponse; 41 | import java.util.Optional; 42 | 43 | public class Updater { 44 | public static final String RESOURCE_PAGE = "https://www.spigotmc.org/resources/44245/"; 45 | 46 | public static final URI LATEST_UPDATE_API_ENDPOINT = URI.create("https://api.spiget.org/v2/resources/44245/versions/latest"); 47 | 48 | private final StaffChatPlugin plugin; 49 | private final HttpClient client; 50 | private final String userAgent; 51 | 52 | private @NullOr TaskContext task; 53 | private @NullOr Version latestAvailableVersion; 54 | 55 | Updater(StaffChatPlugin plugin) { 56 | this.plugin = plugin; 57 | this.client = HttpClient.newHttpClient(); 58 | 59 | this.userAgent = 60 | plugin.getName() + "/" + plugin.version() + " (Minecraft) " + 61 | plugin.getServer().getName() + "/" + plugin.getServer().getVersion(); 62 | 63 | plugin.debug(getClass()).log("Init", () -> "User-Agent: " + userAgent); 64 | 65 | reload(); 66 | } 67 | 68 | public boolean isRunningUpdateCheckTask() { 69 | return task != null; 70 | } 71 | 72 | public Optional latestAvailableVersion() { 73 | return Optional.ofNullable(latestAvailableVersion); 74 | } 75 | 76 | public Optional latestUpdateVersion() { 77 | return latestAvailableVersion().filter(plugin.version()::lessThan); 78 | } 79 | 80 | public boolean isOutdated() { 81 | return latestUpdateVersion().isPresent(); 82 | } 83 | 84 | public void end() { 85 | if (task != null) { 86 | task.cancel(); 87 | } 88 | } 89 | 90 | public void reload() { 91 | if (plugin.config().getOrDefault(StaffChatConfig.UPDATE_CHECKER_ENABLED)) { 92 | if (task == null || task.isCancelled()) { 93 | plugin.debug(getClass()).log("Reload", () -> "Update checker enabled: starting task"); 94 | this.task = plugin.async().delay(10).ticks().every(7).hours().run(this::checkForUpdates); 95 | } else { 96 | plugin.debug(getClass()).log("Reload", () -> "Update checker enabled: task already running"); 97 | } 98 | } else { 99 | latestAvailableVersion = null; 100 | 101 | if (task != null) { 102 | plugin.debug(getClass()).log("Reload", () -> "Update checker disabled: ending previously-enabled task"); 103 | task.cancel(); 104 | } else { 105 | plugin.debug(getClass()).log("Reload", () -> "Update checker disabled: will not start task"); 106 | } 107 | } 108 | } 109 | 110 | private void checkForUpdates() { 111 | HttpRequest request = 112 | HttpRequest.newBuilder(LATEST_UPDATE_API_ENDPOINT) 113 | .setHeader("User-Agent", userAgent) 114 | .build(); 115 | 116 | try { 117 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 118 | 119 | if (response.statusCode() != 200) { 120 | plugin.debug(getClass()).log("Update Check: Failure", () -> 121 | "Cannot check latest version, response status code: " + response.statusCode() 122 | ); 123 | return; 124 | } 125 | 126 | JsonObject json = new JsonParser().parse(response.body()).getAsJsonObject(); 127 | this.latestAvailableVersion = Version.valueOf(json.get("name").getAsString()); 128 | 129 | plugin.debug(getClass()).log("Update Check: Success", () -> 130 | "Found latest available version: " + latestAvailableVersion + " (current: " + plugin.version() + ")" 131 | ); 132 | 133 | plugin.sync().run(() -> notifyIfUpdateAvailable(plugin.getServer().getConsoleSender())); 134 | } catch (Exception e) { 135 | plugin.debug(getClass()).logException("Update Check: Failure", e); 136 | } 137 | } 138 | 139 | private void print(String text) { 140 | plugin.getLogger().info(ChatColor.BLUE + text); 141 | } 142 | 143 | public void notifyIfUpdateAvailable(CommandSender sender) { 144 | latestUpdateVersion().ifPresent(version -> 145 | { 146 | if (sender instanceof Player) { 147 | plugin.messages().notifyUpdateAvailable((Player) sender, version); 148 | } else if (sender instanceof ConsoleCommandSender) { 149 | print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); 150 | print("An update is available: " + version + " (current: " + plugin.version() + ")"); 151 | print("Get the update @ " + RESOURCE_PAGE); 152 | print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); 153 | } 154 | }); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/commands/ManageStaffChatCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.commands; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import com.rezzedup.util.constants.Aggregates; 27 | import com.rezzedup.util.constants.MatchRules; 28 | import com.rezzedup.util.constants.annotations.AggregatedResult; 29 | import org.bukkit.command.Command; 30 | import org.bukkit.command.CommandExecutor; 31 | import org.bukkit.command.CommandSender; 32 | import org.bukkit.command.TabCompleter; 33 | import org.bukkit.entity.Player; 34 | import pl.tlinkowski.annotation.basic.NullOr; 35 | 36 | import java.util.ArrayList; 37 | import java.util.List; 38 | import java.util.Locale; 39 | import java.util.Set; 40 | import java.util.stream.Collectors; 41 | 42 | import static com.rezzedup.discordsrv.staffchat.util.Strings.colorful; 43 | 44 | public class ManageStaffChatCommand implements CommandExecutor, TabCompleter { 45 | private static final Set RELOAD_ALIASES = Set.of("reload"); 46 | private static final Set DEBUG_ALIASES = Set.of("debug"); 47 | private static final Set HELP_ALIASES = Set.of("help", "usage", "?"); 48 | 49 | @AggregatedResult 50 | private static final Set ALL_OPTION_ALIASES = 51 | Aggregates.fromThisClass() 52 | .constantsOfType(String.class) 53 | .matching( 54 | MatchRules.of().all("ALIAS").collections(true) 55 | ) 56 | .toSet(); 57 | 58 | private final StaffChatPlugin plugin; 59 | 60 | public ManageStaffChatCommand(StaffChatPlugin plugin) { 61 | this.plugin = plugin; 62 | } 63 | 64 | @Override 65 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 66 | @NullOr String option = (args.length >= 1) ? args[0].toLowerCase(Locale.ROOT) : null; 67 | 68 | if (option == null || HELP_ALIASES.contains(option)) { 69 | usage(sender, label); 70 | } else if (RELOAD_ALIASES.contains(option)) { 71 | reload(sender); 72 | } else if (DEBUG_ALIASES.contains(option)) { 73 | debug(sender); 74 | } else { 75 | sender.sendMessage(colorful( 76 | "&9&lDiscordSRV-Staff-Chat&f: &7&oUnknown arguments: " + String.join(" ", args) 77 | )); 78 | } 79 | 80 | return true; 81 | } 82 | 83 | @Override 84 | public @NullOr List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 85 | @NullOr List suggestions = null; 86 | 87 | if (args.length <= 0) { 88 | suggestions = new ArrayList<>(ALL_OPTION_ALIASES); 89 | } else if (args.length == 1) { 90 | String last = args[0].toLowerCase(Locale.ROOT); 91 | 92 | suggestions = 93 | ALL_OPTION_ALIASES.stream() 94 | .filter(option -> option.contains(last)) 95 | .collect(Collectors.toCollection(ArrayList::new)); 96 | } 97 | 98 | if (suggestions != null) { 99 | suggestions.sort(String.CASE_INSENSITIVE_ORDER); 100 | } 101 | return suggestions; 102 | } 103 | 104 | private void usage(CommandSender sender, String label) { 105 | sender.sendMessage(colorful( 106 | "&9DiscordSRV-&lStaff&9-&lChat &fv" + plugin.getDescription().getVersion() + " Usage:" 107 | )); 108 | 109 | sender.sendMessage(colorful("&f- &7/staffchat &9Toggle automatic staff chat")); 110 | sender.sendMessage(colorful("&f- &7/staffchat &9Send a message to staff chat")); 111 | sender.sendMessage(colorful("&f- &7/leavestaffchat &9Leave the staff chat")); 112 | sender.sendMessage(colorful("&f- &7/joinstaffchat &9Rejoin the staff chat")); 113 | sender.sendMessage(colorful("&f- &7/" + label.toLowerCase() + " reload &9Reload configs")); 114 | sender.sendMessage(colorful("&f- &7/" + label.toLowerCase() + " debug &9Toggle debugging")); 115 | 116 | if (plugin.debugger().isEnabled()) { 117 | sender.sendMessage(colorful("&2→ &aDebugging is currently enabled")); 118 | } else { 119 | sender.sendMessage(colorful("&7→ &8Debugging is currently disabled")); 120 | } 121 | 122 | plugin.updater().notifyIfUpdateAvailable(sender); 123 | } 124 | 125 | private void reload(CommandSender sender) { 126 | plugin.debug(getClass()).log("Reload", () -> "Reloading configs and data..."); 127 | 128 | plugin.config().reload(); 129 | plugin.messages().reload(); 130 | plugin.data().reload(); 131 | plugin.updater().reload(); 132 | 133 | sender.sendMessage(colorful("&9&lDiscordSRV-Staff-Chat&f: Reloaded.")); 134 | } 135 | 136 | private void debug(CommandSender sender) { 137 | boolean enabled = !plugin.debugger().isEnabled(); 138 | plugin.debugger().setEnabled(enabled); 139 | 140 | if (enabled) { 141 | plugin.debugger().schedulePluginStatus(getClass(), "Debug Toggle"); 142 | sender.sendMessage(colorful("&9[Debug] &2→ &aEnabled debugging")); 143 | 144 | if (sender instanceof Player) { 145 | sender.sendMessage(colorful("&9[Debug]&o Sending a test message...")); 146 | plugin.sync().delay(10).ticks().run(() -> 147 | plugin.getServer().dispatchCommand(sender, "staffchat Hello! Just testing things...") 148 | ); 149 | } 150 | } else { 151 | sender.sendMessage(colorful("&9[Debug] &4→ &cDisabled debugging")); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/commands/StaffChatCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.commands; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import org.bukkit.command.Command; 27 | import org.bukkit.command.CommandExecutor; 28 | import org.bukkit.command.CommandSender; 29 | import org.bukkit.command.ConsoleCommandSender; 30 | import org.bukkit.entity.Player; 31 | 32 | public class StaffChatCommand implements CommandExecutor { 33 | private final StaffChatPlugin plugin; 34 | 35 | public StaffChatCommand(StaffChatPlugin plugin) { 36 | this.plugin = plugin; 37 | } 38 | 39 | @Override 40 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 41 | if (args.length <= 0) { 42 | // Show usage to console (only players can enable auto chat) 43 | if (!(sender instanceof Player)) { 44 | return false; 45 | } 46 | plugin.data().getOrCreateProfile((Player) sender).toggleAutomaticStaffChat(); 47 | } else { 48 | String message = String.join(" ", args); 49 | 50 | if (sender instanceof Player) { 51 | plugin.submitMessageFromPlayer((Player) sender, message); 52 | } else if (sender instanceof ConsoleCommandSender) { 53 | plugin.submitMessageFromConsole(message); 54 | } else { 55 | sender.sendMessage("Unsupported command sender type: " + sender.getClass().getSimpleName()); 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/commands/ToggleStaffChatCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.commands; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import org.bukkit.command.Command; 27 | import org.bukkit.command.CommandExecutor; 28 | import org.bukkit.command.CommandSender; 29 | import org.bukkit.entity.Player; 30 | 31 | public class ToggleStaffChatCommand implements CommandExecutor { 32 | private final StaffChatPlugin plugin; 33 | 34 | public ToggleStaffChatCommand(StaffChatPlugin plugin) { 35 | this.plugin = plugin; 36 | } 37 | 38 | @Override 39 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 40 | if (sender instanceof Player) { 41 | // Either join or leave so... 42 | plugin.data().getOrCreateProfile((Player) sender) 43 | .receivesStaffChatMessages(command.getName().contains("join")); 44 | } else { 45 | sender.sendMessage("Only players may run this command."); 46 | } 47 | 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/commands/ToggleStaffChatSoundsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.commands; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatProfile; 27 | import org.bukkit.command.Command; 28 | import org.bukkit.command.CommandExecutor; 29 | import org.bukkit.command.CommandSender; 30 | import org.bukkit.entity.Player; 31 | import org.jetbrains.annotations.NotNull; 32 | 33 | public class ToggleStaffChatSoundsCommand implements CommandExecutor { 34 | private final StaffChatPlugin plugin; 35 | 36 | public ToggleStaffChatSoundsCommand(StaffChatPlugin plugin) { 37 | this.plugin = plugin; 38 | } 39 | 40 | @Override 41 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 42 | if (sender instanceof Player) { 43 | Player player = (Player) sender; 44 | StaffChatProfile profile = plugin.data().getOrCreateProfile(player); 45 | boolean toggle = !profile.receivesStaffChatSounds(); 46 | profile.receivesStaffChatSounds(toggle); 47 | 48 | plugin.debug(getClass()).log(() -> String.format( 49 | "Player: %s (%s) has %s receiving staff chat sounds", 50 | player.getName(), profile.uuid(), ((toggle) ? "enabled (unmuted)" : "disabled (muted)") 51 | )); 52 | 53 | if (toggle) { 54 | plugin.messages().notifySoundsUnmuted(player); 55 | } else { 56 | plugin.messages().notifySoundsMuted(player); 57 | } 58 | } else { 59 | sender.sendMessage("Only players may run this command."); 60 | } 61 | 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/commands/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat.commands; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/config/Configs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.config; 24 | 25 | import com.github.zafarkhaja.semver.Version; 26 | import com.rezzedup.util.valuables.Adapter; 27 | import community.leaf.configvalues.bukkit.YamlAccessor; 28 | 29 | import java.nio.file.Path; 30 | import java.util.Optional; 31 | import java.util.logging.Logger; 32 | 33 | public class Configs { 34 | private Configs() { 35 | throw new UnsupportedOperationException(); 36 | } 37 | 38 | public static final Version NO_VERSION = Version.forIntegers(0, 0, 0); 39 | 40 | public static YamlAccessor VERSION = 41 | YamlAccessor.of(Adapter.of( 42 | object -> { 43 | try { 44 | return Optional.of(Version.valueOf(String.valueOf(object))); 45 | } catch (RuntimeException e) { 46 | return Optional.empty(); 47 | } 48 | }, 49 | version -> Optional.of(String.valueOf(version)) 50 | )); 51 | 52 | public static void couldNotLoad(Logger logger, Path path) { 53 | logger.warning("Couldn't load configuration: " + path.getFileName()); 54 | logger.warning("Default values will be used until the config file is repaired."); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/config/MessagesConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.config; 24 | 25 | import com.github.zafarkhaja.semver.Version; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 27 | import com.rezzedup.discordsrv.staffchat.Updater; 28 | import com.rezzedup.discordsrv.staffchat.util.MappedPlaceholder; 29 | import com.rezzedup.discordsrv.staffchat.util.Strings; 30 | import com.rezzedup.util.constants.Aggregates; 31 | import com.rezzedup.util.constants.annotations.AggregatedResult; 32 | import community.leaf.configvalues.bukkit.DefaultYamlValue; 33 | import community.leaf.configvalues.bukkit.ExampleYamlValue; 34 | import community.leaf.configvalues.bukkit.YamlValue; 35 | import community.leaf.configvalues.bukkit.data.Load; 36 | import community.leaf.configvalues.bukkit.data.YamlDataFile; 37 | import community.leaf.configvalues.bukkit.migrations.Migration; 38 | import community.leaf.configvalues.bukkit.util.Sections; 39 | import org.bukkit.entity.Player; 40 | import pl.tlinkowski.annotation.basic.NullOr; 41 | 42 | import java.util.List; 43 | import java.util.function.Predicate; 44 | 45 | public class MessagesConfig extends YamlDataFile { 46 | public static final YamlValue VERSION = 47 | YamlValue.of("meta.config-version", Configs.VERSION).maybe(); 48 | 49 | public static final DefaultYamlValue PREFIX = 50 | YamlValue.ofString("placeholders.prefix") 51 | .defaults("&d(&5&l&oStaff&d)"); 52 | 53 | public static final ExampleYamlValue EXAMPLE_PLACEHOLDER = 54 | YamlValue.ofString("placeholders.example") 55 | .example("Define your own placeholders here!"); 56 | 57 | public static final DefaultYamlValue IN_GAME_PLAYER_FORMAT = 58 | YamlValue.ofString("messages.in-game-formats.player") 59 | .migrates(Migration.move("in-game-message-format")) 60 | .defaults("%prefix% %name%&7:&f %message%"); 61 | 62 | public static final DefaultYamlValue IN_GAME_DISCORD_FORMAT = 63 | YamlValue.ofString("messages.in-game-formats.discord") 64 | .migrates(Migration.move("discord-message-format")) 65 | .defaults("&9&ldiscord &f→ %prefix% %name%&7:&f %message%"); 66 | 67 | public static final DefaultYamlValue IN_GAME_CONSOLE_FORMAT = 68 | YamlValue.ofString("messages.in-game-formats.console") 69 | .defaults("%prefix% [CONSOLE]&7:&f %message%"); 70 | 71 | public static final DefaultYamlValue DISCORD_CONSOLE_FORMAT = 72 | YamlValue.ofString("messages.discord-formats.console") 73 | .defaults("**`CONSOLE:`** %message%"); 74 | 75 | public static final DefaultYamlValue AUTO_ENABLED_NOTIFICATION = 76 | YamlValue.ofString("notifications.automatic-staff-chat.enabled") 77 | .migrates(Migration.move("enable-staff-chat")) 78 | .defaults("%prefix% &2→&a &nEnabled&a automatic staff chat"); 79 | 80 | public static final DefaultYamlValue AUTO_DISABLED_NOTIFICATION = 81 | YamlValue.ofString("notifications.automatic-staff-chat.disabled") 82 | .migrates(Migration.move("disable-staff-chat")) 83 | .defaults("%prefix% &4→&c &nDisabled&c automatic staff chat"); 84 | 85 | public static final DefaultYamlValue LEFT_CHAT_NOTIFICATION_SELF = 86 | YamlValue.ofString("notifications.leave.self") 87 | .defaults( 88 | "%prefix% &4→&c You &nleft&c the staff chat&r\n" + 89 | "&8&oYou won't receive any staff chat messages" 90 | ); 91 | 92 | public static final DefaultYamlValue LEFT_CHAT_NOTIFICATION_OTHERS = 93 | YamlValue.ofString("notifications.leave.others") 94 | .defaults("%prefix% &4→&c %player% &nleft&c the staff chat"); 95 | 96 | public static final DefaultYamlValue LEFT_CHAT_NOTIFICATION_REMINDER = 97 | YamlValue.ofString("notifications.leave.reminder") 98 | .defaults("&8&o(Reminder: you left the staff chat)"); 99 | 100 | public static final DefaultYamlValue LEFT_CHAT_DISABLED_ERROR = 101 | YamlValue.ofString("notifications.leave.disabled") 102 | .defaults( 103 | "%prefix% &6→&e You cannot leave the staff chat\n" + 104 | "&8&oLeaving the staff chat is currently disabled" 105 | ); 106 | 107 | public static final DefaultYamlValue JOIN_CHAT_NOTIFICATION_SELF = 108 | YamlValue.ofString("notifications.join.self") 109 | .defaults( 110 | "%prefix% &2→&a You &njoined&a the staff chat&r\n" + 111 | "&8&oYou will now receive staff chat messages again" 112 | ); 113 | 114 | public static final DefaultYamlValue JOIN_CHAT_NOTIFICATION_OTHERS = 115 | YamlValue.ofString("notifications.join.others") 116 | .defaults("%prefix% &2→&a %player% &njoined&a the staff chat"); 117 | 118 | public static final DefaultYamlValue MUTE_SOUNDS_NOTIFICATION = 119 | YamlValue.ofString("notifications.sounds.muted") 120 | .defaults( 121 | "%prefix% &4→&c You have &nmuted&c staff chat sounds" 122 | ); 123 | 124 | public static final DefaultYamlValue UNMUTE_SOUNDS_NOTIFICATION = 125 | YamlValue.ofString("notifications.sounds.unmuted") 126 | .defaults( 127 | "%prefix% &2→&a You have &nunmuted&a staff chat sounds" 128 | ); 129 | 130 | @AggregatedResult 131 | public static final List> VALUES = 132 | Aggregates.fromThisClass().constantsOfType(YamlValue.type()).toList(); 133 | 134 | private final StaffChatPlugin plugin; 135 | 136 | private @NullOr MappedPlaceholder definitions = null; 137 | 138 | public MessagesConfig(StaffChatPlugin plugin) { 139 | super(plugin.directory(), "messages.config.yml", Load.LATER); 140 | this.plugin = plugin; 141 | 142 | reloadsWith(() -> 143 | { 144 | if (isInvalid()) { 145 | Configs.couldNotLoad(plugin.getLogger(), getFilePath()); 146 | plugin.debug(getClass()).log("Reload", () -> "Couldn't load: " + getInvalidReason()); 147 | 148 | // Add default placeholders 149 | if (definitions == null) { 150 | definitions = new MappedPlaceholder(); 151 | definitions.map("prefix").to(PREFIX::getDefaultValue); 152 | } 153 | 154 | return; 155 | } 156 | 157 | Version existing = get(VERSION).orElse(Configs.NO_VERSION); 158 | boolean isOutdated = existing.lessThan(plugin.version()); 159 | 160 | if (isOutdated) { 161 | plugin.debug(getClass()).log("Reload", () -> "Updating outdated config: " + existing); 162 | set(VERSION, plugin.version()); 163 | } 164 | 165 | headerFromResource("messages.config.header.txt"); 166 | defaultValues(VALUES); 167 | 168 | if (isUpdated()) { 169 | plugin.debug(getClass()).log("Reload", () -> "Saving updated config and backing up old config: v" + existing); 170 | backupThenSave(plugin.backups(), "v" + existing); 171 | } 172 | 173 | // Remove old placeholder definitions 174 | definitions = null; 175 | 176 | // Load defined placeholders 177 | Sections.get(data(), "placeholders").ifPresent(section -> 178 | { 179 | definitions = new MappedPlaceholder(); 180 | 181 | for (String key : section.getKeys(false)) { 182 | @NullOr String value = section.getString(key); 183 | if (Strings.isEmptyOrNull(value)) { 184 | continue; 185 | } 186 | definitions.map(key).to(() -> value); 187 | } 188 | }); 189 | }); 190 | } 191 | 192 | public MappedPlaceholder placeholders() { 193 | MappedPlaceholder placeholders = new MappedPlaceholder(); 194 | if (definitions != null) { 195 | placeholders.inherit(definitions); 196 | } 197 | return placeholders; 198 | } 199 | 200 | public MappedPlaceholder placeholders(Player player) { 201 | MappedPlaceholder placeholders = placeholders(); 202 | 203 | placeholders.map("user", "name", "username", "player", "sender").to(player::getName); 204 | placeholders.map("nickname", "displayname").to(player::getDisplayName); 205 | 206 | return placeholders; 207 | } 208 | 209 | private void sendNotification(Player player, String message) { 210 | player.sendMessage(message); 211 | plugin.config().playNotificationSound(player); 212 | } 213 | 214 | private void sendNotification(Player player, DefaultYamlValue self, @NullOr DefaultYamlValue others) { 215 | MappedPlaceholder placeholders = placeholders(player); 216 | sendNotification(player, Strings.colorful(placeholders.update(getOrDefault(self)))); 217 | 218 | if (others == null) { 219 | return; 220 | } 221 | 222 | String notification = Strings.colorful(placeholders.update(getOrDefault(others))); 223 | plugin.getServer().getConsoleSender().sendMessage(notification); 224 | 225 | plugin.onlineStaffChatParticipants() 226 | .filter(Predicate.not(player::equals)) 227 | .forEach(staff -> sendNotification(staff, notification)); 228 | } 229 | 230 | public void notifyAutoChatEnabled(Player enabler) { 231 | sendNotification(enabler, AUTO_ENABLED_NOTIFICATION, null); 232 | } 233 | 234 | public void notifyAutoChatDisabled(Player disabler) { 235 | sendNotification(disabler, AUTO_DISABLED_NOTIFICATION, null); 236 | } 237 | 238 | public void notifyLeaveChat(Player leaver, boolean notifyOthers) { 239 | @NullOr DefaultYamlValue others = (notifyOthers) ? LEFT_CHAT_NOTIFICATION_OTHERS : null; 240 | sendNotification(leaver, LEFT_CHAT_NOTIFICATION_SELF, others); 241 | } 242 | 243 | public void notifyLeavingChatIsDisabled(Player leaver) { 244 | sendNotification(leaver, LEFT_CHAT_DISABLED_ERROR, null); 245 | } 246 | 247 | public void notifyJoinChat(Player joiner, boolean notifyOthers) { 248 | @NullOr DefaultYamlValue others = (notifyOthers) ? JOIN_CHAT_NOTIFICATION_OTHERS : null; 249 | sendNotification(joiner, JOIN_CHAT_NOTIFICATION_SELF, others); 250 | } 251 | 252 | public void notifySoundsMuted(Player player) { 253 | sendNotification(player, MUTE_SOUNDS_NOTIFICATION, null); 254 | } 255 | 256 | public void notifySoundsUnmuted(Player player) { 257 | sendNotification(player, UNMUTE_SOUNDS_NOTIFICATION, null); 258 | } 259 | 260 | // 261 | // Unconfigurable notifications 262 | // 263 | 264 | public void notifyUpdateAvailable(Player manager, Version version) { 265 | sendNotification(manager, Strings.colorful( 266 | "&9DiscordSRV-&lStaff&9-&lChat&6 →&e Update available: &f" + 267 | version + " &6&o(" + plugin.version() + ")&r\n" + "&9&o&n" + Updater.RESOURCE_PAGE 268 | )); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/config/StaffChatConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.config; 24 | 25 | import com.github.zafarkhaja.semver.Version; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 27 | import com.rezzedup.discordsrv.staffchat.StaffChatProfile; 28 | import com.rezzedup.util.constants.Aggregates; 29 | import com.rezzedup.util.constants.annotations.AggregatedResult; 30 | import community.leaf.configvalues.bukkit.DefaultYamlValue; 31 | import community.leaf.configvalues.bukkit.YamlValue; 32 | import community.leaf.configvalues.bukkit.data.Load; 33 | import community.leaf.configvalues.bukkit.data.YamlDataFile; 34 | import community.leaf.configvalues.bukkit.migrations.Migration; 35 | import org.bukkit.Sound; 36 | import org.bukkit.entity.Player; 37 | 38 | import java.util.List; 39 | 40 | public class StaffChatConfig extends YamlDataFile { 41 | public static final YamlValue VERSION = 42 | YamlValue.of("meta.config-version", Configs.VERSION).maybe(); 43 | 44 | public static final DefaultYamlValue METRICS_ENABLED = 45 | YamlValue.ofBoolean("plugin.metrics") 46 | .migrates(Migration.move("metrics")) 47 | .defaults(true); 48 | 49 | public static final DefaultYamlValue UPDATE_CHECKER_ENABLED = 50 | YamlValue.ofBoolean("plugin.updates.check-for-updates").defaults(true); 51 | 52 | public static final DefaultYamlValue NOTIFY_IF_UPDATE_AVAILABLE = 53 | YamlValue.ofBoolean("plugin.updates.notify-in-game").defaults(true); 54 | 55 | public static final DefaultYamlValue PERSIST_TOGGLES = 56 | YamlValue.ofBoolean("staff-chat.toggles.chat-toggles-persist-after-restart").defaults(true); 57 | 58 | public static final DefaultYamlValue LEAVING_STAFFCHAT_ENABLED = 59 | YamlValue.ofBoolean("staff-chat.toggles.let-staff-members-leave-staffchat").defaults(true); 60 | 61 | public static final DefaultYamlValue NOTIFY_IF_TOGGLE_ENABLED = 62 | YamlValue.ofBoolean("staff-chat.toggles.notify-toggle-status-on-join") 63 | .migrates(Migration.move("notify-staff-chat-enabled-on-join")) 64 | .defaults(true); 65 | 66 | public static final DefaultYamlValue PREFIXED_CHAT_ENABLED = 67 | YamlValue.ofBoolean("staff-chat.prefixed.enable-prefixed-chat-messages") 68 | .migrates(Migration.move("enable-prefixed-chat-messages")) 69 | .defaults(false); 70 | 71 | public static final DefaultYamlValue PREFIXED_CHAT_IDENTIFIER = 72 | YamlValue.ofString("staff-chat.prefixed.prefixed-chat-identifier") 73 | .migrates(Migration.move("prefixed-chat-identifier")) 74 | .defaults("@"); 75 | 76 | // Message Sound 77 | 78 | public static final DefaultYamlValue MESSAGE_SOUND_ENABLED = 79 | YamlValue.ofBoolean("sounds.messages.enabled").defaults(true); 80 | 81 | public static final DefaultYamlValue MESSAGE_SOUND_NAME = 82 | YamlValue.ofSound("sounds.messages.name").defaults(Sound.ENTITY_ITEM_PICKUP); 83 | 84 | public static final DefaultYamlValue MESSAGE_SOUND_VOLUME = 85 | YamlValue.ofFloat("sounds.messages.volume").defaults(1.0F); 86 | 87 | public static final DefaultYamlValue MESSAGE_SOUND_PITCH = 88 | YamlValue.ofFloat("sounds.messages.pitch").defaults(0.5F); 89 | 90 | // Notification Sound 91 | 92 | public static final DefaultYamlValue NOTIFICATION_SOUND_ENABLED = 93 | YamlValue.ofBoolean("sounds.notifications.enabled").defaults(true); 94 | 95 | public static final DefaultYamlValue NOTIFICATION_SOUND_NAME = 96 | YamlValue.ofSound("sounds.notifications.name").defaults(Sound.ENTITY_ITEM_PICKUP); 97 | 98 | public static final DefaultYamlValue NOTIFICATION_SOUND_VOLUME = 99 | YamlValue.ofFloat("sounds.notifications.volume").defaults(1.0F); 100 | 101 | public static final DefaultYamlValue NOTIFICATION_SOUND_PITCH = 102 | YamlValue.ofFloat("sounds.notifications.pitch").defaults(0.75F); 103 | 104 | @AggregatedResult 105 | public static final List> VALUES = 106 | Aggregates.fromThisClass().constantsOfType(YamlValue.type()).toList(); 107 | 108 | private final StaffChatPlugin plugin; 109 | 110 | public StaffChatConfig(StaffChatPlugin plugin) { 111 | super(plugin.directory(), "staff-chat.config.yml", Load.LATER); 112 | this.plugin = plugin; 113 | 114 | reloadsWith(() -> 115 | { 116 | if (isInvalid()) { 117 | Configs.couldNotLoad(plugin.getLogger(), getFilePath()); 118 | plugin.debug(getClass()).log("Reload", () -> "Couldn't load: " + getInvalidReason()); 119 | return; 120 | } 121 | 122 | Version existing = get(VERSION).orElse(Configs.NO_VERSION); 123 | boolean isOutdated = existing.isLowerThan(plugin.version()); 124 | 125 | if (isOutdated) { 126 | plugin.debug(getClass()).log("Reload", () -> "Updating outdated config: " + existing); 127 | set(VERSION, plugin.version()); 128 | } 129 | 130 | headerFromResource("staff-chat.config.header.txt"); 131 | defaultValues(VALUES); 132 | 133 | if (isUpdated()) { 134 | plugin.debug(getClass()).log("Reload", () -> "Saving updated config and backing up old config: v" + existing); 135 | backupThenSave(plugin.backups(), "v" + existing); 136 | } 137 | }); 138 | } 139 | 140 | private void playSound( 141 | Player player, 142 | DefaultYamlValue enabled, 143 | DefaultYamlValue sound, 144 | DefaultYamlValue volume, 145 | DefaultYamlValue pitch 146 | ) { 147 | if (!getOrDefault(enabled)) { 148 | return; 149 | } 150 | 151 | boolean sounds = plugin.data().getProfile(player) 152 | .map(StaffChatProfile::receivesStaffChatSounds) 153 | .orElse(true); 154 | 155 | if (sounds) { 156 | player.playSound( 157 | player.getLocation().add(0, 0.5, 0), 158 | getOrDefault(sound), 159 | getOrDefault(volume), 160 | getOrDefault(pitch) 161 | ); 162 | } 163 | } 164 | 165 | public void playMessageSound(Player player) { 166 | playSound(player, MESSAGE_SOUND_ENABLED, MESSAGE_SOUND_NAME, MESSAGE_SOUND_VOLUME, MESSAGE_SOUND_PITCH); 167 | } 168 | 169 | public void playNotificationSound(Player player) { 170 | playSound(player, NOTIFICATION_SOUND_ENABLED, NOTIFICATION_SOUND_NAME, NOTIFICATION_SOUND_VOLUME, NOTIFICATION_SOUND_PITCH); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/config/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat.config; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/AutoStaffChatToggleEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatProfile; 26 | import org.bukkit.event.HandlerList; 27 | 28 | public class AutoStaffChatToggleEvent extends ProfileToggleEvent { 29 | public AutoStaffChatToggleEvent(StaffChatProfile profile, boolean toggleState) { 30 | super(profile, toggleState); 31 | } 32 | 33 | public boolean isEnablingAutomaticChat() { 34 | return getToggleState(); 35 | } 36 | 37 | // 38 | // - - - HandlerList boilerplate - - - 39 | // 40 | 41 | public static final HandlerList HANDLERS = new HandlerList(); 42 | 43 | @Override 44 | public HandlerList getHandlers() { 45 | return HANDLERS; 46 | } 47 | 48 | public static HandlerList getHandlerList() { 49 | return HANDLERS; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/ConsoleStaffChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.ChatService; 26 | import org.bukkit.Bukkit; 27 | import org.bukkit.command.ConsoleCommandSender; 28 | import org.bukkit.event.HandlerList; 29 | 30 | @SuppressWarnings("unused") 31 | public class ConsoleStaffChatMessageEvent extends StaffChatMessageEvent { 32 | public ConsoleStaffChatMessageEvent(String text) { 33 | super(Bukkit.getConsoleSender(), text, text); 34 | } 35 | 36 | @Override 37 | public final ChatService getSource() { 38 | return ChatService.MINECRAFT; 39 | } 40 | 41 | @Override 42 | public final ChatService getDestination() { 43 | return ChatService.DISCORD; 44 | } 45 | 46 | // 47 | // - - - HandlerList boilerplate - - - 48 | // 49 | 50 | public static final HandlerList HANDLERS = new HandlerList(); 51 | 52 | @Override 53 | public HandlerList getHandlers() { 54 | return HANDLERS; 55 | } 56 | 57 | public static HandlerList getHandlerList() { 58 | return HANDLERS; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/DiscordStaffChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.ChatService; 26 | import github.scarsz.discordsrv.dependencies.jda.api.entities.Message; 27 | import github.scarsz.discordsrv.dependencies.jda.api.entities.User; 28 | import org.bukkit.event.HandlerList; 29 | 30 | @SuppressWarnings("unused") 31 | public class DiscordStaffChatMessageEvent extends StaffChatMessageEvent { 32 | public DiscordStaffChatMessageEvent(User author, Message message, String text) { 33 | super(author, message, text); 34 | } 35 | 36 | @Override 37 | public final ChatService getSource() { 38 | return ChatService.DISCORD; 39 | } 40 | 41 | @Override 42 | public final ChatService getDestination() { 43 | return ChatService.MINECRAFT; 44 | } 45 | 46 | // 47 | // - - - HandlerList boilerplate - - - 48 | // 49 | 50 | public static final HandlerList HANDLERS = new HandlerList(); 51 | 52 | @Override 53 | public HandlerList getHandlers() { 54 | return HANDLERS; 55 | } 56 | 57 | public static HandlerList getHandlerList() { 58 | return HANDLERS; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/PlayerStaffChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.ChatService; 26 | import org.bukkit.entity.Player; 27 | import org.bukkit.event.HandlerList; 28 | 29 | @SuppressWarnings("unused") 30 | public class PlayerStaffChatMessageEvent extends StaffChatMessageEvent { 31 | public PlayerStaffChatMessageEvent(Player author, String text) { 32 | super(author, text, text); 33 | } 34 | 35 | @Override 36 | public final ChatService getSource() { 37 | return ChatService.MINECRAFT; 38 | } 39 | 40 | @Override 41 | public final ChatService getDestination() { 42 | return ChatService.DISCORD; 43 | } 44 | 45 | // 46 | // - - - HandlerList boilerplate - - - 47 | // 48 | 49 | public static final HandlerList HANDLERS = new HandlerList(); 50 | 51 | @Override 52 | public HandlerList getHandlers() { 53 | return HANDLERS; 54 | } 55 | 56 | public static HandlerList getHandlerList() { 57 | return HANDLERS; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/ProfileToggleEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatProfile; 26 | import org.bukkit.event.Cancellable; 27 | import org.bukkit.event.Event; 28 | 29 | import java.util.Objects; 30 | 31 | public abstract class ProfileToggleEvent extends Event implements Cancellable { 32 | private final StaffChatProfile profile; 33 | private final boolean toggleState; 34 | 35 | private boolean isQuiet = false; 36 | 37 | public ProfileToggleEvent(StaffChatProfile profile, boolean toggleState) { 38 | this.profile = Objects.requireNonNull(profile, "profile"); 39 | this.toggleState = toggleState; 40 | } 41 | 42 | public StaffChatProfile getProfile() { 43 | return profile; 44 | } 45 | 46 | public boolean getToggleState() { 47 | return toggleState; 48 | } 49 | 50 | public boolean isQuiet() { 51 | return isQuiet; 52 | } 53 | 54 | public void setQuiet(boolean quiet) { 55 | this.isQuiet = quiet; 56 | } 57 | 58 | // 59 | // - - - Cancellable boilerplate - - - 60 | // 61 | 62 | private boolean isCancelled = false; 63 | 64 | @Override 65 | public final boolean isCancelled() { 66 | return isCancelled; 67 | } 68 | 69 | @Override 70 | public final void setCancelled(boolean cancelled) { 71 | isCancelled = cancelled; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/ReceivingStaffChatToggleEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatProfile; 26 | import org.bukkit.event.HandlerList; 27 | 28 | public class ReceivingStaffChatToggleEvent extends ProfileToggleEvent { 29 | public ReceivingStaffChatToggleEvent(StaffChatProfile profile, boolean toggleState) { 30 | super(profile, toggleState); 31 | } 32 | 33 | public boolean isJoiningStaffChat() { 34 | return getToggleState(); 35 | } 36 | 37 | public boolean isLeavingStaffChat() { 38 | return !getToggleState(); 39 | } 40 | 41 | // 42 | // - - - HandlerList boilerplate - - - 43 | // 44 | 45 | public static final HandlerList HANDLERS = new HandlerList(); 46 | 47 | @Override 48 | public HandlerList getHandlers() { 49 | return HANDLERS; 50 | } 51 | 52 | public static HandlerList getHandlerList() { 53 | return HANDLERS; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/StaffChatMessageEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.events; 24 | 25 | import com.rezzedup.discordsrv.staffchat.ChatService; 26 | import org.bukkit.event.Cancellable; 27 | import org.bukkit.event.Event; 28 | 29 | import java.util.Objects; 30 | 31 | public abstract class StaffChatMessageEvent extends Event implements Cancellable { 32 | private final A author; 33 | private final M message; 34 | private String text; 35 | 36 | public StaffChatMessageEvent(A author, M message, String text) { 37 | this.author = Objects.requireNonNull(author, "author"); 38 | this.message = Objects.requireNonNull(message, "message"); 39 | this.text = Objects.requireNonNull(text, "text"); 40 | } 41 | 42 | public abstract ChatService getSource(); 43 | 44 | public abstract ChatService getDestination(); 45 | 46 | public final A getAuthor() { 47 | return author; 48 | } 49 | 50 | public final M getMessage() { 51 | return message; 52 | } 53 | 54 | public final String getText() { 55 | return text; 56 | } 57 | 58 | public final void setText(String text) { 59 | this.text = Objects.requireNonNull(text, "text"); 60 | } 61 | 62 | // 63 | // - - - Cancellable boilerplate - - - 64 | // 65 | 66 | private boolean isCancelled = false; 67 | 68 | @Override 69 | public final boolean isCancelled() { 70 | return isCancelled; 71 | } 72 | 73 | @Override 74 | public final void setCancelled(boolean cancelled) { 75 | isCancelled = cancelled; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/events/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat.events; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/DiscordSrvLoadedLaterListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.listeners; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import community.leaf.eventful.bukkit.annotations.EventListener; 27 | import org.bukkit.event.Listener; 28 | import org.bukkit.event.server.PluginEnableEvent; 29 | 30 | @SuppressWarnings("unused") 31 | public class DiscordSrvLoadedLaterListener implements Listener { 32 | private final StaffChatPlugin plugin; 33 | 34 | public DiscordSrvLoadedLaterListener(StaffChatPlugin plugin) { 35 | this.plugin = plugin; 36 | } 37 | 38 | @EventListener 39 | public void onPluginEnable(PluginEnableEvent event) { 40 | if (StaffChatPlugin.DISCORDSRV.equals(event.getPlugin().getName())) { 41 | plugin.debug(getClass()).log(event, () -> "DiscordSRV loaded late: " + event.getPlugin()); 42 | plugin.debugger().schedulePluginStatus(getClass(), "Loaded Late"); 43 | plugin.subscribeToDiscordSrv(event.getPlugin()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/DiscordStaffChatListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.listeners; 24 | 25 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 26 | import github.scarsz.discordsrv.api.Subscribe; 27 | import github.scarsz.discordsrv.api.events.DiscordGuildMessagePreProcessEvent; 28 | 29 | @SuppressWarnings("unused") 30 | public class DiscordStaffChatListener { 31 | private final StaffChatPlugin plugin; 32 | 33 | public DiscordStaffChatListener(StaffChatPlugin plugin) { 34 | this.plugin = plugin; 35 | } 36 | 37 | @Subscribe 38 | public void onDiscordChat(DiscordGuildMessagePreProcessEvent event) { 39 | if (event.getChannel().equals(plugin.getDiscordChannelOrNull())) { 40 | event.setCancelled(true); // Cancel this message from getting sent to global chat. 41 | 42 | // Handle this on the main thread next tick. 43 | plugin.sync().run(() -> plugin.submitMessageFromDiscord(event.getAuthor(), event.getMessage())); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/JoinNotificationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.listeners; 24 | 25 | import com.rezzedup.discordsrv.staffchat.Permissions; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 27 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 28 | import community.leaf.eventful.bukkit.ListenerOrder; 29 | import community.leaf.eventful.bukkit.annotations.EventListener; 30 | import org.bukkit.entity.Player; 31 | import org.bukkit.event.Listener; 32 | import org.bukkit.event.player.PlayerJoinEvent; 33 | import org.bukkit.event.player.PlayerQuitEvent; 34 | 35 | import java.util.ArrayDeque; 36 | import java.util.Deque; 37 | 38 | public class JoinNotificationListener implements Listener { 39 | private final StaffChatPlugin plugin; 40 | 41 | public JoinNotificationListener(StaffChatPlugin plugin) { 42 | this.plugin = plugin; 43 | } 44 | 45 | @EventListener(ListenerOrder.EARLY) 46 | public void onPlayerJoin(PlayerJoinEvent event) { 47 | Player player = event.getPlayer(); 48 | plugin.data().updateProfile(player); 49 | 50 | Deque reminders = new ArrayDeque<>(); 51 | 52 | if (plugin.config().getOrDefault(StaffChatConfig.NOTIFY_IF_TOGGLE_ENABLED)) { 53 | if (Permissions.ACCESS.allows(player)) { 54 | if (plugin.data().isAutomaticStaffChatEnabled(player)) { 55 | plugin.debug(getClass()).log(event, () -> 56 | "Player " + event.getPlayer().getName() + " joined: " + 57 | "reminding them that they have automatic staff-chat enabled" 58 | ); 59 | 60 | reminders.add(() -> plugin.messages().notifyAutoChatEnabled(player)); 61 | } 62 | 63 | if (!plugin.data().isReceivingStaffChatMessages(player)) { 64 | plugin.debug(getClass()).log(event, () -> 65 | "Player " + event.getPlayer().getName() + " joined: " + 66 | "reminding them that they previously left the staff-chat" 67 | ); 68 | 69 | reminders.add(() -> plugin.messages().notifyLeaveChat(player, false)); 70 | } 71 | } 72 | } 73 | 74 | if (plugin.config().getOrDefault(StaffChatConfig.NOTIFY_IF_UPDATE_AVAILABLE)) { 75 | if (Permissions.MANAGE.allows(player)) { 76 | plugin.updater().latestUpdateVersion().ifPresent(version -> 77 | { 78 | plugin.debug(getClass()).log(event, () -> 79 | "Player " + event.getPlayer().getName() + " joined: " + 80 | "notifying them that a new update is available (" + version + ")" 81 | ); 82 | 83 | reminders.add(() -> plugin.messages().notifyUpdateAvailable(player, version)); 84 | }); 85 | } 86 | } 87 | 88 | if (reminders.isEmpty()) { 89 | return; 90 | } 91 | 92 | plugin.sync().delay(10).ticks().every(10).ticks().run(task -> 93 | { 94 | if (reminders.isEmpty()) { 95 | task.cancel(); 96 | } else { 97 | reminders.pop().run(); 98 | } 99 | }); 100 | } 101 | 102 | @EventListener(ListenerOrder.EARLY) 103 | public void onPlayerQuit(PlayerQuitEvent event) { 104 | // Might as well update the profile (cleanup) 105 | plugin.data().updateProfile(event.getPlayer()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/PlayerPrefixedMessageListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.listeners; 24 | 25 | import com.rezzedup.discordsrv.staffchat.Permissions; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 27 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 28 | import com.rezzedup.discordsrv.staffchat.util.Strings; 29 | import community.leaf.eventful.bukkit.CancellationPolicy; 30 | import community.leaf.eventful.bukkit.ListenerOrder; 31 | import community.leaf.eventful.bukkit.annotations.CancelledEvents; 32 | import community.leaf.eventful.bukkit.annotations.EventListener; 33 | import org.bukkit.entity.Player; 34 | import org.bukkit.event.Listener; 35 | import org.bukkit.event.player.AsyncPlayerChatEvent; 36 | 37 | @SuppressWarnings("unused") 38 | public class PlayerPrefixedMessageListener implements Listener { 39 | private final StaffChatPlugin plugin; 40 | 41 | public PlayerPrefixedMessageListener(StaffChatPlugin plugin) { 42 | this.plugin = plugin; 43 | } 44 | 45 | @EventListener(ListenerOrder.EARLY) 46 | @CancelledEvents(CancellationPolicy.REJECT) 47 | public void onPrefixedChatEarly(AsyncPlayerChatEvent event) { 48 | if (!plugin.config().getOrDefault(StaffChatConfig.PREFIXED_CHAT_ENABLED)) { 49 | return; 50 | } 51 | 52 | Player player = event.getPlayer(); 53 | if (Permissions.ACCESS.denies(player)) { 54 | return; 55 | } 56 | 57 | String identifier = plugin.config().getOrDefault(StaffChatConfig.PREFIXED_CHAT_IDENTIFIER); 58 | if (Strings.isEmptyOrNull(identifier)) { 59 | plugin.debug(getClass()).log(event, () -> "Early Listener: Prefixed chat is enabled but identifier is undefined"); 60 | return; 61 | } 62 | 63 | String message = event.getMessage(); 64 | if (!message.startsWith(identifier)) { 65 | return; 66 | } 67 | 68 | plugin.debug(getClass()).log(event, () -> 69 | "Early Listener: Detected prefixed chat from player(" + player.getName() + "): message(\"" + message + "\")" 70 | ); 71 | 72 | event.setCancelled(true); // Cancel this message from getting sent to global chat. 73 | // Handle message in a later listener order, allowing other plugins to modify the message. 74 | } 75 | 76 | @EventListener(ListenerOrder.MONITOR) 77 | public void onPrefixedChatMonitor(AsyncPlayerChatEvent event) { 78 | // Event should already be cancelled in the early listener. 79 | if (!event.isCancelled()) { 80 | return; 81 | } 82 | 83 | if (!plugin.config().getOrDefault(StaffChatConfig.PREFIXED_CHAT_ENABLED)) { 84 | return; 85 | } 86 | 87 | Player player = event.getPlayer(); 88 | if (Permissions.ACCESS.denies(player)) { 89 | return; 90 | } 91 | 92 | String identifier = plugin.config().getOrDefault(StaffChatConfig.PREFIXED_CHAT_IDENTIFIER); 93 | if (Strings.isEmptyOrNull(identifier)) { 94 | plugin.debug(getClass()).log(event, () -> "Monitor Listener: Prefixed chat is enabled but identifier is undefined"); 95 | return; 96 | } 97 | 98 | String message = event.getMessage(); 99 | if (!message.startsWith(identifier)) { 100 | return; 101 | } 102 | 103 | String unprefixed = message.substring(identifier.length()).trim(); 104 | String submission = (Strings.isEmptyOrNull(unprefixed)) ? message : unprefixed; 105 | 106 | plugin.debug(getClass()).log(event, () -> 107 | "Monitor Listener: Sending prefixed chat from player(" + player.getName() + ") identified " + 108 | "by prefix(\"" + identifier + "\"): message(\"" + submission + "\")" 109 | ); 110 | 111 | // Handle this on the main thread next tick. 112 | plugin.sync().run(() -> plugin.submitMessageFromPlayer(player, submission)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/PlayerStaffChatToggleListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.listeners; 24 | 25 | import com.rezzedup.discordsrv.staffchat.Permissions; 26 | import com.rezzedup.discordsrv.staffchat.StaffChatPlugin; 27 | import com.rezzedup.discordsrv.staffchat.config.StaffChatConfig; 28 | import com.rezzedup.discordsrv.staffchat.events.AutoStaffChatToggleEvent; 29 | import com.rezzedup.discordsrv.staffchat.events.ReceivingStaffChatToggleEvent; 30 | import community.leaf.eventful.bukkit.CancellationPolicy; 31 | import community.leaf.eventful.bukkit.ListenerOrder; 32 | import community.leaf.eventful.bukkit.annotations.CancelledEvents; 33 | import community.leaf.eventful.bukkit.annotations.EventListener; 34 | import org.bukkit.entity.Player; 35 | import org.bukkit.event.Listener; 36 | import org.bukkit.event.player.AsyncPlayerChatEvent; 37 | import pl.tlinkowski.annotation.basic.NullOr; 38 | 39 | @SuppressWarnings("unused") 40 | public class PlayerStaffChatToggleListener implements Listener { 41 | private final StaffChatPlugin plugin; 42 | 43 | public PlayerStaffChatToggleListener(StaffChatPlugin plugin) { 44 | this.plugin = plugin; 45 | } 46 | 47 | @EventListener(ListenerOrder.FIRST) 48 | public void onAutomaticChatFirst(AsyncPlayerChatEvent event) { 49 | if (plugin.data().isAutomaticStaffChatEnabled(event.getPlayer())) { 50 | event.setCancelled(true); // Cancel this message from getting sent to global chat. 51 | // Handle message in a later listener order, allowing other plugins to modify the message. 52 | } 53 | } 54 | 55 | @EventListener(ListenerOrder.MONITOR) 56 | public void onAutomaticChatMonitor(AsyncPlayerChatEvent event) { 57 | Player player = event.getPlayer(); 58 | if (!plugin.data().isAutomaticStaffChatEnabled(player)) { 59 | return; 60 | } 61 | 62 | event.setCancelled(true); // Cancel this message from getting sent to global chat. 63 | // The event could've been uncancelled since cancelling it the first time. 64 | 65 | if (Permissions.ACCESS.allows(player)) { 66 | plugin.debug(getClass()).log(event, () -> 67 | "Player " + player.getName() + " has automatic staff-chat enabled" 68 | ); 69 | 70 | // Handle this on the main thread next tick. 71 | plugin.sync().run(() -> plugin.submitMessageFromPlayer(event.getPlayer(), event.getMessage())); 72 | } else { 73 | plugin.debug(getClass()).log(event, () -> 74 | "Player " + player.getName() + " has automatic staff-chat enabled " + 75 | "but they don't have permission to use the staff chat" 76 | ); 77 | 78 | // Remove this non-staff profile (but in sync 'cus it calls an event). 79 | plugin.sync().run(() -> { 80 | plugin.data().updateProfile(player); 81 | player.chat(event.getMessage()); 82 | }); 83 | } 84 | } 85 | 86 | @EventListener(ListenerOrder.LAST) 87 | @CancelledEvents(CancellationPolicy.REJECT) 88 | public void onToggleAutoChat(AutoStaffChatToggleEvent event) { 89 | @NullOr Player player = event.getProfile().toPlayer().orElse(null); 90 | 91 | plugin.debug(getClass()).log(event, () -> { 92 | String name = (player == null) ? "" : player.getName(); 93 | String enabled = (event.isEnablingAutomaticChat()) ? "Enabled" : "Disabled"; 94 | return enabled + " automatic staff-chat for player: " + name + " (" + event.getProfile().uuid() + ")"; 95 | }); 96 | 97 | if (player == null || event.isQuiet()) { 98 | return; 99 | } 100 | 101 | if (event.isEnablingAutomaticChat()) { 102 | plugin.messages().notifyAutoChatEnabled(player); 103 | } else { 104 | plugin.messages().notifyAutoChatDisabled(player); 105 | } 106 | } 107 | 108 | @EventListener(ListenerOrder.EARLY) 109 | @CancelledEvents(CancellationPolicy.REJECT) 110 | public void onLeavingStaffChatIsDisabled(ReceivingStaffChatToggleEvent event) { 111 | if (event.isJoiningStaffChat()) { 112 | return; 113 | } 114 | if (plugin.config().getOrDefault(StaffChatConfig.LEAVING_STAFFCHAT_ENABLED)) { 115 | return; 116 | } 117 | 118 | // Leaving is disabled, cancel the event. 119 | event.setCancelled(true); 120 | 121 | @NullOr Player player = event.getProfile().toPlayer().orElse(null); 122 | 123 | plugin.debug(getClass()).log(event, () -> { 124 | String name = (player == null) ? "" : player.getName(); 125 | return "Player: " + name + " (" + event.getProfile().uuid() + ") " + 126 | "tried to leave the staff chat, but leaving is disabled in the config"; 127 | }); 128 | 129 | if (player == null || event.isQuiet()) { 130 | return; 131 | } 132 | 133 | plugin.messages().notifyLeavingChatIsDisabled(player); 134 | } 135 | 136 | @EventListener(ListenerOrder.LAST) 137 | @CancelledEvents(CancellationPolicy.REJECT) 138 | public void onToggleReceivingMessages(ReceivingStaffChatToggleEvent event) { 139 | @NullOr Player player = event.getProfile().toPlayer().orElse(null); 140 | 141 | plugin.debug(getClass()).log(event, () -> { 142 | String name = (player == null) ? "" : player.getName(); 143 | String left = (event.isLeavingStaffChat()) ? "left" : "joined"; 144 | return "Player: " + name + " (" + event.getProfile().uuid() + ") " + left + " the staff-chat"; 145 | }); 146 | 147 | if (player == null || event.isQuiet()) { 148 | return; 149 | } 150 | 151 | boolean broadcastToEveryone = 152 | event.getProfile().sinceLeftStaffChat().isPresent() != event.isLeavingStaffChat(); 153 | 154 | if (event.isLeavingStaffChat()) { 155 | plugin.messages().notifyLeaveChat(player, broadcastToEveryone); 156 | } else { 157 | plugin.messages().notifyJoinChat(player, broadcastToEveryone); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/listeners/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat.listeners; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/util/FileIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.util; 24 | 25 | import java.io.IOException; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.time.LocalDate; 29 | import java.util.function.Consumer; 30 | 31 | public class FileIO { 32 | private FileIO() { 33 | throw new UnsupportedOperationException(); 34 | } 35 | 36 | public static void write(Path filePath, String contents, Consumer exceptions) { 37 | try { 38 | Files.writeString(filePath, contents); 39 | } catch (IOException e) { 40 | exceptions.accept(e); 41 | } 42 | } 43 | 44 | public static void backup(Path existingFilePath, Path backupFilePath, Consumer exceptions) { 45 | // Nothing to back up. 46 | if (!Files.isRegularFile(existingFilePath)) { 47 | return; 48 | } 49 | 50 | try { 51 | Path backupDirectory = backupFilePath.getParent(); 52 | if (!Files.isDirectory(backupDirectory)) { 53 | Files.createDirectories(backupDirectory); 54 | } 55 | 56 | String backupFileName = backupFilePath.getFileName().toString(); 57 | int lastDot = backupFileName.lastIndexOf('.'); 58 | String name = (lastDot > 0) ? backupFileName.substring(0, lastDot) : ""; 59 | String extension = (lastDot > 0) ? backupFileName.substring(lastDot) : ""; 60 | 61 | for (int i = 1; ; i++) { 62 | String attemptedName = name + ".backup_" + LocalDate.now() + "_" + i + extension; 63 | Path attemptedBackupFilePath = backupDirectory.resolve(attemptedName); 64 | 65 | if (Files.isRegularFile(attemptedBackupFilePath)) { 66 | continue; 67 | } 68 | 69 | Files.move(existingFilePath, attemptedBackupFilePath); 70 | return; 71 | } 72 | } catch (IOException e) { 73 | exceptions.accept(e); 74 | } 75 | } 76 | 77 | public static void backup(Path existingFilePath, Path backupFilePath) { 78 | backup(existingFilePath, backupFilePath, e -> { 79 | throw new RuntimeException(e); 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/util/MappedPlaceholder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.util; 24 | 25 | import pl.tlinkowski.annotation.basic.NullOr; 26 | 27 | import java.util.HashMap; 28 | import java.util.Locale; 29 | import java.util.Map; 30 | import java.util.Objects; 31 | import java.util.function.Supplier; 32 | import java.util.regex.Pattern; 33 | 34 | public class MappedPlaceholder { 35 | public static Pattern PATTERN = Pattern.compile("%(.+?)%"); 36 | 37 | protected final Map> placeholders = new HashMap<>(); 38 | 39 | public String get(@NullOr String placeholder) { 40 | if (Strings.isEmptyOrNull(placeholder)) { 41 | return ""; 42 | } 43 | 44 | Supplier supplier = placeholders.get(placeholder.toLowerCase(Locale.ROOT)); 45 | if (supplier == null) { 46 | return ""; 47 | } 48 | 49 | @NullOr Object result = null; 50 | try { 51 | result = supplier.get(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | 56 | return (result == null) ? "" : String.valueOf(result); 57 | } 58 | 59 | private static String escape(String literal) { 60 | return literal.replace("\\", "\\\\").replace("$", "\\$"); 61 | } 62 | 63 | public String update(String message) { 64 | return PATTERN.matcher(message).replaceAll(mr -> { 65 | String value = get(mr.group(1)); 66 | return escape((value.isEmpty()) ? mr.group() : value); 67 | }); 68 | } 69 | 70 | public Putter map(String... placeholders) { 71 | Objects.requireNonNull(placeholders, "placeholders"); 72 | if (placeholders.length <= 0) { 73 | throw new IllegalArgumentException("Empty placeholders array"); 74 | } 75 | return new Putter(placeholders); 76 | } 77 | 78 | public void inherit(MappedPlaceholder from) { 79 | placeholders.putAll(from.placeholders); 80 | } 81 | 82 | public class Putter { 83 | private final String[] aliases; 84 | 85 | private Putter(String[] aliases) { 86 | this.aliases = aliases; 87 | } 88 | 89 | public void to(Supplier supplier) { 90 | Objects.requireNonNull(supplier, "supplier"); 91 | 92 | for (String alias : aliases) { 93 | if (Strings.isEmptyOrNull(alias)) { 94 | continue; 95 | } 96 | placeholders.put(alias.toLowerCase(Locale.ROOT), supplier); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/util/Strings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.rezzedup.discordsrv.staffchat.util; 24 | 25 | import net.md_5.bungee.api.ChatColor; 26 | import pl.tlinkowski.annotation.basic.NullOr; 27 | 28 | import java.util.HashSet; 29 | import java.util.Set; 30 | import java.util.regex.Matcher; 31 | import java.util.regex.Pattern; 32 | 33 | public class Strings { 34 | private Strings() { 35 | } 36 | 37 | private static final Pattern HASH_HEX_COLOR_PATTERN = Pattern.compile("(?i)&x?#(?[a-f0-9]{6})"); 38 | 39 | public static String colorful(@NullOr String text) { 40 | if (isEmptyOrNull(text)) { 41 | return ""; 42 | } 43 | 44 | Matcher matcher = HASH_HEX_COLOR_PATTERN.matcher(text); 45 | @NullOr Set replaced = null; 46 | 47 | while (matcher.find()) { 48 | if (replaced == null) { 49 | replaced = new HashSet<>(); 50 | } 51 | 52 | String match = matcher.group(); 53 | if (replaced.contains(match)) { 54 | continue; 55 | } 56 | 57 | StringBuilder bungeeHexFormat = new StringBuilder("&x"); 58 | 59 | for (char c : matcher.group("hex").toCharArray()) { 60 | bungeeHexFormat.append('&').append(c); 61 | } 62 | 63 | text = text.replace(match, bungeeHexFormat.toString()); 64 | replaced.add(match); 65 | } 66 | 67 | return ChatColor.translateAlternateColorCodes('&', text); 68 | } 69 | 70 | public static boolean isEmptyOrNull(@NullOr String text) { 71 | return text == null || text.isEmpty(); 72 | } 73 | 74 | public static String orEmpty(@NullOr String text) { 75 | return (isEmptyOrNull(text)) ? "" : text; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/rezzedup/discordsrv/staffchat/util/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright © 2017-2024 RezzedUp and Contributors 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | @NonNullPackage 24 | package com.rezzedup.discordsrv.staffchat.util; 25 | 26 | import pl.tlinkowski.annotation.basic.NonNullPackage; 27 | -------------------------------------------------------------------------------- /src/main/resources/messages.config.header.txt: -------------------------------------------------------------------------------- 1 | 2 | ${project.name}: Messages Config 3 | 4 | --- 5 | 6 | Supported hex color code formats: &x&R&R&G&G&B&B and &#RRGGBB 7 | 8 | Player placeholders: 9 | %name%: player's name 10 | %nickname%: player's nickname, if they have one (falls back to regular name) 11 | 12 | Discord placeholders: 13 | %name%: discord user's name 14 | %discriminator%: discord user's discriminator (#0000) 15 | %nickname%: discord user's nickname if they have one (falls back to regular name) 16 | %toprole%: discord user's topmost role 17 | %toproleinitial%: the first character of the discord user's topmost role 18 | %toprolealias%: alias for the discord user's topmost role as defined in DiscordSRV's config 19 | %toprolecolor%: an RGB color code derived from the discord user's topmost role 20 | %allroles%: all the discord user's roles 21 | 22 | You may also use placeholders from PlaceholderAPI if it is installed. 23 | 24 | --- 25 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ${project.name} 2 | version: ${project.version} 3 | 4 | author: RezzedUp 5 | description: Staff chat plugin that hooks into DiscordSRV. 6 | website: https://modrinth.com/plugin/discordsrv-staff-chat 7 | 8 | main: com.rezzedup.discordsrv.staffchat.StaffChatPlugin 9 | api-version: 1.13 10 | load: POSTWORLD 11 | softdepend: [ DiscordSRV, PlaceholderAPI ] 12 | 13 | commands: 14 | staffchat: 15 | aliases: [ adminchat, schat, achat, sc, ac, a ] 16 | description: Toggle or send a message to staff chat. 17 | permission: staffchat.access 18 | usage: |- 19 | / - toggle automatic staff chat 20 | / - send a message to the staff chat 21 | managestaffchat: 22 | aliases: [ discordsrv-staff-chat, discordsrvstaffchat, discordstaffchat, discordadminchat, manageadminchat ] 23 | description: Manage and get information about DiscordSRV-Staff-Chat 24 | permission: staffchat.manage 25 | usage: / 26 | leavestaffchat: 27 | aliases: [ leaveadminchat ] 28 | description: Leave the staff chat (stop receiving messages). 29 | permission: staffchat.access 30 | usage: / 31 | joinstaffchat: 32 | aliases: [ joinadminchat ] 33 | description: Rejoin the staff chat (receive messages again). 34 | permission: staffchat.access 35 | usage: / 36 | togglestaffchatsounds: 37 | aliases: [ toggleadminchatsounds ] 38 | description: Mute or unmute staff chat sounds for yourself. 39 | permission: staffchat.access 40 | usage: / 41 | 42 | permissions: 43 | staffchat.*: 44 | children: 45 | staffchat.manage: true 46 | staffchat.access: true 47 | staffchat.manage: 48 | description: Manage the staff chat plugin in-game (e.g. reloading) 49 | staffchat.access: 50 | descriptions: Send and receive messages via staff chat 51 | -------------------------------------------------------------------------------- /src/main/resources/staff-chat.config.header.txt: -------------------------------------------------------------------------------- 1 | 2 | ${project.name} v${project.version} by RezzedUp 3 | 4 | --- 5 | 6 | This plugin *requires* DiscordSRV in order to access Discord. 7 | However, it will work just fine in-game regardless if DiscordSRV is installed or not. 8 | 9 | --- 10 | 11 | Installation (Discord Setup): 12 | 13 | - 1) Install this plugin and DiscordSRV 14 | Get DiscordSRV here: https://modrinth.com/plugin/discordsrv 15 | 16 | - 2) Add a "staff-chat" channel to DiscordSRV's config 17 | (in: /plugins/DiscordSRV/config.yml) 18 | 19 | EXAMPLE: 20 | EXAMPLE: # Channel links from game to Discord 21 | EXAMPLE: # Replace the "00000000000" below with your staff channel's ID 22 | EXAMPLE: Channels: {"global": "00000000000", "staff-chat": "00000000000"} 23 | EXAMPLE: # ^ ^ 24 | EXAMPLE: # All staff chat messages will be sent to this channel. 25 | EXAMPLE: 26 | 27 | IMPORTANT NOTE: 28 | The channel's name can be anything on Discord (since DiscordSRV uses its channel ID) ... 29 | However, it MUST be called "staff-chat" in the config. 30 | **This is very important!** 31 | 32 | - 3) Run: `/discord reload` or restart the server 33 | 34 | - 4) Give staff ranks permission to use the staff chat: `staffchat.access` 35 | 36 | --- 37 | 38 | This config can be reloaded in-game with: `/managestaffchat reload` 39 | (Be sure to give yourself permission to manage the staff chat with: `staffchat.manage`) 40 | 41 | --- 42 | 43 | Find available sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 44 | --------------------------------------------------------------------------------