command refactor & improvements

This commit is contained in:
Chipmunk 2024-04-02 17:53:10 -04:00
parent 5f3560910b
commit d3e576f7b9
69 changed files with 1267 additions and 1065 deletions

8
bot.js
View file

@ -16,9 +16,9 @@ function createBot (options = {}) {
options.prefix ??= '!' options.prefix ??= '!'
options.brand ??= 'vanilla' // found that mineflayer has this so i added it here lol options.brand ??= 'vanilla' // found that mineflayer has this so i added it here lol
options.colors ??= {} options.styles ??= {}
options.colors.primary ??= 'white' options.styles.primary ??= { color: 'white' }
options.colors.secondary ??= 'green' options.styles.secondary ??= { color: 'green' }
options.autoReconnect ??= false options.autoReconnect ??= false
options.randomizeUsername ??= false options.randomizeUsername ??= false
@ -48,7 +48,7 @@ function createBot (options = {}) {
bot.prefix = options.prefix bot.prefix = options.prefix
bot.brand = options.brand bot.brand = options.brand
bot.colors = options.colors bot.styles = options.styles
bot.autoReconnect = options.autoReconnect bot.autoReconnect = options.autoReconnect
bot.randomizeUsername = options.randomizeUsername bot.randomizeUsername = options.randomizeUsername
bot['online-mode'] = options['online-mode'] bot['online-mode'] = options['online-mode']

View file

@ -1,23 +0,0 @@
const name = 'badapple'
const description = 'Plays a badapple video.'
const usages = ['[stop]']
const aliases = ['badapple']
const enabled = true
const permLevel = 0
function execute (bot, cmd, player, args) {
if (args[0] === 'stop') {
bot.video.stop()
bot.music.skip()
return
}
bot.video.summon(player.UUID, (uuids) => {
bot.music.stop()
bot.music.queue.push('./music/badapple.mid')
bot.video.play('./videos/badapple.txt', uuids)
})
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,47 +0,0 @@
const name = 'blacklist'
const description = 'idk'
const usages = [
'add <pattern> <flags>',
'remove <pattern> <flags>',
'list'
]
const aliases = ['blacklist']
const enabled = true
const permLevel = 1
function execute (bot, cmd, player, args, handler) {
const subCmd = args.shift()
let i, msg
switch (subCmd) {
case 'add':
bot.blacklist.push([args[0], args[1]])
bot.core.run(`minecraft:tellraw @a ${JSON.stringify([
{ text: 'Added regex ', color: bot.colors.primary },
{ text: `/${args[0]}/${args[1]}`, color: bot.colors.secondary },
' to the blacklist.'
])}`)
break
case 'remove':
i = bot.blacklist.indexOf([args[0], args[1]])
if (i < 0) throw new Error(`There is no regex /${args[0]}/${args[1]} in the blacklist.`)
bot.blacklist.splice(i, 1)
bot.core.run(`minecraft:tellraw @a ${JSON.stringify([
{ text: 'Removed regex ', color: bot.colors.primary },
{ text: `/${args[0]}/${args[1]}`, color: bot.colors.secondary },
' from the blacklist.'
])}`)
break
case 'list':
msg = [{ text: 'Regexes:\n', color: bot.colors.primary }]
bot.blacklist.forEach(([pattern, flags]) => {
msg.push({ text: `/${pattern}/${flags}\n`, color: bot.colors.secondary })
})
msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1)
bot.core.run(`minecraft:tellraw @a ${JSON.stringify(msg)}`)
}
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,42 +1,51 @@
const name = 'bruhify' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'recyclebot'
const usages = ['<text>']
const aliases = ['bruhify']
const enabled = true
const permLevel = 0
let isBruhifying = false
const colorsys = require('colorsys') const colorsys = require('colorsys')
function execute (bot, cmd, player, args, handler) { module.exports = {
if (isBruhifying) throw new Error('The bot is already bruhifying text!') isBruhifying: false,
isBruhifying = true
const message = args.join(' ') register (dispatcher) {
const node = dispatcher.register(
literal('bruhify')
.then(
argument('message', greedyString())
.executes(this.bruhifyCommand.bind(this))
)
)
const lines = [] node.description = 'recyclebot'
let j = 0 node.permissionLevel = 0
for (let i = 0; i < message.length; i++) { },
const result = []
let hue = j
message.split('').forEach((char) => {
result.push({ text: char, color: colorsys.hsv2Hex(hue, 100, 100) })
hue += 355 / Math.max(message.length, 20)
})
lines.push(JSON.stringify([{ text: '▚ ', color: 'light_purple' }].concat(result, [' ▚']))) bruhifyCommand (context) {
j += 355 / Math.max(message.length, 20) if (this.isBruhifying) throw new Error('The bot is already bruhifying text!')
} this.isBruhifying = true
let k = 0 const source = context.source
const interval = setInterval(() => { const bot = source.bot
bot.core.run(`minecraft:tellraw @a ${lines[k]}`) const message = context.getArgument('message')
if (++k > lines.length) {
clearInterval(interval) const lines = []
isBruhifying = false let j = 0
for (let i = 0; i < message.length; i++) {
const result = []
let hue = j
message.split('').forEach((char) => {
result.push({ text: char, color: colorsys.hsv2Hex(hue, 100, 100) })
hue += 355 / Math.max(message.length, 20)
})
lines.push([{ text: '▚ ', color: 'light_purple' }].concat(result, [' ▚']))
j += 355 / Math.max(message.length, 20)
} }
}, 50)
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel } let k = 0
const interval = setInterval(() => {
source.sendFeedback(lines[k], false)
if (++k >= lines.length) {
clearInterval(interval)
this.isBruhifying = false
}
}, 50)
}
}

View file

@ -1,13 +1,21 @@
const name = 'cb' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Runs a command in the command core'
const usages = ['<command...>']
const aliases = ['cb']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('cb')
.then(
argument('command', greedyString())
.executes(this.runCommand)
)
)
function execute (bot, cmd, player, args) { node.description = 'Runs a command in the command core'
bot.core.run(args.join(' ')) node.permissionLevel = 0
},
runCommand (context) {
const bot = context.source.bot
bot.core.run(context.getArgument('command'))
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,19 +1,33 @@
const name = 'clearchat' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Clears the chat'
const usages = ['[selector]']
const aliases = ['clearchat', 'cc']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('clearchat')
.executes(this.clearChatCommand.bind(this))
.then(
argument('targets', greedyString())
.executes(this.targettedclearChatCommand.bind(this))
)
)
function execute (bot, cmd, player, args, handler) { dispatcher.register(literal('cc').executes(this.clearChatCommand.bind(this)).redirect(node))
const text = []
while (text.length < 100) { node.description = 'Clears the chat of everyone or a specific player'
text.push('\n') node.permissionLevel = 0
},
clearChatCommand (context) {
const bot = context.source.bot
this.sendclearChatCommandMessage(bot, '@a')
},
targettedclearChatCommand (context) {
const bot = context.source.bot
this.sendclearChatCommandMessage(bot, context.getArgument('targets'))
},
sendclearChatCommandMessage (bot, targets) {
bot.tellraw({ text: '\n'.repeat(100) + 'The chat has been cleared', color: 'dark_green' }, targets)
} }
text.push({ text: 'The chat has been cleared', color: 'dark_green' })
bot.core.run(`/minecraft:tellraw ${args.join(' ') || '@a'} ${JSON.stringify(text)}`)
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,66 +1,110 @@
const name = 'client' const { literal, argument, string, DynamicCommandExceptionType } = require('brigadier-commands')
const description = 'Creates and manages clients using minecraft-protocol.' const { json } = require('../util/command/argument/json')
const usages = ['create <options (json)>', 'end <i>', 'write <i> <name> <data (json)>', 'list'] const TextMessage = require('../util/command/text_message')
const aliases = ['client']
const enabled = true
const permLevel = 0
const mc = require('minecraft-protocol') const mc = require('minecraft-protocol')
const clients = []
const sectionRegex = /\u00a7.?/g
const util = require('util')
function execute (bot, cmd, player, args, handler) { const UNKNOWN_CLIENT_ERROR = new DynamicCommandExceptionType(string => new TextMessage(['Unknown client: ', string]))
const subCmd = args.shift()
switch (subCmd) { module.exports = {
case 'create': { clients: [],
const options = { host: bot.host, port: bot.port, ...JSON.parse(args.join(' ').replace(sectionRegex, '')) }
const client = mc.createClient(options) register (dispatcher) {
const idx = clients.length const node = dispatcher.register(
literal('client')
.then(
literal('create')
.then(
argument('options', json())
.executes(this.createCommand.bind(this))
)
)
.then(
literal('write')
.then(
argument('client', string())
.then(
argument('name', string())
.then(
argument('data', json())
.executes(this.writeCommand.bind(this))
)
)
)
)
.then(
literal('end')
.then(
argument('client', string())
.executes(this.endCommand.bind(this))
)
)
.then(
literal('list')
.executes(this.listCommand.bind(this))
)
)
client.once('login', () => bot.core.run('minecraft:tellraw @a ' + JSON.stringify([{ text: client.username, color: bot.colors.primary }, ' logged in']))) node.description = 'Creates and manages this.clients using minecraft-protocol'
node.permissionLevel = 0
},
client.on('end', () => { createCommand (context) {
clients.splice(idx, 1) const source = context.source
bot.core.run('minecraft:tellraw @a ' + JSON.stringify([{ text: client.username, color: bot.colors.primary }, ' ended'])) const bot = source.bot
})
client.on('error', (err) => bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: util.inspect(err).replace(/\n.*/g, ''), color: bot.colors.error }))) const options = { host: bot.host, port: bot.port, ...context.getArgument('options') }
clients.push(client) const client = mc.createClient(options)
} const idx = this.clients.length
break
case 'end': {
const stridx = args.join(' ').replace(sectionRegex, '')
let idx = Number(stridx)
if (!Number.isInteger(idx)) clients.forEach((client, cidx) => { if (client.username === stridx) idx = cidx })
if (!Number.isInteger(idx)) throw new Error('Unknown client: ' + stridx)
clients[idx].end() client.once('login', () => source.sendFeedback([{ text: client.username, ...bot.styles.primary }, ' logged in']), false)
clients.splice(idx, 1)
}
break
case 'write': {
const stridx = args.shift().replace(sectionRegex, '')
let idx = Number(stridx)
if (!Number.isInteger(idx)) clients.forEach((client, cidx) => { if (client.username === stridx) idx = cidx })
if (!Number.isInteger(idx)) throw new Error('Unknown client: ' + stridx)
const name = args.shift() client.on('end', () => {
const data = JSON.parse(args.join(' ').replace(sectionRegex, '')) this.clients.splice(idx, 1)
source.sendFeedback([{ text: client.username, ...bot.styles.primary }, ' ended'], false)
})
clients[idx].write(name, data) client.on('error', (err) => bot.tellraw({ text: err.toString(), ...bot.styles.error }, '@a'))
}
break this.clients.push(client)
case 'list': },
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: 'Clients: ' + clients.map(client => client.username).join('\u00a7r, '), color: bot.colors.primary }))
break writeCommand (context) {
default: const idx = this.findClientIdxByString(context.getArgument('client'))
throw new Error('Invalid or missing argument')
const name = context.getArgument('name')
const data = context.getArgument('data')
this.clients[idx].write(name, data)
},
endCommand (context) {
const source = context.source
const idx = this.findClientIdxByString(context.getArgument('client'))
this.clients[idx].end()
this.clients.splice(idx, 1)
},
listCommand (context) {
const source = context.source
const bot = source.bot
const list = []
for (let i = 0; i < this.clients.length; i++) {
const client = this.clients[i]
if (i !== 0) list.push(', ')
list.push(client.username)
}
source.sendFeedback([{ text: 'Clients: ', ...bot.styles.primary }, list], false)
},
findClientIdxByString (string) {
let idx = Number(string)
if (!this.clients[idx]) this.clients.forEach((client, cidx) => { if (client.username === string) idx = cidx })
if (!this.clients[idx]) throw UNKNOWN_CLIENT_ERROR.create(string)
return idx
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,49 +1,92 @@
const name = 'cloop' const { literal, argument, integer, greedyString } = require('brigadier-commands')
const description = 'Loops commands'
const usages = [
'add <command...>',
'remove <index>',
'list'
]
const aliases = ['cloop']
const enabled = true
const permLevel = 1 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('cloop')
.requires(source => source.permissionLevel >= node.permissionLevel)
.then(
literal('add')
.then(
argument('interval', integer())
.then(
argument('command', greedyString())
.executes(this.addCommand)
)
)
)
.then(
literal('remove')
.then(
argument('index', integer())
.executes(this.removeCommand)
)
)
.then(
literal('list')
.executes(this.listCommand)
)
.then(
literal('clear')
.executes(this.clearCommand)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Loops commands in the command core'
const subCommand = args.shift() node.permissionLevel = 1
},
let interval, command, i, msg addCommand (context) {
switch (subCommand) { const source = context.source
case 'add': const bot = source.bot
interval = parseFloat(args.shift())
command = args.join(' ').replace(/u00a7.?/g, '')
bot.cloops.push({ command, interval })
bot.core.run(`/tellraw @a ${JSON.stringify([ const interval = context.getArgument('interval')
{ text: 'Added command ', color: bot.colors.primary }, const command = context.getArgument('command')
{ text: command, color: bot.colors.secondary }, bot.cloops.push({ command, interval })
' to cloops.'
])}`)
break
case 'remove':
i = parseFloat(args.shift())
bot.cloops.splice(i, 1)
bot.core.run(`/tellraw @a ${JSON.stringify([ source.sendFeedback([
{ text: 'Removed cloop ', color: bot.colors.primary }, { text: 'Looping command ', ...bot.styles.primary },
{ text: i, color: bot.colors.secondary }, { text: command, ...bot.styles.secondary },
'.' ' at interval ',
])}`) { text: String(interval), ...bot.styles.secondary }
break ], false)
case 'list': },
msg = [{ text: 'Cloops: \n', color: bot.colors.primary }]
for (const i in bot.cloops) { removeCommand (context) {
msg.push({ text: `${i}: ` }) const source = context.source
msg.push({ text: `${bot.cloops[i].command}\n`, color: bot.colors.secondary }) const bot = source.bot
}
bot.core.run(`/tellraw @a ${JSON.stringify(msg)}`) const idx = context.getArgument('index')
bot.cloops.splice(idx, 1)
source.sendFeedback([
{ text: 'Removed cloop ', ...bot.styles.primary },
{ text: String(idx), ...bot.styles.secondary }
], false)
},
listCommand (context) {
const source = context.source
const bot = source.bot
const msg = [{ text: 'Cloops: \n', ...bot.styles.primary }]
for (let i = 0; i < bot.cloops.length; i++) {
if (i !== 0) msg.push('\n')
msg.push(
String(i),
': ',
{ text: String(bot.cloops[i].command), ...bot.styles.secondary }
)
}
source.sendFeedback(msg, false)
},
clearCommand (context) {
const source = context.source
const bot = source.bot
bot.cloops.splice(0, bot.cloops.length)
source.sendFeedback({ text: 'Cleared cloops', ...bot.styles.primary }, false)
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,16 +0,0 @@
const name = 'consoleserver'
const description = 'sets the console server'
const usages = ['<host...>']
const aliases = ['consoleserver', 'consolesvr', 'csvr']
const enabled = false
const permLevel = 0
function execute (bot, cmd, player, args) {
const host = args.join(' ')
bot.getBots().forEach(bot => {
bot.console.host = host
})
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +0,0 @@
const name = 'crashmf'
const description = 'Crashes mineflayer bots.'
const usage = '{prefix}crashmf'
const aliases = ['crashmf']
const enabled = true
const permLevel = 0
function execute (bot, cmd, entity, args, handler) {
bot.core.run(`/tellraw @a ${JSON.stringify({ translate: 'translation.test.invalid', with: ['amogeese'] })}`)
}
module.exports = { name, description, usage, aliases, enabled, execute, permLevel }

View file

@ -1,37 +1,43 @@
const name = 'credits' const { literal } = require('brigadier-commands')
const description = 'Shows bot credits.' const pkg = require('../package.json')
const usages = []
const aliases = ['credits']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('credits')
.executes(this.creditsCommand)
)
const pack = require('./../package.json') node.description = 'Lists people who have contributed to the bot'
node.permissionLevel = 0
},
function execute (bot, cmd, entity, args, handler) { creditsCommand (context) {
bot.core.run(`/tellraw @a ${JSON.stringify([ const source = context.source
{ text: '', color: 'gray' }, const bot = source.bot
{ text: 'Credits\n', color: bot.colors.primary, bold: true },
{ text: '_ChipMC_', color: 'blue' }, source.sendFeedback([
' - creating the bot\n', { text: '', color: 'gray' },
{ text: 'Credits\n', ...bot.styles.primary, bold: true },
{ text: 'hhhzzzsss', color: 'aqua', bold: true }, { text: '_ChipMC_', color: 'blue' },
' and ', ' - creating the bot\n',
{ text: 'eva', color: 'light_purple', italic: true },
' - creating the midi converter.\n',
{ text: 'ma', color: 'aqua' }, { text: 'hhhzzzsss', color: 'aqua', bold: true },
{ text: 'ni', color: 'light_purple' }, ' and ',
{ text: 'a', color: 'white' }, { text: 'eva', color: 'light_purple', italic: true },
{ text: 'pl', color: 'light_purple' }, ' - creating the midi converter.\n',
{ text: 'ay', color: 'aqua' },
' and ',
{ text: 'ayunami2000', color: 'red' },
' - creating the image converter',
`\n\nDependencies: ${Object.entries(pack.dependencies).join(' ')}` { text: 'ma', color: 'aqua' },
])}`) { text: 'ni', color: 'light_purple' },
{ text: 'a', color: 'white' },
{ text: 'pl', color: 'light_purple' },
{ text: 'ay', color: 'aqua' },
' and ',
{ text: 'ayunami2000', color: 'red' },
' - creating the image converter',
'\n\nDependencies: ' + Object.entries(pkg.dependencies).map(entry => entry.join(' ')).join(', ')
], false)
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

25
commands/csvr.js Normal file
View file

@ -0,0 +1,25 @@
const { literal, argument, greedyString } = require('brigadier-commands')
module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('csvr')
.then(
argument('host', greedyString())
.executes(this.consoleServerCommand)
)
)
node.description = 'Sets the console server'
node.permissionLevel = 0
},
consoleServerCommand (context) {
const bot = context.source.bot
const host = context.getArgument('host')
for (const other of bot.bots) {
other.console.host = host
}
}
}

View file

@ -1,17 +0,0 @@
const name = 'destroy'
const description = 'destroy'
const usages = ['destroy']
const aliases = ['destroy']
const enabled = true
const permLevel = 1
function execute (bot, cmd, entity, args, handler) {
let i = 0
setInterval(() => {
bot.core.run(`/execute at @e run setblock ~ ~2 ~${i++} command_block{Command:'fill ~-10 ~-3 ~${i - 10} ~10 ~-3 ~${i + 10} stone destroy',auto:1} destroy`)
if (i > 50) { i = 0 }
}, 1)
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +1,21 @@
const name = 'echo' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Echoes text'
const usages = ['<message...>']
const aliases = ['echo']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('echo')
.then(
argument('message', greedyString())
.executes(this.echoCommand)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Sends a message in chat as the bot'
bot.core.run(`essentials:sudo ${bot.uuid} c:${args.join(' ')}`) node.permissionLevel = 0
},
echoCommand (context) {
const bot = context.source.bot
bot.core.run(`essentials:sudo ${bot.uuid} c:${context.getArgument('message')}`)
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +1,18 @@
const name = 'end' const { literal } = require('brigadier-commands')
const description = 'Ends the bot\'s client.'
const usages = []
const aliases = ['end']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('end')
.executes(this.endCommand)
)
function execute (bot, cmd, player, args, handler) { node.description = "Ends the bot's connection to the server"
bot.end() node.permissionLevel = 0
},
endCommand (context) {
const bot = context.source.bot
bot.end()
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +0,0 @@
const name = 'esc'
const description = 'among.us website'
const usages = []
const aliases = ['esc']
const enabled = true
const permLevel = 0
function execute (bot, cmd, player, args, handler) {
bot.core.run('/tellraw @a ' + JSON.stringify({ text: 'Click here to get kicked!', underlined: true, clickEvent: { action: 'run_command', value: '/\x1bi' } }))
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,39 +0,0 @@
const name = 'function'
const description = 'Runs mcfunctions.'
const usages = ['run <filepath...>', 'list']
const aliases = ['function', 'func']
const enabled = true
const permLevel = 0
const fs = require('fs')
function execute (bot, cmd, player, args, handler) {
const subCmd = args.shift()
let filepath, files, msg
switch (subCmd) {
case 'run':
filepath = `./functions/${args.join(' ').replace(/§./g, '')}`
if (!filepath.endsWith('.mcfunction')) filepath += '.mcfunction'
if (/\.\.\//.test(filepath) || !fs.existsSync(filepath)) throw new Error('Invalid function name')
bot.runFunction(filepath)
break
case 'list':
files = fs.readdirSync('./functions')
// files.filter((file) => file.endsWith('.mid'))
msg = [{ text: 'Functions:\n', color: bot.colors.primary }]
files.forEach((file) => {
msg.push(file)
msg.push({ text: ', ', color: bot.colors.secondary })
})
msg.splice(-1, 1) // msg[msg.length - 1].text = '.'
bot.core.run(`/tellraw @a ${JSON.stringify(msg)}`)
break
default:
throw new Error('Missing or invalid argument')
}
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,4 +1,7 @@
const { CommandDispatcher, builder: { LiteralArgumentBuilder: { literal }, RequiredArgumentBuilder: { argument } }, arguments: { StringArgumentType: { greedyString } } } = require('brigadier-commands') const { literal, argument, greedyString, DynamicCommandExceptionType } = require('brigadier-commands')
const TextMessage = require('../util/command/text_message')
const UNKNOWN_COMMAND_ERROR = new DynamicCommandExceptionType(command => new TextMessage(['Unknown command: ', command]))
module.exports = { module.exports = {
register (dispatcher) { register (dispatcher) {
@ -60,6 +63,7 @@ module.exports = {
const commandName = context.getArgument('command') const commandName = context.getArgument('command')
let node = nodes.find(node => node.name === commandName) let node = nodes.find(node => node.name === commandName)
if (!node) throw UNKNOWN_COMMAND_ERROR.create(commandName)
if (node.redirect) node = node.redirect if (node.redirect) node = node.redirect
const aliases = [node, ...nodes.filter(_node => _node.redirect === node)].map(node => node.name) const aliases = [node, ...nodes.filter(_node => _node.redirect === node)].map(node => node.name)
@ -68,7 +72,7 @@ module.exports = {
let msg let msg
if (usages.length !== 1) { if (usages.length !== 1) {
msg = [ msg = [
{ text: bot.prefix + node.name, color: bot.colors.primary }, { text: bot.prefix + node.name, ...bot.styles.primary },
{ text: ' (' + aliases.join(', ') + ')', color: 'white' }, { text: ' (' + aliases.join(', ') + ')', color: 'white' },
{ text: ` - ${node.description}\n`, color: 'gray' } { text: ` - ${node.description}\n`, color: 'gray' }
] ]
@ -76,18 +80,18 @@ module.exports = {
msg.push(bot.prefix + node.name) msg.push(bot.prefix + node.name)
msg.push({ msg.push({
text: ` ${usage}\n`, text: ` ${usage}\n`,
color: bot.colors.secondary, ...bot.styles.secondary,
clickEvent: { action: 'suggest_command', value: node.name + ' ' + usage } clickEvent: { action: 'suggest_command', value: node.name + ' ' + usage }
}) })
}) })
msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1) msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1)
} else { } else {
msg = [ msg = [
{ text: bot.prefix + node.name, color: bot.colors.primary }, { text: bot.prefix + node.name, ...bot.styles.primary },
{ text: ' (' + aliases.join(', ') + ')', color: 'white' }, { text: ' (' + aliases.join(', ') + ')', color: 'white' },
{ {
text: ` ${usages[0]}`, text: ` ${usages[0]}`,
color: bot.colors.secondary, ...bot.styles.secondary,
clickEvent: { action: 'suggest_command', value: node.name + ' ' + usages[0] } clickEvent: { action: 'suggest_command', value: node.name + ' ' + usages[0] }
}, },
{ text: ` - ${node.description}`, color: 'gray' } { text: ` - ${node.description}`, color: 'gray' }

View file

@ -1,16 +1,28 @@
const name = 'hole' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Hole.' const { createNameSelector } = require('../util/command/utility')
const usage = '{prefix}hole <selector>'
const aliases = ['hole']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('hole')
.then(
argument('targets', greedyString())
.executes(this.holeCommand)
)
)
function execute (bot, cmd, entity, args, handler) { node.description = 'Creates a hole at the position of a players'
let selector = args.join(' ') node.permissionLevel = 0
if (selector.includes(' ') && !selector.startsWith('@')) { selector = `@a[name="${selector.replace(/"/, '\\"')}"]` } },
bot.core.run(`/execute at ${selector} run setblock ~ 1 ~ command_block{Command:'fill ~-1 0 ~-1 ~1 255 ~1 air destroy',auto:1}`) holeCommand (context) {
const source = context.source
const bot = source.bot
let selector = context.getArgument('targets')
const selector0 = selector[0]
if (selector0 !== '@' && selector0 !== '"' && selector0 !== "'" && selector.includes(' ')) selector = createNameSelector(selector)
bot.core.run(`execute at ${selector} run setblock ~ 1 ~ command_block{Command:'fill ~-1 0 ~-1 ~1 255 ~1 air destroy',auto:1b}`)
}
} }
module.exports = { name, description, usage, aliases, enabled, execute, permLevel }

View file

@ -1,77 +1,78 @@
const name = 'image' const { literal, argument, DynamicCommandExceptionType } = require('brigadier-commands')
const description = 'Renders an image.' const { location, path, isUrl } = require('../util/command/argument/location')
const usages = [ const TextMessage = require('../util/command/text_message')
'render <name/url>',
'list'
]
const aliases = ['image']
const enabled = true
const permLevel = 0
const fs = require('fs') const fs = require('fs')
const convertImage = require('../util/convert-image.js')
const nbt = require('prismarine-nbt')
const SNBT = require('../util/snbt.js')
function execute (bot, cmd, player, args, handler) { const IMAGE_DIRECTORY = 'images'
const subCmd = args.shift() const FILE_DOES_NOT_EXIST_ERROR = new DynamicCommandExceptionType(filename => new TextMessage(['File ', filename, ' does not exist']))
let src, files, primary, msg module.exports = {
switch (subCmd) { register (dispatcher) {
case 'render': const node = dispatcher.register(
src = args.join(' ').replace(/§.?/g, '') literal('image')
if (/https?:\/\//.test(src)) { .then(
bot.core.run(`/minecraft:tellraw @a ${JSON.stringify([ literal('render')
{ text: 'Attempting to convert image at ', color: bot.colors.primary }, .then(
{ text: src, color: bot.colors.secondary }, argument('location', location(IMAGE_DIRECTORY))
'.' .executes(this.renderCommand)
])}`) )
} else { )
src = `./images/${src}` .then(
literal('list')
.executes(this.listCommand.bind(this))
.then(
argument('location', path(IMAGE_DIRECTORY))
.executes(this.locationListCommand.bind(this))
)
)
)
if (!src.endsWith('.jpg')) { src += '.jpg' } node.description = 'Renders images'
node.permissionLevel = 0
},
if (src.match(/\//g).length > 2) { throw new Error('Invalid image name.') } renderCommand (context) {
const source = context.source
const bot = source.bot
if (!fs.existsSync(src)) { throw new Error('Invalid image name.') } const src = context.getArgument('location')
bot.core.run(`/minecraft:tellraw @a ${JSON.stringify([ if (!isUrl(src) && !fs.existsSync(src)) throw FILE_DOES_NOT_EXIST_ERROR.create()
{ text: 'Attempting to convert image ', color: bot.colors.primary },
{ text: args.join(' ').replace(/§.?/g, ''), color: bot.colors.secondary }, source.sendFeedback([
'.' { text: 'Attempting to convert image ', ...bot.styles.primary },
])}`) { text: src, ...bot.styles.secondary },
], true)
convertImage(src, (err, lines) => {
if (err) {
source.sendError(err.toString(), false)
return
} }
convertImage(src, (err, lines) => { lines.forEach((line, i) => {
if (err) { bot.exploits.execute(`at ${player.UUID} run summon armor_stand ~ ~${(i * -0.05) + (lines.length * 0.05) - 0.3} ~ ${SNBT.stringify(nbt.comp({ CustomName: nbt.string(line), CustomNameVisible: nbt.byte(1), Invisible: nbt.byte(1), Marker: nbt.byte(1), Health: nbt.float(0), DeathTime: nbt.int(99999) }))}`)
bot.core.run(`minecraft:tellraw @a ${JSON.stringify({ text: err.message, color: bot.colors.error })}`) if ((i + 1) >= lines.length) source.sendFeedback({ text: 'Finished rendering!', ...bot.styles.primary }, true)
return
}
lines.forEach((line, i) => {
bot.exploits.execute(`at ${player.UUID} run summon armor_stand ~ ~${(i * -0.05) + (lines.length * 0.05) - 0.3} ~ ${SNBT.stringify(nbt.comp({ CustomName: nbt.string(line), CustomNameVisible: nbt.byte(1), Invisible: nbt.byte(1), Marker: nbt.byte(1), Health: nbt.float(0), DeathTime: nbt.int(99999) }))}`)
if ((i + 1) >= lines.length) bot.core.run(`minecraft:tellraw @a ${JSON.stringify({ text: 'Finished rendering!', color: bot.colors.primary })}`)
})
}) })
break })
case 'list': },
files = fs.readdirSync('./images')
files.filter((file) => file.endsWith('.jpg'))
primary = false listCommand (context) {
msg = [{ text: 'Images - ', color: 'gray' }] this.listImages(context, IMAGE_DIRECTORY)
files.forEach((file) => { },
msg.push({
text: `${file} `, locationListCommand (context) {
color: (((primary = !primary)) ? bot.colors.primary : bot.colors.secondary), this.listImages(context, context.getArgument('location'))
clickEvent: { action: 'run_command', value: `${bot.prefix}${name} render ${file}` }, },
hoverEvent: { action: 'show_text', value: 'Click to render the image' }
}) async listImages (context, path) {
}) const source = context.source
bot.core.run(`/tellraw @a ${JSON.stringify(msg)}`) const bot = source.bot
break
default: try {
throw new Error('Invalid or missing argument.') const list = await bot.listFiles(path)
source.sendFeedback(['', { text: 'Images - ', ...bot.styles.primary }, ...list], false)
} catch (error) {
source.sendError(error.toString())
}
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,26 +1,48 @@
const name = 'kahoot' const { literal, argument, integer, greedyString } = require('brigadier-commands')
const description = 'kahoot client lol'
const usages = ['join <pin> <username>', 'leave', 'answer <answer>']
const aliases = ['kahoot']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('kahoot')
.then(
literal('join')
.then(
argument('pin', integer())
.then(
argument('username', greedyString())
.executes(this.joinCommand)
)
)
)
.then(
literal('leave')
.executes(this.leaveCommand)
)
.then(
literal('answer')
.then(
argument('answer', integer())
.executes(this.answerCommand)
)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'kahoot client lol'
const subCmd = args.shift() node.permissionLevel = 0
switch (subCmd) { },
case 'join':
bot.kahoot.join(parseFloat(args.shift()), args.join(' ')) joinCommand (context) {
break const bot = context.source.bot
case 'leave': bot.kahoot.join(context.getArgument('pin'), context.getArgument('username'))
bot.kahoot.leave() },
break
case 'answer': leaveCommand (context) {
bot.kahoot.answer(parseFloat(args.join(' '))) const bot = context.source.bot
break bot.kahoot.leave()
default: },
throw new Error('Invalid or missing argument.')
answerCommand (context) {
const bot = context.source.bot
bot.kahoot.answer(context.getArgument('answer'))
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,40 +0,0 @@
const name = 'kbwl'
const description = 'white list'
const usages = ['on', 'off', 'add', 'remove', 'list']
const aliases = ['kbwl']
const enabled = true
const permLevel = 1
function execute (bot, cmd, player, args, handler) {
const subCommand = args.shift()
const username = args.join(' ')
let i
switch (subCommand) {
case 'on':
bot.kbwl.players.push(player.name)
bot.kbwl.enabled = true
bot.core.run('bcraw &bKBWL is now on.')
break
case 'off':
bot.kbwl.enabled = false
bot.core.run('bcraw &bKBWL is now off.')
break
case 'add':
bot.kbwl.players.push(username)
bot.core.run(`bcraw &aAdded ${username} to the whitelist.`)
break
case 'remove':
i = bot.kbwl.players.indexOf(username)
if (i < 0) { return bot.core.run(`/bcraw &cThe player ${username} is not whitelisted!`) }
bot.kbwl.players.splice(1, i)
break
case 'list':
bot.core.run(`/tellraw @a ${JSON.stringify(`Whitelisted players: \n${bot.kbwl.players.join('\n')}`)}`)
break
default:
throw new Error('Invalid or missing argument.')
}
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,50 +1,84 @@
const name = 'mail' const { literal, argument, string, greedyString } = require('brigadier-commands')
const description = 'Shows mail.' const { createUuidSelector } = require('../util/command/utility')
const usages = ['send <username> <message>', 'list', 'clear']
const aliases = ['mail']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const listNode = literal('list').executes(this.listCommand).build()
const nbt = require('prismarine-nbt') const node = dispatcher.register(
const SNBT = require('../util/snbt.js') literal('mail')
const toNBTUUID = require('../util/uuid-to-nbt-uuid.js') .then(
literal('send')
.then(
argument('username', string())
.then(
argument('message', greedyString())
.executes(this.sendCommand)
)
)
)
.then(
listNode
)
.then(
literal('read')
.executes(this.listCommand)
.redirect(listNode)
)
.then(
literal('clear')
.executes(this.clearCommand)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Sends and receives mail from players'
const subCmd = args.shift() node.permissionLevel = 0
},
let u, message, messages, msg sendCommand (context) {
switch (subCmd) { const source = context.source
case 'send': const bot = source.bot
u = args.shift() const player = source.getPlayerOrThrow()
message = args.join(' ')
bot.sendMail(player.name, u, message)
bot.core.run(`minecraft:tellraw @a[nbt=${SNBT.stringify(nbt.comp({ UUID: toNBTUUID(player.UUID) }))}] ${JSON.stringify([
{ text: 'Sent ', color: bot.colors.primary },
{ text: message, color: bot.colors.secondary },
' to ',
{ text: u, color: bot.colors.secondary },
'.'
])}`)
break
case 'list':
messages = bot.mail[player.name]
if (!messages || messages.length < 1) return bot.core.run(`minecraft:tellraw @a[nbt=${SNBT.stringify(nbt.comp({ UUID: toNBTUUID(player.UUID) }))}] ${JSON.stringify({ text: 'You have no mail', color: bot.colors.primary })}`)
msg = [{ text: 'Mail:\n', color: bot.colors.primary }]
messages.forEach((message) => {
msg.push(`${message.sender} (from ${message.host}): `)
msg.push({ text: `${message.message}\n`, color: bot.colors.secondary })
})
msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1)
bot.core.run(`minecraft:tellraw @a[nbt=${SNBT.stringify(nbt.comp({ UUID: toNBTUUID(player.UUID) }))}] ${JSON.stringify(msg)}`) const username = context.getArgument('username')
break const message = context.getArgument('message')
case 'clear':
bot.mail[player.name] = [] bot.sendMail(player.username, username, message)
bot.core.run(`minecraft:tellraw @a[nbt=${SNBT.stringify(nbt.comp({ UUID: toNBTUUID(player.UUID) }))}] ${JSON.stringify([ bot.tellraw([
{ text: 'Your mail has been cleared.', color: bot.colors.primary } { text: 'Sent ', ...bot.styles.primary },
])}`) { text: message, ...bot.styles.secondary },
' to ',
{ text: username, ...bot.styles.secondary }
], createUuidSelector(player.uuid))
},
listCommand (context) {
const source = context.source
const bot = source.bot
const player = source.getPlayerOrThrow()
const messages = bot.mail[player.username]
if (!messages || messages.length < 1) {
bot.tellraw({ text: 'You have no mail', ...bot.styles.primary }, createUuidSelector(player.uuid))
return
}
const msg = [{ text: 'Mail:\n', ...bot.styles.primary }]
messages.forEach((message) => {
msg.push(`${message.sender} (from ${message.host}): `)
msg.push({ text: `${message.message}\n`, ...bot.styles.secondary })
})
msg[msg.length - 1].text = msg[msg.length - 1].text.slice(0, -1)
bot.tellraw(msg, createUuidSelector(player.uuid))
},
clearCommand (context) {
const source = context.source
const bot = source.bot
const player = source.getPlayerOrThrow()
delete bot.mail[player.username]
bot.tellraw({ text: 'Your mail has been cleared', ...bot.styles.primary }, createUuidSelector(player.uuid))
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

38
commands/matrix.js Normal file
View file

@ -0,0 +1,38 @@
const { literal } = require('brigadier-commands')
module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('matrix')
.executes(this.matrixCommand)
)
node.description = 'Sends the Matrix invite'
node.permissionLevel = 0
},
matrixCommand (context) {
const source = context.source
const bot = source.bot
source.sendFeedback([
{ text: 'Join the ', color: 'gray' },
{ text: 'ChipmunkBot Matrix', ...bot.styles.primary },
' at ',
{
text: bot.matrix.inviteUrl,
...bot.styles.secondary,
hoverEvent: {
action: 'show_text',
contents: 'Click to copy the invite link to your clipboard!'
},
clickEvent: {
action: 'copy_to_clipboard', // * Minecraft, and Java's URI class in general, seem to hate `#`, so open_url does not work.
value: bot.matrix.inviteUrl
}
},
'!'
], false)
}
}

View file

@ -1,95 +1,96 @@
const name = 'music' const { literal, argument, DynamicCommandExceptionType } = require('brigadier-commands')
const description = 'Plays music' const { location, path, isUrl } = require('../util/command/argument/location')
const usages = [ const TextMessage = require('../util/command/text_message')
'play <song>',
'list',
'skip',
'stop'
]
const aliases = ['music']
const enabled = true
const permLevel = 0
const fs = require('fs') const fs = require('fs')
const path = require('path')
const https = require('https')
function execute (bot, cmd, player, args, handler) { const SONG_DIRECTORY = 'songs'
const subCmd = args.shift() const FILE_DOES_NOT_EXIST_ERROR = new DynamicCommandExceptionType(filename => new TextMessage(['File ', filename, ' does not exist']))
let filepath, file, files, primary, msg, split module.exports = {
switch (subCmd) { register (dispatcher) {
case 'play': const node = dispatcher.register(
filepath = args.join(' ').replace(/\xa7.?/g, '') literal('music')
/* .then(
if (/https?:\/\//.test(filepath)) { literal('play')
https.get(filepath, (res) => { .then(
// Open file in local filesystem argument('location', location(SONG_DIRECTORY))
tmpobj = tmp.fileSync() .executes(this.playCommand)
file = fs.createWriteStream(tmpobj.name) )
)
.then(
literal('skip')
.executes(this.skipCommand)
)
.then(
literal('stop')
.executes(this.stopCommand)
)
.then(
literal('loop')
.executes(this.loopCommand)
)
.then(
literal('list')
.executes(this.listCommand.bind(this))
.then(
argument('location', path(SONG_DIRECTORY))
.executes(this.locationListCommand.bind(this))
)
)
)
// Write data into local file node.description = 'Plays songs using note block sounds'
res.pipe(file) node.permissionLevel = 0
},
// Close the file playCommand (context) {
file.on('finish', () => { const source = context.source
file.close() const bot = source.bot
bot.music.queue.push(tmpobj.name)
})
})// .on("error", (err) => {
// console.log("Error: ", err.message);
// });
return
}
*/
filepath = path.join('music', filepath)
// if (!filepath.endsWith('.mid')) { filepath += '.mid' }
split = filepath.split('/') const filepath = context.getArgument('location')
if (split[0] !== 'music') { throw new Error('geese') }
if (!fs.existsSync(filepath)) { throw new Error('Invalid song name.') } if (!isUrl(filepath) && !fs.existsSync(filepath)) throw FILE_DOES_NOT_EXIST_ERROR.create(filepath)
if (!bot.music.playing) { bot.music.queue.push(filepath)
bot.music.play(filepath) source.sendFeedback([
} else { { text: 'Added ', ...bot.styles.primary },
bot.music.queue.push(filepath) { text: filepath.replace(/.+\//g, ''), ...bot.styles.secondary },
bot.core.run('minecraft:tellraw @a ' + JSON.stringify([ ' to the music queue.'
{ text: 'Added ', color: bot.colors.primary }, ], false)
{ text: filepath.replace(/.+\//g, ''), color: bot.colors.secondary }, },
' to the music queue.'
]))
}
break
case 'list':
files = fs.readdirSync('./music')
// files.filter((file) => file.endsWith('.mid'))
primary = false skipCommand (context) {
msg = [{ text: 'Songs - ', color: 'gray' }] const bot = context.bot
files.forEach((file) => { bot.music.skip()
msg.push({ },
text: `${file} `,
color: (((primary = !primary)) ? bot.colors.primary : bot.colors.secondary), stopCommand (context) {
clickEvent: { action: 'run_command', value: `${bot.prefix}${name} play ${file}` }, const bot = context.bot
hoverEvent: { action: 'show_text', value: 'Click to play the song' } bot.music.stop()
}) },
})
bot.core.run(`/tellraw @a ${JSON.stringify(msg)}`) loopCommand (context) {
break const bot = context.bot
case 'skip': bot.music.looping = !bot.music.looping
bot.music.skip() },
break
case 'stop': listCommand (context) {
bot.music.stop() this.listSongs(context, SONG_DIRECTORY)
break },
case 'loop':
bot.music.looping = !bot.music.looping locationListCommand (context) {
break this.listSongs(context, context.getArgument('location'))
default: },
throw new Error('Invalid or missing argument.')
async listSongs (context, path) {
const source = context.source
const bot = source.bot
try {
const list = await bot.listFiles(path)
source.sendFeedback(['', { text: 'Songs - ', ...bot.styles.primary }, ...list], false)
} catch (error) {
source.sendError(error.toString())
}
} }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,17 +1,24 @@
const name = 'myuser' const { literal } = require('brigadier-commands')
const description = 'Shows your username'
const usages = []
const aliases = ['myuser']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('myuser')
.executes(this.myUserCommand)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Sends the username of the sender of the command'
bot.core.run(`/tellraw @a ${JSON.stringify([ node.permissionLevel = 0
{ text: 'Your username is: ', color: bot.colors.primary }, },
{ text: player.name, color: bot.colors.secondary },
'.' myUserCommand (context) {
])}`) const source = context.source
const bot = source.bot
const player = source.getPlayerOrThrow()
source.sendFeedback([
{ text: 'Your username is: ', ...bot.styles.primary },
{ text: player.username, ...bot.styles.secondary }
], false)
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,14 +1,25 @@
const name = 'netmsg' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Sends a message as each bot.'
const usages = ['<message...>']
const aliases = ['netmsg']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('netmsg')
.then(
argument('message', greedyString())
.executes(this.netMsgCommand)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Runs a command in the command core'
const host = bot.host node.permissionLevel = 0
bot.getBots().forEach((bot) => bot.fancyMsg(host, player.name, args.join(' '))) },
netMsgCommand (context) {
const source = context.source
const bot = source.bot
const message = context.getArgument('message')
const host = bot.host
bot.bots.forEach((bot) => bot.fancyMsg(host, source.displayName, message))
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,25 +0,0 @@
const name = 'pos'
const description = 'Gets the position of a player'
const usages = ['<selector>']
const aliases = ['pos']
const enabled = true
const permLevel = 0
function execute (bot, cmd, player, args) {
bot.getEntityPos(args.join(' '), (position) => {
const { x, y, z } = position
bot.core.run(`minecraft:tellraw @a ${JSON.stringify([
{ text: 'Position: ', color: bot.colors.primary },
{
text: `[${x}, ${y}, ${z}]`,
color: bot.colors.secondary,
clickEvent: { action: 'run_command', value: `/essentials:tp ${x} ${y} ${z}` },
hoverEvent: { action: 'show_text', value: 'Click to teleport' }
}
])}`)
// bot.chatQueue.push(`&aPosition: &2${x} ${y} ${z}`)
})
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,14 +0,0 @@
const name = 'printcodes'
const description = 'Prints permission codes to console.'
const usage = '{prefix}printcodes'
const aliases = ['printcodes']
const enabled = true
const permLevel = 0
function execute (bot, cmd, entity, args, handler) {
require('../cperms.js').printCodes()
bot.core.run('/bcraw &aPrinted codes to console.')
}
module.exports = { name, description, usage, aliases, enabled, execute, permLevel }

View file

@ -1,24 +1,31 @@
const name = 'rainbowify' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Makes text rainbow'
const usages = ['<message...>']
const aliases = ['rainbowify']
const enabled = true
const permLevel = 0
const colorsys = require('colorsys') const colorsys = require('colorsys')
function execute (bot, cmd, player, args, handler) { module.exports = {
const message = args.join(' ') register (dispatcher) {
const node = dispatcher.register(
literal('rainbowify')
.then(
argument('message', greedyString())
.executes(this.rainbowifyCommand)
)
)
const result = [] node.description = 'Makes text rainbow'
let hue = 0 node.permissionLevel = 0
message.split('').forEach((char) => { },
result.push({ text: char, color: colorsys.hsv2Hex(hue, 100, 100) })
hue += 355 / Math.max(message.length, 20)
})
bot.core.run(`/tellraw @a ${JSON.stringify(result)}`) rainbowifyCommand (context) {
} const source = context.source
const message = context.getArgument('message')
module.exports = { name, description, usages, aliases, enabled, execute, permLevel } const result = []
let hue = 0
message.split('').forEach((char) => {
result.push({ text: char, color: colorsys.hsv2Hex(hue, 100, 100) })
hue += 355 / Math.max(message.length, 20)
})
source.sendFeedback(result, false)
}
}

View file

@ -1,18 +1,27 @@
const name = 'randomteleport' const { literal } = require('brigadier-commands')
const description = 'Teleports you to a random location.'
const usage = '{prefix}randomteleport'
const aliases = ['randomteleport', 'randomtele', 'randomtp', 'rtp']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('randomteleport')
.executes(this.randomTeleportCommand)
)
function execute (bot, cmd, player, args, handler) { dispatcher.register(literal('rtp').executes(this.randomTeleportCommand).redirect(node))
bot.core.run(`/essentials:sudo ${player.UUID} c:/tppos ${randomInt(-30000000, 30000000)} 256 ${randomInt(-30000000, 30000000)}`)
// setTimeout(() => bot.core.run(`/essentials:sudo ${player.UUID} c:/top`), 100) node.description = 'Teleports the sender to a random location'
node.permissionLevel = 0
},
randomTeleportCommand (context) {
const source = context.source
const bot = source.bot
const player = source.getPlayerOrThrow()
bot.core.run(`tp ${player.uuid} ${randomInt(-30000000, 30000000)} 256 ${randomInt(-30000000, 30000000)}`)
}
} }
function randomInt (min, max) { function randomInt (min, max) {
return Math.floor((Math.random() * (max - min) + min) + 1) return Math.floor((Math.random() * (max - min)) + min)
} }
module.exports = { name, description, usage, aliases, enabled, execute, permLevel }

View file

@ -1,13 +1,18 @@
const name = 'rc' const { literal } = require('brigadier-commands')
const description = 'Resets the bot\'s command core.'
const usages = []
const aliases = ['rc']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('rc')
.executes(this.refillCoreCommand)
)
function execute (bot, cmd, entity, args, handler) { node.description = "Refills the bot's command core"
bot.core.reset() node.permissionLevel = 0
},
refillCoreCommand (context) {
const bot = context.source.bot
bot.core.reset()
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,16 +1,21 @@
const name = 'reload' const { literal } = require('brigadier-commands')
const description = 'Attempts to reload all commands.'
const usages = []
const aliases = ['reload']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('reload')
.executes(this.reloadCommand)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Attempts to reload all commands'
bot.core.run(`minecraft:tellraw @a ${JSON.stringify([ node.permissionLevel = 0
{ text: 'Reloading!', color: bot.colors.primary } },
])}`)
handler.reload() reloadCommand (context) {
const source = context.source
const bot = source.bot
source.sendFeedback({ text: 'Reloading!', ...bot.styles.primary }, true)
bot.commands.reload()
}
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +0,0 @@
const name = 'section'
const description = 'sus ploit.'
const usages = []
const aliases = ['section']
const enabled = true
const permLevel = 0
function execute (bot, cmd, entity, args, handler) {
bot.core.run(`/tellraw @a ${JSON.stringify({ text: 'Click here to get kicked!', underlined: true, clickEvent: { action: 'run_command', value: '/\u00a7' } })}`)
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,24 +1,36 @@
const name = 'seen' const { literal, argument, string, DynamicCommandExceptionType } = require('brigadier-commands')
const description = 'Shows when a player was first and last seen' const TextMessage = require('../util/command/text_message')
const usages = [] const NEVER_SEEN_ERROR = new DynamicCommandExceptionType(username => new TextMessage([username, ' was never seen']))
const aliases = ['seen']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('seen')
.then(
argument('username', string())
.executes(this.seenCommand)
)
)
node.description = 'Shows when a player was first and last seen'
node.permissionLevel = 0
},
seenCommand (context) {
const source = context.source
const bot = source.bot
const username = context.getArgument('username')
if (bot.seen[username] == null) throw NEVER_SEEN_ERROR.create(username)
function execute (bot, cmd, player, args, handler) {
const username = args.join(' ')
if (bot.seen[username] != null) {
const { first, last } = bot.seen[username] const { first, last } = bot.seen[username]
bot.core.run('tellraw @a ' + JSON.stringify([ source.sendFeedback([
{ text: '', color: bot.colors.primary }, { text: '', ...bot.styles.primary },
{ text: username, color: bot.colors.secondary }, { text: username, ...bot.styles.secondary },
' was first seen on ', ' was first seen on ',
{ text: first, color: bot.colors.secondary }, { text: first, ...bot.styles.secondary },
' and last seen on ', ' and last seen on ',
{ text: last, color: bot.colors.secondary } { text: last, ...bot.styles.secondary }
])) ], false)
} else throw new Error(username + ' was never seen') }
} }
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,20 +1,34 @@
const name = 'spawnmob' const { literal, argument, integer, string, greedyString } = require('brigadier-commands')
const description = 'but better' const nbt = require('prismarine-nbt')
const usages = ['<amount> [entity]'] const snbt = require('../util/snbt.js')
const aliases = ['spawnmob']
const enabled = true
const permLevel = 0 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('spawnmob')
.then(
argument('amount', integer())
.then(
argument('entity', string())
.executes(this.spawnMobCommand)
)
)
)
function execute (bot, cmd, player, args, handler) { node.description = 'Summons multiple entities of a specific type'
const amount = parseInt(args.shift()) || 0 node.permissionLevel = 0
const entity = args.shift() ?? 'pig' },
const arr = [] spawnMobCommand (context) {
while (arr.length < amount) { const source = context.source
arr.push(`{id:'${entity.replace(/'/g, '\\\'')}'}`) const bot = source.bot
const player = source.getPlayerOrThrow()
const amount = context.getArgument('amount')
const entity = context.getArgument('entity')
const data = snbt.stringify(nbt.comp({ id: nbt.string(entity) }))
const passengers = Array(amount).fill(data)
bot.core.run(`execute at ${player.uuid} run setblock ~ ~-1 ~ command_block${snbt.stringify(nbt.comp({ auto: nbt.byte(1), Command: nbt.string(`summon area_effect_cloud ~ ~1 ~ {Passengers:[${passengers.join(',')}]}`) }))} destroy`)
} }
bot.exploits.execute(`at ${player.UUID} run summon area_effect_cloud ~ ~ ~ {Passengers:[${arr.join(',')}]}`) }
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,13 +0,0 @@
const name = 'teleport'
const description = 'halal tp command lol'
const usages = ['<args...>']
const aliases = ['teleport', 'tp']
const enabled = true
const permLevel = 0
function execute (bot, cmd, player, args, handler) {
bot.exploits.execute(`as ${player.UUID} at @s run teleport ${args.join(' ')}`)
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel }

View file

@ -1,42 +1,73 @@
const name = 'urban' const { literal, argument, greedyString } = require('brigadier-commands')
const description = 'Shows word definitions from the Urban Dictionary'
const usages = ['<word...>']
const aliases = ['urban']
const enabled = true
const permLevel = 0
const ud = require('urban-dictionary') const ud = require('urban-dictionary')
function execute (bot, cmd, player, args, handler) { module.exports = {
// Callback register (dispatcher) {
ud.define(args.join(' ').replace(/§./, ''), (error, results) => { const node = dispatcher.register(
if (error) { literal('urban')
bot.core.run(`/tellraw @a ${JSON.stringify([ .then(
{ text: error.message, color: bot.colors.error } argument('word', greedyString())
])}`) .executes(this.urbanCommand.bind(this))
return )
} )
node.description = 'Shows word definitions from the Urban Dictionary'
node.permissionLevel = 0
},
async urbanCommand (context) {
const source = context.source
const definitions = await ud.define(context.getArgument('word'))
.catch(error => source.sendError(error.toString()))
if (!definitions) return
const msg = [{ text: '', color: 'gray' }] const msg = [{ text: '', color: 'gray' }]
results.forEach((result) => { for (const definition of definitions) {
msg.push({ text: '[', color: 'dark_gray' }) msg.push(
msg.push({ text: 'Urban', color: 'red' }) { text: '[', color: 'dark_gray' },
msg.push({ text: '] ', color: 'dark_gray' }) { text: 'Urban', color: 'red' },
msg.push({ text: `${result.word} `, bold: true }) { text: '] ', color: 'dark_gray' },
const a = result.definition.replace(/\r\n?/g, '\n').split(/\[|\]/) { text: `${definition.word} `, bold: true },
for (let i = 0; i < a.length; i += 2) { ...this.parseDefinitionText(definition.definition, source),
msg.push({ text: a[i] }) '\n'
if (a[i + 1] != null) { )
msg.push( }
{ text: a[i + 1], underlined: true, clickEvent: { action: 'suggest_command', value: `${bot.prefix}${name} ${a[i + 1]}` } }
)
}
}
msg[msg.length - 1].text += '\n'
})
bot.core.run(`minecraft:tellraw @a ${JSON.stringify(msg)}`)
})
}
module.exports = { name, description, usages, aliases, enabled, execute, permLevel } msg.pop()
source.sendFeedback(msg, false)
},
parseDefinitionText (text, source) {
const prefix = source.bot.prefix
const texts = []
let string = ''
for (let i = 0; i < text.length; i++) {
let c = text[i]
if (c === '[') {
if (string) texts.push(string)
string = ''
let subword = ''
i++
for (; i < text.length; i++) {
c = text[i]
if (c === ']') {
if (subword) texts.push({ text: subword, underlined: true, clickEvent: { action: 'suggest_command', value: prefix + 'urban ' + subword } })
subword = ''
break
}
else subword += c
}
if (subword) texts.push({ text: subword, underlined: true, clickEvent: { action: 'suggest_command', value: prefix + 'urban ' + subword } })
continue
}
string += c
}
if (string) texts.push(string)
return texts
}
}

View file

@ -1,15 +1,21 @@
const name = 'validate' const { literal } = require('brigadier-commands')
const description = 'Tests trusted code validation.'
const usage = '{prefix}validate'
const aliases = ['validate']
const enabled = true
const permLevel = 1 module.exports = {
register (dispatcher) {
const node = dispatcher.register(
literal('validate')
.requires(source => source.permissionLevel >= 1)
.executes(this.validateCommand)
)
function execute (bot, cmd, entity, args, handler) { node.description = 'Checks if a hash is valid'
bot.core.run(`/tellraw @a ${JSON.stringify([ node.permissionLevel = 1
{ text: 'Valid code.', color: bot.colors.primary } },
])}`)
validateCommand (context) {
const source = context.source
const bot = source.bot
source.sendFeedback({ text: 'Valid hash', ...bot.styles.primary }, false)
}
} }
module.exports = { name, description, usage, aliases, enabled, execute, permLevel }

View file

@ -1,31 +0,0 @@
const name = 'video'
const description = 'Plays videos'
const usages = ['play <filepath...>', 'stop']
const aliases = ['video']
const enabled = true
// const fs = require('fs')
function execute (bot, cmd, entity, args, handler) {
const subCmd = args.shift().toLowerCase()
let filepath
switch (subCmd) {
case 'play':
filepath = args.join(' ').replace(/\u00a7.?/g, '')
// if (!fs.existsSync(filepath) || !fs.statSync(filepath).isFile()) throw new Error('Invalid filepath: '+filepath)
// client.util.writeChat({ text: 'Loading '+filepath })
bot.video.play(filepath)
break
case 'stop':
bot.video.stop()
// client.util.writeChat({ text: 'Stopped video playback.' })
break
// case 'nowplaing':
// client.util.writeChat({ text: 'Currently playing song: '+targetClient.util.music.nowPlaying })
// break
default:
throw new Error('Invalid or missing argument')
}
}
module.exports = { name, description, usages, aliases, enabled, permLevel: 0, execute }

View file

@ -1,17 +0,0 @@
const secretNums = [null, -2.5, 6.9, 4.2069]
function validate (level, username, code) {
for (let i = level; i < secretNums.length; i++) {
if (getCode(level, username) === code) {
return true
}
}
return false
}
function getCode (level, username = ' ') {
const date = new Date()
return String.fromCharCode(Math.floor((date.getDate() + date.getMinutes() - date.getMonth()) / secretNums[level] * username.length * 69))
}
module.exports = { secretNums, validate, getCode }

View file

@ -6,11 +6,12 @@ const moment = require('moment')
const json5 = require('json5') const json5 = require('json5')
const matrix = require('matrix-js-sdk') const matrix = require('matrix-js-sdk')
if (!fs.existsSync('config.json5')) { const configPath = process.argv[2] ?? 'config.json5'
fs.copyFileSync(path.join(__dirname, 'default.json5'), 'config.json5') if (!fs.existsSync(configPath)) {
fs.copyFileSync(path.join(__dirname, 'default.json5'), configPath)
console.info('No config file was found, so a default one was created.') console.info('No config file was found, so a default one was created.')
} }
const config = json5.parse(fs.readFileSync('config.json5', 'utf-8')) const config = json5.parse(fs.readFileSync(configPath, 'utf-8'))
const logdir = 'logs' const logdir = 'logs'
if (!fs.existsSync(logdir)) fs.mkdirSync(logdir) if (!fs.existsSync(logdir)) fs.mkdirSync(logdir)
@ -40,15 +41,17 @@ for (const key in config.matrixClients) {
client.startClient() client.startClient()
} }
const bots = []
for (const options of config.bots) { for (const options of config.bots) {
const mergedOptions = { ...(config.all ?? {}), ...options } const mergedOptions = { ...(config.all ?? {}), ...options }
if (mergedOptions.matrix && typeof mergedOptions.matrix.client !== 'object') mergedOptions.matrix.client = matrixClients[mergedOptions.matrix.client] if (mergedOptions.matrix && typeof mergedOptions.matrix.client !== 'object') mergedOptions.matrix.client = matrixClients[mergedOptions.matrix.client]
const bot = createBot(mergedOptions) const bot = createBot(mergedOptions)
bots.push(bot)
bot.bots = bots
bot.on('error', console.error) bot.on('error', console.error)
bot.console.filepath = logfile bot.console.filepath = logfile
bot.console.setRl(rl) bot.console.setRl(rl)
bot.commands.loadFromDir('commands')
} }

4
package-lock.json generated
View file

@ -561,7 +561,7 @@
}, },
"node_modules/brigadier-commands": { "node_modules/brigadier-commands": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae" "resolved": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#0fcbb5cc6416e9cfe3d045f7d008904eec5da386"
}, },
"node_modules/bs58": { "node_modules/bs58": {
"version": "5.0.0", "version": "5.0.0",
@ -4176,7 +4176,7 @@
} }
}, },
"brigadier-commands": { "brigadier-commands": {
"version": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#c89271d021a1537d3045a93850e0c7ccb6efd9ae", "version": "git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git#0fcbb5cc6416e9cfe3d045f7d008904eec5da386",
"from": "brigadier-commands@git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git" "from": "brigadier-commands@git+https://code.chipmunk.land/ChipmunkMC/node-brigadier-commands.git"
}, },
"bs58": { "bs58": {

View file

@ -26,8 +26,8 @@ function inject (bot) {
} }
function _log (prefix, stdout, data) { function _log (prefix, stdout, data) {
// format it // format it
const _prefix = `[${formatDate()} ${prefix}\u00a7r] ` const _prefix = `[${formatDate()} ${prefix}\u00a7r] [${bot.host}] `
const stringifyOptions = { lang: bot.registry.language } const stringifyOptions = { lang: bot.registry?.language ?? {} }
const formattedData = _prefix + colorCodeStringify(data, stringifyOptions) + '\n' const formattedData = _prefix + colorCodeStringify(data, stringifyOptions) + '\n'
const ansi = ansiStringify(_prefix, stringifyOptions) + ansiStringify(data, stringifyOptions) + '\x1b[0m\n' const ansi = ansiStringify(_prefix, stringifyOptions) + ansiStringify(data, stringifyOptions) + '\x1b[0m\n'
@ -39,7 +39,7 @@ function inject (bot) {
if (err) console.error(err) if (err) console.error(err)
}) })
} }
bot.tellraw(formattedData, '_ChipMC_')
// log to stdout // log to stdout
stdout.write(ansi + '') stdout.write(ansi + '')
} }
@ -61,7 +61,7 @@ bot.tellraw(formattedData, '_ChipMC_')
function handleLine (line) { function handleLine (line) {
if (bot.host !== bot.console.host && bot.console.host !== 'all') return if (bot.host !== bot.console.host && bot.console.host !== 'all') return
if (line.startsWith('.')) { if (line.startsWith('.')) {
const source = new CommandSource({ bot, permissionLevel: Infinity, sendFeedback }) const source = new CommandSource({ bot, permissionLevel: Infinity, sendFeedback, displayName: 'console' })
bot.commands.execute(line.substring(1), source) bot.commands.execute(line.substring(1), source)
} else { } else {
bot.fancyMsg('test', '_ChipMC_', line) bot.fancyMsg('test', '_ChipMC_', line)

View file

@ -61,13 +61,18 @@ function inject (bot) {
bot.on('packet.player_chat', (packet) => { bot.on('packet.player_chat', (packet) => {
const plain = packet.plainMessage const plain = packet.plainMessage
const unsigned = parseNbtText(packet.unsignedChatContent) const unsigned = parseNbtText(packet.unsignedChatContent)
const sender = bot.players.find(player => player.uuid === packet.senderUuid) let sender = bot.players.find(player => player.uuid === packet.senderUuid)
const type = bot.registry?.chatFormattingById[packet.type] const type = bot.registry?.chatFormattingById[packet.type]
if (!sender) {
bot.console.warn(`Unable to find player_chat sender in player list (uuid: ${packet.senderUuid})`)
sender = undefined
}
bot.emit('player_chat', { plain, unsigned, sender, type: type.name }) bot.emit('player_chat', { plain, unsigned, sender, type: type.name })
bot.emit('chat', unsigned) bot.emit('chat', unsigned)
tryParsingMessage(unsigned, { senderUuid: sender.uuid, players: bot.players, lang: bot.registry.language, plain }) tryParsingMessage(unsigned, { senderUuid: sender?.uuid, players: bot.players, lang: bot.registry.language, plain })
}) })
bot.on('packet.system_chat', (packet) => { bot.on('packet.system_chat', (packet) => {

View file

@ -1,20 +1,33 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const util = require('util') const { CommandDispatcher, literal, argument, greedyString, CommandSyntaxException } = require('brigadier-commands')
const { CommandDispatcher, builder: { LiteralArgumentBuilder: { literal }, RequiredArgumentBuilder: { argument } }, arguments: { StringArgumentType: { greedyString } }, exceptions: { CommandSyntaxException } } = require('brigadier-commands')
const CommandSource = require('../util/command/command_source') const CommandSource = require('../util/command/command_source')
const TextMessage = require('../util/command/text_message') const TextMessage = require('../util/command/text_message')
const colorCodeStringify = require('../util/chat/stringify/color_code') const colorCodeStringify = require('../util/chat/stringify/color_code')
let commands
function loadCommands () {
commands = []
fs.readdirSync('commands').forEach(filename => {
const filepath = path.resolve('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()
function inject (bot) { function inject (bot) {
bot.commands = { bot.commands = {
dispatcher: new CommandDispatcher(), dispatcher: null,
add,
execute, execute,
info, reload,
isCommand,
loadFromDir,
isValid
} }
bot.on('message', ({ sender, plain }) => { bot.on('message', ({ sender, plain }) => {
@ -23,75 +36,23 @@ function inject (bot) {
function sendFeedback (message) { function sendFeedback (message) {
bot.tellraw(message, '@a') bot.tellraw(message, '@a')
} }
bot.commands.execute(plain.substring(bot.prefix.length), new CommandSource({ bot, player: sender, sendFeedback }))
})
function add (command) { const displayName = {
if (command.register) { insertion: sender.username,
command.register(bot.commands.dispatcher) clickEvent: { action: 'suggest_command', value: `/tell ${sender.username} ` },
return hoverEvent: {
} action: 'show_entity',
contents: {
if (isValid(command)) { type: 'minecraft:player',
bot.console.warn(`Command '${command.aliases[0]}' is using the legacy command system!`) id: sender.uuid,
name: sender.username
const _execute = (c, args) => {
try {
const player = c.source.player
command.execute(bot, command.aliases[0], { UUID: player?.uuid, name: player?.username }, args)
} catch (error) {
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: error.toString(), color: 'red' }))
} }
} },
text: sender.username
const requirement = source => source.permissionLevel >= command.permLevel
const node = bot.commands.dispatcher.register(
literal(command.aliases[0])
.executes(c => { _execute(c, []); return 0 })
.requires(requirement)
.then(
argument('args', greedyString())
.executes(c => { _execute(c, c.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 })
.requires(requirement)
.redirect(node)
)
}
// add metadata for help command
node.description = command.description
node.permissionLevel = command.permLevel
return
} }
throw new Error('Invalid command', 'invalid_command') bot.commands.execute(plain.substring(bot.prefix.length), new CommandSource({ bot, player: sender, sendFeedback, displayName }))
} })
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 (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 true }
function execute (command, source) { function execute (command, source) {
try { try {
@ -134,18 +95,22 @@ function inject (bot) {
return text return text
} }
}
function isValid (command) { function reload (loadFiles = true) {
return command != null && bot.commands.dispatcher = new CommandDispatcher()
typeof command.execute === 'function' && if (loadFiles) loadCommands()
typeof command.name === 'string' &&
typeof command.description === 'string' && for (const command of commands) {
Array.isArray(command.usages) && try {
Array.isArray(command.aliases) && command.register(bot.commands.dispatcher)
typeof command.enabled === 'boolean' && } catch (error) {
command.aliases.length > 0 && console.error('Unable to register command:', error)
typeof command.permLevel === 'number' }
}
bot.emit('commands_loaded')
}
reload(false)
} }
module.exports = inject module.exports = inject

View file

@ -87,7 +87,7 @@ function inject (bot) {
bot.core.run(`minecraft:setblock ${Math.floor(oldPos.x)} ${Math.floor(oldPos.y - 1)} ${Math.floor(oldPos.z)} minecraft:air replace mincecraft:command:block`) // Clean up after refills bot.core.run(`minecraft:setblock ${Math.floor(oldPos.x)} ${Math.floor(oldPos.y - 1)} ${Math.floor(oldPos.z)} minecraft:air replace mincecraft:command:block`) // Clean up after refills
bot.core.reset() bot.core.reset()
}) })
setInterval(() => bot.core.refill(), 60 * 1000) setInterval(() => bot.core.reset(), 60 * 1000)
} }
module.exports = inject module.exports = inject

View file

@ -5,7 +5,7 @@ function inject (bot) {
{ text: '[', color: 'dark_gray' }, { text: '[', color: 'dark_gray' },
rank, rank,
{ text: '] ', color: 'dark_gray' }, { text: '] ', color: 'dark_gray' },
[{ text: '', color: bot.colors.secondary }, username], [{ text: '', ...bot.styles.secondary }, username],
{ text: ' ', color: 'dark_gray' }, { text: ' ', color: 'dark_gray' },
message message
], '@a') ], '@a')

View file

@ -14,7 +14,7 @@ function inject (bot, options) {
const startTime = Date.now() const startTime = Date.now()
bot.on('chat_html', async html => { bot.on('chat_html', html => {
sendMessage(html) sendMessage(html)
}) })
@ -25,7 +25,7 @@ function inject (bot, options) {
contents: 'Click to copy the invite link for the Matrix space to your clipboard!' contents: 'Click to copy the invite link for the Matrix space to your clipboard!'
}, },
clickEvent: { clickEvent: {
action: 'copy_to_clipboard', // * Minecraft, and Java's URL class in general, seem to hate `#`, so open_url does not work. action: 'copy_to_clipboard', // * Minecraft, and Java's URI class in general, seem to hate `#`, so open_url does not work.
value: bot.matrix.inviteUrl value: bot.matrix.inviteUrl
} }
} }
@ -37,22 +37,6 @@ function inject (bot, options) {
const permissionLevel = event.sender.powerLevelNorm const permissionLevel = event.sender.powerLevelNorm
let message = content.body let message = content.body
if (content.url) {
message = {
text: '[Attachment]',
color: bot.colors.primary,
clickEvent: {
action: 'open_url',
value: bot.matrix.client.mxcUrlToHttp(content.url)
}
}
} else if (message.startsWith(bot.matrix.commandPrefix)) {
const source = new CommandSource({ bot, permissionLevel, sendFeedback })
bot.commands.execute(message.substring(bot.matrix.commandPrefix.length), source)
return
}
const senderText = { const senderText = {
text: String(event.sender.rawDisplayName || event.sender.name || event.sender.userId), text: String(event.sender.rawDisplayName || event.sender.name || event.sender.userId),
hoverEvent: { hoverEvent: {
@ -64,6 +48,23 @@ function inject (bot, options) {
value: String(event.sender.userId) value: String(event.sender.userId)
} }
} }
if (content.url) {
message = {
text: '[Attachment]',
...bot.styles.primary,
clickEvent: {
action: 'open_url',
value: bot.matrix.client.mxcUrlToHttp(content.url)
}
}
} else if (message.startsWith(bot.matrix.commandPrefix)) {
const source = new CommandSource({ bot, permissionLevel, sendFeedback, displayName: senderText })
bot.commands.execute(message.substring(bot.matrix.commandPrefix.length), source)
return
}
bot.fancyMsg(matrixPrefix, senderText, message) bot.fancyMsg(matrixPrefix, senderText, message)
}) })

View file

@ -27,9 +27,9 @@ function inject (bot) {
setInterval(() => { setInterval(() => {
if (!bot.music.playing) return if (!bot.music.playing) return
const msg = [ const msg = [
{ text: 'Now Playing', color: bot.colors.primary }, { text: 'Now Playing', ...bot.styles.primary },
{ text: ' | ', color: 'dark_gray' }, { text: ' | ', color: 'dark_gray' },
{ text: bot.music.nowPlaying.name, color: bot.colors.secondary, bold: true }, { text: bot.music.nowPlaying.name, ...bot.styles.secondary, bold: true },
{ text: ' | ', color: 'dark_gray' }, { text: ' | ', color: 'dark_gray' },
format(bot.music.nowPlaying.time), format(bot.music.nowPlaying.time),
{ text: ' / ', color: 'gray' }, { text: ' / ', color: 'gray' },
@ -40,7 +40,7 @@ function inject (bot) {
] ]
if (bot.music.looping) { if (bot.music.looping) {
msg.push({ text: ' | ', color: 'dark_gray' }) msg.push({ text: ' | ', color: 'dark_gray' })
msg.push({ text: 'Looping', color: bot.colors.secondary }) msg.push({ text: 'Looping', ...bot.styles.secondary })
} }
bot.core.run('/title @a actionbar ' + JSON.stringify(msg)) bot.core.run('/title @a actionbar ' + JSON.stringify(msg))
}, 500) }, 500)
@ -72,14 +72,14 @@ function inject (bot) {
song.time = 0 song.time = 0
bot.music.nowPlaying = song bot.music.nowPlaying = song
} catch (err) { } catch (err) {
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: err.message, color: bot.colors.error })) bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: err.message, ...bot.styles.error }))
return return
} }
// play the music lol // play the music lol
bot.core.run(`/tellraw @a ${JSON.stringify([ bot.core.run(`/tellraw @a ${JSON.stringify([
{ text: 'Now playing ', color: bot.colors.primary }, { text: 'Now playing ', ...bot.styles.primary },
{ text: song.name, color: bot.colors.secondary }, { text: song.name, ...bot.styles.secondary },
'.' '.'
])}`) ])}`)
bot.music.playing = true bot.music.playing = true

View file

@ -27,8 +27,8 @@ function inject (bot) {
if (seen[player.username].first == null) { if (seen[player.username].first == null) {
seen[player.username].first = new Date() seen[player.username].first = new Date()
bot.core.run('minecraft:tellraw @a ' + JSON.stringify([ bot.core.run('minecraft:tellraw @a ' + JSON.stringify([
{ text: 'Welcome ', color: bot.colors.primary }, { text: 'Welcome ', ...bot.styles.primary },
{ text: player.username, color: bot.colors.secondary }, { text: player.username, ...bot.styles.secondary },
' to the server!' ' to the server!'
])) ]))
} }

View file

@ -1,9 +0,0 @@
function inject (bot) {
function tellraw (text, target = '@a') {
bot.core.run(`minecraft:tellraw ${target} ${JSON.stringify(text)}`)
}
bot.tellraw = tellraw
}
module.exports = inject

30
plugins/utility.js Normal file
View file

@ -0,0 +1,30 @@
const fs = require('fs/promises')
function inject (bot) {
function tellraw (text, target = '@a') {
bot.core.run(`minecraft:tellraw ${target} ${JSON.stringify(text)}`)
}
async function listFiles (filepath, styling = {}) {
const list = await fs.readdir(filepath)
const msg = []
list.forEach((filename, idx) => {
if (idx !== 0) msg.push(' ')
const highlighting = !(idx & 1) ? bot.styles.secondary : bot.styles.primary
msg.push({
text: filename,
...highlighting,
...styling
})
})
return msg
}
bot.tellraw = tellraw
bot.listFiles = listFiles
}
module.exports = inject

0
songs/file 1 Normal file
View file

0
songs/file 2 Normal file
View file

0
songs/file 3 Normal file
View file

0
songs/file 4 Normal file
View file

0
songs/file 5 Normal file
View file

0
songs/file 6 Normal file
View file

0
songs/file 7 Normal file
View file

0
songs/file 8 Normal file
View file

0
songs/file 9 Normal file
View file

View file

@ -51,7 +51,7 @@ function htmlStringify (text, { lang = {} } = {}) {
if (text.color) { if (text.color) {
const rgb = text.color[0] === '#' ? parseInt(text.color.substring(1), 16) : colormap[text.color] const rgb = text.color[0] === '#' ? parseInt(text.color.substring(1), 16) : colormap[text.color]
if (rgb) string = `<font color="${rgb.toString(16).padStart(6, '0')}">${string}</font>` if (rgb) string = `<font color="#${rgb.toString(16).padStart(6, '0')}">${string}</font>`
} }
// formatting // formatting
@ -80,7 +80,7 @@ function preprocessText (input) {
const hex = colorcodemap[code] const hex = colorcodemap[code]
if (hex) { if (hex) {
string += closing string += closing
string += `<font color="${hex}">` string += `<font color="${hex.toString(16).padStart(6, '0')}">`
closing = '</font>' closing = '</font>'
continue continue
} }

View file

@ -4,14 +4,23 @@ const formatNames = Object.fromEntries(formatting.map(format => [format.name, tr
const baseColors = colors.map(color => intToRgb(color.rgb)) const baseColors = colors.map(color => intToRgb(color.rgb))
function parseJsonText (json) { function parseJsonText (json) {
return JSON.parse(json) try {
return JSON.parse(json)
} catch {
return { text: '' }
}
} }
function parseNbtText (data) { function parseNbtText (data) {
if (typeof data.value !== 'object') return data.value try {
if (Array.isArray(data.value)) return [...data.value] if (data.type === 'byte') return !!data.value
if (data.type === 'list') return data.value.value.map(value => parseNbtText({ value })) if (typeof data.value !== 'object') return data.value
return Object.fromEntries(Object.entries(data.value).map(([key, value]) => ([key === '' ? 'text' : key, parseNbtText(value)]))) if (Array.isArray(data.value)) return [...data.value]
if (data.type === 'list') return data.value.value.map(value => parseNbtText({ type: data.value.type, value }))
return Object.fromEntries(Object.entries(data.value).map(([key, value]) => ([key === '' ? 'text' : key, parseNbtText(value)])))
} catch {
return { text: '' }
}
} }
function normalize (text) { function normalize (text) {

View file

@ -0,0 +1,48 @@
const { ArgumentType } = require('brigadier-commands')
const EXAMPLES = [123, -0.2, 'Hello, world!', true, false, { text: 'Hello, world!' }].map(v => JSON.stringify(v))
class JSONArgumentType extends ArgumentType {
static json () {
return new JSONArgumentType()
}
parse (reader) {
let string = ''
let depth = 0
let stringOpened = false
while (reader.canRead() && (depth !== 0 || reader.peek() !== ' ')) {
const c = reader.peek()
if (c === '\\') {
// Skip over escapes
let len = 2
if (reader.string[reader.cursor + 1] === 'u') len += 4
len = Math.max(len, reader.string.length - reader.cursor)
string += reader.string.substring(reader.cursor, reader.cursor + len)
reader.cursor += len
continue
}
if (c === '[' || c === '{') depth++
else if (c === ']' || c === '}') depth--
else if (c === '"') {
depth += stringOpened ? -1 : 1
stringOpened = !stringOpened
}
string += c
reader.skip()
}
return JSON.parse(string)
}
getExamples () {
return EXAMPLES
}
}
module.exports = JSONArgumentType

View file

@ -0,0 +1,63 @@
const { ArgumentType, SimpleCommandExceptionType, LiteralMessage } = require('brigadier-commands')
const path = require('path/posix')
const EXAMPLES = ['songs/amogus.mid', 'images/cat.jpg', 'videos/badapple.mp4']
const INVALID_URL_EXCEPTION = new SimpleCommandExceptionType(new LiteralMessage('Invalid URL'))
class LocationArgumentType extends ArgumentType {
constructor (allowPaths, allowUrls, root = '') {
super()
this.allowPaths = allowPaths
this.allowUrls = allowUrls
this.root = root
}
static location (root = '') { return new LocationArgumentType(true, true, root) }
static path (root = '') { return new LocationArgumentType(true, false, root) }
static url () { return new LocationArgumentType(false, true) }
parse (reader) {
const string = reader.readString()
if (this.allowUrls) {
if (string.startsWith('http://') || string.startsWith('https://')) {
return string
}
// TODO: Maybe support data URLs?
}
if (!this.allowPaths) {
throw INVALID_URL_EXCEPTION.create()
}
const root = path.resolve(this.root)
const splitPath = string.split(path.sep)
const sanitizedSplitPath = []
let depth = 0
for (const part of splitPath) {
if (part === '' || part === '.') continue
if (part === '..') {
if ((depth - 1) < 0) continue // Do not allow escaping the root directory
depth--
} else {
depth++
}
sanitizedSplitPath.push(part)
}
return [root, ...sanitizedSplitPath].join(path.sep)
}
static isUrl (string) {
return string.startsWith('http://') || string.startsWith('https://')
}
getExamples () {
return EXAMPLES
}
}
module.exports = LocationArgumentType

View file

@ -1,13 +1,14 @@
const { exceptions: { SimpleCommandExceptionType }, LiteralMessage } = require('brigadier-commands') const { SimpleCommandExceptionType, LiteralMessage } = require('brigadier-commands')
const COMMAND_MUST_BE_EXECUTED_BY_A_PLAYER_EXCEPTION = new SimpleCommandExceptionType(new LiteralMessage('Command must be executed by a player')) // TODO: Translations const COMMAND_MUST_BE_EXECUTED_BY_A_PLAYER_EXCEPTION = new SimpleCommandExceptionType(new LiteralMessage('Command must be executed by a player')) // TODO: Translations
class CommandSource { class CommandSource {
constructor ({ bot, player = null, permissionLevel = 0, sendFeedback = () => {} } = {}) { constructor ({ bot, player = null, permissionLevel = 0, sendFeedback = () => {}, displayName = { text: '' } } = {}) {
this.bot = bot this.bot = bot
this.player = player this.player = player
this.permissionLevel = permissionLevel this.permissionLevel = permissionLevel
this.sendFeedback = sendFeedback this.sendFeedback = sendFeedback
this.displayName = displayName
} }
sendError (error) { sendError (error) {

26
util/command/utility.js Normal file
View file

@ -0,0 +1,26 @@
const nbt = require('prismarine-nbt')
const snbt = require('../snbt.js')
const toNbtUuid = require('../uuid-to-nbt-uuid.js')
function escapeString (input) {
let string = '"'
for (let i = 0; i < input.length; i++) {
const c = input[i]
if (c === '\\' || c === '"') string += '\\' + c
else string += c
}
string += '"'
return string
}
function createNameSelector (username) {
return `@a[limit=1,name=${escapeString(username)}]`
}
function createUuidSelector (uuid) {
return `@a[limit=1,nbt=${snbt.stringify(nbt.comp({ UUID: toNbtUuid(uuid) }))}]`
}
module.exports = { escapeString, createNameSelector, createUuidSelector }

View file

@ -6,7 +6,7 @@ const colorsys = require('colorsys')
async function convertImage (src, callback) { async function convertImage (src, callback) {
const img = await canvas.loadImage(src).catch(callback) const img = await canvas.loadImage(src).catch(callback)
if (!(img ?? false)) return if (!img) return
ctx.drawImage(img, 0, 0, cnv.width, cnv.height) ctx.drawImage(img, 0, 0, cnv.width, cnv.height)
const rgba = ctx.getImageData(0, 0, cnv.width, cnv.height).data const rgba = ctx.getImageData(0, 0, cnv.width, cnv.height).data
const lines = [] const lines = []