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

@ -3,12 +3,12 @@ export const config = {
'version': '1.0.0', // Version of the bot 'version': '1.0.0', // Version of the bot
'token': 'the_bot_token', // Discord API Token for this bot 'token': 'the_bot_token', // Discord API Token for this bot
'localToken': 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token" 'localToken': 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
'prefix': 'gu!', // Prefix for all commands 'prefix': '/', // Prefix for all commands
'db': { // Settings for the MySQL database, this is required for use with the API, if you do not want to set this up, you will need to rip all code relating to the DB out of the bot 'db': { // Settings for the MySQL database, this is required for use with the API, if you do not want to set this up, you will need to rip all code relating to the DB out of the bot
'host': '', // IP address for the db, usually localhost 'host': '', // IP address for the db, usually localhost
'localhost': '', // IP address for a secondary OPTIONAL local testing DB, usually also is localhost, but depends on your dev environment 'localhost': '', // IP address for a secondary OPTIONAL local testing DB, usually also is localhost, but depends on your dev environment
'port': 3306, // Port for the db 'port': 3306, // Port for the db
'username': '', // Username for the account that will access your DB, this account will need "DB Manager" admin rights and "REFERENCES" Global Privalages 'username': '', // Username for the account that will access your DB, this account will need "DB Manager" admin rights and "REFERENCES" Global Privileges
'password': '', // Password for the account, user account may need to be authenticated with the "Standard" Authentication Type if this does not work out of the box 'password': '', // Password for the account, user account may need to be authenticated with the "Standard" Authentication Type if this does not work out of the box
'name': '', // Name of the database Schema to use for the bot 'name': '', // Name of the database Schema to use for the bot
}, },
@ -18,7 +18,7 @@ export const config = {
}, },
'logChannel': 'the_log_channel', // Discord channel ID where the bot should put startup messages and other error messages needed 'logChannel': 'the_log_channel', // Discord channel ID where the bot should put startup messages and other error messages needed
'reportChannel': 'the_report_channel', // Discord channel ID where reports will be sent when using the built-in report command 'reportChannel': 'the_report_channel', // Discord channel ID where reports will be sent when using the built-in report command
'devServer': 'the_dev_server', // Discord guild ID where testing of indev features/commands will be handled, used in conjuction with the DEVMODE bool in mod.ts 'devServer': 'the_dev_server', // Discord guild ID where testing of indev features/commands will be handled, used in conjunction with the DEVMODE bool in mod.ts
'owner': 'the_bot_owner', // Discord user ID of the bot admin 'owner': 'the_bot_owner', // Discord user ID of the bot admin
'botLists': [ // Array of objects containing all bot lists that stats should be posted to 'botLists': [ // Array of objects containing all bot lists that stats should be posted to
{ // Bot List object, duplicate for each bot list { // Bot List object, duplicate for each bot list

View File

@ -1,21 +1,8 @@
// This file will create all tables for the artificer schema // This file will create all tables for the artificer schema
// DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK // DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK
import {
// MySQL deps
Client,
} from '../deps.ts';
import { LOCALMODE } from '../flags.ts';
import config from '../config.ts'; import config from '../config.ts';
import { dbClient } from '../src/db.ts';
// Log into the MySQL DB
const dbClient = await new Client().connect({
hostname: LOCALMODE ? config.db.localhost : config.db.host,
port: config.db.port,
username: config.db.username,
password: config.db.password,
});
console.log('Attempting to create DB'); console.log('Attempting to create DB');
await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`); await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`);
@ -25,9 +12,7 @@ console.log('DB created');
console.log('Attempt to drop all tables'); console.log('Attempt to drop all tables');
await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`); await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`);
await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`); await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_prefix;`); await dbClient.execute(`DROP TABLE IF EXISTS guild_settings;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_mod_role;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_clean_channel;`);
console.log('Tables dropped'); console.log('Tables dropped');
console.log('Attempting to create table command_cnt'); console.log('Attempting to create table command_cnt');
@ -47,41 +32,21 @@ await dbClient.execute(`
IN cmd CHAR(20) IN cmd CHAR(20)
) )
BEGIN BEGIN
declare oldcnt bigint unsigned; declare oldCnt bigint unsigned;
set oldcnt = (SELECT count FROM command_cnt WHERE command = cmd); set oldCnt = (SELECT count FROM command_cnt WHERE command = cmd);
UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd; UPDATE command_cnt SET count = oldCnt + 1 WHERE command = cmd;
END END
`); `);
console.log('Stored Procedure created'); console.log('Stored Procedure created');
console.log('Attempting to create table guild_prefix'); console.log('Attempting to create table guild_settings');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE guild_prefix ( CREATE TABLE guild_settings (
guildId bigint unsigned NOT NULL, guildId bigint unsigned NOT NULL,
prefix char(10) NOT NULL, lfgChannelId bigint unsigned NOT NULL,
PRIMARY KEY (guildid), managerRoleId bigint unsigned NOT NULL,
UNIQUE KEY guild_prefix_guildid_UNIQUE (guildid) logChannelId bigint unsigned NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8; PRIMARY KEY (guildId, lfgChannelId)
`);
console.log('Table created');
console.log('Attempting to create table guild_mod_role');
await dbClient.execute(`
CREATE TABLE guild_mod_role (
guildId bigint unsigned NOT NULL,
roleId bigint unsigned NOT NULL,
PRIMARY KEY (guildid),
UNIQUE KEY guild_mod_role_guildid_UNIQUE (guildid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`);
console.log('Table created');
console.log('Attempting to create table guild_clean_channel');
await dbClient.execute(`
CREATE TABLE guild_clean_channel (
guildId bigint unsigned NOT NULL,
channelId bigint unsigned NOT NULL,
PRIMARY KEY (guildid, channelId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log('Table created'); console.log('Table created');

View File

@ -1,26 +1,15 @@
// This file will populate the tables with default values // This file will populate the tables with default values
import { import { dbClient } from '../src/db.ts';
// MySQL deps
Client,
} from '../deps.ts';
import { LOCALMODE } from '../flags.ts'; console.log('Attempting to insert default actions into command_cnt');
import config from '../config.ts'; const actions = [
'cmd-setup',
// Log into the MySQL DB 'cmd-info',
const dbClient = await new Client().connect({ 'cmd-report',
hostname: LOCALMODE ? config.db.localhost : config.db.host, ];
port: config.db.port, for (const action of actions) {
db: config.db.name, await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [action]).catch((e) => {
username: config.db.username,
password: config.db.password,
});
console.log('Attempting to insert default commands into command_cnt');
const commands = ['ping', 'help', 'info', 'version', 'report', 'privacy', 'lfg', 'prefix'];
for (const command of commands) {
await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [command]).catch((e) => {
console.log(`Failed to insert into database`, e); console.log(`Failed to insert into database`, e);
}); });
} }

View File

@ -12,6 +12,7 @@ export {
ApplicationCommandFlags, ApplicationCommandFlags,
ApplicationCommandOptionTypes, ApplicationCommandOptionTypes,
ApplicationCommandTypes, ApplicationCommandTypes,
ChannelTypes,
createBot, createBot,
editBotMember, editBotMember,
editBotStatus, editBotStatus,
@ -21,6 +22,10 @@ export {
sendInteractionResponse, sendInteractionResponse,
sendMessage, sendMessage,
startBot, startBot,
OverwriteTypes,
BitwisePermissionFlags,
MessageComponentTypes,
ButtonStyles,
} from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; } from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
export type { export type {
ActionRow, ActionRow,
@ -37,6 +42,7 @@ export type {
MakeRequired, MakeRequired,
Message, Message,
PermissionStrings, PermissionStrings,
DiscordEmbedField,
} from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; } from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
export { Client } from 'https://deno.land/x/mysql@v2.11.0/mod.ts'; export { Client } from 'https://deno.land/x/mysql@v2.11.0/mod.ts';

1
mod.ts
View File

@ -18,4 +18,5 @@ enableCacheSweepers(bot);
// Start the bot // Start the bot
await startBot(bot); await startBot(bot);
// Announce the slash commands so users can use them
await createSlashCommands(bot); await createSlashCommands(bot);

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 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 failColor = 0xe71212;
export const warnColor = 0xe38f28; export const warnColor = 0xe38f28;
@ -17,8 +18,8 @@ export const getRandomStatus = (guildCount: number): string => {
return statuses[Math.floor((Math.random() * statuses.length) + 1)]; return statuses[Math.floor((Math.random() * statuses.length) + 1)];
}; };
export const isLFGChannel = (channelId: bigint) => { export const isLFGChannel = (guildId: bigint, channelId: bigint) => {
return (lfgChannels.includes(channelId) || channelId === 0n) ? ApplicationCommandFlags.Ephemeral : undefined; return (lfgChannelSettings.has(`${guildId}-${channelId}`) || channelId === 0n || guildId === 0n) ? ApplicationCommandFlags.Ephemeral : undefined;
}; };
export const generateReport = (msg: string) => ({ export const generateReport = (msg: string) => ({
@ -28,3 +29,20 @@ export const generateReport = (msg: string) => ({
description: msg, 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 { 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 utils from '../utils.ts';
import info from './info.ts'; import info from './info.ts';
import report from './report.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) => { export const createSlashCommands = async (bot: Bot) => {
const globalCommands: MakeRequired<CreateApplicationCommand, 'name'>[] = []; const globalCommands: MakeRequired<CreateApplicationCommand, 'name'>[] = [];

View File

@ -12,14 +12,14 @@ const details: CommandDetails = {
}; };
const execute = (bot: Bot, interaction: Interaction) => { 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( bot.helpers.sendInteractionResponse(
interaction.id, interaction.id,
interaction.token, interaction.token,
{ {
type: InteractionResponseTypes.ChannelMessageWithSource, type: InteractionResponseTypes.ChannelMessageWithSource,
data: { data: {
flags: isLFGChannel(interaction.channelId || 0n), flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: `${config.name}, the LFG bot`, title: `${config.name}, the LFG bot`,

View File

@ -1,6 +1,6 @@
import config from '../../config.ts'; import config from '../../config.ts';
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, Bot, Interaction, InteractionResponseTypes, sendMessage } from '../../deps.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 { dbClient, queries } from '../db.ts';
import { CommandDetails } from '../types/commandTypes.ts'; import { CommandDetails } from '../types/commandTypes.ts';
import utils from '../utils.ts'; import utils from '../utils.ts';
@ -22,26 +22,27 @@ const details: CommandDetails = {
}; };
const execute = (bot: Bot, interaction: Interaction) => { const execute = (bot: Bot, interaction: Interaction) => {
console.log(interaction); dbClient.execute(queries.callIncCnt('cmd-report')).catch((e) => utils.commonLoggers.dbError('report.ts', 'call sproc INC_CNT on', e));
dbClient.execute(queries.callIncCnt('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 || 'Missing Options')).catch((e: Error) => sendMessage(bot, config.reportChannel, generateReport(interaction.data.options[0].value as string)).catch((e: Error) => utils.commonLoggers.interactionSendError('report.ts:28', interaction, e));
utils.commonLoggers.interactionSendError('report.ts:28', interaction, e) bot.helpers.sendInteractionResponse(
); interaction.id,
bot.helpers.sendInteractionResponse( interaction.token,
interaction.id, {
interaction.token, type: InteractionResponseTypes.ChannelMessageWithSource,
{ data: {
type: InteractionResponseTypes.ChannelMessageWithSource, flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
data: { embeds: [{
flags: isLFGChannel(interaction.channelId || 0n), color: successColor,
embeds: [{ title: 'Failed command has been reported to my developer.',
color: successColor, description: `For more in depth support, and information about planned maintenance, please join the support server [here](${config.links.supportServer}).`,
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 { 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 config from '../config.ts';
import { Client } from '../deps.ts'; import { Client } from '../deps.ts';
import { LOCALMODE } from '../flags.ts'; import { LOCALMODE } from '../flags.ts';
import { LfgChannelSetting, DBGuildSettings } from './types/commandTypes.ts';
export const dbClient = await new Client().connect({ export const dbClient = await new Client().connect({
hostname: LOCALMODE ? config.db.localhost : config.db.host, hostname: LOCALMODE ? config.db.localhost : config.db.host,
@ -14,4 +15,12 @@ export const queries = {
callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`, 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[]; defaultMemberPermissions?: PermissionStrings[];
}; };
export type Commands = { export type Command = {
details: CommandDetails; details: CommandDetails;
execute: Function; 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}`); 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) => 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}`); 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}`); const dbError = (location: string, type: string, err: Error) => genericLogger(LT.ERROR, `${location} | Failed to ${type} database | Error: ${err.name} - ${err.message}`);
export default { export default {
commonLoggers: { commonLoggers: {
channelUpdateError,
dbError, dbError,
interactionSendError, interactionSendError,
messageGetError, messageGetError,