From 1d2f2eda305c90da2b4a66ca2cbda7223d3401f7 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 2 Feb 2025 03:18:51 +0000 Subject: [PATCH 01/10] =?UTF-8?q?perf(frontend):=20ahocorasick=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E3=81=A3=E3=81=9F=E3=83=AF=E3=83=BC=E3=83=89=E3=83=9F?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/package.json | 1 + .../frontend/src/scripts/check-word-mute.ts | 70 +++++++------ pnpm-lock.yaml | 98 +++++++++++++------ 3 files changed, 110 insertions(+), 59 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 804160baad..f3165ff86c 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -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", diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 98fea1bced..8209f5f810 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -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): Array | false { - // 自分自身 +import * as AhoCorasick from 'modern-ahocorasick'; +export function checkWordMute( + note: Misskey.entities.Note, + me: Misskey.entities.UserLite | null | undefined, + mutedWords: Array, +): Array | 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 = normalMatches.map(match => match[1]).concat(andMatches, regexMatches.map(({ originaL }) => originaL)); + + return matched.length > 0 ? matched : false; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27dfcfccc6..735e26023a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 From fbf7c61c072723ef8a3b4c3346f4fd5677157aa0 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 2 Feb 2025 08:20:09 +0000 Subject: [PATCH 02/10] FIx: pnpm-lock by pnpm install --- pnpm-lock.yaml | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 735e26023a..1d1a873c7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,7 +142,7 @@ importers: version: 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/testing': specifier: 10.4.7 - version: 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) + version: 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)) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -11682,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11702,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11961,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -11976,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12367,7 +12367,7 @@ snapshots: '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -12377,7 +12377,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) espree: 10.3.0 globals: 14.0.0 ignore: 5.3.1 @@ -15195,7 +15195,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -15525,14 +15525,14 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true agent-base@7.1.0: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17130,7 +17130,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.24.0): dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) esbuild: 0.24.0 transitivePeerDependencies: - supports-color @@ -17372,7 +17372,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.2.0 eslint-visitor-keys: 4.2.0 @@ -17817,7 +17817,7 @@ snapshots: follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) for-each@0.3.3: dependencies: @@ -18256,7 +18256,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18289,7 +18289,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true @@ -18297,14 +18297,14 @@ snapshots: https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18646,7 +18646,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -18655,7 +18655,7 @@ snapshots: istanbul-lib-source-maps@5.0.4: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -19075,7 +19075,7 @@ snapshots: 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) + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -19775,7 +19775,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -21174,7 +21174,7 @@ snapshots: require-in-the-middle@7.3.0: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -21599,7 +21599,7 @@ snapshots: socks-proxy-agent@8.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -21708,7 +21708,7 @@ snapshots: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -22442,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) 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) @@ -22460,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@5.5.0) + debug: 4.3.7(supports-color@8.1.1) 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) From 9a71514b4c644d80977ddea36d660ed43f5b96c8 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 2 Feb 2025 08:43:04 +0000 Subject: [PATCH 03/10] Refactor: just import type --- packages/frontend/src/scripts/check-word-mute.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 8209f5f810..b54a0ef860 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -2,8 +2,8 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import * as Misskey from 'misskey-js'; import * as AhoCorasick from 'modern-ahocorasick'; +import type * as Misskey from 'misskey-js'; export function checkWordMute( note: Misskey.entities.Note, me: Misskey.entities.UserLite | null | undefined, From cc1fdece659a2b5dee2c40be0eb44d8a2242de55 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Tue, 4 Feb 2025 22:51:09 +0900 Subject: [PATCH 04/10] =?UTF-8?q?perf:=20=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E6=AF=8E=E3=81=AB=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=83=AF?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E6=B8=A1=E3=81=99=E3=81=AE=E3=82=92?= =?UTF-8?q?=E3=82=84=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/boot/main-boot.ts | 4 + packages/frontend/src/components/MkNote.vue | 24 +++--- .../src/components/MkNoteDetailed.vue | 2 +- .../frontend/src/components/MkNoteSub.vue | 2 +- .../frontend/src/scripts/check-word-mute.ts | 84 +++++++++++++++---- 5 files changed, 83 insertions(+), 33 deletions(-) diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 874e97f3a4..1eac2cddbc 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -24,6 +24,7 @@ import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; +import { initMuteInfo } from '@/scripts/check-word-mute.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => { @@ -343,11 +344,14 @@ export async function mainBoot() { } } + initMuteInfo(); + const main = markRaw(stream.useChannel('main', null, 'System')); // 自分の情報が更新されたとき main.on('meUpdated', i => { updateAccountPartial(i); + initMuteInfo(); }); main.on('readAllNotifications', () => { diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index a23ff9b48e..c77c83b044 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -280,8 +280,8 @@ const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filte const isLong = shouldCollapsed(appearNote.value, urls.value ?? []); const collapsed = ref(appearNote.value.cw == null && isLong); const isDeleted = ref(false); -const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); -const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true)); +const muted = ref(checkMute(appearNote.value, 'soft')); +const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, 'hard', true)); const showSoftWordMutedWord = computed(() => defaultStore.state.showSoftWordMutedWord); const translation = ref(null); const translating = ref(false); @@ -300,20 +300,18 @@ const pleaseLoginContext = computed(() => ({ })); /* Overload FunctionにLintが対応していないのでコメントアウト -function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array | undefined | null, checkOnly: true): boolean; -function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array | undefined | null, checkOnly: false): Array | false | 'sensitiveMute'; +function checkMute(noteToCheck: Misskey.entities.Note, type: 'soft' | 'hard', checkOnly: true): boolean; +function checkMute(noteToCheck: Misskey.entities.Note, type: 'soft' | 'hard', checkOnly: false): Array | false | 'sensitiveMute'; */ -function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array | undefined | null, checkOnly = false): Array | false | 'sensitiveMute' { - if (mutedWords != null) { - const result = checkWordMute(noteToCheck, $i, mutedWords); - if (Array.isArray(result)) return result; +function checkMute(noteToCheck: Misskey.entities.Note, type: 'soft' | 'hard', checkOnly = false): Array | false | 'sensitiveMute' { + const result = checkWordMute(noteToCheck, $i, type); + if (Array.isArray(result)) return result; - const replyResult = noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords); - if (Array.isArray(replyResult)) return replyResult; + const replyResult = noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, type); + if (Array.isArray(replyResult)) return replyResult; - const renoteResult = noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords); - if (Array.isArray(renoteResult)) return renoteResult; - } + const renoteResult = noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, type); + if (Array.isArray(renoteResult)) return renoteResult; if (checkOnly) return false; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 9d3374d433..d1de6a8850 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -296,7 +296,7 @@ const galleryEl = shallowRef>(); const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(false); const isDeleted = ref(false); -const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false); +const muted = ref($i ? checkWordMute(appearNote.value, $i, 'soft') : false); const translation = ref(null); const translating = ref(false); const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index e4bade309b..2070a44e5a 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -62,7 +62,7 @@ const props = withDefaults(defineProps<{ depth: 1, }); -const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false); +const muted = ref($i ? checkWordMute(props.note, $i, 'soft') : false); const showContent = ref(false); const replies = ref([]); diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index b54a0ef860..9f5765cda1 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -3,22 +3,27 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import * as AhoCorasick from 'modern-ahocorasick'; +import { c } from 'node_modules/vite/dist/node/types.d-aGj9QkWt'; import type * as Misskey from 'misskey-js'; -export function checkWordMute( - note: Misskey.entities.Note, - me: Misskey.entities.UserLite | null | undefined, - mutedWords: Array, -): Array | false { - // 自分自身の投稿は対象外 - if (me && (note.userId === me.id)) return false; +import { $i } from '@/account.js'; + +type WordMuteInfo = false | { + normals: string[]; + and: string[][]; + regex: Array<{ original: string; regex: RegExp }>; + ahoCorasick: AhoCorasick.default; +} + +type GlobalMisskeyWordMute = { + soft: WordMuteInfo; + hard: WordMuteInfo; +}; + +function createWordMuteInfo(mutedWords: Array) : WordMuteInfo { if (mutedWords.length <= 0) return false; - - const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim(); - if (text === '') return false; - const normalTexts: string[] = []; const andTexts: string[][] = []; - const regexTexts: Array<{ originaL: string; regex: RegExp }> = []; + const regexTexts: Array<{ original: string; regex: RegExp }> = []; for (const filter of mutedWords) { if (Array.isArray(filter)) { @@ -31,7 +36,7 @@ export function checkWordMute( const regExp = filter.match(/^\/(.+)\/(.*)$/); if (!regExp) continue; try { - regexTexts.push({ originaL: filter, regex: new RegExp(filter.slice(1, -1)) }); + regexTexts.push({ original: filter, regex: new RegExp(filter.slice(1, -1)) }); } catch { // 無効な正規表現はスキップ } @@ -39,17 +44,60 @@ export function checkWordMute( normalTexts.push(filter); } } - // normal wordmute with AhoCorasick + const ac = new AhoCorasick.default(normalTexts); - const normalMatches = ac.search(text); + + return { + normals: normalTexts, + and: andTexts, + regex: regexTexts, + ahoCorasick: ac, + }; +} + +function setWordMuteInfo(mutedWords: Array, hardMutedWords: Array): void { + const soft = createWordMuteInfo(mutedWords); + const hard = createWordMuteInfo(hardMutedWords); + + globalThis._misskeyWordMute = { soft, hard }; +} + +function getWordMuteInfo(): GlobalMisskeyWordMute | undefined { + if (!globalThis._misskeyWordMute) return undefined; + return globalThis._misskeyWordMute as unknown as GlobalMisskeyWordMute; +} + +export function initMuteInfo(): void { + const mutedWords = $i?.mutedWords ?? []; + const hardMutedWords = $i?.hardMutedWords ?? []; + + setWordMuteInfo(mutedWords, hardMutedWords); +} + +export function checkWordMute( + note: Misskey.entities.Note, + me: Misskey.entities.UserLite | null | undefined, + type: 'soft' | 'hard', +): Array | false { + // 自分自身の投稿は対象外 + if (me && (note.userId === me.id)) return false; + + const wordMuteInfo = getWordMuteInfo()?.[type]; + + if (wordMuteInfo == null || wordMuteInfo === false) return false; + + const text = ((note.cw ?? '') + '\n' + (note.text ?? '')).trim(); + if (text === '') return false; + + const normalMatches = wordMuteInfo.ahoCorasick.search(text); // andTexts - const andMatches = andTexts.filter(texts => texts.filter(keyword => keyword !== '').every(keyword => text.includes(keyword))); + const andMatches = wordMuteInfo.and.filter(texts => texts.filter(keyword => keyword !== '').every(keyword => text.includes(keyword))); // RegExp - const regexMatches = regexTexts.filter(({ regex }) => regex.test(text)); + const regexMatches = wordMuteInfo.regex.filter(({ regex }) => regex.test(text)); - const matched: Array = normalMatches.map(match => match[1]).concat(andMatches, regexMatches.map(({ originaL }) => originaL)); + const matched: Array = normalMatches.map(match => match[1]).concat(andMatches, regexMatches.map(({ original }) => original)); return matched.length > 0 ? matched : false; } From ca30df4b90765d765a4d790c076775f11f31fccb Mon Sep 17 00:00:00 2001 From: tai-cha Date: Tue, 4 Feb 2025 22:54:11 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20=E5=90=8D=E5=89=8D=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=20initMuteInfo=20initWordMuteInfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/boot/main-boot.ts | 6 +++--- packages/frontend/src/scripts/check-word-mute.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 1eac2cddbc..95382b956f 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -24,7 +24,7 @@ import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; -import { initMuteInfo } from '@/scripts/check-word-mute.js'; +import { initWordMuteInfo } from '@/scripts/check-word-mute.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => { @@ -344,14 +344,14 @@ export async function mainBoot() { } } - initMuteInfo(); + initWordMuteInfo(); const main = markRaw(stream.useChannel('main', null, 'System')); // 自分の情報が更新されたとき main.on('meUpdated', i => { updateAccountPartial(i); - initMuteInfo(); + initWordMuteInfo(); }); main.on('readAllNotifications', () => { diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 9f5765cda1..942de2984d 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -67,7 +67,7 @@ function getWordMuteInfo(): GlobalMisskeyWordMute | undefined { return globalThis._misskeyWordMute as unknown as GlobalMisskeyWordMute; } -export function initMuteInfo(): void { +export function initWordMuteInfo(): void { const mutedWords = $i?.mutedWords ?? []; const hardMutedWords = $i?.hardMutedWords ?? []; From fe8f20779b608769d3b677da0a163bdb5e30fd5e Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sat, 8 Feb 2025 10:41:56 +0900 Subject: [PATCH 06/10] update modern-ahocorasick to 2.0.3 (unicode fix) --- packages/frontend/package.json | 2 +- pnpm-lock.yaml | 53 ++++++++-------------------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index f3165ff86c..693eb76863 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -54,7 +54,7 @@ "misskey-bubble-game": "workspace:*", "misskey-js": "workspace:*", "misskey-reversi": "workspace:*", - "modern-ahocorasick": "2.0.2", + "modern-ahocorasick": "2.0.3", "photoswipe": "5.4.4", "punycode.js": "2.3.1", "rollup": "4.26.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d1a873c7a..733fb12b28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,7 +142,7 @@ importers: version: 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/testing': specifier: 10.4.7 - version: 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)) + version: 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) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -800,8 +800,8 @@ importers: specifier: workspace:* version: link:../misskey-reversi modern-ahocorasick: - specifier: 2.0.2 - version: 2.0.2 + specifier: 2.0.3 + version: 2.0.3 photoswipe: specifier: 5.4.4 version: 5.4.4 @@ -1160,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)(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(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0)) '@vue/runtime-core': specifier: 3.5.12 version: 3.5.12 @@ -8315,8 +8315,8 @@ packages: resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==} engines: {node: '>= 8'} - modern-ahocorasick@2.0.2: - resolution: {integrity: sha512-qMPR7bQNCcQ258O1yNFvk6f9CQMT6AFIz0Sg6GbRIRcPFNsKYe1XTKGG8O/6oQu9yJiu7tFMqUiQ5c0B/zc5lQ==} + modern-ahocorasick@2.0.3: + resolution: {integrity: sha512-3vsbnf5DmpsaE8Ye892HecJU7kaT2svsIBXNhne1J080WlU9RKjTtE5PgX+OCc2huqGqGYO+rVEsJlJJuQj+Qw==} module-details-from-path@1.0.3: resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} @@ -13082,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/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))': + '@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)': 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) @@ -15272,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)(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(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))': dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 @@ -15287,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)(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(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0) transitivePeerDependencies: - supports-color @@ -19054,35 +19054,6 @@ 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.8)(utf-8-validate@6.0.4) - 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 @@ -19932,7 +19903,7 @@ snapshots: mock-socket@9.3.1: {} - modern-ahocorasick@2.0.2: {} + modern-ahocorasick@2.0.3: {} module-details-from-path@1.0.3: {} @@ -22542,7 +22513,7 @@ snapshots: - supports-color - terser - 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): + 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): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -22567,7 +22538,7 @@ snapshots: optionalDependencies: '@types/node': 22.9.0 happy-dom: 10.0.3 - jsdom: 24.1.1 + jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - less - lightningcss From 2adf917fcf7dbfbb7b4f1dd474a280710209c4e0 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sat, 8 Feb 2025 11:02:22 +0900 Subject: [PATCH 07/10] =?UTF-8?q?Fix:=20=E8=AC=8E=E3=81=AE=E6=B7=B7?= =?UTF-8?q?=E5=85=A5=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/check-word-mute.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 942de2984d..94916aacda 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import * as AhoCorasick from 'modern-ahocorasick'; -import { c } from 'node_modules/vite/dist/node/types.d-aGj9QkWt'; import type * as Misskey from 'misskey-js'; import { $i } from '@/account.js'; From 85b8af4b987226be0e21a910dec33973f2455c5f Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 9 Feb 2025 07:54:10 +0900 Subject: [PATCH 08/10] =?UTF-8?q?test(frontend):=20vitest=E3=81=AB?= =?UTF-8?q?=E3=82=88=E3=82=8B=E3=83=AF=E3=83=BC=E3=83=89=E3=83=9F=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/scripts/check-word-mute.ts | 2 +- packages/frontend/test/mocks.ts | 44 +++++++++++ packages/frontend/test/word-mute.test.ts | 76 +++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 packages/frontend/test/mocks.ts create mode 100644 packages/frontend/test/word-mute.test.ts diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 94916aacda..9ddbc5362e 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -18,7 +18,7 @@ type GlobalMisskeyWordMute = { hard: WordMuteInfo; }; -function createWordMuteInfo(mutedWords: Array) : WordMuteInfo { +export function createWordMuteInfo(mutedWords: Array) : WordMuteInfo { if (mutedWords.length <= 0) return false; const normalTexts: string[] = []; const andTexts: string[][] = []; diff --git a/packages/frontend/test/mocks.ts b/packages/frontend/test/mocks.ts new file mode 100644 index 0000000000..fca3080880 --- /dev/null +++ b/packages/frontend/test/mocks.ts @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type * as Misskey from 'misskey-js'; +import { vi } from 'vitest'; + +export const UserLiteMock = vi.fn(() => { + return { + id: 'xxxxxxxx', + username: 'ai', + host: null, + name: '藍', + avatarUrl: null, + avatarBlurhash: null, + avatarDecorations: [], + emojis: {}, + onlineStatus: 'online', + } as Misskey.entities.UserLite; +}); + +export const NoteMock = vi.fn((options?: { + text?: string, + cw?: string, +}) => { + const user = new UserLiteMock(); + return { + id: 'xxxxxxxx', + // 2025/01/01 00:00:00 UTC on Unix time + createdAt: '1767225600000', + text: options?.text ?? 'Hello, Misskey!', + cw: options?.cw, + userId: user.id, + user: user, + visibility: 'public', + reactionAcceptance: null, + reactionEmojis: {}, + reactions: {}, + reactionCount: 0, + renoteCount: 0, + repliesCount: 0, + } as Misskey.entities.Note; +}); diff --git a/packages/frontend/test/word-mute.test.ts b/packages/frontend/test/word-mute.test.ts new file mode 100644 index 0000000000..f0c65bb1a4 --- /dev/null +++ b/packages/frontend/test/word-mute.test.ts @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { describe, test, assert } from 'vitest'; +import { createWordMuteInfo, checkWordMute } from '@/scripts/check-word-mute.js'; +import { NoteMock } from './mocks.js'; + +type TestCases = { + text: string, + cw?: string, + mutedWords: Array, + result: false | Array, +} + +describe('check-word-mute', () => { + const cases:Array = [ + { + text: 'Hello, Misskey!', + mutedWords: ['Misskey'], + result: [['Misskey']], + }, + // cw + { + text: 'Hello, Misskey!', + cw: 'ai', + mutedWords: ['ai'], + result: [['ai']], + }, + // nothing + { + text: 'Hello, Misskey!', + mutedWords: [], + result: false, + }, + // surrogate pair + { + text: '𠮟る', + mutedWords: ['𠮟'], + result: [['𠮟']], + }, + // grapheme cluster + { + text: '👩‍❤️‍👨', + mutedWords: ['👩'], + result: false, + }, + // regex + { + text: 'Hello, Misskey!', + mutedWords: ['/M[isk]*ey/'], + result: ['/M[isk]*ey/'], + }, + // multi wordas + { + text: 'Hello, Misskey!', + mutedWords: [['Hello', 'Misskey'], ['Mi']], + result: [['Mi'],['Hello', 'Misskey']], + }, + ] + + cases.forEach((c) => { + test(`text: ${c.text}, cw: ${c.cw}, mutedWords: ${c.mutedWords}` , async () => { + // initWordMuteInfoが実行されないので代わりにここで初期化 + (globalThis as any)._misskeyWordMute = { + soft: createWordMuteInfo(c.mutedWords), + hard: createWordMuteInfo([]), + } + + const note = NoteMock({ text: c.text, cw: c.cw }); + const result = checkWordMute(note, null, 'soft'); + assert.deepEqual(result, c.result); + }); + }); +}); From d639f4bd4a5307343fdad4404d908e086cca819f Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 9 Feb 2025 07:55:32 +0900 Subject: [PATCH 09/10] formatting code --- packages/frontend/test/mocks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/test/mocks.ts b/packages/frontend/test/mocks.ts index fca3080880..a4f02bda1c 100644 --- a/packages/frontend/test/mocks.ts +++ b/packages/frontend/test/mocks.ts @@ -16,7 +16,7 @@ export const UserLiteMock = vi.fn(() => { avatarBlurhash: null, avatarDecorations: [], emojis: {}, - onlineStatus: 'online', + onlineStatus: 'online', } as Misskey.entities.UserLite; }); From 32cf70eb9b903c0800d7e482028bfd6cfff04dd8 Mon Sep 17 00:00:00 2001 From: tai-cha Date: Sun, 9 Feb 2025 07:59:59 +0900 Subject: [PATCH 10/10] Fix: typo --- packages/frontend/test/word-mute.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/test/word-mute.test.ts b/packages/frontend/test/word-mute.test.ts index f0c65bb1a4..a4397dfbd4 100644 --- a/packages/frontend/test/word-mute.test.ts +++ b/packages/frontend/test/word-mute.test.ts @@ -52,7 +52,7 @@ describe('check-word-mute', () => { mutedWords: ['/M[isk]*ey/'], result: ['/M[isk]*ey/'], }, - // multi wordas + // multi words { text: 'Hello, Misskey!', mutedWords: [['Hello', 'Misskey'], ['Mi']],