@@ -280,10 +280,30 @@ open class Channel(
280280 }
281281 }
282282 if (! player.hasPermission(" trchat.bypass.repeat" )) {
283- val lastMessage = player.session.lastPublicMessage
283+ val session = player.session
284+ val lastMessage = session.lastPublicMessage
284285 if (Settings .chatSimilarity > 0 && Settings .chatSimilarity <= 1 && Strings .similarDegree(lastMessage, message) >= Settings .chatSimilarity) {
285- player.sendLang(" General-Too-Similar" )
286- return false
286+ val period = Settings .chatSimilarityPeriod.get()?.takeIf { it > 0 } ? : 60000L
287+ val now = System .currentTimeMillis()
288+ if (session.similarPeriodStart == 0L || now - session.similarPeriodStart >= period) {
289+ session.similarPeriodStart = now
290+ session.similarCountInPeriod = 0
291+ }
292+ if (session.similarCountInPeriod >= Settings .chatSimilarityMaxPerPeriod) {
293+ player.sendLang(" General-Too-Similar" )
294+ return false
295+ }
296+ session.similarCountInPeriod++
297+ }
298+ }
299+ if (! player.hasPermission(" trchat.bypass.duplicate" )) {
300+ val maxRepeat = Settings .chatDuplicatePhraseMaxRepeat
301+ if (maxRepeat > 0 && message.length >= 2 ) {
302+ val whitelist = Settings .chatDuplicatePhraseWhitelist.toSet()
303+ if (maxConsecutiveRepeat(message, whitelist) > maxRepeat) {
304+ player.sendLang(" General-Too-Duplicate" )
305+ return false
306+ }
287307 }
288308 }
289309 if (! player.hasPermission(" trchat.bypass.chatcd" )) {
@@ -296,6 +316,24 @@ open class Channel(
296316 if (Function .functions.any { ! it.checkCooldown(player, message) }) {
297317 return false
298318 }
319+ // 消息发送频率检查
320+ if (! player.hasPermission(" trchat.bypass.highfrequency" )) {
321+ val max = Settings .chatHighFrequencyMaxPerPeriod
322+ if (max > 0 ) {
323+ val session = player.session
324+ val period = Settings .chatHighFrequencyPeriod.get()?.takeIf { it > 0 } ? : 60000L
325+ val now = System .currentTimeMillis()
326+ if (session.totalPeriodStart == 0L || now - session.totalPeriodStart >= period) {
327+ session.totalPeriodStart = now
328+ session.totalCountInPeriod = 0
329+ }
330+ if (session.totalCountInPeriod >= max) {
331+ player.sendLang(" General-Too-Frequent" )
332+ return false
333+ }
334+ session.totalCountInPeriod++
335+ }
336+ }
299337 player.updateCooldown(CooldownType .CHAT , Settings .chatCooldown.get())
300338 return true
301339 }
@@ -347,4 +385,53 @@ open class Channel(
347385 }
348386 }
349387 }
388+ }
389+
390+ // 检查重复的子字符串并统计
391+ private fun maxConsecutiveRepeat (message : String , whitelist : Set <String >): Int {
392+ val n = message.length
393+ if (n < 2 ) return 1
394+ var max = 1
395+ val checkWhitelist = whitelist.isNotEmpty()
396+ var i = 0
397+ while (i < n) {
398+ val maxLen = (n - i) / 2
399+ var len = 1
400+ while (len <= maxLen) {
401+ if (checkWhitelist && isWhitelistedUnit(message, i, len, whitelist)) {
402+ len++
403+ continue
404+ }
405+ var count = 1
406+ var j = i + len
407+ while (j + len <= n && message.regionMatches(j, message, j - len, len)) {
408+ count++
409+ j + = len
410+ }
411+ if (count > max) max = count
412+ len++
413+ }
414+ i++
415+ }
416+ return max
417+ }
418+
419+ // 判断重复单元是否被白名单豁免
420+ private fun isWhitelistedUnit (message : String , start : Int , len : Int , whitelist : Set <String >): Boolean {
421+ for (w in whitelist) {
422+ val wl = w.length
423+ if (wl == 0 || len < wl || len % wl != 0 ) continue
424+ val repeat = len / wl
425+ var ok = true
426+ var k = 0
427+ while (k < repeat) {
428+ if (! message.regionMatches(start + k * wl, w, 0 , wl)) {
429+ ok = false
430+ break
431+ }
432+ k++
433+ }
434+ if (ok) return true
435+ }
436+ return false
350437}
0 commit comments