From eeee8c22aabd618f5a83ddead0bc04b61fb18119 Mon Sep 17 00:00:00 2001 From: Justin Dearing Date: Sat, 21 Oct 2023 08:00:57 -0400 Subject: [PATCH] Fixes #25 (#26) * Fixes #25 * Fix the build and #22 * fix build * fixes #18 --- .eslintrc.json | 2 +- __tests__/replacer.test.js | 52 +++++++++++++++++++++++++++++++++++-- index.js | 11 +++----- jest.config.json | 8 +++--- package-lock.json | 6 ++--- replacer.js | 53 +++++++++++++++++++++++++++++++++----- 6 files changed, 109 insertions(+), 23 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 014c668..ea9a760 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,6 +13,6 @@ }, "rules": { "semi": [2, "always"], - "quotes": [2, "single", { "avoidEscape": true }] + "quotes": [2, "single", { "avoidEscape": false }] } } \ No newline at end of file diff --git a/__tests__/replacer.test.js b/__tests__/replacer.test.js index bf6db89..d1a8c70 100644 --- a/__tests__/replacer.test.js +++ b/__tests__/replacer.test.js @@ -1,4 +1,4 @@ - const { replaceFirstMessage } = require('../replacer'); + 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', @@ -6,7 +6,8 @@ describe('Tests the replacer module', () => { '!s hog wild/to the prom with another dude', '!s a real project/your boss made you', 'We never added it officially. I made a hacky one that I never checked in, and before I did zippy made a big refactor and I never integrated', - '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' + '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', + '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' ].map(content => ({ content, author: 'author' @@ -15,6 +16,34 @@ describe('Tests the replacer module', () => { const channel = { send: jest.fn() }; + + it('allows the first character of a search to be whitespace', () => { + const sut = splitReplaceCommand('!s a/b'); + + expect('a'.replace(sut.search, sut.replacement)).toBe('a'); + expect(' a'.replace(sut.search, sut.replacement)).toBe('b'); + }); + + it('tests that multiple concurrent replacementsi n asingle string get formatted correctly', () => { + const sut = splitReplaceCommand('!s z/t'); + const messages = [{ + content: 'nice peppy buzz', + author: 'author' + }]; + const expected = 'author nice peppy bu**tt**'; + const actual = replaceFirstMessage(messages, sut.search, sut.replacement, channel); + + expect(actual).toBe(false); + expect(channel.send).toBeCalledWith(expected); + }); + + it.each(['', null, undefined, false, 0])('tests the case for no replacement', (emptyIshStringIsh) => { + const regex = new RegExp('hog wild', 'gi'); + const actual = replaceFirstMessage(messages, regex, emptyIshStringIsh, channel); + + expect(actual).toBe(false); + expect(channel.send).toBeCalledWith('author No I used this for my demo so I could justify going '); + }); it('tests that the first match is what is returned', () => { const regex = new RegExp('hog wild', 'gi'); @@ -31,4 +60,23 @@ describe('Tests the replacer module', () => { expect(actual).toBe(true); expect(channel.send).not.toBeCalled(); }); + + it('cleanses string and returns URLs', () => { + const inputString = 'This is a sample string with a URL https://www.example.com and another URL http://www.example.org'; + const expectedOutput = { + cleansed: 'This is a sample string with a URL |{|url|}| and another URL |{|url|}|', + urls: ['https://www.example.com', 'http://www.example.org'] + }; + expect(extractUrls(inputString)).toEqual(expectedOutput); + }); + + it('does a replacement but ignored a url', () => { + const expected = 'author **aa** 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 **aa**'; + const sut = splitReplaceCommand('!s oo/aa'); + const actual = replaceFirstMessage(messages, sut.search, sut.replacement, channel); + + expect(actual).toBe(false); + expect(channel.send).toBeCalledWith(expected); + }); + }); \ No newline at end of file diff --git a/index.js b/index.js index 58bd522..bc52ceb 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const { Client, GatewayIntentBits } = require('discord.js'); const config = require('./config'); -const { replaceFirstMessage } = require('./replacer'); +const { replaceFirstMessage, splitReplaceCommand } = require('./replacer'); const { processScores, getScore } = require('./scoring'); @@ -50,16 +50,13 @@ client.on('messageCreate', async (initialQuery) => { { initialQuery.channel.send(initialQuery.author.toString() + ' who is one blocked message'); return; - } - - var response = initialQuery.content.replace(/!s */, '').split('/'); - const regex = new RegExp(response[0].unicodeToMerica(), 'gi'); + } let channel = initialQuery.channel; - const messages = await channel.messages.fetch({ limit: config.MessageFetchCount}); - const failedToFind = replaceFirstMessage(messages, regex, response[1], channel); + const splitMessage = splitReplaceCommand(initialQuery.content); + const failedToFind = replaceFirstMessage(messages, splitMessage.regex, splitMessage.replacement, channel); if(failedToFind) { initialQuery.channel.send(initialQuery.author.toString() + ' nobody said that, dumb ass'); } diff --git a/jest.config.json b/jest.config.json index 040d47e..cdb0cde 100644 --- a/jest.config.json +++ b/jest.config.json @@ -8,10 +8,10 @@ "clearMocks": true, "coverageThreshold": { "global": { - "branches": 43, - "functions": 53, - "lines": 55, - "statements": 53 + "branches": 45, + "functions": 61, + "lines": 60, + "statements": 59 } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 467c483..45f91af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -587,9 +587,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.0", diff --git a/replacer.js b/replacer.js index 67ad763..0fd6fcf 100644 --- a/replacer.js +++ b/replacer.js @@ -8,7 +8,7 @@ function cleanseString(strInput) { return strInput .replace(/[\u201C\u201D]/g, '"') - .replace(/[\u2018\u2019]/g, "'") + .replace(/[\u2018\u2019]/g, '\'') .replace(/\u2026/g, '...') .replace(/\u2013/g, '-') .replace(/\u2014/g, '--'); @@ -19,6 +19,21 @@ String.prototype.unicodeToMerica = function () { return cleanseString(this); }; +/** + * Takes a string as input and outputs an object with two properties: `cleansed` and `urls`. + * The `cleansed` property contains the original string but with all URLs replaced with `|{|url|}|`. + * The `urls` property is an array of all removed URLs. + * + * @param {string} inputString - The input string to cleanse. + * @returns {{cleansed: string, urls: string[]}} - An object with two properties: `cleansed` and `urls`. + */ +function extractUrls(inputString) { + const urlRegex = /((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)/gi; + const urls = inputString.match(urlRegex); + const cleansed = inputString.replace(urlRegex, '|{|url|}|'); + return { cleansed, urls }; + } + /** * Does a replace on the first message that matches regex * @@ -36,17 +51,25 @@ function replaceFirstMessage(messages, regex, replacement, channel) { return true; } - const cleansedMessageText = msg.content.unicodeToMerica(); - if(cleansedMessageText.search(regex) > -1) { + + const cleansedMessage = extractUrls(msg.content.unicodeToMerica()); + if(cleansedMessage.cleansed.search(regex) > -1) { console.log('Match found for message ' + msg.content); let replacePhrase = ''; - if(replacement.length > 0) { - replacePhrase = cleansedMessageText.replace(regex, '**' + replacement + '**'); + if(replacement?.length > 0) { + replacePhrase = cleansedMessage.cleansed + .replace(regex, '\v' + replacement + '\v') + .replace('\v\v', '') + .replace(/\v/g, '**'); + } else { replacePhrase = msg.content.replace(regex, ''); } + cleansedMessage.urls?.forEach(url => { + replacePhrase = replacePhrase.replace('|{|url|}|', url); + }); channel.send(msg.author.toString() + ' ' + replacePhrase); return false; @@ -61,6 +84,24 @@ 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}} + */ +function splitReplaceCommand(replaceCommand) { + var response = replaceCommand.replace(/!s /, '').split('/'); + const search = new RegExp(response[0].unicodeToMerica(), 'gi'); + + return { + search, + replacement: response[1] + }; +} + module.exports = { - replaceFirstMessage + extractUrls, + replaceFirstMessage, + splitReplaceCommand }; \ No newline at end of file