enhance(backend): センシティブワードの設定がハッシュタグトレンドにも適用されるように

This commit is contained in:
syuilo 2023-12-24 15:23:56 +09:00
parent 316ffcea54
commit 6fce36374d
4 changed files with 43 additions and 26 deletions

View File

@ -12,6 +12,17 @@
--> -->
## 2023.12.1
### General
-
### Client
-
### Server
- Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
## 2023.12.0 ## 2023.12.0
### Note ### Note

View File

@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { FeaturedService } from '@/core/FeaturedService.js'; import { FeaturedService } from '@/core/FeaturedService.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '@/core/UtilityService.js';
@Injectable() @Injectable()
export class HashtagService { export class HashtagService {
@ -29,6 +30,7 @@ export class HashtagService {
private featuredService: FeaturedService, private featuredService: FeaturedService,
private idService: IdService, private idService: IdService,
private metaService: MetaService, private metaService: MetaService,
private utilityService: UtilityService,
) { ) {
} }
@ -161,6 +163,7 @@ export class HashtagService {
const instance = await this.metaService.fetch(); const instance = await this.metaService.fetch();
const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
if (hiddenTags.includes(hashtag)) return; if (hiddenTags.includes(hashtag)) return;
if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return;
// YYYYMMDDHHmm (10分間隔) // YYYYMMDDHHmm (10分間隔)
const now = new Date(); const now = new Date();

View File

@ -253,7 +253,7 @@ export class NoteCreateService implements OnApplicationShutdown {
if (data.visibility === 'public' && data.channel == null) { if (data.visibility === 'public' && data.channel == null) {
const sensitiveWords = meta.sensitiveWords; const sensitiveWords = meta.sensitiveWords;
if (this.isSensitive(data, sensitiveWords)) { if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
data.visibility = 'home'; data.visibility = 'home';
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { } else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
data.visibility = 'home'; data.visibility = 'home';
@ -704,31 +704,6 @@ export class NoteCreateService implements OnApplicationShutdown {
this.index(note); this.index(note);
} }
@bindThis
private isSensitive(note: Option, sensitiveWord: string[]): boolean {
if (sensitiveWord.length > 0) {
const text = note.cw ?? note.text ?? '';
if (text === '') return false;
const matched = sensitiveWord.some(filter => {
// represents RegExp
const regexp = filter.match(/^\/(.+)\/(.*)$/);
// This should never happen due to input sanitisation.
if (!regexp) {
const words = filter.split(' ');
return words.every(keyword => text.includes(keyword));
}
try {
return new RE2(regexp[1], regexp[2]).test(text);
} catch (err) {
// This should never happen due to input sanitisation.
return false;
}
});
if (matched) return true;
}
return false;
}
@bindThis @bindThis
private isQuote(note: Option): note is Option & { renote: MiNote } { private isQuote(note: Option): note is Option & { renote: MiNote } {
// sync with misc/is-quote.ts // sync with misc/is-quote.ts

View File

@ -6,6 +6,7 @@
import { URL } from 'node:url'; import { URL } from 'node:url';
import { toASCII } from 'punycode'; import { toASCII } from 'punycode';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import RE2 from 're2';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -41,6 +42,33 @@ export class UtilityService {
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
} }
@bindThis
public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean {
if (sensitiveWords.length === 0) return false;
if (text === '') return false;
const regexpregexp = /^\/(.+)\/(.*)$/;
const matched = sensitiveWords.some(filter => {
// represents RegExp
const regexp = filter.match(regexpregexp);
// This should never happen due to input sanitisation.
if (!regexp) {
const words = filter.split(' ');
return words.every(keyword => text.includes(keyword));
}
try {
// TODO: RE2インスタンスをキャッシュ
return new RE2(regexp[1], regexp[2]).test(text);
} catch (err) {
// This should never happen due to input sanitisation.
return false;
}
});
return matched;
}
@bindThis @bindThis
public extractDbHost(uri: string): string { public extractDbHost(uri: string): string {
const url = new URL(uri); const url = new URL(uri);