From d40612ac522df8f67758daa0ad17eea7cb2d6da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 9 Feb 2024 00:08:33 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(frontend):=20aiscript=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=A7?= =?UTF-8?q?=E3=81=AE=E3=83=8F=E3=82=A4=E3=83=A9=E3=82=A4=E3=83=88=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/package.json | 2 +- .../frontend/src/scripts/code-highlighter.ts | 9 +-- pnpm-lock.yaml | 62 +++++++++++++++++-- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 9e88c6c036..d8ba08501e 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -29,7 +29,7 @@ "@twemoji/parser": "15.0.0", "@vitejs/plugin-vue": "5.0.3", "@vue/compiler-sfc": "3.4.15", - "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6", + "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2", "astring": "1.8.6", "broadcast-channel": "7.0.0", "buraha": "0.0.1", diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts index b11dfed41a..2733897bab 100644 --- a/packages/frontend/src/scripts/code-highlighter.ts +++ b/packages/frontend/src/scripts/code-highlighter.ts @@ -20,7 +20,7 @@ export async function getTheme(mode: 'light' | 'dark', getName = false): Promise const base = [lightTheme, darkTheme].find(x => x.id === theme.base); if (base && base.codeHighlighter) theme.codeHighlighter = Object.assign({}, base.codeHighlighter, theme.codeHighlighter); } - + if (theme.codeHighlighter) { let _res: ThemeRegistration = {}; if (theme.codeHighlighter.base === '_none_') { @@ -55,7 +55,7 @@ export async function getHighlighter(): Promise { export async function initHighlighter() { const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json'); - + await loadWasm(import('shiki/onig.wasm?init')); // テーマの重複を消す @@ -68,10 +68,7 @@ export async function initHighlighter() { themes, langs: [ import('shiki/langs/javascript.mjs'), - { - aliases: ['is', 'ais'], - ...aiScriptGrammar.default, - } as unknown as LanguageRegistration, + aiScriptGrammar.default as unknown as LanguageRegistration, ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 291ec305c7..bdc9a48464 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -704,8 +704,8 @@ importers: specifier: 3.4.15 version: 3.4.15 aiscript-vscode: - specifier: github:aiscript-dev/aiscript-vscode#v0.0.6 - version: github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd + specifier: github:aiscript-dev/aiscript-vscode#v0.1.2 + version: github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07 astring: specifier: 1.8.6 version: 1.8.6 @@ -20001,6 +20001,42 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} + /vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + dev: false + + /vscode-languageclient@9.0.1: + resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==} + engines: {vscode: ^1.82.0} + dependencies: + minimatch: 5.1.2 + semver: 7.5.4 + vscode-languageserver-protocol: 3.17.5 + dev: false + + /vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + dev: false + + /vscode-languageserver-textdocument@1.0.11: + resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + dev: false + + /vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + dev: false + + /vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.17.5 + dev: false + /vue-component-type-helpers@1.8.27: resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} dev: true @@ -20560,11 +20596,27 @@ packages: readable-stream: 3.6.0 dev: false - github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd: - resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/b5a8aa0ad927831a0b867d1c183460a14e6c48cd} + '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz': + resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz} + name: '@aiscript-dev/aiscript-languageserver' + version: 0.1.5 + hasBin: true + dependencies: + seedrandom: 3.0.5 + stringz: 2.1.0 + uuid: 9.0.1 + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.11 + dev: false + + github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07: + resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/793211d40243c8775f6b85f015c221c82cbffb07} name: aiscript-vscode - version: 0.0.6 + version: 0.1.2 engines: {vscode: ^1.83.0} + dependencies: + '@aiscript-dev/aiscript-languageserver': '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz' + vscode-languageclient: 9.0.1 dev: false github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.10)(@storybook/components@7.6.10)(@storybook/core-events@7.6.10)(@storybook/manager-api@7.6.10)(@storybook/preview-api@7.6.10)(@storybook/theming@7.6.10)(@storybook/types@7.6.10)(react-dom@18.2.0)(react@18.2.0): From c0cb76f0ec63dec3dd1d8381a3395c8c84d43a97 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 8 Feb 2024 17:24:51 +0000 Subject: [PATCH 2/3] chore: use vite@5.1.0 / pnpm@8.15.1 --- package.json | 2 +- packages/frontend/package.json | 2 +- pnpm-lock.yaml | 66 +++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index bd4529f159..1a38627361 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@8.12.1", + "packageManager": "pnpm@8.15.1", "workspaces": [ "packages/frontend", "packages/backend", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d8ba08501e..5aaddde5ca 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -71,7 +71,7 @@ "typescript": "5.3.3", "uuid": "9.0.1", "v-code-diff": "1.7.2", - "vite": "5.0.12", + "vite": "5.1.0", "vue": "3.4.15", "vuedraggable": "next" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bdc9a48464..eb0c613e97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -699,7 +699,7 @@ importers: version: 15.0.0 '@vitejs/plugin-vue': specifier: 5.0.3 - version: 5.0.3(vite@5.0.12)(vue@3.4.15) + version: 5.0.3(vite@5.1.0)(vue@3.4.15) '@vue/compiler-sfc': specifier: 3.4.15 version: 3.4.15 @@ -830,8 +830,8 @@ importers: specifier: 1.7.2 version: 1.7.2(vue@3.4.15) vite: - specifier: 5.0.12 - version: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + specifier: 5.1.0 + version: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) vue: specifier: 3.4.15 version: 3.4.15(typescript@5.3.3) @@ -883,7 +883,7 @@ importers: version: 7.6.10(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) '@storybook/react-vite': specifier: 7.6.10 - version: 7.6.10(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.0.12) + version: 7.6.10(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0) '@storybook/testing-library': specifier: 0.2.2 version: 0.2.2 @@ -898,7 +898,7 @@ importers: version: 7.6.10(vue@3.4.15) '@storybook/vue3-vite': specifier: 7.6.10 - version: 7.6.10(typescript@5.3.3)(vite@5.0.12)(vue@3.4.15) + version: 7.6.10(typescript@5.3.3)(vite@5.1.0)(vue@3.4.15) '@testing-library/vue': specifier: 8.0.1 version: 8.0.1(@vue/compiler-sfc@3.4.15)(vue@3.4.15) @@ -4694,7 +4694,7 @@ packages: chalk: 4.1.2 dev: true - /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.12): + /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.0): resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==} peerDependencies: typescript: '>= 4.3.x' @@ -4708,7 +4708,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -6740,7 +6740,7 @@ packages: - supports-color dev: true - /@storybook/builder-vite@7.6.10(typescript@5.3.3)(vite@5.0.12): + /@storybook/builder-vite@7.6.10(typescript@5.3.3)(vite@5.1.0): resolution: {integrity: sha512-qxe19axiNJVdIKj943e1ucAmADwU42fTGgMSdBzzrvfH3pSOmx2057aIxRzd8YtBRnj327eeqpgCHYIDTunMYQ==} peerDependencies: '@preact/preset-vite': '*' @@ -6772,7 +6772,7 @@ packages: magic-string: 0.30.5 rollup: 3.29.4 typescript: 5.3.3 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - encoding - supports-color @@ -7129,7 +7129,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@7.6.10(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.0.12): + /@storybook/react-vite@7.6.10(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0): resolution: {integrity: sha512-YE2+J1wy8nO+c6Nv/hBMu91Edew3K184L1KSnfoZV8vtq2074k1Me/8pfe0QNuq631AncpfCYNb37yBAXQ/80w==} engines: {node: '>=16'} peerDependencies: @@ -7137,16 +7137,16 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.12) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.0) '@rollup/pluginutils': 5.1.0(rollup@4.9.6) - '@storybook/builder-vite': 7.6.10(typescript@5.3.3)(vite@5.0.12) + '@storybook/builder-vite': 7.6.10(typescript@5.3.3)(vite@5.1.0) '@storybook/react': 7.6.10(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) - '@vitejs/plugin-react': 3.1.0(vite@5.0.12) + '@vitejs/plugin-react': 3.1.0(vite@5.1.0) magic-string: 0.30.5 react: 18.2.0 react-docgen: 7.0.1 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -7261,18 +7261,18 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@7.6.10(typescript@5.3.3)(vite@5.0.12)(vue@3.4.15): + /@storybook/vue3-vite@7.6.10(typescript@5.3.3)(vite@5.1.0)(vue@3.4.15): resolution: {integrity: sha512-5f0Rh4PTVEeAI86ybihfN+rHGXXLNiRsoGKinpJSb7hkfsq/L7u3sVCXJwH/qsG+rUJlZyHs3kfa4/Kgyyi3Mg==} engines: {node: ^14.18 || >=16} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 dependencies: - '@storybook/builder-vite': 7.6.10(typescript@5.3.3)(vite@5.0.12) + '@storybook/builder-vite': 7.6.10(typescript@5.3.3)(vite@5.1.0) '@storybook/core-server': 7.6.10 '@storybook/vue3': 7.6.10(vue@3.4.15) - '@vitejs/plugin-vue': 4.5.2(vite@5.0.12)(vue@3.4.15) + '@vitejs/plugin-vue': 4.5.2(vite@5.1.0)(vue@3.4.15) magic-string: 0.30.5 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) vue-docgen-api: 4.64.1(vue@3.4.15) transitivePeerDependencies: - '@preact/preset-vite' @@ -8833,7 +8833,7 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitejs/plugin-react@3.1.0(vite@5.0.12): + /@vitejs/plugin-react@3.1.0(vite@5.1.0): resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -8844,30 +8844,30 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color dev: true - /@vitejs/plugin-vue@4.5.2(vite@5.0.12)(vue@3.4.15): + /@vitejs/plugin-vue@4.5.2(vite@5.1.0)(vue@3.4.15): resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) vue: 3.4.15(typescript@5.3.3) dev: true - /@vitejs/plugin-vue@5.0.3(vite@5.0.12)(vue@3.4.15): + /@vitejs/plugin-vue@5.0.3(vite@5.1.0)(vue@3.4.15): resolution: {integrity: sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) vue: 3.4.15(typescript@5.3.3) dev: false @@ -16910,6 +16910,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -19866,7 +19874,7 @@ packages: mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -19882,8 +19890,8 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0): - resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} + /vite@5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0): + resolution: {integrity: sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -19912,7 +19920,7 @@ packages: dependencies: '@types/node': 20.11.10 esbuild: 0.19.11 - postcss: 8.4.33 + postcss: 8.4.35 rollup: 4.9.6 sass: 1.70.0 terser: 5.27.0 @@ -19984,7 +19992,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.7.0 - vite: 5.0.12(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) + vite: 5.1.0(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) vite-node: 0.34.6(@types/node@20.11.10)(sass@1.70.0)(terser@5.27.0) why-is-node-running: 2.2.2 transitivePeerDependencies: From 614c9a0fc602586710e3f24bb26140bb49c2d54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:07:18 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E7=89=B9=E5=AE=9A=E6=96=87=E5=AD=97?= =?UTF-8?q?=E5=88=97=E3=82=92=E5=90=AB=E3=82=80=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=82=92=E6=8A=95=E7=A8=BF=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=E7=94=A8=E8=A8=AD=E5=AE=9A=E9=A0=85=E7=9B=AE?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#13210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 特定文字列を含むノートを投稿できないようにする管理画面用設定項目を追加 * Serviceでチェックするように変更 --- CHANGELOG.md | 2 + locales/index.d.ts | 12 +++ locales/ja-JP.yml | 3 + .../1707429690000-prohibited-words.js | 16 ++++ packages/backend/src/core/HashtagService.ts | 2 +- .../backend/src/core/NoteCreateService.ts | 10 ++- packages/backend/src/core/UtilityService.ts | 6 +- packages/backend/src/models/Meta.ts | 5 ++ .../src/server/api/endpoints/admin/meta.ts | 8 ++ .../server/api/endpoints/admin/update-meta.ts | 8 ++ .../src/server/api/endpoints/notes/create.ts | 65 +++++++++++------ packages/backend/test/e2e/note.ts | 73 +++++++++++++++++++ .../frontend/src/pages/admin/moderation.vue | 8 ++ packages/misskey-js/src/autogen/types.ts | 2 + 14 files changed, 191 insertions(+), 29 deletions(-) create mode 100644 packages/backend/migration/1707429690000-prohibited-words.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a32c557c94..1d788e1522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ - Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正 * すべてのリモートユーザーのリアクション一覧を見えないようにします - Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように +- Fix: 特定のキーワードを含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207 + * デフォルトは空欄なので適用前と同等の動作になります ### Client - Feat: 新しいゲームを追加 diff --git a/locales/index.d.ts b/locales/index.d.ts index f8c4971655..8f4c9d18e4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4180,6 +4180,18 @@ export interface Locale extends ILocale { * スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。 */ "sensitiveWordsDescription2": string; + /** + * 禁止ワード + */ + "prohibitedWords": string; + /** + * 設定したワードが含まれるノートを投稿しようとした際、エラーとなるようにします。改行で区切って複数設定できます。 + */ + "prohibitedWordsDescription": string; + /** + * スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。 + */ + "prohibitedWordsDescription2": string; /** * 非表示ハッシュタグ */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index cf45c13f75..5348502425 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1041,6 +1041,9 @@ resetPasswordConfirm: "パスワードリセットしますか?" sensitiveWords: "センシティブワード" sensitiveWordsDescription: "設定したワードが含まれるノートの公開範囲をホームにします。改行で区切って複数設定できます。" sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" +prohibitedWords: "禁止ワード" +prohibitedWordsDescription: "設定したワードが含まれるノートを投稿しようとした際、エラーとなるようにします。改行で区切って複数設定できます。" +prohibitedWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" hiddenTags: "非表示ハッシュタグ" hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。" notesSearchNotAvailable: "ノート検索は利用できません。" diff --git a/packages/backend/migration/1707429690000-prohibited-words.js b/packages/backend/migration/1707429690000-prohibited-words.js new file mode 100644 index 0000000000..2dd62d8ff8 --- /dev/null +++ b/packages/backend/migration/1707429690000-prohibited-words.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class prohibitedWords1707429690000 { + name = 'prohibitedWords1707429690000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "prohibitedWords" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "prohibitedWords"`); + } +} diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 5a2417c9cd..712530108e 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -163,7 +163,7 @@ export class HashtagService { const instance = await this.metaService.fetch(); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); if (hiddenTags.includes(hashtag)) return; - if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return; + if (this.utilityService.isKeyWordIncluded(hashtag, instance.sensitiveWords)) return; // YYYYMMDDHHmm (10分間隔) const now = new Date(); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index f7e870831d..153a6406a9 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -151,6 +151,8 @@ type Option = { export class NoteCreateService implements OnApplicationShutdown { #shutdownController = new AbortController(); + public static ContainsProhibitedWordsError = class extends Error {}; + constructor( @Inject(DI.config) private config: Config, @@ -254,13 +256,19 @@ export class NoteCreateService implements OnApplicationShutdown { if (data.visibility === 'public' && data.channel == null) { const sensitiveWords = meta.sensitiveWords; - if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { + if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) { data.visibility = 'home'; } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } + if (!user.host) { + if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { + throw new NoteCreateService.ContainsProhibitedWordsError(); + } + } + const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 5dec36c89e..15b98abe63 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -43,13 +43,13 @@ export class UtilityService { } @bindThis - public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean { - if (sensitiveWords.length === 0) return false; + public isKeyWordIncluded(text: string, keyWords: string[]): boolean { + if (keyWords.length === 0) return false; if (text === '') return false; const regexpregexp = /^\/(.+)\/(.*)$/; - const matched = sensitiveWords.some(filter => { + const matched = keyWords.some(filter => { // represents RegExp const regexp = filter.match(regexpregexp); // This should never happen due to input sanitisation. diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 3265e85dd7..bcde2db0b7 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -76,6 +76,11 @@ export class MiMeta { }) public sensitiveWords: string[]; + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public prohibitedWords: string[]; + @Column('varchar', { length: 1024, array: true, default: '{}', }) diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 0627c5055c..2af9e7cd9a 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -156,6 +156,13 @@ export const meta = { type: 'string', }, }, + prohibitedWords: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + }, + }, bannedEmailDomains: { type: 'array', optional: true, nullable: false, @@ -515,6 +522,7 @@ export default class extends Endpoint { // eslint- blockedHosts: instance.blockedHosts, silencedHosts: instance.silencedHosts, sensitiveWords: instance.sensitiveWords, + prohibitedWords: instance.prohibitedWords, preservedUsernames: instance.preservedUsernames, hcaptchaSecretKey: instance.hcaptchaSecretKey, mcaptchaSecretKey: instance.mcaptchaSecretKey, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index d76d3dfeea..ce8c8a505d 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -41,6 +41,11 @@ export const paramDef = { type: 'string', }, }, + prohibitedWords: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' }, mascotImageUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true }, @@ -177,6 +182,9 @@ export default class extends Endpoint { // eslint- if (Array.isArray(ps.sensitiveWords)) { set.sensitiveWords = ps.sensitiveWords.filter(Boolean); } + if (Array.isArray(ps.prohibitedWords)) { + set.prohibitedWords = ps.prohibitedWords.filter(Boolean); + } if (Array.isArray(ps.silencedHosts)) { let lastValue = ''; set.silencedHosts = ps.silencedHosts.sort().filter((h) => { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 787cda3834..50969c71cc 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -17,6 +17,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; +import { MetaService } from '@/core/MetaService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -111,6 +113,12 @@ export const meta = { code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', id: '33510210-8452-094c-6227-4a6c05d99f00', }, + + containsProhibitedWords: { + message: 'Cannot post because it contains prohibited words.', + code: 'CONTAINS_PROHIBITED_WORDS', + id: 'aa6e01d3-a85c-669d-758a-76aab43af334', + }, }, } as const; @@ -340,31 +348,40 @@ export default class extends Endpoint { // eslint- } // 投稿を作成 - const note = await this.noteCreateService.create(me, { - createdAt: new Date(), - files: files, - poll: ps.poll ? { - choices: ps.poll.choices, - multiple: ps.poll.multiple ?? false, - expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, - } : undefined, - text: ps.text ?? undefined, - reply, - renote, - cw: ps.cw, - localOnly: ps.localOnly, - reactionAcceptance: ps.reactionAcceptance, - visibility: ps.visibility, - visibleUsers, - channel, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }); + try { + const note = await this.noteCreateService.create(me, { + createdAt: new Date(), + files: files, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + text: ps.text ?? undefined, + reply, + renote, + cw: ps.cw, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUsers, + channel, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, + }); - return { - createdNote: await this.noteEntityService.pack(note, me), - }; + return { + createdNote: await this.noteEntityService.pack(note, me), + }; + } catch (e) { + // TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい + if (e instanceof NoteCreateService.ContainsProhibitedWordsError) { + throw new ApiError(meta.errors.containsProhibitedWords); + } + + throw e; + } }); } } diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index 0280b051f5..1bc8cb591c 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -16,12 +16,14 @@ describe('Note', () => { let alice: misskey.entities.SignupResponse; let bob: misskey.entities.SignupResponse; + let tom: misskey.entities.SignupResponse; beforeAll(async () => { const connection = await initTestDb(true); Notes = connection.getRepository(MiNote); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); + tom = await signup({ username: 'tom', host: 'example.com' }); }, 1000 * 60 * 2); test('投稿できる', async () => { @@ -607,6 +609,77 @@ describe('Note', () => { assert.strictEqual(note2.status, 200); assert.strictEqual(note2.body.createdNote.visibility, 'home'); }); + + test('禁止ワードを含む投稿はエラーになる (単語指定)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'test', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note1 = await api('/notes/create', { + text: 'hogetesthuge', + }, alice); + + assert.strictEqual(note1.status, 400); + assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + '/Test/i', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + const note2 = await api('/notes/create', { + text: 'hogetesthuge', + }, alice); + + assert.strictEqual(note2.status, 400); + assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'Test hoge', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + const note2 = await api('/notes/create', { + text: 'hogeTesthuge', + }, alice); + + assert.strictEqual(note2.status, 400); + assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); + }); + + test('禁止ワードを含んでいてもリモートノートはエラーにならない', async () => { + const prohibited = await api('admin/update-meta', { + prohibitedWords: [ + 'test', + ], + }, alice); + + assert.strictEqual(prohibited.status, 204); + + await new Promise(x => setTimeout(x, 2)); + + const note1 = await api('/notes/create', { + text: 'hogetesthuge', + }, tom); + + assert.strictEqual(note1.status, 200); + }); }); describe('notes/delete', () => { diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 4915bee713..248b4c53ce 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -40,6 +40,11 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + @@ -76,6 +81,7 @@ import FormLink from '@/components/form/link.vue'; const enableRegistration = ref(false); const emailRequiredForSignup = ref(false); const sensitiveWords = ref(''); +const prohibitedWords = ref(''); const hiddenTags = ref(''); const preservedUsernames = ref(''); const tosUrl = ref(null); @@ -86,6 +92,7 @@ async function init() { enableRegistration.value = !meta.disableRegistration; emailRequiredForSignup.value = meta.emailRequiredForSignup; sensitiveWords.value = meta.sensitiveWords.join('\n'); + prohibitedWords.value = meta.prohibitedWords.join('\n'); hiddenTags.value = meta.hiddenTags.join('\n'); preservedUsernames.value = meta.preservedUsernames.join('\n'); tosUrl.value = meta.tosUrl; @@ -99,6 +106,7 @@ function save() { tosUrl: tosUrl.value, privacyPolicyUrl: privacyPolicyUrl.value, sensitiveWords: sensitiveWords.value.split('\n'), + prohibitedWords: prohibitedWords.value.split('\n'), hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), }).then(() => { diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index b7d65406cb..94d6673ac5 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4659,6 +4659,7 @@ export type operations = { hiddenTags: string[]; blockedHosts: string[]; sensitiveWords: string[]; + prohibitedWords: string[]; bannedEmailDomains?: string[]; preservedUsernames: string[]; hcaptchaSecretKey: string | null; @@ -8413,6 +8414,7 @@ export type operations = { hiddenTags?: string[] | null; blockedHosts?: string[] | null; sensitiveWords?: string[] | null; + prohibitedWords?: string[] | null; themeColor?: string | null; mascotImageUrl?: string | null; bannerUrl?: string | null;