This commit is contained in:
u0_a342 2024-08-26 22:32:29 -04:00
commit af05ff16e9
36 changed files with 960 additions and 394 deletions

10
.gitignore vendored
View file

@ -153,4 +153,12 @@ dist
settings.json
# Default secret file
secret.json
secret.json
# botvX user settings
userPref/
# botvX log files
UBotLogs/
botvXLogs/
logs/

View file

@ -2,17 +2,13 @@
## What is it?
botvX is a Minecraft bot for [Kaboom](https://kaboom.pw/) and its clones. It has many of the features that you would expect in a modern Kaboom bot:
botvX is a Minecraft bot originally designed for [Kaboom](https://kaboom.pw/) and its clones. It has many of the features that you would expect in a modern Kaboom bot:
- commands (obviously)
- a self care system
- a command core, to run commands quickly
- a hashing system, to enable trusted users to securely run certain commands in chat
## What does "botvX" mean?
"botvX" means "bot version 10". The v is used to signify that whatever after it is a version, as was done with previous versions (botv4, botv6, botv8, botv9), and the X is the Roman numeral for 10, since this is the 10th major version.
## How to install?
1. Install [Node.js](https://nodejs.org/) for your operating system.

View file

@ -4,8 +4,9 @@ const { getMessage, formatTime } = require('../util/lang.js')
const fs = require('fs')
const botVersion = require('../util/version.js')
const version = require('../version.json')
const index = require('../index.js')
const aboutBot = function (c){
const aboutBot = function (c) {
c.reply({
translate: getMessage(c.lang, 'command.about.author'),
color: c.colors.secondary,
@ -55,34 +56,34 @@ const aboutBot = function (c){
const os2 = function (o2, l) {
switch (o2) {
case 'win32':
return `${os.version()} (${os.release})`
return `${os.version()}`
case 'android':{
try {
const version = cp.execSync('getprop ro.build.version.release').toString('UTF-8').split('\n')[0]
return getMessage(l, 'command.about.serverInfo.os.android', [version])
} catch(e){
getMessage(l, 'command.about.serverInfo.os.android.noVersion')
} catch (e) {
return getMessage(l, 'command.about.serverInfo.os.android.noVersion')
}
}
case 'linux':
case 'freebsd':{
if(fs.readdirSync("/etc").includes("os-release")){
if (fs.readdirSync('/etc').includes('os-release')) {
const osrelease = fs.readFileSync('/etc/os-release').toString('UTF-8').split('\n')
const osrelease2 = {}
for (const i in osrelease) {
if (!osrelease[i].includes('=')) continue
let osrvalue = osrelease[i].split('=')[1]
for (const item of osrelease) {
if (!item.includes('=')) continue
let osrvalue = item.split('=')[1]
if (osrvalue.startsWith('"') && osrvalue.endsWith('"')) { osrvalue = osrvalue.slice(1, osrvalue.length - 1) };
osrelease2[osrelease[i].split('=')[0]] = osrvalue
osrelease2[item.split('=')[0]] = osrvalue
}
if (osrelease2.PRETTY_NAME) {
return getMessage(l, '%s %s', [osrelease2.PRETTY_NAME, os.release()])
return getMessage(l, '%s', [osrelease2.PRETTY_NAME])
} else {
return getMessage(l, `command.about.serverInfo.os.${o2}`, [os.release()])
return getMessage(l, `command.about.serverInfo.os.${o2}`)
}
} else {
return getMessage(l, `command.about.serverInfo.os.${o2}`, [os.release()])
return getMessage(l, `command.about.serverInfo.os.${o2}`)
}
}
default:
@ -90,23 +91,41 @@ const os2 = function (o2, l) {
}
}
const aboutServer = function (c){
const aboutServer = function (c) {
const displayInfo = function (name, infoFunc) {
let thisItem;
let thisItem
try {
thisItem = infoFunc()
} catch(e) {
} catch (e) {
console.error(e)
thisItem = "Error! (check console)"
thisItem = 'Error! (check console)'
}
c.reply({
translate: '%s: %s',
with:[
color: c.colors.primary,
with: [
{
text: getMessage(c.lang, name)
text: getMessage(c.lang, name),
color: c.colors.secondary
},
{
text: thisItem
text: thisItem,
color: c.colors.primary,
clickEvent: {
action: 'copy_to_clipboard',
value: thisItem
},
hoverEvent: {
action: 'show_text',
contents: {
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
},
value: { // Added twice for backwards compatibility
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
}
}
}
]
})
@ -117,14 +136,19 @@ const aboutServer = function (c){
return os2(process.platform, c.lang)
})
// Kernel version: os.release()
displayInfo('command.about.serverInfo.kernelVer', () => {
return os.release()
})
// Processor
if (os.cpus()[0]){
if (os.cpus()[0]) {
displayInfo('command.about.serverInfo.processor', () => {
return os.cpus()[0].model
})
}
if (os.cpus()[0]){
if (os.cpus()[0]) {
// Processor architecture
displayInfo('command.about.serverInfo.arch', () => {
return os.machine()
@ -140,12 +164,12 @@ const aboutServer = function (c){
displayInfo('command.about.serverInfo.hostName', () => {
return os.hostname()
})
// Current working directory
displayInfo('command.about.serverInfo.workingDir', () => {
return process.cwd()
})
// Node.js® version
displayInfo('command.about.serverInfo.nodeVersion', () => {
return process.version
@ -161,13 +185,12 @@ const aboutServer = function (c){
return formatTime(os.uptime() * 1000, c.lang)
})
if (process.platform === 'android') {
// Device model
displayInfo('command.about.serverInfo.os.android.model', () => {
const dModel = cp.execSync('getprop ro.product.model').toString('UTF-8').split('\n')[0]
const dBrand = cp.execSync('getprop ro.product.brand').toString('UTF-8').split('\n')[0]
return `${dBrand} ${dModel}`
const brand = cp.execSync('getprop ro.product.brand').toString('UTF-8').split('\n')[0]
const model = cp.execSync('getprop ro.product.model').toString('UTF-8').split('\n')[0]
return `${brand} ${model}`
})
}
@ -175,15 +198,58 @@ const aboutServer = function (c){
displayInfo('command.about.serverInfo.botVer', () => {
return botVersion
})
}
const displayServerList = function (c) {
index.bot.forEach((item, i) => {
if (item.host.options && item.host.options.hidden && c.verify !== 3 && c.bot.id !== i) return
let message = 'command.about.serverListItem'
if (c.bot.id === i) message = 'command.about.serverListItem.thisServer'
c.reply({
translate: getMessage(c.lang, message),
color: c.colors.secondary,
with: [
{
text: i.toString(),
color: c.colors.primary
},
{
text: `${item.host.host}:${item.host.port}`,
color: c.colors.primary,
clickEvent: {
action: 'copy_to_clipboard',
value: `${item.host.host}:${item.host.port}`
},
hoverEvent: {
action: 'show_text',
contents: {
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
},
value: { // Added twice for backwards compatibility
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
}
}
}
]
})
})
}
module.exports = {
execute: function (c) {
if(c.args[0] === 'server'){
let subcmd = c.args[0]
if (subcmd === 'servers') subcmd = 'serverlist'
if (c.cmdName === 'serverinfo') subcmd = 'server'
if (c.cmdName === 'serverlist' || c.cmdName === 'servers') subcmd = 'serverlist'
if (subcmd === 'server') {
aboutServer(c)
} else if (subcmd === 'serverlist') {
displayServerList(c)
} else {
aboutBot(c)
}
},
aliases: ['info']
aliases: ['info', 'serverlist', 'servers', 'serverinfo']
}

View file

@ -44,26 +44,26 @@ module.exports = {
break
}
case 'list':
for (const i in c.bot.cloops) {
c.bot.cloops.forEach((item, i) => {
c.reply({
translate: getMessage(c.lang, 'command.cloop.list'),
color: c.colors.secondary,
with: [
{
text: i,
text: i.toString(),
color: c.colors.primary
},
{
text: c.bot.cloops[i].command,
text: item.command,
color: c.colors.primary
},
{
text: c.bot.cloops[i].rate + '',
text: item.rate + '',
color: c.colors.primary
}
]
})
}
})
break
case 'clear':
c.bot.clearCloops()

View file

@ -9,14 +9,14 @@ const sortHelp = function sortHelp (c1, c2) {
}
const bpl = fs.readdirSync('./commands')
for (const i in bpl) { // Built-in loadCMD to the help command, to prevent circular require
if (!bpl[i].endsWith('.js')) {
for (const plugin of bpl) {
if (!plugin.endsWith('.js')) {
continue
}
try {
const commandName = bpl[i].split('.js')[0]
const commandName = plugin.split('.js')[0]
if (commandName !== 'help') {
cmds[commandName] = require(`./${bpl[i]}`)
cmds[commandName] = require(`./${plugin}`)
if (cmds[commandName].level === undefined) {
cmds[commandName].level = 0
}
@ -66,7 +66,7 @@ const printHelp = (c) => {
const printCmdHelp = (c) => {
const cmd = c.args[0]
if (!cmds[cmd]) {
if (!cmds[cmd] || (cmds[cmd].hidden && c.type !== 'console')) {
c.reply({ text: getMessage(c.lang, 'command.help.noCommand') })
return
}
@ -81,7 +81,7 @@ const printCmdHelp = (c) => {
if (cmds[cmd].alias) {
console.log(cmds[cmds[cmd].alias])
usage = getMessage(c.lang, `command.${cmds[cmd].alias}.usage`).split('||')
desc = getMessage(c.lang, `command.help.alias`, [cmds[cmd].alias])
desc = getMessage(c.lang, 'command.help.alias', [cmds[cmd].alias])
if (cmds[cmds[cmd].alias].usage) {
usage = cmds[cmds[cmd].alias].usage.split('||')
}
@ -89,7 +89,7 @@ const printCmdHelp = (c) => {
desc = cmds[cmds[cmd].alias].desc
}
}
for (const i in usage) {
for (const item of usage) {
c.reply({
translate: getMessage(c.lang, 'command.help.commandUsage'),
color: c.colors.secondary,
@ -99,7 +99,7 @@ const printCmdHelp = (c) => {
color: c.colors.primary
},
{
text: usage[i],
text: item,
color: c.colors.primary
}
]
@ -152,8 +152,8 @@ if (cmds.help.level === undefined) {
for (const i in cmds) {
if (cmds[i].aliases) {
for (const j in cmds[i].aliases) {
cmds[cmds[i].aliases[j]] = {
for (const alias of cmds[i].aliases) {
cmds[alias] = {
alias: i,
usage: cmds[i].usage,
level: cmds[i].level,

View file

@ -1,22 +1,40 @@
const { bot } = require('../index.js')
const { getMessage } = require('../util/lang.js')
module.exports = {
execute: (c) => {
let host = c.host
let port = c.port
if (c.bot.host.options && c.bot.host.options.hidden) {
host = 'localhost' // Makes hidden servers appear as localhost
port = '25565'
}
const json = {
translate: '[%s] %s: %s',
with: [
{
translate: '%s:%s',
with: [
{
text: c.host,
color: c.colors.primary
},
{
text: c.port + '',
color: c.colors.primary
text: c.serverName,
hoverEvent: {
action: 'show_text',
value: {
translate: '%s: %s:%s',
with: [
{
text: getMessage(c.lang, 'command.netmsg.serverAddress'),
color: c.colors.primary
},
{
text: host,
color: c.colors.primary
},
{
text: port + '',
color: c.colors.primary
}
],
color: c.colors.secondary
}
],
color: c.colors.secondary
},
color: c.colors.primary
},
{
text: c.username,
@ -28,8 +46,9 @@ module.exports = {
],
color: 'white'
}
for (const i in bot) {
bot[i].tellraw('@a', json)
}
bot.forEach(item => {
if (item.host.options && item.host.options.netmsgIncomingDisabled && c.type !== 'console') return
item.tellraw('@a', json)
})
}
}

155
commands/settings.js Normal file
View file

@ -0,0 +1,155 @@
const { languages, getMessage } = require('../util/lang.js')
const fs = require('fs')
const settings = require('../settings.json')
module.exports = {
execute: (c) => {
if (c.type === 'console') {
c.reply({
text: getMessage(c.lang, 'command.settings.disabled.console'),
color: c.colors.secondary
})
return
}
if (settings.userSettingsDisabled) {
c.reply({
text: getMessage(c.lang, 'command.settings.disabled.global'),
color: c.colors.secondary
})
return
}
const subcmd = c.args.splice(0, 1)[0]
switch (subcmd) {
case 'set':{
const allowedKeys = ['colorPrimary', 'colorSecondary', 'lang']
const key = c.args.splice(0, 1)[0]
if (!allowedKeys.includes(key)) {
c.reply({
text: getMessage(c.lang, 'command.settings.error.invalidKey'),
color: c.colors.secondary
})
return
}
const value = c.args.join(' ')
if (value === '' && key === 'lang') {
// Show all valid languages to user
for (const item of languages) {
c.reply({
translate: '%s (%s)',
color: c.colors.secondary,
with: [
{
text: getMessage(item, 'language.name'),
color: c.colors.primary
},
{
text: getMessage(item, 'language.region'),
color: c.colors.primary
}
],
hoverEvent: {
action: 'show_text',
value: {
translate: getMessage(item, 'command.settings.setLanguage'),
with: [
{
text: `${c.prefix}settings set lang ${item}`,
color: c.colors.secondary
}
]
}
}
})
}
return
}
if (value === '') {
c.reply({
text: getMessage(c.lang, 'command.settings.error.mustProvideValue'),
color: c.colors.secondary
})
return
}
if (key === 'lang' && !languages.includes(value)) {
c.reply({
text: getMessage(c.lang, 'command.settings.error.invalidLanguage'),
color: c.colors.secondary
})
return
}
c.prefs[key] = value
// Save to file
fs.writeFileSync(`userPref/${c.uuid}.json`, JSON.stringify(c.prefs))
// Delete require cache
for (const i in require.cache) {
if (i.endsWith(`${c.uuid}.json`)) delete require.cache[i]
}
c.reply({
text: getMessage(c.lang, 'command.settings.saved'),
color: c.colors.secondary
})
break
}
case 'get':
c.reply({
translate: '%s: %s',
color: c.colors.primary,
with: [
{
text: getMessage(c.lang, 'command.settings.get.colorPrimary'),
color: c.colors.secondary
},
{
text: c.colors.primary,
color: c.colors.primary
}
]
})
c.reply({
translate: '%s: %s',
color: c.colors.primary,
with: [
{
text: getMessage(c.lang, 'command.settings.get.colorSecondary'),
color: c.colors.secondary
},
{
text: c.colors.secondary,
color: c.colors.secondary
}
]
})
c.reply({
translate: '%s: %s (%s)',
color: c.colors.primary,
with: [
{
text: getMessage(c.lang, 'command.settings.get.language'),
color: c.colors.secondary
},
{
text: getMessage(c.lang, 'language.name'),
color: c.colors.primary
},
{
text: getMessage(c.lang, 'language.region'),
color: c.colors.primary
}
]
})
break
default:
c.reply({
translate: getMessage(c.lang, 'command.cloop.error.subcommand'),
color: c.colors.secondary,
with: [
{
text: `${c.prefix}help settings`,
color: c.colors.primary
}
]
})
}
}
}

View file

@ -37,6 +37,6 @@ module.exports = {
*/
hidden: true, // To show the command on the help command list, remove this line (optional)
consoleIndex: true, // When run from console, the second argument will be a bot ID (optional)
aliases: ['example', 'testing'], // Other command names that will work the same (optional)
aliases: ['example'], // Other command names that will work the same (optional)
level: 0 // Permission level required to run this command (optional)
}

49
commands/test.js Normal file
View file

@ -0,0 +1,49 @@
const { getMessage } = require('../util/lang.js')
module.exports = {
execute: (c) => {
const reply = function (name, item) {
return {
translate: '%s: %s',
color: c.colors.primary,
with: [
{
text: getMessage(c.lang, `command.test.${name}`),
color: c.colors.secondary
},
{
text: item,
color: c.colors.primary,
clickEvent: {
action: 'copy_to_clipboard',
value: item
},
hoverEvent: {
action: 'show_text',
contents: {
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
},
value: { // Added twice for backwards compatibility
text: getMessage(c.lang, 'copyText'),
color: c.colors.secondary
}
}
}
]
}
}
c.reply(reply('uuid', c.uuid))
c.reply(reply('username', c.username))
c.reply(reply('nickname', c.nickname))
c.reply(reply('command', c.command))
c.reply(reply('msgType', c.msgType))
c.reply(reply('prefix', c.prefix))
c.reply(reply('args', c.args.join(', ')))
c.reply(reply('verify', c.verify.toString()))
c.reply(reply('host', c.host))
c.reply(reply('port', c.port.toString()))
c.reply(reply('lang', c.lang))
c.reply(reply('colorPrimary', c.colors.primary))
c.reply(reply('colorSecondary', c.colors.secondary))
}
}

View file

@ -16,6 +16,6 @@ module.exports = {
]
})
},
aliases: ['validate'],
aliases: ['verify'],
level: 1
}

View file

@ -1,22 +1,34 @@
const m = require('minecraft-protocol')
const settings = require('./settings.json')
const generateUser = require('./util/usergen.js')
const EventEmitter = require('node:events')
const fs = require('fs')
if (!fs.readdirSync('.').includes('settings.json')) {
console.log('Settings file is missing, using defaults.')
fs.copyFileSync('settings_example.json', 'settings.json')
}
if (!fs.readdirSync('.').includes('secret.json')) {
console.log('Secrets file is missing, using defaults.')
fs.copyFileSync('secret_example.json', 'secret.json')
console.log('Please change the hashing keys in the secrets file.')
}
const m = require('minecraft-protocol')
const generateUser = require('./util/usergen.js')
const EventEmitter = require('node:events')
const settings = require('./settings.json')
module.exports.bot = []
const loadplug = (botno) => {
const botplug = []
const bpl = fs.readdirSync('plugins')
for (const i in bpl) {
if (!bpl[i].endsWith('.js')) {
continue
}
try {
botplug.push(require(`./plugins/${bpl[i]}`))
} catch (e) { console.log(e) }
const botplug = []
const bpl = fs.readdirSync('plugins')
for (const plugin of bpl) {
if (!plugin.endsWith('.js')) {
continue
}
try {
botplug.push(require(`./plugins/${plugin}`))
} catch (e) { console.log(e) }
}
const loadplug = (botno) => {
botplug.forEach((plug) => {
try {
if (plug.load) {
@ -66,8 +78,8 @@ const createBot = function createBot (host, oldId) {
})
}
for (const i in settings.servers) {
createBot(settings.servers[i])
for (const server of settings.servers) {
createBot(server)
}
module.exports.createBot = createBot

View file

@ -1,4 +1,6 @@
{
"language.name": "English",
"language.region": "United States",
"time.week": " week ",
"time.weekPlural": " weeks ",
"time.day": " day ",
@ -9,6 +11,7 @@
"time.minutePlural": " minutes ",
"time.second": " second ",
"time.secondPlural": " seconds ",
"chat.antiSpamTriggered": "Anti-spam has been triggered for this server.",
"command.about.usage": "",
"command.about.desc": "About the bot",
"command.cb.usage": " <command>",
@ -27,17 +30,21 @@
"command.refill.desc": "Refill core",
"command.say.usage": " <message>",
"command.say.desc": "Sends a message to chat",
"command.settings.usage": " get|| set <key> <value>",
"command.settings.desc": "Set your user preferences",
"command.stop.usage": "",
"command.stop.desc": "Restart bot",
"command.template.usage": " <required> [optional]",
"command.template.desc": "Does nothing",
"command.tpr.desc": "Teleport to a random location",
"command.test.usage": " [args...]",
"command.test.desc": "Chat parsing debugger command",
"command.tpr.usage": "",
"command.tpr.desc": "Teleport to a random location",
"command.verify.usage": " [args...]",
"command.verify.desc": "Check the hashing system",
"command.about.author": "%s - a Minecraft bot made by %s for Kaboom and clones",
"command.about.version": "Version %s",
"command.about.preRelease": "This is prerelease software - there may be errors, and features may be changed or removed at any time.",
"command.about.preRelease": "This is a development version - there may be errors, and features may be changed or removed at any time. Please report any errors to the bot's developer.",
"command.about.sourceCode": "Source code: %s",
"command.about.sourceCode.openInBrowser": "Click to open the source code link in your default browser",
"command.cloop.error.tooShort": "Command loops must have a rate above 20ms.",
@ -57,14 +64,38 @@
"command.help.permsConsole": "Console",
"command.help.noCommand": "Command does not exist",
"command.help.alias": "Alias to %s",
"command.serverinfo.deprecated": "The legacy version 9 serverinfo command will be removed in the future. The command's features will be merged with the \"about\" command as \"about server\" (or an alias).",
"command.netmsg.disabled": "This command has been disabled on this server.",
"command.netmsg.serverAddress": "Server Address",
"command.settings.disabled.console": "This command cannot be run from the console.",
"command.settings.get.colorPrimary": "Primary color",
"command.settings.get.colorSecondary": "Secondary color",
"command.settings.get.language": "Language",
"command.settings.setLanguage": "Run %s to set this as your language",
"command.settings.error.invalidKey": "Invalid key",
"command.settings.error.invalidLanguage": "Invalid language",
"command.settings.error.mustProvideValue": "You must provide a value",
"command.settings.saved": "Settings saved.",
"command.test.uuid": "UUID",
"command.test.username": "Username",
"command.test.nickname": "Nickname",
"command.test.command": "Command",
"command.test.msgType": "Message type",
"command.test.prefix": "Prefix",
"command.test.args": "Arguments",
"command.test.verify": "Permission level",
"command.test.host": "Server host",
"command.test.port": "Server port",
"command.test.lang": "Language",
"command.test.colorPrimary": "Primary color",
"command.test.colorSecondary": "Secondary color",
"command.about.serverInfo.os.android": "Android %s",
"command.about.serverInfo.os.android.noVersion": "Android",
"command.about.serverInfo.os.freebsd": "FreeBSD %s",
"command.about.serverInfo.os.linux": "Linux %s",
"command.about.serverInfo.os.freebsd": "FreeBSD",
"command.about.serverInfo.os.linux": "Linux",
"command.about.serverInfo.os.macos": "macOS",
"command.about.serverInfo.os.macos_old": "OS X",
"command.about.serverInfo.os": "Operating system",
"command.about.serverInfo.kernelVer": "Kernel version",
"command.about.serverInfo.processor": "CPU",
"command.about.serverInfo.arch": "Architecture",
"command.about.serverInfo.osUsername": "Username",
@ -79,6 +110,8 @@
"command.about.serverInfo.os.android.model": "Device model",
"command.about.serverInfo.botName": "Bot name",
"command.about.serverInfo.botVer": "Bot version",
"command.about.serverListItem": "Server %s - %s",
"command.about.serverListItem.thisServer": "Server %s - %s (this server)",
"command.tpr.success": "Teleporting %s to %s, %s, %s",
"command.verify.success": "Successfully verified with permission level %s",
"command.error": "An error occured (check console for more info)",

36
package-lock.json generated
View file

@ -7,28 +7,28 @@
"": {
"name": "botv10",
"version": "10.0.0",
"license": "UNLICENSED",
"license": "MIT",
"dependencies": {
"minecraft-protocol": "^1.45.0",
"prismarine-chat": "^1.10.0"
}
},
"node_modules/@azure/msal-common": {
"version": "14.14.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.0.tgz",
"integrity": "sha512-OxcOk9H1/1fktHh6//VCORgSNJc2dCQObTm6JNmL824Z6iZSO6eFo/Bttxe0hETn9B+cr7gDouTQtsRq3YPuSQ==",
"version": "14.14.1",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.1.tgz",
"integrity": "sha512-2Q3tqNz/PZLfSr8BvcHZVpRRfSn4MjGSqjj9J+HlBsmbf1Uu4P0WeXnemjTJwwx9KrmplsrN3UkZ/LPOR720rw==",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-node": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.12.0.tgz",
"integrity": "sha512-jmk5Im5KujRA2AcyCb0awA3buV8niSrwXZs+NBJWIvxOz76RvNlusGIqi43A0h45BPUy93Qb+CPdpJn82NFTIg==",
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.13.0.tgz",
"integrity": "sha512-DhP97ycs7qlCVzzzWGzJiwAFyFj5okno74E4FUZ61oCLfKh4IxA1kxirqzrWuYZWpBe9HVPL6GA4NvmlEOBN5Q==",
"license": "MIT",
"dependencies": {
"@azure/msal-common": "14.14.0",
"@azure/msal-common": "14.14.1",
"jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0"
},
@ -37,12 +37,12 @@
}
},
"node_modules/@types/node": {
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz",
"integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
"version": "22.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz",
"integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.13.0"
"undici-types": "~6.19.2"
}
},
"node_modules/@types/readable-stream": {
@ -427,9 +427,9 @@
"license": "MIT"
},
"node_modules/minecraft-data": {
"version": "3.67.0",
"resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.67.0.tgz",
"integrity": "sha512-/hLeYXopx9o1UdViPPFenLJ3hT5S4qUEwLQM0MAHOIhqkAUGXdkl47O7ohG+f87DH3+cZksbbM61sTnSRsQpsA==",
"version": "3.68.0",
"resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.68.0.tgz",
"integrity": "sha512-pNBTi39a1zbFpN9itwi0YSL3hqAsSw38D7pE9C6m+aURmXljpBlNTO+TkpZxxDv4KqqtNBOhmkj4x46IDW6R+Q==",
"license": "MIT"
},
"node_modules/minecraft-folder-path": {
@ -762,9 +762,9 @@
"license": "MIT"
},
"node_modules/undici-types": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/uri-js": {

View file

@ -10,6 +10,6 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "UNLICENSED",
"license": "MIT",
"description": ""
}

View file

@ -1,97 +1,118 @@
const settings = require('../settings.json')
const parsePlain = require('../util/chatparse_plain.js')
const parseConsole = require('../util/chatparse_console.js')
const parse1204 = require('../util/chatparse_1204.js')
const messageTypes = [
'',
'chat.type.emote',
'commands.message.display.incoming',
'commands.message.display.outgoing',
'',
'chat.type.announcement',
'chat.type.team.text',
'chat.type.team.sent'
]
const parse1204 = require('../util/parseNBT.js')
const { getMessage } = require('../util/lang.js')
const convertChatStyleItem = (item) => {
const output = {}
for (const i in item) {
output[i] = item[i].value
}
return output
}
const convertChatTypeItem = (item) => {
if (item.style) {
return {
translation_key: item.translation_key.value,
parameters: item.parameters.value.value,
style: convertChatStyleItem(item.style.value)
}
} else {
return {
translation_key: item.translation_key.value,
parameters: item.parameters.value.value,
style: {}
}
}
}
module.exports = {
load: (b) => {
b._client.on('profileless_chat', (data) => {
if (data.type === 4) {
const json = parse1204(data.message)
const parsed = parsePlain(json)
const split = parsed.split(': ')
const chatName = split.splice(0, 1)[0]
const username = b.findRealName(chatName)
const uuid = b.findUUID(username)
b.emit('chat', { json, type: 'profileless', uuid, message: split.join(': '), username })
} else if (data.type === 6 || data.type === 7) {
b.emit('chat', {
json: {
translate: messageTypes[data.type],
color: (data.type === 2 || data.type === 3) ? 'gray' : 'reset',
with: [
parse1204(data.target),
parse1204(data.name),
data.message
]
},
type: 'profileless',
uuid: data.senderUuid,
message: parsePlain(data.message),
username: parsePlain(parse1204(data.name))
})
} else {
b.emit('chat', {
json: {
translate: messageTypes[data.type],
color: (data.type === 2 || data.type === 3) ? 'gray' : 'reset',
with: [
parse1204(data.name),
parse1204(data.message)
]
},
type: 'profileless',
uuid: '00000000-0000-0000-0000-000000000000',
message: parsePlain(parse1204(data.message)),
username: parsePlain(parse1204(data.name))
b.messageCount = 0
b.chatDisabledUntil = 0
b.interval.antiSpam = setInterval(() => {
b.messageCount = 0
}, 4000)
b.messageTypes = []
b._client.on('registry_data', (data) => {
if (data.codec.value['minecraft:chat_type']) {
b.messageTypes = data.codec.value['minecraft:chat_type']
const nbtItems = data.codec.value['minecraft:chat_type'].value.value.value.value
nbtItems.forEach((item, i) => {
b.messageTypes[i] = convertChatTypeItem(item.element.value.chat.value)
})
}
})
b._client.on('profileless_chat', (data) => {
const messageType = b.messageTypes[data.type]
const json = { translate: messageType.translation_key, with: [] }
messageType.parameters.forEach((item, i) => {
if (item === 'content') {
json.with[i] = parse1204(data.message)
} else if (item === 'sender') {
json.with[i] = parse1204(data.name)
} else if (item === 'target') {
json.with[i] = parse1204(data.target)
}
})
for (const i in messageType.style) {
json[i] = messageType.style[i]
}
let username = ''
let nickname = ''
let uuid = '00000000-0000-0000-0000-000000000000'
let message = ''
if (messageType.translation_key === '%s') {
const parsed = parsePlain(json)
const split = parsed.split(': ')
const chatName = split.splice(0, 1)[0]
const chatNameSplit = chatName.split(' ')
nickname = chatNameSplit[chatNameSplit.length - 1]
username = b.findRealName(chatName)
uuid = b.findUUID(username)
message = split.join(': ')
} else {
message = parsePlain(parse1204(data.message))
uuid = b.findUUID(parsePlain(parse1204(data.name)))
nickname = b.findDisplayName(uuid)
username = parsePlain(parse1204(data.name))
}
b.emit('chat', {
json,
type: 'profileless',
uuid,
message,
nickname,
username
})
})
b._client.on('player_chat', (data) => {
if (data.type === 4) {
b.emit('chat', { json: parse1204(data.unsignedChatContent), type: 'player', uuid: data.senderUuid, message: data.plainMessage, username: parsePlain(parse1204(data.networkName)) })
} else if (data.type === 6 || data.type === 7) {
b.emit('chat', {
json: {
translate: messageTypes[data.type],
color: data.type === 2 ? 'gray' : 'reset',
with: [
parse1204(data.networkTargetName),
parse1204(data.networkName),
data.plainMessage
]
},
type: 'player',
uuid: data.senderUuid,
message: parsePlain(data.plainMessage),
username: parsePlain(parse1204(data.networkName))
})
} else {
b.emit('chat', {
json: {
translate: messageTypes[data.type],
color: (data.type === 2 || data.type === 3) ? 'gray' : 'reset',
with: [
parse1204(data.networkName),
data.plainMessage
]
},
type: 'player',
uuid: data.senderUuid,
message: parsePlain(data.plainMessage),
username: parsePlain(parse1204(data.networkName))
})
const messageType = b.messageTypes[data.type]
const json = { translate: messageType.translation_key, with: [] }
messageType.parameters.forEach((item, i) => {
if (item === 'content') {
if (messageType.translation_key === '%s') {
json.with[i] = parse1204(data.unsignedChatContent)
} else {
json.with[i] = data.plainMessage
}
} else if (item === 'sender') {
json.with[i] = parse1204(data.networkName)
} else if (item === 'target') {
json.with[i] = parse1204(data.networkTargetName)
}
})
for (const i in messageType.style) {
json[i] = messageType.style[i]
}
b.emit('chat', {
json,
type: 'player',
uuid: data.senderUuid,
message: data.plainMessage,
nickname: parsePlain(parse1204(data.networkName)),
username: b.findRealNameFromUUID(data.senderUuid)
})
})
b._client.on('system_chat', (data) => {
@ -99,15 +120,25 @@ module.exports = {
const parsed = parsePlain(json)
const split = parsed.split(': ')
const chatName = split.splice(0, 1)[0]
const chatNameSplit = chatName.split(' ')
const nickname = chatNameSplit[chatNameSplit.length - 1]
const username = b.findRealName(chatName)
const uuid = b.findUUID(username)
b.emit('chat', { json, type: 'system', uuid, message: split.join(': '), username })
b.emit('chat', {
json,
type: 'system',
uuid,
message: split.join(': '),
nickname,
username
})
})
b._client.on('chat', (data) => { // Legacy chat
b._client.on('chat', (data) => { // Legacy chat for versions <1.19
const json = parse1204(data.message)
const parsed = parsePlain(json)
let chatName
let nickname
let username
let message
let uuid
@ -119,30 +150,39 @@ module.exports = {
uuid = b.findUUID(username)
} else { // Servers with Extras chat, such as Kaboom
const split = parsed.split(': ')
message = split.join(': ')
uuid = b.findUUID(username)
chatName = split.splice(0, 1)[0]
const chatNameSplit = chatName.split(' ')
nickname = chatNameSplit[chatNameSplit.length - 1]
username = b.findRealName(chatName)
uuid = b.findUUID(username)
message = split.join(': ')
}
b.emit('chat', { json, type: 'legacy', uuid: data.uuid ? data.uuid : uuid, message, username })
if (data.uuid) uuid = data.uuid
b.emit('chat', {
json,
type: 'legacy',
uuid,
message,
nickname,
username
})
})
b.on('chat', (data) => {
b.messageCount++
if (Date.now() < b.chatDisabledUntil) return
if (b.messageCount >= 100) {
b.info(getMessage(settings.defaultLang, 'chat.antiSpamTriggered'))
b.chatDisabledUntil = Date.now() + 30000
return
}
const msgConsole = parseConsole(data.json)
const msgPlain = parsePlain(data.json)
if (settings.logJSONmessages) console.log(data.json)
if (msgPlain.endsWith('\n\n\n\n\nThe chat has been cleared')) return
if (msgPlain.startsWith('Command set: ')) return
b.emit('plainchat', msgPlain)
b.emit('plainchat', msgPlain, data.type)
b.displayChat(data.type, `${msgConsole}\x1b[0m`)
const fullCommand = data.message
for (const i in b.prefix) {
if (fullCommand.startsWith(b.prefix[i])) {
const command = fullCommand.slice(b.prefix[i].length)
b.runCommand(data.username, data.uuid, command, b.prefix[i])
}
}
})
}
}

32
plugins/chatlog.js Executable file
View file

@ -0,0 +1,32 @@
const chatlog = require('../util/chatlog.js')
const fs = require('fs')
const settings = require('../settings.json')
const checkLog = () => {
if (settings.disableLogging) return
try {
if (!fs.readdirSync('.').includes('logs')) fs.mkdirSync('logs')
const dateToday = new Date(Date.now())
const dateTomorrow = new Date(Date.now() + 86400000)
const filenameToday = `${dateToday.getUTCMonth() + 1}-${dateToday.getUTCDate()}-${dateToday.getUTCFullYear()}`
const filenameTomorrow = `${dateTomorrow.getUTCMonth() + 1}-${dateTomorrow.getUTCDate()}-${dateTomorrow.getUTCFullYear()}`
if (!fs.readdirSync('./logs').includes(filenameToday)) fs.mkdirSync(`logs/${filenameToday}`)
if (!fs.readdirSync('./logs').includes(filenameTomorrow)) fs.mkdirSync(`logs/${filenameTomorrow}`) // Create tomorrow's log directory early
} catch (e) {
console.log(e) // Prevents some crashes when disk space is full or when the permissions are incorrect
}
}
setInterval(checkLog, 3600000) // Runs once every hour,
checkLog() // and at bot startup.
module.exports = {
load: (b) => {
b.on('plainchat', (msg, type) => {
if (!settings.disableLogging && !settings.disableChatLogging) chatlog(`chat_${b.host.host}_${b.host.port}`, `[${type}] ${msg}`)
})
b.on('command', (name, uuid, text) => {
if (!settings.disableLogging && !settings.disableCommandLogging) chatlog(`cmd_${b.host.host}_${b.host.port}`, `${name} (${uuid}): ${text}`)
})
}
}

View file

@ -14,8 +14,8 @@ module.exports = {
b.cloops.splice(index, 1)
}
b.clearCloops = function () {
for (const i in b.cloops) {
clearInterval(b.cloops[i].interval)
for (const cloop of b.cloops) {
clearInterval(cloop.interval)
}
b.cloops = []
}

View file

@ -3,13 +3,38 @@ const hashcheck = require('../util/hashcheck.js')
const settings = require('../settings.json')
const { getMessage } = require('../util/lang.js')
const cmds = require('../util/commands.js')
const fs = require('fs')
if (!fs.readdirSync('.').includes('userPref') && !settings.userSettingsDisabled) fs.mkdirSync('userPref')
const loadSettings = function (uuid) {
try {
if (settings.userSettingsDisabled) {
return {}
} else {
return require(`../userPref/${uuid}.json`)
}
} catch (e) {
return {}
}
}
module.exports = {
load: (b) => {
b.prefix = settings.prefix
b.lastCmd = 0
b.runCommand = (name, uuid, text, prefix) => {
b.on('chat', (data) => {
const fullCommand = data.message
for (const prefix of b.prefix) {
if (fullCommand.startsWith(prefix)) {
const command = fullCommand.slice(prefix.length)
b.runCommand(data.username, data.nickname, data.uuid, command, data.type, prefix)
}
}
})
b.runCommand = (name, nickname, uuid, text, msgType, prefix) => {
if (uuid === '00000000-0000-0000-0000-000000000000') return
if (Date.now() - b.lastCmd <= 1000) return
const userSettings = loadSettings(uuid)
b.lastCmd = Date.now()
const cmd = text.split(' ')
const lang = settings.defaultLang
@ -17,6 +42,7 @@ module.exports = {
if (verify > 0) {
text = cmd.slice(0, cmd.length - 1).join(' ')
}
b.emit('command', name, uuid, text, prefix)
if (cmds[cmd[0].toLowerCase()]) {
const command = cmds[cmd[0].toLowerCase()]
if (command.level !== undefined && command.level > verify) {
@ -32,7 +58,7 @@ module.exports = {
return
}
try {
cmds[cmd[0].toLowerCase()].execute(new Command(uuid, name, 'nick N/A', text, prefix, b, verify))
cmds[cmd[0].toLowerCase()].execute(new Command(uuid, name, nickname, text, msgType, prefix, b, verify, userSettings))
} catch (e) {
console.log(e)
b.tellraw(uuid, {

View file

@ -19,7 +19,7 @@ module.exports = {
b.advanceccq = function () {
if (b.ccq[0] && b.ccq[0].length !== 0) {
b._client.write('update_command_block', {
command: b.ccq[0],
command: '/',
location: {
x: b.commandPos.x + b.blocknoX,
y: b.commandPos.y + b.blocknoY,
@ -29,7 +29,7 @@ module.exports = {
flags: 1
})
b._client.write('update_command_block', {
command: b.ccq[0],
command: b.ccq[0].substr(0, 32767),
location: {
x: b.commandPos.x + b.blocknoX,
y: b.commandPos.y + b.blocknoY,
@ -55,6 +55,14 @@ module.exports = {
}
b._client.on('login', () => {
b._client.write('settings', {
locale: 'ru_RU',
viewDistance: 4,
chatFlags: 0, // Enable full chat functionality
chatColors: true,
skinParts: 127, // Allow the second layer of the skin, when the bot is sudoed to do /skin
mainHand: 1 // Right hand
})
b.add_sc_task('cc', () => {
b.chat(b.refillCoreCmd)
}, true)
@ -63,7 +71,7 @@ module.exports = {
})
})
b.on('ccstart', () => {
setTimeout(() => { b.interval.ccqi = setInterval(b.advanceccq, 3) }, 1000) // 1 Second and 3 Milliseconds
setTimeout(() => { b.interval.ccqi = setInterval(b.advanceccq, 2) }, 1000)
b.ccStarted = true
})
b.on('chat', (data) => {

View file

@ -1,29 +1,29 @@
const parse = require('../util/chatparse_plain')
const parseNBT = require('../util/chatparse_1204')
const parseNBT = require('../util/parseNBT')
module.exports = {
load: (b) => {
b.players = {}
b._client.on('player_info', (data) => {
const buffer2 = {}
for (const i in data.data) {
for (const player of data.data) {
let uuid
if (data.data[i].uuid) {
uuid = data.data[i].uuid
} else if (data.data[i].UUID) {
uuid = data.data[i].UUID
if (player.uuid) {
uuid = player.uuid
} else if (player.UUID) {
uuid = player.UUID
}
let displayName
if (data.data[i].displayName !== undefined) {
displayName = data.data[i].displayName
if (player.displayName !== undefined) {
displayName = player.displayName
} else {
displayName = '{"text":"[[[[ No display name ]]]]"}'
}
if (data.data[i].player && data.data[i].player.name !== undefined) {
buffer2[uuid] = { realName: data.data[i].player.name, displayName: parse(parseNBT(displayName)) }
} else if (data.data[i].name !== undefined) {
buffer2[uuid] = { realName: data.data[i].name, displayName: parse(parseNBT(displayName)) }
} else if (data.data[i].displayName !== undefined) {
buffer2[uuid] = { displayName: displayName.plain }
if (player.player && player.player.name !== undefined) {
buffer2[uuid] = { realName: player.player.name, displayName: parse(parseNBT(displayName)) }
} else if (player.name !== undefined) {
buffer2[uuid] = { realName: player.name, displayName: parse(parseNBT(displayName)) }
} else if (player.displayName !== undefined) {
buffer2[uuid] = { displayName: parse(parseNBT(displayName)) }
}
}
for (const uuid in buffer2) {
@ -48,5 +48,20 @@ module.exports = {
}
return '[[[[ no name ]]]]'
}
b.findRealNameFromUUID = (name) => {
if (b.players[name]) {
return b.players[name].realName
} else {
return '[[[[ no name ]]]]'
}
}
b.findDisplayName = (name) => {
if (b.players[name]) {
const displayName = b.players[name].displayName.split(' ')
return displayName[displayName.length - 1]
} else {
return '[[[[ No display name ]]]]'
}
}
}
}

View file

@ -11,13 +11,18 @@ class SCTask {
module.exports = {
load: (b) => {
b.sc_tasks = {}
b.selfcareRun = 0
b.interval.sc = setInterval(() => {
if (Date.now() - b.selfcareRun <= 600) {
return
}
for (const i in b.sc_tasks) {
if (b.sc_tasks[i].failed) {
b.sc_tasks[i].failTask()
}
}
}, 1000)
b.selfcareRun = Date.now()
}, 40)
b.add_sc_task = (name, failTask, startFailed) => {
b.sc_tasks[name] = new SCTask(failTask, startFailed)
}
@ -53,7 +58,7 @@ module.exports = {
})
}
// Gamemode
// Gamemode / end portal bug
b.add_sc_task('gamemode', () => {
b.chat('/minecraft:gamemode creative')
})
@ -62,6 +67,8 @@ module.exports = {
b.sc_tasks.gamemode.failed = 1
} else if (p.reason === 3 && p.gameMode === 1) {
b.sc_tasks.gamemode.failed = 0
} else if (p.reason === 4) {
b.sc_tasks.respawn.failed = 1
}
})
@ -71,7 +78,8 @@ module.exports = {
b.sc_tasks.respawn.failed = 0
})
b.on('chat', (data) => {
if (data.json.translate === 'chat.disabled.options' || (data.json.extra && data.json.extra[0] && data.json.extra[0].translate === 'chat.disabled.options')) {
if (data.json.translate === 'chat.disabled.options' || (data.json.extra && data.json.extra[0] && data.json.extra[0].translate === 'chat.disabled.options') ||
data.json.translate === 'Chat disabled in client options' || (data.json.extra && data.json.extra[0] && data.json.extra[0].translate === 'Chat disabled in client options')) {
b.sc_tasks.respawn.failed = 1
}
})

View file

@ -1,6 +1,4 @@
{
"secret":"/path/to/secrets/file/secret.json",
"name": "Minecraft Bot",
"version_mc": "1.20.4",
"defaultLang": "en-US",
"terminalMode": "blackTerminal_24bit",

View file

@ -1,23 +1,41 @@
// HOW TO WRITE CLASS JS
const settings = require('../settings.json')
class Command {
constructor (uuid, user, nick, cmd, prefix, bot, verify, lang = settings.defaultLang) {
constructor (uuid, user, nick, cmd, msgType, prefix, bot, verify, prefs) {
this.send = (text, uuid) => { bot.tellraw(uuid || '@a', text) }
this.reply = text => bot.tellraw(uuid, text)
this.uuid = uuid
this.username = user
this.nickname = nick
this.command = cmd
this.msgType = msgType
this.prefix = prefix
this.bot = bot
this.type = 'minecraft'
this.index = bot.id
this.args = cmd.split(' ').slice(1)
this.cmdName = cmd.split(' ')[0]
this.verify = verify
this.host = bot.host.host
this.port = bot.host.port
this.lang = lang
this.colors = settings.colors
this.serverName = bot.host.options.name
this.prefs = prefs
if (prefs.lang) {
this.lang = prefs.lang
} else {
this.lang = settings.defaultLang
}
const _colors = {}
if (prefs.colorPrimary) {
_colors.primary = prefs.colorPrimary
} else {
_colors.primary = settings.colors.primary
}
if (prefs.colorSecondary) {
_colors.secondary = prefs.colorSecondary
} else {
_colors.secondary = settings.colors.secondary
}
this.colors = _colors
}
}

View file

@ -1,25 +1,26 @@
// HOW TO WRITE CLASS JS
const index = require('../index.js')
const parse = require('../util/chatparse_console.js')
const settings = require('../settings.json')
class ConsoleCommand {
constructor (cmd, index2) {
this.send = () => {} // not needed for console
this.send = () => {}
this.reply = text => process.stdout.write(parse(text) + '\n')
this.uuid = 'dde5a2a6-ebdd-7bbb-8eac-f75b10c10446' // hard-coded because uuid does not exist at console
this.uuid = 'dde5a2a6-ebdd-7bbb-8eac-f75b10c10446'
this.username = 'Owner'
this.nickname = 'Console'
this.nickname = 'Owner'
this.command = cmd
this.prefix = '' // prefix does not exist at console
this.msgType = '_bot_console'
this.prefix = ''
this.bot = index2 >= 0
? index.bot[index2]
: {}
this.type = 'console'
this.index = index2
this.args = cmd.split(' ').slice(1)
this.cmdName = cmd.split(' ')[0]
this.verify = 3
this.host = ''
this.port = '3' // :3
this.port = '3'
this.serverName = 'botvX Console'
this.lang = settings.defaultLang
this.colors = settings.colors
}

17
util/chatlog.js Normal file
View file

@ -0,0 +1,17 @@
const fs = require('fs')
const settings = require('../settings.json')
module.exports = function (fileName, item) {
if (settings.disableLogging) return
const dateToday = new Date(Date.now())
const UTCYears = dateToday.getUTCFullYear()
const UTCMonths = dateToday.getUTCMonth() + 1
const UTCDays = dateToday.getUTCDate()
const UTCHours = dateToday.getUTCHours()
const UTCMinutes = dateToday.getUTCMinutes().toString().padStart(2, '0')
const UTCSeconds = dateToday.getUTCSeconds().toString().padStart(2, '0')
const UTCMilliSeconds = dateToday.getUTCMilliseconds().toString().padStart(3, '0')
const filenameToday = `${UTCMonths}-${UTCDays}-${UTCYears}`
const logDate = `${UTCMonths}/${UTCDays}/${UTCYears} ${UTCHours}:${UTCMinutes}:${UTCSeconds}.${UTCMilliSeconds}`
fs.appendFileSync(`logs/${filenameToday}/${fileName}.txt`, `[${logDate}] ${item}\n`)
}

View file

@ -11,21 +11,45 @@ if (_consoleColors[settings.terminalMode]) {
consoleColors = _consoleColors.none.fourBit
consoleColors24 = _consoleColors.none.twentyFourBit
}
const process8bitColorChannel = (value) => {
if (value < 65) return 0
if (value < 115) return 1
if (value < 155) return 2
if (value < 195) return 3
if (value < 235) return 4
return 5
}
const hexColorParser = (color) => {
if (!consoleColors24.enabled || consoleColors24.bit !== 24) { // Hex color parsing to the 8 bit and 4 bit modes has not been implemented yet
if (!consoleColors24.enabled || consoleColors24.bit === 4) { // Hex color parsing to the 4 bit mode has not been implemented yet
return ''
}
let out = '\x1B[0;'
const redChannel = Number(`0x${color.slice(1, 3)}`)
const greenChannel = Number(`0x${color.slice(3, 5)}`)
const blueChannel = Number(`0x${color.slice(5, 7)}`)
if (!consoleColors24.lightMode && redChannel < 64 && greenChannel < 64 && blueChannel < 64) {
out += '48;2;220;220;220;'
} else if (consoleColors24.lightMode && ((redChannel > 192 && greenChannel > 192 && blueChannel > 192) || greenChannel > 160)) {
out += '48;2;0;0;0;'
if (consoleColors24.bit === 24) {
let out = '\x1B[0;'
const redChannel = Number(`0x${color.slice(1, 3)}`)
const greenChannel = Number(`0x${color.slice(3, 5)}`)
const blueChannel = Number(`0x${color.slice(5, 7)}`)
if (!consoleColors24.lightMode && redChannel < 64 && greenChannel < 64 && blueChannel < 64) {
out += '48;2;220;220;220;'
} else if (consoleColors24.lightMode && ((redChannel > 192 && greenChannel > 192 && blueChannel > 192) || greenChannel > 160)) {
out += '48;2;0;0;0;'
}
return out + `38;2;${redChannel};${greenChannel};${blueChannel}m`
} else if (consoleColors24.bit === 8) {
let out = '\x1B[0;'
const redChannel = Number(`0x${color.slice(1, 3)}`)
const greenChannel = Number(`0x${color.slice(3, 5)}`)
const blueChannel = Number(`0x${color.slice(5, 7)}`)
if (!consoleColors24.lightMode && redChannel < 65 && greenChannel < 65 && blueChannel < 65) {
out += '48;5;253;'
} else if (consoleColors24.lightMode && ((redChannel > 194 && greenChannel > 194 && blueChannel > 194) || greenChannel > 154)) {
out += '48;5;16;'
}
const redOut = process8bitColorChannel(redChannel)
const greenOut = process8bitColorChannel(greenChannel)
const blueOut = process8bitColorChannel(blueChannel)
const colorValue = 16 + 36 * redOut + 6 * greenOut + blueOut
return out + `38;5;${colorValue}m`
}
return out + `38;2;${redChannel};${greenChannel};${blueChannel}m`
}
const processColor = (col, rcol) => {
@ -41,8 +65,8 @@ const processColor = (col, rcol) => {
}
const parse = function (_data, l = 0, resetColor = consoleColors.reset) {
if (l >= 12) {
return ['', '', '']
if (l >= 4) {
return ''
}
let data
if (typeof _data === 'string') {
@ -70,23 +94,25 @@ const parse = function (_data, l = 0, resetColor = consoleColors.reset) {
if (typeof _text === 'number') {
_text = _text.toString()
}
out += _text.replaceAll('\x1b', '').replaceAll('\x0e', '') // Remove escape codes and [SO] from console format
out += _text.replaceAll('\x1b', '').replaceAll('\x0e', '')
}
if (data.translate) {
let trans = data.translate.replace(/%%/g, '\ue123').replaceAll('\x1b', '').replaceAll('\x0e', '') // Remove escape codes from console format
let trans = data.translate.replaceAll('%%', '\ud900\ud801').replaceAll('\x1b', '').replaceAll('\x0e', '')
if (lang[trans] !== undefined) {
trans = lang[trans].replace(/%%/g, '\ue123')
}
for (const i in data.with) {
const j2 = parse(data.with[i], l + 1, data.color ? processColor(data.color, resetColor) : resetColor)
trans = trans.replace(/%s/, j2.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
trans = trans.replaceAll(`%${+i + 1}$s`, j2.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
if (data.with) {
data.with.forEach((item, i) => {
const j2 = parse(item, l + 1, data.color ? processColor(data.color, resetColor) : resetColor)
trans = trans.replace(/%s/, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
trans = trans.replaceAll(`%${+i + 1}$s`, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
})
}
out += trans.replace(/\ue123/g, '%').replace(/\ue124/g, '%s').replace(/\ue125/g, '$s')
out += trans.replaceAll('\ud900\ud801', '%').replaceAll('\ud900\ud804', '%s').replaceAll('\ud900\ud805', '$s')
}
if (data.extra) {
for (const i in data.extra) {
const parsed = parse(data.extra[i], l, data.color ? processColor(data.color, resetColor) : resetColor)
for (const item of data.extra) {
const parsed = parse(item, l, data.color ? processColor(data.color, resetColor) : resetColor)
out += parsed
}
}

96
util/chatparse_mc.js Normal file
View file

@ -0,0 +1,96 @@
const lang = require('./mc_lang.js')
const consoleColors = {
dark_red: '§4',
red: '§c',
dark_green: '§2',
green: '§a',
gold: '§6',
yellow: '§e',
dark_blue: '§1',
blue: '§9',
dark_purple: '§5',
light_purple: '§d',
dark_aqua: '§3',
aqua: '§b',
black: '§0',
gray: '§7',
dark_gray: '§8',
white: '§f',
reset: '§r§f'
}
const processColor = (col, rcol) => {
let out
if (col === 'reset') {
out = rcol
} else {
out = consoleColors[col]
}
return out
}
const parse = function (_data, l = 0, resetColor = consoleColors.reset) {
if (l >= 4) {
return ''
}
let data
if (typeof _data === 'string') {
data = { text: _data, color: 'reset' }
} else if (typeof _data === 'number') {
data = { text: _data + '', color: 'reset' }
} else if (_data.constructor === Array) {
data = { extra: _data, color: 'reset' }
} else {
data = _data
}
if (data['']) {
data.text = data['']
if (!data.color) data.color = 'reset'
}
let out = ''
if (data.color) {
out += processColor(data.color, resetColor)
} else {
out += resetColor
}
if (data.text) {
let _text = data.text
if (typeof _text === 'number') {
_text = _text.toString()
}
out += _text.replaceAll('\x1b', '').replaceAll('\x0e', '')
}
if (data.translate) {
let trans = data.translate.replaceAll('%%', '\ud900\ud801').replaceAll('\x1b', '').replaceAll('\x0e', '')
if (lang[trans] !== undefined) {
trans = lang[trans].replace(/%%/g, '\ue123')
}
if (data.with) {
data.with.forEach((item, i) => {
const j2 = parse(item, l + 1, data.color ? processColor(data.color, resetColor) : resetColor)
trans = trans.replace(/%s/, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
trans = trans.replaceAll(`%${+i + 1}$s`, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
})
}
out += trans.replaceAll('\ud900\ud801', '%').replaceAll('\ud900\ud804', '%s').replaceAll('\ud900\ud805', '$s')
}
if (data.extra) {
for (const item of data.extra) {
const parsed = parse(item, l, data.color ? processColor(data.color, resetColor) : resetColor)
out += parsed
}
}
out += resetColor
return out
}
const parse2 = function (_data, l, resetColor) {
try {
return parse(_data)
} catch (e) {
console.error(e)
return `\x1B[0m\x1B[38;2;255;85;85mAn error occured while parsing a message. See console for more information.\nJSON that caused the error: ${JSON.stringify(_data)}`
}
}
module.exports = parse2

View file

@ -1,7 +1,7 @@
const lang = require('./mc_lang.js')
const parse = function (_data, l = 0) {
if (l >= 12) {
return ['', '', '']
if (l >= 4) {
return ''
}
let data
if (typeof _data === 'string') {
@ -31,14 +31,14 @@ const parse = function (_data, l = 0) {
}
for (const i in data.with) {
const j2 = parse(data.with[i], l + 1)
trans = trans.replace(/%s/, j2.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
trans = trans.replaceAll(`%${+i + 1}$s`, j2.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
trans = trans.replace(/%s/, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
trans = trans.replaceAll(`%${+i + 1}$s`, j2.replaceAll('%s', '\ud900\ud804').replaceAll('$s', '\ud900\ud805'))
}
out += trans.replace(/\ue123/g, '%').replace(/\ue124/g, '%s').replace(/\ue125/g, '$s')
out += trans.replaceAll('\ud900\ud801', '%').replaceAll('\ud900\ud804', '%s').replaceAll('\ud900\ud805', '$s')
}
if (data.extra) {
for (const i in data.extra) {
const parsed = parse(data.extra[i], l)
for (const item of data.extra) {
const parsed = parse(item, l)
out += parsed
}
}

View file

@ -1,19 +1,19 @@
const fs = require('fs')
const cmds = Object.create(null)
const bpl = fs.readdirSync('./commands')
for (const i in bpl) {
if (!bpl[i].endsWith('.js')) {
for (const plugin of bpl) {
if (!plugin.endsWith('.js')) {
continue
}
try {
const commandName = bpl[i].split('.js')[0]
cmds[commandName] = require(`../commands/${bpl[i]}`)
const commandName = plugin.split('.js')[0]
cmds[commandName] = require(`../commands/${plugin}`)
if (cmds[commandName].level === undefined) {
cmds[commandName].level = 0
}
if (cmds[commandName].aliases) {
for (const j in cmds[commandName].aliases) {
cmds[cmds[commandName].aliases[j]] = {
for (const alias of cmds[commandName].aliases) {
cmds[alias] = {
execute: cmds[commandName].execute,
alias: commandName,
usage: cmds[commandName].usage,

View file

@ -53,23 +53,23 @@
},
"blackTerminal_8bit":{
"fourBit":{
"dark_red": "\u001B[0;38;5;1m",
"red": "\u001B[0;38;5;9m",
"dark_green": "\u001B[0;38;5;2m",
"green": "\u001B[0;38;5;10m",
"gold": "\u001B[0;38;5;3m",
"yellow": "\u001B[0;38;5;11m",
"dark_blue": "\u001B[0;38;5;4m",
"blue": "\u001B[0;38;5;12m",
"dark_purple": "\u001B[0;38;5;5m",
"light_purple": "\u001B[0;38;5;13m",
"dark_aqua": "\u001B[0;38;5;6m",
"aqua": "\u001B[0;38;5;14m",
"black": "\u001B[0;48;5;15;38;5;0m",
"gray": "\u001B[0;38;5;7m",
"dark_gray": "\u001B[0;38;5;8m",
"white": "\u001B[0;38;5;15m",
"reset": "\u001B[0;38;5;15m"
"dark_red": "\u001B[0;38;5;124m",
"red": "\u001B[0;38;5;203m",
"dark_green": "\u001B[0;38;5;34m",
"green": "\u001B[0;38;5;83m",
"gold": "\u001B[0;38;5;214m",
"yellow": "\u001B[0;38;5;227m",
"dark_blue": "\u001B[0;38;5;19m",
"blue": "\u001B[0;38;5;63m",
"dark_purple": "\u001B[0;38;5;127m",
"light_purple": "\u001B[0;38;5;207m",
"dark_aqua": "\u001B[0;38;5;37m",
"aqua": "\u001B[0;38;5;87m",
"black": "\u001B[0;48;5;253;38;5;16m",
"gray": "\u001B[0;38;5;145m",
"dark_gray": "\u001B[0;38;5;59m",
"white": "\u001B[0;38;5;231m",
"reset": "\u001B[0;38;5;231m"
},
"twentyFourBit":{
"enabled": true,
@ -79,23 +79,23 @@
},
"whiteTerminal_8bit":{
"fourBit":{
"dark_red": "\u001B[0;38;5;1m",
"red": "\u001B[0;38;5;9m",
"dark_green": "\u001B[0;38;5;2m",
"green": "\u001B[0;48;5;0;38;5;10m",
"gold": "\u001B[0;38;5;3m",
"yellow": "\u001B[0;48;5;0;38;5;11m",
"dark_blue": "\u001B[0;38;5;4m",
"blue": "\u001B[0;38;5;12m",
"dark_purple": "\u001B[0;38;5;5m",
"light_purple": "\u001B[0;38;5;13m",
"dark_aqua": "\u001B[0;38;5;6m",
"aqua": "\u001B[0;48;5;0;38;5;14m",
"black": "\u001B[0;38;5;0m",
"gray": "\u001B[0;38;5;7m",
"dark_gray": "\u001B[0;38;5;8m",
"white": "\u001B[0;48;5;0;38;5;15m",
"reset": "\u001B[0;48;5;0;38;5;15m"
"dark_red": "\u001B[0;38;5;124m",
"red": "\u001B[0;38;5;203m",
"dark_green": "\u001B[0;38;5;34m",
"green": "\u001B[0;48;5;16;38;5;83m",
"gold": "\u001B[0;38;5;214m",
"yellow": "\u001B[0;48;5;16;38;5;227m",
"dark_blue": "\u001B[0;38;5;19m",
"blue": "\u001B[0;38;5;63m",
"dark_purple": "\u001B[0;38;5;127m",
"light_purple": "\u001B[0;38;5;207m",
"dark_aqua": "\u001B[0;38;5;37m",
"aqua": "\u001B[0;48;5;16;38;5;87m",
"black": "\u001B[0;38;5;16m",
"gray": "\u001B[0;38;5;145m",
"dark_gray": "\u001B[0;38;5;59m",
"white": "\u001B[0;48;5;16;38;5;231m",
"reset": "\u001B[0;48;5;16;38;5;231m"
},
"twentyFourBit":{
"enabled": true,

View file

@ -1,6 +1,5 @@
const crypto = require('crypto')
const settings = require('../settings.json')
const secret = require(settings.secret)
const secret = require('../secret.json')
module.exports = function (cmd) {
const cmdWithoutHash = cmd.slice(0, cmd.length - 1).join(' ')
const _dateString = Date.now().toString()

View file

@ -2,13 +2,13 @@ const fs = require('fs')
const languages = {}
const loadplug = (botno) => {
const bpl = fs.readdirSync('util/lang')
for (const i in bpl) {
if (!bpl[i].endsWith('.json')) {
const bpl = fs.readdirSync('lang')
for (const plugin of bpl) {
if (!plugin.endsWith('.json')) {
continue
}
try {
languages[bpl[i].split('.')[0]] = require(`./lang/${bpl[i]}`)
languages[plugin.split('.')[0]] = require(`../lang/${plugin}`)
} catch (e) { console.log(e) }
}
}
@ -18,17 +18,20 @@ const getMessage = function (l, msg, with2) {
let message = msg.replace(/%%/g, '\ue123')
if (languages[l] && languages[l][message] !== undefined) {
message = languages[l][message].replace(/%%/g, '\ue123')
} else if (languages[l] && languages['en-US'][message] !== undefined) {
} else if (languages['en-US'] && languages['en-US'][message] !== undefined) {
message = languages['en-US'][message].replace(/%%/g, '\ue123')
}
for (const i in with2) {
message = message.replace(/%s/, with2[i].replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
message = message.replaceAll(`%${+i + 1}$s`, with2[i].replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
if (with2) {
with2.forEach((withItem, i) => {
message = message.replace(/%s/, withItem.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
message = message.replaceAll(`%${+i + 1}$s`, withItem.replace(/%s/g, '\ue124').replace(/\$s/g, '\ue125'))
})
}
return message.replace(/\ue123/g, '%').replace(/\ue124/g, '%s').replace(/\ue125/g, '$s')
}
module.exports = {
languages: Object.keys(languages),
getMessage,
formatTime: function (time, language) {
let finalString = ''

View file

@ -1,70 +0,0 @@
{
"time.week": " week ",
"time.weekPlural": " weeks ",
"time.day": " day ",
"time.dayPlural": " days ",
"time.hour": " houw ",
"time.hourPlural": " houws ",
"time.minute": " minyute ",
"time.minutePlural": " minyutes ",
"time.second": " second ",
"time.secondPlural": " seconds ",
"command.about.usage": "",
"command.about.desc": "About the bot",
"command.cb.usage": " <command>",
"command.cb.desc": "Wun a command in a command bwock",
"command.cloop.usage": " add <wate> <command>|| remove <index>|| list|| clear",
"command.cloop.desc": "Manage command woops",
"command.eval.usage": " <code>",
"command.eval.desc": "Wun JavaScwipt code",
"command.help.usage": " [cmd]",
"command.help.desc": "Shows command hewp",
"command.logoff.usage": "",
"command.logoff.desc": "Disconnyect and weconnyect the bot fwom a sewvew",
"command.netmsg.usage": " <message>",
"command.netmsg.desc": "Send a message to aww sewvews the bot is connyected to",
"command.refill.usage": "",
"command.refill.desc": "Wefiww cowe",
"command.say.usage": " <message>",
"command.say.desc": "Sends a message to chat",
"command.serverinfo.usage": "",
"command.serverinfo.desc": "Get system/bot info",
"command.stop.usage": "",
"command.stop.desc": "Westawt bot",
"command.template.usage": " <wequiwed> [optionyaw]",
"command.template.desc": "Does nyothing",
"command.tpr.desc": "Tewepowt to a wandom wocation",
"command.tpr.usage": "",
"command.verify.usage": " [awgs...]",
"command.verify.desc": "Check the hashing system",
"command.about.author": "%s - a Minyecwaft bot made by %s",
"command.about.version": "Vewsion %s",
"command.about.preRelease": "This is pwewewease softwawe - thewe may be ewwows, and featuwes may be changed ow wemoved at any time.",
"command.about.sourceCode": "Souwce code: %s",
"command.about.sourceCode.openInBrowser": "Cwick to open the souwce code wink in youw defauwt bwowsew",
"command.about.serverinfo": "To view system infowmation, wun the command %s.",
"command.cloop.error.tooShort": "Command woops must have a wate above 20ms.",
"command.cloop.error.subcommand": "Unknyown subcommand, pwease do %s",
"command.cloop.success.add": "Added command woop with command %s and wate %s",
"command.cloop.success.remove": "Wemoved command woop %s",
"command.cloop.success.clear": "Cweawed aww command woops",
"command.cloop.list": "%s: Command: %s Wate: %s",
"command.help.cmdList": "Commands:",
"command.help.commandInfo": "%s%s - %s",
"command.help.commandUsage": "Usage - %s%s",
"command.help.commandDesc": "Descwiption - %s",
"command.help.commandPerms": "Wequiwed pewmissions - %s",
"command.help.permsNormal": "Nyowmaw",
"command.help.permsTrusted": "Twusted",
"command.help.permsOwner": "Ownyew",
"command.help.permsConsole": "Consowe",
"command.help.noCommand": "Command does nyot exist",
"command.help.alias": "Awias to %s",
"command.tpr.success": "Tewepowting %s to %s, %s, %s",
"command.verify.success": "Successfuwwy vewified with pewmission wevew %s",
"command.error": "An ewwow occuwed (check consowe fow mowe info)",
"command.disallowed.perms": "You do nyot have pewmission to wun this command. If you do have pewmission, pwease make suwe you put the command hash at the end, ow wan the command thwough youw cwient's hashing system.",
"command.disallowed.perms.yourLevel": "Youw pewmission wevew: %s",
"command.disallowed.perms.cmdLevel": "Command wequiwes: %s",
"copyText": "Cwick to copy!"
}

View file

@ -1,3 +1,4 @@
const crypto = require('crypto')
const rsg = function (count) {
let output = ''
for (let i = 0; i < count; i++) {
@ -29,9 +30,19 @@ const rsg = function (count) {
}
return output
}
const rsgLegal = function (count) {
let output = ''
if (Math.random() > 0.5) {
output += 'uwu_'
} else {
output += 'owo_'
}
output += crypto.randomBytes(count).toString('hex')
return output
}
module.exports = function (legal) {
if (legal) {
return Math.floor(Math.random() * 1000000).toString()
return rsgLegal(6)
} else {
return rsg(6 + Math.floor(Math.random() * 3))
}

View file

@ -1,7 +1,7 @@
{
"botName": "botvX Dev",
"botVersion": "10.0.0-beta.2",
"botVersion": "10.1.0-alpha.1",
"botAuthor": "a5a06d596f15c7db",
"isPreRelease": true,
"sourceURL": "https://code.chipmunk.land/7cc5c4f330d47060/botvX"
}
}