第28批:英文聊天机翻双语展示 + 域名过滤按illegal_domains.txt后缀判定修误伤
任务1(新增)英文聊天机翻: - 所有消息一律不拦截原样放行,仅「整条消息不含中文字符」时异步机翻 - 原始消息由MC正常广播后,追加[机翻]双语展示行 - 中文夹杂英文(如「用water免伤」)原样输出不机翻 - 复用领地系统YOUDAO→JIANXIN→GOOGLE降级链,MaterialTranslator新增 translateChatText公开入口+独立缓存(上限500) - 专用2线程池隔离HTTP阻塞,10秒/人冷却防刷,onDisable关闭线程池 任务2(修复)域名过滤误伤: - extractUrls裸域名token必须命中illegal_domains.txt后缀(含中文后缀) 才收录,显式协议URL(含://)无条件收录——修「今天.天气真好啊」整句误判 - shouldBlockUrl同步改后缀判定,不再对点分隔无条件拦截 - illegal_domains.txt补全TLD+中文后缀节(工作区遗留扩展一并入库)
This commit is contained in:
@@ -1038,7 +1038,19 @@ public class ChatFilterManager {
|
||||
"[\\s.,;!?,。;!?、)》」』】"
|
||||
+ "\\]\\[\\((]+$",
|
||||
"");
|
||||
if (!url.isEmpty()) urls.add(url);
|
||||
if (url.isEmpty()) continue;
|
||||
// ★ 28批修复(用户口径「接入illegal_domains.txt,命中的中英文后缀一律拦截」):
|
||||
// 显式协议URL(含 ://,如 http://1.2.3.4/x、https://a.com)无条件收录;
|
||||
// 裸域名/下划线 token 必须命中非法域名后缀(含中文后缀:中国/网络/公司…
|
||||
// 与英文后缀 com/cn/net…,均来自 illegal_domains.txt)才收录。
|
||||
// —— 修「今天.天气真好啊」被 URL_PATTERN 点分隔支整句匹配为域名而误伤。
|
||||
if (!url.contains("://")) {
|
||||
String probe = url;
|
||||
int slash = probe.indexOf('/');
|
||||
if (slash > 0) probe = probe.substring(0, slash);
|
||||
if (!containsIllegalDomain(probe)) continue;
|
||||
}
|
||||
if (!urls.contains(url)) urls.add(url);
|
||||
}
|
||||
// ★ 变形URL加强(2026-10-08):点周带空白(baidu . com)压缩空白后再判定;
|
||||
// 仅当顶级域命中「非法域名后缀」才收录,避免误伤 "OK. Good" 之类正常英文句子
|
||||
@@ -1090,17 +1102,24 @@ public class ChatFilterManager {
|
||||
|
||||
/**
|
||||
* 判断URL是否应被拦截
|
||||
* 下划线分隔的字符串(如 player_name),必须命中非法域名后缀才拦截
|
||||
* 点分隔或标准URL正常拦截
|
||||
* ★ 28批修复(用户口径):接入 illegal_domains.txt,命中的中英文后缀一律拦截,其余放行。
|
||||
* - 白名单优先放行;
|
||||
* - 显式协议URL(含 ://,如 http://a.com、http://1.2.3.4/x)非白名单一律拦截
|
||||
* (IP直链无后缀可查,保持反广告强度);
|
||||
* - 裸域名/下划线 token(如 xxx.com、xxx_com、今天.天气真好啊):
|
||||
* 必须命中非法域名后缀(英文 com/cn/net… 或中文 中国/网络/公司…)才拦截 —— 修误伤。
|
||||
*/
|
||||
public boolean shouldBlockUrl(String url) {
|
||||
if (isWhitelisted(url)) return false;
|
||||
// 下划线分隔 → 检查是否非法域名,不在列表则跳过(合法玩家名下划线误匹配)
|
||||
if (url.contains("_")) {
|
||||
return containsIllegalDomain(url);
|
||||
// 显式协议URL → 非白名单一律拦截(原行为保持)
|
||||
if (url.contains("://")) {
|
||||
return true;
|
||||
}
|
||||
// 点分隔或标准URL正常拦截
|
||||
return true;
|
||||
// 裸域名/下划线token → 必须命中非法域名后缀才拦截
|
||||
String probe = url;
|
||||
int slash = probe.indexOf('/');
|
||||
if (slash > 0) probe = probe.substring(0, slash);
|
||||
return containsIllegalDomain(probe);
|
||||
}
|
||||
|
||||
public boolean isLikelyDomain(String url) {
|
||||
|
||||
@@ -1012,6 +1012,9 @@ public class Main extends JavaPlugin
|
||||
}
|
||||
if (db != null) db.close();
|
||||
if (garbage != null) garbage.close();
|
||||
if (chatTransPool != null) {
|
||||
chatTransPool.shutdownNow();
|
||||
}
|
||||
if (questTracker != null)
|
||||
questTracker.shutdown();
|
||||
if (homeMgr != null)
|
||||
@@ -6273,7 +6276,11 @@ public class Main extends JavaPlugin
|
||||
// ===== URL检测 =====
|
||||
List<String> urls =
|
||||
chatFilter.extractUrls(msg);
|
||||
if (urls.isEmpty()) return;
|
||||
if (urls.isEmpty()) {
|
||||
// 28批:无链接的正常消息放行 → 触发英文聊天机翻(不拦截,追加双语展示)
|
||||
tryScheduleChatTranslation(p, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤白名单 + 下划线误匹配(player_name 不在非法域名列表则跳过)
|
||||
List<String> blocked = new ArrayList<>();
|
||||
@@ -6282,7 +6289,11 @@ public class Main extends JavaPlugin
|
||||
blocked.add(url);
|
||||
}
|
||||
}
|
||||
if (blocked.isEmpty()) return;
|
||||
if (blocked.isEmpty()) {
|
||||
// 28批:URL 全部放行(白名单/未命中非法后缀)→ 同样触发英文聊天机翻
|
||||
tryScheduleChatTranslation(p, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消消息
|
||||
e.setCancelled(true);
|
||||
@@ -6362,6 +6373,68 @@ public class Main extends JavaPlugin
|
||||
|
||||
// ==================== 聊天过滤辅助方法 ====================
|
||||
|
||||
// ===== 英文聊天机翻(28批新增) =====
|
||||
/** 同一玩家机翻冷却(毫秒),防刷屏触发大量 API 请求 */
|
||||
private final java.util.Map<String, Long> chatTransCd = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
/** 机翻专用线程池(HTTP 阻塞最多 ~9s,不能占用 ForkJoinPool.commonPool) */
|
||||
private final java.util.concurrent.ExecutorService chatTransPool =
|
||||
java.util.concurrent.Executors.newFixedThreadPool(2, r -> {
|
||||
Thread t = new Thread(r, "Sdf1-chat-translator");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
/**
|
||||
* 英文聊天机翻调度(28批,用户定稿口径):
|
||||
* - 所有消息一律不拦截、原样放行(交 MC 自行处理),本方法只做「追加双语展示」;
|
||||
* - 仅「整条消息不含任何中文字符」的英文消息(如 what are you doing?)才机翻,
|
||||
* 原始消息由 MC 正常广播之后,再追加一条「[机翻] 原文 → 译文」;
|
||||
* - 中文消息或中文夹杂英文(如「你需要用water来免伤」)一律原样输出、不做机翻
|
||||
* —— 脱离第三方模型无法判定英文单词在语境中的真实含义,强行翻反而出错。
|
||||
* 异步:HTTP 翻译在线程池执行(每源3s超时,YOUDAO→JIANXIN→GOOGLE 降级),
|
||||
* 结果回主线程广播;任何异常静默吞掉,绝不影响聊天主流程。
|
||||
*/
|
||||
private void tryScheduleChatTranslation(Player p, String msg) {
|
||||
try {
|
||||
if (p == null || msg == null) return;
|
||||
String text = msg.trim();
|
||||
if (text.isEmpty() || text.length() > 200) return;
|
||||
// 整条消息必须「不含任何中文字符」才机翻(含中文=原样输出)
|
||||
boolean hasChinese = false;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c >= '\u4e00' && c <= '\u9fff') { hasChinese = true; break; }
|
||||
}
|
||||
if (hasChinese) return;
|
||||
// 至少要有一个英文字母(纯数字/符号无翻译意义)
|
||||
boolean hasLetter = false;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { hasLetter = true; break; }
|
||||
}
|
||||
if (!hasLetter) return;
|
||||
// 冷却防刷(10秒/人)
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = chatTransCd.get(p.getName());
|
||||
if (last != null && now - last < 10_000L) return;
|
||||
chatTransCd.put(p.getName(), now);
|
||||
|
||||
final String original = text;
|
||||
final String sender = p.getName();
|
||||
java.util.concurrent.CompletableFuture.supplyAsync(
|
||||
() -> MaterialTranslator.translateChatText(original), chatTransPool)
|
||||
.thenAccept(zh -> {
|
||||
if (zh == null || zh.isEmpty()) return;
|
||||
// 切主线程追加「机翻」双语展示(原始消息已由 MC 自行广播)
|
||||
Bukkit.getScheduler().runTask(this, () ->
|
||||
Bukkit.broadcastMessage("§8[机翻] §7" + sender + ": §f"
|
||||
+ original + " §7→ §a" + zh));
|
||||
});
|
||||
} catch (Throwable ignored) {
|
||||
// 机翻任何异常都不影响聊天
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤内容中的链接(用于告示牌、编辑书等非聊天场景)
|
||||
* @return 是否有违规链接
|
||||
|
||||
@@ -309,6 +309,35 @@ public class MaterialTranslator {
|
||||
*/
|
||||
public static void clearCache() {
|
||||
cache.clear();
|
||||
chatCache.clear();
|
||||
}
|
||||
|
||||
// ==================== 聊天整句机翻(28批新增) ====================
|
||||
|
||||
/** 聊天机翻独立缓存(与材料名缓存隔离),上限保护防内存膨胀 */
|
||||
private static final ConcurrentHashMap<String, String> chatCache = new ConcurrentHashMap<>();
|
||||
private static final int CHAT_CACHE_MAX = 500;
|
||||
|
||||
/**
|
||||
* 聊天整句机翻(英→中)。复用 translateWithFallback 的
|
||||
* YOUDAO→JIANXIN→GOOGLE 降级链(与领地系统同一套 API 流程)。
|
||||
* 结果独立缓存(上限500条,满则整体清空重来),不污染材料名缓存。
|
||||
* 同步阻塞 HTTP(每源 3s 超时),调用方必须放在异步线程执行。
|
||||
* 返回 null 表示翻译失败或译文无意义(如与原文相同)。
|
||||
*/
|
||||
public static String translateChatText(String text) {
|
||||
if (text == null) return null;
|
||||
String key = text.trim();
|
||||
if (key.isEmpty()) return null;
|
||||
String hit = chatCache.get(key);
|
||||
if (hit != null) return hit;
|
||||
String zh = translateWithFallback(key);
|
||||
if (zh != null && !zh.isEmpty() && !zh.equals(key)) {
|
||||
if (chatCache.size() >= CHAT_CACHE_MAX) chatCache.clear();
|
||||
chatCache.put(key, zh);
|
||||
return zh;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,3 +33,426 @@ party
|
||||
fun
|
||||
zone
|
||||
space
|
||||
name
|
||||
pro
|
||||
aero
|
||||
coop
|
||||
museum
|
||||
jobs
|
||||
travel
|
||||
asia
|
||||
cat
|
||||
edu
|
||||
gov
|
||||
int
|
||||
mil
|
||||
ac
|
||||
ad
|
||||
ae
|
||||
af
|
||||
ag
|
||||
ai
|
||||
al
|
||||
am
|
||||
ao
|
||||
aq
|
||||
ar
|
||||
as
|
||||
at
|
||||
au
|
||||
aw
|
||||
ax
|
||||
az
|
||||
ba
|
||||
bb
|
||||
bd
|
||||
be
|
||||
bf
|
||||
bg
|
||||
bh
|
||||
bi
|
||||
bj
|
||||
bm
|
||||
bn
|
||||
bo
|
||||
br
|
||||
bs
|
||||
bt
|
||||
bw
|
||||
by
|
||||
bz
|
||||
ca
|
||||
cd
|
||||
cg
|
||||
ch
|
||||
ci
|
||||
ck
|
||||
cl
|
||||
cm
|
||||
cr
|
||||
cu
|
||||
cv
|
||||
cy
|
||||
cz
|
||||
de
|
||||
dj
|
||||
dk
|
||||
dm
|
||||
do
|
||||
dz
|
||||
ec
|
||||
ee
|
||||
eg
|
||||
er
|
||||
es
|
||||
et
|
||||
eu
|
||||
fi
|
||||
fj
|
||||
fk
|
||||
fm
|
||||
fo
|
||||
fr
|
||||
gd
|
||||
ge
|
||||
gf
|
||||
gg
|
||||
gh
|
||||
gi
|
||||
gl
|
||||
gm
|
||||
gn
|
||||
gp
|
||||
gq
|
||||
gr
|
||||
gs
|
||||
gt
|
||||
gu
|
||||
gw
|
||||
gy
|
||||
hk
|
||||
hn
|
||||
hr
|
||||
ht
|
||||
hu
|
||||
id
|
||||
ie
|
||||
il
|
||||
im
|
||||
in
|
||||
iq
|
||||
ir
|
||||
is
|
||||
it
|
||||
je
|
||||
jm
|
||||
jo
|
||||
jp
|
||||
ke
|
||||
kg
|
||||
kh
|
||||
ki
|
||||
km
|
||||
kn
|
||||
kp
|
||||
kr
|
||||
kw
|
||||
ky
|
||||
kz
|
||||
la
|
||||
lb
|
||||
lc
|
||||
li
|
||||
lk
|
||||
lr
|
||||
ls
|
||||
lt
|
||||
lu
|
||||
lv
|
||||
ly
|
||||
ma
|
||||
mc
|
||||
md
|
||||
me
|
||||
mg
|
||||
mh
|
||||
mk
|
||||
mm
|
||||
mn
|
||||
mo
|
||||
mp
|
||||
mq
|
||||
mr
|
||||
ms
|
||||
mt
|
||||
mu
|
||||
mv
|
||||
mw
|
||||
mx
|
||||
my
|
||||
mz
|
||||
na
|
||||
nc
|
||||
ne
|
||||
nf
|
||||
ng
|
||||
ni
|
||||
nl
|
||||
no
|
||||
np
|
||||
nr
|
||||
nu
|
||||
nz
|
||||
om
|
||||
pa
|
||||
pe
|
||||
pf
|
||||
pg
|
||||
ph
|
||||
pk
|
||||
pl
|
||||
pm
|
||||
pn
|
||||
pr
|
||||
ps
|
||||
pt
|
||||
pw
|
||||
py
|
||||
qa
|
||||
re
|
||||
ro
|
||||
rs
|
||||
rw
|
||||
sa
|
||||
sb
|
||||
sc
|
||||
sd
|
||||
se
|
||||
sg
|
||||
sh
|
||||
si
|
||||
sk
|
||||
sl
|
||||
sm
|
||||
sn
|
||||
so
|
||||
sr
|
||||
ss
|
||||
st
|
||||
sv
|
||||
sx
|
||||
sy
|
||||
sz
|
||||
tc
|
||||
td
|
||||
tf
|
||||
tg
|
||||
th
|
||||
tj
|
||||
tl
|
||||
tm
|
||||
tn
|
||||
to
|
||||
tt
|
||||
tw
|
||||
tz
|
||||
ug
|
||||
uk
|
||||
um
|
||||
us
|
||||
uy
|
||||
uz
|
||||
va
|
||||
vc
|
||||
ve
|
||||
vg
|
||||
vi
|
||||
vn
|
||||
vu
|
||||
wf
|
||||
ye
|
||||
yt
|
||||
za
|
||||
zm
|
||||
zw
|
||||
ac.uk
|
||||
co.uk
|
||||
org.uk
|
||||
gov.uk
|
||||
edu.au
|
||||
ac.nz
|
||||
co.nz
|
||||
org.nz
|
||||
gov.au
|
||||
co.jp
|
||||
ne.jp
|
||||
or.jp
|
||||
ed.jp
|
||||
ac.jp
|
||||
go.jp
|
||||
co.kr
|
||||
or.kr
|
||||
re.kr
|
||||
go.kr
|
||||
ac.kr
|
||||
ne.kr
|
||||
com.cn
|
||||
net.cn
|
||||
org.cn
|
||||
gov.cn
|
||||
edu.cn
|
||||
ac.cn
|
||||
mil.cn
|
||||
com.hk
|
||||
net.hk
|
||||
org.hk
|
||||
idv.hk
|
||||
com.tw
|
||||
net.tw
|
||||
org.tw
|
||||
gov.tw
|
||||
edu.tw
|
||||
com.sg
|
||||
net.sg
|
||||
org.sg
|
||||
gov.sg
|
||||
edu.sg
|
||||
per.sg
|
||||
com.my
|
||||
net.my
|
||||
org.my
|
||||
gov.my
|
||||
edu.my
|
||||
per.my
|
||||
com.vn
|
||||
net.vn
|
||||
org.vn
|
||||
gov.vn
|
||||
edu.vn
|
||||
ac.vn
|
||||
com.ph
|
||||
net.ph
|
||||
org.ph
|
||||
gov.ph
|
||||
edu.ph
|
||||
com.pk
|
||||
net.pk
|
||||
org.pk
|
||||
gov.pk
|
||||
edu.pk
|
||||
com.bd
|
||||
net.bd
|
||||
org.bd
|
||||
gov.bd
|
||||
edu.bd
|
||||
com.lk
|
||||
net.lk
|
||||
gov.lk
|
||||
ac.lk
|
||||
com.np
|
||||
net.np
|
||||
org.np
|
||||
gov.np
|
||||
com.mm
|
||||
com.kh
|
||||
com.kg
|
||||
com.kz
|
||||
com.ge
|
||||
com.am
|
||||
com.az
|
||||
com.by
|
||||
com.km
|
||||
com.sc
|
||||
co.id
|
||||
web.id
|
||||
co.th
|
||||
or.th
|
||||
ac.th
|
||||
go.th
|
||||
net.th
|
||||
com.mu
|
||||
org.mu
|
||||
co.za
|
||||
org.za
|
||||
web.za
|
||||
com.eg
|
||||
net.eg
|
||||
com.ma
|
||||
net.ma
|
||||
org.ma
|
||||
co.ke
|
||||
or.ke
|
||||
ac.ke
|
||||
co.tz
|
||||
or.tz
|
||||
ac.tz
|
||||
co.ug
|
||||
or.ug
|
||||
com.sn
|
||||
net.sn
|
||||
org.sn
|
||||
co.rw
|
||||
ac.rw
|
||||
com.cm
|
||||
co.bi
|
||||
co.dz
|
||||
com.dj
|
||||
co.so
|
||||
co.tg
|
||||
co.np
|
||||
ac.cy
|
||||
com.cy
|
||||
co.il
|
||||
org.il
|
||||
net.il
|
||||
ac.il
|
||||
gov.il
|
||||
co.ma
|
||||
com.kw
|
||||
net.kw
|
||||
org.kw
|
||||
com.om
|
||||
net.om
|
||||
com.ps
|
||||
com.sy
|
||||
com.jo
|
||||
net.jo
|
||||
com.lb
|
||||
net.lb
|
||||
com.tn
|
||||
com.tj
|
||||
com.tf
|
||||
app
|
||||
dev
|
||||
page
|
||||
shop
|
||||
work
|
||||
website
|
||||
wiki
|
||||
blog
|
||||
live
|
||||
best
|
||||
cool
|
||||
red
|
||||
icu
|
||||
one
|
||||
ltd
|
||||
# ===== 中文域名(真实可注册,按最后一段匹配)=====
|
||||
# ICANN 官方中文 TLD
|
||||
中国
|
||||
网络
|
||||
公司
|
||||
我爱你
|
||||
# .cn 注册局中文通用域名
|
||||
集团
|
||||
商城
|
||||
网店
|
||||
在线
|
||||
游戏
|
||||
网址
|
||||
网站
|
||||
商店
|
||||
企业
|
||||
娱乐
|
||||
移动
|
||||
中文网
|
||||
|
||||
Reference in New Issue
Block a user