optimize receive

This commit is contained in:
yincong
2026-05-08 11:27:36 +08:00
parent e0989d288b
commit dc7925edd2
7 changed files with 841 additions and 404 deletions
+9 -404
View File
@@ -82,268 +82,6 @@ function generateAESKey() {
return key;
}
function getProtobufRawBytes(pBuffer, scanSize) {
const tags = [0x12, 0x1A, 0x2A, 0x42, 0x52, 0x5A];
let uint8Array;
try {
const mem = pBuffer.readByteArray(scanSize);
if (!mem) return [];
uint8Array = new Uint8Array(mem);
} catch (e) {
console.error("读取内存失败: " + e);
return [];
}
let finalResults = [];
let i = 0x1a;
tags.forEach(targetTag => {
let found = false;
for (; i < uint8Array.length; i++) {
if (uint8Array[i] === targetTag) {
// 1. 解析 Varint 长度 (支持 1-5 字节长度标识)
let length = 0;
let shift = 0;
let bytesReadForLen = 0;
i = i + 1;
let lenNum = 0;
while (i < uint8Array.length) {
let b = uint8Array[i];
length |= (b & 0x7F) << shift;
bytesReadForLen++;
i++;
lenNum++;
if (!(b & 0x80)) break;
shift += 7;
}
// 2. 截取原始 Byte 数据
if (i + length <= uint8Array.length) {
let addNum = 0
if (targetTag === 0x12 || targetTag === 0x1A || targetTag === 0x2A) {
addNum = lenNum + 1;
}
let rawData = uint8Array.slice(i, i + length);
if (targetTag === 0x42) {
finalResults.push(rawData);
} else {
finalResults.push(decodeProtobufString(rawData));
}
i += length;
} else {
finalResults.push(null); // 长度越界
}
found = true;
break; // 找到第一个匹配的 Tag 就跳出
}
}
if (!found) finalResults.push(null); // 未找到该 Tag
});
for (; i < uint8Array.length; i++) {
if (uint8Array[i] === 0x60 && i + 10 <= uint8Array.length) {
finalResults.push(uint8Array.slice(i + 1, i + 10))
}
}
return finalResults;
}
function getCleanString(uint8Array) {
var out = "";
var i = 0;
var len = uint8Array.length;
while (i < len) {
var c = uint8Array[i++];
// 1. 处理单字节 (ASCII: 0xxxxxxx)
if (c < 0x80) {
// 只保留可见字符 (Space 32 到 ~ 126)
if (c >= 32 && c <= 126) {
out += String.fromCharCode(c);
}
}
// 2. 处理双字节 (110xxxxx 10xxxxxx)
else if ((c & 0xE0) === 0xC0 && i < len) {
var c2 = uint8Array[i++];
if ((c2 & 0xC0) === 0x80) {
// 这种通常是特殊拉丁字母等,按需保留
var charCode = ((c & 0x1F) << 6) | (c2 & 0x3F);
out += String.fromCharCode(charCode);
} else {
i--;
}
}
// 3. 处理三字节 (1110xxxx 10xxxxxx 10xxxxxx) -> 绝大多数汉字在此
else if ((c & 0xF0) === 0xE0 && i + 1 < len) {
var c2 = uint8Array[i++];
var c3 = uint8Array[i++];
if ((c2 & 0xC0) === 0x80 && (c3 & 0xC0) === 0x80) {
var charCode = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);
if (
(charCode >= 0x4E00 && charCode <= 0x9FA5) || // 基本汉字
(charCode >= 0x3000 && charCode <= 0x303F) || // 常用中文标点 (。,、)
(charCode >= 0xFF00 && charCode <= 0xFFEF) || // 全角符号/标点 (!:?)
(charCode >= 0x2000 && charCode <= 0x206F) || // 常用标点扩展 (含 \u2005)
(charCode >= 0x3400 && charCode <= 0x4DBF) // 扩展 A 区汉字
) {
out += String.fromCharCode(charCode);
}
} else {
i -= 2;
}
} else if ((c & 0xF8) === 0xF0 && i + 2 < len) {
var c2 = uint8Array[i++];
var c3 = uint8Array[i++];
var c4 = uint8Array[i++];
if ((c2 & 0xC0) === 0x80 && (c3 & 0xC0) === 0x80 && (c4 & 0xC0) === 0x80) {
// 计算 Unicode 码点
var codePoint = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F);
// Emoji 范围通常在 U+1F000 到 U+1F9FF 之间
if (codePoint >= 0x1F000 && codePoint <= 0x1FADF) {
// 使用 fromCodePoint 处理 4 字节字符
out += String.fromCodePoint(codePoint);
}
} else {
i -= 3;
}
}
}
return out;
}
function readProtobufVarint(uint8Array, offset) {
let value = 0;
let shift = 0;
for (let i = offset; i < uint8Array.length && i - offset < 10; i++) {
const b = uint8Array[i];
value += (b & 0x7F) * Math.pow(2, shift);
if ((b & 0x80) === 0) {
return {value: value, nextOffset: i + 1};
}
shift += 7;
}
return null;
}
function decodeProtobufString(uint8Array, depth) {
if (depth === undefined) {
depth = 0;
}
if (depth < 2) {
const nested = tryDecodeNestedProtobufString(uint8Array, depth);
if (nested !== null) {
return nested;
}
}
return getCleanString(uint8Array);
}
function tryDecodeNestedProtobufString(uint8Array, depth) {
if (!uint8Array || uint8Array.length < 2) {
return null;
}
let offset = 0;
const candidates = [];
while (offset < uint8Array.length) {
const tagInfo = readProtobufVarint(uint8Array, offset);
if (!tagInfo) {
return null;
}
const fieldNumber = Math.floor(tagInfo.value / 8);
const wireType = tagInfo.value & 0x07;
if (fieldNumber <= 0) {
return null;
}
offset = tagInfo.nextOffset;
if (wireType === 2) {
const lenInfo = readProtobufVarint(uint8Array, offset);
if (!lenInfo) {
return null;
}
const valueStart = lenInfo.nextOffset;
const valueEnd = valueStart + lenInfo.value;
if (valueEnd > uint8Array.length) {
return null;
}
const value = uint8Array.slice(valueStart, valueEnd);
const text = decodeProtobufString(value, depth + 1);
if (text) {
candidates.push({fieldNumber: fieldNumber, text: text});
}
offset = valueEnd;
} else if (wireType === 0) {
const valueInfo = readProtobufVarint(uint8Array, offset);
if (!valueInfo) {
return null;
}
offset = valueInfo.nextOffset;
} else if (wireType === 1) {
offset += 8;
if (offset > uint8Array.length) {
return null;
}
} else if (wireType === 5) {
offset += 4;
if (offset > uint8Array.length) {
return null;
}
} else {
return null;
}
}
if (candidates.length === 0) {
return null;
}
for (const candidate of candidates) {
if (candidate.fieldNumber === 2) {
return candidate.text;
}
}
return candidates.map(candidate => candidate.text).join("");
}
function protobufVarintToNumberString(uint8Array) {
let result = BigInt(0);
let shift = BigInt(0);
for (let i = 0; i < uint8Array?.length; i++) {
const byte = uint8Array[i];
// 1. 取出低 7 位并累加到结果中
// (BigInt(byte & 0x7F) << shift)
result += BigInt(byte & 0x7F) << shift;
// 2. 检查最高位 (MSB)。如果为 0,说明这个数字结束了
if ((byte & 0x80) === 0) {
return result.toString();
}
// 3. 准备处理下一个 7 位
shift += BigInt(7);
}
return result.toString();
}
function generateBytes(n) {
// 生成随机字符串
@@ -1858,83 +1596,21 @@ function setReceiver() {
// header: true,
// ansi: true
// }));
const fields = getProtobufRawBytes(currentPtr, x2)
const sender = fields[0]
const receiver = fields[1]
const content = fields[2]
const mediaContent = fields[3]
const xml = fields[4]
const userContent = fields[5]
const msgId = protobufVarintToNumberString(fields[6])
if (typeof sender !== "string" || sender === "" || typeof receiver !== "string" || receiver === "" ||
typeof content !== "string" || content === "" || typeof msgId !== "string" || msgId === "") {
// 过滤非聊天消息:
// 1. field 2 是 varint (tag=0x10) 而非嵌套 message (tag=0x12) → 同步/通知消息
// 2. field 2 wrapper长度 < 128 (单字节varint) → wrapper内容过小,非聊天消息
if (currentPtr.add(2).readU8() === 0x10 || currentPtr.add(2).readU8() === 0x16 || (currentPtr.add(3).readU8() & 0x80) === 0) {
return;
}
var selfId = receiver
var msgType = "private"
var groupId = ""
var senderUser = sender
var senderNickname = ""
var messages = getMessages(content, sender, mediaContent);
if (sender.includes("@chatroom")) {
msgType = "group"
groupId = sender
let splitIndex = content.indexOf(':')
const sendUserStart = content.indexOf('wxid_')
senderUser = content.substring(sendUserStart, splitIndex).trim();
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
const atUser = atUserMatch ? atUserMatch[1] : null;
if (atUser) {
atUser.split(',').forEach(atUser => {
atUser = atUser.trim();
if (atUser) {
messages.push({type: "at", data: {qq: atUser}});
}
});
}
// 处理用户的名称
splitIndex = userContent?.indexOf(':')
if (splitIndex === -1) {
splitIndex = userContent?.indexOf('在群聊中@了你') !== -1 ? userContent?.indexOf('在群聊中@了你') : userContent?.indexOf('在群聊中发了一段语')
senderNickname = userContent?.substring(0, splitIndex).trim();
} else {
senderNickname = userContent?.substring(0, splitIndex).trim();
}
if (!senderNickname) {
senderNickname = senderUser
}
} else {
// 处理用户的名称
const splitIndex = userContent?.indexOf(':')
senderNickname = userContent?.substring(0, splitIndex).trim();
if (!senderNickname) {
senderNickname = senderUser
}
}
const mem = currentPtr.readByteArray(x2);
if (!mem) return;
const uint8Array = new Uint8Array(mem);
send({
time: Date.now(),
post_type: "message",
message_type: msgType,
user_id: senderUser, // 发送人的 ID
self_id: selfId, // 接收人的 ID
group_id: groupId, // 群 ID
message_id: msgId,
type: "send",
raw: {peerUid: msgId},
message: messages,
sender: {user_id: senderUser, nickname: senderNickname},
msgsource: xml,
raw_message: content,
show_content: userContent
type: "protobuf_msg",
data: Array.from(uint8Array),
})
},
});
@@ -2179,75 +1855,4 @@ function triggerDownload(receiver, cdnUrl, aesKey, filePath, fileType) {
return startDwMedia(downloadGlobalX0, downloadFileX1);
}
function getMessages(content, sender, mediaContent) {
var messages = [];
if (sender.includes("@chatroom")) {
let splitIndex = content.indexOf(':')
let pureContent = content.substring(splitIndex + 1).trim();
const parts = pureContent.split('\u2005');
for (let part of parts) {
part = part.trim();
if (part.startsWith("<?xml version=\"1.0\"?><msg><img")) {
messages.push({type: "image", data: {text: part}});
} else if (part.startsWith("<msg><voicemsg")) {
messages.push({type: "record", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><appmsg")) {
const regex = /<type>(.*?)<\/type>/s;
const match = part.match(regex);
if (match.length > 1) {
switch (match[1]) {
case "5":
messages.push({type: "share", data: {text: part}});
break
case "6":
messages.push({type: "file", data: {text: part}});
break
}
}
} else if (part.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: part}});
} else if (part.startsWith("<sysmsg") || part.startsWith("<?xml version=\"1.0\"?><sysmsg")) {
messages.push({type: "sys", data: {text: part}});
} else {
messages.push({type: "text", data: {text: part}});
}
}
} else {
if (content.startsWith("<?xml version=\"1.0\"?><msg><img")) {
messages.push({type: "image", data: {text: content}});
} else if (content.startsWith("<msg><voicemsg")) {
const audioStart = mediaContent.indexOf(2);
if (audioStart !== -1) {
mediaContent = mediaContent.subarray(audioStart);
}
messages.push({type: "record", data: {text: content, media: Array.from(mediaContent)}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><appmsg")) {
const regex = /<type>(.*?)<\/type>/s;
const match = content.match(regex);
if (match.length > 1) {
switch (match[1]) {
case "5":
messages.push({type: "share", data: {text: content}});
break
case "6":
messages.push({type: "file", data: {text: content}});
break
}
}
} else if (content.startsWith("<msg><emoji")) {
messages.push({type: "face", data: {text: content}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: content}});
} else if (content.startsWith("<sysmsg") || content.startsWith("<?xml version=\"1.0\"?><sysmsg")) {
messages.push({type: "sys", data: {text: content}});
} else {
messages.push({type: "text", data: {text: content}});
}
}
return messages;
}
// -------------------------接收消息分区-------------------------