import { ButtonInteraction, GuildMember, Interaction, Message, MessageActionRow, MessageButton, MessageSelectMenu, MessageSelectOptionData, SelectMenuInteraction } from "discord.js"; import { TextChannel } from "discord.js"; import * as Opt from "../types/Opt"; import InteractionRoles from "./data"; //initialize discord const Discord = require('discord.js'), client = new Discord.Client({ intents: [ 'GUILD_MEMBERS' ] }); //on read /** * send message containing menu * @param c text channel to send message * @param menuOpts menu data object */ function sendMenu(c: TextChannel, menuOpts: Opt.Menu) { let row = new MessageActionRow() let select = new MessageSelectMenu() .setCustomId(menuOpts.id) .setPlaceholder(menuOpts.palceholder) .setMaxValues(menuOpts.max === 'all' ? menuOpts.options.length : menuOpts.max) .setMinValues(0); let options: MessageSelectOptionData[] = []; for (let i = 0; i < menuOpts.options.length; i++) { let opts: Opt.MenuOptions = menuOpts.options[i]; let option: MessageSelectOptionData = { label: opts.label, value: opts.value }; if (opts.emoji && opts.emoji.length) option.emoji = opts.emoji; if (opts.description && opts.description.length) option.description = opts.description; if (opts.default) option.default = true; options.push(option); } select.addOptions(options); row.addComponents(select) c.send({ content: menuOpts.message, components: [row] }); } /** * send message containing row * @param c text channel to send message * @param rowOpts row data object */ function sendRow(c: TextChannel, rowOpts: Opt.Row) { let row = new MessageActionRow(); let options: MessageButton[] = []; for (let i = 0; i < rowOpts.buttons.length; i++) { let opts: Opt.RowButton = rowOpts.buttons[i]; let option = new MessageButton() .setStyle(opts.style) .setLabel(opts.label) .setCustomId(opts.id); options.push(option); } row.addComponents(options); c.send({ content: rowOpts.message, components: [row] }); } //uncomment if you want to create a message, will change to slash command client.on('message', (m: Message) => { if (m.author.id === '167336999844315137' && m.content === '.') { for (let i = 0; i < InteractionRoles.length; i++) { switch (InteractionRoles[i].type) { case 'menu': sendMenu(m.channel as TextChannel, InteractionRoles[i] as Opt.Menu); break; case 'row': sendRow(m.channel as TextChannel, InteractionRoles[i] as Opt.Row); break; } } } }); //on interaction event client.on('interactionCreate', (interaction: Interaction) => { if (interaction.isSelectMenu()) handleSelect(interaction as SelectMenuInteraction); else if (interaction.isButton()) handleButton(interaction as ButtonInteraction); }); //on menu click async function handleSelect(interaction: SelectMenuInteraction) { //start typing await interaction.deferReply({ ephemeral: true }); //get custom id let customId: string; if (interaction.component instanceof MessageSelectMenu) customId = interaction.component.customId; else if ('custom_id' in interaction.component) customId = interaction.component.custom_id; //list of menus from data with matching id let menus = InteractionRoles.filter(opt => opt.type === 'menu' && opt.id === customId); if (menus.length) { //use first menu with matching id let m = menus[0] as Opt.Menu, vals = interaction.values || []; //list of changes let changed: { add: string[], rem: string[] } = { add: [], rem: [] }; //loop through all the options in the menu for (let i = 0; i < m.options.length; i++) { let opt: Opt.MenuOptions = m.options[i], {roleid} = opt; //if there's no role id, then nothing can be done if (roleid && interaction.member instanceof GuildMember) { //since it is a GuildMember, let's force typescript to rememeber it let member = interaction.member as GuildMember; //get corresponding role let role = await interaction.guild.roles.fetch(roleid); //if enabled if (vals && vals.includes(opt.value)) { //if member doesn't have the role if (!member.roles.cache.some(r => r.id === role.id)) { //give role await member.roles.add(role); changed.add.push(role.name); } //else if member does have the role } else if (member.roles.cache.some(r => r.id === role.id)) { //remove role await member.roles.remove(role); changed.rem.push(role.name); } } } //convert changes to a message let message = []; if (changed.add.length) message.push(`Added \`${changed.add.join(', ')}\``); if (changed.rem.length) message.push(`Removed \`${changed.rem.join(', ')}\``); interaction.editReply(message.length ? message.join('\n') : 'No Changes'); } else interaction.editReply('Error: Unknown menu'); }; async function handleButton(interaction: ButtonInteraction) { //start typing await interaction.deferReply({ ephemeral: true }); //get custom id let customId: string; if (interaction.component instanceof MessageButton) customId = interaction.component.customId; else if ('custom_id' in interaction.component) customId = interaction.component.custom_id; //list of button rows from data let rows = InteractionRoles.filter(opt => opt.type === 'row'); //loop through each the buttons rows for (let i = 0; i < rows.length; i++) { let row = rows[i] as Opt.Row; //filter to just buttons with matching id let matchingButtons = row.buttons.filter(opt => opt.id === customId); if (matchingButtons.length) { //if there's no role id, then nothing can be done if (matchingButtons[0].roleid && interaction.member instanceof GuildMember) { //since it is a GuildMember, let's force typescript to rememeber it let member = interaction.member as GuildMember; //get corresponding role let role = await interaction.guild.roles.fetch(matchingButtons[0].roleid); //if clicker (member) does have the role if (member.roles.cache.some(r => r.id === role.id)) { //remove role await member.roles.remove(role.id); interaction.editReply(`Removed \`${role.name}\``); } else { //add role await member.roles.add(role.id); interaction.editReply(`Added \`${role.name}\``); } break; } } } }; //login with token client.login(require('fs').readFileSync('../token').toString());