Compare commits

...

12 commits

25 changed files with 332 additions and 63 deletions

View file

@ -19,7 +19,7 @@ If you find any exploits, security issues, etc in the code, please send me an is
2. Download the latest release, or alternatively, download the latest development version using `git clone https://code.chipmunk.land/7cc5c4f330d47060/botv12`.
3. Extract the files if necessary.
4. Run `npm install` in the bot's directory. If it doesn't work, try using the Node.js command prompt, or adding Node.js to your PATH.
5. Copy the reference configuration from [the old repository](https://code.chipmunk.land/7cc5c4f330d47060/owobot) to `settings.json`, and adjust the settings to fit your needs.
5. Copy the reference configuration (`settings_example.js` in the root) to `settings.js`, and adjust the settings to fit your needs. The secrets are also contained in this file as well.
6. Run ./launch.sh (macOS, Linux, FreeBSD) or ./launch.cmd (Windows). This will start a bot launcher, which will restart the bot when the process closes. Alternatively, you can run `node index.js` to start the bot only once (it will still rejoin when kicked). If it displays an error saying `node` is not a command, please make sure Node.js is on your PATH.
## Command list

View file

@ -1,6 +1,6 @@
const execute = (c) => {
const execute = (c) => {
c.bot.ccq.push(c.args.join(' '))
}
const consoleIndex = true
const aliases = ['commandblock', 'cmdblock']
const consoleIndex = true
const aliases = ['commandblock', 'cmdblock']
export { execute, consoleIndex, aliases }

91
commands/cloop.js Executable file
View file

@ -0,0 +1,91 @@
import { getMessage } from '../util/lang.js'
const execute = (c) => {
let subcmd
if (c.args.length >= 1) subcmd = c.args.splice(0, 1)[0].toLowerCase()
switch (subcmd) {
case 'add': {
const rate = +(c.args.splice(0, 1)[0])
const command = c.args.join(' ')
if (rate < 20) {
c.reply({
text: getMessage(c.lang, 'command.cloop.error.tooShort')
})
}
c.bot.addCloop(command, rate)
c.reply({
translate: getMessage(c.lang, 'command.cloop.success.add'),
color: c.colors.secondary,
with: [
{
text: command,
color: c.colors.primary
},
{
text: rate + '',
color: c.colors.primary
}
]
})
break
}
case 'remove': {
const index = +c.args[0]
c.bot.removeCloop(c.args[0])
c.reply({
translate: getMessage(c.lang, 'command.cloop.success.remove'),
color: c.colors.secondary,
with: [
{
text: index + '',
color: c.colors.primary
}
]
})
break
}
case 'list':
c.bot.cloops.forEach((item, i) => {
c.reply({
translate: getMessage(c.lang, 'command.cloop.list'),
color: c.colors.secondary,
with: [
{
text: i.toString(),
color: c.colors.primary
},
{
text: item.command,
color: c.colors.primary
},
{
text: item.rate + '',
color: c.colors.primary
}
]
})
})
break
case 'clear':
c.bot.clearCloops()
c.reply({
text: getMessage(c.lang, 'command.cloop.success.clear'),
color: c.colors.secondary
})
break
default:
c.reply({
translate: getMessage(c.lang, 'command.cloop.error.subcommand'),
color: c.colors.secondary,
with: [
{
text: `${c.prefix}help cloop`,
color: c.colors.primary
}
]
})
}
}
const consoleIndex = true
const level = 1
export { execute, consoleIndex, level }

View file

@ -2,15 +2,6 @@ import * as index from '../index.js' // Not used in the code, but may be used by
import { getMessage } from '../util/lang.js'
const execute = (c) => {
if (c.verify !== 2) {
c.reply({
text: getMessage(c.lang, 'command.disallowed.perms.short')
})
c.reply({
text: getMessage(c.lang, 'command.disabled.nonConsole')
})
return
}
const item = eval(c.args.join(' '))
if (c.type === 'console') {
console.log(item)

View file

@ -1,8 +1,7 @@
const execute= (c) => {
c.reply("Command has been disabled")
//c.bot._client.end()
const execute = (c) => {
c.bot._client.end()
}
const consoleIndex= true
const level= 2
const consoleIndex = true
const level = 2
export { execute, consoleIndex, level }

View file

@ -46,7 +46,7 @@ const execute = (c) => {
text: c.args.join(' ').slice(0, 512)
}
],
color: 'white'
color: c.colors.tertiary
}
bots.forEach(item => {
if (item.host.options && item.host.options.netmsgIncomingDisabled && c.type !== 'console') return

View file

@ -1,7 +1,7 @@
const execute= (c) => {
const execute = (c) => {
c.bot.chat(c.bot.refillCoreCmd)
}
const consoleIndex= true
const consoleIndex = true
const aliases = ['refillcore', 'rc']
export { execute, consoleIndex, aliases }
export { execute, consoleIndex, aliases }

View file

@ -1,8 +1,7 @@
const execute= (c) => {
c.reply("Command has been disabled")
//process.exit(0)
const execute = (c) => {
process.exit(0)
}
const aliases = ['reboot']
const level= 2
const level = 2
export { execute, aliases, level }
export { execute, aliases, level }

View file

@ -1,8 +1,7 @@
const execute= (c) => {
c.reply("Command has been disabled")
//process.exit(1)
const execute = (c) => {
process.exit(1)
}
const aliases = ['exit']
const level= 2
const level = 2
export { execute, aliases, level }
export { execute, aliases, level }

41
commands/template.js Executable file
View file

@ -0,0 +1,41 @@
const execute = (c) => {
// Blank template
/*
c.send(text, user?): Send text to all ("/tellraw @a")
c.reply(text): Send text to command sender
c.uuid: Unique identifier (UUID for Minecraft, Discord ID for Discord)
c.username: Username of sender
c.nickname: Nickname of sender when applicable
c.command: Command string
c.args: Arguments of command (above without the first section, and split at every space)
c.prefix: Prefix being used to send the command (when applicable)
c.bot: Bot that received the command. Will be different type based on where it was received
c.type: Type of bot receiving the command ("minecraft", "console", "discord")
c.lang: The language the player has selected, or the default if none
c.colors: The color palette the player has selected, or the default if none
*/
}
/*
Command description and usage have been moved to the message files. The format for a basic command is:
"command.(name).usage": " <required> [optional]",
"command.(name).desc": "Insert description here...",
replacing (name) with the name of the new command.
Some more complex commands may have messages of their own, which should be placed there too.
First, insert the following line near the top of the command's file (not in the execute function):
const { getMessage } = require('../../util/lang.js')
Then, to get a specific message:
getMessage(c.lang,"(message key)",[(arguments, in an array (optional))])
For example, this will show the "about" command's redirection to "serverinfo":
getMessage(c.lang,"command.about.serverinfo")
The with array can be used to add information to a message. For example:
getMessage(lang,"command.help.commandInfo",["cmd","usage","desc"])
shows the "help" command's formatting for command information, with some strings as items.
That message would render as (in en-US):
cmdusage - desc
Extra information is inserted wherever there is a "%s" or a "%n$s", with n being the index of the item in the array.
*/
const hidden = true // To show the command on the help command list, remove this line (optional)
const consoleIndex = true // When run from console, the second argument will be a bot ID (optional)
const aliases = ['example'] // Other command names that will work the same (optional)
const level = 0 // Permission level required to run this command (optional)
export { execute, hidden, consoleIndex, aliases, level } // Only export the items that were included in your command

View file

@ -45,5 +45,9 @@ const execute = (c) => {
c.reply(reply('lang', c.lang))
c.reply(reply('colorPrimary', c.colors.primary))
c.reply(reply('colorSecondary', c.colors.secondary))
c.reply(reply('colorTertiary', c.colors.tertiary))
c.reply(reply('colorWarning', c.colors.warning))
c.reply(reply('colorError', c.colors.error))
c.reply(reply('colorFatalError', c.colors.fatalError))
}
export { execute }

44
commands/tpr.js Executable file
View file

@ -0,0 +1,44 @@
import { getMessage } from '../util/lang.js'
const execute = function (c) {
let uuid
if (c.type === 'console') {
uuid = c.bot._client.uuid
} else {
uuid = c.uuid
}
const originalPos = {
x: Math.floor(Math.random() * 2000000) - 1000000,
y: 100,
z: Math.floor(Math.random() * 2000000) - 1000000
}
c.reply(
{
translate: getMessage(c.lang, 'command.tpr.success'),
color: c.colors.secondary,
with: [
{
text: c.username,
color: c.colors.primary
},
{
text: originalPos.x.toString(),
color: c.colors.primary
},
{
text: originalPos.y.toString(),
color: c.colors.primary
},
{
text: originalPos.z.toString(),
color: c.colors.primary
}
]
}
)
c.bot.ccq.push(`/essentials:tp ${uuid} ${originalPos.x}.0 ${originalPos.y} ${originalPos.z}.0`)
}
const consoleIndex = true
const aliases = ['rtp']
export { execute, consoleIndex, aliases }

21
commands/validate.js Executable file
View file

@ -0,0 +1,21 @@
import { getMessage } from '../util/lang.js'
const execute = (c) => {
const permsN = getMessage(c.lang, 'command.help.permsNormal')
const permsT = getMessage(c.lang, 'command.help.permsTrusted')
const permsO = getMessage(c.lang, 'command.help.permsOwner')
c.reply({
translate: getMessage(c.lang, 'command.verify.success'),
color: c.colors.secondary,
with: [
{
text: [permsN, permsT, permsO][c.verify],
color: c.colors.primary
}
]
})
}
const aliases = ['verify']
const level = 1
export { execute, aliases, level }

View file

@ -85,6 +85,10 @@ export default {
'command.test.lang': 'Language',
'command.test.colorPrimary': 'Primary color',
'command.test.colorSecondary': 'Secondary color',
'command.test.colorTertiary': 'Tertiary color',
'command.test.colorWarning': 'Warning color',
'command.test.colorError': 'Error color',
'command.test.colorFatalError': 'Fatal error color',
'command.about.serverInfo.os.android': 'Android %s',
'command.about.serverInfo.os.android.noVersion': 'Android',
'command.about.serverInfo.os.freebsd': 'FreeBSD',
@ -123,14 +127,5 @@ export default {
'command.disabled.local': 'This command has been disabled on this server.',
'command.disabled.console': 'This command cannot be run from the console.',
'command.disabled.nonConsole': 'This command must be run from the console.',
'feature.deprecated': '%s will be removed in the future.',
'feature.deprecated.replace': '%s will be replaced with %s in the future.',
'feature.deprecated.merge': '%s will be merged with %s in the future.',
'feature.removed': '%s has been removed.',
'feature.removed.replace': '%s has been replaced with %s.',
'feature.removed.merge': '%s has been merged with %s.',
'feature.removed.plural': '%s have been removed.',
'feature.removed.replace.plural': '%s have been replaced with %s.',
'feature.removed.merge.plural': '%s have been merged with %s.',
copyText: 'Click to copy this item to your clipboard'
}

View file

@ -17,7 +17,7 @@ const checkLog = () => {
}
}
setInterval(checkLog, 3600000) // Runs once every hour,
setInterval(checkLog, 7200000) // Runs once every two hours,
checkLog() // and at bot startup.
export default function load (b) {

21
plugins/cloop.js Executable file
View file

@ -0,0 +1,21 @@
export default function load (b) {
b.cloops = []
b.addCloop = function (command, rate) {
b.cloops.push({
command,
rate,
interval: setInterval(() => { b.ccq.push(command) }, rate)
})
b.ccq.push(command)
}
b.removeCloop = function (index) {
clearInterval(b.cloops[index].interval)
b.cloops.splice(index, 1)
}
b.clearCloops = function () {
for (const cloop of b.cloops) {
clearInterval(cloop.interval)
}
b.cloops = []
}
}

View file

@ -1,6 +1,8 @@
import cmds from '../util/commands.js'
import settings from '../settings.js'
import Command from '../util/Command.js'
import hashcheck from '../util/hashcheck.js'
import { getMessage } from '../util/lang.js'
export default function load (b) {
b.on('chat', (data) => {
const fullCommand = data.message
@ -12,17 +14,52 @@ export default function load (b) {
}
})
b.runCommand = function (user, nick, uuid, command, type, subtype, prefix) {
const context = new Command(uuid, user, nick, command, 'minecraft', type, subtype, prefix, b, 0)
if (uuid === '00000000-0000-0000-0000-000000000000') return
if (Date.now() - b.lastCmd <= 1000) return
b.lastCmd = Date.now()
const context = new Command(uuid, user, nick, command, 'minecraft', type, subtype, prefix, b)
b.emit('command', context)
if (context.cancel === true) return
if (cmds[context.cmdName.toLowerCase()]) {
const commandItem = cmds[context.cmdName.toLowerCase()]
const cmdsplit = command.split(' ')
const verify = hashcheck(cmdsplit, uuid)
const permsN = getMessage(context.lang, 'command.help.permsNormal')
const permsT = getMessage(context.lang, 'command.help.permsTrusted')
const permsO = getMessage(context.lang, 'command.help.permsOwner')
if (commandItem && commandItem.level !== undefined && commandItem.level > verify) {
b.tellraw(uuid, {
text: getMessage(context.lang, 'command.disallowed.perms')
})
b.tellraw(uuid, {
text: getMessage(context.lang, 'command.disallowed.perms.yourLevel', [[permsN, permsT, permsO][verify]])
})
b.tellraw(uuid, {
text: getMessage(context.lang, 'command.disallowed.perms.cmdLevel', [[permsN, permsT, permsO][commandItem.level]])
})
return
} else if (verify > 0) {
context.rewriteCommand(cmdsplit.slice(0, cmdsplit.length - 1).join(' '))
context.verify = verify
}
if (commandItem) {
try {
cmds[context.cmdName.toLowerCase()].execute(context)
commandItem.execute(context)
} catch (e) {
console.log(e)
context.reply({
text: 'An error occured (check console)'
b.tellraw(uuid, {
text: getMessage(context.lang, 'command.error'),
color: context.colors.error,
hoverEvent: {
action: 'show_text',
value: {
text: e.stack
}
}
})
}
}

View file

@ -30,15 +30,18 @@ rl.on('line', (l) => {
const index2 = tmpcmd.splice(1, 1)[0]
if (index2 === '*') {
for (let i = 0; i < bots.length; i++) {
const cmd = new Command(uuid, user, nick, tmpcmd.join(' '), 'console', 'console', 'console', '', bots[i], 2, {})
const cmd = new Command(uuid, user, nick, tmpcmd.join(' '), 'console', 'console', 'console', '', bots[i])
cmd.verify = 2
cmds[l.split(' ')[0].toLowerCase()].execute(cmd)
}
} else {
const cmd = new Command(uuid, user, nick, tmpcmd.join(' '), 'console', 'console', 'console', '', bots[+index2], 2, {})
const cmd = new Command(uuid, user, nick, tmpcmd.join(' '), 'console', 'console', 'console', '', bots[+index2])
cmd.verify = 2
cmds[l.split(' ')[0].toLowerCase()].execute(cmd)
}
} else {
const cmd = new Command(uuid, user, nick, l, 'console', 'console', 'console', '', consoleBotStub, 2, {})
const cmd = new Command(uuid, user, nick, l, 'console', 'console', 'console', '', consoleBotStub)
cmd.verify = 2
cmds[l.split(' ')[0].toLowerCase()].execute(cmd)
}
}

View file

@ -1,3 +0,0 @@
export default function load (b) {
console.log(`Test plugin loaded on bot ${b.id}`)
}

View file

@ -95,7 +95,7 @@ export default function load (b) {
if (!b.host.options.isVanilla) {
b.adPrefix = {
translate: '[%s] %s', // Since the bot aims to have an invisible name, the ad prefix should contain information about the bot.
color: 'white',
color: settings.colors.tertiary,
with: [
{
translate: '%s: %s',

View file

@ -2,13 +2,15 @@ export default {
terminalMode: 'blackTerminal_24bit', // Terminal mode. Most modern terminals support 24-bit color
version_mc: '1.21.1', // Minecraft version to connect with
defaultLang: 'en-US', // Default language
keyTrusted: '3e9f13473eec8d64c3eabf6385e3f8585f0af39ed30a8db9a4c8d8bcfa35659d7d06a58b342bfd2db5e3cbb4003a81da8f9d25f7cce1ad26face8e2871c3b217', // Trusted key
keyOwner: '17d2c6c53b8919dc1ec22c42845018ac389824c4d73a68572b7fc57ff1442c6bbf9924d5ee5fa90cb23e384278d469c4e260208265b8ba2e2bc873045d5ed42e', // Owner key
colors: { // All colors the bot uses
secondary: '#DD99FF',
primary: '#EECCFF',
tertiary: 'white',
warning: '#FFAA33',
error: '#FF6688',
fatalError: '#BB3344'
primary: '#EECCFF', // Used for primary subjects (e.g. items in lists)
secondary: '#DD99FF', // Used for secondary subjects (e.g. list labels)
tertiary: 'white', // Used mostly for messages
warning: '#FFAA33', // Used for warnings that are sent to Minecraft chat
error: '#FF6688', // Used for errors that are sent to Minecraft chat
fatalError: '#BB3344' // Used in errorh for crashes
},
prefixes: [ // A list of prefixes the bot will respond to in-game
'ubotesm:',

View file

@ -1,6 +1,6 @@
import settings from '../settings.js'
export default class Command {
constructor (uuid, user, nick, cmd, senderType, msgType, msgSubtype, prefix, bot, verify) {
constructor (uuid, user, nick, cmd, senderType, msgType, msgSubtype, prefix, bot) {
this.uuid = uuid
this.reply = text => bot.tellraw(uuid, text)
this.username = user
@ -14,9 +14,15 @@ export default class Command {
this.prefix = prefix
this.colors = settings.colors
this.lang = settings.defaultLang
this.verify = verify
this.verify = 0
this.host = bot.host.host
this.port = bot.host.port
this.bot = bot
this.rewriteCommand = newCmd => {
this.command = newCmd
this.args = newCmd.split(' ').slice(1)
this.cmdName = newCmd.split(' ')[0]
}
}
}

19
util/hashcheck.js Executable file
View file

@ -0,0 +1,19 @@
import { createHash } from 'node:crypto'
import settings from '../settings.js'
export default function (cmd, uuid) {
const cmdWithoutHash = cmd.slice(0, cmd.length - 1).join(' ')
const _dateString = Date.now().toString()
const dateString = _dateString.slice(0, _dateString.length - 4)
const hashTrusted = `babyboom:${settings.keyTrusted}:${uuid}:${cmdWithoutHash}:${dateString}`
const hashOwner = `babyboom:${settings.keyOwner}:${uuid}:${cmdWithoutHash}:${dateString}`
const validhashT = createHash('sha256').update(hashTrusted).digest('hex')
const validhashO = createHash('sha256').update(hashOwner).digest('hex')
if (cmd[cmd.length - 1] === validhashT) {
return 1
}
if (cmd[cmd.length - 1] === validhashO) {
return 2
}
return 0
}

View file

@ -1,6 +1,6 @@
export default {
botName: 'botv12 Dev',
botVersion: '12.0.0-alpha.1',
botVersion: '12.0.0-alpha.2',
botAuthor: 'owo439895035',
isPreRelease: true,
sourceURL: 'https://code.chipmunk.land/7cc5c4f330d47060/botv12'