src/automod/index.ts (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
import { hasManagerPermissions } from "@/utils/discord/permissions";
import type { Client, Message, Snowflake } from "discord.js";
type UserId = Snowflake;
type Pending = {
channels: Set<string>;
timer: NodeJS.Timeout;
};
const pendingReports = new Map<UserId, Pending>();
const cooldowns = new Map<UserId, NodeJS.Timeout>();
async function getLogChannel(client: Client) {
return client.channels.fetch("1477508937287864431");
}
export async function handleMessage(client: Client, message: Message) {
if (message.author.bot || !message.guildId) return;
handleSpamLinks(client, message);
}
async function handleSpamLinks(client: Client, message: Message) {
if (message.author.bot || !message.guildId || !message.member) return;
if (await hasManagerPermissions(message.member)) return;
if (message.channelId === "1378529224192819250") return;
const { content } = message;
const blacklisted = [
"discord.gg/",
"discordapp.com/invite/",
"discord.com/invite/",
];
if (!blacklisted.some((link) => content.includes(link))) return;
if (message.channel.isSendable()) {
message.reply(
`<@${message.author.id}> your message contained an invite link and was deleted to prevent phishing attempts.`,
);
}
const logChannel = await getLogChannel(client);
if (cooldowns.has(message.author.id)) {
if (message.deletable) message.delete();
return;
}
const existing = pendingReports.get(message.author.id);
if (!existing) {
if (logChannel?.isSendable()) {
await message.forward(logChannel);
logChannel.send(
`Deleted a message from <@${message.author.id}> in <#${message.channelId}> containing a potential phishing link.`,
);
message.delete();
}
const timer = setTimeout(
() => flushReport(client, message.author.id),
30_000,
);
pendingReports.set(message.author.id, {
channels: new Set([message.channelId]),
timer,
});
return;
}
existing.channels.add(message.channelId);
clearTimeout(existing.timer);
existing.timer = setTimeout(
() => flushReport(client, message.author.id),
30_000,
);
}
async function flushReport(client: Client, authorId: UserId) {
const pending = pendingReports.get(authorId);
if (!pending) return;
pendingReports.delete(authorId);
clearTimeout(pending.timer);
const logChannel = await getLogChannel(client);
if (!logChannel?.isSendable()) return;
const channels = Array.from(pending.channels);
const channelMentions = channels.map((c) => `<#${c}>`).join(", ");
await logChannel.send(
`<@${authorId}> posted invite/phishing links across ${channels.length} channel(s): ${channelMentions}`,
);
const cooldownTimer = setTimeout(
() => cooldowns.delete(authorId),
5 * 60 * 1000,
);
cooldowns.set(authorId, cooldownTimer);
}
|