1
1
mirror of https://github.com/Burn-E99/GroupUp.git synced 2026-01-06 19:37:54 -05:00

Slight rework to commands, db redesign, setup command almost done

This commit is contained in:
Ean Milligan (Bastion)
2023-01-28 20:58:24 -05:00
parent 9cb615e68d
commit a43fade7d5
13 changed files with 427 additions and 99 deletions

View File

@ -1,6 +1,7 @@
import { ApplicationCommandFlags } from '../deps.ts';
import { ApplicationCommandFlags, Bot, Interaction, InteractionResponseTypes } from '../deps.ts';
import config from '../config.ts';
import { lfgChannels } from './db.ts';
import { lfgChannelSettings } from './db.ts';
import utils from './utils.ts';
export const failColor = 0xe71212;
export const warnColor = 0xe38f28;
@ -17,8 +18,8 @@ export const getRandomStatus = (guildCount: number): string => {
return statuses[Math.floor((Math.random() * statuses.length) + 1)];
};
export const isLFGChannel = (channelId: bigint) => {
return (lfgChannels.includes(channelId) || channelId === 0n) ? ApplicationCommandFlags.Ephemeral : undefined;
export const isLFGChannel = (guildId: bigint, channelId: bigint) => {
return (lfgChannelSettings.has(`${guildId}-${channelId}`) || channelId === 0n || guildId === 0n) ? ApplicationCommandFlags.Ephemeral : undefined;
};
export const generateReport = (msg: string) => ({
@ -28,3 +29,20 @@ export const generateReport = (msg: string) => ({
description: msg,
}],
});
export const somethingWentWrong = (bot: Bot, interaction: Interaction, errorCode: string) =>
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Something went wrong...',
description: 'You should not be able to get here. Please try again and if the issue continues, `/report` this issue to the developers with the error code below.',
fields: [{
name: 'Error Code:',
value: errorCode,
}],
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('commandUtils.ts', interaction, e));

View File

@ -1,11 +1,12 @@
import { Bot, CreateApplicationCommand, log, LT, MakeRequired } from '../../deps.ts';
import { Commands } from '../types/commandTypes.ts';
import { Command } from '../types/commandTypes.ts';
import utils from '../utils.ts';
import info from './info.ts';
import report from './report.ts';
import setup from './setup.ts';
export const commands: Array<Commands> = [info, report];
export const commands: Array<Command> = [info, report, setup];
export const createSlashCommands = async (bot: Bot) => {
const globalCommands: MakeRequired<CreateApplicationCommand, 'name'>[] = [];

View File

@ -12,14 +12,14 @@ const details: CommandDetails = {
};
const execute = (bot: Bot, interaction: Interaction) => {
dbClient.execute(queries.callIncCnt('info')).catch((e) => utils.commonLoggers.dbError('info.ts', 'call sproc INC_CNT on', e));
dbClient.execute(queries.callIncCnt('cmd-info')).catch((e) => utils.commonLoggers.dbError('info.ts', 'call sproc INC_CNT on', e));
bot.helpers.sendInteractionResponse(
interaction.id,
interaction.token,
{
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.channelId || 0n),
flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
embeds: [{
color: infoColor2,
title: `${config.name}, the LFG bot`,

View File

@ -1,6 +1,6 @@
import config from '../../config.ts';
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, Bot, Interaction, InteractionResponseTypes, sendMessage } from '../../deps.ts';
import { generateReport, isLFGChannel, successColor } from '../commandUtils.ts';
import { generateReport, isLFGChannel, somethingWentWrong, successColor } from '../commandUtils.ts';
import { dbClient, queries } from '../db.ts';
import { CommandDetails } from '../types/commandTypes.ts';
import utils from '../utils.ts';
@ -22,26 +22,27 @@ const details: CommandDetails = {
};
const execute = (bot: Bot, interaction: Interaction) => {
console.log(interaction);
dbClient.execute(queries.callIncCnt('report')).catch((e) => utils.commonLoggers.dbError('report.ts', 'call sproc INC_CNT on', e));
sendMessage(bot, config.reportChannel, generateReport(interaction.data?.options?.[0].value as string || 'Missing Options')).catch((e: Error) =>
utils.commonLoggers.interactionSendError('report.ts:28', interaction, e)
);
bot.helpers.sendInteractionResponse(
interaction.id,
interaction.token,
{
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.channelId || 0n),
embeds: [{
color: successColor,
title: 'Failed command has been reported to my developer.',
description: `For more in depth support, and information about planned maintenance, please join the support server [here](${config.links.supportServer}).`,
}],
dbClient.execute(queries.callIncCnt('cmd-report')).catch((e) => utils.commonLoggers.dbError('report.ts', 'call sproc INC_CNT on', e));
if (interaction.data?.options?.[0].value) {
sendMessage(bot, config.reportChannel, generateReport(interaction.data.options[0].value as string)).catch((e: Error) => utils.commonLoggers.interactionSendError('report.ts:28', interaction, e));
bot.helpers.sendInteractionResponse(
interaction.id,
interaction.token,
{
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
embeds: [{
color: successColor,
title: 'Failed command has been reported to my developer.',
description: `For more in depth support, and information about planned maintenance, please join the support server [here](${config.links.supportServer}).`,
}],
},
},
},
).catch((e: Error) => utils.commonLoggers.interactionSendError('report.ts:44', interaction, e));
).catch((e: Error) => utils.commonLoggers.interactionSendError('report.ts:44', interaction, e));
} else {
somethingWentWrong(bot, interaction, 'reportMissingAllOptions');
}
};
export default {

321
src/commands/setup.ts Normal file
View File

@ -0,0 +1,321 @@
import config from '../../config.ts';
import { ApplicationCommandFlags, ApplicationCommandOptionTypes, ApplicationCommandTypes, ButtonStyles, Bot, ChannelTypes, Interaction, InteractionResponseTypes, sendMessage, OverwriteTypes, botId, MessageComponentTypes, DiscordEmbedField } from '../../deps.ts';
import { failColor, infoColor2, somethingWentWrong, successColor } from '../commandUtils.ts';
import { dbClient, queries, lfgChannelSettings } from '../db.ts';
import { CommandDetails } from '../types/commandTypes.ts';
import utils from '../utils.ts';
const withoutMgrRole = 'without-manager-role';
const withMgrRole = 'with-manager-role';
const managerRoleStr = 'manager-role';
const logChannelStr = 'log-channel';
const details: CommandDetails = {
name: 'setup',
description: `Configures this channel to be a dedicated event channel to be managed by ${config.name}.`,
type: ApplicationCommandTypes.ChatInput,
defaultMemberPermissions: ['ADMINISTRATOR'],
options: [
{
name: withoutMgrRole,
type: ApplicationCommandOptionTypes.SubCommand,
description: `This will configure ${config.name} without a manager role.`,
},
{
name: withMgrRole,
type: ApplicationCommandOptionTypes.SubCommand,
description: `This will configure ${config.name} with a manager role.`,
options: [
{
name: managerRoleStr,
type: ApplicationCommandOptionTypes.Role,
description: 'This role will be allowed to manage all events in this guild.',
required: true,
},
{
name: logChannelStr,
type: ApplicationCommandOptionTypes.Channel,
description: `This channel is where ${config.name} will send Audit Messages whenever a manager updates an event.`,
required: true,
channelTypes: [ChannelTypes.GuildText],
},
],
},
],
};
const execute = async (bot: Bot, interaction: Interaction) => {
dbClient.execute(queries.callIncCnt('cmd-setup')).catch((e) => utils.commonLoggers.dbError('setup.ts', 'call sproc INC_CNT on', e));
const setupOpts = interaction.data?.options?.[0];
if (setupOpts && setupOpts.name && interaction.channelId && interaction.guildId) {
if (lfgChannelSettings.has(`${interaction.guildId}-${interaction.channelId}`)) {
// Cannot setup a lfg channel that is already set up
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Unable to setup LFG channel.',
description: 'This channel is already set as an LFG channel. If you need to edit the channel, please run `/delete lfg-channel` in this channel and then run `/setup` again.\n\nThis will not harm any active events in this channel and simply resets the settings for this channel.',
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
return;
}
const messages = await bot.helpers.getMessages(interaction.channelId, { limit: 100 });
if (messages.size < 100) {
let logChannelId = 0n;
let managerRoleId = 0n;
let logChannelErrorOut = false;
let mgrRoleErrorOut = false;
const introFields: Array<DiscordEmbedField> = [{
name: 'Editing/Deleting your event:',
value: 'To edit or delete your event, simply click on the ✏️ or 🗑️ buttons respectively.',
}];
const permissionFields: Array<DiscordEmbedField> = [
{
name: `Please make sure ${config.name} has the following permissions:`,
value: '`MANAGE_GUILD`\n`MANAGE_CHANNELS`\n`MANAGE_ROLES`\n`MANAGE_MESSAGES`\n\nThe only permission that is required after setup completes is `MANAGE_MESSAGES`.',
},
];
if (setupOpts.name === withMgrRole) {
introFields.push({
name: `${config.name} Manager Details:`,
value: `${config.name} Managers with the <@&${managerRoleId}> role may edit or delete events in this guild, along with using the following commands to update the activity members:
\`/join\`
\`/leave\`
\`/alternate\`
The Discord Slash Command system will ensure you provide all the required details.`,
})
if (setupOpts.options?.length) {
setupOpts.options.forEach(opt => {
if (opt.name === managerRoleStr) {
managerRoleId = BigInt(opt.value as string || '0');
} else if (opt.name === logChannelStr) {
logChannelId = BigInt(opt.value as string || '0');
}
});
if (logChannelId === 0n || managerRoleId === 0n) {
// One or both Ids did not get parsed
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Unable to setup log channel or manager role.',
description: `${config.name} attempted to set the log channel or manager role, but one or both were undefined. Please try again and if the issue continues, \`/report\` this issue to the developers with the error code below.`,
fields: [{
name: 'Error Code:',
value: `setupLog${logChannelId}Mgr${managerRoleId}`,
}],
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
return;
}
} else {
// Discord broke?
somethingWentWrong(bot, interaction, 'setupMissingRoleMgrOptions');
return;
}
// Test sending a message to the logChannel
await sendMessage(bot, logChannelId, {
embeds: [{
title: `This is the channel ${config.name} will be logging events to.`,
description: `${config.name} will only send messages here as frequently as your event managers update events.`,
color: infoColor2,
}]
}).catch((e: Error) => {
utils.commonLoggers.messageSendError('setup.ts', 'log-test', e);
logChannelErrorOut = true;
});
if (logChannelErrorOut) {
// Cannot send message into log channel, error out
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Unable to setup log channel.',
description: `${config.name} attempted to send a message to the specified log channel.`,
fields: [
{
name: `Please allow ${config.name} to send messages in the requested channel.`,
value: `${config.name}`,
},
],
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
return;
}
// Set permissions for managerId
await bot.helpers.editChannelPermissionOverrides(interaction.channelId, {
id: managerRoleId,
type: OverwriteTypes.Role,
allow: ['SEND_MESSAGES'],
}).catch((e: Error) => {
utils.commonLoggers.channelUpdateError('setup.ts', 'manager-allow', e);
mgrRoleErrorOut = true;
});
}
// Set permissions for everyone, skip if we already failed to set roles
!mgrRoleErrorOut && await bot.helpers.editChannelPermissionOverrides(interaction.channelId, {
id: interaction.guildId,
type: OverwriteTypes.Role,
deny: ['SEND_MESSAGES'],
}).catch((e: Error) => {
utils.commonLoggers.channelUpdateError('setup.ts', 'everyone-deny', e);
mgrRoleErrorOut = true;
});
if (mgrRoleErrorOut) {
// Cannot update role overrides on channel, error out
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Unable to set lfg channel permissions.',
description: `${config.name} attempted to update the permissions for the current channel, but could not.`,
fields: permissionFields,
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
return;
}
// Delete all messages that are not LFG posts
const msgsToDel: Array<bigint> = [];
const oldLfgMsgs: Array<bigint> = []
messages.forEach(msg => {
if (msg.authorId === botId && msg.embeds.length && msg.embeds[0].footer && msg.embeds[0].footer.text.includes('Created by:')) {
oldLfgMsgs.push(msg.id);
} else {
msgsToDel.push(msg.id);
}
});
if (msgsToDel.length) {
await bot.helpers.deleteMessages(interaction.channelId, msgsToDel, 'Cleaning LFG Channel').catch((e: Error) => utils.commonLoggers.messageDeleteError('setup.ts', 'bulk-msg-cleanup', e));
}
// Retrofit all old LFG posts that we found
if (oldLfgMsgs.length) {
// TODO: Retrofit old LFG posts, should delete ones that have already passed, should begin watching these events
}
// Store the ids to the db
let dbErrorOut = false;
await dbClient.execute('INSERT INTO guild_settings(guildId,lfgChannelId,managerRoleId,logChannelId) values(?,?,?,?)', [interaction.guildId, interaction.channelId, managerRoleId, logChannelId]).catch((e) => {
utils.commonLoggers.dbError('setup.ts', 'insert into guild_settings', e);
dbErrorOut = true;
});
if (dbErrorOut) {
// DB died?
somethingWentWrong(bot, interaction, 'setupDBInsertFailed');
return;
}
// Store the ids to the active map
lfgChannelSettings.set(`${interaction.guildId}-${interaction.channelId}`, {
managed: setupOpts.name === withMgrRole,
managerRoleId,
logChannelId,
});
// Send the initial introduction message
const createNewEventBtn = 'Create New Event';
const introMsg = await sendMessage(bot, interaction.channelId, {
content: `Welcome to <#${interaction.channelId}>, managed by <@${botId}>!`,
embeds: [{
title: `To get started, click on the '${createNewEventBtn}' button below!`,
color: successColor,
fields: introFields,
}],
components: [{
type: MessageComponentTypes.ActionRow,
components: [{
type: MessageComponentTypes.Button,
label: createNewEventBtn,
customId: 'temp', // TODO: set this
style: ButtonStyles.Success,
}],
}],
}).catch((e: Error) => utils.commonLoggers.messageSendError('setup.ts', 'init-msg', e));
if (introMsg) {
bot.helpers.pinMessage(interaction.channelId, introMsg.id).catch((e: Error) => utils.commonLoggers.messageSendError('setup.ts', 'pin-init-msg', e));
// Complete the interaction
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: successColor,
title: 'LFG Channel setup complete!',
description: `${config.name} has finished setting up this channel. You may safely dismiss this message.`,
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
} else {
// Could not send initial message
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Failed to send the initial message!',
fields: permissionFields,
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
}
} else {
// Too many messages to delete, give up
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Unable to setup LFG channel.',
description: `${config.name} attempted to clean this channel, but encountered too many messages (100 or more). There are two ways to move forward:`,
fields: [
{
name: 'Is this channel a dedicated LFG Channel?',
value: 'You either need to manually clean this channel or create a brand new channel for events.',
inline: true,
},
{
name: 'Is this a chat channel that you want events mixed into?',
value: 'You do not need to run the `/setup` command, and instead should use the `/lfg create` command.',
inline: true,
},
],
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('setup.ts', interaction, e));
}
} else {
// Discord fucked up?
somethingWentWrong(bot, interaction, 'setupMissingAllOptions');
}
};
export default {
details,
execute,
};

View File

@ -1,6 +1,7 @@
import config from '../config.ts';
import { Client } from '../deps.ts';
import { LOCALMODE } from '../flags.ts';
import { LfgChannelSetting, DBGuildSettings } from './types/commandTypes.ts';
export const dbClient = await new Client().connect({
hostname: LOCALMODE ? config.db.localhost : config.db.host,
@ -14,4 +15,12 @@ export const queries = {
callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`,
};
export const lfgChannels: Array<bigint> = [1055568692697649232n];
export const lfgChannelSettings: Map<string, LfgChannelSetting> = new Map();
const getGuildSettings = await dbClient.query('SELECT * FROM guild_settings');
getGuildSettings.forEach((g: DBGuildSettings) => {
lfgChannelSettings.set(`${g.guildId}-${g.lfgChannelId}`, {
managed: g.managerRoleId === 0n && g.logChannelId === 0n,
managerRoleId: g.managerRoleId,
logChannelId: g.logChannelId,
});
});

View File

@ -9,7 +9,20 @@ export type CommandDetails = {
defaultMemberPermissions?: PermissionStrings[];
};
export type Commands = {
export type Command = {
details: CommandDetails;
execute: Function;
};
export type LfgChannelSetting = {
managed: boolean;
managerRoleId: bigint;
logChannelId: bigint;
};
export type DBGuildSettings = {
guildId: bigint;
lfgChannelId: bigint;
managerRoleId: bigint;
logChannelId: bigint;
};

View File

@ -19,10 +19,14 @@ const reactionAddError = (location: string, message: Message | string, err: Erro
genericLogger(LT.ERROR, `${location} | Failed to add emoji (${emoji}) to message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const reactionDeleteError = (location: string, message: Message | string, err: Error, emoji: string) =>
genericLogger(LT.ERROR, `${location} | Failed to delete emoji (${emoji}) from message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const channelUpdateError = (location: string, message: string, err: Error) =>
genericLogger(LT.ERROR, `${location} | Failed to update channel | ${message} | Error: ${err.name} - ${err.message}`);
const dbError = (location: string, type: string, err: Error) => genericLogger(LT.ERROR, `${location} | Failed to ${type} database | Error: ${err.name} - ${err.message}`);
export default {
commonLoggers: {
channelUpdateError,
dbError,
interactionSendError,
messageGetError,