From a9a120d3fc4db53d98fbf041592a930bdc87dd86 Mon Sep 17 00:00:00 2001 From: Jay Williams Date: Tue, 21 Feb 2023 16:24:10 +0000 Subject: [PATCH 1/2] Add crosspost detection feature --- .env.example | 6 ++++++ modules/crosspost/index.js | 35 +++++++++++++++++++++++++++++++++++ modules/crosspost/store.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 modules/crosspost/index.js create mode 100644 modules/crosspost/store.js diff --git a/.env.example b/.env.example index 09a78dd..e7cf911 100644 --- a/.env.example +++ b/.env.example @@ -19,5 +19,11 @@ DISCORD_PATREON_CHANNEL = # Discord role ID to add to users when they first join DISCORD_MEMBER_ROLE = +# Discord channel IDs which should be checked for crossposting. Comma separated +DISCORD_CROSSPOST_CHECK_CHANNELS = + +# (OPTIONAL) Number of historical messages to check for crossposts. Default: 10 +# DISCORD_CROSSPOST_HISTORY_MESSAGES = 10 + # Sensitivity for the command trigger - defaults to 0.5 SIMILARITY_SENSITIVITY = diff --git a/modules/crosspost/index.js b/modules/crosspost/index.js new file mode 100644 index 0000000..9c55355 --- /dev/null +++ b/modules/crosspost/index.js @@ -0,0 +1,35 @@ +const { MessageEmbed } = require('discord.js'); +const store = require("./store"); + +module.exports = function (client) { + client.on('message', async message => { + // Ignore bots, DMs, etc + if (!message.author || !message.channel || !message.guild) return; + + // Ensure we have crosspost checks enabled for this channel before doing anything + if (!store.channelEnabled(message.channel)) return; + + // Check if the user has already recently posted a very similar message + let previousMessage = store.findMatch(message); + + if (previousMessage) { + // Remove the message from the cache so a double-warning isn't possible + store.removeMessage(previousMessage); + // Reply with warning message + await message.reply( + new MessageEmbed() + .setTitle(`<:crosspost:999440431521742928> It looks like you already posted that...`) + .setColor(`#94df03`) + .setURL(`https://discord.com/channels/${previousMessage.guild.id}/${previousMessage.channel.id}/${previousMessage.id}`) + .setDescription( + `<@${message.author.id}> sent the same message to <#${previousMessage.channel.id}> . + + It's best to ask your question in just a single channel otherwise it can cause confusion between those trying to help!` + ) + ); + } else { + // Else, log the message in case the user crossposts it in future + store.addMessage(message); + } + }); +}; diff --git a/modules/crosspost/store.js b/modules/crosspost/store.js new file mode 100644 index 0000000..65854ee --- /dev/null +++ b/modules/crosspost/store.js @@ -0,0 +1,38 @@ +let recentMessages = []; + +crosspostMessageStore = { + channelEnabled(channel) { + // If anything is empty or undefined then return false + if (!process.env.DISCORD_CROSSPOST_CHECK_CHANNELS || !channel) return false; + return process.env.DISCORD_CROSSPOST_CHECK_CHANNELS.split(",").includes(channel.id); + }, + + addMessage(message) { + recentMessages.push(message); + // Ensure we have the right amount of messages still in the cache + module.exports.cleanup(); + }, + + cleanup() { + recentMessages = recentMessages.slice(0 - parseInt(process.env.DISCORD_CROSSPOST_HISTORY_MESSAGES ?? 10)); + }, + + removeMessage(message) { + recentMessages = recentMessages.filter(m => !m.equals(message)); + }, + + findMatch(message) { + for (const possibleMatch of recentMessages) { + // Ensure match has the same author + if (!possibleMatch.author || !possibleMatch.author.equals(message.author)) continue; + // Ensure both messages have a non-empty content + if (!possibleMatch.content || !message.content || possibleMatch.content === "" || message.content === "") continue; + // Ensure they are in different channels + if (!possibleMatch.channel || !message.channel || message.channel.equals(possibleMatch.channel)) continue; + // If the content is the same, then this is a match - return it + if (possibleMatch.content.trim() === message.content.trim()) return possibleMatch; + } + } +}; + +module.exports = crosspostMessageStore; From b56e333ff5a0b28540263082ddb47fcc4a144119 Mon Sep 17 00:00:00 2001 From: Jay Williams Date: Tue, 21 Feb 2023 16:28:20 +0000 Subject: [PATCH 2/2] Limit to only messages over 50 chars to avoid false positives --- modules/crosspost/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/crosspost/index.js b/modules/crosspost/index.js index 9c55355..8b12d43 100644 --- a/modules/crosspost/index.js +++ b/modules/crosspost/index.js @@ -9,6 +9,9 @@ module.exports = function (client) { // Ensure we have crosspost checks enabled for this channel before doing anything if (!store.channelEnabled(message.channel)) return; + // Check to make sure the message is longer than 50 characters so that small messages like ok or thanks don't get caught + if (message.content.length < 50) return; + // Check if the user has already recently posted a very similar message let previousMessage = store.findMatch(message);