A powerful and extendable Discord bot, with it's own module system :3 thevoid.cafe/projects/voidy
at develop 113 lines 3.6 kB view raw
1import { 2 ActionRowBuilder, 3 ButtonBuilder, 4 ButtonStyle, 5 EmbedBuilder, 6 Events, 7 type GuildMember, 8 type TextChannel, 9} from "discord.js"; 10import { Event } from "@voidy/framework"; 11import type { IGuildConfig } from "../schemas/GuildConfig"; 12 13const TWO_DAYS = 2 * 24 * 60 * 60 * 1000; 14const TWO_WEEKS = 14 * 24 * 60 * 60 * 1000; 15const TWO_MONTHS = 60 * 24 * 60 * 60 * 1000; 16 17function getAccountRisk(createdAt: Date): { level: string; color: number } { 18 const age = Date.now() - createdAt.getTime(); 19 20 if (age < TWO_DAYS) return { level: "Extreme", color: 0xe21e1e }; 21 if (age < TWO_WEEKS) return { level: "High", color: 0xe24d1e }; 22 if (age < TWO_MONTHS) return { level: "Medium", color: 0xe2bb1e }; 23 return { level: "Fairly Safe", color: 0x74e21e }; 24} 25 26export default new Event({ 27 id: "guildMemberAdd", 28 name: Events.GuildMemberAdd, 29 30 execute: async (ctx, member: unknown) => { 31 const m = member as GuildMember; 32 const { client, buttonId } = ctx; 33 34 const cache = client.data.get("guildConfig") as Map<string, IGuildConfig> | undefined; 35 const config = cache?.get(m.guild.id); 36 if (!config) return; 37 38 // Auto-role assignment 39 const roleId = m.user.bot ? config.autoRoles?.botRoleId : config.autoRoles?.memberRoleId; 40 if (roleId) { 41 try { 42 await m.roles.add(roleId); 43 } catch (err) { 44 console.error(`[Guild] Failed to assign auto-role to ${m.user.tag}:`, err); 45 } 46 } 47 48 // Welcome message 49 if (config.welcome?.channelId) { 50 const welcomeChannel = m.guild.channels.cache.get(config.welcome.channelId) as 51 | TextChannel 52 | undefined; 53 54 if (welcomeChannel) { 55 const embed = new EmbedBuilder() 56 .setColor(m.user.accentColor ?? 0x5865f2) 57 .setThumbnail(m.user.displayAvatarURL({ size: 256 })) 58 .setDescription(`Welcome to **${m.guild.name}**, ${m}!`) 59 .setTimestamp(); 60 61 await welcomeChannel.send({ embeds: [embed] }); 62 } 63 } 64 65 // Member logging 66 if (config.logging?.channelId) { 67 const logChannel = m.guild.channels.cache.get(config.logging.channelId) as 68 | TextChannel 69 | undefined; 70 71 if (!logChannel) return; 72 73 const risk = getAccountRisk(m.user.createdAt); 74 const createdTimestamp = Math.floor(m.user.createdTimestamp / 1000); 75 76 const embed = new EmbedBuilder() 77 .setColor(risk.color) 78 .setAuthor({ 79 name: `${m.user.tag} joined`, 80 iconURL: m.user.displayAvatarURL(), 81 }) 82 .addFields( 83 { name: "User", value: `${m}`, inline: true }, 84 { name: "Account Created", value: `<t:${createdTimestamp}:R>`, inline: true }, 85 { name: "Risk Level", value: risk.level, inline: true }, 86 ) 87 .setFooter({ text: `ID: ${m.id}` }) 88 .setTimestamp(); 89 90 if (roleId) { 91 embed.addFields({ name: "Auto-Role", value: `<@&${roleId}>`, inline: true }); 92 } 93 94 const components: ActionRowBuilder<ButtonBuilder>[] = []; 95 96 if (risk.level === "High" || risk.level === "Extreme") { 97 const row = new ActionRowBuilder<ButtonBuilder>().addComponents( 98 new ButtonBuilder() 99 .setCustomId(buttonId("MemberLogging", "Kick", m.id)) 100 .setLabel("Kick") 101 .setStyle(ButtonStyle.Danger), 102 new ButtonBuilder() 103 .setCustomId(buttonId("MemberLogging", "Ban", m.id)) 104 .setLabel("Ban") 105 .setStyle(ButtonStyle.Danger), 106 ); 107 components.push(row); 108 } 109 110 await logChannel.send({ embeds: [embed], components }); 111 } 112 }, 113});