import { Client, GatewayIntentBits, Events, Collection, CommandInteraction, CommandInteractionOption, ApplicationCommandOptionType } from 'discord.js'; import * as fs from 'fs'; const { token, sitePort, siteURL, clientId, clientSecret } = JSON.parse(fs.readFileSync('./config.json', 'utf8')); import * as path from 'path'; import { initializeAnnouncements } from './lib/subscriptions'; import * as log from './lib/log'; import chalk from 'chalk'; import prettyBytes from 'pretty-bytes'; import { Command } from './types/index'; import { startServer } from './web/web'; import { init as initPVP } from './lib/rpg/pvp'; import { autocomplete } from './lib/autocomplete'; import { setupListeners } from './lib/events'; const bot = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildScheduledEvents, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages, ], }); bot.config = { token, sitePort, siteURL, clientId, clientSecret }; async function init() { log.nonsense('booting chip...'); log.nonsense('starting up web interface...'); await startServer(bot, sitePort); log.nonsense('setting up connection...'); try { await bot.login(token); } catch (err) { log.error('error: network hardware broken?', err); log.error(`${chalk.bold('emergency mode could not be established.')} shutting down.`); process.exit(1); } initPVP(bot); } bot.on(Events.ClientReady, async () => { log.info('jillo online'); log.nonsense('finishing launch'); initializeAnnouncements(bot); bot.commands = new Collection(); const cmdFiles = fs.readdirSync(path.join(__dirname, './commands')).filter((file) => file.endsWith('.js')); for (const file of cmdFiles) { const cmd = (await import(`./commands/${file}`)).default as Command; bot.commands.set(cmd.data.name, cmd); if (cmd.onClientReady) cmd.onClientReady(bot); } log.info('jillo firmware up and running'); log.nonsense(`| running on ${process.platform} ${process.config.variables.host_arch}`); log.nonsense(`| node ${process.version} V8 v${process.versions.v8}`); const memory = process.memoryUsage(); log.nonsense(`| ${prettyBytes(memory.rss)} memory usage, ${prettyBytes(memory.heapUsed)} / ${prettyBytes(memory.heapTotal)} heap usage`); }); setupListeners(bot); function stringifyArg(arg: CommandInteractionOption): string { switch (arg.type) { case ApplicationCommandOptionType.Boolean: case ApplicationCommandOptionType.Integer: case ApplicationCommandOptionType.Number: case ApplicationCommandOptionType.String: return `${arg.name}:${arg.value}`; case ApplicationCommandOptionType.Channel: return `${arg.name}:#${arg.channel?.name}`; case ApplicationCommandOptionType.Role: return `${arg.name}:@${arg.role?.name}`; case ApplicationCommandOptionType.User: return `${arg.name}:@${arg.user?.displayName}`; case ApplicationCommandOptionType.Mentionable: return `${arg.name}:${arg.channel?.id || arg.user?.id || arg.role?.id}`; case ApplicationCommandOptionType.Attachment: return `${arg.name}:`; case ApplicationCommandOptionType.Subcommand: case ApplicationCommandOptionType.SubcommandGroup: return `${arg.name} ${arg.options?.map(stringifyArg).join(' ')}`; } } function stringifyCommand(cmd: CommandInteraction) { return `/${cmd.commandName} ${cmd.options.data.map(stringifyArg).join(' ')}`; } bot.on(Events.InteractionCreate, async (interaction) => { if (interaction.isCommand()) { const command = interaction.client.commands.get(interaction.commandName); if (!command) return; log.nonsense(stringifyCommand(interaction)); try { await command.execute(interaction); } catch (error) { if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) interaction.reply({ content: `\`ERROR\`\n\`\`\`\n${error}\n\`\`\``, ephemeral: true }); if (interaction.deferred) interaction.followUp(`\`ERROR\`\n\`\`\`\n${error}\n\`\`\``); log.error(error); } } else if (interaction.isAutocomplete()) { const command = interaction.client.commands.get(interaction.commandName); if (!command) return; try { if (!command.autocomplete) throw `Trying to invoke autocomplete for command ${interaction.commandName} which does not have it defined`; await autocomplete(command.autocomplete)(interaction); } catch (error) { log.error(error); } } }); process.on('uncaughtException', err => { log.error(err); }); init();