mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
feat: Implement complete transform & Build & Upload FakeForwardMsg
This commit is contained in:
parent
9c17dc1b8f
commit
c9e3bbcd9f
@ -1,17 +1,18 @@
|
||||
import * as os from 'os';
|
||||
import { InstanceContext, NapCatCore } from '..';
|
||||
import {ChatType, InstanceContext, NapCatCore} from '..';
|
||||
import offset from '@/core/external/offset.json';
|
||||
import { PacketClient, RecvPacketData } from '@/core/packet/client';
|
||||
import { PacketSession } from "@/core/packet/session";
|
||||
import { PacketHexStr, PacketPacker } from "@/core/packet/packer";
|
||||
import { NapProtoMsg } from '@/core/packet/proto/NapProto';
|
||||
import { OidbSvcTrpcTcp0X9067_202_Rsp_Body } from '@/core/packet/proto/oidb/Oidb.0x9067_202';
|
||||
import { OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp } from '@/core/packet/proto/oidb/OidbBase';
|
||||
import { OidbSvcTrpcTcp0XFE1_2RSP } from '@/core/packet/proto/oidb/Oidb.0XFE1_2';
|
||||
import { LogWrapper } from "@/common/log";
|
||||
import { SendLongMsgResp } from "@/core/packet/proto/message/action";
|
||||
import { PacketMsg } from "@/core/packet/msg/message";
|
||||
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||
import {PacketClient, RecvPacketData} from '@/core/packet/client';
|
||||
import {PacketSession} from "@/core/packet/session";
|
||||
import {PacketHexStr} from "@/core/packet/packer";
|
||||
import {NapProtoMsg} from '@/core/packet/proto/NapProto';
|
||||
import {OidbSvcTrpcTcp0X9067_202_Rsp_Body} from '@/core/packet/proto/oidb/Oidb.0x9067_202';
|
||||
import {OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp} from '@/core/packet/proto/oidb/OidbBase';
|
||||
import {OidbSvcTrpcTcp0XFE1_2RSP} from '@/core/packet/proto/oidb/Oidb.0XFE1_2';
|
||||
import {LogWrapper} from "@/common/log";
|
||||
import {SendLongMsgResp} from "@/core/packet/proto/message/action";
|
||||
import {PacketMsg} from "@/core/packet/msg/message";
|
||||
import {OidbSvcTrpcTcp0x6D6Response} from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||
import {PacketMsgPicElement} from "@/core/packet/msg/element";
|
||||
|
||||
interface OffsetType {
|
||||
[key: string]: {
|
||||
@ -28,14 +29,12 @@ export class NTQQPacketApi {
|
||||
logger: LogWrapper
|
||||
serverUrl: string | undefined;
|
||||
qqVersion: string | undefined;
|
||||
packetPacker: PacketPacker;
|
||||
packetSession: PacketSession | undefined;
|
||||
|
||||
constructor(context: InstanceContext, core: NapCatCore) {
|
||||
this.context = context;
|
||||
this.core = core;
|
||||
this.logger = core.context.logger;
|
||||
this.packetPacker = new PacketPacker(this.logger);
|
||||
this.packetSession = undefined;
|
||||
const config = this.core.configLoader.configData;
|
||||
if (config && config.packetServer && config.packetServer.length > 0) {
|
||||
@ -70,13 +69,13 @@ export class NTQQPacketApi {
|
||||
}
|
||||
|
||||
async sendPokePacket(group: number, peer: number) {
|
||||
const data = this.packetPacker.packPokePacket(group, peer);
|
||||
await this.sendPacket('OidbSvcTrpcTcp.0xed3_1', data, false);
|
||||
const data = this.packetSession?.packer.packPokePacket(group, peer);
|
||||
await this.sendPacket('OidbSvcTrpcTcp.0xed3_1', data!, false);
|
||||
}
|
||||
|
||||
async sendRkeyPacket() {
|
||||
const packet = this.packetPacker.packRkeyPacket();
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0x9067_202', packet, true);
|
||||
const packet = this.packetSession?.packer.packRkeyPacket();
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0x9067_202', packet!, true);
|
||||
if (!ret?.hex_data) return [];
|
||||
const body = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(ret.hex_data, 'hex')).body;
|
||||
const retData = new NapProtoMsg(OidbSvcTrpcTcp0X9067_202_Rsp_Body).decode(body);
|
||||
@ -86,8 +85,8 @@ export class NTQQPacketApi {
|
||||
async sendStatusPacket(uin: number): Promise<{ status: number; ext_status: number; } | undefined> {
|
||||
let status = 0;
|
||||
try {
|
||||
const packet = this.packetPacker.packStatusPacket(uin);
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0xfe1_2', packet, true);
|
||||
const packet = this.packetSession?.packer.packStatusPacket(uin);
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0xfe1_2', packet!, true);
|
||||
const data = Buffer.from(ret.hex_data, 'hex');
|
||||
const ext = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2RSP).decode(new NapProtoMsg(OidbSvcTrpcTcpBase).decode(data).body).data.status.value;
|
||||
// ext & 0xff00 + ext >> 16 & 0xff
|
||||
@ -103,20 +102,37 @@ export class NTQQPacketApi {
|
||||
}
|
||||
|
||||
async sendSetSpecialTittlePacket(groupCode: string, uid: string, tittle: string) {
|
||||
const data = this.packetPacker.packSetSpecialTittlePacket(groupCode, uid, tittle);
|
||||
await this.sendPacket('OidbSvcTrpcTcp.0x8fc_2', data, true);
|
||||
const data = this.packetSession?.packer.packSetSpecialTittlePacket(groupCode, uid, tittle);
|
||||
await this.sendPacket('OidbSvcTrpcTcp.0x8fc_2', data!, true);
|
||||
}
|
||||
|
||||
private async uploadResources(msg: PacketMsg[], groupUin: number = 0){
|
||||
const reqList = []
|
||||
for (const m of msg){
|
||||
for (const e of m.msg){
|
||||
if (e instanceof PacketMsgPicElement){
|
||||
reqList.push(this.packetSession?.highwaySession.uploadImage({
|
||||
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
|
||||
peerUid: String(groupUin) ? String(groupUin) : this.core.selfInfo.uid
|
||||
}, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(reqList);
|
||||
}
|
||||
|
||||
async sendUploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {
|
||||
const data = this.packetPacker.packUploadForwardMsg(this.core.selfInfo.uid, msg, groupUin);
|
||||
const ret = await this.sendPacket('trpc.group.long_msg_interface.MsgService.SsoSendLongMsg', data, true);
|
||||
await this.uploadResources(msg, groupUin);
|
||||
const data = await this.packetSession?.packer.packUploadForwardMsg(this.core.selfInfo.uid, msg, groupUin);
|
||||
const ret = await this.sendPacket('trpc.group.long_msg_interface.MsgService.SsoSendLongMsg', data!, true);
|
||||
this.logger.logDebug('sendUploadForwardMsg', ret);
|
||||
const resp = new NapProtoMsg(SendLongMsgResp).decode(Buffer.from(ret.hex_data, 'hex'));
|
||||
return resp.result.resId;
|
||||
}
|
||||
|
||||
async sendGroupFileDownloadReq(groupUin: number, fileUUID: string) {
|
||||
const data = this.packetPacker.packGroupFileDownloadReq(groupUin, fileUUID);
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0x6d6_2', data, true);
|
||||
const data = this.packetSession?.packer.packGroupFileDownloadReq(groupUin, fileUUID);
|
||||
const ret = await this.sendPacket('OidbSvcTrpcTcp.0x6d6_2', data!, true);
|
||||
const body = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(ret.hex_data, 'hex')).body;
|
||||
const resp = new NapProtoMsg(OidbSvcTrpcTcp0x6D6Response).decode(body);
|
||||
if (resp.download.retCode !== 0){
|
||||
|
@ -34,16 +34,16 @@ export class PacketHighwaySession {
|
||||
protected packer: PacketPacker;
|
||||
private cachedPrepareReq: Promise<void> | null = null;
|
||||
|
||||
constructor(logger: LogWrapper, client: PacketClient) {
|
||||
constructor(logger: LogWrapper, client: PacketClient, packer: PacketPacker) {
|
||||
this.packetClient = client;
|
||||
this.logger = logger;
|
||||
this.packer = new PacketPacker(logger);
|
||||
this.sig = {
|
||||
uin: this.packetClient.napCatCore.selfInfo.uin,
|
||||
sigSession: null,
|
||||
sessionKey: null,
|
||||
serverAddr: [],
|
||||
}
|
||||
this.packer = packer;
|
||||
this.packetHighwayClient = new PacketHighwayClient(this.sig, this.logger);
|
||||
}
|
||||
|
||||
|
131
src/core/packet/msg/converter.ts
Normal file
131
src/core/packet/msg/converter.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import {
|
||||
MessageElement,
|
||||
RawMessage,
|
||||
SendArkElement,
|
||||
SendFaceElement,
|
||||
SendFileElement,
|
||||
SendMarkdownElement,
|
||||
SendMarketFaceElement,
|
||||
SendPicElement,
|
||||
SendPttElement,
|
||||
SendReplyElement,
|
||||
SendStructLongMsgElement,
|
||||
SendTextElement,
|
||||
SendVideoElement
|
||||
} from "@/core";
|
||||
import {
|
||||
IPacketMsgElement,
|
||||
PacketMsgAtElement,
|
||||
PacketMsgFaceElement,
|
||||
PacketMsgFileElement,
|
||||
PacketMsgLightAppElement,
|
||||
PacketMsgMarkDownElement,
|
||||
PacketMsgMarkFaceElement,
|
||||
PacketMsgPicElement,
|
||||
PacketMsgPttElement,
|
||||
PacketMsgReplyElement,
|
||||
PacketMsgTextElement,
|
||||
PacketMsgVideoElement,
|
||||
PacketMultiMsgElement
|
||||
} from "@/core/packet/msg/element";
|
||||
import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
|
||||
type SendMessageElementMap = {
|
||||
textElement: SendTextElement,
|
||||
picElement: SendPicElement,
|
||||
replyElement: SendReplyElement,
|
||||
faceElement: SendFaceElement,
|
||||
marketFaceElement: SendMarketFaceElement,
|
||||
videoElement: SendVideoElement,
|
||||
fileElement: SendFileElement,
|
||||
pttElement: SendPttElement,
|
||||
arkElement: SendArkElement,
|
||||
markdownElement: SendMarkdownElement,
|
||||
structLongMsgElement: SendStructLongMsgElement
|
||||
};
|
||||
|
||||
type RawToPacketMsgConverters = {
|
||||
[K in keyof SendMessageElementMap]: (
|
||||
element: SendMessageElementMap[K],
|
||||
msg?: RawMessage,
|
||||
elementWrapper?: MessageElement,
|
||||
) => IPacketMsgElement<SendMessageElementMap[K]> | null;
|
||||
};
|
||||
|
||||
export type rawMsgWithSendMsg = {
|
||||
senderUin: number;
|
||||
senderUid?: string;
|
||||
senderName: string;
|
||||
groupId?: number;
|
||||
time: number;
|
||||
msg: PacketSendMsgElement[]
|
||||
}
|
||||
|
||||
export class PacketMsgConverter {
|
||||
private logger: LogWrapper;
|
||||
|
||||
constructor(logger: LogWrapper) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
rawMsgWithSendMsgToPacketMsg(msg: rawMsgWithSendMsg): PacketMsg {
|
||||
return {
|
||||
senderUid: msg.senderUid ?? '',
|
||||
senderUin: msg.senderUin,
|
||||
senderName: msg.senderName,
|
||||
groupId: msg.groupId,
|
||||
time: msg.time,
|
||||
msg: msg.msg.map((element) => {
|
||||
const key = (Object.keys(this.rawToPacketMsgConverters) as Array<keyof SendMessageElementMap>).find(
|
||||
(k) => (element as any)[k] !== undefined // TODO:
|
||||
);
|
||||
if (key) {
|
||||
const elementData = (element as any)[key]; // TODO:
|
||||
if (elementData) return this.rawToPacketMsgConverters[key](element as any)
|
||||
}
|
||||
return null;
|
||||
}).filter((e) => e !== null)
|
||||
}
|
||||
}
|
||||
|
||||
private rawToPacketMsgConverters: RawToPacketMsgConverters = {
|
||||
textElement: (element: SendTextElement) => {
|
||||
if (element.textElement.atType) {
|
||||
return new PacketMsgAtElement(element)
|
||||
}
|
||||
return new PacketMsgTextElement(element)
|
||||
},
|
||||
picElement: (element: SendPicElement) => {
|
||||
return new PacketMsgPicElement(element)
|
||||
},
|
||||
replyElement: (element: SendReplyElement) => {
|
||||
return new PacketMsgReplyElement(element)
|
||||
},
|
||||
faceElement: (element: SendFaceElement) => {
|
||||
return new PacketMsgFaceElement(element)
|
||||
},
|
||||
marketFaceElement: (element: SendMarketFaceElement) => {
|
||||
return new PacketMsgMarkFaceElement(element)
|
||||
},
|
||||
videoElement: (element: SendVideoElement) => {
|
||||
return new PacketMsgVideoElement(element)
|
||||
},
|
||||
fileElement: (element: SendFileElement) => {
|
||||
return new PacketMsgFileElement(element)
|
||||
},
|
||||
pttElement: (element: SendPttElement) => {
|
||||
return new PacketMsgPttElement(element)
|
||||
},
|
||||
arkElement: (element: SendArkElement) => {
|
||||
return new PacketMsgLightAppElement(element)
|
||||
},
|
||||
markdownElement: (element: SendMarkdownElement) => {
|
||||
return new PacketMsgMarkDownElement(element)
|
||||
},
|
||||
// TODO: check this logic, move it in arkElement?
|
||||
structLongMsgElement: (element: SendStructLongMsgElement) => {
|
||||
return new PacketMultiMsgElement(element)
|
||||
}
|
||||
}
|
||||
}
|
@ -248,7 +248,7 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketMarkFaceElement extends IPacketMsgElement<SendMarketFaceElement> {
|
||||
export class PacketMsgMarkFaceElement extends IPacketMsgElement<SendMarketFaceElement> {
|
||||
emojiName: string;
|
||||
emojiId: string;
|
||||
emojiPackageId: number;
|
||||
@ -357,16 +357,15 @@ export class PacketMultiMsgElement extends IPacketMsgElement<SendStructLongMsgEl
|
||||
resid: string;
|
||||
message: PacketMsg[];
|
||||
|
||||
constructor(rawElement: SendStructLongMsgElement)
|
||||
constructor(rawElement: SendStructLongMsgElement, message?: PacketMsg[]) {
|
||||
super(rawElement);
|
||||
this.resid = rawElement.structLongMsgElement.resId;
|
||||
this.message = message ?? [];
|
||||
}
|
||||
|
||||
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||
get JSON() {
|
||||
const id = crypto.randomUUID();
|
||||
const elementJson = {
|
||||
return {
|
||||
app: "com.tencent.multimsg",
|
||||
config: {
|
||||
autosize: 1,
|
||||
@ -388,20 +387,23 @@ export class PacketMultiMsgElement extends IPacketMsgElement<SendStructLongMsgEl
|
||||
text: `${packetMsg.senderName}: ${packetMsg.msg.map(msg => msg.toPreview()).join('')}`,
|
||||
})),
|
||||
resid: this.resid,
|
||||
source: "聊天记录",
|
||||
source: "聊天记录", // TODO:
|
||||
summary: `查看${this.message.length}条转发消息`,
|
||||
uniseq: id,
|
||||
}
|
||||
},
|
||||
prompt: "[聊天记录]",
|
||||
ver: "0.0.0.5",
|
||||
view: "contact"
|
||||
view: "contact",
|
||||
}
|
||||
}
|
||||
|
||||
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||
return [{
|
||||
lightAppElem: {
|
||||
data: Buffer.concat([
|
||||
Buffer.from([0x01]),
|
||||
zlib.deflateSync(Buffer.from(JSON.stringify(elementJson), 'utf-8'))
|
||||
zlib.deflateSync(Buffer.from(JSON.stringify(this.JSON), 'utf-8'))
|
||||
])
|
||||
}
|
||||
}]
|
||||
|
@ -16,16 +16,22 @@ import {LogWrapper} from "@/common/log";
|
||||
import {PacketMsg} from "@/core/packet/msg/message";
|
||||
import {OidbSvcTrpcTcp0x6D6} from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||
import {OidbSvcTrpcTcp0XE37_1200} from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||
import {PacketMsgConverter} from "@/core/packet/msg/converter";
|
||||
import {PacketClient} from "@/core/packet/client";
|
||||
|
||||
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
||||
|
||||
export class PacketPacker {
|
||||
private readonly logger: LogWrapper;
|
||||
private readonly packetBuilder: PacketMsgBuilder;
|
||||
readonly logger: LogWrapper;
|
||||
readonly client: PacketClient;
|
||||
readonly packetBuilder: PacketMsgBuilder;
|
||||
readonly packetConverter: PacketMsgConverter;
|
||||
|
||||
constructor(logger: LogWrapper) {
|
||||
constructor(logger: LogWrapper, client: PacketClient) {
|
||||
this.logger = logger;
|
||||
this.client = client;
|
||||
this.packetBuilder = new PacketMsgBuilder(logger);
|
||||
this.packetConverter = new PacketMsgConverter(logger);
|
||||
}
|
||||
|
||||
private toHexStr(byteArray: Uint8Array): PacketHexStr {
|
||||
@ -96,8 +102,7 @@ export class PacketPacker {
|
||||
return this.toHexStr(this.packOidbPacket(0xfe1, 2, oidb_0xfe1_2));
|
||||
}
|
||||
|
||||
packUploadForwardMsg(selfUid: string, msg: PacketMsg[], groupUin: number = 0): PacketHexStr {
|
||||
// this.logger.logDebug("packUploadForwardMsg START!!!", selfUid, msg, groupUin);
|
||||
async packUploadForwardMsg(selfUid: string, msg: PacketMsg[], groupUin: number = 0): Promise<PacketHexStr> {
|
||||
const msgBody = this.packetBuilder.buildFakeMsg(selfUid, msg);
|
||||
const longMsgResultData = new NapProtoMsg(LongMsgResult).encode(
|
||||
{
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { PacketClient } from "@/core/packet/client";
|
||||
import { PacketHighwaySession } from "@/core/packet/highway/session";
|
||||
import { LogWrapper } from "@/common/log";
|
||||
import {PacketPacker} from "@/core/packet/packer";
|
||||
|
||||
export class PacketSession {
|
||||
readonly logger: LogWrapper;
|
||||
readonly client: PacketClient;
|
||||
readonly packer: PacketPacker;
|
||||
readonly highwaySession: PacketHighwaySession;
|
||||
|
||||
constructor(logger: LogWrapper, client: PacketClient) {
|
||||
this.logger = logger;
|
||||
this.client = client;
|
||||
this.highwaySession = new PacketHighwaySession(this.logger, this.client);
|
||||
this.packer = new PacketPacker(this.logger, this.client);
|
||||
this.highwaySession = new PacketHighwaySession(this.logger, this.client, this.packer);
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import {ActionName} from '../types';
|
||||
import {FromSchema, JSONSchema} from 'json-schema-to-ts';
|
||||
import {ChatType, SendTextElement} from "@/core";
|
||||
import {PacketMsgPicElement, PacketMsgTextElement} from "@/core/packet/msg/element";
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: {type: ['number', 'string']},
|
||||
pic: {type: 'string'},
|
||||
},
|
||||
required: ['group_id', 'pic'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class UploadForwardMsg extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.UploadForwardMsg;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
if (!this.core.apis.PacketApi.available) {
|
||||
throw new Error('PacketClient is not init');
|
||||
}
|
||||
// throw new Error('Not implemented');
|
||||
const peer = {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: "10001", // TODO: must be a valid group id
|
||||
}
|
||||
const img = await this.core.apis.FileApi.createValidSendPicElement(
|
||||
{
|
||||
deleteAfterSentFiles: [],
|
||||
peer: peer
|
||||
},
|
||||
"", // TODO:
|
||||
"www",
|
||||
0,
|
||||
)
|
||||
const sendImg = new PacketMsgPicElement(img);
|
||||
console.log(JSON.stringify(img));
|
||||
await this.core.apis.PacketApi.packetSession?.highwaySession.uploadImage(
|
||||
peer, sendImg
|
||||
)
|
||||
return await this.core.apis.PacketApi.sendUploadForwardMsg([
|
||||
{
|
||||
groupId: 10001,
|
||||
senderId: 10001,
|
||||
senderName: "qwq",
|
||||
time: Math.floor(Date.now() / 1000),
|
||||
msg: [new PacketMsgTextElement({
|
||||
textElement: {
|
||||
content: "Love from Napcat.Packet~"
|
||||
}
|
||||
} as SendTextElement)]
|
||||
},
|
||||
{
|
||||
groupId: 10001,
|
||||
senderId: 10001,
|
||||
senderName: "qwq",
|
||||
time: Math.floor(Date.now() / 1000),
|
||||
msg: [new PacketMsgTextElement({
|
||||
textElement: {
|
||||
content: "Nya~"
|
||||
}
|
||||
} as SendTextElement), sendImg]
|
||||
}], 10001); // TODO: must be a valid group id
|
||||
}
|
||||
}
|
@ -87,7 +87,6 @@ import { GroupPoke } from './group/GroupPoke';
|
||||
import { GetUserStatus } from './extends/GetUserStatus';
|
||||
import { GetRkey } from './extends/GetRkey';
|
||||
import { SetSpecialTittle } from './extends/SetSpecialTittle';
|
||||
import { UploadForwardMsg } from "@/onebot/action/extends/UploadForwardMsg";
|
||||
import { GetGroupShutList } from './group/GetGroupShutList';
|
||||
import { GetGroupMemberList } from './group/GetGroupMemberList';
|
||||
import { GetGroupFileUrl } from "@/onebot/action/file/GetGroupFileUrl";
|
||||
@ -193,7 +192,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
||||
new GetUserStatus(obContext, core),
|
||||
new GetRkey(obContext, core),
|
||||
new SetSpecialTittle(obContext, core),
|
||||
new UploadForwardMsg(obContext, core),
|
||||
// new UploadForwardMsg(obContext, core),
|
||||
new GetGroupShutList(obContext, core),
|
||||
new GetGroupFileUrl(obContext, core),
|
||||
];
|
||||
|
@ -6,14 +6,18 @@ import {
|
||||
OB11PostContext,
|
||||
OB11PostSendMsg,
|
||||
} from '@/onebot/types';
|
||||
import { ActionName, BaseCheckResult } from '@/onebot/action/types';
|
||||
import { decodeCQCode } from '@/onebot/cqcode';
|
||||
import { MessageUnique } from '@/common/message-unique';
|
||||
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core';
|
||||
import {ActionName, BaseCheckResult} from '@/onebot/action/types';
|
||||
import {decodeCQCode} from '@/onebot/cqcode';
|
||||
import {MessageUnique} from '@/common/message-unique';
|
||||
import {ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, SendMessageElement} from '@/core';
|
||||
import BaseAction from '../BaseAction';
|
||||
import {rawMsgWithSendMsg} from "@/core/packet/msg/converter";
|
||||
import {PacketMsg} from "@/core/packet/msg/message";
|
||||
import {PacketMultiMsgElement} from "@/core/packet/msg/element";
|
||||
|
||||
export interface ReturnDataType {
|
||||
message_id: number;
|
||||
res_id?: string;
|
||||
}
|
||||
|
||||
export enum ContextMode {
|
||||
@ -26,7 +30,7 @@ export enum ContextMode {
|
||||
export function normalize(message: OB11MessageMixType, autoEscape = false): OB11MessageData[] {
|
||||
return typeof message === 'string' ? (
|
||||
autoEscape ?
|
||||
[{ type: OB11MessageDataType.text, data: { text: message } }] :
|
||||
[{type: OB11MessageDataType.text, data: {text: message}}] :
|
||||
decodeCQCode(message)
|
||||
) : Array.isArray(message) ? message : [message];
|
||||
}
|
||||
@ -96,10 +100,10 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
|
||||
};
|
||||
}
|
||||
return { valid: true };
|
||||
return {valid: true};
|
||||
}
|
||||
|
||||
async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||
async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
|
||||
this.contextMode = ContextMode.Normal;
|
||||
if (payload.message_type === 'group') this.contextMode = ContextMode.Group;
|
||||
if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
|
||||
@ -111,17 +115,19 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
);
|
||||
|
||||
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||
const returnMsg = await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
|
||||
if (returnMsg) {
|
||||
const packetMode = this.core.apis.PacketApi.available
|
||||
const returnMsgAndResId = packetMode
|
||||
? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[])
|
||||
: await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
|
||||
if (returnMsgAndResId.message) {
|
||||
const msgShortId = MessageUnique.createUniqueMsgId({
|
||||
guildId: '',
|
||||
peerUid: peer.peerUid,
|
||||
chatType: peer.chatType,
|
||||
}, returnMsg!.msgId);
|
||||
return { message_id: msgShortId! };
|
||||
} else {
|
||||
throw Error('发送转发消息失败');
|
||||
}, (returnMsgAndResId.message)!.msgId);
|
||||
return {message_id: msgShortId!, res_id: returnMsgAndResId.res_id};
|
||||
}
|
||||
throw Error('发送转发消息失败');
|
||||
} else {
|
||||
// if (getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||
// const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
|
||||
@ -131,13 +137,61 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
}
|
||||
// log("send msg:", peer, sendElements)
|
||||
|
||||
const { sendElements, deleteAfterSentFiles } = await this.obContext.apis.MsgApi
|
||||
const {sendElements, deleteAfterSentFiles} = await this.obContext.apis.MsgApi
|
||||
.createSendElements(messages, peer);
|
||||
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles);
|
||||
return { message_id: returnMsg!.id! };
|
||||
return {message_id: returnMsg!.id!};
|
||||
}
|
||||
|
||||
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
|
||||
private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
|
||||
message: RawMessage | null,
|
||||
res_id?: string
|
||||
}> {
|
||||
const logger = this.core.context.logger;
|
||||
const packetMsg: PacketMsg[] = [];
|
||||
for (const node of messageNodes) {
|
||||
if ((node.data.id && typeof node.data.content !== "string") || !node.data.id) {
|
||||
const OB11Data = normalize(node.data.content);
|
||||
const {sendElements} = await this.obContext.apis.MsgApi.createSendElements(OB11Data, msgPeer);
|
||||
const packetMsgElements: rawMsgWithSendMsg = {
|
||||
senderUin: node.data.user_id ?? +this.core.selfInfo.uin,
|
||||
senderName: node.data.nickname,
|
||||
groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined,
|
||||
time: Date.now(),
|
||||
msg: sendElements,
|
||||
}
|
||||
logger.logDebug(`handleForwardedNodesPacket 开始转换 ${JSON.stringify(packetMsgElements)}`);
|
||||
const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements);
|
||||
logger.logDebug(`handleForwardedNodesPacket 转换为 ${JSON.stringify(transformedMsg)}`);
|
||||
packetMsg.push(transformedMsg!);
|
||||
} else {
|
||||
logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${JSON.stringify(node)}`);
|
||||
}
|
||||
}
|
||||
const resid = await this.core.apis.PacketApi.sendUploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
||||
const forwardJson = new PacketMultiMsgElement({
|
||||
elementType: ElementType.STRUCTLONGMSG,
|
||||
elementId: "",
|
||||
structLongMsgElement: {
|
||||
xmlContent: "",
|
||||
resId: resid
|
||||
}
|
||||
}, packetMsg).JSON;
|
||||
const finallySendElements = {
|
||||
elementType: ElementType.ARK,
|
||||
elementId: "",
|
||||
arkElement: {
|
||||
bytesData: JSON.stringify(forwardJson),
|
||||
},
|
||||
} as SendArkElement
|
||||
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], [], true).catch(_ => undefined)
|
||||
return {message: returnMsg ?? null, res_id: resid};
|
||||
}
|
||||
|
||||
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
|
||||
message: RawMessage | null,
|
||||
res_id?: string
|
||||
}> {
|
||||
const selfPeer = {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: this.core.selfInfo.uid,
|
||||
@ -147,7 +201,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
for (const messageNode of messageNodes) {
|
||||
const nodeId = messageNode.data.id;
|
||||
if (nodeId) {
|
||||
//对Mgsid和OB11ID混用情况兜底
|
||||
// 对Msgid和OB11ID混用情况兜底
|
||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
||||
if (!nodeMsg) {
|
||||
logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', nodeId);
|
||||
@ -167,15 +221,15 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
}
|
||||
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
||||
if (nodeMsg) {
|
||||
nodeMsgIds.push(nodeMsg.msgId);
|
||||
MessageUnique.createUniqueMsgId(selfPeer, nodeMsg.msgId);
|
||||
nodeMsgIds.push(nodeMsg.message!.msgId);
|
||||
MessageUnique.createUniqueMsgId(selfPeer, nodeMsg.message!.msgId);
|
||||
}
|
||||
//完成子卡片生成跳过后续
|
||||
continue;
|
||||
}
|
||||
const { sendElements } = await this.obContext.apis.MsgApi
|
||||
const {sendElements} = await this.obContext.apis.MsgApi
|
||||
.createSendElements(OB11Data, destPeer);
|
||||
|
||||
|
||||
//拆分消息
|
||||
|
||||
const MixElement = sendElements.filter(
|
||||
@ -214,7 +268,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
continue;
|
||||
}
|
||||
const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
||||
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
|
||||
srcPeer = srcPeer ?? {chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid};
|
||||
if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
||||
needSendSelf = true;
|
||||
}
|
||||
@ -237,10 +291,14 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
||||
try {
|
||||
logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
|
||||
return await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds);
|
||||
return {
|
||||
message: await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds)
|
||||
};
|
||||
} catch (e) {
|
||||
logger.logError.bind(this.core.context.logger)('forward failed', e);
|
||||
return null;
|
||||
return {
|
||||
message: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,6 @@ export enum ActionName {
|
||||
GetUserStatus = "nc_get_user_status",
|
||||
GetRkey = "nc_get_rkey",
|
||||
SetSpecialTittle = "set_group_special_title",
|
||||
UploadForwardMsg = "upload_forward_msg",
|
||||
// UploadForwardMsg = "upload_forward_msg",
|
||||
GetGroupShutList = "get_goup_shut_list",
|
||||
}
|
||||
|
@ -26,15 +26,15 @@ import {
|
||||
OB11MessageFileBase,
|
||||
OB11MessageForward,
|
||||
} from '@/onebot';
|
||||
import { OB11Entities } from '@/onebot/entities';
|
||||
import { EventType } from '@/onebot/event/OB11BaseEvent';
|
||||
import { encodeCQCode } from '@/onebot/cqcode';
|
||||
import { uri2local } from '@/common/file';
|
||||
import { RequestUtil } from '@/common/request';
|
||||
import {OB11Entities} from '@/onebot/entities';
|
||||
import {EventType} from '@/onebot/event/OB11BaseEvent';
|
||||
import {encodeCQCode} from '@/onebot/cqcode';
|
||||
import {uri2local} from '@/common/file';
|
||||
import {RequestUtil} from '@/common/request';
|
||||
import fs from 'node:fs';
|
||||
import fsPromise from 'node:fs/promises';
|
||||
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||
import { decodeSysMessage } from '@/core/packet/proto/old/ProfileLike';
|
||||
import {OB11FriendAddNoticeEvent} from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||
import {decodeSysMessage} from '@/core/packet/proto/old/ProfileLike';
|
||||
|
||||
type RawToOb11Converters = {
|
||||
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
||||
@ -497,8 +497,7 @@ export class OneBotMsgApi {
|
||||
const uri2LocalRes = await uri2local(this.core.NapCatTempPath, thumb);
|
||||
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
|
||||
}
|
||||
const videoEle = await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
|
||||
return videoEle;
|
||||
return await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
|
||||
},
|
||||
|
||||
[OB11MessageDataType.voice]: async (sendMsg, context) =>
|
||||
|
Loading…
x
Reference in New Issue
Block a user