@zhin.js/sensitive-filter 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,198 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import {
3
+ createSensitiveWordRegex,
4
+ getEnabledWords,
5
+ type SensitiveFilterOptions,
6
+ politicalWords,
7
+ violenceWords,
8
+ pornWords,
9
+ homophoneWords,
10
+ } from '../src/sensitive-words'
11
+
12
+ describe('敏感词过滤测试', () => {
13
+ describe('敏感词正则表达式', () => {
14
+ it('应该创建有效的正则表达式', () => {
15
+ const words = ['测试', '敏感词']
16
+ const regex = createSensitiveWordRegex(words)
17
+
18
+ expect(regex).toBeInstanceOf(RegExp)
19
+ // 使用 match 方法测试,因为正则表达式带有 'g' 标志,test() 方法会有状态问题
20
+ expect('这是测试'.match(regex)).toBeTruthy()
21
+ expect('这是敏感词'.match(regex)).toBeTruthy()
22
+ expect('这是正常内容'.match(regex)).toBeNull()
23
+ })
24
+
25
+ it('应该转义特殊字符', () => {
26
+ const words = ['$test', '.*word']
27
+ const regex = createSensitiveWordRegex(words)
28
+
29
+ expect('$test'.match(regex)).toBeTruthy()
30
+ expect('.*word'.match(regex)).toBeTruthy()
31
+ })
32
+
33
+ it('应该不区分大小写', () => {
34
+ const words = ['Test']
35
+ const regex = createSensitiveWordRegex(words)
36
+
37
+ expect('test'.match(regex)).toBeTruthy()
38
+ expect('TEST'.match(regex)).toBeTruthy()
39
+ expect('TeSt'.match(regex)).toBeTruthy()
40
+ })
41
+ })
42
+
43
+ describe('获取启用的敏感词', () => {
44
+ it('应该返回所有启用的敏感词', () => {
45
+ const options: SensitiveFilterOptions = {
46
+ political: true,
47
+ violence: true,
48
+ porn: true,
49
+ homophone: true,
50
+ }
51
+
52
+ const words = getEnabledWords(options)
53
+
54
+ expect(words.length).toBeGreaterThan(0)
55
+ expect(words).toContain(politicalWords[0])
56
+ expect(words).toContain(violenceWords[0])
57
+ expect(words).toContain(pornWords[0])
58
+ expect(words).toContain(homophoneWords[0])
59
+ })
60
+
61
+ it('应该只返回启用类别的敏感词', () => {
62
+ const options: SensitiveFilterOptions = {
63
+ political: true,
64
+ violence: false,
65
+ porn: false,
66
+ homophone: false,
67
+ }
68
+
69
+ const words = getEnabledWords(options)
70
+
71
+ expect(words).toContain(politicalWords[0])
72
+ expect(words).not.toContain(violenceWords[0])
73
+ expect(words).not.toContain(pornWords[0])
74
+ })
75
+
76
+ it('应该包含自定义敏感词', () => {
77
+ const options: SensitiveFilterOptions = {
78
+ custom: ['自定义词1', '自定义词2'],
79
+ }
80
+
81
+ const words = getEnabledWords(options)
82
+
83
+ expect(words).toContain('自定义词1')
84
+ expect(words).toContain('自定义词2')
85
+ })
86
+
87
+ it('应该正确过滤谐音词', () => {
88
+ const options: SensitiveFilterOptions = {
89
+ political: false,
90
+ violence: false,
91
+ porn: false,
92
+ prohibited: false,
93
+ fraud: false,
94
+ illegal: false,
95
+ homophone: true,
96
+ }
97
+
98
+ const words = getEnabledWords(options)
99
+
100
+ expect(words).toContain('GCD')
101
+ expect(words).toContain('sb')
102
+ expect(words).toContain('cao')
103
+ })
104
+ })
105
+
106
+ describe('敏感词检测', () => {
107
+ it('应该检测出敏感词', () => {
108
+ const words = ['测试', '敏感']
109
+ const regex = createSensitiveWordRegex(words)
110
+
111
+ const text = '这是一个测试敏感词的文本'
112
+ const matches = text.match(regex)
113
+
114
+ expect(matches).not.toBeNull()
115
+ expect(matches?.length).toBe(2)
116
+ expect(matches).toContain('测试')
117
+ expect(matches).toContain('敏感')
118
+ })
119
+
120
+ it('应该去重检测结果', () => {
121
+ const words = ['测试']
122
+ const regex = createSensitiveWordRegex(words)
123
+
124
+ const text = '测试 测试 测试'
125
+ const matches = text.match(regex)
126
+ const unique = [...new Set(matches || [])]
127
+
128
+ expect(unique.length).toBe(1)
129
+ })
130
+ })
131
+
132
+ describe('敏感词替换', () => {
133
+ it('应该替换敏感词为指定字符', () => {
134
+ const words = ['敏感词']
135
+ const regex = createSensitiveWordRegex(words)
136
+
137
+ const text = '这是一个敏感词测试'
138
+ const result = text.replace(regex, (match) => '*'.repeat(match.length))
139
+
140
+ expect(result).toBe('这是一个***测试')
141
+ })
142
+
143
+ it('应该替换所有匹配的敏感词', () => {
144
+ const words = ['测试', '敏感']
145
+ const regex = createSensitiveWordRegex(words)
146
+
147
+ const text = '测试敏感测试敏感'
148
+ const result = text.replace(regex, (match) => '*'.repeat(match.length))
149
+
150
+ // 文本共8个字符(每个中文字符算1个):"测试"(2) + "敏感"(2) + "测试"(2) + "敏感"(2) = 8
151
+ expect(result).toBe('********')
152
+ })
153
+ })
154
+
155
+ describe('谐音词和变体词过滤', () => {
156
+ it('应该检测拼音缩写', () => {
157
+ const words = ['GCD', 'FLG', 'CNM']
158
+ const regex = createSensitiveWordRegex(words)
159
+
160
+ expect('这是GCD的东西'.match(regex)).toBeTruthy()
161
+ expect('flg是邪教'.match(regex)).toBeTruthy()
162
+ expect('cnm骂人'.match(regex)).toBeTruthy()
163
+ })
164
+
165
+ it('应该检测谐音变体', () => {
166
+ const words = ['共惨党', '政腐', '尼玛']
167
+ const regex = createSensitiveWordRegex(words)
168
+
169
+ expect('共惨党说法'.match(regex)).toBeTruthy()
170
+ expect('政腐机关'.match(regex)).toBeTruthy()
171
+ expect('尼玛真是'.match(regex)).toBeTruthy()
172
+ })
173
+
174
+ it('应该检测符号分隔词', () => {
175
+ const words = ['法_轮_功', '六_四', '毒_品']
176
+ const regex = createSensitiveWordRegex(words)
177
+
178
+ expect('法_轮_功邪教'.match(regex)).toBeTruthy()
179
+ expect('六_四事件'.match(regex)).toBeTruthy()
180
+ expect('毒_品交易'.match(regex)).toBeTruthy()
181
+ })
182
+
183
+ it('应该替换谐音词', () => {
184
+ const words = ['sb', 'cao', 'fuck']
185
+ const regex = createSensitiveWordRegex(words)
186
+
187
+ const text = '你个sb,去cao,fuck off'
188
+ const result = text.replace(regex, (match) => '*'.repeat(match.length))
189
+
190
+ expect(result).toContain('**')
191
+ expect(result).toContain('***')
192
+ expect(result).toContain('****')
193
+ expect(result).not.toContain('sb')
194
+ expect(result).not.toContain('cao')
195
+ expect(result).not.toContain('fuck')
196
+ })
197
+ })
198
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "./lib",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "jsxImportSource": "zhin.js",
16
+ "allowSyntheticDefaultImports": true,
17
+ "experimentalDecorators": true,
18
+ "emitDecoratorMetadata": true,
19
+ "declaration": true,
20
+ "declarationMap": true,
21
+ "sourceMap": true
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["lib", "node_modules", "client", "dist"]
25
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.ts","./src/sensitive-words.ts"],"version":"5.9.3"}