Compare commits

..

6 Commits

Author SHA1 Message Date
手瓜一十雪
fb23087b65 release: 3.1.0 2024-10-21 14:35:57 +08:00
手瓜一十雪
846fee7ac8 fix: error import 2024-10-21 14:33:12 +08:00
手瓜一十雪
977eacc679 try: fix arm64 2024-10-21 14:12:10 +08:00
手瓜一十雪
dacfefe644 style: lint 2024-10-21 10:17:31 +08:00
pk5ls20
345e941e11 chore: remove unnecessary comments 2024-10-21 04:10:53 +08:00
pk5ls20
6cb7d45464 feat & refactor: decouple the forwardMsg construction logic and implement the OB11 element conversion for the forward node. 2024-10-21 04:05:02 +08:00
35 changed files with 406 additions and 333 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "3.0.6", "version": "3.1.0",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "3.0.6", "version": "3.1.0",
"scripts": { "scripts": {
"build:framework": "vite build --mode framework", "build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell", "build:shell": "vite build --mode shell",

View File

@@ -0,0 +1,106 @@
import { PacketMsg } from "@/core/packet/msg/message";
import * as crypto from "node:crypto";
interface ForwardMsgJson {
app: string
config: ForwardMsgJsonConfig,
desc: string,
extra: ForwardMsgJsonExtra,
meta: ForwardMsgJsonMeta,
prompt: string,
ver: string,
view: string
}
interface ForwardMsgJsonConfig {
autosize: number,
forward: number,
round: number,
type: string,
width: number
}
interface ForwardMsgJsonExtra {
filename: string,
tsum: number,
}
interface ForwardMsgJsonMeta {
detail: ForwardMsgJsonMetaDetail
}
interface ForwardMsgJsonMetaDetail {
news: {
text: string
}[],
resid: string,
source: string,
summary: string,
uniseq: string
}
interface ForwardAdaptMsg {
senderName?: string;
isGroupMsg?: boolean;
msg?: ForwardAdaptMsgElement[];
}
interface ForwardAdaptMsgElement {
preview?: string;
}
export class ForwardMsgBuilder {
private static build(resId: string, msg: ForwardAdaptMsg[]): ForwardMsgJson {
const id = crypto.randomUUID();
const isGroupMsg = msg.some(m => m.isGroupMsg);
return {
app: "com.tencent.multimsg",
config: {
autosize: 1,
forward: 1,
round: 1,
type: "normal",
width: 300
},
desc: "[聊天记录]",
extra: {
filename: id,
tsum: msg.length,
},
meta: {
detail: {
news: msg.length === 0 ? [{
text: "Nya~ This message is send from NapCat.Packet!",
}] : msg.map(m => ({
text: `${m.senderName}: ${m.msg?.map(msg => msg.preview).join('')}`,
})),
resid: resId,
source: isGroupMsg ? "群聊的聊天记录" :
msg.length
? Array.from(new Set(msg.map(m => m.senderName)))
.join('和') + '的聊天记录'
: '聊天记录',
summary: `查看${msg.length}条转发消息`,
uniseq: id,
}
},
prompt: "[聊天记录]",
ver: "0.0.0.5",
view: "contact",
};
}
static fromResId(resId: string): ForwardMsgJson {
return this.build(resId, []);
}
static fromPacketMsg(resId: string, packetMsg: PacketMsg[]): ForwardMsgJson {
return this.build(resId, packetMsg.map(msg => ({
senderName: msg.senderName,
isGroupMsg: msg.groupId !== undefined,
msg: msg.msg.map(m => ({
preview: m.toPreview(),
}))
})));
}
}

View File

@@ -1 +1 @@
export const napCatVersion = '3.0.6'; export const napCatVersion = '3.1.0';

View File

@@ -318,7 +318,7 @@ export class NTQQGroupApi {
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> { async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow'); const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
let once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 1, 2000, (params) => params.sceneId === sceneId) const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 1, 2000, (params) => params.sceneId === sceneId)
.catch(); .catch();
const result = await this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, num); const result = await this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, num);
if (result.errCode !== 0) { if (result.errCode !== 0) {

View File

@@ -13,7 +13,7 @@ import { SendLongMsgResp } from "@/core/packet/proto/message/action";
import { PacketMsg } from "@/core/packet/msg/message"; import { PacketMsg } from "@/core/packet/msg/message";
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6"; import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
import { PacketMsgPicElement } from "@/core/packet/msg/element"; import { PacketMsgPicElement } from "@/core/packet/msg/element";
import { c } from 'vite/dist/node/types.d-aGj9QkWt';
interface OffsetType { interface OffsetType {
[key: string]: { [key: string]: {
@@ -27,7 +27,7 @@ const typedOffset: OffsetType = offset;
export class NTQQPacketApi { export class NTQQPacketApi {
context: InstanceContext; context: InstanceContext;
core: NapCatCore; core: NapCatCore;
logger: LogWrapper logger: LogWrapper;
serverUrl: string | undefined; serverUrl: string | undefined;
qqVersion: string | undefined; qqVersion: string | undefined;
packetSession: PacketSession | undefined; packetSession: PacketSession | undefined;
@@ -64,7 +64,7 @@ export class NTQQPacketApi {
if (this.packetSession && this.packetSession.client) { if (this.packetSession && this.packetSession.client) {
this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger)); this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger));
} }
} };
await this.packetSession.client.connect(cb); await this.packetSession.client.connect(cb);
return true; return true;
} }
@@ -112,7 +112,7 @@ export class NTQQPacketApi {
} }
private async uploadResources(msg: PacketMsg[], groupUin: number = 0) { private async uploadResources(msg: PacketMsg[], groupUin: number = 0) {
const reqList = [] const reqList = [];
for (const m of msg) { for (const m of msg) {
for (const e of m.msg) { for (const e of m.msg) {
if (e instanceof PacketMsgPicElement) { if (e instanceof PacketMsgPicElement) {
@@ -143,6 +143,6 @@ export class NTQQPacketApi {
if (resp.download.retCode !== 0) { if (resp.download.retCode !== 0) {
throw new Error(`sendGroupFileDownloadReq error: ${resp.download.clientWording}`); throw new Error(`sendGroupFileDownloadReq error: ${resp.download.clientWording}`);
} }
return `https://${resp.download.downloadDns}/ftn_handler/${Buffer.from(resp.download.downloadUrl).toString('hex')}/?fname=` return `https://${resp.download.downloadDns}/ftn_handler/${Buffer.from(resp.download.downloadUrl).toString('hex')}/?fname=`;
} }
} }

View File

@@ -18,5 +18,9 @@
"3.2.13-28788-x64": { "3.2.13-28788-x64": {
"send": "A0CEC20", "send": "A0CEC20",
"recv": "A0D2520" "recv": "A0D2520"
},
"3.2.13-28788-arm64":{
"send": "6E91018",
"recv": "6E94850"
} }
} }

View File

@@ -42,7 +42,7 @@ export class PacketHighwaySession {
sigSession: null, sigSession: null,
sessionKey: null, sessionKey: null,
serverAddr: [], serverAddr: [],
} };
this.packer = packer; this.packer = packer;
this.packetHighwayClient = new PacketHighwayClient(this.sig, this.logger); this.packetHighwayClient = new PacketHighwayClient(this.sig, this.logger);
} }
@@ -69,8 +69,8 @@ export class PacketHighwaySession {
const rsp = new NapProtoMsg(HttpConn0x6ff_501Response).decode( const rsp = new NapProtoMsg(HttpConn0x6ff_501Response).decode(
Buffer.from(req.hex_data, 'hex') Buffer.from(req.hex_data, 'hex')
); );
this.sig.sigSession = rsp.httpConn.sigSession this.sig.sigSession = rsp.httpConn.sigSession;
this.sig.sessionKey = rsp.httpConn.sessionKey this.sig.sessionKey = rsp.httpConn.sessionKey;
for (const info of rsp.httpConn.serverInfos) { for (const info of rsp.httpConn.serverInfos) {
if (info.serviceType !== 1) continue; if (info.serviceType !== 1) continue;
for (const addr of info.serverAddrs) { for (const addr of info.serverAddrs) {
@@ -78,7 +78,7 @@ export class PacketHighwaySession {
this.sig.serverAddr.push({ this.sig.serverAddr.push({
ip: int32ip2str(addr.ip), ip: int32ip2str(addr.ip),
port: addr.port port: addr.port
}) });
} }
} }
} }
@@ -118,7 +118,7 @@ export class PacketHighwaySession {
hash: { hash: {
fileSha1: [sha1] fileSha1: [sha1]
} }
}) });
await this.packetHighwayClient.upload( await this.packetHighwayClient.upload(
1004, 1004,
fs.createReadStream(img.path, { highWaterMark: BlockSize }), fs.createReadStream(img.path, { highWaterMark: BlockSize }),
@@ -157,7 +157,7 @@ export class PacketHighwaySession {
hash: { hash: {
fileSha1: [sha1] fileSha1: [sha1]
} }
}) });
await this.packetHighwayClient.upload( await this.packetHighwayClient.upload(
1003, 1003,
fs.createReadStream(img.path, { highWaterMark: BlockSize }), fs.createReadStream(img.path, { highWaterMark: BlockSize }),

View File

@@ -53,7 +53,7 @@ abstract class HighwayUploader {
uint32LoginSigType: 8, uint32LoginSigType: 8,
appId: 1600001604, appId: 1600001604,
} }
}) });
} }
abstract upload(): Promise<void>; abstract upload(): Promise<void>;
@@ -90,7 +90,7 @@ export class HighwayTcpUploader extends HighwayUploader {
const upload = new Promise<void>((resolve, _) => { const upload = new Promise<void>((resolve, _) => {
const socket = net.connect(this.trans.port, this.trans.server, () => { const socket = net.connect(this.trans.port, this.trans.server, () => {
this.trans.data.pipe(highwayTransForm).pipe(socket, { end: false }); this.trans.data.pipe(highwayTransForm).pipe(socket, { end: false });
}) });
const handleRspHeader = (header: Buffer) => { const handleRspHeader = (header: Buffer) => {
const rsp = new NapProtoMsg(RespDataHighwayHead).decode(header); const rsp = new NapProtoMsg(RespDataHighwayHead).decode(header);
if (rsp.errorCode !== 0) { if (rsp.errorCode !== 0) {
@@ -111,25 +111,25 @@ export class HighwayTcpUploader extends HighwayUploader {
} catch (e) { } catch (e) {
this.logger.logError(`[Highway] tcpUpload parse response error: ${e}`); this.logger.logError(`[Highway] tcpUpload parse response error: ${e}`);
} }
}) });
socket.on('close', () => { socket.on('close', () => {
this.logger.logDebug('[Highway] tcpUpload socket closed.'); this.logger.logDebug('[Highway] tcpUpload socket closed.');
resolve(); resolve();
}) });
socket.on('error', (err) => { socket.on('error', (err) => {
this.logger.logError('[Highway] tcpUpload socket.on error:', err); this.logger.logError('[Highway] tcpUpload socket.on error:', err);
}) });
this.trans.data.on('error', (err) => { this.trans.data.on('error', (err) => {
this.logger.logError('[Highway] tcpUpload readable error:', err); this.logger.logError('[Highway] tcpUpload readable error:', err);
socket.end(); socket.end();
}) });
}) });
const timeout = new Promise<void>((_, reject) => { const timeout = new Promise<void>((_, reject) => {
setTimeout(() => { setTimeout(() => {
reject(new Error(`[Highway] tcpUpload timeout after ${this.trans.timeout}s`)) reject(new Error(`[Highway] tcpUpload timeout after ${this.trans.timeout}s`));
}, (this.trans.timeout ?? Infinity) * 1000 }, (this.trans.timeout ?? Infinity) * 1000
) );
}) });
await Promise.race([upload, timeout]); await Promise.race([upload, timeout]);
} }
} }
@@ -139,7 +139,7 @@ export class HighwayHttpUploader extends HighwayUploader {
async upload(): Promise<void> { async upload(): Promise<void> {
let offset = 0; let offset = 0;
for await (const chunk of this.trans.data) { for await (const chunk of this.trans.data) {
let block = chunk as Buffer; const block = chunk as Buffer;
try { try {
await this.uploadBlock(block, offset); await this.uploadBlock(block, offset);
} catch (err) { } catch (err) {
@@ -153,7 +153,7 @@ export class HighwayHttpUploader extends HighwayUploader {
private async uploadBlock(block: Buffer, offset: number): Promise<void> { private async uploadBlock(block: Buffer, offset: number): Promise<void> {
const chunkMD5 = crypto.createHash('md5').update(block).digest(); const chunkMD5 = crypto.createHash('md5').update(block).digest();
const payload = this.buildPicUpHead(offset, block.length, chunkMD5); const payload = this.buildPicUpHead(offset, block.length, chunkMD5);
const frame = Frame.pack(Buffer.from(payload), block) const frame = Frame.pack(Buffer.from(payload), block);
const resp = await this.httpPostHighwayContent(frame, `http://${this.trans.server}:${this.trans.port}/cgi-bin/httpconn?htcmd=0x6FF0087&uin=${this.trans.uin}`); const resp = await this.httpPostHighwayContent(frame, `http://${this.trans.server}:${this.trans.port}/cgi-bin/httpconn?htcmd=0x6FF0087&uin=${this.trans.uin}`);
const [head, body] = Frame.unpack(resp); const [head, body] = Frame.unpack(resp);
const headData = new NapProtoMsg(RespDataHighwayHead).decode(head); const headData = new NapProtoMsg(RespDataHighwayHead).decode(head);

View File

@@ -5,7 +5,7 @@ import {NTHighwayIPv4} from "@/core/packet/proto/highway/highway";
export const int32ip2str = (ip: number) => { export const int32ip2str = (ip: number) => {
ip = ip & 0xffffffff; ip = ip & 0xffffffff;
return [ip & 0xff, (ip & 0xff00) >> 8, (ip & 0xff0000) >> 16, ((ip & 0xff000000) >> 24) & 0xff].join('.'); return [ip & 0xff, (ip & 0xff00) >> 8, (ip & 0xff0000) >> 16, ((ip & 0xff000000) >> 24) & 0xff].join('.');
} };
export const oidbIpv4s2HighwayIpv4s = (ipv4s: NapProtoEncodeStructType<typeof IPv4>[]): NapProtoEncodeStructType<typeof NTHighwayIPv4>[] =>{ export const oidbIpv4s2HighwayIpv4s = (ipv4s: NapProtoEncodeStructType<typeof IPv4>[]): NapProtoEncodeStructType<typeof NTHighwayIPv4>[] =>{
return ipv4s.map((ip) => { return ipv4s.map((ip) => {
@@ -15,6 +15,6 @@ export const oidbIpv4s2HighwayIpv4s = (ipv4s: NapProtoEncodeStructType<typeof IP
ip: int32ip2str(ip.outIP!), ip: int32ip2str(ip.outIP!),
}, },
port: ip.outPort! port: ip.outPort!
} as NapProtoEncodeStructType<typeof NTHighwayIPv4> } as NapProtoEncodeStructType<typeof NTHighwayIPv4>;
}) });
} };

View File

@@ -82,50 +82,50 @@ export class PacketMsgConverter {
); );
if (key) { if (key) {
const elementData = (element as any)[key]; // TODO: const elementData = (element as any)[key]; // TODO:
if (elementData) return this.rawToPacketMsgConverters[key](element as any) if (elementData) return this.rawToPacketMsgConverters[key](element as any);
} }
return null; return null;
}).filter((e) => e !== null) }).filter((e) => e !== null)
} };
} }
private rawToPacketMsgConverters: RawToPacketMsgConverters = { private rawToPacketMsgConverters: RawToPacketMsgConverters = {
textElement: (element: SendTextElement) => { textElement: (element: SendTextElement) => {
if (element.textElement.atType) { if (element.textElement.atType) {
return new PacketMsgAtElement(element) return new PacketMsgAtElement(element);
} }
return new PacketMsgTextElement(element) return new PacketMsgTextElement(element);
}, },
picElement: (element: SendPicElement) => { picElement: (element: SendPicElement) => {
return new PacketMsgPicElement(element) return new PacketMsgPicElement(element);
}, },
replyElement: (element: SendReplyElement) => { replyElement: (element: SendReplyElement) => {
return new PacketMsgReplyElement(element) return new PacketMsgReplyElement(element);
}, },
faceElement: (element: SendFaceElement) => { faceElement: (element: SendFaceElement) => {
return new PacketMsgFaceElement(element) return new PacketMsgFaceElement(element);
}, },
marketFaceElement: (element: SendMarketFaceElement) => { marketFaceElement: (element: SendMarketFaceElement) => {
return new PacketMsgMarkFaceElement(element) return new PacketMsgMarkFaceElement(element);
}, },
videoElement: (element: SendVideoElement) => { videoElement: (element: SendVideoElement) => {
return new PacketMsgVideoElement(element) return new PacketMsgVideoElement(element);
}, },
fileElement: (element: SendFileElement) => { fileElement: (element: SendFileElement) => {
return new PacketMsgFileElement(element) return new PacketMsgFileElement(element);
}, },
pttElement: (element: SendPttElement) => { pttElement: (element: SendPttElement) => {
return new PacketMsgPttElement(element) return new PacketMsgPttElement(element);
}, },
arkElement: (element: SendArkElement) => { arkElement: (element: SendArkElement) => {
return new PacketMsgLightAppElement(element) return new PacketMsgLightAppElement(element);
}, },
markdownElement: (element: SendMarkdownElement) => { markdownElement: (element: SendMarkdownElement) => {
return new PacketMsgMarkDownElement(element) return new PacketMsgMarkDownElement(element);
}, },
// TODO: check this logic, move it in arkElement? // TODO: check this logic, move it in arkElement?
structLongMsgElement: (element: SendStructLongMsgElement) => { structLongMsgElement: (element: SendStructLongMsgElement) => {
return new PacketMultiMsgElement(element) return new PacketMultiMsgElement(element);
}
} }
};
} }

View File

@@ -1,6 +1,5 @@
import assert from "node:assert"; import assert from "node:assert";
import * as zlib from "node:zlib"; import * as zlib from "node:zlib";
import * as crypto from "node:crypto";
import { NapProtoEncodeStructType, NapProtoMsg } from "@/core/packet/proto/NapProto"; import { NapProtoEncodeStructType, NapProtoMsg } from "@/core/packet/proto/NapProto";
import { import {
CustomFace, CustomFace,
@@ -28,10 +27,9 @@ import {
} from "@/core"; } from "@/core";
import { MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq"; import { MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message"; import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message";
import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
// raw <-> packet // raw <-> packet
// TODO: check ob11 -> raw impl!
// TODO: parse to raw element
// TODO: SendStructLongMsgElement // TODO: SendStructLongMsgElement
export abstract class IPacketMsgElement<T extends PacketSendMsgElement> { export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
protected constructor(rawElement: T) { protected constructor(rawElement: T) {
@@ -103,7 +101,7 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> { export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
path: string; path: string;
name: string name: string;
size: number; size: number;
md5: string; md5: string;
width: number; width: number;
@@ -133,7 +131,7 @@ export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
pbElem: new NapProtoMsg(MsgInfo).encode(this.msgInfo), pbElem: new NapProtoMsg(MsgInfo).encode(this.msgInfo),
businessType: 10, businessType: 10,
} }
}] }];
} }
toPreview(): string { toPreview(): string {
@@ -187,7 +185,7 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
uid: String(this.targetUid), uid: String(this.targetUid),
}), }),
} : undefined, } : undefined,
}] }];
} }
toPreview(): string { toPreview(): string {
@@ -221,7 +219,7 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
}), }),
businessType: 1 businessType: 1
} }
}] }];
} else if (this.faceId < 260) { } else if (this.faceId < 260) {
return [{ return [{
face: { face: {
@@ -239,7 +237,7 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
}), }),
businessType: 1 businessType: 1
} }
}] }];
} }
} }
@@ -278,11 +276,11 @@ export class PacketMsgMarkFaceElement extends IPacketMsgElement<SendMarketFaceEl
field8: 1 field8: 1
} }
} }
}] }];
} }
toPreview(): string { toPreview(): string {
return this.emojiName; return `[${this.emojiName}]`;
} }
} }
@@ -320,11 +318,11 @@ export class PacketMsgLightAppElement extends IPacketMsgElement<SendArkElement>
zlib.deflateSync(Buffer.from(this.payload, 'utf-8')) zlib.deflateSync(Buffer.from(this.payload, 'utf-8'))
]) ])
} }
}] }];
} }
toPreview(): string { toPreview(): string {
return "[小程序]"; return "[卡片消息]";
} }
} }
@@ -345,11 +343,11 @@ export class PacketMsgMarkDownElement extends IPacketMsgElement<SendMarkdownElem
}), }),
businessType: 1 businessType: 1
} }
}] }];
} }
toPreview(): string { toPreview(): string {
return this.content; return `[Markdown消息 ${this.content}]`;
} }
} }
@@ -363,58 +361,15 @@ export class PacketMultiMsgElement extends IPacketMsgElement<SendStructLongMsgEl
this.message = message ?? []; this.message = message ?? [];
} }
get isGroupMsg(): boolean {
return this.message.some(msg => msg.groupId !== undefined);
}
get JSON() {
const id = crypto.randomUUID();
return {
app: "com.tencent.multimsg",
config: {
autosize: 1,
forward: 1,
round: 1,
type: "normal",
width: 300
},
desc: "[聊天记录]",
extra: {
filename: id,
tsum: this.message.length,
},
meta: {
detail: {
news: this.message.length === 0 ? [{
text: "[Nya~ This message is send from NapCat.Packet!]",
}] : this.message.map(packetMsg => ({
text: `${packetMsg.senderName}: ${packetMsg.msg.map(msg => msg.toPreview()).join('')}`,
})),
resid: this.resid,
source: this.isGroupMsg ? "群聊的聊天记录" :
this.message.length
? Array.from(new Set(this.message.map(msg => msg.senderName)))
.join('和') + '的聊天记录'
: '聊天记录',
summary: `查看${this.message.length}条转发消息`,
uniseq: id,
}
},
prompt: "[聊天记录]",
ver: "0.0.0.5",
view: "contact",
}
}
buildElement(): NapProtoEncodeStructType<typeof Elem>[] { buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
return [{ return [{
lightAppElem: { lightAppElem: {
data: Buffer.concat([ data: Buffer.concat([
Buffer.from([0x01]), Buffer.from([0x01]),
zlib.deflateSync(Buffer.from(JSON.stringify(this.JSON), 'utf-8')) zlib.deflateSync(Buffer.from(JSON.stringify(ForwardMsgBuilder.fromPacketMsg(this.resid, this.message)), 'utf-8'))
]) ])
} }
}] }];
} }
toPreview(): string { toPreview(): string {

View File

@@ -1,6 +1,6 @@
import * as zlib from "node:zlib"; import * as zlib from "node:zlib";
import * as crypto from "node:crypto"; import * as crypto from "node:crypto";
import {calculateSha1} from "@/core/packet/utils/crypto/hash" import { calculateSha1 } from "@/core/packet/utils/crypto/hash";
import { NapProtoMsg } from "@/core/packet/proto/NapProto"; import { NapProtoMsg } from "@/core/packet/proto/NapProto";
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase"; import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202"; import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
@@ -217,7 +217,7 @@ export class PacketPacker {
noNeedCompatMsg: false, noNeedCompatMsg: false,
} }
} }
) );
return this.toHexStr(this.packOidbPacket(0x11c4, 100, req, true, false)); return this.toHexStr(this.packOidbPacket(0x11c4, 100, req, true, false));
} }
@@ -285,7 +285,7 @@ export class PacketPacker {
noNeedCompatMsg: false, noNeedCompatMsg: false,
} }
} }
) );
return this.toHexStr(this.packOidbPacket(0x11c5, 100, req, true, false)); return this.toHexStr(this.packOidbPacket(0x11c5, 100, req, true, false));
} }
@@ -299,7 +299,7 @@ export class PacketPacker {
fileId: fileUUID fileId: fileUUID
} }
}), true, false) }), true, false)
) );
} }
packC2CFileDownloadReq(selfUid: string, fileUUID: string, fileHash: string): PacketHexStr { packC2CFileDownloadReq(selfUid: string, fileUUID: string, fileHash: string): PacketHexStr {
@@ -319,6 +319,6 @@ export class PacketPacker {
field200: 1, field200: 1,
field99999: Buffer.from([0xc0, 0x85, 0x2c, 0x01]) field99999: Buffer.from([0xc0, 0x85, 0x2c, 0x01])
}) })
) );
} }
} }

View File

@@ -12,7 +12,7 @@ export const DataHighwayHead = {
dataFlag: ProtoField(7, ScalarType.UINT32), dataFlag: ProtoField(7, ScalarType.UINT32),
commandId: ProtoField(8, ScalarType.UINT32), commandId: ProtoField(8, ScalarType.UINT32),
buildVer: ProtoField(9, ScalarType.BYTES, true), buildVer: ProtoField(9, ScalarType.BYTES, true),
} };
export const FileUploadExt = { export const FileUploadExt = {
unknown1: ProtoField(1, ScalarType.INT32), unknown1: ProtoField(1, ScalarType.INT32),
@@ -20,7 +20,7 @@ export const FileUploadExt = {
unknown3: ProtoField(3, ScalarType.INT32), unknown3: ProtoField(3, ScalarType.INT32),
entry: ProtoField(100, () => FileUploadEntry), entry: ProtoField(100, () => FileUploadEntry),
unknown200: ProtoField(200, ScalarType.INT32), unknown200: ProtoField(200, ScalarType.INT32),
} };
export const FileUploadEntry = { export const FileUploadEntry = {
busiBuff: ProtoField(100, () => ExcitingBusiInfo), busiBuff: ProtoField(100, () => ExcitingBusiInfo),
@@ -28,14 +28,14 @@ export const FileUploadEntry = {
clientInfo: ProtoField(300, () => ExcitingClientInfo), clientInfo: ProtoField(300, () => ExcitingClientInfo),
fileNameInfo: ProtoField(400, () => ExcitingFileNameInfo), fileNameInfo: ProtoField(400, () => ExcitingFileNameInfo),
host: ProtoField(500, () => ExcitingHostConfig), host: ProtoField(500, () => ExcitingHostConfig),
} };
export const ExcitingBusiInfo = { export const ExcitingBusiInfo = {
busId: ProtoField(1, ScalarType.INT32), busId: ProtoField(1, ScalarType.INT32),
senderUin: ProtoField(100, ScalarType.UINT64), senderUin: ProtoField(100, ScalarType.UINT64),
receiverUin: ProtoField(200, ScalarType.UINT64), receiverUin: ProtoField(200, ScalarType.UINT64),
groupCode: ProtoField(400, ScalarType.UINT64), groupCode: ProtoField(400, ScalarType.UINT64),
} };
export const ExcitingFileEntry = { export const ExcitingFileEntry = {
fileSize: ProtoField(100, ScalarType.UINT64), fileSize: ProtoField(100, ScalarType.UINT64),
@@ -44,7 +44,7 @@ export const ExcitingFileEntry = {
md5S2: ProtoField(400, ScalarType.BYTES), md5S2: ProtoField(400, ScalarType.BYTES),
fileId: ProtoField(600, ScalarType.STRING), fileId: ProtoField(600, ScalarType.STRING),
uploadKey: ProtoField(700, ScalarType.BYTES), uploadKey: ProtoField(700, ScalarType.BYTES),
} };
export const ExcitingClientInfo = { export const ExcitingClientInfo = {
clientType: ProtoField(100, ScalarType.INT32), clientType: ProtoField(100, ScalarType.INT32),
@@ -52,31 +52,31 @@ export const ExcitingClientInfo = {
terminalType: ProtoField(300, ScalarType.INT32), terminalType: ProtoField(300, ScalarType.INT32),
clientVer: ProtoField(400, ScalarType.STRING), clientVer: ProtoField(400, ScalarType.STRING),
unknown: ProtoField(600, ScalarType.INT32), unknown: ProtoField(600, ScalarType.INT32),
} };
export const ExcitingFileNameInfo = { export const ExcitingFileNameInfo = {
fileName: ProtoField(100, ScalarType.STRING), fileName: ProtoField(100, ScalarType.STRING),
} };
export const ExcitingHostConfig = { export const ExcitingHostConfig = {
hosts: ProtoField(200, () => ExcitingHostInfo, false, true), hosts: ProtoField(200, () => ExcitingHostInfo, false, true),
} };
export const ExcitingHostInfo = { export const ExcitingHostInfo = {
url: ProtoField(1, () => ExcitingUrlInfo), url: ProtoField(1, () => ExcitingUrlInfo),
port: ProtoField(2, ScalarType.UINT32), port: ProtoField(2, ScalarType.UINT32),
} };
export const ExcitingUrlInfo = { export const ExcitingUrlInfo = {
unknown: ProtoField(1, ScalarType.INT32), unknown: ProtoField(1, ScalarType.INT32),
host: ProtoField(2, ScalarType.STRING), host: ProtoField(2, ScalarType.STRING),
} };
export const LoginSigHead = { export const LoginSigHead = {
uint32LoginSigType: ProtoField(1, ScalarType.UINT32), uint32LoginSigType: ProtoField(1, ScalarType.UINT32),
bytesLoginSig: ProtoField(2, ScalarType.BYTES), bytesLoginSig: ProtoField(2, ScalarType.BYTES),
appId: ProtoField(3, ScalarType.UINT32), appId: ProtoField(3, ScalarType.UINT32),
} };
export const NTV2RichMediaHighwayExt = { export const NTV2RichMediaHighwayExt = {
fileUuid: ProtoField(1, ScalarType.STRING), fileUuid: ProtoField(1, ScalarType.STRING),
@@ -85,25 +85,25 @@ export const NTV2RichMediaHighwayExt = {
msgInfoBody: ProtoField(6, () => MsgInfoBody, false, true), msgInfoBody: ProtoField(6, () => MsgInfoBody, false, true),
blockSize: ProtoField(10, ScalarType.UINT32), blockSize: ProtoField(10, ScalarType.UINT32),
hash: ProtoField(11, () => NTHighwayHash), hash: ProtoField(11, () => NTHighwayHash),
} };
export const NTHighwayHash = { export const NTHighwayHash = {
fileSha1: ProtoField(1, ScalarType.BYTES, false, true), fileSha1: ProtoField(1, ScalarType.BYTES, false, true),
} };
export const NTHighwayNetwork = { export const NTHighwayNetwork = {
ipv4s: ProtoField(1, () => NTHighwayIPv4, false, true), ipv4s: ProtoField(1, () => NTHighwayIPv4, false, true),
} };
export const NTHighwayIPv4 = { export const NTHighwayIPv4 = {
domain: ProtoField(1, () => NTHighwayDomain), domain: ProtoField(1, () => NTHighwayDomain),
port: ProtoField(2, ScalarType.UINT32), port: ProtoField(2, ScalarType.UINT32),
} };
export const NTHighwayDomain = { export const NTHighwayDomain = {
isEnable: ProtoField(1, ScalarType.BOOL), isEnable: ProtoField(1, ScalarType.BOOL),
ip: ProtoField(2, ScalarType.STRING), ip: ProtoField(2, ScalarType.STRING),
} };
export const ReqDataHighwayHead = { export const ReqDataHighwayHead = {
msgBaseHead: ProtoField(1, () => DataHighwayHead, true), msgBaseHead: ProtoField(1, () => DataHighwayHead, true),
@@ -111,7 +111,7 @@ export const ReqDataHighwayHead = {
bytesReqExtendInfo: ProtoField(3, ScalarType.BYTES, true), bytesReqExtendInfo: ProtoField(3, ScalarType.BYTES, true),
timestamp: ProtoField(4, ScalarType.UINT64), timestamp: ProtoField(4, ScalarType.UINT64),
msgLoginSigHead: ProtoField(5, () => LoginSigHead, true), msgLoginSigHead: ProtoField(5, () => LoginSigHead, true),
} };
export const RespDataHighwayHead = { export const RespDataHighwayHead = {
msgBaseHead: ProtoField(1, () => DataHighwayHead, true), msgBaseHead: ProtoField(1, () => DataHighwayHead, true),
@@ -124,7 +124,7 @@ export const RespDataHighwayHead = {
timestamp: ProtoField(8, ScalarType.UINT64), timestamp: ProtoField(8, ScalarType.UINT64),
range: ProtoField(9, ScalarType.UINT64), range: ProtoField(9, ScalarType.UINT64),
isReset: ProtoField(10, ScalarType.UINT32), isReset: ProtoField(10, ScalarType.UINT32),
} };
export const SegHead = { export const SegHead = {
serviceId: ProtoField(1, ScalarType.UINT32, true), serviceId: ProtoField(1, ScalarType.UINT32, true),
@@ -140,7 +140,7 @@ export const SegHead = {
queryTimes: ProtoField(11, ScalarType.UINT32), queryTimes: ProtoField(11, ScalarType.UINT32),
updateCacheIp: ProtoField(12, ScalarType.UINT32), updateCacheIp: ProtoField(12, ScalarType.UINT32),
cachePort: ProtoField(13, ScalarType.UINT32, true), cachePort: ProtoField(13, ScalarType.UINT32, true),
} };
export const GroupAvatarExtra = { export const GroupAvatarExtra = {
type: ProtoField(1, ScalarType.UINT32), type: ProtoField(1, ScalarType.UINT32),
@@ -148,8 +148,8 @@ export const GroupAvatarExtra = {
field3: ProtoField(3, () => GroupAvatarExtraField3), field3: ProtoField(3, () => GroupAvatarExtraField3),
field5: ProtoField(5, ScalarType.UINT32), field5: ProtoField(5, ScalarType.UINT32),
field6: ProtoField(6, ScalarType.UINT32), field6: ProtoField(6, ScalarType.UINT32),
} };
export const GroupAvatarExtraField3 = { export const GroupAvatarExtraField3 = {
field1: ProtoField(1, ScalarType.UINT32), field1: ProtoField(1, ScalarType.UINT32),
} };

View File

@@ -118,7 +118,7 @@ export const MarketFace = {
export const MarketFacePbRes = { export const MarketFacePbRes = {
field8: ProtoField(8, ScalarType.INT32) field8: ProtoField(8, ScalarType.INT32)
} };
export const CustomFace = { export const CustomFace = {
guid: ProtoField(1, ScalarType.BYTES), guid: ProtoField(1, ScalarType.BYTES),
@@ -315,7 +315,7 @@ export const SrcMsgPbRes = {
senderUid: ProtoField(6, ScalarType.STRING, true), senderUid: ProtoField(6, ScalarType.STRING, true),
receiverUid: ProtoField(7, ScalarType.STRING, true), receiverUid: ProtoField(7, ScalarType.STRING, true),
friendSeq: ProtoField(8, ScalarType.UINT32, true), friendSeq: ProtoField(8, ScalarType.UINT32, true),
} };
export const LightAppElem = { export const LightAppElem = {
data: ProtoField(1, ScalarType.BYTES), data: ProtoField(1, ScalarType.BYTES),
@@ -358,4 +358,4 @@ export const QSmallFaceExtra = {
export const MarkdownData = { export const MarkdownData = {
content: ProtoField(1, ScalarType.STRING) content: ProtoField(1, ScalarType.STRING)
} };

View File

@@ -160,7 +160,7 @@ export const PicUrlExtInfo = {
export const VideoExtInfo = { export const VideoExtInfo = {
VideoCodecFormat: ProtoField(1, ScalarType.UINT32), VideoCodecFormat: ProtoField(1, ScalarType.UINT32),
} };
export const ExtBizInfo = { export const ExtBizInfo = {
Pic: ProtoField(1, () => PicExtBizInfo), Pic: ProtoField(1, () => PicExtBizInfo),

View File

@@ -13,20 +13,20 @@ export const NTV2RichMediaResp = {
uploadKeyRenewal: ProtoField(8, () => UploadKeyRenewalResp), uploadKeyRenewal: ProtoField(8, () => UploadKeyRenewalResp),
downloadSafe: ProtoField(9, () => DownloadSafeResp), downloadSafe: ProtoField(9, () => DownloadSafeResp),
extension: ProtoField(99, ScalarType.BYTES, true), extension: ProtoField(99, ScalarType.BYTES, true),
} };
export const MultiMediaRespHead = { export const MultiMediaRespHead = {
common: ProtoField(1, () => CommonHead), common: ProtoField(1, () => CommonHead),
retCode: ProtoField(2, ScalarType.UINT32), retCode: ProtoField(2, ScalarType.UINT32),
message: ProtoField(3, ScalarType.STRING), message: ProtoField(3, ScalarType.STRING),
} };
export const DownloadResp = { export const DownloadResp = {
rKeyParam: ProtoField(1, ScalarType.STRING), rKeyParam: ProtoField(1, ScalarType.STRING),
rKeyTtlSecond: ProtoField(2, ScalarType.UINT32), rKeyTtlSecond: ProtoField(2, ScalarType.UINT32),
info: ProtoField(3, () => DownloadInfo), info: ProtoField(3, () => DownloadInfo),
rKeyCreateTime: ProtoField(4, ScalarType.UINT32), rKeyCreateTime: ProtoField(4, ScalarType.UINT32),
} };
export const DownloadInfo = { export const DownloadInfo = {
domain: ProtoField(1, ScalarType.STRING), domain: ProtoField(1, ScalarType.STRING),
@@ -36,7 +36,7 @@ export const DownloadInfo = {
ipv6s: ProtoField(5, () => IPv6, false, true), ipv6s: ProtoField(5, () => IPv6, false, true),
picUrlExtInfo: ProtoField(6, () => PicUrlExtInfo), picUrlExtInfo: ProtoField(6, () => PicUrlExtInfo),
videoExtInfo: ProtoField(7, () => VideoExtInfo), videoExtInfo: ProtoField(7, () => VideoExtInfo),
} };
export const IPv4 = { export const IPv4 = {
outIP: ProtoField(1, ScalarType.UINT32), outIP: ProtoField(1, ScalarType.UINT32),
@@ -44,7 +44,7 @@ export const IPv4 = {
inIP: ProtoField(3, ScalarType.UINT32), inIP: ProtoField(3, ScalarType.UINT32),
inPort: ProtoField(4, ScalarType.UINT32), inPort: ProtoField(4, ScalarType.UINT32),
ipType: ProtoField(5, ScalarType.UINT32), ipType: ProtoField(5, ScalarType.UINT32),
} };
export const IPv6 = { export const IPv6 = {
outIP: ProtoField(1, ScalarType.BYTES), outIP: ProtoField(1, ScalarType.BYTES),
@@ -52,7 +52,7 @@ export const IPv6 = {
inIP: ProtoField(3, ScalarType.BYTES), inIP: ProtoField(3, ScalarType.BYTES),
inPort: ProtoField(4, ScalarType.UINT32), inPort: ProtoField(4, ScalarType.UINT32),
ipType: ProtoField(5, ScalarType.UINT32), ipType: ProtoField(5, ScalarType.UINT32),
} };
export const UploadResp = { export const UploadResp = {
uKey: ProtoField(1, ScalarType.STRING, true), uKey: ProtoField(1, ScalarType.STRING, true),
@@ -64,13 +64,13 @@ export const UploadResp = {
ext: ProtoField(7, () => RichMediaStorageTransInfo, false, true), ext: ProtoField(7, () => RichMediaStorageTransInfo, false, true),
compatQMsg: ProtoField(8, ScalarType.BYTES), compatQMsg: ProtoField(8, ScalarType.BYTES),
subFileInfos: ProtoField(10, () => SubFileInfo, false, true), subFileInfos: ProtoField(10, () => SubFileInfo, false, true),
} };
export const RichMediaStorageTransInfo = { export const RichMediaStorageTransInfo = {
subType: ProtoField(1, ScalarType.UINT32), subType: ProtoField(1, ScalarType.UINT32),
extType: ProtoField(2, ScalarType.UINT32), extType: ProtoField(2, ScalarType.UINT32),
extValue: ProtoField(3, ScalarType.BYTES), extValue: ProtoField(3, ScalarType.BYTES),
} };
export const SubFileInfo = { export const SubFileInfo = {
subType: ProtoField(1, ScalarType.UINT32), subType: ProtoField(1, ScalarType.UINT32),
@@ -78,32 +78,32 @@ export const SubFileInfo = {
uKeyTtlSecond: ProtoField(3, ScalarType.UINT32), uKeyTtlSecond: ProtoField(3, ScalarType.UINT32),
ipv4s: ProtoField(4, () => IPv4, false, true), ipv4s: ProtoField(4, () => IPv4, false, true),
ipv6s: ProtoField(5, () => IPv6, false, true), ipv6s: ProtoField(5, () => IPv6, false, true),
} };
export const DownloadSafeResp = { export const DownloadSafeResp = {
} };
export const UploadKeyRenewalResp = { export const UploadKeyRenewalResp = {
ukey: ProtoField(1, ScalarType.STRING), ukey: ProtoField(1, ScalarType.STRING),
ukeyTtlSec: ProtoField(2, ScalarType.UINT64), ukeyTtlSec: ProtoField(2, ScalarType.UINT64),
} };
export const MsgInfoAuthResp = { export const MsgInfoAuthResp = {
authCode: ProtoField(1, ScalarType.UINT32), authCode: ProtoField(1, ScalarType.UINT32),
msg: ProtoField(2, ScalarType.BYTES), msg: ProtoField(2, ScalarType.BYTES),
resultTime: ProtoField(3, ScalarType.UINT64), resultTime: ProtoField(3, ScalarType.UINT64),
} };
export const UploadCompletedResp = { export const UploadCompletedResp = {
msgSeq: ProtoField(1, ScalarType.UINT64), msgSeq: ProtoField(1, ScalarType.UINT64),
} };
export const DeleteResp = { export const DeleteResp = {
} };
export const DownloadRKeyResp = { export const DownloadRKeyResp = {
rKeys: ProtoField(1, () => RKeyInfo, false, true), rKeys: ProtoField(1, () => RKeyInfo, false, true),
} };
export const RKeyInfo = { export const RKeyInfo = {
rkey: ProtoField(1, ScalarType.STRING), rkey: ProtoField(1, ScalarType.STRING),
@@ -111,4 +111,4 @@ export const RKeyInfo = {
storeId: ProtoField(3, ScalarType.UINT32), storeId: ProtoField(3, ScalarType.UINT32),
rkeyCreateTime: ProtoField(4, ScalarType.UINT32, true), rkeyCreateTime: ProtoField(4, ScalarType.UINT32, true),
type: ProtoField(5, ScalarType.UINT32, true), type: ProtoField(5, ScalarType.UINT32, true),
} };

View File

@@ -76,7 +76,7 @@ export function decrypt(encrypted: Buffer, key: Buffer) {
encrypted.writeInt32BE(r1, i); encrypted.writeInt32BE(r1, i);
encrypted.writeInt32BE(r2, i + 4); encrypted.writeInt32BE(r2, i + 4);
} }
if (Buffer.compare(encrypted.subarray(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL if (Buffer.compare(encrypted.subarray(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL;
// if (Buffer.compare(encrypted.slice(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL; // if (Buffer.compare(encrypted.slice(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL;
return encrypted.subarray((encrypted[0] & 0x07) + 3, encrypted.length - 7); return encrypted.subarray((encrypted[0] & 0x07) + 3, encrypted.length - 7);
// return encrypted.slice((encrypted[0] & 0x07) + 3, encrypted.length - 7); // return encrypted.slice((encrypted[0] & 0x07) + 3, encrypted.length - 7);

View File

@@ -27,7 +27,7 @@ export class GetGroupFileUrl extends GetPacketStatusDepends<Payload, GetGroupFil
if (contextMsgFile?.fileUUID) { if (contextMsgFile?.fileUUID) {
return { return {
url: await this.core.apis.PacketApi.sendGroupFileDownloadReq(+payload.group_id, contextMsgFile.fileUUID) url: await this.core.apis.PacketApi.sendGroupFileDownloadReq(+payload.group_id, contextMsgFile.fileUUID)
} };
} }
throw new Error('real fileUUID not found!'); throw new Error('real fileUUID not found!');
} }

View File

@@ -13,7 +13,7 @@ import {ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, Sen
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { rawMsgWithSendMsg } from "@/core/packet/msg/converter"; import { rawMsgWithSendMsg } from "@/core/packet/msg/converter";
import { PacketMsg } from "@/core/packet/msg/message"; import { PacketMsg } from "@/core/packet/msg/message";
import {PacketMultiMsgElement} from "@/core/packet/msg/element"; import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
export interface ReturnDataType { export interface ReturnDataType {
message_id: number; message_id: number;
@@ -115,7 +115,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
); );
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) { if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const packetMode = this.core.apis.PacketApi.available const packetMode = this.core.apis.PacketApi.available;
const returnMsgAndResId = packetMode const returnMsgAndResId = packetMode
? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[]) ? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[])
: await this.handleForwardedNodes(peer, messages as OB11MessageNode[]); : await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
@@ -126,6 +126,8 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
chatType: peer.chatType, chatType: peer.chatType,
}, (returnMsgAndResId.message)!.msgId); }, (returnMsgAndResId.message)!.msgId);
return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id }; return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id };
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`);
} }
throw Error('发送转发消息失败'); throw Error('发送转发消息失败');
} else { } else {
@@ -143,6 +145,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
return { message_id: returnMsg!.id! }; return { message_id: returnMsg!.id! };
} }
// TODO: recursively handle forwarded nodes
private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{ private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
message: RawMessage | null, message: RawMessage | null,
res_id?: string res_id?: string
@@ -159,7 +162,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined, groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined,
time: Date.now(), time: Date.now(),
msg: sendElements, msg: sendElements,
} };
logger.logDebug(`handleForwardedNodesPacket 开始转换 ${JSON.stringify(packetMsgElements)}`); logger.logDebug(`handleForwardedNodesPacket 开始转换 ${JSON.stringify(packetMsgElements)}`);
const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements); const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements);
logger.logDebug(`handleForwardedNodesPacket 转换为 ${JSON.stringify(transformedMsg)}`); logger.logDebug(`handleForwardedNodesPacket 转换为 ${JSON.stringify(transformedMsg)}`);
@@ -169,22 +172,20 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
} }
} }
const resid = await this.core.apis.PacketApi.sendUploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0); const resid = await this.core.apis.PacketApi.sendUploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
const forwardJson = new PacketMultiMsgElement({ const forwardJson = ForwardMsgBuilder.fromPacketMsg(resid, packetMsg);
elementType: ElementType.STRUCTLONGMSG,
elementId: "",
structLongMsgElement: {
xmlContent: "",
resId: resid
}
}, packetMsg).JSON;
const finallySendElements = { const finallySendElements = {
elementType: ElementType.ARK, elementType: ElementType.ARK,
elementId: "", elementId: "",
arkElement: { arkElement: {
bytesData: JSON.stringify(forwardJson), bytesData: JSON.stringify(forwardJson),
}, },
} as SendArkElement } as SendArkElement;
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], [], true).catch(_ => undefined) let returnMsg: RawMessage | undefined;
try {
returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], [], true).catch(_ => undefined);
} catch (e) {
logger.logWarn("发送伪造合并转发消息失败!", e);
}
return { message: returnMsg ?? null, res_id: resid }; return { message: returnMsg ?? null, res_id: resid };
} }

View File

@@ -10,16 +10,16 @@ export abstract class GetPacketStatusDepends<PT, RT> extends BaseAction<PT, RT>
return { return {
valid: false, valid: false,
message: "packetServer不可用请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置", message: "packetServer不可用请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置",
} };
} }
return { return {
valid: true, valid: true,
} };
} }
} }
export class GetPacketStatus extends GetPacketStatusDepends<any, null> { export class GetPacketStatus extends GetPacketStatusDepends<any, null> {
async _handle(payload: any) { async _handle(payload: any) {
return null return null;
} }
} }

View File

@@ -35,6 +35,7 @@ import fs from 'node:fs';
import fsPromise from 'node:fs/promises'; import fsPromise from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
import { decodeSysMessage } from '@/core/packet/proto/old/ProfileLike'; import { decodeSysMessage } from '@/core/packet/proto/old/ProfileLike';
import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
type RawToOb11Converters = { type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -600,7 +601,13 @@ export class OneBotMsgApi {
[OB11MessageDataType.node]: async () => undefined, [OB11MessageDataType.node]: async () => undefined,
[OB11MessageDataType.forward]: async () => undefined, [OB11MessageDataType.forward]: async ({ data }, context) => {
const jsonData = ForwardMsgBuilder.fromResId(data.id);
return this.ob11ToRawConverters.json({
data: { data: JSON.stringify(jsonData) },
type: OB11MessageDataType.json
}, context);
},
[OB11MessageDataType.xml]: async () => undefined, [OB11MessageDataType.xml]: async () => undefined,

View File

@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
undefined, undefined,
SettingButton('V3.0.6', 'napcat-update-button', 'secondary'), SettingButton('V3.1.0', 'napcat-update-button', 'secondary'),
), ),
]), ]),
SettingList([ SettingList([

View File

@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
void 0, void 0,
SettingButton("V3.0.6", "napcat-update-button", "secondary") SettingButton("V3.1.0", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([