Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add crosspost detection #75

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
38 changes: 38 additions & 0 deletions modules/crosspost/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 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);

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}> <t:${(message.createdTimestamp / 1000).toFixed(0)}:R>.

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);
}
});
};
38 changes: 38 additions & 0 deletions modules/crosspost/store.js
Original file line number Diff line number Diff line change
@@ -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;