From 6cff5195e7406205dbebdf4aa81e5aa5f0fb270e Mon Sep 17 00:00:00 2001 From: Justin Dearing Date: Sun, 31 Mar 2024 23:30:39 -0400 Subject: [PATCH] Boggers bad (#38) * First pass * OK this probably works * It is finished --- __tests__/config.test.js | 18 ++++++++--------- __tests__/one-blocked-message.test.js | 2 +- __tests__/replacer.test.js | 29 +++++++++++++++++++++++++-- __tests__/scoring.test.js | 3 ++- config.js | 6 ++++++ index.js | 10 +++++---- jest.config.json | 10 +++++---- replacer.js | 19 ++++++++++++++++-- 8 files changed, 74 insertions(+), 23 deletions(-) diff --git a/__tests__/config.test.js b/__tests__/config.test.js index e2f27fc..940a45b 100644 --- a/__tests__/config.test.js +++ b/__tests__/config.test.js @@ -2,14 +2,8 @@ describe('Tests the config module', () => { const OLD_ENV = process.env; - beforeAll(() => { - - }); - - beforeEach(() => { - // If we mock this, then it just won't do anything, which is what we want to do. - jest.mock('dotenv'); - }); + // If we mock this, then it just won't do anything, which is what we want to do. + jest.mock('dotenv'); afterEach(() => { jest.clearAllMocks(); @@ -29,7 +23,8 @@ describe('Tests the config module', () => { 'RealestOneBlockedPercent': 5, 'TheRealests': [ 'kerouac5', - ] + ], + 'SearchPhrasesToBlock': [], }); }); @@ -68,4 +63,9 @@ describe('Tests the config module', () => { expect(config.MessageFetchCount).toEqual(Number.parseInt(element)); }); + it.each([' ', '\t\n, ,', ',,,'])('Empty whitespace only value %s are ignored for SEARCH_PHRASES_TO_BLOCK', (element) => { + process.env.SEARCH_PHRASES_TO_BLOCK = element; + const config = require('../config').getConfig(); + expect(config.SearchPhrasesToBlock).toEqual([]); + }); }); diff --git a/__tests__/one-blocked-message.test.js b/__tests__/one-blocked-message.test.js index ea0e36f..fe9eca9 100644 --- a/__tests__/one-blocked-message.test.js +++ b/__tests__/one-blocked-message.test.js @@ -1,6 +1,6 @@ const { oneBlockedMessage } = require('../one-blocked-message'); -jest.mock('dotenv'); + jest.mock('../config', () => ({ getConfig: () => ({TheRealests: [ 'testRealest' ], OneBlockedPercent: 1, diff --git a/__tests__/replacer.test.js b/__tests__/replacer.test.js index bcb8128..2357c5a 100644 --- a/__tests__/replacer.test.js +++ b/__tests__/replacer.test.js @@ -1,5 +1,13 @@ - const { replaceFirstMessage, splitReplaceCommand, extractUrls } = require('../replacer'); +jest.mock('../config'); +const config = require('../config'); +config.getConfig.mockReturnValue({ + SearchPhrasesToBlock: ['boogers', 'dong'] +}); +const { replaceFirstMessage, splitReplaceCommand, extractUrls } = require('../replacer'); + + describe('Tests the replacer module', () => { + const messages = [ 'No I used this for my demo so I could justify going hog wild', 'lol is this why you went so hog wild getting the environment set up like a real project instead of brans script kiddy level hackery?', @@ -9,7 +17,8 @@ describe('Tests the replacer module', () => { 'I guess I could get back to working on the bot but honestly I haven’t written code for a living since 2006, zippy would run circles around me', 'This is a sample string with a URL https://www.example.com and another URL http://www.example.org', 'This is not the only version, this is just an example', - 'oo https://www.google.com/search?q=aaron+burr&sca_esv=575309331&sxsrf=AM9HkKngs20KZwsuZ8WffUtq81ntoB-7ww%3A1697847658021&source=hp&ei=aRkzZeaKOciGptQPoJy78Ag&iflsig=AO6bgOgAAAAAZTMnet89R6hwn_gqlPxJYlrXn89wh42m&ved=0ahUKEwim46K074WCAxVIg4kEHSDODo4Q4dUDCA0&uact=5&oq=aaron+burr&gs_lp=Egdnd3Mtd2l6IgphYXJvbiBidXJyMgsQLhiABBixAxiDATIFEAAYgAQyCxAAGIAEGLEDGIMBMhEQLhiABBjHARivARiYBRibBTIIEC4YgAQYsQMyBRAAGIAEMgUQLhiABDIFEAAYgAQyBRAAGIAEMgsQLhivARjHARiABEiJGVCoBFi8EXABeACQAQCYAVagAZQFqgECMTC4AQPIAQD4AQGoAgrCAg0QLhjHARjRAxjqAhgnwgIHECMY6gIYJ8ICDRAuGMcBGK8BGOoCGCfCAhAQABgDGI8BGOUCGOoCGIwDwgIREC4YgAQYsQMYgwEYxwEY0QPCAgsQLhiKBRixAxiDAcICDhAuGIAEGLEDGMcBGNEDwgILEAAYigUYsQMYgwHCAgsQLhiABBjHARjRA8ICCBAAGIAEGLEDwgIIEC4YsQMYgAQ&sclient=gws-wiz oo' + 'oo https://www.google.com/search?q=aaron+burr&sca_esv=575309331&sxsrf=AM9HkKngs20KZwsuZ8WffUtq81ntoB-7ww%3A1697847658021&source=hp&ei=aRkzZeaKOciGptQPoJy78Ag&iflsig=AO6bgOgAAAAAZTMnet89R6hwn_gqlPxJYlrXn89wh42m&ved=0ahUKEwim46K074WCAxVIg4kEHSDODo4Q4dUDCA0&uact=5&oq=aaron+burr&gs_lp=Egdnd3Mtd2l6IgphYXJvbiBidXJyMgsQLhiABBixAxiDATIFEAAYgAQyCxAAGIAEGLEDGIMBMhEQLhiABBjHARivARiYBRibBTIIEC4YgAQYsQMyBRAAGIAEMgUQLhiABDIFEAAYgAQyBRAAGIAEMgsQLhivARjHARiABEiJGVCoBFi8EXABeACQAQCYAVagAZQFqgECMTC4AQPIAQD4AQGoAgrCAg0QLhjHARjRAxjqAhgnwgIHECMY6gIYJ8ICDRAuGMcBGK8BGOoCGCfCAhAQABgDGI8BGOUCGOoCGIwDwgIREC4YgAQYsQMYgwEYxwEY0QPCAgsQLhiKBRixAxiDAcICDhAuGIAEGLEDGMcBGNEDwgILEAAYigUYsQMYgwHCAgsQLhiABBjHARjRA8ICCBAAGIAEGLEDwgIIEC4YsQMYgAQ&sclient=gws-wiz oo', + 'I have boogers', ].map(content => ({ content, author: 'author' @@ -78,6 +87,7 @@ describe('Tests the replacer module', () => { const actual = replaceFirstMessage(messages, sut.search, sut.replacement, channel); expect(actual).toBe(false); + expect(sut.isBlockedPhrase).toBe(false); expect(channel.send).toBeCalledWith(expected); }); @@ -87,7 +97,22 @@ describe('Tests the replacer module', () => { const actual = replaceFirstMessage(messages, sut.search, sut.replacement, channel); expect(actual).toBe(false); + expect(sut.isBlockedPhrase).toBe(false); expect(channel.send).toBeCalledWith(expected); }); + + it('respects config.SearchPhrasesToBlock for the search', () => { + const sut = splitReplaceCommand('!s green boogers/ancedote'); + + expect(sut.isBlockedPhrase).toBe(true); + expect(channel.send).not.toBeCalled(); + }); + + it('respects config.SearchPhrasesToBlock for the replace', () => { + const sut = splitReplaceCommand('!s ancedote/dong'); + + expect(sut.isBlockedPhrase).toBe(true); + expect(channel.send).not.toBeCalled(); + }); }); \ No newline at end of file diff --git a/__tests__/scoring.test.js b/__tests__/scoring.test.js index 5a8369f..3d4ff67 100644 --- a/__tests__/scoring.test.js +++ b/__tests__/scoring.test.js @@ -24,7 +24,8 @@ describe('Tests for the scoring module', () => { 'urch++', 'poly--', 'urch++', - 'urch--' + 'urch--', + 'exercise one uncovered line', ].forEach(async (phrase) => { await processScores({ content: phrase }); }); diff --git a/config.js b/config.js index bea1d90..85340c8 100644 --- a/config.js +++ b/config.js @@ -4,6 +4,7 @@ const getConfig = () => { const messesageFetchCount = Number.parseInt(process.env.MESSAGE_FETCH_COUNT); const oneBlockedPercent = Number.parseFloat(process.env.ONE_BLOCKED_PERCENT); const realestOneBlockedPercentneBlockedPercent = Number.parseFloat(process.env.REALEST_ONE_BLOCKED_PERCENT); + const searchPhrasesToBlock = (process.env.SEARCH_PHRASES_TO_BLOCK ?? '').split(',').filter(phrase => phrase.trim() !== ''); return { /** @@ -41,6 +42,11 @@ const getConfig = () => { * Percentage change of getting "who is one blocked message" for the realest. */ RealestOneBlockedPercent: (realestOneBlockedPercentneBlockedPercent >= 0) ? realestOneBlockedPercentneBlockedPercent : 5, + + /** + * Phrases to ignore in a search and replace. + */ + SearchPhrasesToBlock : searchPhrasesToBlock, /** * The discord token. diff --git a/index.js b/index.js index e97e86c..34b3a45 100644 --- a/index.js +++ b/index.js @@ -36,12 +36,14 @@ client.on('messageCreate', async (initialQuery) => { } let channel = initialQuery.channel; - + // TODO: we need to encapsulate all thse calls to replacer functions in another module because SOLID const messages = await channel.messages.fetch({ limit: config.MessageFetchCount}); const splitMessage = splitReplaceCommand(initialQuery.content); - const failedToFind = replaceFirstMessage(messages, splitMessage.search, splitMessage.replacement, channel); - if(failedToFind) { - initialQuery.channel.send(initialQuery.author.toString() + ' nobody said that, dumb ass'); + if(!splitMessage.isBlockedPhrase) { + const failedToFind = replaceFirstMessage(messages, splitMessage.search, splitMessage.replacement, channel); + if(failedToFind) { + initialQuery.channel.send(initialQuery.author.toString() + ' nobody said that, dumb ass'); + } } } else if (initialQuery.content.indexOf('!score ') == 0) diff --git a/jest.config.json b/jest.config.json index 9fc827e..ea70885 100644 --- a/jest.config.json +++ b/jest.config.json @@ -6,12 +6,14 @@ "!./coverage/**" ], "clearMocks": true, - "coverageThreshold": { + "resetMocks": true, + "resetModules": true, + "coverageThreshold": { "global": { - "branches": 56, - "functions": 75, + "branches": 59, + "functions": 77, "lines": 66, - "statements": 65 + "statements": 66 } } } \ No newline at end of file diff --git a/replacer.js b/replacer.js index 76e0a35..6d366d1 100644 --- a/replacer.js +++ b/replacer.js @@ -1,3 +1,5 @@ +const config = require('./config').getConfig(); + /** * Function that takes a string and then returns a "dumbed down" version * of it. Without smart quotes, etc. @@ -88,18 +90,31 @@ function replaceFirstMessage(messages, regex, replacement, channel) { * Takes the replace message and turn it into a searh regex and a replace message * * @param {string} replaceCommand - * @returns {{search:RegExp, replacement:string}} + * @returns {{search:RegExp, isBlockedPhrase:boolean, replacement:string}} */ function splitReplaceCommand(replaceCommand) { var response = replaceCommand.replace(/!s /, '').split('/'); const search = new RegExp(response[0].unicodeToMerica(), 'gi'); + const replacement = response[1]; return { search, - replacement: response[1] + isBlockedPhrase: isBlockedSearchPhrase(response[0]) || isBlockedSearchPhrase(replacement), + replacement }; } +/** + * Indicates if a phrase is blocekd from search and replace. + * @param {string} phrase The phrase to check to see if its blocked. + * @returns true if the phrase is blocked. False otherwise. + */ +function isBlockedSearchPhrase(phrase) { + return config + .SearchPhrasesToBlock + .findIndex(blockedPhrase => phrase.match(new RegExp(blockedPhrase.normalize('NFD').replace(/[\u0300-\u036f]/g, ''), 'iu'))) > -1; +} + module.exports = { extractUrls, replaceFirstMessage,