chipmunkbot3/plugins/commands.js
2024-06-24 13:10:17 -04:00

132 lines
3.9 KiB
JavaScript

const fs = require('fs')
const path = require('path')
const { CommandDispatcher, literal, argument, greedyString, CommandSyntaxException } = require('brigadier-commands')
const CommandSource = require('../util/command/command_source')
const TextMessage = require('../util/command/text_message')
const colorCodeStringify = require('../util/chat/stringify/color_code')
let commands
function loadCommands () {
commands = []
for (const filename of fs.readdirSync(path.resolve(__dirname, '..', 'commands'))) {
const filepath = path.resolve(__dirname, '..', 'commands', filename)
if (!filepath.endsWith('.js') || !fs.statSync(filepath).isFile()) return
try {
delete require.cache[require.resolve(filepath)]
commands.push(require(filepath))
} catch (error) {
console.error('Error loading command', filepath, ':', error)
}
}
}
loadCommands()
const patchedExecuteSource = CommandDispatcher.prototype.execute
.toString()
.replace('context.command(', 'await context.command(')
const patchedExecute = eval(`const { ParseResults, StringReader } = require('brigadier-commands'); ({ async ${patchedExecuteSource} })`).execute
function inject (bot, options) {
bot.commands = {
dispatcher: null,
execute,
reload,
prefixes: options.prefixes ?? ['default.']
}
let cleanups = []
bot.on('message', ({ sender, plain }) => {
const prefix = bot.commands.prefixes.find(prefix => plain.startsWith(prefix))
if (!prefix) return
function sendFeedback (message) {
bot.tellraw(message, '@a')
}
const displayName = {
insertion: sender.username,
clickEvent: { action: 'suggest_command', value: `/tell ${sender.username} ` },
hoverEvent: {
action: 'show_entity',
contents: {
type: 'minecraft:player',
id: sender.uuid,
name: sender.username
}
},
text: sender.username
}
bot.commands.execute(plain.substring(prefix.length), new CommandSource({ bot, player: sender, sendFeedback, displayName }))
})
async function execute (command, source) {
try {
return await patchedExecute.call(bot.commands.dispatcher, command, source)
} catch (error) {
if (error instanceof CommandSyntaxException) {
const text = (error._message instanceof TextMessage) ? error._message.text : error._message.getString()
source.sendError(text)
const context = getContext(error)
if (context) source.sendError(context)
return 1
}
source.sendError({ translate: 'command.failed', hoverEvent: { action: 'show_text', contents: error.stack } })
return 1
}
}
function getContext (error) {
const _cursor = error.cursor
const input = error.input
if (input == null || _cursor < 0) {
return null
}
const text = [{ text: '', color: 'gray', clickEvent: { action: 'suggest_command', value: bot.prefix + input } }]
const cursor = Math.min(input.length, _cursor)
if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) {
text.push('...')
}
text.push(
input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor),
{ text: input.substring(cursor), color: 'red', underline: true },
{ translate: 'command.context.here', color: 'red', italic: true }
)
return text
}
function reload (loadFiles = true) {
for (const cleanup of cleanups) cleanup()
if (loadFiles) loadCommands()
bot.commands.dispatcher = new CommandDispatcher()
cleanups = []
for (let command of commands) {
try {
if (command.create) command = command.create()
if (command.cleanup) cleanups.push(() => command.cleanup())
command.register(bot.commands.dispatcher, { bot })
} catch (error) {
console.error('Unable to register command:', error)
}
}
bot.emit('commands_loaded')
}
reload(false)
}
module.exports = inject