all repos — stealth-developers @ 103b51d2a555d6d158d8806791b6ec07ac30c3d1

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);
}