added slash command support

This commit is contained in:
Ashley Rosch
2021-08-20 03:19:44 -05:00
parent d751db58f7
commit bc835922a5

View File

@@ -1,21 +1,43 @@
import { ButtonInteraction, GuildMember, Interaction, Message, MessageActionRow, MessageButton, MessageSelectMenu, MessageSelectOptionData, SelectMenuInteraction } from "discord.js"; import { ButtonInteraction, Client, CommandInteraction, GuildMember, Interaction, Message, MessageActionRow, MessageButton, MessageSelectMenu, MessageSelectOptionData, PartialDMChannel, SelectMenuInteraction, TextBasedChannels } from "discord.js";
import { TextChannel } from "discord.js"; import { TextChannel } from "discord.js";
import * as Opt from "../types/Opt"; import * as Opt from "../types/Opt";
import refreshCommands from "./slash";
import InteractionRoles from "./data"; import { InteractionRoles, GuildId } from "./data";
type ReplyableInteraction = ButtonInteraction | CommandInteraction | SelectMenuInteraction;
const isReplyableInteraction = (res: any): boolean => res instanceof ButtonInteraction || res instanceof CommandInteraction || res instanceof SelectMenuInteraction;
const sendMessage = (res: ReplyableInteraction | TextBasedChannels, msg: any, ephemeral?: boolean) => {
let arg: any = {};
if (typeof msg === 'object')
arg = msg;
else
arg.content = msg;
if (isReplyableInteraction(res) && ephemeral)
arg.ephemeral = true;
if (isReplyableInteraction(res))
(res as ReplyableInteraction).reply(arg);
else
(res as TextBasedChannels).send(arg);
}
//initialize discord //initialize discord
const Discord = require('discord.js'), const Discord = require('discord.js'),
client = new Discord.Client({ intents: [ client: Client = new Discord.Client({ intents: [
'GUILD_MEMBERS' 'GUILD_MEMBERS'
] }); ] });
/** /**
* send message containing menu * send message containing menu
* @param c text channel to send message * @param res text channel to send message or interaction to reply to
* @param menuOpts menu data object * @param menuOpts menu data object
*/ */
function sendMenu(c: TextChannel, menuOpts: Opt.Menu) { function sendMenu(res: ReplyableInteraction|TextBasedChannels, menuOpts: Opt.Menu, ephemeral?: boolean) {
let row = new MessageActionRow() let row = new MessageActionRow()
@@ -52,10 +74,13 @@ function sendMenu(c: TextChannel, menuOpts: Opt.Menu) {
select.addOptions(options); select.addOptions(options);
row.addComponents(select) row.addComponents(select)
c.send({ let arg = {
content: menuOpts.message, content: menuOpts.message,
components: [row] components: [row]
}); };
sendMessage(res, arg, ephemeral);
} }
/** /**
@@ -63,7 +88,7 @@ function sendMenu(c: TextChannel, menuOpts: Opt.Menu) {
* @param c text channel to send message * @param c text channel to send message
* @param rowOpts row data object * @param rowOpts row data object
*/ */
function sendRow(c: TextChannel, rowOpts: Opt.Row) { function sendRow(res: ReplyableInteraction|TextBasedChannels, rowOpts: Opt.Row, ephemeral?: boolean) {
let row = new MessageActionRow(); let row = new MessageActionRow();
@@ -84,43 +109,88 @@ function sendRow(c: TextChannel, rowOpts: Opt.Row) {
row.addComponents(options); row.addComponents(options);
c.send({ let arg = {
content: rowOpts.message, content: rowOpts.message,
components: [row] components: [row]
}); };
sendMessage(res, arg, ephemeral);
} }
//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 //on interaction event
client.on('interactionCreate', (interaction: Interaction) => { client.on('interactionCreate', (interaction: Interaction) => {
if (interaction.inGuild()) {
if (interaction.isSelectMenu()) if (interaction.isSelectMenu())
handleSelect(interaction as SelectMenuInteraction); handleSelect(interaction as SelectMenuInteraction);
else if (interaction.isButton()) else if (interaction.isButton())
handleButton(interaction as ButtonInteraction); handleButton(interaction as ButtonInteraction);
else if (interaction.isCommand())
handleCommand(interaction as CommandInteraction);
}
}); });
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;
}
}
}
};
//on menu click //on menu click
async function handleSelect(interaction: SelectMenuInteraction) { async function handleSelect(interaction: SelectMenuInteraction) {
@@ -208,60 +278,109 @@ async function handleSelect(interaction: SelectMenuInteraction) {
}; };
async function handleButton(interaction: ButtonInteraction) { //on slash command
async function handleCommand(interaction: CommandInteraction) {
//start typing console.log(interaction.commandName)
await interaction.deferReply({ ephemeral: true });
//get custom id let category: number;
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 switch (interaction.commandName) {
let rows = InteractionRoles.filter(opt => opt.type === 'row');
//loop through each the buttons rows case 'roles':
for (let i = 0; i < rows.length; i++) {
let row = rows[i] as Opt.Row; category = interaction.options.getInteger('category', true);
sendRoles(interaction, InteractionRoles[category].id, true);
break;
//filter to just buttons with matching id case 'rolesall':
let matchingButtons = row.buttons.filter(opt => opt.id === customId); if (interaction.user.id === '167336999844315137') {
if (matchingButtons.length) { let givenChannel = interaction.options.getChannel('channel'),
channel: TextChannel = await client.channels.fetch(interaction.channelId) as TextChannel;
//if there's no role id, then nothing can be done if (givenChannel && givenChannel.type === 'GUILD_TEXT')
if (matchingButtons[0].roleid && interaction.member instanceof GuildMember) { channel = givenChannel as TextChannel;
//since it is a GuildMember, let's force typescript to rememeber it if (channel) {
let member = interaction.member as GuildMember; category = interaction.options.getInteger('category');
sendRoles(channel, category ? InteractionRoles[category].id : undefined);
interaction.reply({
content: `sending message in <#${channel.id}>`,
ephemeral: true
})
//get corresponding role } else
let role = await interaction.guild.roles.fetch(matchingButtons[0].roleid); interaction.reply({
content: 'error finding channel to send message in',
ephemeral: true
})
//if clicker (member) does have the role } else
if (member.roles.cache.some(r => r.id === role.id)) { interaction.reply({
content: 'missing permissions',
ephemeral: true
})
break;
//remove role case 'updateroles':
await member.roles.remove(role.id); //TODO reload data.ts
interaction.editReply(`Removed \`${role.name}\``); //TODO reload slash.ts
interaction.reply({
} else { content: 'command incomplete',
ephemeral: true
//add role })
await member.roles.add(role.id); break;
interaction.editReply(`Added \`${role.name}\``);
} }
}
async function sendRoles(res: ReplyableInteraction|TextBasedChannels, id?: string, ephemeral?: boolean) {
if (id) {
let rowarr = InteractionRoles.filter(r => r.id === id);
if (rowarr.length) {
let row = rowarr[0];
if (row.type === 'menu')
sendMenu(res, row as Opt.Menu, ephemeral);
else if (row.type === 'row')
sendRow(res, row as Opt.Row, ephemeral);
} else
sendMessage(res, `invalid id ${id}`, ephemeral);
} else if (isReplyableInteraction(res))
sendMessage(res, `missing id`, ephemeral);
else {
for (let i = 0; i < InteractionRoles.length; i++) {
switch (InteractionRoles[i].type) {
case 'menu':
sendMenu(res, InteractionRoles[i] as Opt.Menu, ephemeral);
break;
case 'row':
sendRow(res, InteractionRoles[i] as Opt.Row, ephemeral);
break; break;
} }
}
} }
}; }
}
//register slash commands
client.on('ready', () => {
console.log('Ready')
setInterval(() => refreshCommands(client.user.id), 1000*60*60*24);
refreshCommands(client.user.id)
});
//login with token //login with token
client.login(require('fs').readFileSync('../token').toString()); client.login(require('fs').readFileSync('../token').toString());