experimental new command system & fixes
This commit is contained in:
parent
716257c45f
commit
5a69865919
10 changed files with 237 additions and 140 deletions
|
@ -1,16 +0,0 @@
|
|||
const name = 'eval'
|
||||
const description = 'secure!!1'
|
||||
const usages = ['<code...>']
|
||||
const aliases = ['eval']
|
||||
const enabled = true
|
||||
|
||||
const permLevel = 0
|
||||
|
||||
const util = require('util')
|
||||
|
||||
function execute (bot, cmd, player, args, handler) {
|
||||
const result = bot.eval(args.join(' ').replace(/\xa7.?/g, ''))
|
||||
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: util.inspect(result), color: bot.colors.primary }))
|
||||
}
|
||||
|
||||
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }
|
135
commands/help.js
135
commands/help.js
|
@ -1,81 +1,98 @@
|
|||
const name = 'help'
|
||||
const description = 'Lists commands or shows info about a command.'
|
||||
const usages = ['[command]']
|
||||
const aliases = ['help']
|
||||
const enabled = true
|
||||
const { CommandDispatcher, builder: { LiteralArgumentBuilder: { literal }, RequiredArgumentBuilder: { argument } }, arguments: { StringArgumentType: { greedyString } } } = require('brigadier-commands')
|
||||
|
||||
const permLevel = 0
|
||||
module.exports = {
|
||||
register (dispatcher) {
|
||||
const node = dispatcher.register(
|
||||
literal('help')
|
||||
.executes(this.listCommands)
|
||||
.then(
|
||||
argument('command', greedyString())
|
||||
.executes(this.showCommandInfo)
|
||||
)
|
||||
)
|
||||
|
||||
function execute (bot, cmd, entity, args) {
|
||||
if (args.length > 0) {
|
||||
if (!bot.commands.isCommand(args[0])) { return bot.core.run(`/tellraw @a ${JSON.stringify({ text: `Unknown command: ${bot.prefix}${args[0]}`, color: bot.colors.error })}`) }
|
||||
node.description = 'Lists commands or shows info about a command'
|
||||
node.permissionLevel = 0
|
||||
},
|
||||
|
||||
const command = bot.commands.info(args.shift())
|
||||
listCommands (context) {
|
||||
const source = context.source
|
||||
const bot = source.bot
|
||||
|
||||
const nodes = bot.commands.dispatcher.root.getChildren()
|
||||
|
||||
const publicList = []
|
||||
const trustedList = []
|
||||
const adminList = []
|
||||
const unknownList = []
|
||||
nodes.forEach(node => {
|
||||
if (node.redirect) return // ignore aliases
|
||||
|
||||
const msg = {
|
||||
color: 'dark_aqua',
|
||||
text: bot.prefix + node.name + ' ',
|
||||
clickEvent: { action: 'suggest_command', value: bot.prefix + 'help ' + node.name },
|
||||
hoverEvent: { action: 'show_text', value: 'Click to see info about the command' }
|
||||
}
|
||||
if (node.permissionLevel === 0) {
|
||||
msg.color = 'green'
|
||||
publicList.push(msg)
|
||||
} else if (node.permissionLevel === 1) {
|
||||
msg.color = 'red'
|
||||
trustedList.push(msg)
|
||||
} else if (node.permissionLevel === 2) {
|
||||
msg.color = 'dark_red'
|
||||
adminList.push(msg)
|
||||
} else {
|
||||
unknownList.push(msg)
|
||||
}
|
||||
})
|
||||
|
||||
const msg = [{ text: 'Commands - ', color: 'gray' }, ...publicList, ...trustedList, ...adminList, ...unknownList]
|
||||
source.sendFeedback(msg, false)
|
||||
},
|
||||
|
||||
showCommandInfo (context) {
|
||||
const source = context.source
|
||||
const bot = source.bot
|
||||
|
||||
const nodes = bot.commands.dispatcher.root.getChildren()
|
||||
|
||||
const commandName = context.getArgument('command')
|
||||
let node = nodes.find(node => node.name === commandName)
|
||||
if (node.redirect) node = node.redirect
|
||||
|
||||
const aliases = [node, ...nodes.filter(_node => _node.redirect === node)].map(node => node.name)
|
||||
const usages = [...bot.commands.dispatcher.getSmartUsage(node, source, false).values()]
|
||||
|
||||
let msg
|
||||
if (command.usages.length !== 1) {
|
||||
if (usages.length !== 1) {
|
||||
msg = [
|
||||
{ text: bot.prefix + command.name, color: bot.colors.primary },
|
||||
{ text: ' (' + command.aliases.join(', ') + ')', color: 'white' },
|
||||
{ text: ` - ${command.description}\n`, color: 'gray' }
|
||||
{ text: bot.prefix + node.name, color: bot.colors.primary },
|
||||
{ text: ' (' + aliases.join(', ') + ')', color: 'white' },
|
||||
{ text: ` - ${node.description}\n`, color: 'gray' }
|
||||
]
|
||||
command.usages.forEach((usage, i) => {
|
||||
msg.push(bot.prefix + command.name)
|
||||
usages.forEach((usage, i) => {
|
||||
msg.push(bot.prefix + node.name)
|
||||
msg.push({
|
||||
text: ` ${usage}\n`,
|
||||
color: bot.colors.secondary,
|
||||
clickEvent: { action: 'suggest_command', value: command.name + ' ' + usage }
|
||||
// hoverEvent: { action: 'show_text', value: 'Click to teleport' }
|
||||
clickEvent: { action: 'suggest_command', value: node.name + ' ' + usage }
|
||||
})
|
||||
})
|
||||
msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1)
|
||||
} else {
|
||||
msg = [
|
||||
{ text: bot.prefix + command.name, color: bot.colors.primary },
|
||||
{ text: ' (' + command.aliases.join(', ') + ')', color: 'white' },
|
||||
{ text: bot.prefix + node.name, color: bot.colors.primary },
|
||||
{ text: ' (' + aliases.join(', ') + ')', color: 'white' },
|
||||
{
|
||||
text: ` ${command.usages[0]}`,
|
||||
text: ` ${usages[0]}`,
|
||||
color: bot.colors.secondary,
|
||||
clickEvent: { action: 'suggest_command', value: command.name + ' ' + command.usages[0] }
|
||||
clickEvent: { action: 'suggest_command', value: node.name + ' ' + usages[0] }
|
||||
},
|
||||
{ text: ` - ${command.description}`, color: 'gray' }
|
||||
{ text: ` - ${node.description}`, color: 'gray' }
|
||||
]
|
||||
}
|
||||
return bot.core.run(`minecraft:tellraw @a ${JSON.stringify(msg)}`)
|
||||
source.sendFeedback(msg, false)
|
||||
}
|
||||
let commands = []
|
||||
Object.keys(bot.commands.commands).forEach((command) => {
|
||||
if (bot.commands.isCommand(command) && !commands.includes(bot.commands.info(command))) { commands.push(bot.commands.info(command)) }
|
||||
})
|
||||
commands = commands.filter((command) => command.enabled)
|
||||
|
||||
const publicList = []
|
||||
const trustedList = []
|
||||
const adminList = []
|
||||
const unknownList = []
|
||||
commands.forEach((command) => {
|
||||
const msg = {
|
||||
color: 'dark_aqua',
|
||||
text: bot.prefix + command.name + ' ',
|
||||
clickEvent: { action: 'run_command', value: bot.prefix + aliases[0] + ' ' + command.name },
|
||||
hoverEvent: { action: 'show_text', value: 'Click to see info about the command' }
|
||||
}
|
||||
if (command.permLevel === 0) {
|
||||
msg.color = 'green'
|
||||
publicList.push(msg)
|
||||
} else if (command.permLevel === 1) {
|
||||
msg.color = 'red'
|
||||
trustedList.push(msg)
|
||||
} else if (command.permLevel === 2) {
|
||||
msg.color = 'dark_red'
|
||||
adminList.push(msg)
|
||||
} else {
|
||||
unknownList.push(msg)
|
||||
}
|
||||
})
|
||||
|
||||
const msg = [{ text: 'Commands - ', color: 'gray' }, ...publicList, ...trustedList, ...adminList, ...unknownList]
|
||||
bot.core.run(`/tellraw @a ${JSON.stringify(msg)}`)
|
||||
}
|
||||
|
||||
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }
|
||||
|
|
5
index.js
5
index.js
|
@ -27,15 +27,16 @@ filepath += '.log'
|
|||
fs.writeFileSync(filepath, '')
|
||||
|
||||
const servers = [
|
||||
'play.kaboom.pw:25565:kaboom',
|
||||
'chipmunk.land:25565:kaboom'
|
||||
]
|
||||
|
||||
const bots = createBots(servers, {
|
||||
username: 'MusicBot',
|
||||
username: ' ',
|
||||
prefix: "'",
|
||||
colors: { primary: 'green', secondary: 'dark_green', error: 'red' },
|
||||
version: '1.20.4',
|
||||
randomizeUsername: false,
|
||||
randomizeUsername: true,
|
||||
autoReconnect: true
|
||||
// 'online-mode': { enabled: false, username: 'removed lol', password: null }
|
||||
})
|
||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -4,11 +4,11 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "chipmunktest",
|
||||
"dependencies": {
|
||||
"@mozilla/readability": "^0.4.1",
|
||||
"@skeldjs/client": "^2.15.17",
|
||||
"@tonejs/midi": "^2.0.27",
|
||||
"brigadier-commands": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git",
|
||||
"colorsys": "^1.0.22",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"kahoot.js-api": "^2.4.0",
|
||||
|
@ -517,6 +517,10 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/brigadier-commands": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae"
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
|
@ -3941,6 +3945,10 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"brigadier-commands": {
|
||||
"version": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae",
|
||||
"from": "brigadier-commands@git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git"
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"prismarine-nbt": "^2.2.0",
|
||||
"rfb2": "^0.2.2",
|
||||
"standard": "^16.0.4",
|
||||
"urban-dictionary": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git"
|
||||
"urban-dictionary": "git+https://code.chipmunk.land/ChipmunkMC/urban-dictionary.git",
|
||||
"brigadier-commands": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
const { CommandDispatcher, builder: { LiteralArgumentBuilder: { literal }, RequiredArgumentBuilder: { argument } }, arguments: { StringArgumentType: { greedyString } }, exceptions: { CommandSyntaxException } } = require('brigadier-commands')
|
||||
const CommandSource = require('../util/command_source')
|
||||
const TextMessage = require('../util/text_message')
|
||||
|
||||
function inject (bot) {
|
||||
bot.commands = {
|
||||
commands: {},
|
||||
dispatcher: new CommandDispatcher(),
|
||||
add,
|
||||
execute,
|
||||
info,
|
||||
|
@ -14,57 +17,109 @@ function inject (bot) {
|
|||
}
|
||||
|
||||
bot.on('message', (player, message) => {
|
||||
if (!message.startsWith(bot.prefix)) { return }
|
||||
if (!message.startsWith(bot.prefix)) return
|
||||
|
||||
const args = message.slice(bot.prefix.length).split(' ')
|
||||
const command = args.shift().toLowerCase()
|
||||
|
||||
if (!isCommand(command)) { return bot.core.run(`/tellraw @a ${JSON.stringify({ text: `Unknown command: ${bot.prefix}${command}`, color: bot.colors.error })}`) }
|
||||
|
||||
bot.commands.execute(bot, command, player, args)
|
||||
const sendFeedback = message => bot.core.run('minecraft:tellraw @a ' + JSON.stringify(message))
|
||||
bot.commands.execute(message.substring(bot.prefix.length), new CommandSource({ bot, sendFeedback }))
|
||||
})
|
||||
|
||||
function add (command) {
|
||||
if (!isValid(command)) throw new Error('Invalid command', 'invalid_command')
|
||||
command.aliases.forEach(alias => (bot.commands.commands[alias.toLowerCase()] = command))
|
||||
if (command.register) {
|
||||
command.register(bot.commands.dispatcher)
|
||||
return
|
||||
}
|
||||
|
||||
if (isValid(command)) {
|
||||
bot.console.warn(`Command '${command.aliases[0]}' is using the legacy command system!`)
|
||||
|
||||
const _execute = args => command.execute(bot, command.aliases[0], {}, args)
|
||||
|
||||
const node = bot.commands.dispatcher.register(
|
||||
literal(command.aliases[0])
|
||||
.executes(context => { _execute([]); return 0 })
|
||||
.then(
|
||||
argument('args', greedyString())
|
||||
.executes(context => { _execute(context.getArgument('args').split(' ')); return 0 })
|
||||
)
|
||||
)
|
||||
for (let i = 1; i < command.aliases.length; i++) {
|
||||
bot.commands.dispatcher.register(
|
||||
literal(command.aliases[i])
|
||||
.executes(context => { _execute([]); return 0 })
|
||||
.redirect(node)
|
||||
)
|
||||
}
|
||||
|
||||
// add metadata for help command
|
||||
node.description = command.description
|
||||
node.permissionLevel = command.permLevel
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
throw new Error('Invalid command', 'invalid_command')
|
||||
}
|
||||
|
||||
function loadFromDir (dirpath) {
|
||||
fs.readdirSync(dirpath).forEach(filename => {
|
||||
const filepath = path.resolve(dirpath, filename)
|
||||
if (!filepath.endsWith('js') || !fs.statSync(filepath).isFile()) return
|
||||
try {
|
||||
bot.commands.add(require(filepath))
|
||||
} catch (err) {
|
||||
bot.console.error('Error loading command ' + filepath + ': ' + util.inspect(err))
|
||||
} catch (error) {
|
||||
bot.console.error('Error loading command ' + filepath + ': ' + util.inspect(error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function info (command) {
|
||||
const info = bot.commands.commands[command] ?? command
|
||||
if (isValid(info)) { return info }
|
||||
}
|
||||
function isCommand (command) { return bot.commands.info(command) != null }
|
||||
async function execute (bot, command, player, args, ...custom) {
|
||||
const info = bot.commands.info(command)
|
||||
if (info == null) {
|
||||
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: 'Unknown command: ' + bot.prefix + command, color: bot.colors.error }))
|
||||
return
|
||||
}
|
||||
if (!info.enabled) {
|
||||
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: bot.prefix + command + 'is disabled', color: bot.colors.error }))
|
||||
return
|
||||
}
|
||||
if (info.permLevel > 0) {
|
||||
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: 'Trusted commands are currently disabled', color: bot.colors.error }))
|
||||
return
|
||||
}
|
||||
|
||||
function isCommand (command) { return true }
|
||||
|
||||
function execute (command, source) {
|
||||
try {
|
||||
return await info.execute(bot, command, player, args, ...custom)
|
||||
} catch (err) {
|
||||
bot.console.error('Error executing command ' + command + ': ' + util.inspect(err))
|
||||
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: err.message, color: bot.colors.error }))
|
||||
bot.commands.dispatcher.execute(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
|
||||
}
|
||||
|
||||
source.sendError({ translate: 'command.failed', hoverEvent: { action: 'show_text', contents: error.stack } })
|
||||
}
|
||||
}
|
||||
|
||||
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 isValid (command) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const fs = require('fs')
|
||||
const util = require('util')
|
||||
const moment = require('moment')
|
||||
const CommandSource = require('../util/command_source')
|
||||
const parseText = require('../util/text_parser')
|
||||
const ansimap = {
|
||||
0: '\x1b[0m\x1b[30m',
|
||||
1: '\x1b[0m\x1b[34m',
|
||||
|
@ -72,24 +74,18 @@ function inject (bot) {
|
|||
function handleLine (line) {
|
||||
if (bot.host !== bot.console.host && bot.console.host !== 'all') return
|
||||
if (line.startsWith('.')) {
|
||||
const args = line.slice(1).trim().split(' ')
|
||||
const command = args.shift()
|
||||
|
||||
if (!bot.commands.isCommand(command)) {
|
||||
bot.console.error('Unknown command: ' + command)
|
||||
return
|
||||
}
|
||||
const info = bot.commands.info(command)
|
||||
try {
|
||||
info.execute(bot, command, bot.player, args)
|
||||
} catch (err) {
|
||||
bot.console.error(`Error executing ${command} in console: ${util.inspect(err)}`)
|
||||
}
|
||||
const source = new CommandSource({ bot, sendFeedback })
|
||||
bot.commands.execute(line.substring(1), source)
|
||||
} else {
|
||||
bot.fancyMsg('test', '_ChipMC_', line)
|
||||
rl?.prompt(true)
|
||||
}
|
||||
}
|
||||
|
||||
function sendFeedback (message) {console.log(message)
|
||||
const { raw } = parseText(message);console.log(raw)
|
||||
bot.console.log(raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
13
util/command_source.js
Normal file
13
util/command_source.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
class CommandSource {
|
||||
constructor ({ bot, permissionLevel = 0, sendFeedback = () => {} } = {}) {
|
||||
this.bot = bot
|
||||
this.permissionLevel = permissionLevel
|
||||
this.sendFeedback = sendFeedback
|
||||
}
|
||||
|
||||
sendError (error) {
|
||||
this.sendFeedback([{ text: '', color: 'red' }, error], false)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CommandSource
|
18
util/text_message.js
Normal file
18
util/text_message.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
const parseText = require('./text_parser.js')
|
||||
|
||||
class TextMessage {
|
||||
constructor (text) {
|
||||
this.text = text
|
||||
}
|
||||
|
||||
getString () {
|
||||
const { clean } = parseText(this.text)
|
||||
return clean
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.getString()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TextMessage
|
|
@ -1,4 +1,4 @@
|
|||
const { language } = require('minecraft-data')('1.17.1')
|
||||
const { language } = require('minecraft-data')('1.20.4')
|
||||
|
||||
const colormap = {
|
||||
black: '§0',
|
||||
|
@ -69,6 +69,10 @@ function parseText (json) {
|
|||
function parseJson (json, parent) {
|
||||
if (typeof json === 'string') {
|
||||
json = { text: json }
|
||||
} else if (Array.isArray(json)) {
|
||||
const root = json.shift()
|
||||
root.extra = json
|
||||
json = root
|
||||
}
|
||||
|
||||
json.color ??= parent.color
|
||||
|
@ -94,24 +98,24 @@ function parseJson (json, parent) {
|
|||
raw += json['']
|
||||
}
|
||||
if (json.translate) { // I checked with the native minecraft code. This is how Minecraft does the matching and group indexing. -hhhzzzsss
|
||||
if (language[json.translate]) {
|
||||
const _with = json.with ?? []
|
||||
let i = 0
|
||||
raw += language[json.translate].replace(/%(?:(\\d+)\\$)?(s|%)/g, (g0, g1) => {
|
||||
if (g0 === '%%') {
|
||||
return '%'
|
||||
let format = language[json.translate]
|
||||
if (typeof format !== 'string') format = json.fallback
|
||||
if (typeof format !== 'string') format = json.translate
|
||||
|
||||
const _with = json.with ?? []
|
||||
let i = 0
|
||||
raw += format.replace(/%(?:(\\d+)\\$)?(s|%)/g, (g0, g1) => {
|
||||
if (g0 === '%%') {
|
||||
return '%'
|
||||
} else {
|
||||
const idx = g1 ? parseInt(g1) : i++
|
||||
if (_with[idx]) {
|
||||
return parseJson(_with[idx], json)
|
||||
} else {
|
||||
const idx = g1 ? parseInt(g1) : i++
|
||||
if (_with[idx]) {
|
||||
return parseJson(_with[idx], json)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
return ''
|
||||
}
|
||||
})
|
||||
} else {
|
||||
raw += json.translate
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (json.extra) {
|
||||
json.extra.forEach((extra) => {
|
||||
|
|
Loading…
Reference in a new issue