diff --git a/main/chatparser.js b/main/chatparser.js index 888b820..4c958d5 100644 --- a/main/chatparser.js +++ b/main/chatparser.js @@ -1,5 +1,4 @@ const lang = require("./en_us.json"); // translate message -const nbt = require('prismarine-nbt'); function uuidFromIntArray (arr) { const buf = Buffer.alloc(16) @@ -7,18 +6,33 @@ function uuidFromIntArray (arr) { return buf.toString('hex') } -function processNbtMessage(msg) { +function simplify (data) { try { - if (typeof msg === 'string') { // just create simple nbt structure - msg = { - id: 'string', - value: msg - }; + if (data === undefined) return data; + function transform (value, type) { + if (type === 'compound') { + return Object.keys(value).reduce(function (acc, key) { + acc[key] = simplify(value[key]) + return acc + }, {}) + } + if (type === 'list') { + return value.value.map(function (v) { return transform(v, value.type) }) + } + return value } + return transform(data.value, data.type) + } catch (e) { + return e; + } +} - if (!msg || msg.type === 'end') return msg; +function processNbtMessage (msg) { + try { + if (!msg || msg.type === 'end') return null; + if (typeof msg === 'string') return msg - const simplified = nbt.simplify(msg); // Ensure nbt is defined elsewhere + const simplified = simplify(msg); // Ensure nbt is defined elsewhere const json = JSON.stringify(simplified, (key, val) => { if (key === 'id' && Array.isArray(val)) return uuidFromIntArray(val); return val; @@ -26,80 +40,85 @@ function processNbtMessage(msg) { return json; } catch (e) { - return `{"text":"${e}"}`; + return e; } } function inject(bot) { -let playerchat = {}; + +let playerchat = {}; // im think this can fix memory leak. let systemchat = {}; let profilelesschat = {}; - -bot.on('end', () => { // im think this can fix memory leak. - playerchat = {}; delete playerchat; - systemchat = {}; delete systemchat; - profilelesschat = {}; delete profilelesschat; -}) +let actionbar = {}; bot.on('system_chat', (packet) => { // system - if (packet.isActionBar) return; + if (packet.isActionBar) { + actionbar = {}; + console.log(`Actionbar: ${packet}`); + return; + }; + systemchat = {}; - systemchat.jsonMsg = JSON.parse(processNbtMessage(packet.content)); - systemchat.message = parseMinecraftMessage(processNbtMessage(packet.content)); - systemchat.nocolor_message = parseMinecraftMessageNoColor(processNbtMessage(packet.content)); - bot.emit('Custom_SystemChat', systemchat.message, systemchat); - bot.emit('Custom_AllChat', systemchat.message, systemchat); + systemchat.jsonMsg = simplify(packet.content); + systemchat.message = parseMinecraftMessage(simplify(packet.content)); + systemchat.nocolor_message = parseMinecraftMessageNoColor(simplify(packet.content)); + + bot.emit('custom_systemchat', systemchat.message, systemchat, packet); + bot.emit('custom_allchat', systemchat.message, systemchat, packet); + }); -bot.on('profileless_chat', (packet) => { // sudo and vanish +bot.on('profileless_chat', (packet) => { // kinda player_chat + profilelesschat = {}; profilelesschat.type = packet.type - profilelesschat.formattedMessage = parseMinecraftMessage(processNbtMessage(packet.message)) - profilelesschat.senderName = parseMinecraftMessage(processNbtMessage(packet.name)) - profilelesschat.targetName = parseMinecraftMessage(processNbtMessage(packet.target)) - - profilelesschat.nocolor_formattedMessage = parseMinecraftMessageNoColor(processNbtMessage(packet.message)) - profilelesschat.nocolor_senderName = parseMinecraftMessageNoColor(processNbtMessage(packet.name)) - profilelesschat.nocolor_targetName = parseMinecraftMessageNoColor(processNbtMessage(packet.target)) + profilelesschat.formattedMessage = simplify(packet.message); + profilelesschat.senderName = simplify(packet.name); + profilelesschat.targetName = simplify(packet.target); switch (profilelesschat.type) { - case 1: // /me text - profilelesschat.message = `* ${profilelesschat.senderName} ${profilelesschat.formattedMessage}`; - profilelesschat.nocolor_msg = `* ${profilelesschat.nocolor_senderName} ${profilelesschat.nocolor_formattedMessage}`; + case 1: // /minecraft:me + profilelesschat.message = parseMinecraftMessage({ "translate": "chat.type.emote", "with": [ profilelesschat.senderName, profilelesschat.formattedMessage ]}); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ "translate": "chat.type.emote", "with": [ profilelesschat.senderName, profilelesschat.formattedMessage ]}); break; - case 2: // someone /tell you text - profilelesschat.message = `${profilelesschat.senderName} whispers to you: ${profilelesschat.formattedMessage}`; - profilelesschat.nocolor_msg = `${profilelesschat.nocolor_senderName} whispers to you: ${profilelesschat.nocolor_formattedMessage}`; + case 2: // player /minecraft:tell + profilelesschat.message = parseMinecraftMessage({ translate: "commands.message.display.incoming", with: [ profilelesschat.senderName, profilelesschat.formattedMessage ], color: "gray", italic: true }); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ translate: "commands.message.display.incoming", with: [ profilelesschat.senderName, profilelesschat.formattedMessage ], color: "gray", italic: true }); break; - case 3: // you /tell someone text - profilelesschat.message = `You whisper to ${profilelesschat.targetName}: ${profilelesschat.formattedMessage}`; - profilelesschat.nocolor_msg = `You whisper to ${profilelesschat.nocolor_targetName}: ${profilelesschat.nocolor_formattedMessage}`; + case 3: // you /minecraft:tell + profilelesschat.message = parseMinecraftMessage({ translate: "commands.message.display.outgoing", with: [ profilelesschat.targetName, profilelesschat.formattedMessage ], color: "gray", italic: true }); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ translate: "commands.message.display.outgoing", with: [ profilelesschat.targetName, profilelesschat.formattedMessage ], color: "gray", italic: true }); break; case 4: // player chat - profilelesschat.message = profilelesschat.formattedMessage; - profilelesschat.nocolor_msg = profilelesschat.nocolor_formattedMessage; + profilelesschat.message = parseMinecraftMessage(profilelesschat.formattedMessage); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor(profilelesschat.formattedMessage); break; - case 5: // /say text - profilelesschat.message = `[${profilelesschat.senderName}] ${profilelesschat.formattedMessage}`; - profilelesschat.nocolor_msg = `[${profilelesschat.nocolor_senderName}] ${profilelesschat.nocolor_formattedMessage}`; + case 5: // + profilelesschat.message = parseMinecraftMessage({ translate: 'chat.type.announcement', with: [ profilelesschat.senderName, profilelesschat.formattedMessage ]}); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ translate: 'chat.type.announcement', with: [ profilelesschat.senderName, profilelesschat.formattedMessage ]}); break; - case 6: // /minecraft:teammsg text - profilelesschat.message = `${profilelesschat.targetName} <${profilelesschat.senderName}> ${profilelesschat.formattedMessage}`; - profilelesschat.nocolor_msg = `${profilelesschat.nocolor_targetName} <${profilelesschat.nocolor_senderName}> ${profilelesschat.formattedMessage}`; + case 6: // player /minecraft:teammsg || /teammsg + profilelesschat.message = parseMinecraftMessage({ translate: 'chat.type.team.text', with: [ profilelesschat.targetName, profilelesschat.senderName, profilelesschat.formattedMessage ]}); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ translate: 'chat.type.team.text', with: [ profilelesschat.targetName, profilelesschat.senderName, profilelesschat.formattedMessage ]}); break; + case 7: // you /minecraft:teammsg || /teammsg + profilelesschat.message = parseMinecraftMessage({ translate: 'chat.type.team.sent', with: [ profilelesschat.targetName, profilelesschat.senderName, profilelesschat.formattedMessage ]}); + profilelesschat.nocolor_msg = parseMinecraftMessageNoColor({ translate: 'chat.type.team.sent', with: [ profilelesschat.targetName, profilelesschat.senderName, profilelesschat.formattedMessage ]}); + break; default: - console.log(`Unknown player_chat packet. Type: ${profilelesschat.type}`); + console.log(`Unknown player_chat packet. Type: ${playerchat.type}`); console.log(packet); break; } - - bot.emit('Custom_ProfilelessChat', profilelesschat.message, profilelesschat) - bot.emit('Custom_AllChat', profilelesschat.message, profilelesschat) + + bot.emit('custom_profilelesschat', profilelesschat.message, profilelesschat, packet) + bot.emit('custom_allchat', profilelesschat.message, profilelesschat, packet) }) + bot.on('player_chat', (packet) => { // player playerchat = {}; @@ -107,48 +126,46 @@ bot.on('player_chat', (packet) => { // player playerchat.type = packet.type; playerchat.sender = packet.senderUuid; - playerchat.unsignedContent = parseMinecraftMessage(processNbtMessage(packet.unsignedChatContent)); - playerchat.formattedMessage = parseMinecraftMessage(processNbtMessage(packet.formattedMessage)); - playerchat.senderName = parseMinecraftMessage(processNbtMessage(packet.networkName)); - playerchat.targetName = parseMinecraftMessage(processNbtMessage(packet.networkTargetName)); - - playerchat.nocolor_unsignedContent = parseMinecraftMessageNoColor(processNbtMessage(packet.unsignedChatContent)); - playerchat.nocolor_formattedMessage = parseMinecraftMessageNoColor(processNbtMessage(packet.formattedMessage)); - playerchat.nocolor_senderName = parseMinecraftMessageNoColor(processNbtMessage(packet.networkName)); - playerchat.nocolor_targetName = parseMinecraftMessageNoColor(processNbtMessage(packet.networkTargetName)); - - switch (playerchat.type) { - case 1: // /me text - playerchat.message = `* ${playerchat.senderName} ${playerchat.formattedMessage}`; - playerchat.nocolor_msg = `* ${playerchat.nocolor_senderName} ${playerchat.nocolor_formattedMessage}`; + playerchat.unsignedContent = simplify(packet.unsignedChatContent); + playerchat.senderName = simplify(packet.networkName); + playerchat.targetName = simplify(packet.networkTargetName); + + switch (playerchat.type) { // vanish off + case 1: // /minecraft:me + playerchat.message = parseMinecraftMessage({ "translate": "chat.type.emote", "with": [ playerchat.senderName, playerchat.plainMessage ]}); + playerchat.nocolor_msg = parseMinecraftMessageNoColor({ "translate": "chat.type.emote", "with": [ playerchat.senderName, playerchat.plainMessage ]}); break; - case 2: // someone /tell you text - playerchat.message = `${playerchat.senderName} whispers to you: ${playerchat.plainMessage || playerchat.formattedMessage}`; - playerchat.nocolor_msg = `${playerchat.nocolor_senderName} whispers to you: ${playerchat.plainMessage || playerchat.nocolor_formattedMessage}`; + case 2: // player /minecraft:tell + playerchat.message = parseMinecraftMessage({ translate: "commands.message.display.incoming", with: [ playerchat.senderName, playerchat.plainMessage ], color: "gray", italic: true }); + playerchat.nocolor_msg = parseMinecraftMessageNoColor({ translate: "commands.message.display.incoming", with: [ playerchat.senderName, playerchat.plainMessage ], color: "gray", italic: true }); break; - case 3: // you /tell someone text - playerchat.message = `You whisper to ${playerchat.targetName}: ${playerchat.plainMessage || playerchat.formattedMessage}`; - playerchat.nocolor_msg = `You whisper to ${playerchat.nocolor_targetName}: ${playerchat.plainMessage || playerchat.nocolor_formattedMessage}`; + case 3: // you /minecraft:tell + playerchat.message = parseMinecraftMessage({ translate: "commands.message.display.outgoing", with: [ playerchat.targetName, playerchat.plainMessage ], color: "gray", italic: true }); + playerchat.nocolor_msg = parseMinecraftMessageNoColor({ translate: "commands.message.display.outgoing", with: [ playerchat.targetName, playerchat.plainMessage ], color: "gray", italic: true }); break; case 4: // player chat - playerchat.message = playerchat.unsignedContent; - playerchat.nocolor_msg = playerchat.nocolor_unsignedContent; + playerchat.message = parseMinecraftMessage(playerchat.unsignedContent); + playerchat.nocolor_msg = parseMinecraftMessageNoColor(playerchat.unsignedContent); break; - case 5: // /say text - playerchat.message = `[${playerchat.senderName}] ${playerchat.plainMessage || playerchat.formattedMessage}`; - playerchat.nocolor_msg = `[${playerchat.nocolor_senderName}] ${playerchat.plainMessage || playerchat.nocolor_formattedMessage}`; + case 5: // + playerchat.message = parseMinecraftMessage({ translate: 'chat.type.announcement', with: [ playerchat.senderName, playerchat.plainMessage ]}); + playerchat.nocolor_msg = parseMinecraftMessageNoColor({ translate: 'chat.type.announcement', with: [ playerchat.senderName, playerchat.plainMessage ]}); break; - case 6: // /minecraft:teammsg text - playerchat.message = `${playerchat.targetName} <${playerchat.senderName}> ${playerchat.plainMessage || playerchat.formattedMessage}`; - playerchat.nocolor_msg = `${playerchat.nocolor_targetName} <${playerchat.nocolor_senderName}> ${playerchat.plainMessage || playerchat.formattedMessage}`; + case 6: // player /minecraft:teammsg || /teammsg + playerchat.message = parseMinecraftMessage({ translate: 'chat.type.team.text', with: [ playerchat.targetName, playerchat.senderName, playerchat.plainMessage ]}); + playerchat.nocolor_msg = parseMinecraftMessageNoColor({ translate: 'chat.type.team.text', with: [ playerchat.targetName, playerchat.senderName, playerchat.plainMessage ]}); break; + case 7: // you /minecraft:teammsg || /teammsg + playerchat.message = parseMinecraftMessage({ translate: 'chat.type.team.sent', with: [ playerchat.targetName, playerchat.senderName, playerchat.plainMessage ]}); + playerchat.message = parseMinecraftMessageNoColor({ translate: 'chat.type.team.sent', with: [ playerchat.targetName, playerchat.senderName, playerchat.plainMessage ]}); + break; default: console.log(`Unknown player_chat packet. Type: ${playerchat.type}`); console.log(packet); break; } - bot.emit('Custom_PlayerChat', playerchat.message, playerchat); - bot.emit('Custom_AllChat', playerchat.message, playerchat) + bot.emit('custom_playerchat', playerchat.message, playerchat, packet); + bot.emit('custom_allchat', playerchat.message, playerchat, packet) }); } @@ -172,24 +189,16 @@ const ansiFormatCodes = { function parseMinecraftMessage(component) { if (component === undefined) return; - - let jsonComponent; - try { - jsonComponent = JSON.parse(component); - } catch (e) { - console.error("Invalid JSON format:", component); - return ''; - } function extractText(comp) { let text = ''; - if (comp.text && typeof comp.text === 'string' || typeof comp.text === 'number' && comp.text !== undefined && comp.text !== null) { + if (comp.text && typeof comp.text === 'string' || typeof comp.text === 'number') { text += comp.text; } - if (comp[""] && typeof comp[""] === 'string' || typeof comp[""] === 'number' && comp[""] !== undefined && comp[""] !== null) { + if (comp[""] && typeof comp[""] === 'string' || typeof comp[""] === 'number') { text += comp[""]; } - if (comp && typeof comp === 'string' || typeof comp === 'number' && comp !== undefined && comp !== null) { + if (comp && typeof comp === 'string' || typeof comp === 'number') { return comp; } @@ -200,63 +209,83 @@ function parseMinecraftMessage(component) { }); } - if (comp.translate) { + if (comp.translate) { // im cook. let translateString = lang[comp.translate] || comp.translate; + let DefaultTranslateString = lang[comp.translate] || comp.translate; + let DefaultMsg = false; if (comp.with) { const withArgs = comp.with.map(arg => extractText(arg)); - withArgs.forEach((arg, index) => { - const regex = new RegExp(`%${index + 1}\\$s`, 'g'); - const formattedArg = formatfunction(comp, arg); - if (translateString.length + formattedArg.length > 2048) return text += 'Translate Crash'; - translateString = translateString.replace(regex, formattedArg); - }); - let usedReplacements = 0; - translateString = translateString.replace(/%s/g, () => { + translateString = translateString.replace(/%s/g, (match, offset, string) => { + if (offset > 0 && string[offset - 1] === '%') { + return 's'; + } + if (usedReplacements < withArgs.length) { const formattedArg = formatfunction(comp, withArgs[usedReplacements]); if (translateString.length + formattedArg.length > 2048) return 'Translate Crash'; usedReplacements++; return formattedArg; } + + DefaultMsg = true; return "%s"; }); + + translateString = translateString.replace(/%(\d+)\$s/g, (match, index, stringindex, string) => { + const argIndex = parseInt(index) - 1; + + if (argIndex >= withArgs.length) { + DefaultMsg = true; + return match; + } + + if (stringindex > 0 && string[stringindex - 1] === '%') { + return match; + } + + const formattedArg = formatfunction(comp, withArgs[argIndex]); + if (translateString.length + formattedArg.length > 2048) return 'Translate Crash'; + return formattedArg; + }); } - text += translateString; + if (DefaultMsg) { + text += formatfunction(comp, DefaultTranslateString); + } else { + text += formatfunction(comp, translateString); + } } + + text = parseMinecraftColor(comp.color) + parseMinecraftFormat(comp) + text + ansiFormatCodes['reset']; return text; } - return extractText(jsonComponent) + ansiFormatCodes['reset']; + + return extractText(component) + ansiFormatCodes['reset']; } function parseMinecraftMessageNoColor(component) { if (component === undefined) return; - let jsonComponent; - try { - jsonComponent = JSON.parse(component); - } catch (e) { - console.error("Invalid JSON format:", component); - } function extractText(comp) { let text = ''; - if (comp.text && typeof comp.text === 'string' || typeof comp.text === 'number' && comp.text !== undefined && comp.text !== null) { + if (comp.text && typeof comp.text === 'string' || typeof comp.text === 'number') { text += comp.text; } - if (comp[""] && typeof comp[""] === 'string' || typeof comp[""] === 'number' && comp[""] !== undefined && comp[""] !== null) { + if (comp[""] && typeof comp[""] === 'string' || typeof comp[""] === 'number') { text += comp[""]; } - if (comp && typeof comp === 'string' || typeof comp === 'number' && comp !== undefined && comp !== null) { + if (comp && typeof comp === 'string' || typeof comp === 'number') { return comp; } + if (comp.extra) { if (!Array.isArray(comp.extra)) comp.extra = [comp.extra] comp.extra.forEach(subComp => { @@ -266,39 +295,60 @@ function parseMinecraftMessageNoColor(component) { if (comp.translate) { let translateString = lang[comp.translate] || comp.translate; + let DefaultTranslateString = lang[comp.translate] || comp.translate; + let DefaultMsg = false; if (comp.with) { const withArgs = comp.with.map(arg => extractText(arg)); - withArgs.forEach((arg, index) => { - const regex = new RegExp(`%${index + 1}\\$s`, 'g'); - const formattedArg = arg; - if (translateString.length + formattedArg.length > 2048) return 'Translate Crash'; - translateString = translateString.replace(regex, formattedArg); - }); - let usedReplacements = 0; - translateString = translateString.replace(/%s/g, () => { + translateString = translateString.replace(/%s/g, (match, offset, string) => { + if (offset > 0 && string[offset - 1] === '%') { + return 's'; + } + if (usedReplacements < withArgs.length) { const formattedArg = withArgs[usedReplacements]; if (translateString.length + formattedArg.length > 2048) return 'Translate Crash'; usedReplacements++; return formattedArg; } + + DefaultMsg = true; return "%s"; }); + + translateString = translateString.replace(/%(\d+)\$s/g, (match, index, stringindex, string) => { + const argIndex = parseInt(index) - 1; + + if (argIndex >= withArgs.length) { + DefaultMsg = true; + return match; + } + + if (stringindex > 0 && string[stringindex - 1] === '%') { + return match; + } + + const formattedArg = withArgs[argIndex]; + if (translateString.length + formattedArg.length > 2048) return 'Translate Crash'; + return formattedArg; + }); } - text += translateString; + if (DefaultMsg) { + text += DefaultTranslateString; + } else { + text += translateString; + } } return text; } - msgText = extractText(jsonComponent) - // msgText = msgText.replace('ยง', '&') - return msgText; + + return extractText(component); } function formatfunction(comp, text) { diff --git a/main/main.js b/main/main.js index 4e36139..60a1fe6 100644 --- a/main/main.js +++ b/main/main.js @@ -5,30 +5,33 @@ const bot = mc.createClient({ // host: 'chipmunk.land', // host: '168.100.225.224', // Neko port: 25565, - username: 'catparser', // cat + username: 'susparser', // cat version: "1.20.4", }); ChatParse.inject(bot); // load chatparser function -bot.on('custom_PlayerChat', (message, playerchat) => { +bot.on('custom_playerchat', (message, playerchat, packet) => { console.log(`PlayerChat: ${message}`); }); -bot.on('Custom_SystemChat', (message, systemchat) => { +bot.on('custom_systemchat', (message, systemchat, packet) => { console.log(`SystemChat: ${message}`); }); -bot.on('Custom_ProfilelessChat', (message, profilelesschat) => { +bot.on('custom_profilelesschat', (message, profilelesschat, packet) => { console.log(`ProfilelessChat: ${message}`) -}) +}); /* -bot.on('Custom_AllChat', (message, chat) => { + +bot.on('custom_allchat', (message, chat, packet) => { console.log(`AllChat: ${message}`) }) + */ + bot.on('error', (err) => { console.error('Bot Error:', err); });