Change tab size

This commit is contained in:
Dakedres 2025-03-18 16:25:11 -06:00
parent 7912b455e9
commit ece60db183
2 changed files with 407 additions and 407 deletions

View File

@ -1,65 +1,65 @@
export default { export default {
rollRegex: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/, rollRegex: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/, optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/,
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g, descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
macroNameRegex: /^[a-z0-9]+$/, macroNameRegex: /^[a-z0-9]+$/,
commands: { commands: {
about: { about: {
name: 'about', name: 'about',
description: "Get information about dicedicedice" description: "Get information about dicedicedice"
}, },
macro: { macro: {
name: 'macro', name: 'macro',
description: "Manage macros", description: "Manage macros",
'dm_permission': false, 'dm_permission': false,
options: [ options: [
{ {
name: 'add', name: 'add',
description: "Define a dice macro", description: "Define a dice macro",
type: 1, // Sub command type: 1, // Sub command
options: [ options: [
{ {
name: "name", name: "name",
description: "Name of the macro", description: "Name of the macro",
type: 3, // String type: 3, // String
required: true required: true
}, },
{ {
name: "dice", name: "dice",
description: "The dice expression to save as a macro", description: "The dice expression to save as a macro",
type: 3, // String type: 3, // String
required: true required: true
} }
] ]
}, },
{ {
name: 'remove', name: 'remove',
description: "Remove a macro", description: "Remove a macro",
type: 1, // Sub command type: 1, // Sub command
options: [ options: [
{ {
name: "name", name: "name",
description: "Name of the macro", description: "Name of the macro",
type: 3, // String type: 3, // String
required: true, required: true,
autocomplete: true, autocomplete: true,
getAutocomplete: interaction => { getAutocomplete: interaction => {
let macros = globalThis.macroCache.get(interaction.guild.id) let macros = globalThis.macroCache.get(interaction.guild.id)
return macros ? Object.keys(macros) : [] return macros ? Object.keys(macros) : []
} }
} }
] ]
} }
] ]
} }
}, },
iconUrl: 'https://github.com/Dakedres/dicedicedice/raw/main/assets/eater-transparent.png', iconUrl: 'https://github.com/Dakedres/dicedicedice/raw/main/assets/eater-transparent.png',
errorMessage: error => `\ errorMessage: error => `\
Something went wrong trying to execute that command. Something went wrong trying to execute that command.
\`\`\`fix \`\`\`fix
${error.toString()} ${error.toString()}
@ -67,7 +67,7 @@ ${error.toString()}
If this issue persists please report it here: <https://github.com/Dakedres/dicedicedice/issues>\ If this issue persists please report it here: <https://github.com/Dakedres/dicedicedice/issues>\
`, `,
aboutMessage: (guildCount) => `\ aboutMessage: (guildCount) => `\
A discord bot for metaphorically "rolling dice"/generating random values. Made for use with Weaverdice systems. A discord bot for metaphorically "rolling dice"/generating random values. Made for use with Weaverdice systems.
Present in ~${guildCount} guilds! Present in ~${guildCount} guilds!

View File

@ -11,508 +11,508 @@ globalThis.macroCache = new Map()
const db = new ClassicLevel('./db') const db = new ClassicLevel('./db')
const client = new Client({ const client = new Client({
intents: [ intents: [
GatewayIntentBits.Guilds, GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent, GatewayIntentBits.MessageContent,
GatewayIntentBits.DirectMessages GatewayIntentBits.DirectMessages
], ],
partials: [ partials: [
Partials.Channel Partials.Channel
] ]
}) })
const rest = new REST().setToken(process.env.DISCORD_TOKEN) const rest = new REST().setToken(process.env.DISCORD_TOKEN)
const ParseRollInt = (value, defaultValue) => const ParseRollInt = (value, defaultValue) =>
value ? parseInt(value) : defaultValue value ? parseInt(value) : defaultValue
const ParseOptionRoll = expression => { const ParseOptionRoll = expression => {
let match = constants.optionRollRegex.exec(expression.trim()) let match = constants.optionRollRegex.exec(expression.trim())
let [ let [
count, count,
modeSize, modeSize,
mode, mode,
size, size,
operationModifier, operationModifier,
operation, operation,
modifier modifier
] = match ] = match
.slice(1) .slice(1)
return { return {
count: ParseRollInt(count), count: ParseRollInt(count),
mode, mode,
size: ParseRollInt(size), size: ParseRollInt(size),
operation, operation,
modifier: ParseRollInt(modifier), modifier: ParseRollInt(modifier),
descriptionConditions: PullDescription(expression, match) descriptionConditions: PullDescription(expression, match)
} }
} }
const ParseRoll = expression => { const ParseRoll = expression => {
let match = constants.rollRegex.exec(expression.trim()) let match = constants.rollRegex.exec(expression.trim())
if(match == null) if(match == null)
return return
let [ let [
count, count,
mode, mode,
size, size,
modifierString, modifierString,
operation, operation,
modifier modifier
] = match.slice(1) ] = match.slice(1)
return { return {
count: ParseRollInt(count, 1), count: ParseRollInt(count, 1),
mode, mode,
size: ParseRollInt(size), size: ParseRollInt(size),
operation, operation,
modifier: ParseRollInt(modifier), modifier: ParseRollInt(modifier),
descriptionConditions: PullDescription(expression, match) descriptionConditions: PullDescription(expression, match)
} }
} }
const PullDescription = (expression, match) => { const PullDescription = (expression, match) => {
if(match[0].length == expression.length) if(match[0].length == expression.length)
return return
return ParseDescription(expression.slice(match[0].length)) return ParseDescription(expression.slice(match[0].length))
} }
const ParseDescription = description => { const ParseDescription = description => {
let conditions = [] let conditions = []
let match let match
while((match = constants.descriptionRegex.exec(description)) !== null) { while((match = constants.descriptionRegex.exec(description)) !== null) {
let range let range
let [ let [
rangeExp, rangeExp,
valueExp, valueExp,
content content
] = match.slice(2) ] = match.slice(2)
if(rangeExp) { if(rangeExp) {
let split = rangeExp.split('-') let split = rangeExp.split('-')
range = { range = {
lower: ParseRollInt(split[0], -Infinity), lower: ParseRollInt(split[0], -Infinity),
upper: ParseRollInt(split[1], Infinity) upper: ParseRollInt(split[1], Infinity)
} }
} else if(valueExp) { } else if(valueExp) {
range = { range = {
upper: valueExp, upper: valueExp,
lower: valueExp lower: valueExp
} }
} }
conditions.push({ conditions.push({
range, range,
content: content.trim() content: content.trim()
}) })
} }
return conditions return conditions
} }
const OnMessage = (message, respond) => { const OnMessage = (message, respond) => {
let dice = ParseRoll(message.content) let dice = ParseRoll(message.content)
if(dice == undefined) if(dice == undefined)
return // No dice return // No dice
RollDice(dice, respond) RollDice(dice, respond)
} }
const RollDice = (dice, respond) => { const RollDice = (dice, respond) => {
if(dice.size > 255) { if(dice.size > 255) {
respond('That die is way too big... .-.') respond('That die is way too big... .-.')
return return
} else if(dice.size < 2) { } else if(dice.size < 2) {
respond('I cannot even fathom a die with that geometry ;-;') respond('I cannot even fathom a die with that geometry ;-;')
return return
} }
if(dice.count > 100) { if(dice.count > 100) {
respond('I don\'t have that many dice O_O') respond('I don\'t have that many dice O_O')
return return
} }
let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ] let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ]
.map(n => Math.ceil((n / 256) * dice.size)) .map(n => Math.ceil((n / 256) * dice.size))
let result = 0 let result = 0
let operationSymbol = dice.operation let operationSymbol = dice.operation
let response = '' let response = ''
switch(dice.mode.toLowerCase()) { switch(dice.mode.toLowerCase()) {
case 'd': case 'd':
result = rolls.reduce((a, v) => a + v, 0) result = rolls.reduce((a, v) => a + v, 0)
break break
case 'h': case 'h':
result = rolls.reduce((a, v) => v > a ? v : a, 0) result = rolls.reduce((a, v) => v > a ? v : a, 0)
break break
case 'l': case 'l':
result = rolls.reduce((a, v) => v < a ? v : a, Infinity) result = rolls.reduce((a, v) => v < a ? v : a, Infinity)
break break
case 'f': case 'f':
let pseudoMedian = Math.floor(dice.size / 2) let pseudoMedian = Math.floor(dice.size / 2)
let resultDistance = -1 let resultDistance = -1
for(let roll of rolls) { for(let roll of rolls) {
let distance = Math.abs(roll - pseudoMedian) let distance = Math.abs(roll - pseudoMedian)
if(distance > resultDistance) { if(distance > resultDistance) {
result = roll result = roll
resultDistance = distance resultDistance = distance
} }
} }
break break
} }
switch(dice.operation) { switch(dice.operation) {
case '+': case '+':
result += dice.modifier result += dice.modifier
break break
case '-': case '-':
result -= dice.modifier result -= dice.modifier
break break
case 'x': case 'x':
operationSymbol = '*' operationSymbol = '*'
case '*': case '*':
result = result * dice.modifier result = result * dice.modifier
break break
case '/': case '/':
result = result / dice.modifier result = result / dice.modifier
break break
} }
if(dice.descriptionConditions) { if(dice.descriptionConditions) {
for(let { range, content } of dice.descriptionConditions) { for(let { range, content } of dice.descriptionConditions) {
if(!range || result >= range.lower && result <= range.upper) if(!range || result >= range.lower && result <= range.upper)
response += `'${content}', ` response += `'${content}', `
} }
} }
response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}` response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}`
if(dice.operation) { if(dice.operation) {
response += ' ' + operationSymbol + ' ' + dice.modifier response += ' ' + operationSymbol + ' ' + dice.modifier
} }
respond(response) respond(response)
} }
const SaveReply = (message, reply) => { const SaveReply = (message, reply) => {
globalThis.replies.set(message.id, { globalThis.replies.set(message.id, {
id: reply.id, id: reply.id,
timestamp: Date.now() timestamp: Date.now()
}) })
} }
const MessageCycle = async message => { const MessageCycle = async message => {
OnMessage(message, async content => { OnMessage(message, async content => {
SaveReply(message, await message.reply(content) ) SaveReply(message, await message.reply(content) )
}) })
} }
const RehandleMessage = async (message, reply) => { const RehandleMessage = async (message, reply) => {
OnMessage(message, async content => { OnMessage(message, async content => {
SaveReply(message, await reply.edit(content) ) SaveReply(message, await reply.edit(content) )
}) })
} }
const PruneReplies = () => { const PruneReplies = () => {
for(let [ id, entry ] of globalThis.replies.entries()) { for(let [ id, entry ] of globalThis.replies.entries()) {
let age = Date.now() - entry.timestamp let age = Date.now() - entry.timestamp
if(age > 1000 * 60 * 3) { if(age > 1000 * 60 * 3) {
globalThis.replies.delete(id) globalThis.replies.delete(id)
} }
} }
} }
const InteractionRespond = (interaction, content) => { const InteractionRespond = (interaction, content) => {
let reply = { content, ephemeral: true } let reply = { content, ephemeral: true }
if(interaction.replied || interaction.deferred) { if(interaction.replied || interaction.deferred) {
return interaction.followUp(reply) return interaction.followUp(reply)
} else { } else {
return interaction.reply(reply) return interaction.reply(reply)
} }
} }
const ErrorHandler = (interaction) => (error) => const ErrorHandler = (interaction) => (error) =>
InteractionRespond(interaction, constants.errorMessage(error) ) InteractionRespond(interaction, constants.errorMessage(error) )
.catch(reportingError => console.error('Could not display error message:\n ', reportingError) ) .catch(reportingError => console.error('Could not display error message:\n ', reportingError) )
const Command = (data, callback) => { const Command = (data, callback) => {
globalThis.commands.set(data.name, { globalThis.commands.set(data.name, {
data, data,
execute: callback execute: callback
}) })
} }
const Subcommands = (data, subcommandCallbacks) => const Subcommands = (data, subcommandCallbacks) =>
Command(data, interaction => { Command(data, interaction => {
return subcommandCallbacks[interaction.options.getSubcommand()](interaction) return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
}) })
const OpenMacros = guildId => const OpenMacros = guildId =>
db.sublevel(guildId).sublevel('macros') db.sublevel(guildId).sublevel('macros')
const ReloadMacros = async guildId => { const ReloadMacros = async guildId => {
let commands = [] let commands = []
let macros = OpenMacros(guildId) let macros = OpenMacros(guildId)
let cacheEntry = {} let cacheEntry = {}
for await (let [ name, dice ] of macros.iterator() ) { for await (let [ name, dice ] of macros.iterator() ) {
cacheEntry[name] = dice cacheEntry[name] = dice
commands.push({ commands.push({
name, name,
description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100), description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
options: [ options: [
{ {
name: "options", name: "options",
description: "Dice, modifiers, or descriptions to apply over the macro", description: "Dice, modifiers, or descriptions to apply over the macro",
type: 3 type: 3
} }
] ]
}) })
} }
globalThis.macroCache.set(guildId, cacheEntry) globalThis.macroCache.set(guildId, cacheEntry)
await rest.put( await rest.put(
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId), Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
{ body: globalThis.commands } { body: globalThis.commands }
) )
.catch(err => console.error('Failed to reload macros:', err) ) .catch(err => console.error('Failed to reload macros:', err) )
} }
const Elipsify = (string, maxLength) => const Elipsify = (string, maxLength) =>
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
const PruneDB = async () => { const PruneDB = async () => {
let validIds = [] let validIds = []
for await(let key of db.keys()) { for await(let key of db.keys()) {
let [ guildId ] = key.split('!').slice(1) let [ guildId ] = key.split('!').slice(1)
if(validIds.includes(guildId)) if(validIds.includes(guildId))
continue continue
if(client.guilds.cache.has(guildId)) { if(client.guilds.cache.has(guildId)) {
validIds.push(guildId) validIds.push(guildId)
} else { } else {
await db.del(key) await db.del(key)
} }
} }
return validIds return validIds
} }
const Responses = (interaction, ephemeral) => async content => const Responses = (interaction, ephemeral) => async content =>
interaction.reply({ content, ephemeral }) interaction.reply({ content, ephemeral })
const Subscribe = (event, callback) => { const Subscribe = (event, callback) => {
client.on(event, (...args) => { client.on(event, (...args) => {
return callback(...args) return callback(...args)
.catch(err => console.error(err)) .catch(err => console.error(err))
}) })
} }
const HandleCommand = async interaction => { const HandleCommand = async interaction => {
if(globalThis.commands.has(interaction.commandName) ) { if(globalThis.commands.has(interaction.commandName) ) {
globalThis.commands.get(interaction.commandName).execute(interaction) globalThis.commands.get(interaction.commandName).execute(interaction)
.catch(ErrorHandler(interaction)) .catch(ErrorHandler(interaction))
return return
} }
await interaction.deferReply() await interaction.deferReply()
let roll = globalThis.macroCache.get(interaction.guild.id)[interaction.commandName] let roll = globalThis.macroCache.get(interaction.guild.id)[interaction.commandName]
if(roll) { if(roll) {
let dice = ParseRoll(roll) let dice = ParseRoll(roll)
let options = interaction.options.get('options') let options = interaction.options.get('options')
if(options) { if(options) {
let optionDice = ParseOptionRoll(options.value) let optionDice = ParseOptionRoll(options.value)
for(let [ key, value ] of Object.entries(optionDice)) { for(let [ key, value ] of Object.entries(optionDice)) {
if(value) if(value)
dice[key] = Array.isArray(value) ? value.concat(dice[key]) : value dice[key] = Array.isArray(value) ? value.concat(dice[key]) : value
} }
} }
RollDice(dice, content => interaction.followUp(content) ) RollDice(dice, content => interaction.followUp(content) )
} }
} }
const FindOption = (options, name) => const FindOption = (options, name) =>
options.find(option => option.name == name) options.find(option => option.name == name)
const HandleAutocomplete = async interaction => { const HandleAutocomplete = async interaction => {
if(globalThis.commands.has(interaction.commandName) ) { if(globalThis.commands.has(interaction.commandName) ) {
let { data } = globalThis.commands.get(interaction.commandName) let { data } = globalThis.commands.get(interaction.commandName)
let subcommand = interaction.options.getSubcommand() let subcommand = interaction.options.getSubcommand()
let focusedOption = interaction.options.getFocused(true) let focusedOption = interaction.options.getFocused(true)
if(subcommand !== undefined) { if(subcommand !== undefined) {
data = FindOption(data.options, subcommand) data = FindOption(data.options, subcommand)
} }
let option = FindOption(data.options, focusedOption.name) let option = FindOption(data.options, focusedOption.name)
if(!option) { if(!option) {
console.error('Could not find option: ' + focusedOption) console.error('Could not find option: ' + focusedOption)
return return
} }
let filtered = option let filtered = option
.getAutocomplete(interaction) .getAutocomplete(interaction)
.filter(choice => choice.startsWith(focusedOption.value) ) .filter(choice => choice.startsWith(focusedOption.value) )
.map(choice => ({ name: choice, value: choice }) ) .map(choice => ({ name: choice, value: choice }) )
await interaction.respond(filtered) await interaction.respond(filtered)
} }
} }
const Interaction = interaction => { const Interaction = interaction => {
if(interaction.isChatInputCommand()) { if(interaction.isChatInputCommand()) {
return HandleCommand(interaction) return HandleCommand(interaction)
} else if(interaction.isAutocomplete()) { } else if(interaction.isAutocomplete()) {
return HandleAutocomplete(interaction) return HandleAutocomplete(interaction)
.catch(console.error) .catch(console.error)
} }
} }
const MessageUpdate = async (oldMessage, newMessage) => { const MessageUpdate = async (oldMessage, newMessage) => {
if(globalThis.replies.has(newMessage.id) ) { if(globalThis.replies.has(newMessage.id) ) {
let { id } = globalThis.replies.get(newMessage.id) let { id } = globalThis.replies.get(newMessage.id)
newMessage.channel.messages.fetch(id) newMessage.channel.messages.fetch(id)
.then(reply => RehandleMessage(newMessage, reply) ) .then(reply => RehandleMessage(newMessage, reply) )
.catch(err => MessageCycle(newMessage) ) .catch(err => MessageCycle(newMessage) )
} else { } else {
MessageCycle(newMessage) MessageCycle(newMessage)
} }
} }
const About = async (interaction) => { const About = async (interaction) => {
let embed = { let embed = {
title: 'dicedicedice', title: 'dicedicedice',
thumbnail: { thumbnail: {
url: constants.iconUrl url: constants.iconUrl
}, },
description: constants.aboutMessage(client.guilds.cache.size) description: constants.aboutMessage(client.guilds.cache.size)
} }
await interaction.reply({ await interaction.reply({
embeds: [ embed ], embeds: [ embed ],
ephemeral: true ephemeral: true
}) })
} }
const AddMacro = async (interaction) => { const AddMacro = async (interaction) => {
let respond = Responses(interaction, true) let respond = Responses(interaction, true)
let name = interaction.options.get('name').value.toLowerCase() let name = interaction.options.get('name').value.toLowerCase()
if(!constants.macroNameRegex.test(name)) if(!constants.macroNameRegex.test(name))
return respond("Please provide a macro name that consists of only alphanumeric characters.") return respond("Please provide a macro name that consists of only alphanumeric characters.")
if(commands.has(name)) if(commands.has(name))
return respond("Uhh,, I think that macro name is already taken by my own commands, sorry.") return respond("Uhh,, I think that macro name is already taken by my own commands, sorry.")
let macros = globalThis.macroCache.get(interaction.guild.id) let macros = globalThis.macroCache.get(interaction.guild.id)
if(macros && !macros[name] && Object.keys(macros).length >= 100) if(macros && !macros[name] && Object.keys(macros).length >= 100)
return respond("I can't keep track of that many macros,, ;-;") return respond("I can't keep track of that many macros,, ;-;")
let dice = interaction.options.get('dice').value let dice = interaction.options.get('dice').value
if(!constants.rollRegex.test(dice) ) if(!constants.rollRegex.test(dice) )
return respond("Please provide a valid roll expression.") return respond("Please provide a valid roll expression.")
await interaction.deferReply({ ephemeral: true }) await interaction.deferReply({ ephemeral: true })
await Promise.all([ await Promise.all([
OpenMacros(interaction.guild.id).put(name, dice), OpenMacros(interaction.guild.id).put(name, dice),
ReloadMacros(interaction.guild.id) ReloadMacros(interaction.guild.id)
]) ])
interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`) interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`)
} }
const RemoveMacro = async (interaction) => { const RemoveMacro = async (interaction) => {
let name = interaction.options.get('name').value.toLowerCase() let name = interaction.options.get('name').value.toLowerCase()
let macros = macroCache.get(interaction.guild.id) let macros = macroCache.get(interaction.guild.id)
let respond = openResponses(interaction, true) let respond = openResponses(interaction, true)
if(!macros) if(!macros)
return respond('There aren\'t even any macros in this guild!') return respond('There aren\'t even any macros in this guild!')
let dice = macros && macroCache.get(interaction.guild.id)[name] let dice = macros && macroCache.get(interaction.guild.id)[name]
if(!dice) if(!dice)
return respond("There isn't a macro with that name .-.") return respond("There isn't a macro with that name .-.")
await interaction.deferReply({ ephemeral: true }) await interaction.deferReply({ ephemeral: true })
await Promise.all([ await Promise.all([
openMacros(interaction.guild.id).del(name), openMacros(interaction.guild.id).del(name),
reloadMacros(interaction.guild.id) reloadMacros(interaction.guild.id)
]) ])
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``) await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
} }
const Start = async () => { const Start = async () => {
Command( Command(
constants.commands.about, constants.commands.about,
About About
) )
Subcommands(constants.commands.macro, { Subcommands(constants.commands.macro, {
add: AddMacro, add: AddMacro,
remove: RemoveMacro remove: RemoveMacro
}) })
Subscribe('interactionCreate', Interaction) Subscribe('interactionCreate', Interaction)
Subscribe('messageCreate', MessageCycle) Subscribe('messageCreate', MessageCycle)
Subscribe('messageUpdate', MessageUpdate) Subscribe('messageUpdate', MessageUpdate)
Subscribe('ready', async () => { Subscribe('ready', async () => {
console.log("Logged in!") console.log("Logged in!")
let guildIds = await PruneDB() let guildIds = await PruneDB()
for(let guildId of guildIds) for(let guildId of guildIds)
await ReloadMacros(guildId) await ReloadMacros(guildId)
console.log("Ready") console.log("Ready")
}) })
await rest.put( await rest.put(
Routes.applicationCommands(process.env.DISCORD_ID), Routes.applicationCommands(process.env.DISCORD_ID),
{ {
body: [ ...globalThis.commands.values() ] body: [ ...globalThis.commands.values() ]
.map(command => command.data ) .map(command => command.data )
} }
) )
.catch(err => console.error('Command registration failed: ', err) ) .catch(err => console.error('Command registration failed: ', err) )
console.log(process.env) console.log(process.env)
await client.login(process.env.DISCORD_TOKEN) await client.login(process.env.DISCORD_TOKEN)
.catch(err => console.error('Login failed: ', err) ) .catch(err => console.error('Login failed: ', err) )
setInterval(PruneReplies, 1000 * 60) setInterval(PruneReplies, 1000 * 60)
} }
Start() Start()