finished automod

pull/11/head
TheCodedProf 3 years ago
parent 486bca3c43
commit 5b53a8c56b

@ -1,10 +1,34 @@
import type Discord from "discord.js";
import { ActionRowBuilder, AnyComponent, AnyComponentBuilder, AnySelectMenuInteraction, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, ChannelSelectMenuBuilder, ChannelSelectMenuInteraction, CommandInteraction, Guild, GuildMember, GuildTextBasedChannel, MentionableSelectMenuBuilder, Message, Role, RoleSelectMenuBuilder, RoleSelectMenuInteraction, SelectMenuBuilder, StringSelectMenuBuilder, StringSelectMenuInteraction, StringSelectMenuOptionBuilder, UserSelectMenuBuilder, UserSelectMenuInteraction } from "discord.js";
import { ActionRowBuilder,
AnySelectMenuInteraction,
APIMessageComponentEmoji,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
ChannelSelectMenuBuilder,
ChannelSelectMenuInteraction,
CommandInteraction,
Interaction,
Message,
MessageComponentInteraction,
ModalBuilder,
ModalSubmitInteraction,
RoleSelectMenuBuilder,
RoleSelectMenuInteraction,
StringSelectMenuBuilder,
StringSelectMenuInteraction,
StringSelectMenuOptionBuilder,
TextInputBuilder,
TextInputStyle,
UserSelectMenuBuilder,
UserSelectMenuInteraction
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import { LoadingEmbed } from "../../utils/defaults.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@ -75,7 +99,7 @@ const toSelectMenu = async (interaction: StringSelectMenuInteraction, m: Message
let i: AnySelectMenuInteraction | ButtonInteraction;
try {
i = await interaction.channel!.awaitMessageComponent({filter: i => i.user.id === interaction.user.id, time: 300000});
i = await m.awaitMessageComponent({filter: i => i.user.id === interaction.user.id, time: 300000});
} catch(e) {
closed = true;
break;
@ -177,7 +201,152 @@ const wordMenu = async (interaction: StringSelectMenuInteraction, m: Message, cu
}> => {
let closed = false;
do {
closed = true;
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("enabled")
.setLabel("Enabled")
.setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji),
);
const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("edit")
.setPlaceholder("Edit... ")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Words")
.setDescription("Edit your list of words to filter")
.setValue("words"),
new StringSelectMenuOptionBuilder()
.setLabel("Allowed Users")
.setDescription("Users who will be unaffected by the word filter")
.setValue("allowedUsers"),
new StringSelectMenuOptionBuilder()
.setLabel("Allowed Roles")
.setDescription("Roles that will be unaffected by the word filter")
.setValue("allowedRoles"),
new StringSelectMenuOptionBuilder()
.setLabel("Allowed Channels")
.setDescription("Channels where the word filter will not apply")
.setValue("allowedChannels")
)
.setDisabled(!current.enabled)
);
const embed = new EmojiEmbed()
.setTitle("Word Filters")
.setDescription(
`${emojiFromBoolean(current.enabled)} **Enabled**\n` +
`**Strict Words:** ${listToAndMore(current.words.strict, 5)}\n` +
`**Loose Words:** ${listToAndMore(current.words.loose, 5)}\n\n` +
`**Users:** ` + listToAndMore(current.allowed.users.map(user => `<@${user}>`), 5) + `\n` +
`**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `<@&${role}>`), 5) + `\n` +
`**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `<#${channel}>`), 5)
)
.setStatus("Success")
.setEmoji("GUILD.SETTINGS.GREEN")
await interaction.editReply({embeds: [embed], components: [selectMenu, buttons]});
let i: ButtonInteraction | StringSelectMenuInteraction;
try {
i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
} catch (e) {
closed = true;
break;
}
if(i.isButton()) {
await i.deferUpdate();
switch(i.customId) {
case "back":
closed = true;
break;
case "enabled":
current.enabled = !current.enabled;
break;
}
} else {
switch(i.values[0]) {
case "words":
await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Word Filter")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
.setEmoji("GUILD.SETTINGS.GREEN")
], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("back")
)]})
const modal = new ModalBuilder()
.setTitle("Word Filter")
.setCustomId("wordFilter")
.addComponents(
new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId("wordStrict")
.setLabel("Strict Words")
.setPlaceholder("Matches anywhere in the message, including surrounded by other characters")
.setValue(current.words.strict.join(", ") ?? "")
.setStyle(TextInputStyle.Paragraph)
.setRequired(false)
),
new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId("wordLoose")
.setLabel("Loose Words")
.setPlaceholder("Matches only if the word is by itself, surrounded by spaces or punctuation")
.setValue(current.words.loose.join(", ") ?? "")
.setStyle(TextInputStyle.Paragraph)
.setRequired(false)
)
)
await i.showModal(modal);
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
(m) => m.customId === "back"
);
} catch (e) {
break;
}
if (!out) break;
if(out.isButton()) break;
current.words.strict = out.fields.getTextInputValue("wordStrict")
.split(",").map(s => s.trim()).filter(s => s.length > 0);
current.words.loose = out.fields.getTextInputValue("wordLoose")
.split(",").map(s => s.trim()).filter(s => s.length > 0);
break;
case "allowedUsers":
await i.deferUpdate();
current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Word Filter");
break;
case "allowedRoles":
await i.deferUpdate();
current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Word Filter");
break;
case "allowedChannels":
await i.deferUpdate();
current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Word Filter");
break;
}
}
} while(!closed);
return current;
}
@ -231,15 +400,15 @@ const inviteMenu = async (interaction: StringSelectMenuInteraction, m: Message,
.setDescription(
"Automatically deletes invites sent by users (outside of staff members and self promotion channels)" + `\n\n` +
`${emojiFromBoolean(current.enabled)} **${current.enabled ? "Enabled" : "Disabled"}**\n\n` +
`**Users:** ` + listToAndMore(current.allowed.users.map(user => `> <@${user}>`), 5) + `\n` +
`**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `> <@&${role}>`), 5) + `\n` +
`**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `> <#${channel}>`), 5)
`**Users:** ` + listToAndMore(current.allowed.users.map(user => `<@${user}>`), 5) + `\n` +
`**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `<@&${role}>`), 5) + `\n` +
`**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `<#${channel}>`), 5)
)
.setStatus("Success")
.setEmoji("GUILD.SETTINGS.GREEN")
await interaction.editReply({embeds: [embed], components: [buttons, menu]});
await interaction.editReply({embeds: [embed], components: [menu, buttons]});
let i: ButtonInteraction | StringSelectMenuInteraction;
try {
@ -301,7 +470,172 @@ const mentionMenu = async (interaction: StringSelectMenuInteraction, m: Message,
let closed = false;
do {
closed = true;
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("everyone")
.setLabel(current.everyone ? "Everyone" : "No one")
.setStyle(current.everyone ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(emojiFromBoolean(current.everyone, "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("roles")
.setLabel(current.roles ? "Roles" : "No roles")
.setStyle(current.roles ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(emojiFromBoolean(current.roles, "id") as APIMessageComponentEmoji)
);
const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("toEdit")
.setPlaceholder("Edit mention settings")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Mass Mention Amount")
.setDescription("The amount of mentions before the bot will delete the message")
.setValue("mass"),
new StringSelectMenuOptionBuilder()
.setLabel("Roles")
.setDescription("Roles that are able to be mentioned")
.setValue("roles"),
)
)
const allowedMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("allowed")
.setPlaceholder("Edit exceptions")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Users")
.setDescription("Users that are unaffected by the mention filter")
.setValue("users"),
new StringSelectMenuOptionBuilder()
.setLabel("Roles")
.setDescription("Roles that are unaffected by the mention filter")
.setValue("roles"),
new StringSelectMenuOptionBuilder()
.setLabel("Channels")
.setDescription("Channels where anyone is unaffected by the mention filter")
.setValue("channels")
)
)
const embed = new EmojiEmbed()
.setTitle("Mention Settings")
.setDescription(
`Log when members mention:\n` +
`${emojiFromBoolean(true)} **${current.mass}+ members** in one message\n` +
`${emojiFromBoolean(current.everyone)} **Everyone**\n` +
`${emojiFromBoolean(current.roles)} **Roles**\n` +
(current.allowed.rolesToMention.length > 0 ? `> *Except for ${listToAndMore(current.allowed.rolesToMention.map(r => `<@&${r}>`), 3)}*\n` : "") +
"\n" +
`Except if...\n` +
`> ${current.allowed.users.length > 0 ? `Member is: ${listToAndMore(current.allowed.users.map(u => `<@${u}>`), 3)}\n` : ""}` +
`> ${current.allowed.roles.length > 0 ? `Member has role: ${listToAndMore(current.allowed.roles.map(r => `<@&${r}>`), 3)}\n` : ""}` +
`> ${current.allowed.channels.length > 0 ? `In channel: ${listToAndMore(current.allowed.channels.map(c => `<#${c}>`), 3)}\n` : ""}`
)
.setStatus("Success")
.setEmoji("GUILD.SETTINGS.GREEN")
await interaction.editReply({embeds: [embed], components: [menu, allowedMenu, buttons]});
let i: ButtonInteraction | StringSelectMenuInteraction;
try {
i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
} catch (e) {
closed = true;
break;
}
if(i.isButton()) {
await i.deferUpdate();
switch (i.customId) {
case "back":
closed = true;
break;
case "everyone":
current.everyone = !current.everyone;
break;
case "roles":
current.roles = !current.roles;
break;
}
} else {
switch (i.customId) {
case "toEdit":
switch (i.values[0]) {
case "mass":
await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Word Filter")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
.setEmoji("GUILD.SETTINGS.GREEN")
], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("back")
)]})
const modal = new ModalBuilder()
.setTitle("Mass Mention Amount")
.setCustomId("mass")
.addComponents(
new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId("mass")
.setPlaceholder("Amount")
.setMinLength(1)
.setMaxLength(3)
.setStyle(TextInputStyle.Short)
)
)
await i.showModal(modal);
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
(m) => m.customId === "back"
);
} catch (e) {
break;
}
if (!out) break;
if(out.isButton()) break;
current.mass = parseInt(out.fields.getTextInputValue("mass"));
break;
case "roles":
await i.deferUpdate();
current.allowed.rolesToMention = await toSelectMenu(interaction, m, current.allowed.rolesToMention, "role", "Mention Settings");
break;
}
break;
case "allowed":
await i.deferUpdate();
switch (i.values[0]) {
case "users":
current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Mention Settings");
break;
case "roles":
current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Mention Settings");
break;
case "channels":
current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Mention Settings");
break;
}
break;
}
}
} while(!closed);
return current
}

@ -6,7 +6,7 @@ export const event = "messageDelete";
export async function callback(client: NucleusClient, message: Message) {
if (message.author.id === client.user!.id) return;
if (message.author.bot) return;
if (client.noLog.includes(`${message.id}/${message.channel.id}/${message.id}`)) return;
if (client.noLog.includes(`${message.guild!.id}/${message.channel.id}/${message.id}`)) return;
const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger;
const auditLog = (await getAuditLog(message.guild!, AuditLogEvent.MemberBanAdd))
.filter((entry: GuildAuditLogsEntry) => (entry.target! as User).id === message.author.id)[0];

@ -9,19 +9,18 @@ import { createHash } from "crypto";
interface NSFWSchema {
nsfw: boolean;
errored?: boolean;
}
interface MalwareSchema {
safe: boolean;
errored?: boolean;
}
export async function testNSFW(link: string): Promise<NSFWSchema> {
const [p, hash] = await saveAttachment(link);
console.log("Checking an image")
let alreadyHaveCheck = await client.database.scanCache.read(hash)
if(alreadyHaveCheck) return { nsfw: alreadyHaveCheck.data };
console.log("Was not in db")
const data = new FormData();
console.log(link);
data.append("file", createReadStream(p));
const result = await fetch("https://unscan.p.rapidapi.com/", {
method: "POST",
@ -31,13 +30,14 @@ export async function testNSFW(link: string): Promise<NSFWSchema> {
},
body: data
})
.then((response) => response.json() as Promise<NSFWSchema>)
.then((response) => response.status === 200 ? response.json() as Promise<NSFWSchema> : { nsfw: false, errored: true })
.catch((err) => {
console.error(err);
return { nsfw: false };
return { nsfw: false, errored: true };
});
console.log(result);
client.database.scanCache.write(hash, result.nsfw);
if(!result.errored) {
client.database.scanCache.write(hash, result.nsfw);
}
return { nsfw: result.nsfw };
}
@ -48,7 +48,6 @@ export async function testMalware(link: string): Promise<MalwareSchema> {
const data = new URLSearchParams();
let f = createReadStream(p);
data.append("file", f.read(fs.statSync(p).size));
console.log(link);
const result = await fetch("https://unscan.p.rapidapi.com/malware", {
method: "POST",
headers: {
@ -57,18 +56,18 @@ export async function testMalware(link: string): Promise<MalwareSchema> {
},
body: data
})
.then((response) => response.json() as Promise<MalwareSchema>)
.then((response) => response.status === 200 ? response.json() as Promise<MalwareSchema> : { safe: true, errored: true })
.catch((err) => {
console.error(err);
return { safe: true };
return { safe: true, errored: true };
});
console.log(result);
client.database.scanCache.write(hash, result.safe);
if (!result.errored) {
client.database.scanCache.write(hash, result.safe);
}
return { safe: result.safe };
}
export async function testLink(link: string): Promise<{ safe: boolean; tags: string[] }> {
console.log(link);
let alreadyHaveCheck = await client.database.scanCache.read(link)
if(alreadyHaveCheck) return { safe: alreadyHaveCheck.data, tags: [] };
const scanned: { safe?: boolean; tags?: string[] } = await fetch("https://unscan.p.rapidapi.com/link", {
@ -84,7 +83,6 @@ export async function testLink(link: string): Promise<{ safe: boolean; tags: str
console.error(err);
return { safe: true, tags: [] };
});
console.log(scanned);
client.database.scanCache.write(link, scanned.safe ?? true, []);
return {
safe: scanned.safe ?? true,
@ -93,10 +91,11 @@ export async function testLink(link: string): Promise<{ safe: boolean; tags: str
}
export async function saveAttachment(link: string): Promise<[string, string]> {
const image = (await fetch(link)).arrayBuffer().toString();
const image = await (await fetch(link)).arrayBuffer()
const fileName = generateFileName(link.split("/").pop()!.split(".").pop()!);
writeFileSync(fileName, image, "base64");
return [fileName, createHash('sha512').update(image, 'base64').digest('base64')];
const enc = new TextDecoder("utf-8");
writeFileSync(fileName, new DataView(image), "base64");
return [fileName, createHash('sha512').update(enc.decode(image), 'base64').digest('base64')];
}
const linkTypes = {

@ -10,7 +10,7 @@ const wait = promisify(setTimeout);
export const Logger = {
renderUser(user: Discord.User | string) {
if (typeof user === "string") return `${user} [<@${user}>]`;
if (typeof user === "string") user = client.users.cache.get(user) as Discord.User;
return `${user.username} [<@${user.id}>]`;
},
renderTime(t: number) {
@ -55,7 +55,6 @@ export const Logger = {
const config = await client.database.guilds.read(log.hidden.guild);
if (!config.logging.logs.enabled) return;
if (!toHexArray(config.logging.logs.toLog).includes(log.meta.calculateType)) {
console.log("Not logging this type of event");
return;
}
if (config.logging.logs.channel) {

Loading…
Cancel
Save