perf(frontend): ahocorasickを使ったワードミュート

This commit is contained in:
tai-cha 2025-02-02 03:18:51 +00:00
parent 9230ee52a0
commit 1d2f2eda30
3 changed files with 110 additions and 59 deletions

View File

@ -54,6 +54,7 @@
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"modern-ahocorasick": "2.0.2",
"photoswipe": "5.4.4",
"punycode.js": "2.3.1",
"rollup": "4.26.0",

View File

@ -3,41 +3,53 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
export function checkWordMute(note: Misskey.entities.Note, me: Misskey.entities.UserLite | null | undefined, mutedWords: Array<string | string[]>): Array<string | string[]> | false {
// 自分自身
import * as AhoCorasick from 'modern-ahocorasick';
export function checkWordMute(
note: Misskey.entities.Note,
me: Misskey.entities.UserLite | null | undefined,
mutedWords: Array<string | string[]>,
): Array<string | string[]> | false {
// 自分自身の投稿は対象外
if (me && (note.userId === me.id)) return false;
if (mutedWords.length <= 0) return false;
if (mutedWords.length > 0) {
const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim();
const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim();
if (text === '') return false;
if (text === '') return false;
const normalTexts: string[] = [];
const andTexts: string[][] = [];
const regexTexts: Array<{ originaL: string; regex: RegExp }> = [];
const matched = mutedWords.filter(filter => {
if (Array.isArray(filter)) {
// Clean up
const filteredFilter = filter.filter(keyword => keyword !== '');
if (filteredFilter.length === 0) return false;
return filteredFilter.every(keyword => text.includes(keyword));
for (const filter of mutedWords) {
if (Array.isArray(filter)) {
if (filter.length === 1) {
normalTexts.push(filter[0]);
} else {
// represents RegExp
const regexp = filter.match(/^\/(.+)\/(.*)$/);
// This should never happen due to input sanitisation.
if (!regexp) return false;
try {
return new RegExp(regexp[1], regexp[2]).test(text);
} catch (err) {
// This should never happen due to input sanitisation.
return false;
}
andTexts.push(filter);
}
});
if (matched.length > 0) return matched;
} else if (filter.startsWith('/') && filter.endsWith('/')) {
const regExp = filter.match(/^\/(.+)\/(.*)$/);
if (!regExp) continue;
try {
regexTexts.push({ originaL: filter, regex: new RegExp(filter.slice(1, -1)) });
} catch {
// 無効な正規表現はスキップ
}
} else {
normalTexts.push(filter);
}
}
// normal wordmute with AhoCorasick
const ac = new AhoCorasick.default(normalTexts);
const normalMatches = ac.search(text);
return false;
// andTexts
const andMatches = andTexts.filter(texts => texts.filter(keyword => keyword !== '').every(keyword => text.includes(keyword)));
// RegExp
const regexMatches = regexTexts.filter(({ regex }) => regex.test(text));
const matched: Array<string | string[]> = normalMatches.map(match => match[1]).concat(andMatches, regexMatches.map(({ originaL }) => originaL));
return matched.length > 0 ? matched : false;
}

View File

@ -799,6 +799,9 @@ importers:
misskey-reversi:
specifier: workspace:*
version: link:../misskey-reversi
modern-ahocorasick:
specifier: 2.0.2
version: 2.0.2
photoswipe:
specifier: 5.4.4
version: 5.4.4
@ -1157,7 +1160,7 @@ importers:
version: 7.17.0(eslint@9.14.0)(typescript@5.6.3)
'@vitest/coverage-v8':
specifier: 1.6.0
version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))
version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))
'@vue/runtime-core':
specifier: 3.5.12
version: 3.5.12
@ -7854,6 +7857,7 @@ packages:
lodash.get@4.4.2:
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
@ -8311,6 +8315,9 @@ packages:
resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
engines: {node: '>= 8'}
modern-ahocorasick@2.0.2:
resolution: {integrity: sha512-qMPR7bQNCcQ258O1yNFvk6f9CQMT6AFIz0Sg6GbRIRcPFNsKYe1XTKGG8O/6oQu9yJiu7tFMqUiQ5c0B/zc5lQ==}
module-details-from-path@1.0.3:
resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
@ -11675,7 +11682,7 @@ snapshots:
'@babel/traverse': 7.23.5
'@babel/types': 7.24.7
convert-source-map: 2.0.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@ -11695,7 +11702,7 @@ snapshots:
'@babel/traverse': 7.24.7
'@babel/types': 7.24.7
convert-source-map: 2.0.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@ -11954,7 +11961,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.25.6
'@babel/types': 7.24.7
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -11969,7 +11976,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.24.7
'@babel/parser': 7.25.6
'@babel/types': 7.25.6
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -12360,7 +12367,7 @@ snapshots:
'@eslint/config-array@0.18.0':
dependencies:
'@eslint/object-schema': 2.1.4
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@ -12370,7 +12377,7 @@ snapshots:
'@eslint/eslintrc@3.1.0':
dependencies:
ajv: 6.12.6
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
espree: 10.3.0
globals: 14.0.0
ignore: 5.3.1
@ -13075,7 +13082,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7)':
'@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7))':
dependencies:
'@nestjs/common': 10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -15188,7 +15195,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.17.0
'@typescript-eslint/visitor-keys': 7.17.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.4
@ -15265,7 +15272,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))':
'@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))':
dependencies:
'@ampproject/remapping': 2.2.1
'@bcoe/v8-coverage': 0.2.3
@ -15280,7 +15287,7 @@ snapshots:
std-env: 3.7.0
strip-literal: 2.1.0
test-exclude: 6.0.0
vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0)
vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0)
transitivePeerDependencies:
- supports-color
@ -15518,14 +15525,14 @@ snapshots:
agent-base@6.0.2:
dependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
optional: true
agent-base@7.1.0:
dependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@ -17123,7 +17130,7 @@ snapshots:
esbuild-register@3.5.0(esbuild@0.24.0):
dependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
esbuild: 0.24.0
transitivePeerDependencies:
- supports-color
@ -17365,7 +17372,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
escape-string-regexp: 4.0.0
eslint-scope: 8.2.0
eslint-visitor-keys: 4.2.0
@ -17810,7 +17817,7 @@ snapshots:
follow-redirects@1.15.9(debug@4.3.7):
optionalDependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
for-each@0.3.3:
dependencies:
@ -18249,7 +18256,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@ -18282,7 +18289,7 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
optional: true
@ -18290,14 +18297,14 @@ snapshots:
https-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@ -18639,7 +18646,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@ -18648,7 +18655,7 @@ snapshots:
istanbul-lib-source-maps@5.0.4:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@ -19047,6 +19054,35 @@ snapshots:
jsdoc-type-pratt-parser@4.1.0: {}
jsdom@24.1.1:
dependencies:
cssstyle: 4.0.1
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.1
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.5
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.12
parse5: 7.2.1
rrweb-cssom: 0.7.1
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 4.1.4
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
optional: true
jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
dependencies:
cssstyle: 4.0.1
@ -19739,7 +19775,7 @@ snapshots:
micromark@4.0.0:
dependencies:
'@types/debug': 4.1.12
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
decode-named-character-reference: 1.0.2
devlop: 1.1.0
micromark-core-commonmark: 2.0.0
@ -19896,6 +19932,8 @@ snapshots:
mock-socket@9.3.1: {}
modern-ahocorasick@2.0.2: {}
module-details-from-path@1.0.3: {}
ms@2.0.0: {}
@ -21136,7 +21174,7 @@ snapshots:
require-in-the-middle@7.3.0:
dependencies:
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
module-details-from-path: 1.0.3
resolve: 1.22.8
transitivePeerDependencies:
@ -21561,7 +21599,7 @@ snapshots:
socks-proxy-agent@8.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
socks: 2.7.1
transitivePeerDependencies:
- supports-color
@ -21670,7 +21708,7 @@ snapshots:
arg: 5.0.2
bluebird: 3.7.2
check-more-types: 2.24.0
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
execa: 5.1.1
lazy-ass: 1.6.0
ps-tree: 1.2.0
@ -22404,7 +22442,7 @@ snapshots:
vite-node@1.6.0(@types/node@22.9.0)(sass@1.79.3)(terser@5.36.0):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
pathe: 1.1.2
picocolors: 1.0.1
vite: 5.4.11(@types/node@22.9.0)(sass@1.79.3)(terser@5.36.0)
@ -22422,7 +22460,7 @@ snapshots:
vite-node@1.6.0(@types/node@22.9.0)(sass@1.79.4)(terser@5.36.0):
dependencies:
cac: 6.7.14
debug: 4.3.7(supports-color@8.1.1)
debug: 4.3.7(supports-color@5.5.0)
pathe: 1.1.2
picocolors: 1.0.1
vite: 5.4.11(@types/node@22.9.0)(sass@1.79.4)(terser@5.36.0)
@ -22504,7 +22542,7 @@ snapshots:
- supports-color
- terser
vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0):
vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0):
dependencies:
'@vitest/expect': 1.6.0
'@vitest/runner': 1.6.0
@ -22529,7 +22567,7 @@ snapshots:
optionalDependencies:
'@types/node': 22.9.0
happy-dom: 10.0.3
jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
jsdom: 24.1.1
transitivePeerDependencies:
- less
- lightningcss