Revert "Websockets."
This commit is contained in:
parent
9ffce17791
commit
eeafcf8f83
65
client.js
65
client.js
@ -1,65 +0,0 @@
|
|||||||
import readline from 'node:readline/promises'
|
|
||||||
import { stdin, stdout } from 'node:process'
|
|
||||||
import WebSocket from 'ws'
|
|
||||||
import { error } from 'node:console'
|
|
||||||
|
|
||||||
let lastMessage = ''
|
|
||||||
|
|
||||||
const writePrompt = () => {
|
|
||||||
stdout.write('> ')
|
|
||||||
if(lastMessage)
|
|
||||||
cli.write(lastMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
const send = object => new Promise((resolve, reject) => {
|
|
||||||
ws.send(JSON.stringify(object), error => {
|
|
||||||
if(error)
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleMessage = async content => {
|
|
||||||
lastMessage = content
|
|
||||||
|
|
||||||
send({
|
|
||||||
type: "message",
|
|
||||||
content: content
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log(error)
|
|
||||||
writePrompt()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const printMessage = messageBuf => {
|
|
||||||
let indent = ' '.repeat(2)
|
|
||||||
let message = messageBuf.toString('utf-8')
|
|
||||||
|
|
||||||
message = indent + message.replaceAll('\n', '\n' + indent) + '\n'
|
|
||||||
stdout.write(message)
|
|
||||||
writePrompt()
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = () => {
|
|
||||||
send({
|
|
||||||
type: 'login',
|
|
||||||
name: 'dev',
|
|
||||||
password: 'dev'
|
|
||||||
})
|
|
||||||
cli.on('line', handleMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Hooks
|
|
||||||
//
|
|
||||||
|
|
||||||
const ws = new WebSocket('ws://localhost:8080')
|
|
||||||
|
|
||||||
const cli = readline.createInterface({
|
|
||||||
input: stdin,
|
|
||||||
output: stdout
|
|
||||||
})
|
|
||||||
|
|
||||||
ws.on('error', console.error)
|
|
||||||
ws.on('open', start)
|
|
||||||
ws.on('message', printMessage)
|
|
@ -21,7 +21,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classic-level": "^1.3.0",
|
"classic-level": "^1.3.0",
|
||||||
"discord.js": "^14.11.0",
|
"discord.js": "^14.11.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3"
|
||||||
"ws": "^8.13.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,41 +4,6 @@ const constants = {
|
|||||||
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
||||||
macroNameRegex: /^[a-z0-9]+$/,
|
macroNameRegex: /^[a-z0-9]+$/,
|
||||||
|
|
||||||
events: {
|
|
||||||
login: 'login'
|
|
||||||
},
|
|
||||||
|
|
||||||
schemas: {
|
|
||||||
events: {
|
|
||||||
login: {
|
|
||||||
'name': 'string',
|
|
||||||
'password': 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
invalidPacket: error => ({
|
|
||||||
type: 'error',
|
|
||||||
id: 0,
|
|
||||||
message: 'Invalid packet: ' + error
|
|
||||||
}),
|
|
||||||
invalidReference: error => ({
|
|
||||||
type: 'error',
|
|
||||||
id: 0,
|
|
||||||
message: 'Invalid packet reference: ' + error
|
|
||||||
}),
|
|
||||||
badLogin: () => ({
|
|
||||||
type: 'error',
|
|
||||||
id: 10,
|
|
||||||
message: 'There is no client with that name, or the password does not match.'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
clients: new Map([
|
|
||||||
[ 'dev', 'dev' ]
|
|
||||||
]),
|
|
||||||
|
|
||||||
commands: {
|
commands: {
|
||||||
about: {
|
about: {
|
||||||
name: 'about',
|
name: 'about',
|
||||||
@ -87,7 +52,6 @@ const constants = {
|
|||||||
|
|
||||||
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
|
||||||
|
539
src/index.js
539
src/index.js
@ -1,108 +1,42 @@
|
|||||||
import constants from './constants.js'
|
import { Client, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
|
||||||
import { WebSocketServer } from 'ws'
|
import * as dotenv from 'dotenv'
|
||||||
import { EventEmitter } from 'node:events'
|
import constants from './constants.js';
|
||||||
|
import { ClassicLevel } from 'classic-level';
|
||||||
|
|
||||||
let connections = new Map()
|
dotenv.config()
|
||||||
|
|
||||||
//
|
const replies = new Map()
|
||||||
// Login & events
|
const commands = new Map()
|
||||||
//
|
const db = new ClassicLevel('./db')
|
||||||
|
const macroCache = new Map()
|
||||||
|
|
||||||
|
const parseRollInt = (value, defaultValue) =>
|
||||||
|
value ? parseInt(value) : defaultValue
|
||||||
|
|
||||||
const handleConnection = ws => {
|
const parseOptionRoll = expression => {
|
||||||
let client
|
let match = constants.optionRollRegex.exec(expression.trim())
|
||||||
|
|
||||||
ws.on('error', console.error)
|
let [
|
||||||
|
count,
|
||||||
ws.on('message', data => {
|
modeSize,
|
||||||
let event
|
mode,
|
||||||
|
size,
|
||||||
try {
|
operationModifier,
|
||||||
event = JSON.parse(data.toString('utf-8') )
|
operation,
|
||||||
} catch(err) {
|
modifier
|
||||||
sendToWebsocket(ws, constants.errors.invalidPacket(err) )
|
] = match
|
||||||
return
|
.slice(1)
|
||||||
}
|
|
||||||
|
return {
|
||||||
if(typeof event !== 'object' || Array.isArray(event) ) {
|
count: parseRollInt(count),
|
||||||
sendToWebsocket(ws, constants.errors.invalidPacket('Event is not an object') )
|
mode,
|
||||||
return
|
size: parseRollInt(size),
|
||||||
}
|
operation,
|
||||||
|
modifier: parseRollInt(modifier),
|
||||||
console.log(event)
|
descriptionConditions: pullDescription(expression, match)
|
||||||
|
|
||||||
if(client) {
|
|
||||||
if(event.reference && typeof event.reference == 'object')
|
|
||||||
sendToWebsocket(ws, constants.errors.invalidReference('Reference cannot be an object') )
|
|
||||||
|
|
||||||
event.client = client
|
|
||||||
handleEvent(event)
|
|
||||||
} else if(event.type === constants.events.login) {
|
|
||||||
client = handleLogin(event, ws)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleLogin = (event, ws) => {
|
|
||||||
if(constants.clients.get(event.name) !== event.password) {
|
|
||||||
replyToWebsocket(ws, event, constants.errors.badLogin() )
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('worked?')
|
|
||||||
|
|
||||||
connections.set(event.name, ws)
|
|
||||||
replyToWebsocket(ws, event, { type: 'success' })
|
|
||||||
return event.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendToWebsocket = (ws, event) =>
|
|
||||||
ws.send(JSON.stringify(event) )
|
|
||||||
|
|
||||||
const replyToWebsocket = (ws, toEvent, withEvent) => {
|
|
||||||
let event = {
|
|
||||||
...withEvent,
|
|
||||||
reference: toEvent.reference
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(connections)
|
|
||||||
|
|
||||||
return sendToWebsocket(ws, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEvent = event => {
|
|
||||||
if(typeof event.type != 'string') {
|
|
||||||
reply(event, constants.errors.invalidPacket("No event type.") )
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.emit(event.type, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const reply = (toEvent, withEvent) =>
|
|
||||||
replyToWebsocket(connections.get(toEvent.client), toEvent, withEvent)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Command handling
|
|
||||||
//
|
|
||||||
|
|
||||||
const handleMessage = message => {
|
|
||||||
console.log(message)
|
|
||||||
|
|
||||||
let dice = parseRoll(message.content)
|
|
||||||
|
|
||||||
const respond = content => reply(message, {
|
|
||||||
type: 'message',
|
|
||||||
content
|
|
||||||
})
|
|
||||||
|
|
||||||
if(dice)
|
|
||||||
return rollDice(dice, respond)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Rolls
|
|
||||||
// Most of this is pulled straight from the original
|
|
||||||
// Discord version atm.
|
|
||||||
|
|
||||||
const parseRoll = expression => {
|
const parseRoll = expression => {
|
||||||
let match = constants.rollRegex.exec(expression.trim())
|
let match = constants.rollRegex.exec(expression.trim())
|
||||||
|
|
||||||
@ -128,9 +62,6 @@ const parseRoll = expression => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseRollInt = (value, defaultValue) =>
|
|
||||||
value ? parseInt(value) : defaultValue
|
|
||||||
|
|
||||||
const pullDescription = (expression, match) => {
|
const pullDescription = (expression, match) => {
|
||||||
if(match[0].length == expression.length)
|
if(match[0].length == expression.length)
|
||||||
return
|
return
|
||||||
@ -138,6 +69,50 @@ const pullDescription = (expression, match) => {
|
|||||||
return parseDescription(expression.slice(match[0].length))
|
return parseDescription(expression.slice(match[0].length))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseDescription = description => {
|
||||||
|
let conditions = []
|
||||||
|
let match
|
||||||
|
|
||||||
|
while((match = constants.descriptionRegex.exec(description)) !== null) {
|
||||||
|
let range
|
||||||
|
let [
|
||||||
|
rangeExp,
|
||||||
|
valueExp,
|
||||||
|
content
|
||||||
|
] = match.slice(2)
|
||||||
|
|
||||||
|
if(rangeExp) {
|
||||||
|
let split = rangeExp.split('-')
|
||||||
|
|
||||||
|
range = {
|
||||||
|
lower: parseRollInt(split[0], -Infinity),
|
||||||
|
upper: parseRollInt(split[1], Infinity)
|
||||||
|
}
|
||||||
|
} else if(valueExp) {
|
||||||
|
range = {
|
||||||
|
upper: valueExp,
|
||||||
|
lower: valueExp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions.push({
|
||||||
|
range,
|
||||||
|
content: content.trim()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMessage = (message, respond) => {
|
||||||
|
let dice = parseRoll(message.content)
|
||||||
|
|
||||||
|
if(dice == undefined)
|
||||||
|
return // No dice
|
||||||
|
|
||||||
|
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... .-.')
|
||||||
@ -222,16 +197,362 @@ const rollDice = (dice, respond) => {
|
|||||||
respond(response)
|
respond(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
const saveReply = (message, reply) => {
|
||||||
// Hooks
|
replies.set(message.id, {
|
||||||
//
|
id: reply.id,
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const bot = new EventEmitter()
|
const messageCycle = async message => {
|
||||||
|
handleMessage(message, async content => {
|
||||||
|
saveReply(message, await message.reply(content) )
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
bot.on('message', handleMessage)
|
const rehandleMessage = async (message, reply) => {
|
||||||
|
handleMessage(message, async content => {
|
||||||
|
saveReply(message, await reply.edit(content) )
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const server = new WebSocketServer({
|
const pruneReplies = () => {
|
||||||
port: 8080
|
for(let [ id, entry ] of replies.entries()) {
|
||||||
|
let age = Date.now() - entry.timestamp
|
||||||
|
|
||||||
|
if(age > 1000 * 60 * 3) {
|
||||||
|
replies.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactionRespond = (interaction, content) => {
|
||||||
|
let reply = { content, ephemeral: true }
|
||||||
|
|
||||||
|
if(interaction.replied || interaction.deferred) {
|
||||||
|
return interaction.followUp(reply)
|
||||||
|
} else {
|
||||||
|
return interaction.reply(reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleError = (interaction) => (error) =>
|
||||||
|
interactionRespond(interaction, constants.errorMessage(error) )
|
||||||
|
.catch(reportingError => console.error('Could not display error message:\n ', reportingError) )
|
||||||
|
|
||||||
|
|
||||||
|
const addCommand = (data, callback) => {
|
||||||
|
commands.set(data.name, {
|
||||||
|
data,
|
||||||
|
execute: callback
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addSubcommands = (data, subcommandCallbacks) =>
|
||||||
|
addCommand(data, interaction => {
|
||||||
|
return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
|
||||||
|
})
|
||||||
|
|
||||||
|
const openMacros = guildId =>
|
||||||
|
db.sublevel(guildId).sublevel('macros')
|
||||||
|
|
||||||
|
const reloadMacros = async guildId => {
|
||||||
|
let commands = []
|
||||||
|
let macros = openMacros(guildId)
|
||||||
|
let cacheEntry = {}
|
||||||
|
|
||||||
|
for await (let [ name, dice ] of macros.iterator() ) {
|
||||||
|
cacheEntry[name] = dice
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
name,
|
||||||
|
description: elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "options",
|
||||||
|
description: "Dice, modifiers, or descriptions to apply over the macro",
|
||||||
|
type: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
macroCache.set(guildId, cacheEntry)
|
||||||
|
|
||||||
|
await rest.put(
|
||||||
|
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
|
||||||
|
{ body: commands }
|
||||||
|
)
|
||||||
|
.catch(err => console.error('Failed to reload macros:', err) )
|
||||||
|
}
|
||||||
|
|
||||||
|
const elipsify = (string, maxLength) =>
|
||||||
|
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
|
||||||
|
|
||||||
|
const pruneDB = async () => {
|
||||||
|
let validIds = []
|
||||||
|
|
||||||
|
for await(let key of db.keys()) {
|
||||||
|
let [ guildId ] = key.split('!').slice(1)
|
||||||
|
|
||||||
|
if(validIds.includes(guildId))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if(client.guilds.cache.has(guildId)) {
|
||||||
|
validIds.push(guildId)
|
||||||
|
} else {
|
||||||
|
await db.del(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validIds
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addCommand(
|
||||||
|
constants.commands.about,
|
||||||
|
async interaction => {
|
||||||
|
let embed = {
|
||||||
|
title: 'dicedicedice',
|
||||||
|
thumbnail: {
|
||||||
|
url: constants.iconUrl
|
||||||
|
},
|
||||||
|
description: constants.aboutMessage(client.guilds.cache.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
embeds: [ embed ],
|
||||||
|
ephemeral: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const openResponses = (interaction, ephemeral) => async content =>
|
||||||
|
interaction.reply({ content, ephemeral })
|
||||||
|
|
||||||
|
addSubcommands({
|
||||||
|
name: 'macro',
|
||||||
|
description: "Manage macros",
|
||||||
|
'dm_permission': false,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'add',
|
||||||
|
description: "Define a dice macro",
|
||||||
|
type: 1, // Sub command
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
description: "Name of the macro",
|
||||||
|
type: 3, // String
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dice",
|
||||||
|
description: "The dice expression to save as a macro",
|
||||||
|
type: 3, // String
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
description: "Remove a macro",
|
||||||
|
type: 1, // Sub command
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
description: "Name of the macro",
|
||||||
|
type: 3, // String
|
||||||
|
required: true,
|
||||||
|
autocomplete: true,
|
||||||
|
getAutocomplete: interaction => {
|
||||||
|
let macros = macroCache.get(interaction.guild.id)
|
||||||
|
|
||||||
|
return macros ? Object.keys(macros) : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
add: async interaction => {
|
||||||
|
let respond = openResponses(interaction, true)
|
||||||
|
let name = interaction.options.get('name').value.toLowerCase()
|
||||||
|
|
||||||
|
if(!constants.macroNameRegex.test(name))
|
||||||
|
return respond("Please provide a macro name that consists of only alphanumeric characters.")
|
||||||
|
|
||||||
|
if(commands.has(name))
|
||||||
|
return respond("Uhh,, I think that macro name is already taken by my own commands, sorry.")
|
||||||
|
|
||||||
|
let macros = macroCache.get(interaction.guild.id)
|
||||||
|
|
||||||
|
if(macros && !macros[name] && Object.keys(macros).length >= 100)
|
||||||
|
return respond("I can't keep track of that many macros,, ;-;")
|
||||||
|
|
||||||
|
let dice = interaction.options.get('dice').value
|
||||||
|
|
||||||
|
if(!constants.rollRegex.test(dice) )
|
||||||
|
return respond("Please provide a valid roll expression.")
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: true })
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
openMacros(interaction.guild.id).put(name, dice),
|
||||||
|
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.`)
|
||||||
|
},
|
||||||
|
remove: async interaction => {
|
||||||
|
let name = interaction.options.get('name').value.toLowerCase()
|
||||||
|
let macros = macroCache.get(interaction.guild.id)
|
||||||
|
let respond = openResponses(interaction, true)
|
||||||
|
|
||||||
|
if(!macros)
|
||||||
|
return respond('There aren\'t even any macros in this guild!')
|
||||||
|
|
||||||
|
let dice = macros && macroCache.get(interaction.guild.id)[name]
|
||||||
|
|
||||||
|
if(!dice)
|
||||||
|
return respond("There isn't a macro with that name .-.")
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: true })
|
||||||
|
await Promise.all([
|
||||||
|
openMacros(interaction.guild.id).del(name),
|
||||||
|
reloadMacros(interaction.guild.id)
|
||||||
|
])
|
||||||
|
|
||||||
|
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
server.on('connection', handleConnection)
|
|
||||||
|
|
||||||
|
const client = new Client({
|
||||||
|
intents: [
|
||||||
|
GatewayIntentBits.Guilds,
|
||||||
|
GatewayIntentBits.GuildMessages,
|
||||||
|
GatewayIntentBits.MessageContent,
|
||||||
|
GatewayIntentBits.DirectMessages
|
||||||
|
],
|
||||||
|
partials: [
|
||||||
|
Partials.Channel
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const safeSubscribe = (event, callback) => {
|
||||||
|
client.on(event, (...args) => {
|
||||||
|
return callback(...args)
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const rest = new REST().setToken(process.env.DISCORD_TOKEN)
|
||||||
|
|
||||||
|
safeSubscribe('ready', async () => {
|
||||||
|
console.log("Logged in!")
|
||||||
|
|
||||||
|
let guildIds = await pruneDB()
|
||||||
|
|
||||||
|
for(let guildId of guildIds)
|
||||||
|
await reloadMacros(guildId)
|
||||||
|
|
||||||
|
console.log("Ready")
|
||||||
|
})
|
||||||
|
|
||||||
|
safeSubscribe('messageCreate', messageCycle)
|
||||||
|
|
||||||
|
safeSubscribe('messageUpdate', async (oldMessage, newMessage) => {
|
||||||
|
if(replies.has(newMessage.id) ) {
|
||||||
|
let { id } = replies.get(newMessage.id)
|
||||||
|
|
||||||
|
newMessage.channel.messages.fetch(id)
|
||||||
|
.then(reply => rehandleMessage(newMessage, reply) )
|
||||||
|
.catch(err => messageCycle(newMessage) )
|
||||||
|
} else {
|
||||||
|
messageCycle(newMessage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCommand = async interaction => {
|
||||||
|
if(commands.has(interaction.commandName) ) {
|
||||||
|
commands.get(interaction.commandName).execute(interaction)
|
||||||
|
.catch(handleError(interaction))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.deferReply()
|
||||||
|
let roll = macroCache.get(interaction.guild.id)[interaction.commandName]
|
||||||
|
|
||||||
|
if(roll) {
|
||||||
|
let dice = parseRoll(roll)
|
||||||
|
let options = interaction.options.get('options')
|
||||||
|
|
||||||
|
if(options) {
|
||||||
|
let optionDice = parseOptionRoll(options.value)
|
||||||
|
|
||||||
|
for(let [ key, value ] of Object.entries(optionDice)) {
|
||||||
|
if(value)
|
||||||
|
dice[key] = Array.isArray(value) ? value.concat(dice[key]) : value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rollDice(dice, content => interaction.followUp(content) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const findOption = (options, name) =>
|
||||||
|
options.find(option => option.name == name)
|
||||||
|
|
||||||
|
const handleAutocomplete = async interaction => {
|
||||||
|
if(commands.has(interaction.commandName) ) {
|
||||||
|
let { data } = commands.get(interaction.commandName)
|
||||||
|
let subcommand = interaction.options.getSubcommand()
|
||||||
|
let focusedOption = interaction.options.getFocused(true)
|
||||||
|
|
||||||
|
if(subcommand !== undefined) {
|
||||||
|
data = findOption(data.options, subcommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = findOption(data.options, focusedOption.name)
|
||||||
|
|
||||||
|
if(!option) {
|
||||||
|
console.error('Could not find option: ' + focusedOption)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let filtered = option
|
||||||
|
.getAutocomplete(interaction)
|
||||||
|
.filter(choice => choice.startsWith(focusedOption.value) )
|
||||||
|
.map(choice => ({ name: choice, value: choice }) )
|
||||||
|
|
||||||
|
await interaction.respond(filtered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safeSubscribe('interactionCreate', interaction => {
|
||||||
|
if(interaction.isChatInputCommand()) {
|
||||||
|
return handleCommand(interaction)
|
||||||
|
} else if(interaction.isAutocomplete()) {
|
||||||
|
return handleAutocomplete(interaction)
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
await rest.put(
|
||||||
|
Routes.applicationCommands(process.env.DISCORD_ID),
|
||||||
|
{
|
||||||
|
body: [ ...commands.values() ]
|
||||||
|
.map(command => command.data )
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(err => console.error('Command registration failed: ', err) )
|
||||||
|
|
||||||
|
await client.login(process.env.DISCORD_TOKEN)
|
||||||
|
.catch(err => console.error('Login failed: ', err) )
|
||||||
|
|
||||||
|
setInterval(pruneReplies, 1000 * 60)
|
||||||
|
})()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user