SensitiveWordProxy.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. local DebugProxy = requireProxy("DebugProxy")
  2. local SensitiveWordProxy = class("SensitiveWordProxy", DebugProxy)
  3. SensitiveWordProxy.NAME = global.ProxyTable.SensitiveWordProxy
  4. local utf8 = require("util/utf8")
  5. local tostring = tostring
  6. local tinsert = table.insert
  7. local sfind = string.find
  8. local sgsub = string.gsub
  9. local sbyte = string.byte
  10. local slen = string.len
  11. local ssub = string.sub
  12. ------------------------------------------------------------
  13. -- 简单正则匹配,匹配出所有网址
  14. local function findSensitivePattren(str)
  15. local items = {}
  16. while true do
  17. local pattern = { sfind(str, "(%w+%.%a+)") }
  18. if not pattern[3] then
  19. break
  20. end
  21. tinsert(items, {b = pattern[1], c = pattern[2] - pattern[1]+1, w = pattern[3]})
  22. local p = ssub(str, 1, pattern[1]-1)
  23. local s = ssub(str, pattern[2]+1, slen(str))
  24. str = p .. s
  25. end
  26. return #items > 0, items
  27. end
  28. -- 字符串匹配,非正常匹配字符
  29. local function fixMalformedPattern(v)
  30. v = sgsub(v, "%%", "%%%%")
  31. v = sgsub(v, "%.", "%%.")
  32. v = sgsub(v, "%(", "%%(")
  33. v = sgsub(v, "%)", "%%)")
  34. v = sgsub(v, "%+", "%%+")
  35. v = sgsub(v, "%-", "%%-")
  36. v = sgsub(v, "%*", "%%*")
  37. v = sgsub(v, "%?", "%%?")
  38. v = sgsub(v, "%[", "%%[")
  39. v = sgsub(v, "%]", "%%]")
  40. v = sgsub(v, "%^", "%%^")
  41. v = sgsub(v, "%$", "%%$")
  42. return v
  43. end
  44. -- 结束标记,使用数字作为Key,解决字符作为Key产生的无法匹配问题
  45. local endFlag = 0
  46. -- 是否是特殊字符,在处理敏感字时跳过夹在中间的特殊字符
  47. local charaterHash = {}
  48. local charaterList = {
  49. "`","~", "!","!", "@","@", "$","¥", "%", "^","…", "&", "*", "(", ")", "_","-", "+","=", "\\", "/", "?","?", "[","]", " ", " ", "\t", "\n", "\r", "<", ">", "'", "\"", "—",
  50. "#", ",", ",", ".", "。", ":",";", ":", ";", "�",
  51. }
  52. for i, v in ipairs(charaterList) do
  53. charaterHash[v] = 1
  54. end
  55. local function isCharaters(s)
  56. if charaterHash[s] == 1 then
  57. return true
  58. end
  59. return false
  60. end
  61. ------------------------------------------------------------
  62. -- 字典树
  63. -- 字母转小写
  64. -- 排除特殊字符,中间插入特殊字符先清特殊字符再匹配
  65. ------------------------------------------------------------
  66. ------------------------------------------------------------
  67. -- 字典树
  68. local TrieNode = class("TrieNode")
  69. function TrieNode:ctor()
  70. self.subNodes = {}
  71. end
  72. function TrieNode:addSubNode(k, subNode)
  73. self.subNodes[k] = subNode
  74. end
  75. function TrieNode:getSubNode(k)
  76. return self.subNodes[k]
  77. end
  78. ---
  79. local Trie = class("Trie")
  80. function Trie:ctor()
  81. self.root = TrieNode.new()
  82. end
  83. function Trie:add(word)
  84. -- 转换为小写
  85. word = string.lower(word)
  86. local utf8len = utf8:length(word)
  87. local currNode = self.root
  88. for i = 1, utf8len do
  89. local s = utf8:sub(word, i, 1)
  90. -- 特殊字符
  91. if isCharaters(s) then
  92. else
  93. local subNode = currNode:getSubNode(s)
  94. if subNode == nil then
  95. subNode = TrieNode.new()
  96. currNode:addSubNode(s, subNode)
  97. end
  98. currNode = subNode
  99. end
  100. end
  101. currNode:addSubNode(endFlag, TrieNode.new())
  102. end
  103. function Trie:search(word, isEasy)
  104. -- 转换为小写
  105. word = string.lower(word)
  106. local items = {}
  107. local utf8len = utf8:length(word)
  108. local index = 1
  109. while true do
  110. local numChars = self:search_sub(word, index, utf8len)
  111. if numChars > 0 then
  112. tinsert(items, {b = index, c = numChars, w = utf8:sub(word, index, numChars)})
  113. end
  114. -- 尽量少匹配
  115. if isEasy and #items == 1 then
  116. break
  117. end
  118. if index > utf8len then
  119. break
  120. end
  121. if numChars > 0 then
  122. index = index + numChars
  123. else
  124. index = index + 1
  125. end
  126. end
  127. return #items > 0, items
  128. end
  129. function Trie:search_sub(word, startIndex, utf8len)
  130. local numChars = 0
  131. local complete = false
  132. local currNode = self.root
  133. for i = startIndex, utf8len do
  134. local s = utf8:sub(word, i, 1)
  135. numChars = numChars + 1
  136. -- 是特殊字符
  137. if not (isCharaters(s)) then
  138. -- 不存在子节点
  139. local subNode = currNode:getSubNode(s)
  140. if subNode == nil then
  141. break
  142. end
  143. -- 完整结束了
  144. if (subNode:getSubNode(endFlag)) then
  145. complete = true
  146. break
  147. end
  148. currNode = subNode
  149. end
  150. end
  151. if not complete then
  152. numChars = 0
  153. end
  154. return numChars
  155. end
  156. ------------------------------------------------------------
  157. function SensitiveWordProxy:ctor()
  158. SensitiveWordProxy.super.ctor(self)
  159. self._words_trie = Trie.new()
  160. self._talk_charaters = {}
  161. self._name_charaters = {}
  162. end
  163. function SensitiveWordProxy:lazyInit()
  164. if self._isInited then
  165. return nil
  166. end
  167. self._isInited = true
  168. -- 基础配置表
  169. local config = requireGameConfig("cfg_sensitive_normal.lua")
  170. for k, v in pairs(config) do
  171. self._words_trie:add(v)
  172. end
  173. -- 自定义敏感字
  174. local filePath = "data_config/sensitive_words.txt"
  175. local text = ""
  176. if global.FileUtilCtl:isFileExist(filePath) then
  177. local function callback(dataTable)
  178. if dataTable[1] and dataTable[1] ~= "" then
  179. text = string.trim(dataTable[1])
  180. if text ~= "" then
  181. self._words_trie:add(text)
  182. end
  183. end
  184. end
  185. LoadTxt(filePath, nil, callback)
  186. end
  187. -- 聊天 额外屏蔽
  188. self._talk_charaters = { "\\", "¥", "%", "\t", "\n", "\r", "'", "\"", "卍" }
  189. -- 取名 额外屏蔽
  190. self._name_charaters = {
  191. "\\", "/", "¥", "%", "(", ")", "+", "-", "*", "?", "[", "]", "^", "$", " ", " ", "\t", "\n", "\r", "<", ">", "!",
  192. "@", "&", "=", "'", "\"", "_", "#", ";", ":", "【", "】", "“", "”", "。", ",", "《", "》", "?", "、", "!","卍","卐"
  193. }
  194. end
  195. ----------------------------------------------------------------------
  196. -- 是否包含敏感字,敏感级别=取名
  197. function SensitiveWordProxy:IsHaveSensitiveName(str)
  198. self:lazyInit()
  199. local found = false
  200. local items = {}
  201. -- 特殊字符匹配
  202. if not found then
  203. for i, v in ipairs(self._name_charaters) do
  204. local s = fixMalformedPattern(v)
  205. if sfind(str, s, 1, true) ~= nil then
  206. found = true
  207. break
  208. end
  209. end
  210. end
  211. -- 字典树匹配
  212. if not found then
  213. found, items = self._words_trie:search(str, true)
  214. end
  215. -- 正则匹配
  216. if not found then
  217. found, items = findSensitivePattren(str)
  218. end
  219. return found
  220. end
  221. function SensitiveWordProxy:IsHaveSensitiveAddFilter(str, callback)
  222. local found = self:IsHaveSensitiveName(str)
  223. if found then
  224. if callback then
  225. callback(false)
  226. end
  227. return
  228. end
  229. -- 使用第三方敏感字检测
  230. local AuthProxy = global.Facade:retrieveProxy(global.ProxyTable.AuthProxy)
  231. AuthProxy:RequestWordFilteringQQ(1, str, callback)
  232. end
  233. function SensitiveWordProxy:IsHaveSensitiveEx(strList, callback)
  234. if not strList or #strList <= 0 then
  235. callback(false)
  236. return
  237. end
  238. local idx = 1
  239. local function checkHaveSensitive(str)
  240. self:IsHaveSensitiveAddFilter(str, function(state)
  241. -- 检测,不通过
  242. if not state then
  243. callback(false)
  244. return
  245. end
  246. -- 检测,全部通过
  247. if idx == #strList then
  248. callback(true)
  249. return
  250. end
  251. idx = idx + 1
  252. checkHaveSensitive(strList[idx])
  253. end)
  254. end
  255. checkHaveSensitive(strList[idx])
  256. end
  257. ----------------------------------------------------------------------
  258. ----------------------------------------------------------------------
  259. -- 是否包含敏感字,敏感级别=聊天
  260. function SensitiveWordProxy:fixSensitiveTalkAddFilter(str, callback, type, ex_param)
  261. self:lazyInit()
  262. local replace = "*"
  263. local found = false
  264. local items = {}
  265. local replacedWords = {}
  266. local originStr = str
  267. if ex_param then
  268. ex_param.originStr = originStr
  269. end
  270. -- 特殊字符被替换掉
  271. for i, v in ipairs(self._talk_charaters) do
  272. local s = fixMalformedPattern(v)
  273. local n = 0
  274. str, n = sgsub(str, s, replace)
  275. if n > 0 then
  276. table.insert(replacedWords, s)
  277. end
  278. end
  279. -- 配置表敏感字
  280. found, items = self._words_trie:search(str)
  281. for i, v in ipairs(items) do
  282. local s = fixMalformedPattern(v.w)
  283. local n = 0
  284. str, n = sgsub(str, s, replace)
  285. if n > 0 then
  286. table.insert(replacedWords, s)
  287. end
  288. end
  289. -- 正则匹配的敏感字,域名等
  290. found, items = findSensitivePattren(str)
  291. for _, v in ipairs(items) do
  292. local s = fixMalformedPattern(v.w)
  293. local n = 0
  294. str, n = sgsub(str, s, replace)
  295. if n > 0 then
  296. table.insert(replacedWords, s)
  297. end
  298. end
  299. if ex_param then
  300. ex_param.replacedWords = replacedWords
  301. end
  302. -- 使用第三方敏感字检测
  303. local AuthProxy = global.Facade:retrieveProxy(global.ProxyTable.AuthProxy)
  304. if type and type == 1 then
  305. AuthProxy:RequestWordFilteringQQ(3, str, callback)
  306. else
  307. AuthProxy:RequestWordFilteringQQ(2, str, callback, ex_param)
  308. end
  309. end
  310. -- 是否包含敏感字,敏感级别=聊天
  311. function SensitiveWordProxy:IsHaveSensitiveChat(str)
  312. self:lazyInit()
  313. local found = false
  314. local items = {}
  315. -- 特殊字符匹配
  316. if not found then
  317. for i, v in ipairs(self._talk_charaters) do
  318. local s = fixMalformedPattern(v)
  319. if sfind(str, s, 1, true) ~= nil then
  320. found = true
  321. break
  322. end
  323. end
  324. end
  325. -- 字典树匹配
  326. if not found then
  327. found, items = self._words_trie:search(str, true)
  328. end
  329. -- 正则匹配
  330. if not found then
  331. found, items = findSensitivePattren(str)
  332. end
  333. return found
  334. end
  335. function SensitiveWordProxy:IsHaveSensitiveTalkAddFilter(str, callback, ex_param)
  336. local found = self:IsHaveSensitiveChat(str)
  337. if found then
  338. if callback then
  339. callback(false)
  340. end
  341. return
  342. end
  343. -- 使用第三方敏感字检测
  344. local AuthProxy = global.Facade:retrieveProxy(global.ProxyTable.AuthProxy)
  345. AuthProxy:RequestWordFilteringQQ(2, str, callback, ex_param)
  346. end
  347. return SensitiveWordProxy