diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts new file mode 100644 index 0000000..0549c36 --- /dev/null +++ b/src/events/channelCreate.ts @@ -0,0 +1,109 @@ +import client from "../client"; +import { AuditLogEvent, Events, GuildAuditLogsEntry, PermissionFlagsBits, User } from "discord.js"; +import { getGuildSetting } from "../tools/data"; +import { Badword, database } from "../data"; +import { IsNull } from "typeorm"; +import getDefaultEmbed, { getFailedEmbed } from "../tools/defaultEmbeds"; +import { getGuildChannel } from "../tools/discord"; +import { Color, Emoji } from "../tools/design"; + +client.on(Events.ChannelCreate, async (newChannel) => { + if (newChannel.isDMBased()) return; + const name = newChannel.name.toLowerCase(); + + const guild = newChannel.guild; + const settings = await getGuildSetting(guild.id); + + const globalBlocklist = await database.getRepository(Badword).find({ + where: { + guildID: IsNull() + } + }); + const localBlocklist = await database.getRepository(Badword).find({ + where: { + guildID: guild.id + } + }); + + const blocklist = [...globalBlocklist, ...localBlocklist]; + let found: string | null = null; + + for (let i = 0; i < blocklist.length; i++) { + const word = blocklist[i]; + if (!word) continue; + + if (!name.includes(word.value)) continue; + found = word.value; + break; + } + + if (found === null) return; + + let responsibleUser: User | null; + + try { + const clientMember = await guild.members.fetchMe(); + const canSeeAuditLog = clientMember.permissions.has(PermissionFlagsBits.ViewAuditLog); + const auditLogs = canSeeAuditLog ? await guild.fetchAuditLogs({ + type: AuditLogEvent.ChannelCreate, + limit: 50 + }) : undefined; + + const change = auditLogs?.entries.filter((entry) => { + if (entry.target.id !== newChannel.id) return false; + + return entry.changes.filter((change) => { + return change.key === "name" && change.new === newChannel.name; + }).length > 0; + }).reduce>((unknown, curr) => { + if (!unknown) return curr; + const prev = unknown as GuildAuditLogsEntry + return curr.createdTimestamp > prev.createdTimestamp ? curr : prev; + }, null); + + responsibleUser = change?.executor || null; + } catch (error) { + responsibleUser = null; + } + + const logChannel = settings.notificationChannelID ? await getGuildChannel(guild.id, settings.notificationChannelID) : null; + + try { + if (!newChannel.deletable) throw new Error("Missing permissions to delete channel"); + await newChannel.delete("[Automated] Detected blocked word in channel name"); + } catch (error) { + if (!logChannel || !logChannel.isTextBased()) return; + const embed = getFailedEmbed(); + embed.setDescription(`Couldn't delete <#${newChannel.id}> (${newChannel.id}): ${error instanceof Error ? error.message : error}`); + embed.addFields({ + name: "Detected banned word:", + value: `||${found}||` + }, { + name: "Channel created by:", + value: responsibleUser ? `${responsibleUser.tag} (${responsibleUser.id})` : "`Couldn't detect responsible user :(`" + }); + + logChannel.send({ + embeds: [embed] + }).catch(() => {}); + return; + } + + if (!logChannel || !logChannel.isTextBased()) return; + const embed = getDefaultEmbed(); + embed.setTitle(`${Emoji.SECURITY_CHALLENGE_FAILED} Blocked word detected`); + embed.setDescription(`||#${newChannel.name}|| (${newChannel.id}) has been deleted because its name contained a blocked word.`); + embed.setColor(Color.WARNING_YELLOW); + embed.addFields({ + name: "Detected banned word:", + value: `||${found}||`, + inline: true + }, { + name: "Channel created by:", + value: responsibleUser ? `${responsibleUser.tag} (${responsibleUser.id})` : "`Couldn't detect responsible user :(`" + }); + + logChannel.send({ + embeds: [embed] + }).catch(() => {}); +}); \ No newline at end of file diff --git a/src/events/index.ts b/src/events/index.ts index ab87147..ca7292e 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,2 +1,3 @@ import "./channelUpdate"; -import "./guildDelete"; \ No newline at end of file +import "./guildDelete"; +import "./channelCreate"; \ No newline at end of file