├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── compiler.xml ├── dictionaries │ ├── Arasp.xml │ └── sky.xml ├── encodings.xml ├── gradle.xml ├── hotswap_agent.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── libraries │ ├── Gradle__io_izzel_taboolib_TabooLib_all_5_17.xml │ ├── Gradle__io_izzel_taboolib_loader_TabooLibloader_all_1_4.xml │ ├── Gradle__me_clip_placeholderapi_2_10_4.xml │ ├── Gradle__org_bstats_bstats_bukkit_1_7.xml │ └── Gradle__org_spigotmc_spigot_1_15_2_R0_1_SNAPSHOT.xml ├── misc.xml ├── modules.xml ├── modules │ ├── TrChat.iml │ ├── TrChat.main.iml │ └── TrChat.test.iml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── UPDATES_CN.md ├── UPDATES_EN.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs ├── BungeeCord.jar └── PlaceholderAPI.jar ├── settings.gradle └── src └── main ├── java └── me │ └── arasple │ └── mc │ └── trchat │ ├── TrChat.java │ ├── TrChatBungee.java │ ├── TrChatFiles.java │ ├── TrChatLoader.java │ ├── api │ └── TrChatAPI.java │ ├── bstats │ ├── Metrics.java │ ├── MetricsBukkit.java │ └── MetricsBungee.java │ ├── bungee │ └── ListenerBungeeTransfer.java │ ├── channels │ ├── ChannelGlobal.java │ ├── ChannelPrivate.java │ └── ChannelStaff.java │ ├── chat │ ├── ChatFormats.java │ ├── format │ │ ├── Format.java │ │ ├── PriFormat.java │ │ └── objects │ │ │ ├── JsonComponent.java │ │ │ └── MsgComponent.java │ ├── listeners │ │ ├── ListenerAnvilChange.java │ │ ├── ListenerBookEdit.java │ │ ├── ListenerChatEvent.java │ │ ├── ListenerCommandController.java │ │ ├── ListenerSignChange.java │ │ ├── ListenerTabComplete.java │ │ └── ListenerTrChatInfo.java │ └── obj │ │ └── ChatType.java │ ├── cmds │ ├── CommandFilter.java │ ├── CommandGlobalShout.java │ ├── CommandPrivateMessage.java │ ├── CommandReply.java │ └── CommandStaffChat.java │ ├── data │ ├── Cooldowns.java │ └── Users.java │ ├── filter │ ├── ChatFilter.java │ ├── listeners │ │ └── PacketListener.java │ └── processer │ │ ├── BCConvert.java │ │ ├── Filter.java │ │ ├── FilterSet.java │ │ ├── FilteredObject.java │ │ └── WordNode.java │ ├── func │ ├── ChatFunctions.java │ └── imp │ │ └── Function.java │ ├── hook │ └── TrChatPlaceholders.java │ ├── logs │ └── ChatLogs.java │ ├── menus │ └── MenuFilterControl.java │ ├── nms │ ├── AbstractPacketUtils.java │ └── InternalPacketUtils.java │ ├── updater │ └── Updater.java │ └── utils │ ├── Bungees.java │ ├── Js.java │ ├── MessageColors.java │ ├── Notifys.java │ ├── PacketUtils.java │ ├── Players.java │ ├── Pts.java │ └── Vars.java └── resources ├── bungee.yml ├── channels.yml ├── filter.yml ├── formats.yml ├── function.yml ├── lang ├── en_US.yml ├── zh_CN.yml └── zh_TW.yml ├── plugin.yml └── settings.yml /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Default ignored files 3 | /workspace.xml 4 | /.gradle 5 | /.idea 6 | /gradle 7 | /libs -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/dictionaries/Arasp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arasple 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/dictionaries/sky.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cooldown 5 | cooldowns 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/hotswap_agent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 115 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | 59 | 60 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__io_izzel_taboolib_TabooLib_all_5_17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__io_izzel_taboolib_loader_TabooLibloader_all_1_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__me_clip_placeholderapi_2_10_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__org_bstats_bstats_bukkit_1_7.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__org_spigotmc_spigot_1_15_2_R0_1_SNAPSHOT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Class structureJava 23 | 24 | 25 | Code maturityJava 26 | 27 | 28 | Google Web Toolkit 29 | 30 | 31 | JUnitJava 32 | 33 | 34 | Java 35 | 36 | 37 | Java 5Java language level migration aidsJava 38 | 39 | 40 | Java 7Java language level migration aidsJava 41 | 42 | 43 | Java 8Java language level migration aidsJava 44 | 45 | 46 | Java language level migration aidsJava 47 | 48 | 49 | JavadocJava 50 | 51 | 52 | Numeric issuesJava 53 | 54 | 55 | PerformanceJava 56 | 57 | 58 | PortabilityJava 59 | 60 | 61 | Probable bugsJava 62 | 63 | 64 | Resource managementJava 65 | 66 | 67 | Spring 68 | 69 | 70 | Spring AOPSpring 71 | 72 | 73 | TestNGJava 74 | 75 | 76 | Threading issuesJava 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules/TrChat.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | BUNGEECORD 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /.idea/modules/TrChat.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | BUNGEECORD 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.idea/modules/TrChat.test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrChat 2 | Advanced chat control for Minecraft servers 3 | -------------------------------------------------------------------------------- /UPDATES_CN.md: -------------------------------------------------------------------------------- 1 | # TrChat Update Logs # 2 | 3 | #### VERSION 1.8 4 | ``` 5 | - 这将会是 TrChat 的最后一个版本, 6 | - 无新增内容, 主要是修复一些小问题和优化 7 | 8 | - 注意, 1.13+ 的命令补全绕过权限是: trchat.bypass.tabcomplete 9 | ``` 10 | 11 | #### VERSION 1.7 12 | - #### 1.70 13 | - Since: 2020.1.15 14 | - Updates: 15 | - 新增 en_US 语言,默认 16 | - 更新 TabooLib 至 5.14 17 | - 相关配置文件的默认注释更改为英文 18 | - 发布至 SpigotMC 19 | - 修复了 v1.62 中的空指针报错 20 | - 默认情况下关闭云端词库 21 | - R2 22 | - 优化了英文语言下物品名称获取方式 23 | - 新增 Command Controller 命令控制系统, 黑白名单任选 24 | - ^ 支持正则表达式匹配、自定义参数是否忽略、绕过权限以及 TLocale 反馈消息 25 | - ^ (请参考新版本,手动添加新的配置项) 26 | - R3 27 | - CommandController 将自动检测别称命令的主命令判断 28 | - 修复了 Function 模块的 {0} 变量无法在 text 中使用 29 | #### VERSION 1.6 30 | - ##### 1.62 31 | - DATE: 2019.12.1 32 | - OVERVIEW: 33 | - 修复了 Bungee 版不工作 34 | - 现在单个 JSON 组件也支持使用 "Requirement" 属性 35 | - 移除调试信息 36 | - 强制全局喊话现在将记录在后台 37 | - 修复了跨服私聊变量报错 38 | - 修复同服 At 重复的问题 39 | - 现支持在全局喊话中 At 玩家 40 | - 过滤器菜单别称新增 /chatfilter, /trfilter 41 | - 修复了云端词库 连不上/不显示 的问题 42 | - 修复了聊天日志不能追加写入的问题 43 | - 新增语言项支持自定义监听聊天的格式 44 | - 支持 1.15 版本 45 | - 修复了一个数据包监听报错 46 | - 新增强制全局喊话的前缀 47 | - 修复了 PlaceholderAPI 下载链接失效 48 | - 修复了铁砧敏感词库无法过滤的问题 49 | - ##### 1.61 50 | - DATE: 2019.11.30 51 | - OVERVIEW: 52 | - 修正裁剪了敏感词库 53 | - 修复了监听格式错误 54 | - 修复了 JSON 模块悬浮信息末尾少了字符 55 | - 新增手动重载方式,聊天框内喊:"#TRCHAT-RELOAD" 56 | - ##### 1.6 :) 57 | - DATE: 2019.11.30 58 | - OVERVIEW: 59 | - 该版本重制了许多的内容,重构了大量模块 60 | - 为了顺利更新到新版本,你需要删除旧文件夹 61 | - 敬请享用 :) 62 | - LOG: 63 | - 优化了更新检测器 64 | - 新增检测提醒用户删除旧版本文件夹 65 | - 优化、重写了大量代码 66 | - 配置文件结构革新 67 | - PlaceholderAPI 为必需前置,若未安装将自动下载重启 68 | ![](https://i.loli.net/2019/11/30/M7QuCip2jZUNkGd.png) 69 | - 功能性: 70 | - 聊天日志,记录存储详尽的内容 71 | - 改进聊天控制系统 72 | - 新增反复读 73 | - 匹配文本相似度 74 | - 自定义 TLocale 提示 75 | - 过滤器: 76 | - 云端词库新增大量内容, 1.2w+ 77 | - 云端词库新增阿里云镜像,双重下载源保障 78 | - 监听多种数据包处理过滤 79 | - 每名玩家独立的开关配置 80 | - 可视化 GUI 控制过滤器开关 81 | ![](https://i.loli.net/2019/11/30/wxynG2okcFpLj4l.png) 82 | - 聊天格式: 83 | - 优先级筛选支持使用 JS 表达式 84 | - 聊天频道: 85 | - 现在支持强制全局聊天 86 | - 聊天功能: 87 | - 新增 URL展示,将链接缩短为一个自定义格式的 JSON 模块 88 | - 新增 B站分享,将av号视频缩短为一个超链接 JSON 模块 89 | - 新增 手机号/身份证 屏蔽隐藏为 JSON 模块 90 | - 新增 QQ号快速分享,缩短为一个超链接 JSON 模块 91 | - 不仅如此,你可以自定义编辑匹配,触发替换内容 92 | ![](https://i.loli.net/2019/11/30/2WVEyrz4jYZwgmI.png) 93 | ![](https://i.loli.net/2019/11/30/lWYbH7ztPJVfSje.png) 94 | ![](https://i.loli.net/2019/11/30/onepTGQrh8yNamz.png) 95 | 96 | #### VERSION 1.5 97 | - ##### 1.51 98 | - 修复了部分情况下 PlaceholderAPI 的报错 99 | - 修复了配置文件控制颜色代码开关无效的问题 100 | - 新增了强制默认聊天颜色的权限节点 trchat.color.force-defaultcolor 101 | - 屏蔽了 bStats 的一个报错 102 | - 优化了更新检测器 103 | 104 | #### Old logs 105 | Version 1.5: 106 | Date: 2019.10.19 107 | Updates: 108 | - 改进部分代码 109 | - 修复 "functions" 命名错误 110 | - 修复了一些报错 111 | - 正式改名为 TrChat, 更新介绍图 112 | 113 | Version 1.4: 114 | Date: 2019.10.12 115 | Updates: 116 | - [!] 该版本重制配置文件内容较多, 请备份+删除 TrChat文件夹 后更新 117 | - >> 118 | - 配置文件重写, 分为 settings.yml / formats.yml / filter.yml / functions.yml 119 | - 云端敏感词库上线, 自动更新敏感词、支持自定义白名单词 (正在持续增加中) 120 | - 每个聊天频道均支持多个自定义聊天格式,优先级权限筛选 121 | - 添加了一个选项,可以自动下载并载入指定PAPI变量拓展 122 | - 修复了1.12展示物品报错 123 | - 修复了旧版本的各种错误、漏洞 124 | - 移除了 PerWorldChat 功能 125 | 126 | Version 1.3X: 127 | Date: 2019.8.30 - 2019.9.13 128 | Updates: 129 | 1.31: 130 | - 新增 @At 在线玩家功能 (音效+TITLE提示/忽略大小写式判断/自定义At文字高亮) 131 | 1.32: 132 | - 修复消息默认彩色代码配置问题 133 | - 现在颜色代码也可以在编辑书中使用(需要指定权限) 134 | 1.33: 135 | - 修复玩家数据报错 136 | - 修复API事件不被call 137 | - 自动更新配置文件选项 138 | - 聊天内容的发包将改为 CHAT 类型 139 | - 依赖更至 Taboolib v5.04 140 | 1.34: 141 | - 修复 <> 中内容消失的问题 142 | - 禁止在私聊中At玩家 143 | - 新增At玩家的冷却 (常规/单个玩家冷却) 144 | - 缓存展示物品,提高性能 145 | 1.35: 146 | - 修复了数据读写的问题 147 | - 修复了v1.34新增的BUG 148 | - 重写了更新检测器 149 | - 移除了控制面板 150 | - 新增聊天内容字符长度的限制 151 | 1.36: 152 | - 增加了更多 bStats 自定义统计项 153 | - 修复了私聊中过滤器绕过权限无效的问题 154 | - 改进了过滤器 155 | 1.37: 156 | - 修复了 GlobalShoutEvent & PrivateMessageEvent 157 | - 修复了 私聊报错问题 158 | - 现在可以通过 "[展示物品变量]-[槽位]" 来展示背包指定位置的物品 159 | - 👆 例如 %i-0 ~ %i-9 160 | 1.38: 161 | - 修复了1.12版无法全局喊话问题 162 | 1.39: 163 | - 修复了At功能 164 | 165 | Version 1.3: 166 | Date: 2019.8.18 167 | Updates: 168 | - 依赖更新至 Taboolib v5.03 169 | - 改进了基于权限的彩聊代码处理 170 | - 改进了插件部分服务注入方式 171 | - 改进了控制面板GUI跨版本物品兼容 172 | - 改进了私聊频道代码 173 | - 改进了物品展示处理方式 174 | - 修复了自定义物品展示代码失效 175 | - 一条消息现在支持同时展示多个物品 176 | - 改由 Taboolib 存储数据 177 | - 新增命令 /spy, 在游戏内切换是否监听玩家私聊 178 | - 现在颜色代码的权限支持木牌/铁砧使用彩色代码! 179 | - 新增多个配置节点决定是否启用颜色代码功能 180 | - 炫酷的载入文字画LOGO (可修改/关闭) 181 | - bStats 更换为 MetricsLite 并优化, 减小了插件体积 182 | - 提供了牛逼的API (TrChatAPI) 方便自定义拓展 183 | - LChatPrivateMessageEvent - API 私聊事件 184 | - LChatShoutEvent - API 私聊事件 185 | - 配置文件大改, 新增配置版号 (自动备份更新) 186 | 187 | Version 1.2: 188 | Date: 2019.8.17 20:30 189 | Updates: 190 | - 支持 CatServer 191 | - 全局喊话将强制要求Bungee启用, 否则则提示玩家 192 | - 修复了聊天冷却发送时事件未取消的问题 193 | - 更新通知新增 Mcbbs 地址 194 | 195 | Version 1.1: 196 | Date: 2019.8.17 13:30 197 | Updates: 198 | - 优化代码 199 | - 自动读取 spigot.yml 配置判断是否启用 Bungee 支持 200 | - 管理频道支持单独 Spigot 使用 201 | - 现在即使未在管理频道, 也可通过/staff [MESSAGE]发送管频消息 202 | - 改进BungeeCord的监听器 203 | 204 | Version 1.0: 205 | Date: 2019.8.16 206 | Updates: 207 | - 正式版 -------------------------------------------------------------------------------- /UPDATES_EN.md: -------------------------------------------------------------------------------- 1 | # TrChat Update Logs # 2 | 3 | #### VERSION 1.7 4 | - #### 1.70 5 | - Since: 2020.1.15 6 | - Updates: 7 | - Added en_US locale as default 8 | - Update TabooLib to 5.14 9 | - Fixed a error in v1.62 10 | - R2 11 | - Fixed item-display can not get the local item name 12 | - Added Command Controller, white/black list for commands 13 | - ^ Support regex, arguments ignore, bypass permission and TLocale response 14 | - ^ (Please refer to the new version and manually update the settings) 15 | - R3 16 | - Command Controller will now check aliases of command 17 | - Fixed a variable issue in function.yml 18 | - R4 19 | - Fixed small bugs 20 | - Added anti-tab for 1.13+, bypass permission `trchat.bypass.tabcomplete` 21 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import java.text.SimpleDateFormat 2 | 3 | plugins { 4 | id 'java' 5 | id 'com.github.johnrengelman.shadow' version '4.0.4' 6 | } 7 | 8 | configurations { 9 | group = 'me.arasple.mc.trchat' 10 | version = '1.71' 11 | 12 | sourceCompatibility = 1.8 13 | targetCompatibility = 1.8 14 | 15 | tasks.withType(JavaCompile) { 16 | options.encoding = 'UTF-8' 17 | } 18 | 19 | defaultTasks 'buildJar' 20 | } 21 | 22 | task buildJar(dependsOn: [clean, shadowJar]) 23 | 24 | repositories { 25 | maven { url "https://maven.aliyun.com/repository/central" } 26 | maven { url "http://ptms.ink:8081/repository/codemc-nms/" } 27 | maven { url "http://ptms.ink:8081/repository/maven-releases/" } 28 | maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } 29 | maven { url "https://oss.sonatype.org/content/repositories/snapshots" } 30 | maven { url "https://repo.codemc.org/repository/maven-public" } 31 | maven { url "http://repo.extendedclip.com/content/repositories/placeholderapi/" } 32 | maven { url "https://jitpack.io" } 33 | mavenCentral() 34 | } 35 | 36 | dependencies { 37 | compile 'org.spigotmc:spigot:1.15.2-R0.1-SNAPSHOT' 38 | compile 'org.bstats:bstats-bukkit:1.7' 39 | compile 'io.izzel.taboolib:TabooLib:5.17:all' 40 | compile 'io.izzel.taboolib.loader:TabooLibloader:1.4:all' 41 | compile 'me.clip:placeholderapi:2.10.4' 42 | shadow fileTree(dir: 'libs', includes: ['*.jar']) 43 | } 44 | 45 | shadowJar { 46 | dependencies { 47 | include(dependency('org.bstats:bstats-bukkit:1.7')) 48 | include(dependency('io.izzel.taboolib.loader:TabooLibloader:1.4:all')) 49 | } 50 | relocate "io.izzel.taboolib.loader", project.group 51 | relocate "org.bstats.bukkit", "me.arasple.mc.trhologram.bstats" 52 | } 53 | 54 | processResources { 55 | from(sourceSets.main.resources.srcDirs) { 56 | include 'plugin.yml' 57 | expand 'version': project.version, 'built': new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss.SSSZ").format(new Date()) 58 | } 59 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arasple/TrChat/e44eb5d25b5e16482b6b92496d95555c1a8451cd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Dec 14 18:26:04 CST 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /libs/BungeeCord.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arasple/TrChat/e44eb5d25b5e16482b6b92496d95555c1a8451cd/libs/BungeeCord.jar -------------------------------------------------------------------------------- /libs/PlaceholderAPI.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arasple/TrChat/e44eb5d25b5e16482b6b92496d95555c1a8451cd/libs/PlaceholderAPI.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'TrChat' 2 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/TrChat.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat; 2 | 3 | import io.izzel.taboolib.loader.Plugin; 4 | import io.izzel.taboolib.module.inject.TInject; 5 | 6 | /** 7 | * @author Arasple 8 | */ 9 | @Plugin.Version(5.17) 10 | public final class TrChat extends Plugin { 11 | 12 | @TInject(state = TInject.State.STARTING, init = "init", active = "load", cancel = "unload") 13 | private static TrChatLoader loader; 14 | 15 | public static double getTrVersion() { 16 | return 1.71; 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/TrChatBungee.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat; 2 | 3 | import me.arasple.mc.trchat.bstats.MetricsBungee; 4 | import me.arasple.mc.trchat.bungee.ListenerBungeeTransfer; 5 | import net.md_5.bungee.api.plugin.Plugin; 6 | 7 | /** 8 | * @author Arasple 9 | * @date 2019/8/4 22:42 10 | */ 11 | public class TrChatBungee extends Plugin { 12 | 13 | @Override 14 | public void onEnable() { 15 | new MetricsBungee(this); 16 | getProxy().getPluginManager().registerListener(this, new ListenerBungeeTransfer()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/TrChatFiles.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat; 2 | 3 | import io.izzel.taboolib.module.config.TConfig; 4 | import io.izzel.taboolib.module.inject.TInject; 5 | import io.izzel.taboolib.module.inject.TSchedule; 6 | import me.arasple.mc.trchat.chat.ChatFormats; 7 | import me.arasple.mc.trchat.filter.ChatFilter; 8 | import me.arasple.mc.trchat.func.ChatFunctions; 9 | 10 | /** 11 | * @author Arasple 12 | * @date 2019/11/30 9:59 13 | */ 14 | public class TrChatFiles { 15 | 16 | @TInject(value = "settings.yml", locale = "LOCALE-PRIORITY") 17 | private static TConfig settings; 18 | @TInject("formats.yml") 19 | private static TConfig formats; 20 | @TInject("filter.yml") 21 | private static TConfig filter; 22 | @TInject("function.yml") 23 | private static TConfig function; 24 | @TInject("channels.yml") 25 | private static TConfig channels; 26 | 27 | @TSchedule 28 | public static void init() { 29 | filter.listener(() -> ChatFilter.loadFilter(false)).runListener(); 30 | formats.listener(ChatFormats::loadFormats).runListener(); 31 | function.listener(ChatFunctions::loadFunctions).runListener(); 32 | } 33 | 34 | public static TConfig getSettings() { 35 | return settings; 36 | } 37 | 38 | public static TConfig getFormats() { 39 | return formats; 40 | } 41 | 42 | public static TConfig getFilter() { 43 | return filter; 44 | } 45 | 46 | public static TConfig getFunction() { 47 | return function; 48 | } 49 | 50 | public static TConfig getChannels() { 51 | return channels; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/TrChatLoader.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat; 2 | 3 | import io.izzel.taboolib.module.locale.TLocale; 4 | import io.izzel.taboolib.util.Files; 5 | import me.arasple.mc.trchat.chat.ChatFormats; 6 | import me.arasple.mc.trchat.filter.ChatFilter; 7 | import me.arasple.mc.trchat.func.ChatFunctions; 8 | import me.arasple.mc.trchat.updater.Updater; 9 | import me.arasple.mc.trchat.utils.Bungees; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.plugin.Plugin; 12 | 13 | import java.io.File; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | /** 18 | * @author Arasple 19 | * @date 2019/11/29 15:05 20 | */ 21 | public class TrChatLoader { 22 | 23 | private List motd = Arrays.asList( 24 | "", 25 | "§3 ___________ _________ .__ __", 26 | "§3 \\__ __________\\_ ___ \\| |__ _____ _/ |_", 27 | "§3 | | \\_ __ / \\ \\/| | \\\\__ \\\\ __\\", 28 | "§3 | | | | \\\\ \\___| Y \\/ __ \\| |", 29 | "§3 |____| |__| \\______ |___| (____ |__|", 30 | "§3 \\/ \\/ \\/ "); 31 | 32 | void init() { 33 | motd.forEach(l -> Bukkit.getConsoleSender().sendMessage(l)); 34 | TLocale.sendToConsole("PLUGIN.LOADED"); 35 | 36 | if (hookPlaceholderAPI()) { 37 | return; 38 | } 39 | // Updater 40 | Updater.init(TrChat.getPlugin()); 41 | // Chat Filter 42 | ChatFilter.loadFilter(true, Bukkit.getConsoleSender()); 43 | // Chat Formats 44 | ChatFormats.loadFormats(Bukkit.getConsoleSender()); 45 | // Chat Functions 46 | ChatFunctions.loadFunctions(Bukkit.getConsoleSender()); 47 | // Bungees 48 | Bungees.init(); 49 | } 50 | 51 | 52 | void load() { 53 | TLocale.sendToConsole("PLUGIN.ENABLED", TrChat.getPlugin().getDescription().getVersion()); 54 | } 55 | 56 | void unload() { 57 | TLocale.sendToConsole("PLUGIN.DISABLED"); 58 | } 59 | 60 | /** 61 | * 检测前置 PlaceholderAPI 62 | * 并自动下载、重启服务器 63 | */ 64 | private boolean hookPlaceholderAPI() { 65 | Plugin plugin = Bukkit.getPluginManager().getPlugin("PlaceholderAPI"); 66 | File jarFile = new File("plugins/PlaceholderAPI.jar"); 67 | String url = "https://api.spiget.org/v2/resources/6245/download"; 68 | 69 | if (plugin == null) { 70 | jarFile.delete(); 71 | TLocale.sendToConsole("PLUGIN.DEPEND.DOWNLOAD", "PlaceholderAPI"); 72 | if (Files.downloadFile(url, jarFile)) { 73 | TLocale.sendToConsole("PLUGIN.DEPEND.INSTALL", "PlaceholderAPI"); 74 | Bukkit.shutdown(); 75 | } else { 76 | TLocale.sendToConsole("PLUGIN.DEPEND.INSTALL-FAILED", "PlaceholderAPI"); 77 | Bukkit.shutdown(); 78 | } 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/api/TrChatAPI.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.api; 2 | 3 | import me.arasple.mc.trchat.filter.processer.Filter; 4 | import me.arasple.mc.trchat.filter.processer.FilteredObject; 5 | import org.bukkit.entity.Player; 6 | 7 | /** 8 | * @author Arasple 9 | * @date 2019/8/18 0:18 10 | */ 11 | public class TrChatAPI { 12 | 13 | /** 14 | * 根据玩家的权限,过滤的字符串 15 | * 16 | * @param player 玩家 17 | * @param string 字符串 18 | * @return 过滤后的 19 | */ 20 | public static FilteredObject filterString(Player player, String string) { 21 | return Filter.doFilter(string, !player.hasPermission("trchat.bypass.filter")); 22 | } 23 | 24 | public static FilteredObject filterString(Player player, String string, boolean execute) { 25 | return execute ? filterString(player, string) : new FilteredObject(string, 0); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/bstats/Metrics.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.bstats; 2 | 3 | import io.izzel.taboolib.module.inject.TSchedule; 4 | import me.arasple.mc.trchat.TrChat; 5 | 6 | import java.text.DecimalFormat; 7 | 8 | /** 9 | * @author Arasple 10 | */ 11 | public class Metrics { 12 | 13 | private static MetricsBukkit metrics; 14 | private static DecimalFormat doubleFormat = new DecimalFormat("#.#"); 15 | private static int[] coutns = new int[]{0, 0}; 16 | 17 | public static void increase(int index) { 18 | increase(index, 1); 19 | } 20 | 21 | public static void increase(int index, int value) { 22 | if (coutns[index] < Integer.MAX_VALUE) { 23 | coutns[index] += value; 24 | } 25 | } 26 | 27 | @TSchedule 28 | public static void init() { 29 | metrics = new MetricsBukkit(TrChat.getPlugin()); 30 | 31 | // 聊天次数统计 32 | metrics.addCustomChart(new MetricsBukkit.SingleLineChart("chat_counts", () -> { 33 | int i = coutns[0]; 34 | coutns[0] = 0; 35 | return i; 36 | })); 37 | // 敏感词过滤器启用统计 38 | metrics.addCustomChart(new MetricsBukkit.SingleLineChart("filter_counts", () -> { 39 | int i = coutns[1]; 40 | coutns[1] = 0; 41 | return i; 42 | })); 43 | } 44 | 45 | public static MetricsBukkit getMetrics() { 46 | return metrics; 47 | } 48 | 49 | 50 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/bstats/MetricsBungee.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.bstats; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonObject; 5 | import net.md_5.bungee.api.plugin.Plugin; 6 | import net.md_5.bungee.config.Configuration; 7 | import net.md_5.bungee.config.ConfigurationProvider; 8 | import net.md_5.bungee.config.YamlConfiguration; 9 | 10 | import javax.net.ssl.HttpsURLConnection; 11 | import java.io.*; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.net.URL; 14 | import java.nio.charset.StandardCharsets; 15 | import java.nio.file.Path; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.UUID; 19 | import java.util.concurrent.TimeUnit; 20 | import java.util.logging.Level; 21 | import java.util.zip.GZIPOutputStream; 22 | 23 | /** 24 | * bStats collects some data for plugin authors. 25 | * Check out https://bStats.org/ to learn more about bStats! 26 | */ 27 | @SuppressWarnings({"WeakerAccess", "unused"}) 28 | public class MetricsBungee { 29 | 30 | public static final int B_STATS_VERSION = 1; 31 | private static final String URL = "https://bStats.org/submitData/bungeecord"; 32 | private static final List knownMetricsInstances = new ArrayList<>(); 33 | private static boolean logSentData; 34 | private static boolean logResponseStatusText; 35 | 36 | static { 37 | if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { 38 | final String defaultPackage = new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'n', 'g', 'e', 'e', 'c', 'o', 'r', 'd'}); 39 | final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); 40 | if (MetricsBungee.class.getPackage().getName().equals(defaultPackage) || MetricsBungee.class.getPackage().getName().equals(examplePackage)) { 41 | throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); 42 | } 43 | } 44 | } 45 | 46 | private final Plugin plugin; 47 | private boolean enabled; 48 | private String serverUUID; 49 | private boolean logFailedRequests = false; 50 | 51 | public MetricsBungee(Plugin plugin) { 52 | this.plugin = plugin; 53 | try { 54 | loadConfig(); 55 | } catch (IOException e) { 56 | plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e); 57 | return; 58 | } 59 | if (!enabled) { 60 | return; 61 | } 62 | Class usedMetricsClass = getFirstBStatsClass(); 63 | if (usedMetricsClass == null) { 64 | return; 65 | } 66 | if (usedMetricsClass == getClass()) { 67 | linkMetrics(this); 68 | startSubmitting(); 69 | } else { 70 | try { 71 | usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null, this); 72 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { 73 | if (logFailedRequests) { 74 | plugin.getLogger().log(Level.WARNING, "Failed to link to first metrics class " + usedMetricsClass.getName() + "!", e); 75 | } 76 | } 77 | } 78 | } 79 | 80 | public static void linkMetrics(Object metrics) { 81 | knownMetricsInstances.add(metrics); 82 | } 83 | 84 | private static void sendData(Plugin plugin, JsonObject data) throws Exception { 85 | if (data == null) { 86 | throw new IllegalArgumentException("Data cannot be null"); 87 | } 88 | if (logSentData) { 89 | plugin.getLogger().info("Sending data to bStats: " + data.toString()); 90 | } 91 | HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); 92 | byte[] compressedData = compress(data.toString()); 93 | connection.setRequestMethod("POST"); 94 | connection.addRequestProperty("Accept", "application/json"); 95 | connection.addRequestProperty("Connection", "close"); 96 | connection.addRequestProperty("Content-Encoding", "gzip"); 97 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); 98 | connection.setRequestProperty("Content-Type", "application/json"); 99 | connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); 100 | connection.setDoOutput(true); 101 | DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); 102 | outputStream.write(compressedData); 103 | outputStream.flush(); 104 | outputStream.close(); 105 | InputStream inputStream = connection.getInputStream(); 106 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 107 | StringBuilder builder = new StringBuilder(); 108 | String line; 109 | while ((line = bufferedReader.readLine()) != null) { 110 | builder.append(line); 111 | } 112 | bufferedReader.close(); 113 | if (logResponseStatusText) { 114 | plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString()); 115 | } 116 | } 117 | 118 | private static byte[] compress(final String str) throws IOException { 119 | if (str == null) { 120 | return null; 121 | } 122 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 123 | GZIPOutputStream gzip = new GZIPOutputStream(outputStream); 124 | gzip.write(str.getBytes(StandardCharsets.UTF_8)); 125 | gzip.close(); 126 | return outputStream.toByteArray(); 127 | } 128 | 129 | public boolean isEnabled() { 130 | return enabled; 131 | } 132 | 133 | public JsonObject getPluginData() { 134 | JsonObject data = new JsonObject(); 135 | String pluginName = plugin.getDescription().getName(); 136 | String pluginVersion = plugin.getDescription().getVersion(); 137 | data.addProperty("pluginName", pluginName); 138 | data.addProperty("pluginVersion", pluginVersion); 139 | JsonArray customCharts = new JsonArray(); 140 | data.add("customCharts", customCharts); 141 | return data; 142 | } 143 | 144 | private void startSubmitting() { 145 | plugin.getProxy().getScheduler().schedule(plugin, this::submitData, 2, 30, TimeUnit.MINUTES); 146 | } 147 | 148 | private JsonObject getServerData() { 149 | int playerAmount = plugin.getProxy().getOnlineCount(); 150 | playerAmount = Math.min(playerAmount, 500); 151 | int onlineMode = plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0; 152 | String bungeecordVersion = plugin.getProxy().getVersion(); 153 | int managedServers = plugin.getProxy().getServers().size(); 154 | String javaVersion = System.getProperty("java.version"); 155 | String osName = System.getProperty("os.name"); 156 | String osArch = System.getProperty("os.arch"); 157 | String osVersion = System.getProperty("os.version"); 158 | int coreCount = Runtime.getRuntime().availableProcessors(); 159 | JsonObject data = new JsonObject(); 160 | data.addProperty("serverUUID", serverUUID); 161 | data.addProperty("playerAmount", playerAmount); 162 | data.addProperty("managedServers", managedServers); 163 | data.addProperty("onlineMode", onlineMode); 164 | data.addProperty("bungeecordVersion", bungeecordVersion); 165 | data.addProperty("javaVersion", javaVersion); 166 | data.addProperty("osName", osName); 167 | data.addProperty("osArch", osArch); 168 | data.addProperty("osVersion", osVersion); 169 | data.addProperty("coreCount", coreCount); 170 | return data; 171 | } 172 | 173 | private void submitData() { 174 | final JsonObject data = getServerData(); 175 | final JsonArray pluginData = new JsonArray(); 176 | for (Object metrics : knownMetricsInstances) { 177 | try { 178 | Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics); 179 | if (plugin instanceof JsonObject) { 180 | pluginData.add((JsonObject) plugin); 181 | } 182 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { 183 | } 184 | } 185 | data.add("plugins", pluginData); 186 | try { 187 | sendData(plugin, data); 188 | } catch (Exception e) { 189 | if (logFailedRequests) { 190 | plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e); 191 | } 192 | } 193 | } 194 | 195 | private void loadConfig() throws IOException { 196 | Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); 197 | configPath.toFile().mkdirs(); 198 | File configFile = new File(configPath.toFile(), "config.yml"); 199 | if (!configFile.exists()) { 200 | writeFile(configFile, "#bStats collects some data for plugin authors like how many servers are using their plugins.", "#To honor their work, you should not disable it.", "#This has nearly no effect on the server performance!", "#Check out https://bStats.org/ to learn more :)", "enabled: true", "serverUuid: \"" + UUID.randomUUID().toString() + "\"", "logFailedRequests: false", "logSentData: false", "logResponseStatusText: false"); 201 | } 202 | Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); 203 | enabled = configuration.getBoolean("enabled", true); 204 | serverUUID = configuration.getString("serverUuid"); 205 | logFailedRequests = configuration.getBoolean("logFailedRequests", false); 206 | logSentData = configuration.getBoolean("logSentData", false); 207 | logResponseStatusText = configuration.getBoolean("logResponseStatusText", false); 208 | } 209 | 210 | private Class getFirstBStatsClass() { 211 | Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); 212 | configPath.toFile().mkdirs(); 213 | File tempFile = new File(configPath.toFile(), "temp.txt"); 214 | try { 215 | String className = readFile(tempFile); 216 | if (className != null) { 217 | try { 218 | return Class.forName(className); 219 | } catch (ClassNotFoundException ignored) { 220 | } 221 | } 222 | writeFile(tempFile, getClass().getName()); 223 | return getClass(); 224 | } catch (IOException e) { 225 | if (logFailedRequests) { 226 | plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e); 227 | } 228 | return null; 229 | } 230 | } 231 | 232 | private String readFile(File file) throws IOException { 233 | if (!file.exists()) { 234 | return null; 235 | } 236 | try (FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader)) { 237 | return bufferedReader.readLine(); 238 | } 239 | } 240 | 241 | private void writeFile(File file, String... lines) throws IOException { 242 | if (!file.exists()) { 243 | file.createNewFile(); 244 | } 245 | try (FileWriter fileWriter = new FileWriter(file); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) { 246 | for (String line : lines) { 247 | bufferedWriter.write(line); 248 | bufferedWriter.newLine(); 249 | } 250 | } 251 | } 252 | 253 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/bungee/ListenerBungeeTransfer.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.bungee; 2 | 3 | import net.md_5.bungee.api.ProxyServer; 4 | import net.md_5.bungee.api.connection.ProxiedPlayer; 5 | import net.md_5.bungee.api.event.PluginMessageEvent; 6 | import net.md_5.bungee.api.plugin.Listener; 7 | import net.md_5.bungee.chat.ComponentSerializer; 8 | import net.md_5.bungee.event.EventHandler; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.DataInputStream; 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author Arasple 16 | * @date 2019/8/16 18:40 17 | */ 18 | public class ListenerBungeeTransfer implements Listener { 19 | 20 | @EventHandler 21 | public void onTransfer(PluginMessageEvent e) { 22 | try { 23 | ByteArrayInputStream byteArray = new ByteArrayInputStream(e.getData()); 24 | DataInputStream in = new DataInputStream(byteArray); 25 | 26 | String subChannel = in.readUTF(); 27 | String type = in.readUTF(); 28 | 29 | if ("TrChat".equals(subChannel)) { 30 | if ("SendRaw".equals(type)) { 31 | String to = in.readUTF(); 32 | ProxiedPlayer player = ProxyServer.getInstance().getPlayers().stream().filter(p -> p.getName().equalsIgnoreCase(to)).findFirst().orElse(null); 33 | 34 | if (player != null && player.isConnected()) { 35 | String raw = in.readUTF(); 36 | player.sendMessage(ComponentSerializer.parse(raw)); 37 | } 38 | } 39 | if ("BroadcastRaw".equals(type)) { 40 | String raw = in.readUTF(); 41 | ProxyServer.getInstance().broadcast(ComponentSerializer.parse(raw)); 42 | } 43 | if ("SendRawPerm".equals(type)) { 44 | String raw = in.readUTF(); 45 | String perm = in.readUTF(); 46 | 47 | ProxyServer.getInstance().getPlayers().stream().filter(p -> p.hasPermission(perm)).forEach(p -> { 48 | p.sendMessage(ComponentSerializer.parse(raw)); 49 | }); 50 | } 51 | } 52 | } catch (IOException ignored) { 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/channels/ChannelGlobal.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.channels; 2 | 3 | import io.izzel.taboolib.module.tellraw.TellrawJson; 4 | import io.izzel.taboolib.util.chat.ComponentSerializer; 5 | import me.arasple.mc.trchat.bstats.Metrics; 6 | import me.arasple.mc.trchat.chat.ChatFormats; 7 | import me.arasple.mc.trchat.chat.obj.ChatType; 8 | import me.arasple.mc.trchat.utils.Bungees; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.entity.Player; 11 | 12 | /** 13 | * @author Arasple 14 | * @date 2019/8/17 23:06 15 | */ 16 | public class ChannelGlobal { 17 | 18 | public static void execute(Player from, String message) { 19 | TellrawJson format = ChatFormats.getFormat(ChatType.GLOBAL, from).apply(from, message); 20 | String raw = ComponentSerializer.toString(format.getComponentsAll()); 21 | Bungees.sendBungeeData(from, "TrChat", "BroadcastRaw", raw); 22 | format.send(Bukkit.getConsoleSender()); 23 | Metrics.increase(0); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/channels/ChannelPrivate.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.channels; 2 | 3 | import com.google.common.collect.Lists; 4 | import io.izzel.taboolib.module.command.lite.CommandBuilder; 5 | import io.izzel.taboolib.module.inject.TFunction; 6 | import io.izzel.taboolib.module.locale.TLocale; 7 | import io.izzel.taboolib.module.tellraw.TellrawJson; 8 | import io.izzel.taboolib.util.chat.ComponentSerializer; 9 | import me.arasple.mc.trchat.TrChat; 10 | import me.arasple.mc.trchat.bstats.Metrics; 11 | import me.arasple.mc.trchat.chat.ChatFormats; 12 | import me.arasple.mc.trchat.chat.obj.ChatType; 13 | import me.arasple.mc.trchat.cmds.CommandReply; 14 | import me.arasple.mc.trchat.logs.ChatLogs; 15 | import me.arasple.mc.trchat.utils.Bungees; 16 | import org.bukkit.Bukkit; 17 | import org.bukkit.entity.Player; 18 | 19 | import java.util.List; 20 | import java.util.UUID; 21 | 22 | /** 23 | * @author Arasple 24 | * @date 2019/8/17 22:57 25 | */ 26 | @TFunction(enable = "init") 27 | public class ChannelPrivate { 28 | 29 | private static List spying = Lists.newArrayList(); 30 | 31 | public static void init() { 32 | CommandBuilder 33 | .create("spy", TrChat.getPlugin()) 34 | .permission("trchat.admin") 35 | .permissionMessage(TLocale.asString("GENERAL.NO-PERMISSION")) 36 | .tab((sender, args) -> null) 37 | .execute((sender, args) -> { 38 | if (!(sender instanceof Player)) { 39 | TLocale.sendTo(sender, "STAFF-CHANNEL.NOT-PLAYER"); 40 | return; 41 | } 42 | 43 | Player p = (Player) sender; 44 | boolean state = switchSpy(p); 45 | TLocale.sendTo(p, state ? "PRIVATE-MESSAGE.SPY.ON" : "PRIVATE-MESSAGE.SPY.OFF"); 46 | }) 47 | .build() 48 | ; 49 | } 50 | 51 | public static void execute(Player from, String to, String message) { 52 | TellrawJson sender = ChatFormats.getFormat(ChatType.PRIVATE_SEND, from).apply(from, message, from.getName(), to); 53 | TellrawJson receiver = ChatFormats.getFormat(ChatType.PRIVATE_RECEIVE, from).apply(from, message, from.getName(), to); 54 | 55 | Player toPlayer = Bukkit.getPlayerExact(to); 56 | if (toPlayer == null || !toPlayer.isOnline()) { 57 | String raw = ComponentSerializer.toString(receiver.getComponentsAll()); 58 | Bungees.sendBungeeData(from, "TrChat", "SendRaw", to, raw); 59 | } else { 60 | receiver.send(Bukkit.getPlayer(to)); 61 | TLocale.sendTo(Bukkit.getPlayer(to), "PRIVATE-MESSAGE.RECEIVE", from.getName()); 62 | } 63 | 64 | sender.send(from); 65 | 66 | String spyFormat = TLocale.asString("PRIVATE-MESSAGE.SPY-FORMAT", from.getName(), to, message); 67 | 68 | spying.forEach(spy -> { 69 | Player spyPlayer = Bukkit.getPlayer(spy); 70 | if (spyPlayer != null && spyPlayer.isOnline()) { 71 | spyPlayer.sendMessage(spyFormat); 72 | } 73 | }); 74 | Bukkit.getConsoleSender().sendMessage(spyFormat); 75 | ChatLogs.logPrivate(from.getName(), to, message); 76 | CommandReply.getLastMessageFrom().put(from.getUniqueId(), to); 77 | Metrics.increase(0); 78 | } 79 | 80 | public static boolean switchSpy(Player player) { 81 | if (!spying.contains(player.getUniqueId())) { 82 | spying.add(player.getUniqueId()); 83 | } else { 84 | spying.remove(player.getUniqueId()); 85 | } 86 | return spying.contains(player.getUniqueId()); 87 | } 88 | 89 | public static boolean isSpying(Player player) { 90 | return spying.contains(player.getUniqueId()); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/channels/ChannelStaff.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.channels; 2 | 3 | import com.google.common.collect.Lists; 4 | import io.izzel.taboolib.module.tellraw.TellrawJson; 5 | import io.izzel.taboolib.util.chat.ComponentSerializer; 6 | import me.arasple.mc.trchat.bstats.Metrics; 7 | import me.arasple.mc.trchat.chat.ChatFormats; 8 | import me.arasple.mc.trchat.chat.obj.ChatType; 9 | import me.arasple.mc.trchat.utils.Bungees; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | /** 17 | * @author Arasple 18 | * @date 2019/8/16 17:02 19 | */ 20 | public class ChannelStaff { 21 | 22 | private static List staffs = Lists.newArrayList(); 23 | 24 | public static void execute(Player player, String message) { 25 | if (player.hasPermission("trchat.staff")) { 26 | TellrawJson format = ChatFormats.getFormat(ChatType.STAFF, player).apply(player, message); 27 | if (Bungees.isEnable()) { 28 | String raw = ComponentSerializer.toString(format.getComponentsAll()); 29 | Bungees.sendBungeeData(player, "TrChat", "SendRawPerm", raw, "trchat.staff"); 30 | } else { 31 | Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("trchat.staff")).forEach(format::send); 32 | } 33 | Metrics.increase(0); 34 | } 35 | } 36 | 37 | public static boolean switchStaff(Player player) { 38 | if (!staffs.contains(player.getUniqueId())) { 39 | staffs.add(player.getUniqueId()); 40 | } else { 41 | staffs.remove(player.getUniqueId()); 42 | } 43 | return staffs.contains(player.getUniqueId()); 44 | } 45 | 46 | public static boolean isInStaffChannel(Player player) { 47 | return staffs.contains(player.getUniqueId()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/ChatFormats.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat; 2 | 3 | import me.arasple.mc.trchat.TrChatFiles; 4 | import me.arasple.mc.trchat.chat.format.Format; 5 | import me.arasple.mc.trchat.chat.format.PriFormat; 6 | import me.arasple.mc.trchat.chat.obj.ChatType; 7 | import me.arasple.mc.trchat.utils.Js; 8 | import me.arasple.mc.trchat.utils.Notifys; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | 16 | /** 17 | * @author Arasple 18 | * @date 2019/11/30 12:09 19 | */ 20 | public class ChatFormats { 21 | 22 | private static HashMap> formats = new HashMap<>(); 23 | 24 | public static Format getFormat(ChatType type, Player player) { 25 | return formats.computeIfAbsent(type, x -> new ArrayList<>()).stream().filter(format -> Js.checkCondition(player, format.getRequirement())).findFirst().orElse(null); 26 | } 27 | 28 | public static void loadFormats(CommandSender... notify) { 29 | long start = System.currentTimeMillis(); 30 | formats.entrySet().clear(); 31 | 32 | for (ChatType chatType : ChatType.values()) { 33 | if (TrChatFiles.getFormats().contains(chatType.name())) { 34 | List formats = new ArrayList<>(); 35 | TrChatFiles.getFormats().getMapList(chatType.name()).forEach(formatMap -> formats.add(chatType.isPrivate() ? new PriFormat(formatMap) : new Format(formatMap))); 36 | ChatFormats.formats.put(chatType, formats); 37 | } 38 | } 39 | 40 | formats.get(ChatType.PRIVATE_RECEIVE).forEach(format -> format.getMsg().setPrivateChat(true)); 41 | formats.get(ChatType.PRIVATE_SEND).forEach(format -> format.getMsg().setPrivateChat(true)); 42 | 43 | Notifys.notify(notify, "PLUGIN.LOADED-CHAT-FORMATS", (System.currentTimeMillis() - start)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/format/Format.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.format; 2 | 3 | import io.izzel.taboolib.internal.apache.lang3.math.NumberUtils; 4 | import io.izzel.taboolib.module.tellraw.TellrawJson; 5 | import me.arasple.mc.trchat.chat.format.objects.JsonComponent; 6 | import me.arasple.mc.trchat.chat.format.objects.MsgComponent; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/11/30 12:43 16 | */ 17 | public class Format { 18 | 19 | private int priority; 20 | private String requirement; 21 | private List jsons; 22 | private MsgComponent msg; 23 | 24 | public Format(Map formatMap) { 25 | this(formatMap.containsKey("priority") ? NumberUtils.toInt(String.valueOf(formatMap.get("priority")), 0) : Integer.MIN_VALUE, formatMap.containsKey("requirement") ? String.valueOf(formatMap.get("requirement")) : null, JsonComponent.loadList(formatMap.get("parts")), new MsgComponent((LinkedHashMap) formatMap.get("msg"))); 26 | } 27 | 28 | public Format(int priority, String requirement, List jsons, MsgComponent msg) { 29 | this.priority = priority; 30 | this.requirement = requirement; 31 | this.jsons = jsons; 32 | this.msg = msg; 33 | } 34 | 35 | public TellrawJson apply(Player player, String... message) { 36 | TellrawJson format = TellrawJson.create(); 37 | jsons.forEach(x -> format.append(x.toTellrawJson(player))); 38 | format.append(msg.toMsgTellraw(player, message[0])); 39 | return format; 40 | } 41 | 42 | /* 43 | GETTERS & SETTERS 44 | */ 45 | 46 | public String getRequirement() { 47 | return requirement; 48 | } 49 | 50 | public void setRequirement(String requirement) { 51 | this.requirement = requirement; 52 | } 53 | 54 | public List getJsons() { 55 | return jsons; 56 | } 57 | 58 | public void setJsons(List jsons) { 59 | this.jsons = jsons; 60 | } 61 | 62 | public MsgComponent getMsg() { 63 | return msg; 64 | } 65 | 66 | public void setMsg(MsgComponent msg) { 67 | this.msg = msg; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/format/PriFormat.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.format; 2 | 3 | import io.izzel.taboolib.module.tellraw.TellrawJson; 4 | import org.bukkit.entity.Player; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * @author Arasple 10 | * @date 2019/12/1 11:42 11 | */ 12 | public class PriFormat extends Format { 13 | 14 | public PriFormat(Map formatMap) { 15 | super(formatMap); 16 | } 17 | 18 | @Override 19 | public TellrawJson apply(Player player, String... message) { 20 | TellrawJson format = TellrawJson.create(); 21 | getJsons().forEach(x -> format.append(x.toTellrawJson(player, false, "true", message[1], message[2]))); 22 | format.append(getMsg().toTellrawJson(player, message[0])); 23 | return format; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/format/objects/JsonComponent.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.format.objects; 2 | 3 | import io.izzel.taboolib.module.tellraw.TellrawJson; 4 | import io.izzel.taboolib.util.Strings; 5 | import me.arasple.mc.trchat.utils.Js; 6 | import me.arasple.mc.trchat.utils.Vars; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.ArrayList; 10 | import java.util.LinkedHashMap; 11 | import java.util.List; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/11/30 12:42 16 | */ 17 | public class JsonComponent { 18 | 19 | private String requirement; 20 | private String text; 21 | private String hover; 22 | private String suggest; 23 | private String command; 24 | private String url; 25 | 26 | public JsonComponent(String text, List hover, String suggest, String command, String url) { 27 | this.text = text; 28 | this.hover = convertHoverText(hover); 29 | this.suggest = suggest; 30 | this.command = command; 31 | this.url = url; 32 | } 33 | 34 | public JsonComponent(LinkedHashMap partSection) { 35 | if (partSection.containsKey("text")) { 36 | setText(String.valueOf(partSection.get("text"))); 37 | } 38 | if (partSection.containsKey("hover")) { 39 | setHover(convertHoverText(partSection.get("hover"))); 40 | } 41 | if (partSection.containsKey("suggest")) { 42 | setSuggest(String.valueOf(partSection.get("suggest"))); 43 | } 44 | if (partSection.containsKey("command")) { 45 | setCommand(String.valueOf(partSection.get("command"))); 46 | } 47 | if (partSection.containsKey("url")) { 48 | setUrl(String.valueOf(partSection.get("url"))); 49 | } 50 | if (partSection.containsKey("requirement")) { 51 | setRequirement(String.valueOf(partSection.get("requirement"))); 52 | } 53 | } 54 | 55 | public static List loadList(Object parts) { 56 | List jsonComponents = new ArrayList<>(); 57 | ((LinkedHashMap) parts).values().forEach(part -> jsonComponents.add(new JsonComponent((LinkedHashMap) part))); 58 | return jsonComponents; 59 | } 60 | 61 | public TellrawJson toTellrawJson(Player player, String... vars) { 62 | return toTellrawJson(player, false, vars); 63 | } 64 | 65 | public TellrawJson toTellrawJson(Player player, boolean function, String... vars) { 66 | TellrawJson tellraw = TellrawJson.create(); 67 | if (!Js.checkCondition(player, getRequirement())) { 68 | return tellraw; 69 | } 70 | 71 | String text = getText(); 72 | 73 | if (vars.length == 1 && !function) { 74 | text = String.valueOf(vars[0]); 75 | } 76 | if (vars.length > 0) { 77 | if (Boolean.parseBoolean(vars[0])) { 78 | text = text.replaceAll("%toplayer_name%", vars[2]); 79 | } 80 | } 81 | tellraw.append(text != null ? Vars.replace(player, Strings.replaceWithOrder(text, vars)) : "§8[§fNull§8]"); 82 | if (hover != null) { 83 | tellraw.hoverText(Vars.replace(player, Strings.replaceWithOrder(hover, vars))); 84 | } 85 | if (suggest != null) { 86 | tellraw.clickSuggest(Vars.replace(player, Strings.replaceWithOrder(suggest, vars))); 87 | } 88 | if (command != null) { 89 | tellraw.clickCommand(Vars.replace(player, Strings.replaceWithOrder(command, vars))); 90 | } 91 | if (url != null) { 92 | tellraw.clickOpenURL(Vars.replace(player, Strings.replaceWithOrder(url, vars))); 93 | } 94 | return tellraw; 95 | } 96 | 97 | public String convertHoverText(Object object) { 98 | List hovers; 99 | if (object instanceof List) { 100 | hovers = (List) object; 101 | } else { 102 | return String.valueOf(object); 103 | } 104 | StringBuilder hover = new StringBuilder(); 105 | hovers.forEach(l -> hover.append(l).append("\n")); 106 | String result = hover.toString(); 107 | result = result.substring(0, result.length() - 1); 108 | return result; 109 | } 110 | 111 | /* 112 | GETTERS && SETTERS 113 | */ 114 | 115 | public String getRequirement() { 116 | return requirement; 117 | } 118 | 119 | public void setRequirement(String requirement) { 120 | this.requirement = requirement; 121 | } 122 | 123 | public String getText() { 124 | return text; 125 | } 126 | 127 | public void setText(String text) { 128 | this.text = text; 129 | } 130 | 131 | public String getHover() { 132 | return hover; 133 | } 134 | 135 | public void setHover(String hover) { 136 | this.hover = hover; 137 | } 138 | 139 | public String getSuggest() { 140 | return suggest; 141 | } 142 | 143 | public void setSuggest(String suggest) { 144 | this.suggest = suggest; 145 | } 146 | 147 | public String getCommand() { 148 | return command; 149 | } 150 | 151 | public void setCommand(String command) { 152 | this.command = command; 153 | } 154 | 155 | public String getUrl() { 156 | return url; 157 | } 158 | 159 | public void setUrl(String url) { 160 | this.url = url; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/format/objects/MsgComponent.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.format.objects; 2 | 3 | import io.izzel.taboolib.internal.apache.lang3.math.NumberUtils; 4 | import io.izzel.taboolib.module.locale.TLocale; 5 | import io.izzel.taboolib.module.locale.TLocaleLoader; 6 | import io.izzel.taboolib.module.tellraw.TellrawJson; 7 | import io.izzel.taboolib.util.Strings; 8 | import io.izzel.taboolib.util.Variables; 9 | import io.izzel.taboolib.util.item.Items; 10 | import me.arasple.mc.trchat.TrChat; 11 | import me.arasple.mc.trchat.TrChatFiles; 12 | import me.arasple.mc.trchat.data.Cooldowns; 13 | import me.arasple.mc.trchat.data.Users; 14 | import me.arasple.mc.trchat.func.ChatFunctions; 15 | import me.arasple.mc.trchat.func.imp.Function; 16 | import me.arasple.mc.trchat.utils.*; 17 | import org.bukkit.Bukkit; 18 | import org.bukkit.ChatColor; 19 | import org.bukkit.Material; 20 | import org.bukkit.entity.Player; 21 | import org.bukkit.inventory.ItemStack; 22 | 23 | import java.util.LinkedHashMap; 24 | import java.util.List; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * @author Arasple 29 | * @date 2019/11/30 12:42 30 | */ 31 | public class MsgComponent extends JsonComponent { 32 | 33 | private boolean isPrivateChat; 34 | private ChatColor defualtColor; 35 | 36 | public MsgComponent(String text, List hover, String suggest, String command, String url) { 37 | super(text, hover, suggest, command, url); 38 | } 39 | 40 | public MsgComponent(LinkedHashMap partSection) { 41 | super(partSection); 42 | setDefualtColor(ChatColor.getByChar(String.valueOf(partSection.get("default-color")))); 43 | } 44 | 45 | public TellrawJson toMsgTellraw(Player player, String message) { 46 | message = MessageColors.replaceWithPermission(player, message); 47 | 48 | TellrawJson tellraw = TellrawJson.create(); 49 | for (Function function : ChatFunctions.getFunctions().stream().filter(f -> Js.checkCondition(player, f.getRequirement())).collect(Collectors.toList())) { 50 | message = Pts.replacePattern(message, function.getPattern(), function.getFilterTextPattern(), "<" + function.getName() + ":{0}>"); 51 | } 52 | // At & Item Show 53 | boolean atEnabled = Users.getCooldownLeft(player.getUniqueId(), Cooldowns.CooldownType.MENTION) <= 0; 54 | String atFormat = TrChatFiles.getFunction().getStringColored("GENERAL.MENTION.FORMAT"); 55 | if (atEnabled) { 56 | for (String p : Players.getPlayers()) { 57 | if (!TrChatFiles.getFunction().getBoolean("GENERAL.MENTION.SELF-MENTION", false) && p.equalsIgnoreCase(player.getName())) { 58 | continue; 59 | } 60 | message = message.replaceAll("(?i)(@)?" + p, ""); 61 | } 62 | } 63 | boolean itemDisplayEnabled = TrChatFiles.getFunction().getBoolean("GENERAL.ITEM-SHOW.ENABLE", true); 64 | List itemKeys = TrChatFiles.getFunction().getStringList("GENERAL.ITEM-SHOW.KEYS"); 65 | String itemFormat = TrChatFiles.getFunction().getStringColored("GENERAL.ITEM-SHOW.FORMAT", "§8[§3{0} §bx{1}§8]"); 66 | if (itemDisplayEnabled) { 67 | for (String key : itemKeys) { 68 | for (int i = 0; i < 9; i++) { 69 | message = message.replace(key + "-" + i, ""); 70 | } 71 | message = message.replace(key, ""); 72 | } 73 | } 74 | 75 | // Custom Functions 76 | for (Variables.Variable v : new Variables(message).find().getVariableList()) { 77 | if (v.isVariable()) { 78 | String[] args = v.getText().split(":", 2); 79 | if (itemDisplayEnabled && "ITEM".equalsIgnoreCase(args[0])) { 80 | int slot = NumberUtils.toInt(args[1], player.getInventory().getHeldItemSlot()); 81 | ItemStack item = player.getInventory().getItem(slot) != null ? player.getInventory().getItem(slot) : new ItemStack(Material.AIR); 82 | tellraw.append(Users.getItemCache().computeIfAbsent(item, i -> TellrawJson.create().append(Strings.replaceWithOrder(itemFormat, getName(item), item.getType() != Material.AIR ? item.getAmount() : 1) + defualtColor).hoverItem(item))); 83 | continue; 84 | } 85 | if (atEnabled && "AT".equalsIgnoreCase(args[0]) && !isPrivateChat) { 86 | String atPlayer = args[1]; 87 | tellraw.append(Strings.replaceWithOrder(atFormat, atPlayer) + defualtColor); 88 | if (TrChatFiles.getFunction().getBoolean("GENERAL.MENTION.NOTIFY") && Bukkit.getPlayerExact(atPlayer) != null && Bukkit.getPlayerExact(atPlayer).isOnline()) { 89 | TLocale.sendTo(Bukkit.getPlayer(atPlayer), "MENTIONS.NOTIFY", player.getName()); 90 | } 91 | Users.updateCooldown(player.getUniqueId(), Cooldowns.CooldownType.MENTION, TrChatFiles.getFunction().getLong("GENERAL.MENTION.COOLDOWNS")); 92 | continue; 93 | } 94 | Function function = ChatFunctions.mathFunction(args[0]); 95 | if (function != null) { 96 | tellraw.append(function.getDisplayJson().toTellrawJson(player, true, args[1])); 97 | continue; 98 | } 99 | } 100 | tellraw.append(toTellrawPart(player, defualtColor + v.getText(), message)); 101 | } 102 | return tellraw; 103 | } 104 | 105 | public TellrawJson toTellrawPart(Player player, String text, String message) { 106 | TellrawJson tellraw = TellrawJson.create(); 107 | tellraw.append((text != null ? text : "§8[§fNull§8]").replace("$message", message)); 108 | if (getHover() != null) { 109 | tellraw.hoverText(Vars.replace(player, getHover()).replace("$message", message)); 110 | } 111 | if (getSuggest() != null) { 112 | tellraw.clickSuggest(Vars.replace(player, getSuggest()).replace("$message", message)); 113 | } 114 | if (getCommand() != null) { 115 | tellraw.clickCommand(Vars.replace(player, getCommand()).replace("$message", message)); 116 | } 117 | if (getUrl() != null) { 118 | tellraw.clickOpenURL(Vars.replace(player, getUrl()).replace("$message", message)); 119 | } 120 | return tellraw; 121 | } 122 | 123 | public String getName(ItemStack item) { 124 | boolean isChinese = TLocaleLoader.getLocalePriority(TrChat.getPlugin()).get(0).toLowerCase().startsWith("zh"); 125 | if (isChinese) { 126 | return Items.getName(item); 127 | } else { 128 | StringBuilder typeName = new StringBuilder(); 129 | for (String p : item.getType().name().toLowerCase().split("_")) { 130 | char[] chars = p.toCharArray(); 131 | chars[0] -= 32; 132 | typeName.append(new String(chars)).append(" "); 133 | } 134 | return item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : typeName.toString().substring(0, typeName.toString().length() - 1); 135 | } 136 | } 137 | 138 | public ChatColor getDefualtColor() { 139 | return defualtColor; 140 | } 141 | 142 | public void setDefualtColor(ChatColor defualtColor) { 143 | this.defualtColor = defualtColor; 144 | } 145 | 146 | public boolean isPrivateChat() { 147 | return isPrivateChat; 148 | } 149 | 150 | public void setPrivateChat(boolean privateChat) { 151 | isPrivateChat = privateChat; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerAnvilChange.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import me.arasple.mc.trchat.TrChatFiles; 5 | import me.arasple.mc.trchat.api.TrChatAPI; 6 | import me.arasple.mc.trchat.utils.MessageColors; 7 | import org.bukkit.Material; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.EventPriority; 11 | import org.bukkit.event.Listener; 12 | import org.bukkit.event.inventory.InventoryType; 13 | import org.bukkit.event.inventory.PrepareAnvilEvent; 14 | import org.bukkit.inventory.ItemStack; 15 | import org.bukkit.inventory.meta.ItemMeta; 16 | 17 | /** 18 | * @author Arasple 19 | * @date 2019/8/15 21:18 20 | */ 21 | @TListener 22 | public class ListenerAnvilChange implements Listener { 23 | 24 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 25 | public void onAnvilCraft(PrepareAnvilEvent e) { 26 | Player p = (Player) e.getView().getPlayer(); 27 | ItemStack result = e.getResult(); 28 | 29 | if (e.getInventory().getType() != InventoryType.ANVIL || result == null || result.getType() == Material.AIR) { 30 | return; 31 | } 32 | ItemMeta meta = result.getItemMeta(); 33 | if (meta == null || !meta.hasDisplayName()) { 34 | return; 35 | } 36 | String name = meta.getDisplayName(); 37 | if (TrChatFiles.getSettings().getBoolean("CHAT-COLOR.ANVIL")) { 38 | meta.setDisplayName(TrChatAPI.filterString(p, MessageColors.replaceWithPermission(p, name), true).getFiltered()); 39 | } 40 | result.setItemMeta(meta); 41 | e.setResult(result); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerBookEdit.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import me.arasple.mc.trchat.TrChatFiles; 5 | import me.arasple.mc.trchat.utils.MessageColors; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerEditBookEvent; 11 | import org.bukkit.inventory.meta.BookMeta; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/8/15 21:18 16 | */ 17 | @TListener 18 | public class ListenerBookEdit implements Listener { 19 | 20 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 21 | public void onBookEdit(PlayerEditBookEvent e) { 22 | Player p = e.getPlayer(); 23 | if (TrChatFiles.getSettings().getBoolean("CHAT-COLOR.BOOK", true)) { 24 | BookMeta meta = e.getNewBookMeta(); 25 | meta.setPages(MessageColors.replaceWithPermission(p, meta.getPages())); 26 | e.setNewBookMeta(meta); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerChatEvent.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import io.izzel.taboolib.module.locale.TLocale; 5 | import io.izzel.taboolib.module.tellraw.TellrawJson; 6 | import io.izzel.taboolib.util.Strings; 7 | import me.arasple.mc.trchat.TrChatFiles; 8 | import me.arasple.mc.trchat.bstats.Metrics; 9 | import me.arasple.mc.trchat.channels.ChannelGlobal; 10 | import me.arasple.mc.trchat.channels.ChannelStaff; 11 | import me.arasple.mc.trchat.chat.ChatFormats; 12 | import me.arasple.mc.trchat.chat.format.Format; 13 | import me.arasple.mc.trchat.chat.obj.ChatType; 14 | import me.arasple.mc.trchat.data.Cooldowns; 15 | import me.arasple.mc.trchat.data.Users; 16 | import me.arasple.mc.trchat.logs.ChatLogs; 17 | import org.bukkit.Bukkit; 18 | import org.bukkit.entity.Player; 19 | import org.bukkit.event.EventHandler; 20 | import org.bukkit.event.EventPriority; 21 | import org.bukkit.event.Listener; 22 | import org.bukkit.event.player.AsyncPlayerChatEvent; 23 | 24 | /** 25 | * @author Arasple 26 | * @date 2019/11/30 12:10 27 | */ 28 | @TListener 29 | public class ListenerChatEvent implements Listener { 30 | 31 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 32 | public void onChat(AsyncPlayerChatEvent e) { 33 | Player player = e.getPlayer(); 34 | 35 | if (TrChatFiles.getSettings().getStringList("GENERAL.DISABLED-WORLDS").contains(player.getWorld().getName())) { 36 | e.setCancelled(false); 37 | return; 38 | } 39 | if (ChannelStaff.isInStaffChannel(player)) { 40 | e.setCancelled(true); 41 | ChannelStaff.execute(player, e.getMessage()); 42 | return; 43 | } 44 | if (!checkLimits(player, e.getMessage())) { 45 | e.setCancelled(true); 46 | return; 47 | } 48 | if (TrChatFiles.getChannels().getBoolean("FORCE-GLOBAL", false) || e.getMessage().startsWith(TrChatFiles.getChannels().getString("FORCE-GLOBAL-PREFIX", "!all"))) { 49 | e.setCancelled(true); 50 | ChannelGlobal.execute(player, e.getMessage().replace(TrChatFiles.getChannels().getString("FORCE-GLOBAL-PREFIX", "!all"), "")); 51 | return; 52 | } 53 | 54 | Format format = ChatFormats.getFormat(ChatType.NORMAL, player); 55 | 56 | if (format != null) { 57 | e.setCancelled(true); 58 | TellrawJson message = format.apply(player, e.getMessage()); 59 | for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { 60 | message.send(onlinePlayer); 61 | } 62 | message.send(Bukkit.getConsoleSender()); 63 | ChatLogs.log(player, e.getMessage()); 64 | Metrics.increase(0); 65 | } 66 | } 67 | 68 | private boolean checkLimits(Player p, String message) { 69 | if (!p.hasPermission("trchat.bypass.*")) { 70 | long limit = TrChatFiles.getSettings().getLong("CHAT-CONTROL.LENGTH-LIMIT", 100); 71 | if (message.length() > limit) { 72 | TLocale.sendTo(p, "GENERAL.TOO-LONG", message.length(), limit); 73 | return false; 74 | } 75 | } 76 | if (!p.hasPermission("trchat.bypass.itemcd")) { 77 | long itemShowCooldown = Users.getCooldownLeft(p.getUniqueId(), Cooldowns.CooldownType.ITEM_SHOW); 78 | if (TrChatFiles.getFunction().getStringList("GENERAL.ITEM-SHOW.KEYS").stream().anyMatch(message::contains)) { 79 | if (itemShowCooldown > 0) { 80 | TLocale.sendTo(p, "COOLDOWNS.ITEM-SHOW", String.valueOf(itemShowCooldown / 1000D)); 81 | return false; 82 | } else { 83 | Users.updateCooldown(p.getUniqueId(), Cooldowns.CooldownType.ITEM_SHOW, (long) (TrChatFiles.getFunction().getDouble("GENERAL.ITEM-SHOW.COOLDOWNS") * 1000)); 84 | } 85 | } 86 | } 87 | if (!p.hasPermission("trchat.bypass.chatcd")) { 88 | long chatCooldown = Users.getCooldownLeft(p.getUniqueId(), Cooldowns.CooldownType.CHAT); 89 | if (chatCooldown > 0) { 90 | TLocale.sendTo(p, "COOLDOWNS.CHAT", String.valueOf(chatCooldown / 1000D)); 91 | return false; 92 | } else { 93 | Users.updateCooldown(p.getUniqueId(), Cooldowns.CooldownType.CHAT, (long) (TrChatFiles.getSettings().getDouble("CHAT-CONTROL.COOLDOWN") * 1000)); 94 | } 95 | } 96 | if (!p.hasPermission("trchat.bypass.repeat")) { 97 | String lastSay = Users.getLastMessage(p.getUniqueId()); 98 | if (Strings.similarDegree(lastSay, message) > TrChatFiles.getSettings().getDouble("CHAT-CONTROL.ANTI-REPEAT", 0.85)) { 99 | TLocale.sendTo(p, "GENERAL.TOO-SIMILAR"); 100 | return false; 101 | } else { 102 | Users.setLastMessage(p.getUniqueId(), message); 103 | } 104 | } 105 | return true; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerCommandController.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import io.izzel.taboolib.module.locale.TLocale; 5 | import io.izzel.taboolib.util.Strings; 6 | import me.arasple.mc.trchat.TrChatFiles; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.EventPriority; 11 | import org.bukkit.event.Listener; 12 | import org.bukkit.event.player.PlayerCommandPreprocessEvent; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * @author Arasple 20 | * @date 2020/1/16 21:41 21 | */ 22 | @TListener 23 | public class ListenerCommandController implements Listener { 24 | 25 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 26 | public void onCommand(PlayerCommandPreprocessEvent e) { 27 | Player player = e.getPlayer(); 28 | String command = e.getMessage().substring(1); 29 | Map.Entry mCmd = Bukkit.getCommandAliases().entrySet().stream().filter(entry -> Arrays.stream(entry.getValue()).anyMatch(m -> m.equalsIgnoreCase(e.getMessage().substring(1).split(" ")[0]))).findFirst().orElse(null); 30 | 31 | if (TrChatFiles.getFunction().getBoolean("GENERAL.COMMAND-CONTROLLER.ENABLE", true) && Strings.nonEmpty(command) && !player.hasPermission(TrChatFiles.getFunction().getString("GENERAL.COMMAND-CONTROLLER.BYPASS", "trchat.admin"))) { 32 | boolean whitelist = TrChatFiles.getFunction().getString("GENERAL.COMMAND-CONTROLLER.TYPE", "BLACKLIST").equalsIgnoreCase("WHITELIST"); 33 | List matches = TrChatFiles.getFunction().getStringList("GENERAL.COMMAND-CONTROLLER.LIST"); 34 | boolean matched = matches.stream().anyMatch(m -> { 35 | boolean exact = m.toLowerCase().contains(""); 36 | m = m.replaceAll("(?i)", ""); 37 | 38 | if (exact) { 39 | return command.matches("(?i)" + m); 40 | } else { 41 | return command.split(" ")[0].matches("(?i)" + m) || (mCmd != null && mCmd.getKey().matches("(?i)" + m)); 42 | } 43 | }); 44 | // 黑名单下,匹配到 或 白名单下,未匹配到 45 | if ((matched && !whitelist) || (!matched && whitelist)) { 46 | e.setCancelled(true); 47 | TLocale.sendTo(player, "COMMAND-CONTROLLER.DENY"); 48 | } 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerSignChange.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import me.arasple.mc.trchat.TrChatFiles; 5 | import me.arasple.mc.trchat.api.TrChatAPI; 6 | import me.arasple.mc.trchat.utils.MessageColors; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.EventPriority; 10 | import org.bukkit.event.Listener; 11 | import org.bukkit.event.block.SignChangeEvent; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/8/15 21:18 16 | */ 17 | @TListener 18 | public class ListenerSignChange implements Listener { 19 | 20 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 21 | public void onSignChange(SignChangeEvent e) { 22 | Player p = e.getPlayer(); 23 | 24 | for (int i = 0; i < e.getLines().length; i++) { 25 | String line = e.getLine(i); 26 | if (TrChatFiles.getSettings().getBoolean("CHAT-COLOR.SIGN")) { 27 | line = MessageColors.replaceWithPermission(p, line); 28 | } 29 | e.setLine(i, line != null ? TrChatAPI.filterString(p, line, true).getFiltered() : null); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerTabComplete.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.Listener; 7 | import org.bukkit.event.player.PlayerCommandSendEvent; 8 | 9 | /** 10 | * @author Arasple 11 | * @date 2020/1/17 14:41 12 | */ 13 | @TListener 14 | public class ListenerTabComplete implements Listener { 15 | 16 | @EventHandler 17 | public void onTabCommandSend(PlayerCommandSendEvent e) { 18 | Player p = e.getPlayer(); 19 | if (!p.hasPermission("trchat.bypass.tabcomplete")) { 20 | e.getCommands().clear(); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/listeners/ListenerTrChatInfo.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.listeners; 2 | 3 | import io.izzel.taboolib.module.inject.TListener; 4 | import io.izzel.taboolib.module.locale.TLocale; 5 | import io.izzel.taboolib.util.Strings; 6 | import io.izzel.taboolib.util.lite.SoundPack; 7 | import me.arasple.mc.trchat.TrChat; 8 | import me.arasple.mc.trchat.chat.ChatFormats; 9 | import me.arasple.mc.trchat.filter.ChatFilter; 10 | import me.arasple.mc.trchat.func.ChatFunctions; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.EventHandler; 13 | import org.bukkit.event.EventPriority; 14 | import org.bukkit.event.Listener; 15 | import org.bukkit.event.player.AsyncPlayerChatEvent; 16 | import org.bukkit.event.player.PlayerCommandPreprocessEvent; 17 | 18 | /** 19 | * @author Arasple 20 | * @date 2019/11/30 21:56 21 | */ 22 | @TListener 23 | public class ListenerTrChatInfo implements Listener { 24 | 25 | @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) 26 | public void onChat(AsyncPlayerChatEvent e) { 27 | e.setCancelled(react(e.getPlayer(), e.getMessage().startsWith("#") ? e.getMessage().substring(1) : null)); 28 | 29 | if ("#TRCHAT-RELOAD".equals(e.getMessage()) && e.getPlayer().hasPermission("trchat.admin")) { 30 | ChatFormats.loadFormats(e.getPlayer()); 31 | ChatFilter.loadFilter(true, e.getPlayer()); 32 | ChatFunctions.loadFunctions(e.getPlayer()); 33 | e.setCancelled(true); 34 | } 35 | } 36 | 37 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 38 | public void onCommand(PlayerCommandPreprocessEvent e) { 39 | e.setCancelled(react(e.getPlayer(), e.getMessage().substring(1))); 40 | } 41 | 42 | private boolean react(Player p, String message) { 43 | if (!Strings.isBlank(message) && ("trchatr".equalsIgnoreCase(message) || "trixeychat".equalsIgnoreCase(message))) { 44 | TLocale.Display.sendTitle(p, "§3§lTr§b§lChat", "§7Designed by §6Arasple", 10, 35, 10); 45 | TLocale.Display.sendActionBar(p, Strings.replaceWithOrder( 46 | "§2Running version §av{0}§7", 47 | TrChat.getTrVersion() 48 | )); 49 | new SoundPack("BLOCK_NOTE_BLOCK_PLING-1-2").play(p); 50 | return true; 51 | } 52 | return false; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/chat/obj/ChatType.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.chat.obj; 2 | 3 | /** 4 | * @author Arasple 5 | * @date 2019/11/30 13:23 6 | */ 7 | public enum ChatType { 8 | 9 | /** 10 | * 常规聊天 11 | */ 12 | NORMAL, 13 | 14 | /** 15 | * 全局喊话 16 | */ 17 | GLOBAL, 18 | 19 | /** 20 | * 玩家私聊 21 | */ 22 | PRIVATE_SEND, 23 | PRIVATE_RECEIVE, 24 | 25 | /** 26 | * 管理交流 27 | */ 28 | STAFF; 29 | 30 | public boolean isPrivate() { 31 | return this == PRIVATE_RECEIVE || this == PRIVATE_SEND; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/cmds/CommandFilter.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.cmds; 2 | 3 | import io.izzel.taboolib.module.command.base.BaseCommand; 4 | import io.izzel.taboolib.module.command.base.BaseMainCommand; 5 | import io.izzel.taboolib.module.locale.TLocale; 6 | import me.arasple.mc.trchat.menus.MenuFilterControl; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/8/4 21:19 16 | */ 17 | @BaseCommand(name = "filter", aliases = {"filters", "chatfilter", "trfilter"}, permission = "trchat.filter") 18 | public class CommandFilter extends BaseMainCommand { 19 | 20 | @Override 21 | public String getCommandTitle() { 22 | return TLocale.asString("PLUGIN.COMMAND-TITLE"); 23 | } 24 | 25 | @Override 26 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 27 | if (!(sender instanceof Player)) { 28 | TLocale.sendTo(sender, "STAFF-CHANNEL.NOT-PLAYER"); 29 | return true; 30 | } 31 | 32 | Player p = (Player) sender; 33 | MenuFilterControl.displayFor((Player) sender); 34 | return true; 35 | } 36 | 37 | @Override 38 | public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { 39 | return null; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/cmds/CommandGlobalShout.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.cmds; 2 | 3 | import io.izzel.taboolib.module.command.base.BaseCommand; 4 | import io.izzel.taboolib.module.command.base.BaseMainCommand; 5 | import io.izzel.taboolib.module.locale.TLocale; 6 | import io.izzel.taboolib.util.ArrayUtil; 7 | import me.arasple.mc.trchat.channels.ChannelGlobal; 8 | import me.arasple.mc.trchat.utils.Bungees; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author Arasple 17 | * @date 2019/8/4 21:19 18 | */ 19 | @BaseCommand(name = "shout", aliases = {"all", "global"}, permission = "trchat.global") 20 | public class CommandGlobalShout extends BaseMainCommand { 21 | 22 | @Override 23 | public String getCommandTitle() { 24 | return TLocale.asString("PLUGIN.COMMAND-TITLE"); 25 | } 26 | 27 | @Override 28 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 29 | if (!Bungees.isEnable()) { 30 | TLocale.sendTo(sender, "GLOBAL-MESSAGE.NOT-ENABLE"); 31 | return true; 32 | } 33 | if (!(sender instanceof Player)) { 34 | TLocale.sendTo(sender, "GLOBAL-MESSAGE.NOT-PLAYER"); 35 | return true; 36 | } 37 | if (args.length == 0) { 38 | TLocale.sendTo(sender, "GLOBAL-MESSAGE.NO-MESSAGE"); 39 | return true; 40 | } 41 | String message = ArrayUtil.arrayJoin(args, 0); 42 | ChannelGlobal.execute((Player) sender, message); 43 | return true; 44 | } 45 | 46 | @Override 47 | public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { 48 | return null; 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/cmds/CommandPrivateMessage.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.cmds; 2 | 3 | import io.izzel.taboolib.module.command.base.BaseCommand; 4 | import io.izzel.taboolib.module.command.base.BaseMainCommand; 5 | import io.izzel.taboolib.module.locale.TLocale; 6 | import io.izzel.taboolib.util.ArrayUtil; 7 | import me.arasple.mc.trchat.channels.ChannelPrivate; 8 | import me.arasple.mc.trchat.utils.Players; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * @author Arasple 18 | * @date 2019/8/4 21:19 19 | */ 20 | @BaseCommand(name = "msg", aliases = {"message", "tell", "talk", "m"}, permission = "trchat.private") 21 | public class CommandPrivateMessage extends BaseMainCommand { 22 | 23 | @Override 24 | public String getCommandTitle() { 25 | return TLocale.asString("PLUGIN.COMMAND-TITLE"); 26 | } 27 | 28 | @Override 29 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 30 | if (!(sender instanceof Player)) { 31 | TLocale.sendTo(sender, "PRIVATE-MESSAGE.NOT-PLAYER"); 32 | return true; 33 | } 34 | if (args.length == 0) { 35 | TLocale.sendTo(sender, "PRIVATE-MESSAGE.NO-PLAYER"); 36 | return true; 37 | } 38 | if (!Players.isPlayerOnline(args[0])) { 39 | TLocale.sendTo(sender, "PRIVATE-MESSAGE.NOT-EXIST"); 40 | return true; 41 | } else if (args.length == 1) { 42 | TLocale.sendTo(sender, "PRIVATE-MESSAGE.NO-MESSAGE"); 43 | return true; 44 | } 45 | String privateMessage = ArrayUtil.arrayJoin(args, 1); 46 | ChannelPrivate.execute((Player) sender, Players.getPlayerFullName(args[0]), privateMessage); 47 | return true; 48 | } 49 | 50 | @Override 51 | public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { 52 | if (args.length == 0) { 53 | return Players.getPlayers(); 54 | } else if (args.length == 1) { 55 | return Players.getPlayers().stream().filter(s -> s.toLowerCase().startsWith(args[0])).collect(Collectors.toList()); 56 | } 57 | return null; 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/cmds/CommandReply.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.cmds; 2 | 3 | import io.izzel.taboolib.module.command.base.BaseCommand; 4 | import io.izzel.taboolib.module.command.base.BaseMainCommand; 5 | import io.izzel.taboolib.module.locale.TLocale; 6 | import io.izzel.taboolib.util.ArrayUtil; 7 | import me.arasple.mc.trchat.channels.ChannelPrivate; 8 | import me.arasple.mc.trchat.utils.Players; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.UUID; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * @author Arasple 20 | * @date 2020/1/26 16:31 21 | */ 22 | 23 | @BaseCommand(name = "reply", aliases = {"r"}, permission = "trchat.private") 24 | public class CommandReply extends BaseMainCommand { 25 | 26 | private static HashMap lastMessageFrom = new HashMap<>(); 27 | 28 | public static HashMap getLastMessageFrom() { 29 | return lastMessageFrom; 30 | } 31 | 32 | @Override 33 | 34 | public String getCommandTitle() { 35 | return TLocale.asString("PLUGIN.COMMAND-TITLE"); 36 | } 37 | 38 | @Override 39 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 40 | if (!(sender instanceof Player)) { 41 | TLocale.sendTo(sender, "PRIVATE-MESSAGE.NOT-PLAYER"); 42 | return true; 43 | } 44 | if (getLastMessageFrom().containsKey(((Player) sender).getUniqueId())) { 45 | String privateMessage = ArrayUtil.arrayJoin(args, 0); 46 | ChannelPrivate.execute((Player) sender, Players.getPlayerFullName(getLastMessageFrom().get(((Player) sender).getUniqueId())), privateMessage); 47 | } 48 | return true; 49 | } 50 | 51 | @Override 52 | public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { 53 | if (args.length == 0) { 54 | return Players.getPlayers(); 55 | } else if (args.length == 1) { 56 | return Players.getPlayers().stream().filter(s -> s.toLowerCase().startsWith(args[0])).collect(Collectors.toList()); 57 | } 58 | return null; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/cmds/CommandStaffChat.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.cmds; 2 | 3 | import io.izzel.taboolib.module.command.base.BaseCommand; 4 | import io.izzel.taboolib.module.command.base.BaseMainCommand; 5 | import io.izzel.taboolib.module.locale.TLocale; 6 | import io.izzel.taboolib.util.ArrayUtil; 7 | import me.arasple.mc.trchat.channels.ChannelStaff; 8 | import org.bukkit.command.Command; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author Arasple 16 | * @date 2019/8/4 21:19 17 | */ 18 | @BaseCommand(name = "staff", aliases = {"staffchannel"}, permission = "trchat.staff") 19 | public class CommandStaffChat extends BaseMainCommand { 20 | 21 | @Override 22 | public String getCommandTitle() { 23 | return TLocale.asString("PLUGIN.COMMAND-TITLE"); 24 | } 25 | 26 | @Override 27 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 28 | if (!(sender instanceof Player)) { 29 | TLocale.sendTo(sender, "STAFF-CHANNEL.NOT-PLAYER"); 30 | return true; 31 | } 32 | 33 | Player p = (Player) sender; 34 | 35 | if (args.length == 0) { 36 | boolean state = ChannelStaff.switchStaff(p); 37 | TLocale.sendTo(p, state ? "STAFF-CHANNEL.JOIN" : "STAFF-CHANNEL.QUIT"); 38 | } else { 39 | ChannelStaff.execute(p, ArrayUtil.arrayJoin(args, 0)); 40 | } 41 | return true; 42 | } 43 | 44 | @Override 45 | public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { 46 | return null; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/data/Cooldowns.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.data; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author Arasple 9 | * @date 2019/8/15 18:38 10 | */ 11 | public class Cooldowns { 12 | 13 | private List cooldown = Lists.newArrayList(); 14 | 15 | public List getCooldowns() { 16 | return cooldown; 17 | } 18 | 19 | public void setCooldowns(List cooldown) { 20 | this.cooldown = cooldown; 21 | } 22 | 23 | public enum CooldownType { 24 | 25 | /** 26 | * Chat Cooldown Types 27 | */ 28 | 29 | CHAT("Chat"), 30 | ITEM_SHOW("ItemShow"), 31 | MENTION("Mention"); 32 | 33 | private String name; 34 | 35 | CooldownType(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | } 43 | 44 | public static class Cooldown { 45 | 46 | private String id; 47 | private long time; 48 | 49 | public Cooldown() { 50 | 51 | } 52 | 53 | public Cooldown(String id, long time) { 54 | this.id = id; 55 | this.time = time; 56 | } 57 | 58 | public String getId() { 59 | return id; 60 | } 61 | 62 | public void setId(String id) { 63 | this.id = id; 64 | } 65 | 66 | public long getTime() { 67 | return time; 68 | } 69 | 70 | public void setTime(long time) { 71 | this.time = time; 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/data/Users.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.data; 2 | 3 | import io.izzel.taboolib.module.db.local.LocalPlayer; 4 | import io.izzel.taboolib.module.tellraw.TellrawJson; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.inventory.ItemStack; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.UUID; 12 | 13 | /** 14 | * @author Arasple 15 | * @date 2019/11/30 11:30 16 | */ 17 | public class Users { 18 | 19 | private static HashMap itemCache = new HashMap<>(); 20 | private static HashMap cooldowns = new HashMap<>(); 21 | private static HashMap message = new HashMap<>(); 22 | 23 | public static long getCooldownLeft(UUID uuid, Cooldowns.CooldownType type) { 24 | cooldowns.putIfAbsent(uuid, new Cooldowns()); 25 | for (Cooldowns.Cooldown COOLDOWN : cooldowns.get(uuid).getCooldowns()) { 26 | if (COOLDOWN.getId().equalsIgnoreCase(type.getName())) { 27 | return COOLDOWN.getTime() - System.currentTimeMillis(); 28 | } 29 | } 30 | return -1; 31 | } 32 | 33 | public static boolean isInCooldown(UUID uuid, Cooldowns.CooldownType type) { 34 | return getCooldownLeft(uuid, type) > 0; 35 | } 36 | 37 | public static void updateCooldown(UUID uuid, Cooldowns.CooldownType type, long lasts) { 38 | cooldowns.putIfAbsent(uuid, new Cooldowns()); 39 | cooldowns.get(uuid).getCooldowns().removeIf(c -> c.getId().equalsIgnoreCase(type.getName())); 40 | cooldowns.get(uuid).getCooldowns().add(new Cooldowns.Cooldown(type.getName(), System.currentTimeMillis() + lasts)); 41 | } 42 | 43 | public static HashMap getCooldowns() { 44 | return cooldowns; 45 | } 46 | 47 | public static HashMap getItemCache() { 48 | return itemCache; 49 | } 50 | 51 | public static boolean isFilterEnabled(Player user) { 52 | return LocalPlayer.get(user).getBoolean("TRCHAT.FILTER", true); 53 | } 54 | 55 | public static void setFilter(Player user, boolean value) { 56 | LocalPlayer.get(user).set("TRCHAT.FILTER", value); 57 | } 58 | 59 | public static List getIgnoredList(Player user) { 60 | // if (!LocalPlayer.get(user).isSet("TRCHAT.IGNORED")) { 61 | // LocalPlayer.get(user).set("TRCHAT.IGNORED", new ArrayList<>()); 62 | // } 63 | // return LocalPlayer.get(user).getStringList("TRCHAT.IGNORED"); 64 | return new ArrayList<>(); 65 | } 66 | 67 | public static String getLastMessage(UUID uuid) { 68 | return message.getOrDefault(uuid, ""); 69 | } 70 | 71 | public static void setLastMessage(UUID uuid, String msg) { 72 | message.put(uuid, msg); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/ChatFilter.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import io.izzel.taboolib.module.inject.TSchedule; 7 | import io.izzel.taboolib.util.Files; 8 | import me.arasple.mc.trchat.TrChat; 9 | import me.arasple.mc.trchat.TrChatFiles; 10 | import me.arasple.mc.trchat.filter.processer.Filter; 11 | import me.arasple.mc.trchat.filter.processer.FilteredObject; 12 | import me.arasple.mc.trchat.utils.Notifys; 13 | import org.bukkit.Bukkit; 14 | import org.bukkit.command.CommandSender; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * @author Arasple 20 | * @date 2019/10/12 20:52 21 | */ 22 | public class ChatFilter { 23 | 24 | private static String CHATFILTER_CLOUD_LAST_UPDATE; 25 | private static final String[] CHATFILTER_CLOUD_URL = new String[]{ 26 | "https://arasple.oss-cn-beijing.aliyuncs.com/plugins/TrChat/database.json", 27 | "https://raw.githubusercontent.com/Arasple/TrChat-Cloud/master/database.json", 28 | }; 29 | 30 | @TSchedule(delay = 20 * 120, period = 30 * 60 * 20) 31 | static void asyncRefreshCloud() { 32 | loadCloudFilter(0); 33 | } 34 | 35 | /** 36 | * 加载聊天过滤器 37 | * 38 | * @param updateCloud 是否更新云端词库 39 | * @param notify 接受通知反馈 40 | */ 41 | public static void loadFilter(boolean updateCloud, CommandSender... notify) { 42 | // 初始化本地配置 43 | Filter.setSensitiveWord(TrChatFiles.getFilter().getStringList("LOCAL")); 44 | Filter.setPunctuations(TrChatFiles.getFilter().getStringList("IGNORED-PUNCTUATIONS")); 45 | Filter.setReplacement(TrChatFiles.getFilter().getString("REPLACEMENT").charAt(0)); 46 | 47 | // 更新云端词库 48 | if (updateCloud && TrChatFiles.getFilter().getBoolean("CLOUD-THESAURUS.ENABLE", true)) { 49 | loadCloudFilter(0, notify); 50 | } else { 51 | Notifys.notify(notify, "PLUGIN.LOADED-FILTER-LOCAL", TrChatFiles.getFilter().getStringList("LOCAL").size()); 52 | } 53 | } 54 | 55 | /** 56 | * 加载云端聊天敏感词库 57 | * 58 | * @param url 尝试 URL 序号 59 | * @param notify 接受通知反馈 60 | */ 61 | private static void loadCloudFilter(int url, CommandSender... notify) { 62 | Bukkit.getScheduler().runTaskAsynchronously(TrChat.getPlugin(), () -> { 63 | List whitelist = TrChatFiles.getFilter().getStringList("CLOUD-THESAURUS.WHITELIST"); 64 | List collected = Lists.newArrayList(); 65 | String lastUpdateDate; 66 | 67 | try { 68 | JsonObject database = (JsonObject) new JsonParser().parse(Files.readFromURL(CHATFILTER_CLOUD_URL[url])); 69 | if (!database.has("lastUpdateDate") || !database.has("words")) { 70 | throw new NullPointerException("Wrong database json object"); 71 | } 72 | 73 | lastUpdateDate = database.get("lastUpdateDate").getAsString(); 74 | if (CHATFILTER_CLOUD_LAST_UPDATE == null) { 75 | CHATFILTER_CLOUD_LAST_UPDATE = lastUpdateDate; 76 | } else if (CHATFILTER_CLOUD_LAST_UPDATE.equals(lastUpdateDate)) { 77 | return; 78 | } else { 79 | CHATFILTER_CLOUD_LAST_UPDATE = lastUpdateDate; 80 | } 81 | 82 | database.get("words").getAsJsonArray().iterator().forEachRemaining(i -> { 83 | String word = i.getAsString(); 84 | if (whitelist.stream().noneMatch(w -> w.equalsIgnoreCase(word))) { 85 | collected.add(word); 86 | } 87 | }); 88 | } catch (Throwable e) { 89 | if (url == 0) { 90 | loadCloudFilter(url + 1, notify); 91 | } else { 92 | Notifys.notify(notify, "PLUGIN.LOADED-FILTER-LOCAL", TrChatFiles.getFilter().getStringList("LOCAL").size()); 93 | Notifys.notify(notify, "PLUGIN.FAILED-LOAD-FILTER-CLOUD"); 94 | } 95 | return; 96 | } 97 | Notifys.notify(notify, "PLUGIN.LOADED-FILTER-LOCAL", TrChatFiles.getFilter().getStringList("LOCAL").size()); 98 | Notifys.notify(notify, "PLUGIN.LOADED-FILTER-CLOUD", collected.size(), url, lastUpdateDate); 99 | Filter.addSensitiveWord(collected); 100 | }); 101 | } 102 | 103 | /** 104 | * 过滤一个字符串 105 | * 106 | * @param string 待过滤字符串 107 | * @param execute 是否真的过滤 108 | * @return 过滤后的字符串 109 | */ 110 | public static FilteredObject filter(String string, boolean... execute) { 111 | if (execute.length > 0 && !execute[0]) { 112 | return new FilteredObject(string, 0); 113 | } else { 114 | return Filter.doFilter(string); 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/listeners/PacketListener.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.listeners; 2 | 3 | import io.izzel.taboolib.module.packet.Packet; 4 | import io.izzel.taboolib.module.packet.TPacket; 5 | import me.arasple.mc.trchat.data.Users; 6 | import me.arasple.mc.trchat.utils.PacketUtils; 7 | import org.bukkit.entity.Player; 8 | 9 | /** 10 | * @author Arasple 11 | * @date 2019/11/30 10:16 12 | */ 13 | public class PacketListener { 14 | 15 | @TPacket(type = TPacket.Type.SEND) 16 | static boolean filterChat(Player player, Packet packet) { 17 | try { 18 | if (packet == null || player == null || !player.isOnline()) { 19 | return true; 20 | } 21 | if (PacketUtils.get().isAvailable() && Users.isFilterEnabled(player)) { 22 | if (packet.is("PacketPlayOutChat")) { 23 | packet.write("a", PacketUtils.get().filterIChatComponent(packet.read("a"))); 24 | } else if (packet.is("PacketPlayOutWindowItems")) { 25 | PacketUtils.get().filterItemList(packet.read("b")); 26 | } else if (packet.is("PacketPlayOutSetSlot")) { 27 | PacketUtils.get().filterItem(packet.read("c")); 28 | } 29 | } 30 | } catch (Throwable ignored) { 31 | } 32 | return true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/processer/BCConvert.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.processer; 2 | 3 | public class BCConvert { 4 | 5 | private static final char DBC_CHAR_START = 33; 6 | private static final char DBC_CHAR_END = 126; 7 | private static final char SBC_CHAR_START = 65281; 8 | private static final char SBC_CHAR_END = 65374; 9 | private static final int CONVERT_STEP = 65248; 10 | private static final char SBC_SPACE = 12288; 11 | private static final char DBC_SPACE = ' '; 12 | 13 | public static String bj2qj(String src) { 14 | if (src == null) { 15 | return null; 16 | } 17 | StringBuilder buf = new StringBuilder(src.length()); 18 | char[] ca = src.toCharArray(); 19 | for (char c : ca) { 20 | if (c == DBC_SPACE) { 21 | buf.append(SBC_SPACE); 22 | } else if ((c >= DBC_CHAR_START) && (c <= DBC_CHAR_END)) { 23 | buf.append((char) (c + CONVERT_STEP)); 24 | } else { 25 | buf.append(c); 26 | } 27 | } 28 | return buf.toString(); 29 | } 30 | 31 | public static int bj2qj(char src) { 32 | int r = src; 33 | if ((src >= DBC_CHAR_START) && (src <= DBC_CHAR_END)) { 34 | r = src + CONVERT_STEP; 35 | } 36 | return r; 37 | } 38 | 39 | 40 | public static String qj2bj(String src) { 41 | if (src == null) { 42 | return null; 43 | } 44 | StringBuilder buf = new StringBuilder(src.length()); 45 | char[] ca = src.toCharArray(); 46 | for (int i = 0; i < src.length(); i++) { 47 | if (ca[i] >= SBC_CHAR_START && ca[i] <= SBC_CHAR_END) { 48 | buf.append((char) (ca[i] - CONVERT_STEP)); 49 | } else if (ca[i] == SBC_SPACE) { 50 | buf.append(DBC_SPACE); 51 | } else { 52 | buf.append(ca[i]); 53 | } 54 | } 55 | return buf.toString(); 56 | } 57 | 58 | public static int qj2bj(char src) { 59 | int r = src; 60 | if (src >= SBC_CHAR_START && src <= SBC_CHAR_END) { 61 | r = src - CONVERT_STEP; 62 | } else if (src == SBC_SPACE) { 63 | r = DBC_SPACE; 64 | } 65 | return r; 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/processer/Filter.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.processer; 2 | 3 | import me.arasple.mc.trchat.bstats.Metrics; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @author andy 9 | * @version 2.2 10 | * - 11 | * https://github.com/andyzty/sensitivewd-filter 12 | */ 13 | public class Filter { 14 | 15 | private static FilterSet SET; 16 | private static Map NODES; 17 | private static Set PUNCTUATIONS_SET; 18 | private static char SIGN; 19 | 20 | public static void setPunctuations(List punctuations) { 21 | PUNCTUATIONS_SET = new HashSet<>(); 22 | addPunctuations(punctuations); 23 | } 24 | 25 | public static void setSensitiveWord(List punctuations) { 26 | SET = new FilterSet(); 27 | NODES = new HashMap<>(); 28 | addSensitiveWord(punctuations); 29 | } 30 | 31 | public static void setReplacement(char sign) { 32 | Filter.SIGN = sign; 33 | } 34 | 35 | private static void addPunctuations(List punctuations) { 36 | if (!punctuations.isEmpty()) { 37 | char[] chs; 38 | for (String curr : punctuations) { 39 | chs = curr.toCharArray(); 40 | for (char c : chs) { 41 | PUNCTUATIONS_SET.add(charConvert(c)); 42 | } 43 | } 44 | } 45 | } 46 | 47 | public static void addSensitiveWord(List words) { 48 | if (!words.isEmpty()) { 49 | char[] chs; 50 | int fchar; 51 | int lastIndex; 52 | WordNode fnode; 53 | for (String curr : words) { 54 | chs = curr.toCharArray(); 55 | fchar = charConvert(chs[0]); 56 | if (SET.contains(fchar)) { 57 | SET.add(fchar); 58 | fnode = new WordNode(fchar, chs.length == 1); 59 | NODES.put(fchar, fnode); 60 | } else { 61 | fnode = NODES.get(fchar); 62 | if (!fnode.isLast() && chs.length == 1) { 63 | fnode.setLast(true); 64 | } 65 | } 66 | lastIndex = chs.length - 1; 67 | for (int i = 1; i < chs.length; i++) { 68 | fnode = fnode.addIfNoExist(charConvert(chs[i]), i == lastIndex); 69 | } 70 | } 71 | } 72 | } 73 | 74 | public static FilteredObject doFilter(String src) { 75 | return doFilter(src, true); 76 | } 77 | 78 | public static FilteredObject doFilter(String src, boolean filter) { 79 | if (!filter) { 80 | return new FilteredObject(src, 0); 81 | } 82 | char[] chs = src.toCharArray(); 83 | int length = chs.length, curr, curry, k, count = 0; 84 | WordNode node; 85 | for (int i = 0; i < length; i++) { 86 | curr = charConvert(chs[i]); 87 | if (SET.contains(curr)) { 88 | continue; 89 | } 90 | node = NODES.get(curr); 91 | if (node == null) { 92 | continue; 93 | } 94 | boolean couldMark = false; 95 | int markNum = -1; 96 | if (node.isLast()) { 97 | couldMark = true; 98 | markNum = 0; 99 | } 100 | k = i; 101 | curry = curr; 102 | for (; ++k < length; ) { 103 | int temp = charConvert(chs[k]); 104 | if (temp == curry) { 105 | continue; 106 | } 107 | if (PUNCTUATIONS_SET.contains(temp)) { 108 | continue; 109 | } 110 | node = node.querySub(temp); 111 | if (node == null) { 112 | break; 113 | } 114 | if (node.isLast()) { 115 | couldMark = true; 116 | markNum = k - i; 117 | } 118 | curry = temp; 119 | } 120 | if (couldMark) { 121 | for (k = 0; k <= markNum; k++) { 122 | if (!PUNCTUATIONS_SET.contains(charConvert(chs[k + i]))) { 123 | chs[k + i] = SIGN; 124 | } 125 | } 126 | count++; 127 | i = i + markNum; 128 | } 129 | } 130 | Metrics.increase(1, count); 131 | return new FilteredObject(new String(chs), count); 132 | } 133 | 134 | private static int charConvert(char src) { 135 | int r = BCConvert.qj2bj(src); 136 | return (r >= 'A' && r <= 'Z') ? r + 32 : r; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/processer/FilterSet.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.processer; 2 | 3 | public class FilterSet { 4 | 5 | private final long[] elements; 6 | 7 | public FilterSet() { 8 | elements = new long[1 + (65535 >>> 6)]; 9 | } 10 | 11 | public void add(final int no) { 12 | elements[no >>> 6] |= (1L << (no & 63)); 13 | } 14 | 15 | public void add(final int... no) { 16 | for (int currNo : no) { 17 | elements[currNo >>> 6] |= (1L << (currNo & 63)); 18 | } 19 | } 20 | 21 | public void remove(final int no) { 22 | elements[no >>> 6] &= ~(1L << (no & 63)); 23 | } 24 | 25 | public boolean addAndNotify(final int no) { 26 | int eWordNum = no >>> 6; 27 | long oldElements = elements[eWordNum]; 28 | elements[eWordNum] |= (1L << (no & 63)); 29 | return elements[eWordNum] != oldElements; 30 | } 31 | 32 | public boolean removeAndNotify(final int no) { 33 | int eWordNum = no >>> 6; 34 | long oldElements = elements[eWordNum]; 35 | elements[eWordNum] &= ~(1L << (no & 63)); 36 | return elements[eWordNum] != oldElements; 37 | } 38 | 39 | public boolean contains(final int no) { 40 | return (elements[no >>> 6] & (1L << (no & 63))) == 0; 41 | } 42 | 43 | public boolean containsAll(final int... no) { 44 | if (no.length == 0) { 45 | return true; 46 | } 47 | for (int currNo : no) { 48 | if ((elements[currNo >>> 6] & (1L << (currNo & 63))) == 0) { 49 | return false; 50 | } 51 | } 52 | return true; 53 | } 54 | 55 | public boolean containsAllUselessWay(final int... no) { 56 | long[] elements = new long[this.elements.length]; 57 | for (int currNo : no) { 58 | elements[currNo >>> 6] |= (1L << (currNo & 63)); 59 | } 60 | for (int i = 0; i < elements.length; i++) { 61 | if ((elements[i] & ~this.elements[i]) != 0) { 62 | return false; 63 | } 64 | } 65 | return true; 66 | } 67 | 68 | public int size() { 69 | int size = 0; 70 | for (long element : elements) { 71 | size += Long.bitCount(element); 72 | } 73 | return size; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/processer/FilteredObject.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.processer; 2 | 3 | /** 4 | * @author Arasple 5 | * @date 2019/9/12 17:40 6 | */ 7 | public class FilteredObject { 8 | 9 | private String filtered; 10 | private int sensitiveWords; 11 | 12 | public FilteredObject(String filtered, int sensitiveWords) { 13 | this.filtered = filtered; 14 | this.sensitiveWords = sensitiveWords; 15 | } 16 | 17 | public String getFiltered() { 18 | return filtered; 19 | } 20 | 21 | public void setFiltered(String filtered) { 22 | this.filtered = filtered; 23 | } 24 | 25 | public int getSensitiveWords() { 26 | return sensitiveWords; 27 | } 28 | 29 | public void setSensitiveWords(int sensitiveWords) { 30 | this.sensitiveWords = sensitiveWords; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/filter/processer/WordNode.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.filter.processer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | public class WordNode { 7 | 8 | private int value; 9 | private List subNodes; 10 | private boolean isLast; 11 | 12 | public WordNode(int value) { 13 | this.value = value; 14 | } 15 | 16 | public WordNode(int value, boolean isLast) { 17 | this.value = value; 18 | this.isLast = isLast; 19 | } 20 | 21 | private WordNode addSubNode(final WordNode subNode) { 22 | if (subNodes == null) { 23 | subNodes = new LinkedList<>(); 24 | } 25 | subNodes.add(subNode); 26 | return subNode; 27 | } 28 | 29 | public WordNode addIfNoExist(final int value, final boolean isLast) { 30 | if (subNodes == null) { 31 | return addSubNode(new WordNode(value, isLast)); 32 | } 33 | for (WordNode subNode : subNodes) { 34 | if (subNode.value == value) { 35 | if (!subNode.isLast && isLast) 36 | subNode.isLast = true; 37 | return subNode; 38 | } 39 | } 40 | return addSubNode(new WordNode(value, isLast)); 41 | } 42 | 43 | public WordNode querySub(final int value) { 44 | if (subNodes == null) { 45 | return null; 46 | } 47 | for (WordNode subNode : subNodes) { 48 | if (subNode.value == value) { 49 | return subNode; 50 | } 51 | } 52 | return null; 53 | } 54 | 55 | public boolean isLast() { 56 | return isLast; 57 | } 58 | 59 | public void setLast(boolean isLast) { 60 | this.isLast = isLast; 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return value; 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/func/ChatFunctions.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.func; 2 | 3 | import me.arasple.mc.trchat.TrChatFiles; 4 | import me.arasple.mc.trchat.func.imp.Function; 5 | import me.arasple.mc.trchat.utils.Notifys; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.configuration.MemorySection; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author Arasple 14 | * @date 2019/11/30 14:19 15 | */ 16 | public class ChatFunctions { 17 | 18 | private static List functions = new ArrayList<>(); 19 | 20 | public static List getFunctions() { 21 | return functions; 22 | } 23 | 24 | public static void loadFunctions(CommandSender... notify) { 25 | long start = System.currentTimeMillis(); 26 | functions.clear(); 27 | 28 | TrChatFiles.getFunction().getConfigurationSection("CUSTOM").getValues(false).forEach((name, funObj) -> { 29 | functions.add(new Function(name, (MemorySection) funObj)); 30 | }); 31 | 32 | Notifys.notify(notify, "PLUGIN.LOADED-FUNCTIONS", (System.currentTimeMillis() - start)); 33 | } 34 | 35 | public static Function mathFunction(String key) { 36 | return functions.stream().filter(fun -> fun.getName().equalsIgnoreCase(key)).findFirst().orElse(null); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/func/imp/Function.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.func.imp; 2 | 3 | import me.arasple.mc.trchat.chat.format.objects.JsonComponent; 4 | import org.bukkit.configuration.MemorySection; 5 | 6 | import java.util.LinkedHashMap; 7 | 8 | /** 9 | * @author Arasple 10 | * @date 2019/11/30 14:17 11 | */ 12 | public class Function { 13 | 14 | private String requirement; 15 | private String name; 16 | private String pattern; 17 | private String filterTextPattern; 18 | private JsonComponent displayJson; 19 | 20 | public Function(String requirement, String name, String pattern, String filterTextPattern, JsonComponent displayJson) { 21 | this.requirement = requirement; 22 | this.name = name; 23 | this.pattern = pattern; 24 | this.filterTextPattern = filterTextPattern; 25 | this.displayJson = displayJson; 26 | } 27 | 28 | public Function(String name, MemorySection funObj) { 29 | this(funObj.getString("requirement", null), name, funObj.getString("pattern"), funObj.getString("text-filter", null), new JsonComponent((LinkedHashMap) ((MemorySection) funObj.get("display")).getValues(false))); 30 | } 31 | 32 | public String getRequirement() { 33 | return requirement; 34 | } 35 | 36 | public void setRequirement(String requirement) { 37 | this.requirement = requirement; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | public String getPattern() { 49 | return pattern; 50 | } 51 | 52 | public void setPattern(String pattern) { 53 | this.pattern = pattern; 54 | } 55 | 56 | public String getFilterTextPattern() { 57 | return filterTextPattern; 58 | } 59 | 60 | public void setFilterTextPattern(String filterTextPattern) { 61 | this.filterTextPattern = filterTextPattern; 62 | } 63 | 64 | public JsonComponent getDisplayJson() { 65 | return displayJson; 66 | } 67 | 68 | public void setDisplayJson(JsonComponent displayJson) { 69 | this.displayJson = displayJson; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/hook/TrChatPlaceholders.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.hook; 2 | 3 | import io.izzel.taboolib.module.inject.THook; 4 | import me.arasple.mc.trchat.TrChat; 5 | import me.arasple.mc.trchat.data.Users; 6 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 7 | import org.bukkit.entity.Player; 8 | 9 | /** 10 | * @author Arasple 11 | * @date 2019/11/30 11:35 12 | */ 13 | @THook 14 | public class TrChatPlaceholders extends PlaceholderExpansion { 15 | 16 | @Override 17 | public String getIdentifier() { 18 | return "TRCHAT"; 19 | } 20 | 21 | @Override 22 | public String getAuthor() { 23 | return "ARASPLE"; 24 | } 25 | 26 | @Override 27 | public String getVersion() { 28 | return String.valueOf(TrChat.getTrVersion()); 29 | } 30 | 31 | @Override 32 | public boolean persist() { 33 | return true; 34 | } 35 | 36 | @Override 37 | public String onPlaceholderRequest(Player player, String params) { 38 | if (player == null || !player.isOnline()) { 39 | return null; 40 | } 41 | 42 | if ("FILTER".equalsIgnoreCase(params)) { 43 | return String.valueOf(Users.isFilterEnabled(player)); 44 | } 45 | return null; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/logs/ChatLogs.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.logs; 2 | 3 | import com.google.common.collect.Lists; 4 | import io.izzel.taboolib.module.inject.TFunction; 5 | import io.izzel.taboolib.module.inject.TSchedule; 6 | import io.izzel.taboolib.util.Files; 7 | import io.izzel.taboolib.util.Strings; 8 | import jdk.nashorn.internal.objects.annotations.Function; 9 | import me.arasple.mc.trchat.TrChatFiles; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.io.File; 13 | import java.text.SimpleDateFormat; 14 | import java.util.Date; 15 | import java.util.List; 16 | 17 | /** 18 | * @author Arasple 19 | * @date 2019/11/30 16:08 20 | */ 21 | public class ChatLogs { 22 | 23 | private static List waveList = Lists.newArrayList(); 24 | private static SimpleDateFormat dateFormat0 = new SimpleDateFormat("yyyy-M-dd"); 25 | private static SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-M-dd"); 26 | 27 | @TSchedule(delay = 20 * 15, period = 20 * 60 * 5, async = true) 28 | @TFunction.Cancel 29 | public static void writeToFile() { 30 | File logFile = Files.file("plugins/TrChat/logs/" + dateFormat0.format(System.currentTimeMillis()) + ".txt"); 31 | Files.write(logFile, writer -> { 32 | for (String line : waveList) { 33 | writer.write(line); 34 | writer.newLine(); 35 | } 36 | }); 37 | waveList.clear(); 38 | } 39 | 40 | public static void log(Player player, String originalMessage) { 41 | waveList.add(Strings.replaceWithOrder(TrChatFiles.getSettings().getStringColored("GENERAL.LOG", "[{0}] {1}: {2}"), 42 | dateFormat1.format(System.currentTimeMillis()), 43 | player.getName(), 44 | originalMessage 45 | )); 46 | } 47 | 48 | public static void logPrivate(String from, String to, String originalMessage) { 49 | waveList.add(Strings.replaceWithOrder(TrChatFiles.getSettings().getStringColored("GENERAL.LOG", "[{0}] {1} -> {2}: {3}"), 50 | dateFormat1.format(System.currentTimeMillis()), 51 | from, 52 | to, 53 | originalMessage 54 | )); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/menus/MenuFilterControl.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.menus; 2 | 3 | import io.izzel.taboolib.util.item.ItemBuilder; 4 | import io.izzel.taboolib.util.item.inventory.MenuBuilder; 5 | import io.izzel.taboolib.util.lite.Materials; 6 | import io.izzel.taboolib.util.lite.SoundPack; 7 | import me.arasple.mc.trchat.data.Users; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | 11 | /** 12 | * @author Arasple 13 | * @date 2019/11/30 11:40 14 | * Internal Menu For Chat Filter Control 15 | */ 16 | public class MenuFilterControl { 17 | 18 | public static final SoundPack SOUND = new SoundPack("BLOCK_NOTE_BLOCK_PLING-1-2"); 19 | 20 | public static void displayFor(Player player) { 21 | MenuBuilder.builder() 22 | .rows(5) 23 | .title("TrChat Filter") 24 | .items( 25 | "#########", 26 | "", 27 | " A ", 28 | "", 29 | "#########" 30 | ) 31 | .put('#', new ItemBuilder(Materials.CYAN_STAINED_GLASS_PANE.parseItem()).name("§3TrChat §bFilter").build()) 32 | .put('A', getToggleButton(player)) 33 | .event(e -> { 34 | if (e.getSlot() == 'A') { 35 | Users.setFilter(player, !Users.isFilterEnabled(player)); 36 | } 37 | displayFor(player); 38 | }).open(player); 39 | SOUND.play(player); 40 | } 41 | 42 | private static ItemStack getToggleButton(Player player) { 43 | ItemBuilder item; 44 | if (Users.isFilterEnabled(player)) { 45 | item = new ItemBuilder(Materials.LIME_STAINED_GLASS_PANE.parseItem()).name("§3聊天过滤器 §a√").lore("", "§7你已经开启聊天过滤器,", "§7系统将会为您过滤掉聊天", "§7内容中的敏感内容, 祝您游戏愉快", "", "§6▶ §e点击关闭此功能"); 46 | } else { 47 | item = new ItemBuilder(Materials.RED_STAINED_GLASS_PANE.parseItem()).name("§8聊天过滤器 §c×").lore("", "§7你当前已关闭聊天过滤器,", "§7系统将不会为您过滤掉聊天", "§7内容中的敏感内容...", "", "§2▶ §a点击开启此功能"); 48 | } 49 | return item.build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/nms/AbstractPacketUtils.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.nms; 2 | 3 | /** 4 | * @author Arasple 5 | * @date 2019/11/30 11:17 6 | */ 7 | public abstract class AbstractPacketUtils { 8 | 9 | /** 10 | * 过滤 IChatBaseComponent 中的敏感词 11 | * 12 | * @param component 对象 13 | * @return 过滤后的 14 | */ 15 | public abstract Object filterIChatComponent(Object component); 16 | 17 | public abstract void filterItem(Object c); 18 | 19 | public abstract void filterItemList(Object b); 20 | 21 | public abstract boolean isAvailable(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/nms/InternalPacketUtils.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.nms; 2 | 3 | import io.izzel.taboolib.module.lite.SimpleReflection; 4 | import me.arasple.mc.trchat.filter.ChatFilter; 5 | import net.minecraft.server.v1_15_R1.*; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.Material; 8 | import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.meta.ItemMeta; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | /** 17 | * @author Arasple 18 | * @date 2019/11/30 11:16 19 | */ 20 | public class InternalPacketUtils extends AbstractPacketUtils { 21 | 22 | static { 23 | SimpleReflection.checkAndSave(IChatBaseComponent.class); 24 | SimpleReflection.checkAndSave(NonNullList.class); 25 | SimpleReflection.checkAndSave(PacketPlayOutChat.class); 26 | SimpleReflection.checkAndSave(PacketPlayOutWindowItems.class); 27 | SimpleReflection.checkAndSave(PacketPlayOutSetSlot.class); 28 | } 29 | 30 | @Override 31 | public Object filterIChatComponent(Object component) { 32 | try { 33 | String raw = IChatBaseComponent.ChatSerializer.a((IChatBaseComponent) component); 34 | String filtered = ChatFilter.filter(raw).getFiltered(); 35 | return IChatBaseComponent.ChatSerializer.a(filtered); 36 | } catch (Throwable throwable) { 37 | return component; 38 | } 39 | } 40 | 41 | @Override 42 | public void filterItem(Object item) { 43 | ItemStack itemStack = CraftItemStack.asCraftMirror((net.minecraft.server.v1_15_R1.ItemStack) item); 44 | if (itemStack == null || itemStack.getType() == Material.AIR) { 45 | return; 46 | 47 | } 48 | ItemMeta meta = itemStack.getItemMeta(); 49 | if (meta != null) { 50 | if (meta.hasLore() && meta.getLore() != null && meta.getLore().size() > 0) { 51 | List lore = new ArrayList<>(); 52 | meta.getLore().forEach(l -> lore.add(ChatFilter.filter(l).getFiltered())); 53 | meta.setLore(lore); 54 | } 55 | if (meta.hasDisplayName()) { 56 | String tran = ChatFilter.filter(meta.getDisplayName()).getFiltered(); 57 | meta.setDisplayName(meta.getDisplayName().charAt(0) != ChatColor.COLOR_CHAR ? ChatColor.RESET + tran : tran); 58 | } 59 | itemStack.setItemMeta(meta); 60 | } 61 | } 62 | 63 | @Override 64 | public void filterItemList(Object items) { 65 | try { 66 | ((List) items).forEach(this::filterItem); 67 | } catch (Throwable e) { 68 | try { 69 | ((NonNullList) items).forEach(this::filterItem); 70 | } catch (Throwable e2) { 71 | Arrays.asList((ItemStack[]) items).forEach(this::filterItem); 72 | } 73 | } 74 | } 75 | 76 | @Override 77 | public boolean isAvailable() { 78 | return !SimpleReflection.getFields(PacketPlayOutChat.class).isEmpty() && SimpleReflection.getFields(PacketPlayOutChat.class).containsKey("a"); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/updater/Updater.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.updater; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import io.izzel.taboolib.module.inject.TSchedule; 6 | import io.izzel.taboolib.module.locale.TLocale; 7 | import io.izzel.taboolib.util.IO; 8 | import me.arasple.mc.trchat.TrChat; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.entity.Player; 11 | import org.bukkit.event.EventHandler; 12 | import org.bukkit.event.Listener; 13 | import org.bukkit.event.player.PlayerJoinEvent; 14 | import org.bukkit.plugin.Plugin; 15 | 16 | import java.io.BufferedInputStream; 17 | import java.io.InputStream; 18 | import java.net.URL; 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.UUID; 23 | 24 | /** 25 | * @author Arasple 26 | * @date 2019/11/29 21:04 27 | */ 28 | public class Updater implements Listener { 29 | 30 | private static boolean autoUpdate; 31 | private static List noticed = new ArrayList<>(); 32 | private static String url; 33 | private static double version; 34 | private static boolean old; 35 | private static double newVersion; 36 | 37 | public static void init(Plugin plugin) { 38 | url = "https://api.github.com/repos/Arasple/" + plugin.getName() + "/releases/latest"; 39 | version = TrChat.getTrVersion(); 40 | newVersion = version; 41 | 42 | if (!String.valueOf(version).equalsIgnoreCase(plugin.getDescription().getVersion().split("-")[0])) { 43 | TLocale.sendToConsole("ERROR.VERSION"); 44 | Bukkit.shutdown(); 45 | } 46 | Bukkit.getPluginManager().registerEvents(new Updater(), plugin); 47 | } 48 | 49 | private static void notifyOld() { 50 | if (newVersion - version >= 0.2) { 51 | int last = Math.min((int) (1 * ((newVersion - version) / 0.01)), 5); 52 | TLocale.sendToConsole("PLUGIN.UPDATER.TOO-OLD", last); 53 | try { 54 | Thread.sleep(last * 1000); 55 | } catch (InterruptedException ignored) { 56 | } 57 | } else { 58 | if (old) { 59 | TLocale.sendToConsole("PLUGIN.UPDATER.OLD", newVersion); 60 | } else { 61 | TLocale.sendToConsole("PLUGIN.UPDATER." + (version > newVersion ? "DEV" : "LATEST")); 62 | } 63 | } 64 | } 65 | 66 | @TSchedule(delay = 20, period = 10 * 60 * 20, async = true) 67 | private static void grabInfo() { 68 | if (old) { 69 | return; 70 | } 71 | String read; 72 | try (InputStream inputStream = new URL(url).openStream(); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) { 73 | read = IO.readFully(bufferedInputStream, StandardCharsets.UTF_8); 74 | JsonObject json = (JsonObject) new JsonParser().parse(read); 75 | double latestVersion = json.get("tag_name").getAsDouble(); 76 | if (latestVersion > version) { 77 | old = true; 78 | notifyOld(); 79 | } 80 | newVersion = latestVersion; 81 | } catch (Exception ignored) { 82 | } 83 | } 84 | 85 | public static boolean isAutoUpdate() { 86 | return autoUpdate; 87 | } 88 | 89 | public static boolean isOld() { 90 | return old; 91 | } 92 | 93 | public static double getNewVersion() { 94 | return newVersion; 95 | } 96 | 97 | public static double getVersion() { 98 | return version; 99 | } 100 | 101 | 102 | @EventHandler 103 | public void onJoin(PlayerJoinEvent e) { 104 | Player p = e.getPlayer(); 105 | 106 | if (old && !noticed.contains(p.getUniqueId()) && p.hasPermission("trmenu.admin")) { 107 | noticed.add(p.getUniqueId()); 108 | Bukkit.getScheduler().runTaskLaterAsynchronously(TrChat.getPlugin(), () -> TLocale.sendTo(p, "PLUGIN.UPDATER.OLD", newVersion), 1); 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Bungees.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.module.locale.TLocale; 4 | import me.arasple.mc.trchat.TrChat; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.plugin.Plugin; 8 | import org.bukkit.plugin.messaging.PluginMessageListener; 9 | 10 | import java.io.*; 11 | import java.util.Arrays; 12 | 13 | import static org.bukkit.Bukkit.getMessenger; 14 | 15 | /** 16 | * @author Arasple 17 | * @date 2019/8/4 21:23 18 | */ 19 | public class Bungees implements PluginMessageListener { 20 | 21 | private static boolean ENABLE = false; 22 | 23 | public static void init() { 24 | Plugin plugin = TrChat.getPlugin(); 25 | if (!getMessenger().isOutgoingChannelRegistered(plugin, "BungeeCord")) { 26 | getMessenger().registerOutgoingPluginChannel(plugin, "BungeeCord"); 27 | getMessenger().registerIncomingPluginChannel(plugin, "BungeeCord", new Bungees()); 28 | setEnable(Bukkit.getServer().spigot().getConfig().getBoolean("settings.bungeecord", false)); 29 | TLocale.sendToConsole(isEnable() ? "PLUGIN.REGISTERED-BUNGEE" : "PLUGIN.NONE-BUNGEE"); 30 | } 31 | } 32 | 33 | public static void sendBungeeData(Player player, String... args) { 34 | ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); 35 | DataOutputStream out = new DataOutputStream(byteArray); 36 | for (String arg : args) { 37 | try { 38 | out.writeUTF(arg); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | player.sendPluginMessage(TrChat.getPlugin(), "BungeeCord", byteArray.toByteArray()); 44 | } 45 | 46 | public static boolean isEnable() { 47 | return ENABLE; 48 | } 49 | 50 | /* 51 | GETTERS & SETTERS 52 | */ 53 | 54 | public static void setEnable(boolean enable) { 55 | Bungees.ENABLE = enable; 56 | } 57 | 58 | @Override 59 | public void onPluginMessageReceived(String channel, Player player, byte[] message) { 60 | if (channel == null || player == null || message == null) { 61 | return; 62 | } 63 | if (!"BungeeCord".equals(channel)) { 64 | return; 65 | } 66 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(message)); 67 | try { 68 | String subChannel = in.readUTF(); 69 | if ("PlayerList".equals(subChannel)) { 70 | String server = in.readUTF(); 71 | Players.setPlayers(Arrays.asList(in.readUTF().split(", "))); 72 | } 73 | } catch (IOException ignored) { 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Js.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.module.locale.TLocale; 4 | import io.izzel.taboolib.util.Strings; 5 | import io.izzel.taboolib.util.lite.Scripts; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | 9 | import javax.script.SimpleBindings; 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author Arasple 16 | * @date 2019/11/30 13:21 17 | */ 18 | public class Js { 19 | 20 | public static boolean checkCondition(Player player, String requirement) { 21 | requirement = Vars.replace(player, requirement); 22 | 23 | if (Strings.isEmpty(requirement) || "null".equalsIgnoreCase(requirement)) { 24 | return true; 25 | } 26 | 27 | Map bind = new HashMap<>(); 28 | bind.put("player", player); 29 | bind.put("bukkitServer", Bukkit.getServer()); 30 | try { 31 | return (boolean) Scripts.compile(requirement).eval(new SimpleBindings(bind)); 32 | } catch (Throwable e) { 33 | TLocale.sendTo(player, "ERROR.JS", requirement, e.getMessage(), Arrays.toString(e.getStackTrace())); 34 | TLocale.sendToConsole("ERROR.JS", requirement, e.getMessage(), Arrays.toString(e.getStackTrace())); 35 | } 36 | return false; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/MessageColors.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.entity.Player; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * @author Arasple 13 | * @date 2019/8/15 20:52 14 | */ 15 | public class MessageColors { 16 | 17 | public static final List COLOR_CODES = Arrays.stream(ChatColor.values()).map(ChatColor::getChar).collect(Collectors.toList()); 18 | 19 | private static final String COLOR_CHAR = String.valueOf(ChatColor.COLOR_CHAR); 20 | private static final String COLOR_PERMISSION_NODE = "trchat.color."; 21 | private static final String FORCE_CHAT_COLOR_PERMISSION_NODE = "trchat.color.force-defaultcolor."; 22 | 23 | public static List replaceWithPermission(Player player, List strings) { 24 | return player == null ? strings : strings.stream().map(string -> replaceWithPermission(player, string)).collect(Collectors.toList()); 25 | } 26 | 27 | public static String replaceWithPermission(Player player, String string) { 28 | if (player == null) { 29 | return string; 30 | } 31 | 32 | for (Character code : COLOR_CODES) { 33 | if (player.hasPermission(COLOR_PERMISSION_NODE + code)) { 34 | string = StringUtils.replace(string, "&" + code, COLOR_CHAR + code); 35 | } 36 | } 37 | 38 | return string; 39 | } 40 | 41 | public static ChatColor catchDefaultMessageColor(Player player, ChatColor defaultColor) { 42 | if (player.hasPermission(FORCE_CHAT_COLOR_PERMISSION_NODE + "*")) { 43 | return defaultColor; 44 | } 45 | 46 | for (Character code : COLOR_CODES) { 47 | if (player.hasPermission(FORCE_CHAT_COLOR_PERMISSION_NODE + code)) { 48 | return ChatColor.getByChar(code); 49 | } 50 | } 51 | 52 | return defaultColor; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Notifys.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.module.locale.TLocale; 4 | import org.bukkit.command.CommandSender; 5 | 6 | /** 7 | * @author Arasple 8 | * @date 2019/11/30 10:07 9 | */ 10 | public class Notifys { 11 | 12 | public static void notify(CommandSender[] senders, String path, Object... args) { 13 | for (CommandSender sender : senders) { 14 | TLocale.sendTo(sender, path, args); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/PacketUtils.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.module.inject.TInject; 4 | import me.arasple.mc.trchat.nms.AbstractPacketUtils; 5 | 6 | /** 7 | * @author Arasple 8 | * @date 2019/11/30 11:21 9 | */ 10 | public class PacketUtils { 11 | 12 | @TInject(asm = "me.arasple.mc.trchat.nms.InternalPacketUtils") 13 | private static AbstractPacketUtils packetUtils; 14 | 15 | public static AbstractPacketUtils get() { 16 | return packetUtils; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Players.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import com.google.common.collect.Lists; 4 | import io.izzel.taboolib.module.inject.TSchedule; 5 | import me.arasple.mc.trchat.TrChat; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author Arasple 13 | * @date 2019/8/4 21:28 14 | */ 15 | public class Players { 16 | 17 | private static List players = Lists.newArrayList(); 18 | 19 | @TSchedule(delay = 20) 20 | public static void startTask() { 21 | if (Bungees.isEnable()) { 22 | Bukkit.getScheduler().runTaskTimer(TrChat.getPlugin(), () -> { 23 | if (Bukkit.getOnlinePlayers().size() > 0) { 24 | Bungees.sendBungeeData(Bukkit.getOnlinePlayers().iterator().next(), "PlayerList", "ALL"); 25 | } 26 | }, 0, 60); 27 | } 28 | } 29 | 30 | /* 31 | GETTERS & SETTERS 32 | */ 33 | 34 | public static boolean isPlayerOnline(String target) { 35 | Player player = Bukkit.getPlayerExact(target); 36 | return (player != null && player.isOnline()) || players.stream().anyMatch(p -> p.equalsIgnoreCase(target)); 37 | } 38 | 39 | public static String getPlayerFullName(String target) { 40 | Player player = Bukkit.getPlayerExact(target); 41 | return (player != null && player.isOnline()) ? player.getName() : players.stream().filter(p -> p.equalsIgnoreCase(target)).findFirst().orElse(null); 42 | } 43 | 44 | public static List getPlayers() { 45 | List players = Lists.newArrayList(); 46 | players.addAll(Players.players); 47 | Bukkit.getOnlinePlayers().forEach(x -> { 48 | if (!players.contains(x.getName())) { 49 | players.add(x.getName()); 50 | } 51 | }); 52 | return players; 53 | } 54 | 55 | public static void setPlayers(List players) { 56 | Players.players = players; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Pts.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.util.Strings; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * @author Arasple 10 | * @date 2019/11/30 14:30 11 | */ 12 | public class Pts { 13 | 14 | public static String replacePattern(String string, String pattern, String textPattern, String replacement) { 15 | Matcher matcher = Pattern.compile(pattern).matcher(string); 16 | while (matcher.find()) { 17 | String str = matcher.group(); 18 | Matcher m = textPattern != null ? Pattern.compile(textPattern).matcher(str) : null; 19 | String rep = Strings.replaceWithOrder(replacement, textPattern == null ? str : m != null && m.find() ? m.group() : str); 20 | string = string.replaceAll(str, rep); 21 | } 22 | return string; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/arasple/mc/trchat/utils/Vars.java: -------------------------------------------------------------------------------- 1 | package me.arasple.mc.trchat.utils; 2 | 3 | import io.izzel.taboolib.module.inject.TSchedule; 4 | import me.arasple.mc.trchat.TrChat; 5 | import me.arasple.mc.trchat.TrChatFiles; 6 | import me.clip.placeholderapi.PlaceholderAPI; 7 | import me.clip.placeholderapi.PlaceholderAPIPlugin; 8 | import me.clip.placeholderapi.expansion.cloud.CloudExpansion; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * @author Arasple 18 | * @date 2019/11/29 21:29 19 | */ 20 | public class Vars { 21 | 22 | @TSchedule(delay = 20 * 15) 23 | public static void downloadExpansions() { 24 | downloadExpansions(TrChatFiles.getSettings().getStringList("GENERAL.DEPEND-EXPANSIONS")); 25 | } 26 | 27 | /** 28 | * 自动下载 PlaceholderAPI 拓展变量并注册 29 | * 30 | * @param expansions 拓展 31 | */ 32 | public static void downloadExpansions(List expansions) { 33 | if (PlaceholderAPIPlugin.getInstance().getExpansionCloud() == null) { 34 | return; 35 | } 36 | try { 37 | if (expansions != null && expansions.size() > 0) { 38 | if (PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansions().isEmpty()) { 39 | PlaceholderAPIPlugin.getInstance().getExpansionCloud().fetch(false); 40 | } 41 | List unInstalled = expansions.stream().filter(d -> PlaceholderAPI.getExpansions().stream().noneMatch(e -> e.getName().equalsIgnoreCase(d)) && PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(d) != null && !PlaceholderAPIPlugin.getInstance().getExpansionCloud().isDownloading(d)).collect(Collectors.toList()); 42 | if (unInstalled.size() > 0) { 43 | unInstalled.forEach(ex -> { 44 | CloudExpansion cloudExpansion = PlaceholderAPIPlugin.getInstance().getExpansionCloud().getCloudExpansion(ex); 45 | PlaceholderAPIPlugin.getInstance().getExpansionCloud().downloadExpansion(null, cloudExpansion); 46 | }); 47 | Bukkit.getScheduler().runTaskLater(TrChat.getPlugin(), () -> PlaceholderAPIPlugin.getInstance().getExpansionManager().registerAllExpansions(), 20); 48 | } 49 | } 50 | } catch (Throwable ignored) { 51 | } 52 | } 53 | 54 | /** 55 | * 使用 PlaceholderAPI 变量替换 56 | * 57 | * @param player 玩家 58 | * @param strings 内容 59 | * @return 替换后内容 60 | */ 61 | public static List replace(Player player, List strings) { 62 | List results = new ArrayList<>(); 63 | strings.forEach(str -> results.add(replace(player, str))); 64 | return results; 65 | } 66 | 67 | /** 68 | * 使用 PlaceholderAPI 变量替换 69 | * 70 | * @param player 玩家 71 | * @param string 内容 72 | * @return 替换后内容 73 | */ 74 | public static String replace(Player player, String string) { 75 | if (string == null || player == null) { 76 | return string; 77 | } 78 | if (player instanceof Player) { 79 | return PlaceholderAPI.setPlaceholders(player, string); 80 | } 81 | return string; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: TrChat 2 | main: me.arasple.mc.trchat.TrChatBungee 3 | 4 | version: ${version} 5 | 6 | description: Advanced Minecraft Chat Control 7 | 8 | author: Arasple -------------------------------------------------------------------------------- /src/main/resources/channels.yml: -------------------------------------------------------------------------------- 1 | # When enables, every message will be forwarded to Bungee servers 2 | FORCE-GLOBAL: false 3 | 4 | # Messages starting with this will be forwarded to Bungee servers 5 | FORCE-GLOBAL-PREFIX: '!all' -------------------------------------------------------------------------------- /src/main/resources/filter.yml: -------------------------------------------------------------------------------- 1 | CLOUD-THESAURUS: 2 | ENABLE: false 3 | WHITELIST: [] 4 | 5 | LOCAL: 6 | - 'NMSL' 7 | - 'fuck' 8 | - 'shit' 9 | 10 | IGNORED-PUNCTUATIONS: ['!','.',',','#','$','%','&','*','(',')','|','?','/','@','"','\',';','[',']','{','}','+','~','-','_','=','^','<','>',' ',' ','!','。',',','¥','(',')','?','、','“','‘',';','【','】','——','……','《','》','\\'] 11 | REPLACEMENT: '*' -------------------------------------------------------------------------------- /src/main/resources/formats.yml: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # 3 | # ___________ _________ .__ __ 4 | # \__ __________\_ ___ \| |__ _____ _/ |_ 5 | # | | \_ __ / \ \/| | \\__ \\ __\ 6 | # | | | | \\ \___| Y \/ __ \| | 7 | # |____| |__| \______ |___| (____ |__| 8 | # \/ \/ \/ TrChat Formats 9 | # 10 | # Advanced Json format chat 11 | # 12 | # FORMAT-EXAMPLE: 13 | # example-part: # id 14 | # text: 'ABC' # display text 15 | # hover: '%player_name%' # hover display 16 | # command: '/tpa %player_name%' # click execute command 17 | # 18 | ###################################################################### 19 | 20 | NORMAL: 21 | # Requirement to use this format, for example: 'player.isOp()' 22 | - requirement: null 23 | 24 | # Message part 25 | msg: 26 | # Default chat color 27 | # (when the player doesn't have any permission of trchat.color.force-defaultcolor.) 28 | default-color: '7' 29 | hover: '&7Date: %server_time_h:mm:ss a%' 30 | 31 | # Custom Json Format Parts (Before Message Part) 32 | parts: 33 | world: 34 | text: '&8[&3%player_world%&8]' 35 | hover: |- 36 | &r 37 | &8▪ &7Location: &3%player_world%&7, &2%player_x%/%player_y%/%player_z% 38 | &r 39 | &6▶ &eClick here to send a teleport request 40 | &r 41 | command: '/tpa %player_name%' 42 | # (Optional) Requirement to display this JSON part for the player 43 | requirement: null 44 | part-before-player: 45 | text: ' ' 46 | player: 47 | text: '&7%player_name%' 48 | hover: |- 49 | &r 50 | &8▪ &7Ping: &3%player_ping% Ms 51 | &8▪ &7Health: &c%player_health_rounded% ❤ 52 | &r 53 | &6▶ &eClick to chat with me 54 | &r 55 | suggest: '/msg %player_name% ' 56 | part-before-msg: 57 | text: '&7: ' 58 | 59 | ###################################################################### 60 | # 61 | # PRIVATE FORMAT (SENDER) 62 | # 63 | ###################################################################### 64 | PRIVATE_SEND: 65 | - requirement: null 66 | parts: 67 | sender: 68 | text: '&8[&a%player_name% &7➦ %toplayer_name%&3&8] ' 69 | msg: 70 | default-color: 'f' 71 | hover: '&7Date: %server_time_h:mm:ss a%' 72 | 73 | ###################################################################### 74 | # 75 | # PRIVATE FORMAT (RECEIVER) 76 | # 77 | ###################################################################### 78 | PRIVATE_RECEIVE: 79 | - requirement: null 80 | parts: 81 | receive: 82 | text: '&8[&6%player_name% &7➥ &2%toplayer_name%&8] ' 83 | msg: 84 | default-color: 'f' 85 | hover: '&7Date: %server_time_h:mm:ss a%' 86 | 87 | ###################################################################### 88 | # 89 | # GLOBAL CHAT FORMAT 90 | # 91 | ###################################################################### 92 | GLOBAL: 93 | - requirement: null 94 | parts: 95 | server: 96 | text: '&8[&3%server_name%&8] ' 97 | hover: |- 98 | &7Server &3%server_name% 99 | &r 100 | &7Version: &6%server_version% 101 | &7Online: &a%server_online%&7/&2%server_max_players% 102 | &7TPS: &b%server_tps% 103 | player: 104 | text: '&e%player_name%&8: ' 105 | msg: 106 | default-color: 'f' 107 | hover: '&7Date: %server_time_h:mm:ss a%' 108 | 109 | ###################################################################### 110 | # 111 | # STAFF CHANNEL CHAT 112 | # 113 | ###################################################################### 114 | STAFF: 115 | - requirement: null 116 | parts: 117 | staff: 118 | text: '&8[&cSTAFF&8] ' 119 | hover: |- 120 | &7Server &3%server_name% 121 | &r 122 | &7Version: &6%server_version% 123 | &7Online: &a%server_online%&7/&2%server_max_players% 124 | &7TPS: &b%server_tps% 125 | &7 126 | &a▶ &2Click to connect to this server 127 | command: '/server %server_name%' 128 | player: 129 | text: '&e%player_name%&8: ' 130 | msg: 131 | default-color: 'a' 132 | hover: '&7Date: %server_time_h:mm:ss a%' 133 | -------------------------------------------------------------------------------- /src/main/resources/function.yml: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # 3 | # ___________ _________ .__ __ 4 | # \__ __________\_ ___ \| |__ _____ _/ |_ 5 | # | | \_ __ / \ \/| | \\__ \\ __\ 6 | # | | | | \\ \___| Y \/ __ \| | 7 | # |____| |__| \______ |___| (____ |__| 8 | # \/ \/ \/ TrChat Function 9 | # 10 | # 11 | ###################################################################### 12 | 13 | # General (Plugin's internal functions) 14 | GENERAL: 15 | # Command Controller 16 | COMMAND-CONTROLLER: 17 | ENABLE: true 18 | # WHITELIST or BLACKLIST 19 | TYPE: BLACKLIST 20 | # Bypass permission 21 | BYPASS: 'trchat.admin' 22 | # The list of the command you want to control 23 | # Support regex match, dont need '/' 24 | # 25 | # is a tag, by using it, 26 | # the command includes the argument will have to complete match 27 | # 28 | # Example (blackList): 29 | # - 'help' player can not execute any commands' first label is "help" 30 | # - 'help' player can not execute "help", but can execute "help 1", "help 2" 31 | LIST: 32 | - 'ver(sion)?(s)?' 33 | - 'help(s)?' 34 | - 'arasple' 35 | # Display item in chat 36 | ITEM-SHOW: 37 | ENABLE: true 38 | COOLDOWNS: 30 39 | FORMAT: '&8[&3{0} &bx{1}&8]' 40 | KEYS: 41 | - '%i' 42 | - '%item' 43 | - '%i%' 44 | - '%item%' 45 | - '[i]' 46 | - '[item]' 47 | # Mention (@At) someone in chat 48 | MENTION: 49 | ENABLE: true 50 | SELF-MENTION: false 51 | FORMAT: '&r &a@&2{0}&r ' 52 | COOLDOWNS: 30 53 | NOTIFY: true 54 | 55 | # Custom Functions 56 | # Match chat text, format into JSON 57 | CUSTOM: 58 | 59 | # 示例 —— QQ 分享 60 | shareQQ: 61 | # 满足条件表达式才能使用这个功能 62 | requirement: '1!=0' 63 | # 匹配表达式 64 | # 示例模块的表达式部分来自互联网 65 | pattern: 'QQ( )?[1-9]([0-9]{5,11})' 66 | # 变量 {0} 是按下方表达式提取后的内容, 可以不配置此项 67 | text-filter: '[1-9]([0-9]{5,11})' 68 | # 自定义显示 JSON 组件 69 | display: 70 | text: '&8[&3&lQQ&8]' 71 | hover: 72 | - '' 73 | - '&3QQ: &b{0}' 74 | - '' 75 | - '&7这是一个 QQ 账号,' 76 | - '&7你可以点击此项快速打开聊天' 77 | - '' 78 | - '&8[&c!&8] &7请勿进行任何金钱交易' 79 | - '&8[&c!&8] &7交友需谨慎' 80 | url: 'https://wpa.qq.com/msgrd?v=3&uin={0}&site=qq&menu=yes' 81 | 82 | # 示例 —— B站视频 分享 83 | shareBilibili: 84 | pattern: 'av( )?[1-9]([0-9]{0,9})' 85 | text-filter: '[1-9]([0-9]{0,9})' 86 | # 自定义显示 JSON 组件 87 | display: 88 | text: '&8[&f&lBilibili&8]' 89 | hover: 90 | - '' 91 | - '&7这可能是一个 Bilibili 视频,' 92 | - '&7点击即可访问' 93 | - '' 94 | - '&3Av号: &b{0}' 95 | url: 'https://www.bilibili.com/video/av{0}' 96 | 97 | # 示例 —— 防止玩家暴露、分享真实手机号 98 | hidePhoneNumber: 99 | pattern: '((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\d{8}' 100 | display: 101 | text: '&8[&c&m-&8]' 102 | hover: 103 | - '&7该内容疑似为手机号码,' 104 | - '&7已自动屏蔽隐藏.' 105 | - '' 106 | - '&8[&c!&8] &7请勿分享任何隐私信息' 107 | 108 | # 示例 —— 隐藏身份证 109 | hideIDCardNumber: 110 | pattern: '([1-9]\d{5}[12]\d{3}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])\d{3}[0-9xX])' 111 | display: 112 | text: '&8[&c&m-&8]' 113 | hover: 114 | - '&7该内容疑似为身份证号,' 115 | - '&7已自动屏蔽隐藏.' 116 | - '' 117 | - '&8[&c!&8] &7请勿分享任何隐私信息' 118 | - '&7严重者可能处于禁言/封禁' 119 | 120 | # 示例 —— 高亮 IP 121 | glowIP: 122 | pattern: '(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)' 123 | display: 124 | text: '&e&n{0}' 125 | hover: 126 | - '&7这是一个 IP地址' 127 | - '&7点击复制!' 128 | suggest: '{0}' 129 | -------------------------------------------------------------------------------- /src/main/resources/lang/en_US.yml: -------------------------------------------------------------------------------- 1 | PLUGIN: 2 | LOADED: 3 | - '' 4 | - '&3Tr&bChat &7loading...' 5 | - '' 6 | LOADED-CHAT-FORMATS: '&8[&3Tr&bChat&8] &bCHAT &8| &3Loaded chat formats... &8[{0} Ms]' 7 | LOADED-FUNCTIONS: '&8[&3Tr&bChat&8] &bCHAT &8| &3Loaded chat functions... &8[{0} Ms]' 8 | LOADED-FILTER-LOCAL: '&8[&3Tr&bChat&8] &aFILTER &8| &3Loaded local filter words &b{0} &3.' 9 | LOADED-FILTER-CLOUD: '&8[&3Tr&bChat&8] &aFILTER &8| &3Loaded cloud filter words &a{0} &3. &8[{1} - {2}]' 10 | RELOADED: '&8[&3Tr&bChat&8] &7Settings changed, reloaded...' 11 | FAILED-LOAD-FILTER-CLOUD: '&8[&3Tr&bChat&8] &8Unable to update filter thesaurus from the cloud...' 12 | REGISTERED-BUNGEE: '&8[&3Tr&bChat&8] &6Hooked &eBungee &6chat support.' 13 | NONE-BUNGEE: '&8[&3Tr&bChat&8] &7No Bungee support currently' 14 | DEPEND: 15 | DOWNLOAD: '&8[&3Tr&bChat&8] &eDEPEND &8| &7Downloading depend plugin &f{0} &7...' 16 | INSTALL: '&8[&3Tr&bChat&8] &eDEPEND &8| &7Successfullly downloaded &3{0} &7, restarting the server...' 17 | INSTALL-FAILED: '&8[&3Tr&bChat&8] &eDEPEND &8| &7Failed to install depend plugin &c{0} &7...' 18 | ENABLED: 19 | - '&8[&3Tr&bChat&8] &bINFO &8| &3Loaded. TrChat &av{0} &3has been enabled.' 20 | DISABLED: 21 | - '&8[&3Tr&bChat&8] &bINFO &8| &7Disabled. Thanks for using :)' 22 | COMMAND-TITLE: '&3「&8&m--------------------------------------------------&3」' 23 | UPDATE-NOTIFY: 24 | LATEST: '&8[&3Tr&bChat&8] &2You''re running the latest version of &3TrChat&a.' 25 | DEV: '&8[&3Tr&bChat&8] &6You''re running the development version of &3TrChat&6.' 26 | TOO-OLD: 27 | - "&8--------------------------------------------------" 28 | - "&r" 29 | - "&8# &4The version of &cTrChat &4you are running is too old" 30 | - "&8# &4Please update to the latest version in time!" 31 | - "&8# &r" 32 | - "&8# &4SpigotMC: &chttps://www.spigotmc.org/members/435323" 33 | - "&8# &r" 34 | - "&8# &r" 35 | - "&8# &4The server will start in &c&l{0} secs &4..." 36 | - "&r" 37 | - "&8--------------------------------------------------" 38 | HEADER: 39 | - '' 40 | - '§3--------------------------------------------------' 41 | - '&7▪ &3TrChat &aUpdate Notify &8{0} &7➦ &8{1}' 42 | FOOTER: 43 | - '&7▪ &2Github: &a&nhttps://github.com/Arasple/TrChat/releases/latest' 44 | - '§3--------------------------------------------------' 45 | 46 | DEBUG: 47 | 'ON': 48 | - ==: TITLE 49 | title: '&7&lDEBUG MODE' 50 | subtitle: '&3&lis now &a&lEnabled&3&l.' 51 | - ==: SOUND 52 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 53 | 'OFF': 54 | - ==: TITLE 55 | title: '&7&lDEBUG MODE' 56 | subtitle: '&3&lis now &c&lDisabled&3&l.' 57 | - ==: SOUND 58 | sound: 'BLOCK_ANVIL_LAND-1-0' 59 | 60 | ERROR: 61 | VERSION: '&8[&3Tr&bMenu&8] &cERROR &8| &7Plugin version error. Please re-download the plugin.' 62 | JS: 63 | - '&8[&3Tr&bMenu&8] &cERROR &8| &cExpression error: &2{0}' 64 | - '&6{1}' 65 | - '&8{2}' 66 | 67 | GENERAL: 68 | TOO-LONG: '&8[&3Tr&bChat&8] &7Message is too long. &8[&6{0}&8/&2{1}&8]' 69 | TOO-SIMILAR: '&8[&3Tr&bChat&8] &7Please do not repeat your message.' 70 | NO-PERMISSION: '&8[&3Tr&bChat&8] &7You do not have enough permission to do this.' 71 | NOT-PLAYER: '&8[&3Tr&bChat&8] &cYou must be a player to do this.' 72 | 73 | COOLDOWNS: 74 | CHAT: 75 | - ==: ACTION 76 | text: '&7&lYou need to wait after &6{0} secs &7&lbefore chatting again.' 77 | - ==: SOUND 78 | sound: 'ENTITY_ITEM_BREAK-1-2' 79 | ITEM-SHOW: 80 | - ==: ACTION 81 | text: '&3&lYou need to wait after &a{0} secs &3&lbefore showing your item again.' 82 | - ==: SOUND 83 | sound: 'ENTITY_ITEM_BREAK-1-0' 84 | 85 | COMMAND-CONTROLLER: 86 | DENY: '&c&lYou can not use this command.' 87 | 88 | MENTIONS: 89 | NOTIFY: 90 | - ==: ACTION 91 | text: '&d&l&k|&r &3&lPlayer &a&l{0} &3&lmentioned you in the chat &d&l&k|' 92 | - ==: SOUND 93 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 94 | 95 | PRIVATE-MESSAGE: 96 | SPY-FORMAT: '&8[&3Spy&8] &6{0} &2-> &3{1}&f: &7{2}' 97 | SPY: 98 | 'ON': 99 | - ==: ACTION 100 | text: '&c&l&k|&r &a&lSpy Mode Enabled &c&l&k|' 101 | - ==: SOUND 102 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 103 | 'OFF': 104 | - ==: ACTION 105 | text: '&c&l&k|&r &c&lSpy Mode Disabled &c&l&k|' 106 | - ==: SOUND 107 | sound: 'BLOCK_ANVIL_LAND-1-0' 108 | NOT-PLAYER: '&8[&3Tr&bChat&8] &cYou must be a player to do this' 109 | NO-PLAYER: 110 | - ==: ACTION 111 | text: '&3&lPlayer name required' 112 | - ==: SOUND 113 | sound: 'ENTITY_ITEM_BREAK-1-0' 114 | NO-MESSAGE: 115 | - ==: ACTION 116 | text: '&7&lChat context required' 117 | - ==: SOUND 118 | sound: 'ENTITY_ITEM_BREAK-1-0' 119 | NOT-EXIST: '&8[&3Tr&bChat&8] &7The target player is not online or does not exist.' 120 | RECEIVE: 121 | - ==: ACTION 122 | text: '&8[ &a! &8] &3You''ve received a message from &a{0} &3.' 123 | - ==: SOUND 124 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-2' 125 | 126 | GLOBAL-MESSAGE: 127 | NOT-PLAYER: '&8[&3Tr&bChat&8] &cYou must be a player to do this.' 128 | NOT-ENABLE: '&8[&3Tr&bChat&8] &7Unabled to connect &eBungeeCord &7.' 129 | NO-MESSAGE: 130 | - ==: ACTION 131 | text: '&7&lMessage required' 132 | - ==: SOUND 133 | sound: 'ENTITY_ITEM_BREAK-1-0' 134 | 135 | STAFF-CHANNEL: 136 | NOT-PLAYER: '&8[&3Tr&bChat&8] &cYou must be a player to do this.' 137 | NO-MESSAGE: 138 | - ==: ACTION 139 | text: '&7&lMessage required' 140 | - ==: SOUND 141 | sound: 'ENTITY_ITEM_BREAK-1-0' 142 | JOIN: 143 | - ==: ACTION 144 | text: '&a&lStaff chat mode enabled.' 145 | - ==: TEXT 146 | text: '&8[&3Tr&bChat&8] &3You''ve been into the staff chat channel, Re-type the command to exit' 147 | - ==: SOUND 148 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 149 | QUIT: 150 | - ==: ACTION 151 | text: '&3&lStaff chat mode disabled.' 152 | - ==: TEXT 153 | text: '&8[&3Tr&bChat&8] &7You''ve left the staff chat channel...' 154 | - ==: SOUND 155 | sound: 'BLOCK_ANVIL_LAND-1-0' 156 | -------------------------------------------------------------------------------- /src/main/resources/lang/zh_CN.yml: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # 3 | # ___________ _________ .__ __ 4 | # \__ __________\_ ___ \| |__ _____ _/ |_ 5 | # | | \_ __ / \ \/| | \\__ \\ __\ 6 | # | | | | \\ \___| Y \/ __ \| | 7 | # |____| |__| \______ |___| (____ |__| 8 | # \/ \/ \/ 9 | # 10 | # TrChat 的语言文件, 基于 TLocale 11 | # 12 | # 如何自定义更强的消息?查阅文档:https://bkm016.github.io/TabooLib/#/tlocale 13 | # 所有消息均支持 BOOK / SOUND / TITLE / ACTIONBAR / BOSSBAR /JSON 等 14 | # 15 | ###################################################################### 16 | 17 | PLUGIN: 18 | LOADED-CHAT-FORMATS: '&8[&3Tr&bChat&8] &bCHAT &8| &3成功载入聊天格式... &8[{0} Ms]' 19 | LOADED-FUNCTIONS: '&8[&3Tr&bChat&8] &bCHAT &8| &3成功载入自定义聊天功能... &8[{0} Ms]' 20 | LOADED-FILTER-LOCAL: '&8[&3Tr&bChat&8] &aFILTER &8| &3已载入本地敏感词库总计 &b{0} &3个.' 21 | LOADED-FILTER-CLOUD: '&8[&3Tr&bChat&8] &aFILTER &8| &3成功从云端更新敏感词汇总计 &a{0} &3个. &8[{1} - {2}]' 22 | RELOADED: '&8[&3Tr&bChat&8] &7监听到配置文件改动, 已自动载入相关配置等...' 23 | FAILED-LOAD-FILTER-CLOUD: '&8[&3Tr&bChat&8] &8无法从云端更新敏感词库......' 24 | REGISTERED-BUNGEE: '&8[&3Tr&bChat&8] &6已支持 &eBungee &6通讯.' 25 | NONE-BUNGEE: '&8[&3Tr&bChat&8] &7当前无 Bungee 支持.' 26 | DEPEND: 27 | DOWNLOAD: '&8[&3Tr&bChat&8] &eDEPEND &8| &7正在下载前置插件 &f{0} &7...' 28 | INSTALL: '&8[&3Tr&bChat&8] &eDEPEND &8| &7已成功下载前置 &3{0} &7, 即将重启服务端完成安装...' 29 | INSTALL-FAILED: '&8[&3Tr&bChat&8] &eDEPEND &8| &7自动下载前置 &c{0} &7过程出错, 请手动安装, 服务器即将停止...' 30 | LOADED: 31 | - '' 32 | - '&3Tr&bChat &7正在载入中...' 33 | - '' 34 | ENABLED: 35 | - '&8[&3Tr&bChat&8] &bINFO &8| &3加载完毕. TrChat &av{0} &3现已启用, 敬请使用.' 36 | DISABLED: 37 | - '&8[&3Tr&bChat&8] &bINFO &8| &7感谢使用本插件, 您在论坛为本插件评分即是对作者最大的鼓励!' 38 | COMMAND-TITLE: '&3「&8&m--------------------------------------------------&3」' 39 | UPDATE-NOTIFY: 40 | LATEST: '&8[&3Tr&bChat&8] &8你正在运行最新版的 TrChat.' 41 | DEV: '&8[&3Tr&bChat&8] &7你正在运行开发版本的 TrChat, 请及时反馈漏洞!' 42 | TOO-OLD: 43 | - "&8--------------------------------------------------" 44 | - "&r" 45 | - "&8# &4您所运行的 &cTrChat &4版本过旧, 可能潜在很多漏洞" 46 | - "&8# &4请及时更新到最新版本以便获得更好的插件体验!" 47 | - "&8# &r" 48 | - "&8# &4Mcbbs: &chttps://www.mcbbs.net/thread-903335-1-1.html" 49 | - "&8# &r" 50 | - "&8# &r" 51 | - "&8# &4服务器将在 &c&l{0} secs &4后继续启动..." 52 | - "&r" 53 | - "&8--------------------------------------------------" 54 | HEADER: 55 | - '' 56 | - '§3--------------------------------------------------' 57 | - '&7▪ &3TrChat &a更新通知 &8{0} &7➦ &8{1}' 58 | - '' 59 | - '&7▪ &e内容: ' 60 | FOOTER: 61 | - '' 62 | - '&7▪ &2Github下载: &a&nhttps://github.com/Arasple/TrChat/releases/latest' 63 | - '§3--------------------------------------------------' 64 | DEBUG: 65 | 'ON': 66 | - ==: TITLE 67 | title: '&7&l调试模式' 68 | subtitle: '&3&l当前已 &a&l开启&3&l.' 69 | - ==: SOUND 70 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 71 | 'OFF': 72 | - ==: TITLE 73 | title: '&7&l调试模式' 74 | subtitle: '&3&l当前已 &c&l关闭&3&l.' 75 | - ==: SOUND 76 | sound: 'BLOCK_ANVIL_LAND-1-0' 77 | 78 | ERROR: 79 | VERSION: '&8[&3Tr&bMenu&8] &cERROR &8| &7检测版本号时发生异常... 关闭服务器!' 80 | JS: 81 | - '&8[&3Tr&bMenu&8] &cERROR &8| &c表达式计算时出错: &2{0}' 82 | - '&6{1}' 83 | - '&8{2}' 84 | 85 | GENERAL: 86 | TOO-LONG: '&8[&3Tr&bChat&8] &7你的聊天内容过长. &8[&6{0}&8/&2{1}&8]' 87 | TOO-SIMILAR: '&8[&3Tr&bChat&8] &7你的两次聊天内容过于相似, 请勿复读.' 88 | NO-PERMISSION: '&8[&3Tr&bChat&8] &7你没有足够的权限执行此操作.' 89 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必须是一个玩家才能执行此操作.' 90 | 91 | COOLDOWNS: 92 | CHAT: 93 | - ==: ACTION 94 | text: '&7&l您需要等待聊天冷却 &6{0}s &7&l后才能聊天, 请勿频繁刷屏.' 95 | - ==: SOUND 96 | sound: 'ENTITY_ITEM_BREAK-1-2' 97 | ITEM-SHOW: 98 | - ==: ACTION 99 | text: '&3&l您需要等待冷却 &a{0}s &3&l后才能再次展示物品.' 100 | - ==: SOUND 101 | sound: 'ENTITY_ITEM_BREAK-1-0' 102 | 103 | COMMAND-CONTROLLER: 104 | DENY: '&c&l你不能使用该命令...' 105 | 106 | MENTIONS: 107 | NOTIFY: 108 | - ==: ACTION 109 | text: '&d&l&k|&r &3&l玩家 &a&l{0} &3&l在聊天中At了你 &d&l&k|' 110 | - ==: SOUND 111 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 112 | 113 | PRIVATE-MESSAGE: 114 | SPY-FORMAT: '&8[&3监听&8] &6{0} &2-> &3{1}&f: &7{2}' 115 | SPY: 116 | 'ON': 117 | - ==: ACTION 118 | text: '&c&l&k|&r &a&l开启监听私聊模式 &c&l&k|' 119 | - ==: SOUND 120 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 121 | 'OFF': 122 | - ==: ACTION 123 | text: '&c&l&k|&r &c&l关闭监听私聊模式 &c&l&k|' 124 | - ==: SOUND 125 | sound: 'BLOCK_ANVIL_LAND-1-0' 126 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必须是一个玩家才能执行此操作.' 127 | NO-PLAYER: 128 | - ==: ACTION 129 | text: '&3&l请提供一名私聊的对象' 130 | - ==: SOUND 131 | sound: 'ENTITY_ITEM_BREAK-1-0' 132 | NO-MESSAGE: 133 | - ==: ACTION 134 | text: '&7&l请提供私聊内容' 135 | - ==: SOUND 136 | sound: 'ENTITY_ITEM_BREAK-1-0' 137 | NOT-EXIST: '&8[&3Tr&bChat&8] &7目标玩家未在线或不存在.' 138 | RECEIVE: 139 | - ==: ACTION 140 | text: '&8[ &a! &8] &3你收到来自 &a{0} &3的一条私聊消息' 141 | - ==: SOUND 142 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-2' 143 | 144 | GLOBAL-MESSAGE: 145 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必须是一个玩家才能执行此操作.' 146 | NOT-ENABLE: '&8[&3Tr&bChat&8] &7未启用 &eBungeeCord &7支持, 暂不支持喊话.' 147 | NO-MESSAGE: 148 | - ==: ACTION 149 | text: '&7&l你必须提供喊话内容' 150 | - ==: SOUND 151 | sound: 'ENTITY_ITEM_BREAK-1-0' 152 | 153 | STAFF-CHANNEL: 154 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必须是一个玩家才能执行此操作.' 155 | NO-MESSAGE: 156 | - ==: ACTION 157 | text: '&7&l你必须提供聊天内容' 158 | - ==: SOUND 159 | sound: 'ENTITY_ITEM_BREAK-1-0' 160 | JOIN: 161 | - ==: ACTION 162 | text: '&a&l已加入到管理频道聊天' 163 | - ==: TEXT 164 | text: '&8[&3Tr&bChat&8] &3你已经加入到管理频道, 再次输入相同命令即可退出.' 165 | - ==: SOUND 166 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 167 | QUIT: 168 | - ==: ACTION 169 | text: '&3&l离开管理频道聊天' 170 | - ==: TEXT 171 | text: '&8[&3Tr&bChat&8] &7你已离开管理频道, 但仍会收到管频的消息.' 172 | - ==: SOUND 173 | sound: 'BLOCK_ANVIL_LAND-1-0' 174 | -------------------------------------------------------------------------------- /src/main/resources/lang/zh_TW.yml: -------------------------------------------------------------------------------- 1 | PLUGIN: 2 | LOADED-CHAT-FORMATS: '&8[&3Tr&bChat&8] &bCHAT &8| &3成功載入聊天格式... &8[{0} Ms]' 3 | LOADED-FUNCTIONS: '&8[&3Tr&bChat&8] &bCHAT &8| &3成功載入自定義聊天功能... &8[{0} Ms]' 4 | LOADED-FILTER-LOCAL: '&8[&3Tr&bChat&8] &aFILTER &8| &3已載入本地敏感詞庫總計 &b{0} &3個.' 5 | LOADED-FILTER-CLOUD: '&8[&3Tr&bChat&8] &aFILTER &8| &3成功從雲端更新敏感詞彙總計 &a{0} &3個. &8[{1} - {2}]' 6 | RELOADED: '&8[&3Tr&bChat&8] &7監聽到配置文件改動, 已自動載入相關配置等...' 7 | FAILED-LOAD-FILTER-CLOUD: '&8[&3Tr&bChat&8] &8無法從雲端更新敏感詞庫......' 8 | REGISTERED-BUNGEE: '&8[&3Tr&bChat&8] &6已支持 &eBungee &6通訊.' 9 | NONE-BUNGEE: '&8[&3Tr&bChat&8] &7當前無 Bungee 支持.' 10 | DEPEND: 11 | DOWNLOAD: '&8[&3Tr&bChat&8] &eDEPEND &8| &7正在下載前置插件 &f{0} &7...' 12 | INSTALL: '&8[&3Tr&bChat&8] &eDEPEND &8| &7已成功下載前置 &3{0} &7, 即將重啓服務端完成安裝...' 13 | INSTALL-FAILED: '&8[&3Tr&bChat&8] &eDEPEND &8| &7自動下載前置 &c{0} &7過程出錯, 請手動安裝, 服務器即將停止...' 14 | LOADED: 15 | - '' 16 | - '&3Tr&bChat &7正在載入中...' 17 | - '' 18 | ENABLED: 19 | - '&8[&3Tr&bChat&8] &bINFO &8| &3加載完畢. TrChat &av{0} &3現已啓用, 敬請使用.' 20 | DISABLED: 21 | - '&8[&3Tr&bChat&8] &bINFO &8| &7感謝使用本插件, 您在論壇爲本插件評分即是對作者最大的鼓勵!' 22 | COMMAND-TITLE: '&3「&8&m--------------------------------------------------&3」' 23 | UPDATE-NOTIFY: 24 | LATEST: '&8[&3Tr&bChat&8] &8你正在運行最新版的 TrChat.' 25 | DEV: '&8[&3Tr&bChat&8] &7你正在運行開發版本的 TrChat, 請及時反饋漏洞!' 26 | TOO-OLD: 27 | - "&8--------------------------------------------------" 28 | - "&r" 29 | - "&8# &4您所運行的 &cTrChat &4版本過舊, 可能潛在很多漏洞" 30 | - "&8# &4請及時更新到最新版本以便獲得更好的插件體驗!" 31 | - "&8# &r" 32 | - "&8# &4Mcbbs: &chttps://www.mcbbs.net/thread-903335-1-1.html" 33 | - "&8# &r" 34 | - "&8# &r" 35 | - "&8# &4服務器將在 &c&l{0} secs &4後繼續啓動..." 36 | - "&r" 37 | - "&8--------------------------------------------------" 38 | HEADER: 39 | - '' 40 | - '§3--------------------------------------------------' 41 | - '&7▪ &3TrChat &a更新通知 &8{0} &7➦ &8{1}' 42 | - '' 43 | - '&7▪ &e內容: ' 44 | FOOTER: 45 | - '' 46 | - '&7▪ &2Github下載: &a&nhttps://github.com/Arasple/TrChat/releases/latest' 47 | - '§3--------------------------------------------------' 48 | DEBUG: 49 | 'ON': 50 | - ==: TITLE 51 | title: '&7&l調試模式' 52 | subtitle: '&3&l當前已 &a&l開啓&3&l.' 53 | - ==: SOUND 54 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 55 | 'OFF': 56 | - ==: TITLE 57 | title: '&7&l調試模式' 58 | subtitle: '&3&l當前已 &c&l關閉&3&l.' 59 | - ==: SOUND 60 | sound: 'BLOCK_ANVIL_LAND-1-0' 61 | 62 | ERROR: 63 | VERSION: '&8[&3Tr&bMenu&8] &cERROR &8| &7檢測版本號時發生異常... 關閉服務器!' 64 | JS: 65 | - '&8[&3Tr&bMenu&8] &cERROR &8| &c表達式計算時出錯: &2{0}' 66 | - '&6{1}' 67 | - '&8{2}' 68 | 69 | GENERAL: 70 | TOO-LONG: '&8[&3Tr&bChat&8] &7你的聊天內容過長. &8[&6{0}&8/&2{1}&8]' 71 | TOO-SIMILAR: '&8[&3Tr&bChat&8] &7你的兩次聊天內容過于相似, 請勿複讀.' 72 | NO-PERMISSION: '&8[&3Tr&bChat&8] &7你沒有足夠的權限執行此操作.' 73 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必須是一個玩家才能執行此操作.' 74 | 75 | COOLDOWNS: 76 | CHAT: 77 | - ==: ACTION 78 | text: '&7&l您需要等待聊天冷卻 &6{0}s &7&l後才能聊天, 請勿頻繁刷屏.' 79 | - ==: SOUND 80 | sound: 'ENTITY_ITEM_BREAK-1-2' 81 | ITEM-SHOW: 82 | - ==: ACTION 83 | text: '&3&l您需要等待冷卻 &a{0}s &3&l後才能再次展示物品.' 84 | - ==: SOUND 85 | sound: 'ENTITY_ITEM_BREAK-1-0' 86 | 87 | COMMAND-CONTROLLER: 88 | DENY: '&c&l你不能使用该命令...' 89 | 90 | MENTIONS: 91 | NOTIFY: 92 | - ==: ACTION 93 | text: '&d&l&k|&r &3&l玩家 &a&l{0} &3&l在聊天中At了你 &d&l&k|' 94 | - ==: SOUND 95 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 96 | 97 | PRIVATE-MESSAGE: 98 | SPY-FORMAT: '&8[&3監聽&8] &6{0} &2-> &3{1}&f: &7{2}' 99 | SPY: 100 | 'ON': 101 | - ==: ACTION 102 | text: '&c&l&k|&r &a&l開啓監聽私聊模式 &c&l&k|' 103 | - ==: SOUND 104 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 105 | 'OFF': 106 | - ==: ACTION 107 | text: '&c&l&k|&r &c&l關閉監聽私聊模式 &c&l&k|' 108 | - ==: SOUND 109 | sound: 'BLOCK_ANVIL_LAND-1-0' 110 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必須是一個玩家才能執行此操作.' 111 | NO-PLAYER: 112 | - ==: ACTION 113 | text: '&3&l請提供一名私聊的對象' 114 | - ==: SOUND 115 | sound: 'ENTITY_ITEM_BREAK-1-0' 116 | NO-MESSAGE: 117 | - ==: ACTION 118 | text: '&7&l請提供私聊內容' 119 | - ==: SOUND 120 | sound: 'ENTITY_ITEM_BREAK-1-0' 121 | NOT-EXIST: '&8[&3Tr&bChat&8] &7目標玩家未在線或不存在.' 122 | RECEIVE: 123 | - ==: ACTION 124 | text: '&8[ &a! &8] &3你收到來自 &a{0} &3的一條私聊消息' 125 | - ==: SOUND 126 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-2' 127 | 128 | GLOBAL-MESSAGE: 129 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必須是一個玩家才能執行此操作.' 130 | NOT-ENABLE: '&8[&3Tr&bChat&8] &7未啓用 &eBungeeCord &7支持, 暫不支持喊話.' 131 | NO-MESSAGE: 132 | - ==: ACTION 133 | text: '&7&l你必須提供喊話內容' 134 | - ==: SOUND 135 | sound: 'ENTITY_ITEM_BREAK-1-0' 136 | 137 | STAFF-CHANNEL: 138 | NOT-PLAYER: '&8[&3Tr&bChat&8] &c你必須是一個玩家才能執行此操作.' 139 | NO-MESSAGE: 140 | - ==: ACTION 141 | text: '&7&l你必須提供聊天內容' 142 | - ==: SOUND 143 | sound: 'ENTITY_ITEM_BREAK-1-0' 144 | JOIN: 145 | - ==: ACTION 146 | text: '&a&l已加入到管理頻道聊天' 147 | - ==: TEXT 148 | text: '&8[&3Tr&bChat&8] &3你已經加入到管理頻道, 再次輸入相同命令即可退出.' 149 | - ==: SOUND 150 | sound: 'BLOCK_NOTE_BLOCK_PLING-1-0' 151 | QUIT: 152 | - ==: ACTION 153 | text: '&3&l離開管理頻道聊天' 154 | - ==: TEXT 155 | text: '&8[&3Tr&bChat&8] &7你已離開管理頻道, 但仍會收到管頻的消息.' 156 | - ==: SOUND 157 | sound: 'BLOCK_ANVIL_LAND-1-0' 158 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: TrChat 2 | main: me.arasple.mc.trchat.TrChat 3 | 4 | api-version: 1.13 5 | version: ${version} 6 | 7 | description: Advanced Minecraft Chat Control 8 | 9 | built-time: ${built} 10 | 11 | softdepend: 12 | - PlaceholderAPI 13 | 14 | author: Arasple 15 | -------------------------------------------------------------------------------- /src/main/resources/settings.yml: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # 3 | # ___________ _________ .__ __ 4 | # \__ __________\_ ___ \| |__ _____ _/ |_ 5 | # | | \_ __ / \ \/| | \\__ \\ __\ 6 | # | | | | \\ \___| Y \/ __ \| | 7 | # |____| |__| \______ |___| (____ |__| 8 | # \/ \/ \/ TrChat 1.7 9 | # ------ by Arasple 10 | # 11 | ###################################################################### 12 | 13 | # Locale Priority 14 | LOCALE-PRIORITY: 15 | - en_US 16 | - zh_CN 17 | - zh_TW 18 | 19 | # General options 20 | GENERAL: 21 | DEBUG: false 22 | DISABLED-WORLDS: [PUT_YOUR_WORLD_NAME_HERE, ANOTHER_WORLD] 23 | LOG: '[{0}] {1}: {2}' 24 | DEPEND-EXPANSIONS: 25 | - 'player' 26 | - 'server' 27 | - 'vault' 28 | - 'multiverse' 29 | 30 | # Chat Color 31 | # Perm Node: trchat.color. 32 | # 33 | # Enable options 34 | CHAT-COLOR: 35 | CHAT: true 36 | SIGN: true 37 | ANVIL: true 38 | BOOK: true 39 | 40 | # Chat Control 41 | CHAT-CONTROL: 42 | # Anti similar text repeat (Maximum similarity 0-1) 43 | ANTI-REPEAT: 0.85 44 | # Chat cooldown 45 | COOLDOWN: 2.0 46 | # Chat text length limit 47 | LENGTH-LIMIT: 100 --------------------------------------------------------------------------------