diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-05-12 10:33:25 +0000 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-05-12 10:33:25 +0000 |
| commit | 835e76152e982bc6f8bfc09d7afa1aba4d872367 (patch) | |
| tree | 37632f56ebf45c7841a9960266fdaebfd5e00417 /packages/frontend | |
| parent | merge: Fix hidden hashtags showing on the explore / trending page (!1014) (diff) | |
| parent | rename SkWordMuteTest to SkPatternTest (diff) | |
| download | sharkey-835e76152e982bc6f8bfc09d7afa1aba4d872367.tar.gz sharkey-835e76152e982bc6f8bfc09d7afa1aba4d872367.tar.bz2 sharkey-835e76152e982bc6f8bfc09d7afa1aba4d872367.zip | |
merge: Add pattern checker for word mutes (resolves #1003) (!1020)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1020
Closes #1003
Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
Diffstat (limited to 'packages/frontend')
| -rw-r--r-- | packages/frontend/src/components/SkPatternTest.vue | 57 | ||||
| -rw-r--r-- | packages/frontend/src/pages/admin/moderation.vue | 13 | ||||
| -rw-r--r-- | packages/frontend/src/pages/settings/mute-block.word-mute.vue | 50 | ||||
| -rw-r--r-- | packages/frontend/src/utility/check-word-mute.ts | 6 | ||||
| -rw-r--r-- | packages/frontend/src/utility/parse-mutes.ts | 41 |
5 files changed, 125 insertions, 42 deletions
diff --git a/packages/frontend/src/components/SkPatternTest.vue b/packages/frontend/src/components/SkPatternTest.vue new file mode 100644 index 0000000000..2ed2b3fdc3 --- /dev/null +++ b/packages/frontend/src/components/SkPatternTest.vue @@ -0,0 +1,57 @@ +<!-- +SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkFolder> + <template #label>{{ i18n.ts.wordMuteTestLabel }}</template> + + <div class="_gaps"> + <MkTextarea v-model="testWords"> + <template #caption>{{ i18n.ts.wordMuteTestDescription }}</template> + </MkTextarea> + <div><MkButton :disabled="!testWords" @click="testWordMutes">{{ i18n.ts.wordMuteTestTest }}</MkButton></div> + <div v-if="testMatches == null">{{ i18n.ts.wordMuteTestNoResults}}</div> + <div v-else-if="testMatches === ''">{{ i18n.ts.wordMuteTestNoMatch }}</div> + <div v-else>{{ i18n.tsx.wordMuteTestMatch({ words: testMatches }) }}</div> + </div> +</MkFolder> +</template> + +<script setup lang="ts"> +import { ref } from 'vue'; +import { i18n } from '@/i18n'; +import MkFolder from '@/components/MkFolder.vue'; +import MkButton from '@/components/MkButton.vue'; +import MkTextarea from '@/components/MkTextarea.vue'; +import { parseMutes } from '@/utility/parse-mutes'; +import { checkWordMute } from '@/utility/check-word-mute'; + +const props = defineProps<{ + mutedWords?: string | null, +}>(); + +const testWords = ref<string | null>(null); +const testMatches = ref<string | null>(null); + +function testWordMutes() { + if (!testWords.value || !props.mutedWords) { + testMatches.value = null; + return; + } + + try { + const mutes = parseMutes(props.mutedWords); + const matches = checkWordMute(testWords.value, null, mutes); + testMatches.value = matches ? matches.flat(2).join(', ') : ''; + } catch { + // Error is displayed by above function + testMatches.value = null; + } +} +</script> + +<style module lang="scss"> + +</style> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 9675bdc21a..6845bd7ad0 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -47,6 +47,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTextarea v-model="trustedLinkUrlPatterns"> <template #caption>{{ i18n.ts.trustedLinkUrlPatternsDescription }}</template> </MkTextarea> + + <SkPatternTest :mutedWords="trustedLinkUrlPatterns"></SkPatternTest> + <MkButton primary @click="save_trustedLinkUrlPatterns">{{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -71,6 +74,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTextarea v-model="sensitiveWords"> <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> </MkTextarea> + + <SkPatternTest :mutedWords="sensitiveWords"></SkPatternTest> + <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -83,6 +89,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTextarea v-model="prohibitedWords"> <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> </MkTextarea> + + <SkPatternTest :mutedWords="prohibitedWords"></SkPatternTest> + <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -95,6 +104,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTextarea v-model="prohibitedWordsForNameOfUser"> <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> </MkTextarea> + + <SkPatternTest :mutedWords="prohibitedWordsForNameOfUser"></SkPatternTest> + <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -166,6 +178,7 @@ import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; import FormLink from '@/components/form/link.vue'; import MkFolder from '@/components/MkFolder.vue'; +import SkPatternTest from '@/components/SkPatternTest.vue'; const enableRegistration = ref<boolean>(false); const emailRequiredForSignup = ref<boolean>(false); diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue index f5837abe98..36b388b848 100644 --- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue @@ -11,6 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </MkTextarea> </div> + + <SkPatternTest :mutedWords="mutedWords"></SkPatternTest> + <MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </template> @@ -19,8 +22,9 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, watch } from 'vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; +import { parseMutes } from '@/utility/parse-mutes'; +import SkPatternTest from '@/components/SkPatternTest.vue'; const props = defineProps<{ muted: (string[] | string)[]; @@ -30,7 +34,7 @@ const emit = defineEmits<{ (ev: 'save', value: (string[] | string)[]): void; }>(); -const render = (mutedWords) => mutedWords.map(x => { +const render = (mutedWords: (string | string[])[]) => mutedWords.map(x => { if (Array.isArray(x)) { return x.join(' '); } else { @@ -46,47 +50,15 @@ watch(mutedWords, () => { }); async function save() { - const parseMutes = (mutes) => { - // split into lines, remove empty lines and unnecessary whitespace - let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== ''); - - // check each line if it is a RegExp or not - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const regexp = line.match(/^\/(.+)\/(.*)$/); - if (regexp) { - // check that the RegExp is valid - try { - new RegExp(regexp[1], regexp[2]); - // note that regex lines will not be split by spaces! - } catch (err: any) { - // invalid syntax: do not save, do not reset changed flag - os.alert({ - type: 'error', - title: i18n.ts.regexpError, - text: i18n.tsx.regexpErrorDescription({ tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), - }); - // re-throw error so these invalid settings are not saved - throw err; - } - } else { - lines[i] = line.split(' '); - } - } + try { + const parsed = parseMutes(mutedWords.value); - return lines; - }; + emit('save', parsed); - let parsed; - try { - parsed = parseMutes(mutedWords.value); - } catch (err) { + changed.value = false; + } catch { // already displayed error message in parseMutes return; } - - emit('save', parsed); - - changed.value = false; } </script> diff --git a/packages/frontend/src/utility/check-word-mute.ts b/packages/frontend/src/utility/check-word-mute.ts index 37ad678555..26bf593f7b 100644 --- a/packages/frontend/src/utility/check-word-mute.ts +++ b/packages/frontend/src/utility/check-word-mute.ts @@ -4,12 +4,12 @@ */ 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 { +export function checkWordMute(note: string | 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 (me && typeof(note) === 'object' && (note.userId === me.id)) return false; if (mutedWords.length > 0) { - const text = getNoteText(note); + const text = typeof(note) === 'object' ? getNoteText(note) : note; if (text === '') return false; diff --git a/packages/frontend/src/utility/parse-mutes.ts b/packages/frontend/src/utility/parse-mutes.ts new file mode 100644 index 0000000000..1ebd5bcf83 --- /dev/null +++ b/packages/frontend/src/utility/parse-mutes.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +export type Mutes = (string | string[])[]; + +export function parseMutes(mutes: string): Mutes { + // split into lines, remove empty lines and unnecessary whitespace + const lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== ''); + const outLines: Mutes = Array.from(lines); + + // check each line if it is a RegExp or not + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const regexp = line.match(/^\/(.+)\/(.*)$/); + if (regexp) { + // check that the RegExp is valid + try { + new RegExp(regexp[1], regexp[2]); + // note that regex lines will not be split by spaces! + } catch (err: any) { + // invalid syntax: do not save, do not reset changed flag + os.alert({ + type: 'error', + title: i18n.ts.regexpError, + text: i18n.tsx.regexpErrorDescription({ tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), + }); + // re-throw error so these invalid settings are not saved + throw err; + } + } else { + outLines[i] = line.split(' '); + } + } + + return outLines; +} |