Initial setup for standard events

This commit is contained in:
Ean Milligan (Bastion) 2023-01-11 18:06:20 -05:00
parent 460a7ed557
commit 3e6168a396
14 changed files with 310 additions and 37 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ config.ts
logs
db/update.ts
deno.lock

View File

@ -2,7 +2,7 @@ export const config = {
'name': 'Group Up', // Name of the bot
'version': '1.0.0', // Version of the 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
'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

46
deps.ts
View File

@ -1,40 +1,14 @@
// All external dependancies are to be loaded here to make updating dependancy versions much easier
export {
botId,
cache,
cacheHandlers,
deleteMessage,
DiscordActivityTypes,
DiscordButtonStyles,
DiscordInteractionResponseTypes,
DiscordInteractionTypes,
editBotNickname,
editBotStatus,
getGuild,
getMessage,
getUser,
hasGuildPermissions,
Intents,
sendDirectMessage,
sendInteractionResponse,
sendMessage,
startBot,
structures,
} from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
// All external dependencies are to be loaded here to make updating dependency versions much easier
import { getBotIdFromToken } from 'https://deno.land/x/discordeno@16.0.1/mod.ts';
import config from './config.ts';
import { LOCALMODE } from './flags.ts';
export const botId = getBotIdFromToken(LOCALMODE ? config.localToken : config.token);
export type {
ActionRow,
ButtonComponent,
ButtonData,
CreateMessage,
DebugArg,
DiscordenoGuild,
DiscordenoMember,
DiscordenoMessage,
Embed,
EmbedField,
Interaction,
} from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
export { enableCachePlugin, enableCacheSweepers } from 'https://deno.land/x/discordeno@17.0.1/plugins/cache/mod.ts';
export type { BotWithCache } from 'https://deno.land/x/discordeno@17.0.1/plugins/cache/mod.ts';
export { ActivityTypes, createBot, editBotMember, editBotStatus, getBotIdFromToken, Intents, sendInteractionResponse, sendMessage, startBot } from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
export type { ActionRow, Bot, ButtonComponent, CreateMessage, Embed, EventHandlers, Guild, Interaction, Message } from 'https://deno.land/x/discordeno@17.0.1/mod.ts';
export { Client } from 'https://deno.land/x/mysql@v2.11.0/mod.ts';

18
mod.ts
View File

@ -0,0 +1,18 @@
import config from './config.ts';
import { DEBUG, LOCALMODE } from './flags.ts';
import { createBot, enableCachePlugin, enableCacheSweepers, initLog, Intents, startBot } from './deps.ts';
import { events } from './src/events.ts';
// Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup
initLog('logs', DEBUG);
// Set up the Discord Bot
const bot = enableCachePlugin(createBot({
token: LOCALMODE ? config.localToken : config.token,
intents: Intents.MessageContent | Intents.GuildMessages | Intents.DirectMessages | Intents.Guilds | Intents.GuildMessageReactions,
events,
}));
enableCacheSweepers(bot);
// Start the bot
startBot(bot);

30
src/commandUtils.ts Normal file
View File

@ -0,0 +1,30 @@
import config from '../config.ts';
export const failColor = 0xe71212;
export const warnColor = 0xe38f28;
export const successColor = 0x0f8108;
export const infoColor1 = 0x313bf9;
export const infoColor2 = 0x6805e9;
export const getRandomStatus = (guildCount: number): string => {
let status = '';
switch (Math.floor((Math.random() * 5) + 1)) {
case 1:
status = `${config.prefix}help for commands`;
break;
case 2:
status = `Running V${config.version}`;
break;
case 3:
status = `${config.prefix}info to learn more`;
break;
case 4:
status = 'Mention me to check my prefix!';
break;
default:
status = `Running LFGs in ${guildCount} servers`;
break;
}
return status;
};

15
src/db.ts Normal file
View File

@ -0,0 +1,15 @@
import config from '../config.ts';
import { Client } from '../deps.ts';
import { LOCALMODE } from '../flags.ts';
export const dbClient = await new Client().connect({
hostname: LOCALMODE ? config.db.localhost : config.db.host,
port: config.db.port,
db: config.db.name,
username: config.db.username,
password: config.db.password,
});
export const queries = {
callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`,
};

17
src/events.ts Normal file
View File

@ -0,0 +1,17 @@
import { DEVMODE } from '../flags.ts';
import {
// Discordeno deps
EventHandlers,
} from '../deps.ts';
import eventHandlers from './events/_index.ts';
export const events: Partial<EventHandlers> = {};
events.ready = eventHandlers.ready;
events.guildCreate = eventHandlers.guildCreate;
events.guildDelete = eventHandlers.guildDelete;
events.messageCreate = eventHandlers.messageCreate;
if (DEVMODE) {
events.debug = eventHandlers.debug;
}

13
src/events/_index.ts Normal file
View File

@ -0,0 +1,13 @@
import { ready } from './ready.ts';
import { guildCreate } from './guildCreate.ts';
import { guildDelete } from './guildDelete.ts';
import { debug } from './debug.ts';
import { messageCreate } from './messageCreate.ts';
export default {
ready,
guildCreate,
guildDelete,
debug,
messageCreate,
};

8
src/events/debug.ts Normal file
View File

@ -0,0 +1,8 @@
import {
// Log4Deno deps
log,
LT,
} from '../../deps.ts';
import utils from '../utils.ts';
export const debug = (dmsg: string) => log(LT.LOG, `Debug Message | ${utils.jsonStringifyBig(dmsg)}`);

40
src/events/guildCreate.ts Normal file
View File

@ -0,0 +1,40 @@
import config from '../../config.ts';
import {
// Discordeno deps
Bot,
Guild,
// Log4Deno deps
log,
LT,
// Discordeno deps
sendMessage,
} from '../../deps.ts';
import { infoColor1 } from '../commandUtils.ts';
import utils from '../utils.ts';
export const guildCreate = (bot: Bot, guild: Guild) => {
log(LT.LOG, `Handling joining guild ${utils.jsonStringifyBig(guild)}`);
sendMessage(bot, config.logChannel, {
embeds: [{
title: 'Guild Joined!',
color: infoColor1,
fields: [
{
name: 'Name:',
value: `${guild.name}`,
inline: true,
},
{
name: 'Id:',
value: `${guild.id}`,
inline: true,
},
{
name: 'Member Count:',
value: `${guild.memberCount}`,
inline: true,
},
],
}],
}).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:95', 'Join Guild', e));
};

39
src/events/guildDelete.ts Normal file
View File

@ -0,0 +1,39 @@
import config from '../../config.ts';
import {
// Discordeno deps
Bot,
// Log4Deno deps
log,
LT,
// Discordeno deps
sendMessage,
} from '../../deps.ts';
import { warnColor } from '../commandUtils.ts';
import { dbClient } from '../db.ts';
import utils from '../utils.ts';
export const guildDelete = async (bot: Bot, guildId: bigint) => {
log(LT.LOG, `Handling leaving guild ${utils.jsonStringifyBig(guildId)}`);
try {
await dbClient.execute('DELETE FROM guild_prefix WHERE guildId = ?', [guildId]);
await dbClient.execute('DELETE FROM guild_mod_role WHERE guildId = ?', [guildId]);
await dbClient.execute('DELETE FROM guild_clean_channel WHERE guildId = ?', [guildId]);
} catch (e) {
log(LT.WARN, `Failed to remove guild from DB: ${utils.jsonStringifyBig(e)}`);
}
sendMessage(bot, config.logChannel, {
embeds: [{
title: 'Removed from Guild',
color: warnColor,
fields: [
{
name: 'Id:',
value: `${guildId}`,
inline: true,
},
],
}],
}).catch((e: Error) => utils.commonLoggers.messageSendError('guildDelete.ts:28', 'Leave Guild', e));
};

View File

@ -0,0 +1,17 @@
import config from '../../config.ts';
import { Bot, botId, Message } from '../../deps.ts';
export const messageCreate = async (bot: Bot, message: Message) => {
// Ignore all messages that are not commands
if (message.content.indexOf(config.prefix) !== 0) {
// Handle @bot messages
if (message.mentionedUserIds[0] === botId && (message.content.trim().startsWith(`<@${botId}>`) || message.content.trim().startsWith(`<@!${botId}>`))) {
}
// return as we are done handling this command
return;
}
// Ignore all other bots
if (message.isFromBot) return;
};

62
src/events/ready.ts Normal file
View File

@ -0,0 +1,62 @@
import config from '../../config.ts';
import { LOCALMODE } from '../../flags.ts';
import { ActivityTypes, Bot, BotWithCache, editBotMember, editBotStatus, log, LT, sendMessage } from '../../deps.ts';
import { getRandomStatus, successColor } from '../commandUtils.ts';
import utils from '../utils.ts';
export const ready = (rawBot: Bot) => {
const bot = rawBot as BotWithCache;
log(LT.INFO, `${config.name} Logged in!`);
editBotStatus(bot, {
activities: [{
name: 'Booting up . . .',
type: ActivityTypes.Game,
createdAt: new Date().getTime(),
}],
status: 'online',
});
// Interval to rotate the status text every 30 seconds to show off more commands
setInterval(async () => {
log(LT.LOG, 'Changing bot status');
try {
// Wrapped in try-catch due to hard crash possible
editBotStatus(bot, {
activities: [{
name: getRandomStatus(bot.guilds.size + bot.dispatchedGuildIds.size),
type: ActivityTypes.Game,
createdAt: new Date().getTime(),
}],
status: 'online',
});
} catch (e) {
log(LT.ERROR, `Failed to update status: ${utils.jsonStringifyBig(e)}`);
}
}, 30000);
// setTimeout added to make sure the startup message does not error out
setTimeout(() => {
LOCALMODE && editBotMember(bot, config.devServer, { nick: `LOCAL - ${config.name}` });
editBotStatus(bot, {
activities: [{
name: 'Booting Complete',
type: ActivityTypes.Game,
createdAt: new Date().getTime(),
}],
status: 'online',
});
sendMessage(bot, config.logChannel, {
embeds: [{
title: `${config.name} is now Online`,
color: successColor,
fields: [
{
name: 'Version:',
value: `${config.version}`,
inline: true,
},
],
}],
}).catch((e: Error) => utils.commonLoggers.messageSendError('ready.ts:71', 'Startup', e));
}, 1000);
};

39
src/utils.ts Normal file
View File

@ -0,0 +1,39 @@
import {
// Log4Deno deps
log,
LT,
// Discordeno deps
Message,
} from '../deps.ts';
const jsonStringifyBig = (input: any) => {
return JSON.stringify(input, (_key, value) => typeof value === 'bigint' ? value.toString() + 'n' : value);
};
const genericLogger = (level: LT, message: string) => log(level, message);
const messageEditError = (location: string, message: Message | string, err: Error) =>
genericLogger(LT.ERROR, `${location} | Failed to edit message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const messageGetError = (location: string, message: Message | string, err: Error) =>
genericLogger(LT.ERROR, `${location} | Failed to get message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const messageSendError = (location: string, message: Message | string, err: Error) =>
genericLogger(LT.ERROR, `${location} | Failed to send message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const messageDeleteError = (location: string, message: Message | string, err: Error) =>
genericLogger(LT.ERROR, `${location} | Failed to delete message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`);
const reactionAddError = (location: string, message: Message | string, err: Error, emoji: string) =>
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 dbError = (location: string, type: string, err: Error) => genericLogger(LT.ERROR, `${location} | Failed to ${type} database | Error: ${err.name} - ${err.message}`);
export default {
commonLoggers: {
dbError,
messageGetError,
messageEditError,
messageSendError,
messageDeleteError,
reactionAddError,
reactionDeleteError,
},
jsonStringifyBig,
};