From 5e2bcde5f5a315d7f3ddb5d42b25206fb9458642 Mon Sep 17 00:00:00 2001 From: vyneer Date: Mon, 14 Aug 2023 17:29:38 +0300 Subject: [PATCH 1/3] refactor: update banned phrases handling --- index.js | 3 +- lib/chat-utils/parse-commands-from-chat.js | 10 ++++ lib/commands/implementations/banphrase.js | 12 +++-- lib/commands/implementations/unbanphrase.js | 36 +++++++++----- lib/configuration/configure-commands.js | 15 ++++-- lib/message-routing/chat-service-router.js | 15 ++++++ lib/message-routing/message-router.js | 53 +++++++++++---------- lib/services/banned-phrase-emitter.js | 17 +++++++ lib/services/destinychat.js | 10 ++++ lib/services/service-index.js | 2 + 10 files changed, 126 insertions(+), 47 deletions(-) create mode 100644 lib/services/banned-phrase-emitter.js diff --git a/index.js b/index.js index 476dee8..2468eab 100644 --- a/index.js +++ b/index.js @@ -45,7 +45,7 @@ services .then(() => { logger.info(`Configuring for ${chatToConnectTo} chat`); const commandRouter = new CommandRouter(services); - const messageRouter = new MessageRouter({}, services); + const messageRouter = new MessageRouter({ chatConnectedTo: chatToConnectTo }, services); let bot = null; if (chatToConnectTo === 'twitch') { @@ -70,6 +70,7 @@ services services.scheduledCommands, services.fakeScheduler, services.messageRelay, + services.bannedPhrases, ); chatServiceRouter.create(); }) diff --git a/lib/chat-utils/parse-commands-from-chat.js b/lib/chat-utils/parse-commands-from-chat.js index dfa8ece..fdbd693 100644 --- a/lib/chat-utils/parse-commands-from-chat.js +++ b/lib/chat-utils/parse-commands-from-chat.js @@ -51,6 +51,14 @@ function parseCommand(message) { return { commandString: commandString.toLowerCase(), input }; } +function formatAddPhrase(phrase) { + return `ADDPHRASE ${JSON.stringify({ data: phrase })}`; +} + +function formatRemovePhrase(phrase) { + return `REMOVEPHRASE ${JSON.stringify({ data: phrase })}`; +} + module.exports = { parseMessage, parseCommand, @@ -61,4 +69,6 @@ module.exports = { formatUnban, parseWhisper, formatWhisper, + formatAddPhrase, + formatRemovePhrase, }; diff --git a/lib/commands/implementations/banphrase.js b/lib/commands/implementations/banphrase.js index 60ac8b7..567e62b 100644 --- a/lib/commands/implementations/banphrase.js +++ b/lib/commands/implementations/banphrase.js @@ -3,8 +3,13 @@ const Command = require('../command-interface'); const CommandOutput = require('../command-output'); const parseDurationToSeconds = require('../../chat-utils/duration-parser'); -function banphrase(defaultPunishmentDuration, punishmentType) { +function banphrase(chatConnectedTo, defaultPunishmentDuration, punishmentType) { return (input, services) => { + if (chatConnectedTo === 'dgg') { + const bannedPhrase = input; + services.bannedPhrases.addBannedPhrase(bannedPhrase); + return new CommandOutput(null, 'Phrase banned!'); + } const matched = /(\d+[HMDSWwhmds])?\s?(.*)/.exec(input); const duration = _.get(matched, 1, ''); const bannedPhrase = _.get(matched, 2, '').toLowerCase(); @@ -38,6 +43,7 @@ function banphrase(defaultPunishmentDuration, punishmentType) { } module.exports = { - addban: new Command(banphrase(1800, 'ban'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), - addmute: new Command(banphrase(600, 'mute'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), + addban: new Command(banphrase('twitch', 1800, 'ban'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), + addmute: new Command(banphrase('twitch', 600, 'mute'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), + addphrase: new Command(banphrase('dgg'), false, true), }; diff --git a/lib/commands/implementations/unbanphrase.js b/lib/commands/implementations/unbanphrase.js index 8989882..e822a93 100644 --- a/lib/commands/implementations/unbanphrase.js +++ b/lib/commands/implementations/unbanphrase.js @@ -2,19 +2,29 @@ const _ = require('lodash'); const Command = require('../command-interface'); const CommandOutput = require('../command-output'); -function banphrase(input, services) { - const matched = /(.*)/.exec(input); - const phraseToUnban = _.get(matched, 1, '').toLowerCase(); - if (services.spamDetection.hasBannedPhrase(phraseToUnban) === false) { - return Promise.resolve(new CommandOutput(null, 'Phrase is not registered! Did nothing.')); - } - return services.sql - .deleteBannedPhrase(phraseToUnban) - .then(() => { - services.spamDetection.removeBannedPhrase(phraseToUnban); +function banphrase(chatConnectedTo) { + return (input, services) => { + if (chatConnectedTo === 'dgg') { + const bannedPhrase = input; + services.bannedPhrases.removeBannedPhrase(bannedPhrase); return new CommandOutput(null, 'Phrase unbanned! AngelThump'); - }) - .catch((err) => new CommandOutput(err, 'Oops. Something did not work. Check the logs.')); + } + const matched = /(.*)/.exec(input); + const phraseToUnban = _.get(matched, 1, '').toLowerCase(); + if (services.spamDetection.hasBannedPhrase(phraseToUnban) === false) { + return Promise.resolve(new CommandOutput(null, 'Phrase is not registered! Did nothing.')); + } + return services.sql + .deleteBannedPhrase(phraseToUnban) + .then(() => { + services.spamDetection.removeBannedPhrase(phraseToUnban); + return new CommandOutput(null, 'Phrase unbanned! AngelThump'); + }) + .catch((err) => new CommandOutput(err, 'Oops. Something did not work. Check the logs.')); + } } -module.exports = new Command(banphrase, true, true, /(.*)/, false); +module.exports = { + unbanphrase: new Command(banphrase('twitch'), true, true, /(.*)/, false), + removephrase: new Command(banphrase('dgg'), false, true), +} diff --git a/lib/configuration/configure-commands.js b/lib/configuration/configure-commands.js index 5a2f619..6779e59 100644 --- a/lib/configuration/configure-commands.js +++ b/lib/configuration/configure-commands.js @@ -16,8 +16,8 @@ const song = require('../commands/implementations/song'); const earlierSong = require('../commands/implementations/earliersong'); const youtube = require('../commands/implementations/youtube'); const schedule = require('../commands/implementations/schedule'); -const { addban, addmute } = require('../commands/implementations/banphrase'); -const unbanphrase = require('../commands/implementations/unbanphrase'); +const { addban, addmute, addphrase } = require('../commands/implementations/banphrase'); +const { unbanphrase, removephrase } = require('../commands/implementations/unbanphrase'); const live = require('../commands/implementations/live'); const restart = require('../commands/implementations/restart'); const love = require('../commands/implementations/love'); @@ -66,9 +66,6 @@ function registerCommandsFromFiles(commandRegistry, chatConnectedTo, config) { } commandRegistry.registerCommand('!youtube', youtube, ['!video', '!lastvideo', '!yt']); commandRegistry.registerCommand('!schedule', schedule, ['!sch']); - commandRegistry.registerCommand('!addban', addban); - commandRegistry.registerCommand('!addmute', addmute); - commandRegistry.registerCommand('!deleteban', unbanphrase, ['!deletemute', '!dmute', '!dban']); commandRegistry.registerCommand('!live', live, ['!uptime']); commandRegistry.registerCommand('!restart', restart); commandRegistry.registerCommand('!love', love); @@ -85,6 +82,8 @@ function registerCommandsFromFiles(commandRegistry, chatConnectedTo, config) { commandRegistry.registerCommand('!breakingnews', breakingNews, ['!breaking', '!bn']); if (chatConnectedTo === 'dgg') { + commandRegistry.registerCommand('!addphrase', addphrase, ['!addban', '!addmute']); + commandRegistry.registerCommand('!removephrase', removephrase, ['!removeban', '!removemute', '!deletephrase', '!deleteban', '!deletemute', '!dmute', '!dban', '!dphrase']); commandRegistry.registerCommand('!voteban', voteBan); commandRegistry.registerCommand('!voteipban', voteIpban); commandRegistry.registerCommand('!svoteban', svoteBan); @@ -105,6 +104,12 @@ function registerCommandsFromFiles(commandRegistry, chatConnectedTo, config) { ]); commandRegistry.registerCommand('!gulag', gulag); } + + if (chatConnectedTo === 'twitch') { + commandRegistry.registerCommand('!addban', addban); + commandRegistry.registerCommand('!addmute', addmute); + commandRegistry.registerCommand('!removephrase', unbanphrase, ['!removeban', '!removemute', '!deletephrase', '!deleteban', '!deletemute', '!dmute', '!dban', '!dphrase']); + } } async function setupCommandsAndCachesFromDb( diff --git a/lib/message-routing/chat-service-router.js b/lib/message-routing/chat-service-router.js index 47a11e2..33d5e5d 100644 --- a/lib/message-routing/chat-service-router.js +++ b/lib/message-routing/chat-service-router.js @@ -11,6 +11,7 @@ class ChatServiceRouter { messageSchedulerStream, fakeScheduler, messageRelay, + bannedPhrases, ) { this.messageRouter = messageRouter; this.commandRouter = commandRouter; @@ -22,6 +23,7 @@ class ChatServiceRouter { this.fakeScheduler = fakeScheduler; // TODO just refactor other things to use the message relay this.messageRelay = messageRelay; + this.bannedPhrases = bannedPhrases; } create() { @@ -141,6 +143,19 @@ class ChatServiceRouter { this.bot.sendMessage(punishmentObject.reason); } }); + + this.bannedPhrases.on('data', (obj) => { + switch (obj.action) { + case 'add': + this.bot.sendAddPhrase(obj.phrase); + break; + case 'remove': + this.bot.sendRemovePhrase(obj.phrase); + break; + default: + break; + } + }) } } diff --git a/lib/message-routing/message-router.js b/lib/message-routing/message-router.js index 18386b3..4a30fb0 100644 --- a/lib/message-routing/message-router.js +++ b/lib/message-routing/message-router.js @@ -20,6 +20,7 @@ class MessageRouter { this.spamDetection = services.spamDetection; this.roleCache = services.roleCache; this.messageRelay = services.messageRelay; + this.chatConnectedTo = config.chatConnectedTo; } routeIncomingMessages(newMessage) { @@ -36,32 +37,34 @@ class MessageRouter { return; } - const bannedPhrase = this.spamDetection.checkAgainstBannedPhrases(messageContent); - - if (bannedPhrase !== false) { - if (bannedPhrase.type === 'mute') { - this.punishmentStream.write( - makeMute( - user, - bannedPhrase.duration, - `${user} muted for using banned phrase(${bannedPhrase.text}).`, - true, - ), - ); - } else if (bannedPhrase.type === 'ban') { - this.punishmentStream.write( - makeBan( - user, - bannedPhrase.duration, - null, - null, - `${user} banned for using banned phrase(${bannedPhrase.text}).`, - true, - ), - ); + if (this.chatConnectedTo === 'twitch') { + const bannedPhrase = this.spamDetection.checkAgainstBannedPhrases(messageContent); + + if (bannedPhrase !== false) { + if (bannedPhrase.type === 'mute') { + this.punishmentStream.write( + makeMute( + user, + bannedPhrase.duration, + `${user} muted for using banned phrase(${bannedPhrase.text}).`, + true, + ), + ); + } else if (bannedPhrase.type === 'ban') { + this.punishmentStream.write( + makeBan( + user, + bannedPhrase.duration, + null, + null, + `${user} banned for using banned phrase(${bannedPhrase.text}).`, + true, + ), + ); + } + this.chatCache.addMessageToRunningList(user, messageContent); + return; } - this.chatCache.addMessageToRunningList(user, messageContent); - return; } const nukedPhrases = this.punishmentCache.getNukedPhrases(); diff --git a/lib/services/banned-phrase-emitter.js b/lib/services/banned-phrase-emitter.js new file mode 100644 index 0000000..5eb1f09 --- /dev/null +++ b/lib/services/banned-phrase-emitter.js @@ -0,0 +1,17 @@ +const EventEmitter = require('events'); + +class BannedPhrases extends EventEmitter { + constructor() { + super(); + } + + addBannedPhrase(phrase) { + this.emit('data', { action: 'add', phrase }); + } + + removeBannedPhrase(phrase) { + this.emit('data', { action: 'remove', phrase }); + } +} + +module.exports = BannedPhrases; diff --git a/lib/services/destinychat.js b/lib/services/destinychat.js index 4a55a74..ea1c319 100644 --- a/lib/services/destinychat.js +++ b/lib/services/destinychat.js @@ -12,6 +12,8 @@ const { formatUnmute, formatBan, formatUnban, + formatAddPhrase, + formatRemovePhrase, } = require('../chat-utils/parse-commands-from-chat'); class DestinyChat extends EventEmitter { @@ -107,6 +109,14 @@ class DestinyChat extends EventEmitter { }); } + sendAddPhrase(phrase) { + this.ws.send(formatAddPhrase(phrase)) + } + + sendRemovePhrase(phrase) { + this.ws.send(formatRemovePhrase(phrase)) + } + handleError(event) { this.emit('error', event); } diff --git a/lib/services/service-index.js b/lib/services/service-index.js index 6ab4d0b..8de2757 100644 --- a/lib/services/service-index.js +++ b/lib/services/service-index.js @@ -19,6 +19,7 @@ const RedditVote = require('./reddit-vote'); const MessageRelay = require('./message-relay'); const messageMatchingService = require('./message-matching'); const HTMLMetadata = require('./html-metadata'); +const BannedPhrases = require('./banned-phrase-emitter') class Services { constructor(serviceConfigurations, chatConnectedTo) { @@ -49,6 +50,7 @@ class Services { if (chatConnectedTo === 'dgg') { this.redditVote = new RedditVote(serviceConfigurations.redditVote, this.logger); } + this.bannedPhrases = new BannedPhrases(); } prepareAsyncServices() { From 2a5d576be7735b3d5666a182ea03e4a1fe2c9d9e Mon Sep 17 00:00:00 2001 From: vyneer Date: Thu, 29 Feb 2024 19:37:07 +0300 Subject: [PATCH 2/3] refactor: remove `!*phrase` commands, nicer implementation in general --- lib/commands/implementations/banphrase.js | 33 +++++++++++++++------ lib/commands/implementations/unbanphrase.js | 23 +++++++------- lib/configuration/configure-commands.js | 26 +++++++++++----- lib/services/destinychat.js | 4 +-- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/lib/commands/implementations/banphrase.js b/lib/commands/implementations/banphrase.js index 438682d..e7f0c46 100644 --- a/lib/commands/implementations/banphrase.js +++ b/lib/commands/implementations/banphrase.js @@ -3,13 +3,8 @@ const Command = require('../command-interface'); const CommandOutput = require('../command-output'); const parseDurationToSeconds = require('../../chat-utils/duration-parser'); -function banphrase(chatConnectedTo, defaultPunishmentDuration, punishmentType) { +function banPhraseTwitch(defaultPunishmentDuration, punishmentType) { return (input, services) => { - if (chatConnectedTo === 'dgg') { - const bannedPhrase = input; - services.bannedPhrases.addBannedPhrase(bannedPhrase); - return new CommandOutput(null, 'Phrase banned!'); - } const matched = /(\d+[HMDSWwhmds])?\s?(.*)/.exec(input); const duration = _.get(matched, 1, ''); const bannedPhrase = _.get(matched, 2, '').toLowerCase(); @@ -55,8 +50,28 @@ function banphrase(chatConnectedTo, defaultPunishmentDuration, punishmentType) { }; } +function banPhraseDGG() { + return (input, services) => { + const bannedPhrase = input; + services.bannedPhrases.addBannedPhrase(bannedPhrase); + return new CommandOutput(null, 'Phrase banned!'); + }; +} + module.exports = { - addban: new Command(banphrase('twitch', 1800, 'ban'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), - addmute: new Command(banphrase('twitch', 600, 'mute'), true, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), - addphrase: new Command(banphrase('dgg'), false, true), + addbanTwitch: new Command( + banPhraseTwitch(1800, 'ban'), + true, + true, + /(\d+[HMDSWwhmds])?\s?(.*)/, + false, + ), + addmuteTwitch: new Command( + banPhraseTwitch(600, 'mute'), + true, + true, + /(\d+[HMDSWwhmds])?\s?(.*)/, + false, + ), + addbanDGG: new Command(banPhraseDGG(), false, true), }; diff --git a/lib/commands/implementations/unbanphrase.js b/lib/commands/implementations/unbanphrase.js index e822a93..2949de9 100644 --- a/lib/commands/implementations/unbanphrase.js +++ b/lib/commands/implementations/unbanphrase.js @@ -2,13 +2,8 @@ const _ = require('lodash'); const Command = require('../command-interface'); const CommandOutput = require('../command-output'); -function banphrase(chatConnectedTo) { +function unbanPhraseTwitch() { return (input, services) => { - if (chatConnectedTo === 'dgg') { - const bannedPhrase = input; - services.bannedPhrases.removeBannedPhrase(bannedPhrase); - return new CommandOutput(null, 'Phrase unbanned! AngelThump'); - } const matched = /(.*)/.exec(input); const phraseToUnban = _.get(matched, 1, '').toLowerCase(); if (services.spamDetection.hasBannedPhrase(phraseToUnban) === false) { @@ -21,10 +16,18 @@ function banphrase(chatConnectedTo) { return new CommandOutput(null, 'Phrase unbanned! AngelThump'); }) .catch((err) => new CommandOutput(err, 'Oops. Something did not work. Check the logs.')); - } + }; } -module.exports = { - unbanphrase: new Command(banphrase('twitch'), true, true, /(.*)/, false), - removephrase: new Command(banphrase('dgg'), false, true), +function unbanPhraseDGG() { + return (input, services) => { + const bannedPhrase = input; + services.bannedPhrases.removeBannedPhrase(bannedPhrase); + return new CommandOutput(null, 'Phrase unbanned! AngelThump'); + }; } + +module.exports = { + unbanphraseTwitch: new Command(unbanPhraseTwitch(), true, true, /(.*)/, false), + unbanphraseDGG: new Command(unbanPhraseDGG(), false, true), +}; diff --git a/lib/configuration/configure-commands.js b/lib/configuration/configure-commands.js index 6779e59..9c5f74e 100644 --- a/lib/configuration/configure-commands.js +++ b/lib/configuration/configure-commands.js @@ -16,8 +16,8 @@ const song = require('../commands/implementations/song'); const earlierSong = require('../commands/implementations/earliersong'); const youtube = require('../commands/implementations/youtube'); const schedule = require('../commands/implementations/schedule'); -const { addban, addmute, addphrase } = require('../commands/implementations/banphrase'); -const { unbanphrase, removephrase } = require('../commands/implementations/unbanphrase'); +const { addbanDGG, addbanTwitch, addmuteTwitch } = require('../commands/implementations/banphrase'); +const { unbanphraseDGG, unbanphraseTwitch } = require('../commands/implementations/unbanphrase'); const live = require('../commands/implementations/live'); const restart = require('../commands/implementations/restart'); const love = require('../commands/implementations/love'); @@ -82,8 +82,14 @@ function registerCommandsFromFiles(commandRegistry, chatConnectedTo, config) { commandRegistry.registerCommand('!breakingnews', breakingNews, ['!breaking', '!bn']); if (chatConnectedTo === 'dgg') { - commandRegistry.registerCommand('!addphrase', addphrase, ['!addban', '!addmute']); - commandRegistry.registerCommand('!removephrase', removephrase, ['!removeban', '!removemute', '!deletephrase', '!deleteban', '!deletemute', '!dmute', '!dban', '!dphrase']); + commandRegistry.registerCommand('!addban', addbanDGG, ['!addmute']); + commandRegistry.registerCommand('!deleteban', unbanphraseDGG, [ + '!deletemute', + '!removeban', + '!removemute', + '!dmute', + '!dban', + ]); commandRegistry.registerCommand('!voteban', voteBan); commandRegistry.registerCommand('!voteipban', voteIpban); commandRegistry.registerCommand('!svoteban', svoteBan); @@ -106,9 +112,15 @@ function registerCommandsFromFiles(commandRegistry, chatConnectedTo, config) { } if (chatConnectedTo === 'twitch') { - commandRegistry.registerCommand('!addban', addban); - commandRegistry.registerCommand('!addmute', addmute); - commandRegistry.registerCommand('!removephrase', unbanphrase, ['!removeban', '!removemute', '!deletephrase', '!deleteban', '!deletemute', '!dmute', '!dban', '!dphrase']); + commandRegistry.registerCommand('!addban', addbanTwitch); + commandRegistry.registerCommand('!addmute', addmuteTwitch); + commandRegistry.registerCommand('!deleteban', unbanphraseTwitch, [ + '!deletemute', + '!removeban', + '!removemute', + '!dmute', + '!dban', + ]); } } diff --git a/lib/services/destinychat.js b/lib/services/destinychat.js index 30369b0..d1b5771 100644 --- a/lib/services/destinychat.js +++ b/lib/services/destinychat.js @@ -122,11 +122,11 @@ class DestinyChat extends EventEmitter { } sendAddPhrase(phrase) { - this.ws.send(formatAddPhrase(phrase)) + this.ws.send(formatAddPhrase(phrase)); } sendRemovePhrase(phrase) { - this.ws.send(formatRemovePhrase(phrase)) + this.ws.send(formatRemovePhrase(phrase)); } handleError(event) { From d9f03711802925447c03324e49dbac5197548b0b Mon Sep 17 00:00:00 2001 From: vyneer Date: Fri, 1 Mar 2024 19:42:11 +0300 Subject: [PATCH 3/3] fix: ignore duration in new dgg `!addban` --- lib/commands/implementations/banphrase.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/commands/implementations/banphrase.js b/lib/commands/implementations/banphrase.js index e7f0c46..636d06e 100644 --- a/lib/commands/implementations/banphrase.js +++ b/lib/commands/implementations/banphrase.js @@ -52,7 +52,8 @@ function banPhraseTwitch(defaultPunishmentDuration, punishmentType) { function banPhraseDGG() { return (input, services) => { - const bannedPhrase = input; + const matched = /(\d+[HMDSWwhmds])?\s?(.*)/.exec(input); + const bannedPhrase = _.get(matched, 2, '').toLowerCase(); services.bannedPhrases.addBannedPhrase(bannedPhrase); return new CommandOutput(null, 'Phrase banned!'); }; @@ -73,5 +74,5 @@ module.exports = { /(\d+[HMDSWwhmds])?\s?(.*)/, false, ), - addbanDGG: new Command(banPhraseDGG(), false, true), + addbanDGG: new Command(banPhraseDGG(), false, true, /(\d+[HMDSWwhmds])?\s?(.*)/, false), };