From 1630e3748d05fe4facb9acd462c9eb8669afdbca Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 21 Sep 2023 15:39:15 -0300 Subject: [PATCH] move badwords to its own file --- .../services/messages/hooks/badwords.ts | 26 ++++++ .../server/services/messages/service.ts | 82 ++++++++----------- 2 files changed, 58 insertions(+), 50 deletions(-) create mode 100644 apps/meteor/server/services/messages/hooks/badwords.ts diff --git a/apps/meteor/server/services/messages/hooks/badwords.ts b/apps/meteor/server/services/messages/hooks/badwords.ts new file mode 100644 index 000000000000..17641d8f9c7f --- /dev/null +++ b/apps/meteor/server/services/messages/hooks/badwords.ts @@ -0,0 +1,26 @@ +export async function configureBadWords(badWordsList?: string, goodWordsList?: string) { + const { default: Filter } = await import('bad-words'); + + const options = { + list: + badWordsList + ?.split(',') + .map((word) => word.trim()) + .filter(Boolean) || [], + // library definition does not allow optional definition + exclude: undefined, + splitRegex: undefined, + placeHolder: undefined, + regex: undefined, + replaceRegex: undefined, + emptyList: undefined, + }; + + const badWords = new Filter(options); + + if (goodWordsList?.length) { + badWords.removeWords(...goodWordsList.split(',').map((word) => word.trim())); + } + + return badWords; +} diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 8afd4de0fa2c..9f0426de9d15 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -2,7 +2,7 @@ import type { IMessageService } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; import type { IMessage, MessageTypesValues, IUser, IRoom } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; -import Filter from 'bad-words'; +import type BadWordsFilter from 'bad-words'; import { deleteMessage } from '../../../app/lib/server/functions/deleteMessage'; import { sendMessage } from '../../../app/lib/server/functions/sendMessage'; @@ -10,47 +10,33 @@ import { updateMessage } from '../../../app/lib/server/functions/updateMessage'; import { executeSendMessage } from '../../../app/lib/server/methods/sendMessage'; import { executeSetReaction } from '../../../app/reactions/server/setReaction'; import { settings } from '../../../app/settings/server'; +import { configureBadWords } from './hooks/badwords'; -class BadWordsFilter { - private static instance: Filter; +export class MessageService extends ServiceClassInternal implements IMessageService { + protected name = 'message'; - private constructor() { - // no op - } + private badWordsFilter?: BadWordsFilter; - static configure(badWordsList?: string, goodWordsList?: string) { - const options = { - list: - badWordsList - ?.split(',') - .map((word) => word.trim()) - .filter(Boolean) || [], - // library definition does not allow optional definition - exclude: undefined, - splitRegex: undefined, - placeHolder: undefined, - regex: undefined, - replaceRegex: undefined, - emptyList: undefined, - }; - - BadWordsFilter.instance = new Filter(options); - - if (goodWordsList?.length) { - BadWordsFilter.instance.removeWords(...goodWordsList.split(',').map((word) => word.trim())); - } + async created() { + await this.configureBadWords(); } - static getFilter() { - if (!BadWordsFilter.instance) { - throw new Error('BadWordsFilter not configured'); + private async configureBadWords() { + if (!this.isBadWordsFilterEnabled()) { + return; } - return BadWordsFilter.instance; - } -} -export class MessageService extends ServiceClassInternal implements IMessageService { - protected name = 'message'; + settings.watchMultiple( + ['Message_AllowBadWordsFilter', 'Message_BadWordsFilterList', 'Message_BadWordsWhitelist'], + async ([enabled, badWordsList, whiteList]) => { + if (!enabled) { + this.badWordsFilter = undefined; + return; + } + this.badWordsFilter = await configureBadWords(badWordsList as string, whiteList as string); + }, + ); + } async sendMessage({ fromId, rid, msg }: { fromId: string; rid: string; msg: string }): Promise { return executeSendMessage(fromId, { rid, msg }); @@ -104,32 +90,28 @@ export class MessageService extends ServiceClassInternal implements IMessageServ room: IRoom; user: Pick; }): Promise { - // TODO move BadWordsFilter.configure to constructor and watch for settings changes - const badWordsList = settings.get('Message_BadWordsFilterList'); - const whiteList = settings.get('Message_BadWordsWhitelist'); - BadWordsFilter.configure(badWordsList, whiteList); - - // TODO looks like this one was not being used + // TODO looks like this one was not being used (so I'll left it commented) // await this.joinDiscussionOnMessage({ message, room, user }); - message = await this.filterBadWords(message); + // conditionals here should be fast, so they won't add up for each message + if (this.isBadWordsFilterEnabled()) { + message = await this.filterBadWords(message); + } return message; } - private async filterBadWords(message: IMessage): Promise { - if (!message.msg) { - return message; - } + private isBadWordsFilterEnabled() { + return !!settings.get('Message_AllowBadWordsFilter'); + } - if (!settings.get('Message_AllowBadWordsFilter')) { + private async filterBadWords(message: IMessage): Promise { + if (!message.msg || !this.badWordsFilter) { return message; } try { - const filter = BadWordsFilter.getFilter(); - - message.msg = filter.clean(message.msg); + message.msg = this.badWordsFilter.clean(message.msg); } catch (error) { // ignore }