A powerful and extendable Discord bot, with it's own module system :3 thevoid.cafe/projects/voidy
at develop 124 lines 3.9 kB view raw
1import { 2 ChannelType, 3 MessageFlags, 4 PermissionFlagsBits, 5 SlashCommandSubcommandBuilder, 6 type TextChannel, 7} from "discord.js"; 8import { Command, requirePermission } from "@voidy/framework"; 9import { ChatbotChannel } from "../schemas/ChatbotChannel"; 10import { ChatbotRepository } from "../schemas/ChatbotRepository"; 11 12export default new Command({ 13 id: "chatbot.setup", 14 use: [requirePermission(PermissionFlagsBits.ManageGuild)], 15 data: new SlashCommandSubcommandBuilder() 16 .setName("setup") 17 .setDescription("Set up chatbot in a channel (creates webhook automatically).") 18 .addChannelOption((option) => 19 option 20 .setName("channel") 21 .setDescription("Channel to enable chatbot in.") 22 .addChannelTypes(ChannelType.GuildText) 23 .setRequired(true), 24 ) 25 .addIntegerOption((option) => 26 option 27 .setName("interval") 28 .setDescription("How often to send a message.") 29 .setRequired(true) 30 .setMinValue(1), 31 ) 32 .addStringOption((option) => 33 option 34 .setName("unit") 35 .setDescription("Interval unit.") 36 .setRequired(true) 37 .addChoices( 38 { name: "Seconds", value: "seconds" }, 39 { name: "Minutes", value: "minutes" }, 40 ), 41 ) 42 .addStringOption((option) => 43 option 44 .setName("mode") 45 .setDescription("Content source mode.") 46 .addChoices( 47 { name: "Global", value: "global" }, 48 { name: "Local", value: "local" }, 49 ), 50 ) 51 .addStringOption((option) => 52 option.setName("repository").setDescription("Local repository name (required if mode is local)."), 53 ), 54 55 execute: async ({ interaction }) => { 56 const channel = interaction.options.getChannel("channel", true) as TextChannel; 57 const interval = interaction.options.getInteger("interval", true); 58 const unit = interaction.options.getString("unit", true); 59 const mode = (interaction.options.getString("mode") ?? "global") as "global" | "local"; 60 const repoName = interaction.options.getString("repository"); 61 62 const intervalSeconds = unit === "minutes" ? interval * 60 : interval; 63 64 // Check if channel already has chatbot 65 const existing = await ChatbotChannel.findOne({ channelId: channel.id }); 66 if (existing) { 67 await interaction.reply({ 68 content: `Chatbot is already set up in <#${channel.id}>.`, 69 flags: [MessageFlags.Ephemeral], 70 }); 71 return; 72 } 73 74 // Resolve repository for local mode 75 let repositoryId; 76 if (mode === "local") { 77 if (!repoName) { 78 await interaction.reply({ 79 content: "You must specify a repository name when using local mode.", 80 flags: [MessageFlags.Ephemeral], 81 }); 82 return; 83 } 84 85 const repo = await ChatbotRepository.findOne({ 86 scope: "local", 87 guildId: interaction.guildId, 88 name: repoName, 89 }); 90 91 if (!repo) { 92 await interaction.reply({ 93 content: `Local repository "${repoName}" not found. Create one first with \`/chatbot repository create\`.`, 94 flags: [MessageFlags.Ephemeral], 95 }); 96 return; 97 } 98 99 repositoryId = repo._id; 100 } 101 102 await interaction.deferReply({ flags: [MessageFlags.Ephemeral] }); 103 104 // Create webhook in channel 105 const webhook = await channel.createWebhook({ name: "Voidy Chatbot" }); 106 107 await ChatbotChannel.create({ 108 guildId: interaction.guildId, 109 channelId: channel.id, 110 webhookId: webhook.id, 111 webhookToken: webhook.token, 112 mode, 113 repositoryId, 114 enabled: true, 115 intervalSeconds, 116 }); 117 118 const display = unit === "minutes" ? `${interval} minute(s)` : `${interval} second(s)`; 119 120 await interaction.editReply({ 121 content: `Chatbot enabled in <#${channel.id}> using **${mode}** mode, posting every **${display}**.`, 122 }); 123 }, 124});