mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fb09af0e64 | ||
![]() |
0d99d30b2d | ||
![]() |
0000ec8b5b | ||
![]() |
0085bd8a1f | ||
![]() |
617139dfa4 | ||
![]() |
4eb4a612d0 | ||
![]() |
cda5e784f6 | ||
![]() |
d93a280ab3 | ||
![]() |
f7e2b3a4a7 | ||
![]() |
39d9c8fa74 | ||
![]() |
8823895a03 | ||
![]() |
b44a9e696c | ||
![]() |
cf28a3dc17 | ||
![]() |
7416e6caf6 | ||
![]() |
90f6896f3c | ||
![]() |
eebcd0700d | ||
![]() |
133eee0c66 | ||
![]() |
640fb75f74 | ||
![]() |
51dcc1add6 | ||
![]() |
730c928f91 | ||
![]() |
c3b7e111b9 | ||
![]() |
1874e48925 | ||
![]() |
e7a082c91c | ||
![]() |
5d4f45407e | ||
![]() |
17c37ec32f | ||
![]() |
b5f8140c79 | ||
![]() |
63f746c237 | ||
![]() |
dac6709f27 | ||
![]() |
2f08b72d69 | ||
![]() |
0081000ef0 | ||
![]() |
ad4d6a1070 |
BIN
logo.png
BIN
logo.png
Binary file not shown.
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 335 KiB |
@@ -4,7 +4,7 @@
|
||||
"name": "NapCatQQ",
|
||||
"slug": "NapCat.Framework",
|
||||
"description": "高性能的 OneBot 11 协议实现",
|
||||
"version": "3.1.6",
|
||||
"version": "3.1.8",
|
||||
"icon": "./logo.png",
|
||||
"authors": [
|
||||
{
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "3.1.6",
|
||||
"version": "3.1.8",
|
||||
"scripts": {
|
||||
"build:framework": "vite build --mode framework",
|
||||
"build:shell": "vite build --mode shell",
|
||||
|
@@ -55,10 +55,10 @@ export class ForwardMsgBuilder {
|
||||
const isGroupMsg = msg.some(m => m.isGroupMsg);
|
||||
if (!source) {
|
||||
source = isGroupMsg ? "群聊的聊天记录" :
|
||||
msg.length
|
||||
? Array.from(new Set(msg.map(m => m.senderName)))
|
||||
.join('和') + '的聊天记录'
|
||||
: '聊天记录';
|
||||
msg.length
|
||||
? Array.from(new Set(msg.map(m => m.senderName)))
|
||||
.join('和') + '的聊天记录'
|
||||
: '聊天记录';
|
||||
}
|
||||
if (!news) {
|
||||
news = msg.length === 0 ? [{
|
||||
|
@@ -1 +1 @@
|
||||
export const napCatVersion = '3.1.6';
|
||||
export const napCatVersion = '3.1.8';
|
||||
|
@@ -378,8 +378,8 @@ export class NTQQFileApi {
|
||||
};
|
||||
try {
|
||||
if (this.core.apis.PacketApi.available) {
|
||||
let rkey_expired_private = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000;
|
||||
let rkey_expired_group = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000;
|
||||
const rkey_expired_private = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000;
|
||||
const rkey_expired_group = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000;
|
||||
if (rkey_expired_private || rkey_expired_group) {
|
||||
this.packetRkey = await this.core.apis.PacketApi.sendRkeyPacket();
|
||||
}
|
||||
|
@@ -34,7 +34,13 @@ export class NTQQFriendApi {
|
||||
data.forEach((value) => retMap.set(value.uin!, value.uid!));
|
||||
return retMap;
|
||||
}
|
||||
|
||||
async delBuudy(uid: string, tempBlock = false, tempBothDel = false) {
|
||||
return this.context.session.getBuddyService().delBuddy({
|
||||
friendUid: uid,
|
||||
tempBlock: tempBlock,
|
||||
tempBothDel: tempBothDel
|
||||
});
|
||||
}
|
||||
async getBuddyV2ExWithCate(refresh = false) {
|
||||
const categoryMap: Map<string, any> = new Map();
|
||||
const buddyService = this.context.session.getBuddyService();
|
||||
|
@@ -316,18 +316,42 @@ export class NTQQGroupApi {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 1, 2000, (params) => params.sceneId === sceneId)
|
||||
.catch();
|
||||
const result = await this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, num);
|
||||
async tryGetGroupMembersV2(modeListener = false, groupQQ: string, num = 30, timeout = 100): Promise<{
|
||||
infos: Map<string, GroupMember>;
|
||||
finish: boolean;
|
||||
hasNext: boolean | undefined;
|
||||
}>{
|
||||
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1');
|
||||
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 0, timeout, (params) => params.sceneId === sceneId)
|
||||
.catch(() => {});
|
||||
const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num);
|
||||
if (result.errCode !== 0) {
|
||||
throw new Error('获取群成员列表出错,' + result.errMsg);
|
||||
}
|
||||
if (result.result.infos.size === 0) {
|
||||
return (await once)[0].infos;
|
||||
let resMode2;
|
||||
if (modeListener) {
|
||||
const ret = (await once)?.[0];
|
||||
if (ret) {
|
||||
resMode2 = ret;
|
||||
}
|
||||
}
|
||||
return result.result.infos;
|
||||
this.context.session.getGroupService().destroyMemberListScene(sceneId);
|
||||
return {
|
||||
infos: resMode2?.infos || result.result.infos,
|
||||
finish: result.result.finish,
|
||||
hasNext: resMode2?.hasNext,
|
||||
};
|
||||
}
|
||||
|
||||
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
let res = await this.tryGetGroupMembersV2(true, groupQQ);
|
||||
if (res.hasNext || !res.finish || res.infos.size === 0) {
|
||||
res = await this.tryGetGroupMembersV2(false, groupQQ, num);
|
||||
}
|
||||
if ((res.infos.size === 0 || res.infos.size === 30) && res.finish) {
|
||||
res = await this.tryGetGroupMembersV2(true, groupQQ, num);
|
||||
}
|
||||
return res.infos;
|
||||
}
|
||||
|
||||
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
@@ -425,7 +449,7 @@ export class NTQQGroupApi {
|
||||
}
|
||||
|
||||
async getGroupRemainAtTimes(GroupCode: string) {
|
||||
this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode);
|
||||
return this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode);
|
||||
}
|
||||
|
||||
async getMemberExtInfo(groupCode: string, uin: string) {
|
||||
|
@@ -3,7 +3,7 @@ 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 {OidbPacket, PacketHexStr} from "@/core/packet/packer";
|
||||
import { OidbPacket, 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';
|
||||
@@ -62,7 +62,10 @@ export class NTQQPacketApi {
|
||||
this.qqVersion = qqversion;
|
||||
const offsetTable: OffsetType = offset;
|
||||
const table = offsetTable[qqversion + '-' + os.arch()];
|
||||
if (!table) return false;
|
||||
if (!table) {
|
||||
this.logger.logError('PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch());
|
||||
return false;
|
||||
}
|
||||
const url = 'ws://' + this.serverUrl + '/ws';
|
||||
this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core));
|
||||
const cb = () => {
|
||||
@@ -103,7 +106,7 @@ export class NTQQPacketApi {
|
||||
let status = 0;
|
||||
try {
|
||||
const packet = this.packetSession?.packer.packStatusPacket(uin);
|
||||
const ret = await this.sendOidbPacket( packet!, true);
|
||||
const ret = await this.sendOidbPacket(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
|
||||
@@ -146,7 +149,7 @@ export class NTQQPacketApi {
|
||||
peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
|
||||
}, e));
|
||||
}
|
||||
if (e instanceof PacketMsgFileElement){
|
||||
if (e instanceof PacketMsgFileElement) {
|
||||
reqList.push(this.packetSession?.highwaySession.uploadFile({
|
||||
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
|
||||
peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
|
||||
|
4
src/core/external/offset.json
vendored
4
src/core/external/offset.json
vendored
@@ -34,5 +34,9 @@
|
||||
"3.2.12-28971-arm64": {
|
||||
"send": "6E91318",
|
||||
"recv": "6E94B50"
|
||||
},
|
||||
"6.9.56-28418-arm64": {
|
||||
"send": "4471360",
|
||||
"recv": "4473BCC"
|
||||
}
|
||||
}
|
@@ -71,7 +71,8 @@ export class NodeIKernelGroupListener {
|
||||
sceneId: string,
|
||||
ids: string[],
|
||||
infos: Map<string, GroupMember>, // uid -> GroupMember
|
||||
finish: boolean,
|
||||
hasPrev: boolean,
|
||||
hasNext: boolean,
|
||||
hasRobot: boolean
|
||||
}) {
|
||||
}
|
||||
|
@@ -167,7 +167,7 @@ export class PacketHighwaySession {
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1004,
|
||||
fs.createReadStream(img.path, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(img.path, { highWaterMark: BlockSize }),
|
||||
img.size,
|
||||
md5,
|
||||
extend
|
||||
@@ -207,7 +207,7 @@ export class PacketHighwaySession {
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1003,
|
||||
fs.createReadStream(img.path, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(img.path, { highWaterMark: BlockSize }),
|
||||
img.size,
|
||||
md5,
|
||||
extend
|
||||
@@ -244,10 +244,10 @@ export class PacketHighwaySession {
|
||||
hash: {
|
||||
fileSha1: await calculateSha1StreamBytes(video.filePath!)
|
||||
}
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1005,
|
||||
fs.createReadStream(video.filePath!, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(video.filePath!, { highWaterMark: BlockSize }),
|
||||
+video.fileSize!,
|
||||
md5,
|
||||
extend
|
||||
@@ -275,7 +275,7 @@ export class PacketHighwaySession {
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1006,
|
||||
fs.createReadStream(video.thumbPath!, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(video.thumbPath!, { highWaterMark: BlockSize }),
|
||||
+video.thumbSize!,
|
||||
md5,
|
||||
extend
|
||||
@@ -312,10 +312,10 @@ export class PacketHighwaySession {
|
||||
hash: {
|
||||
fileSha1: await calculateSha1StreamBytes(video.filePath!)
|
||||
}
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1001,
|
||||
fs.createReadStream(video.filePath!, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(video.filePath!, { highWaterMark: BlockSize }),
|
||||
+video.fileSize!,
|
||||
md5,
|
||||
extend
|
||||
@@ -343,7 +343,7 @@ export class PacketHighwaySession {
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1002,
|
||||
fs.createReadStream(video.thumbPath!, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(video.thumbPath!, { highWaterMark: BlockSize }),
|
||||
+video.thumbSize!,
|
||||
md5,
|
||||
extend
|
||||
@@ -379,10 +379,10 @@ export class PacketHighwaySession {
|
||||
hash: {
|
||||
fileSha1: [sha1]
|
||||
}
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1008,
|
||||
fs.createReadStream(ptt.filePath, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(ptt.filePath, { highWaterMark: BlockSize }),
|
||||
ptt.fileSize,
|
||||
md5,
|
||||
extend
|
||||
@@ -418,10 +418,10 @@ export class PacketHighwaySession {
|
||||
hash: {
|
||||
fileSha1: [sha1]
|
||||
}
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
1007,
|
||||
fs.createReadStream(ptt.filePath, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(ptt.filePath, { highWaterMark: BlockSize }),
|
||||
ptt.fileSize,
|
||||
md5,
|
||||
extend
|
||||
@@ -484,10 +484,10 @@ export class PacketHighwaySession {
|
||||
}
|
||||
},
|
||||
unknown200: 0,
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
71,
|
||||
fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(file.filePath, { highWaterMark: BlockSize }),
|
||||
file.fileSize,
|
||||
file.fileMd5,
|
||||
ext
|
||||
@@ -549,10 +549,10 @@ export class PacketHighwaySession {
|
||||
},
|
||||
unknown200: 1,
|
||||
unknown3: 0
|
||||
})
|
||||
});
|
||||
await this.packetHighwayClient.upload(
|
||||
95,
|
||||
fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
|
||||
fs.createReadStream(file.filePath, { highWaterMark: BlockSize }),
|
||||
file.fileSize,
|
||||
file.fileMd5,
|
||||
ext
|
||||
|
@@ -30,7 +30,7 @@ abstract class HighwayUploader {
|
||||
reject(new Error(`[Highway] timeout after ${this.trans.timeout}s`));
|
||||
}, (this.trans.timeout ?? Infinity) * 1000
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
buildPicUpHead(offset: number, bodyLength: number, bodyMd5: Uint8Array): Uint8Array {
|
||||
@@ -100,7 +100,7 @@ export class HighwayTcpUploader extends HighwayUploader {
|
||||
const upload = new Promise<void>((resolve, reject) => {
|
||||
const highwayTransForm = new HighwayTcpUploaderTransform(this);
|
||||
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 rsp = new NapProtoMsg(RespDataHighwayHead).decode(header);
|
||||
@@ -159,7 +159,7 @@ export class HighwayHttpUploader extends HighwayUploader {
|
||||
try {
|
||||
await this.uploadBlock(block, offset);
|
||||
} catch (err) {
|
||||
throw new Error(`[Highway] httpUpload Error uploading block at offset ${offset}: ${err}`)
|
||||
throw new Error(`[Highway] httpUpload Error uploading block at offset ${offset}: ${err}`);
|
||||
}
|
||||
offset += block.length;
|
||||
}
|
||||
|
@@ -15,9 +15,9 @@ export class PacketMsgBuilder {
|
||||
|
||||
protected static failBackText = new PacketMsgTextElement(
|
||||
{
|
||||
textElement: {content: "[该消息类型暂不支持查看]"}!
|
||||
textElement: { content: "[该消息类型暂不支持查看]" }!
|
||||
} as SendTextElement
|
||||
)
|
||||
);
|
||||
|
||||
buildFakeMsg(selfUid: string, element: PacketMsg[]): NapProtoEncodeStructType<typeof PushMsgBody>[] {
|
||||
return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
|
||||
@@ -50,7 +50,7 @@ export class PacketMsgBuilder {
|
||||
divSeq: node.groupId ? undefined : 4,
|
||||
msgId: crypto.randomBytes(4).readUInt32LE(0),
|
||||
sequence: crypto.randomBytes(4).readUInt32LE(0),
|
||||
timeStamp: Math.floor(Date.now() / 1000),
|
||||
timeStamp: +node.time.toString().substring(0, 10),
|
||||
field7: BigInt(1),
|
||||
field8: 0,
|
||||
field9: 0,
|
||||
|
@@ -89,11 +89,11 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||
text: {
|
||||
str: this.text,
|
||||
pbReserve: new NapProtoMsg(MentionExtra).encode({
|
||||
type: this.atAll ? 1 : 2,
|
||||
uin: 0,
|
||||
field5: 0,
|
||||
uid: this.targetUid,
|
||||
}
|
||||
type: this.atAll ? 1 : 2,
|
||||
uin: 0,
|
||||
field5: 0,
|
||||
uid: this.targetUid,
|
||||
}
|
||||
)
|
||||
}
|
||||
}];
|
||||
@@ -308,7 +308,7 @@ export class PacketMsgVideoElement extends IPacketMsgElement<SendVideoElement> {
|
||||
this.filePath = element.videoElement.filePath;
|
||||
this.thumbSize = element.videoElement.thumbSize;
|
||||
this.thumbPath = element.videoElement.thumbPath?.get(0);
|
||||
this.fileMd5 = element.videoElement.videoMd5
|
||||
this.fileMd5 = element.videoElement.videoMd5;
|
||||
this.thumbMd5 = element.videoElement.thumbMd5;
|
||||
this.thumbWidth = element.videoElement.thumbWidth;
|
||||
this.thumbHeight = element.videoElement.thumbHeight;
|
||||
@@ -355,7 +355,7 @@ export class PacketMsgPttElement extends IPacketMsgElement<SendPttElement> {
|
||||
}
|
||||
|
||||
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||
return []
|
||||
return [];
|
||||
// if (!this.msgInfo) return [];
|
||||
// return [{
|
||||
// commonElem: {
|
||||
@@ -382,7 +382,7 @@ export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
|
||||
isGroupFile?: boolean;
|
||||
_private_send_uid?: string;
|
||||
_private_recv_uid?: string;
|
||||
_e37_800_rsp?: NapProtoEncodeStructType<typeof OidbSvcTrpcTcp0XE37_800Response>
|
||||
_e37_800_rsp?: NapProtoEncodeStructType<typeof OidbSvcTrpcTcp0XE37_800Response>;
|
||||
|
||||
constructor(element: SendFileElement) {
|
||||
super(element);
|
||||
@@ -423,7 +423,7 @@ export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
|
||||
destUid: this._private_recv_uid,
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||
@@ -443,7 +443,7 @@ export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
|
||||
fileMd5: this.fileMd5,
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
lb.writeUInt16BE(transElemVal.length);
|
||||
return [{
|
||||
transElem: {
|
||||
|
@@ -61,7 +61,7 @@ export class PacketPacker {
|
||||
return {
|
||||
cmd: `OidbSvcTrpcTcp.0x${cmd.toString(16).toUpperCase()}_${subCmd}`,
|
||||
data: this.packetPacket(data)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
packPokePacket(peer: number, group?: number): OidbPacket {
|
||||
@@ -114,7 +114,7 @@ export class PacketPacker {
|
||||
packStatusPacket(uin: number): OidbPacket {
|
||||
const oidb_0xfe1_2 = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2).encode({
|
||||
uin: uin,
|
||||
key: [{key: 27372}]
|
||||
key: [{ key: 27372 }]
|
||||
});
|
||||
return this.packOidbPacket(0xfe1, 2, oidb_0xfe1_2);
|
||||
}
|
||||
@@ -240,68 +240,68 @@ export class PacketPacker {
|
||||
|
||||
async packUploadC2CImgReq(peerUin: string, img: PacketMsgPicElement): Promise<OidbPacket> {
|
||||
const req = new NapProtoMsg(NTV2RichMediaReq).encode({
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 100
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 100
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 1,
|
||||
sceneType: 1,
|
||||
c2C: {
|
||||
accountType: 2,
|
||||
targetUid: peerUin
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 1,
|
||||
sceneType: 1,
|
||||
c2C: {
|
||||
accountType: 2,
|
||||
targetUid: peerUin
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
uploadInfo: [
|
||||
{
|
||||
fileInfo: {
|
||||
fileSize: +img.size,
|
||||
fileHash: img.md5,
|
||||
fileSha1: img.sha1!,
|
||||
fileName: img.name,
|
||||
type: {
|
||||
type: 1,
|
||||
picFormat: img.picType, //TODO: extend NapCat imgType /cc @MliKiowa
|
||||
videoFormat: 0,
|
||||
voiceFormat: 0,
|
||||
},
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
time: 0,
|
||||
original: 1
|
||||
},
|
||||
subFileType: 0,
|
||||
}
|
||||
],
|
||||
tryFastUploadCompleted: true,
|
||||
srvSendMsg: false,
|
||||
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
|
||||
compatQMsgSceneType: 1,
|
||||
extBizInfo: {
|
||||
pic: {
|
||||
bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'),
|
||||
textSummary: "Nya~", // TODO:
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
video: {
|
||||
bytesPbReserve: Buffer.alloc(0),
|
||||
},
|
||||
ptt: {
|
||||
bytesPbReserve: Buffer.alloc(0),
|
||||
bytesReserve: Buffer.alloc(0),
|
||||
bytesGeneralFlags: Buffer.alloc(0),
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
uploadInfo: [
|
||||
{
|
||||
fileInfo: {
|
||||
fileSize: +img.size,
|
||||
fileHash: img.md5,
|
||||
fileSha1: img.sha1!,
|
||||
fileName: img.name,
|
||||
type: {
|
||||
type: 1,
|
||||
picFormat: img.picType, //TODO: extend NapCat imgType /cc @MliKiowa
|
||||
videoFormat: 0,
|
||||
voiceFormat: 0,
|
||||
},
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
time: 0,
|
||||
original: 1
|
||||
},
|
||||
subFileType: 0,
|
||||
}
|
||||
],
|
||||
tryFastUploadCompleted: true,
|
||||
srvSendMsg: false,
|
||||
clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'),
|
||||
compatQMsgSceneType: 1,
|
||||
extBizInfo: {
|
||||
pic: {
|
||||
bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'),
|
||||
textSummary: "Nya~", // TODO:
|
||||
},
|
||||
video: {
|
||||
bytesPbReserve: Buffer.alloc(0),
|
||||
},
|
||||
ptt: {
|
||||
bytesPbReserve: Buffer.alloc(0),
|
||||
bytesReserve: Buffer.alloc(0),
|
||||
bytesGeneralFlags: Buffer.alloc(0),
|
||||
}
|
||||
},
|
||||
clientSeq: 0,
|
||||
noNeedCompatMsg: false,
|
||||
}
|
||||
clientSeq: 0,
|
||||
noNeedCompatMsg: false,
|
||||
}
|
||||
}
|
||||
);
|
||||
return this.packOidbPacket(0x11c5, 100, req, true, false);
|
||||
}
|
||||
@@ -538,7 +538,7 @@ export class PacketPacker {
|
||||
clientSeq: 0,
|
||||
noNeedCompatMsg: false
|
||||
}
|
||||
})
|
||||
});
|
||||
return this.packOidbPacket(0x126E, 100, req, true, false);
|
||||
}
|
||||
|
||||
@@ -600,7 +600,7 @@ export class PacketPacker {
|
||||
clientSeq: 0,
|
||||
noNeedCompatMsg: false
|
||||
}
|
||||
})
|
||||
});
|
||||
return this.packOidbPacket(0x126D, 100, req, true, false);
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ export class PacketPacker {
|
||||
businessId: 3,
|
||||
clientType: 1,
|
||||
flagSupportMediaPlatform: 1
|
||||
})
|
||||
});
|
||||
return this.packOidbPacket(0xE37, 1700, body, false, false);
|
||||
}
|
||||
|
||||
@@ -664,13 +664,13 @@ export class PacketPacker {
|
||||
|
||||
packGroupFileDownloadReq(groupUin: number, fileUUID: string): OidbPacket {
|
||||
return this.packOidbPacket(0x6D6, 2, new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
|
||||
download: {
|
||||
groupUin: groupUin,
|
||||
appId: 7,
|
||||
busId: 102,
|
||||
fileId: fileUUID
|
||||
}
|
||||
}), true, false
|
||||
download: {
|
||||
groupUin: groupUin,
|
||||
appId: 7,
|
||||
busId: 102,
|
||||
fileId: fileUUID
|
||||
}
|
||||
}), true, false
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -118,7 +118,7 @@ export const FileExtra = {
|
||||
|
||||
export const PrivateFileExtra = {
|
||||
field2: ProtoField(2, () => PrivateFileExtraField2),
|
||||
}
|
||||
};
|
||||
|
||||
export const PrivateFileExtraField2 = {
|
||||
field1: ProtoField(1, ScalarType.UINT32),
|
||||
@@ -131,7 +131,7 @@ export const PrivateFileExtraField2 = {
|
||||
fileHash: ProtoField(14, ScalarType.STRING),
|
||||
selfUid: ProtoField(15, ScalarType.STRING),
|
||||
destUid: ProtoField(16, ScalarType.STRING),
|
||||
}
|
||||
};
|
||||
|
||||
export const GroupFileExtra = {
|
||||
field1: ProtoField(1, ScalarType.UINT32),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { ScalarType } from "@protobuf-ts/runtime";
|
||||
import { ProtoField } from "../NapProto";
|
||||
import {OidbSvcTrpcTcp0XE37_800_1200Metadata} from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||
import { OidbSvcTrpcTcp0XE37_800_1200Metadata } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||
|
||||
export const OidbSvcTrpcTcp0XE37_800 = {
|
||||
subCommand: ProtoField(1, ScalarType.UINT32),
|
||||
@@ -59,4 +59,4 @@ export const OidbSvcTrpcTcp0XE37_800Response = {
|
||||
export const OidbSvcTrpcTcp0XE37_800ResponseBody = {
|
||||
field10: ProtoField(10, ScalarType.UINT32, true),
|
||||
field30: ProtoField(30, () => OidbSvcTrpcTcp0XE37_800_1200Metadata, true),
|
||||
}
|
||||
};
|
||||
|
@@ -8,7 +8,7 @@ export const OidbSvcTrpcTcp0XE37_1700 = {
|
||||
businessId: ProtoField(101, ScalarType.INT32, true),
|
||||
clientType: ProtoField(102, ScalarType.INT32, true),
|
||||
flagSupportMediaPlatform: ProtoField(200, ScalarType.INT32, true),
|
||||
}
|
||||
};
|
||||
|
||||
export const ApplyUploadReqV3 = {
|
||||
senderUid: ProtoField(10, ScalarType.STRING, true),
|
||||
@@ -20,4 +20,4 @@ export const ApplyUploadReqV3 = {
|
||||
localPath: ProtoField(70, ScalarType.STRING, true),
|
||||
md5CheckSum: ProtoField(110, ScalarType.BYTES, true),
|
||||
sha3CheckSum: ProtoField(120, ScalarType.BYTES, true),
|
||||
}
|
||||
};
|
||||
|
@@ -9,4 +9,4 @@ export const OidbSvcTrpcTcp0XEB7_Body = {
|
||||
|
||||
export const OidbSvcTrpcTcp0XEB7 = {
|
||||
body: ProtoField(2, () => OidbSvcTrpcTcp0XEB7_Body),
|
||||
}
|
||||
};
|
@@ -2,7 +2,7 @@
|
||||
import * as crypto from 'crypto';
|
||||
import * as stream from 'stream';
|
||||
import * as fs from 'fs';
|
||||
import {CalculateStreamBytesTransform} from "@/core/packet/utils/crypto/sha1StreamBytesTransform";
|
||||
import { CalculateStreamBytesTransform } from "@/core/packet/utils/crypto/sha1StreamBytesTransform";
|
||||
|
||||
function sha1Stream(readable: stream.Readable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@@ -73,7 +73,7 @@ export class Sha1Stream {
|
||||
|
||||
this._count[1] = (this._count[1] + (dataLen >>> 29)) >>> 0;
|
||||
|
||||
let partLen = (this.Sha1BlockSize - index) >>> 0;
|
||||
const partLen = (this.Sha1BlockSize - index) >>> 0;
|
||||
let i = 0;
|
||||
|
||||
if (dataLen >= partLen) {
|
||||
@@ -100,11 +100,11 @@ export class Sha1Stream {
|
||||
|
||||
public final(): Buffer {
|
||||
const digest = Buffer.allocUnsafe(this.Sha1DigestSize);
|
||||
const bits = Buffer.allocUnsafe(8)
|
||||
const bits = Buffer.allocUnsafe(8);
|
||||
bits.writeUInt32BE(this._count[1], 0);
|
||||
bits.writeUInt32BE(this._count[0], 4);
|
||||
|
||||
let index = ((this._count[0] >>> 3) & 0x3F) >>> 0;
|
||||
const index = ((this._count[0] >>> 3) & 0x3F) >>> 0;
|
||||
const padLen = ((index < 56) ? (56 - index) : (120 - index)) >>> 0;
|
||||
this.update(this._padding, padLen);
|
||||
this.update(bits);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as stream from "node:stream";
|
||||
import {Sha1Stream} from "@/core/packet/utils/crypto/sha1Stream";
|
||||
import { Sha1Stream } from "@/core/packet/utils/crypto/sha1Stream";
|
||||
|
||||
export class CalculateStreamBytesTransform extends stream.Transform {
|
||||
private readonly blockSize = 1024 * 1024;
|
||||
|
@@ -66,7 +66,11 @@ export interface NodeIKernelBuddyService {
|
||||
accept: boolean;
|
||||
}): Promise<void>;
|
||||
|
||||
delBuddy(uid: number): void;
|
||||
delBuddy(param: {
|
||||
friendUid: string;
|
||||
tempBlock: boolean;
|
||||
tempBothDel: boolean;
|
||||
}): Promise<unknown>;
|
||||
|
||||
delBatchBuddy(uids: number[]): void;
|
||||
|
||||
|
@@ -12,6 +12,13 @@ import {
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
export interface NodeIKernelGroupService {
|
||||
// --->
|
||||
// 待启用 For Next Version 3.2.0
|
||||
// isTroopMember ? 0 : 111
|
||||
getGroupMemberMaxNum(groupCode: string, serviceType: number): Promise<unknown>;
|
||||
|
||||
getAllGroupPrivilegeFlag(troopUinList: string[], serviceType: number): Promise<unknown>;
|
||||
// <---
|
||||
getGroupExt0xEF0Info(enableGroupCodes: string[], bannedGroupCodes: string[], filter: GroupExt0xEF0InfoFilter, forceFetch: boolean):
|
||||
Promise<GeneralCallResult & { result: { groupExtInfos: Map<string, any> } }>;
|
||||
|
||||
@@ -114,8 +121,8 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
destroyMemberListScene(SceneId: string): void;
|
||||
|
||||
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
|
||||
errCode: number,
|
||||
getNextMemberList(sceneId: string, groupMemberInfoListId: { index: number, uid: string } | undefined, num: number): Promise<{
|
||||
errCode: number,
|
||||
errMsg: string,
|
||||
result: { ids: string[], infos: Map<string, GroupMember>, finish: boolean, hasRobot: boolean }
|
||||
}>;
|
||||
@@ -225,7 +232,15 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
getGroupStatisticInfo(groupCode: string): unknown;
|
||||
|
||||
getGroupRemainAtTimes(groupCode: string): number;
|
||||
getGroupRemainAtTimes(groupCode: string): Promise<GeneralCallResult & {
|
||||
atInfo: {
|
||||
canAtAll: boolean
|
||||
RemainAtAllCountForUin: number
|
||||
RemainAtAllCountForGroup: number
|
||||
atTimesMsg: string
|
||||
canNotAtAllMsg: ''
|
||||
}
|
||||
}>;
|
||||
|
||||
getJoinGroupNoVerifyFlag(groupCode: string): unknown;
|
||||
|
||||
|
@@ -327,8 +327,7 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
setPttPlayedState(...args: unknown[]): unknown;
|
||||
|
||||
//uk1 uk2 true
|
||||
fetchFavEmojiList(str: string, num: number, uk1: boolean, uk2: boolean): Promise<GeneralCallResult & {
|
||||
fetchFavEmojiList(str: string, num: number, backward: boolean, forceRefresh: boolean): Promise<GeneralCallResult & {
|
||||
emojiInfoList: Array<{
|
||||
uin: string,
|
||||
emoId: number,
|
||||
|
@@ -86,7 +86,7 @@ export interface NodeQQNTWrapperUtil {
|
||||
|
||||
calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown;
|
||||
|
||||
fullWordToHalfWord(arg0: string): unknown;
|
||||
fullWordToHalfWord(word: string): unknown;
|
||||
|
||||
getNTUserDataInfoConfig(): unknown;
|
||||
|
||||
|
30
src/onebot/action/go-cqhttp/GetGroupAtAllRemain.ts
Normal file
30
src/onebot/action/go-cqhttp/GetGroupAtAllRemain.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] }
|
||||
},
|
||||
required: ['group_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class GoCQHTTPGetGroupAtAllRemain extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GoCQHTTP_GetGroupAtAllRemain;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
let ret = await this.core.apis.GroupApi.getGroupRemainAtTimes(payload.group_id.toString());
|
||||
if (!ret.atInfo || ret.result !== 0) {
|
||||
throw new Error('atInfo not found');
|
||||
}
|
||||
let data = {
|
||||
can_at_all: ret.atInfo.canAtAll,
|
||||
remain_at_all_count_for_group: ret.atInfo.RemainAtAllCountForGroup,
|
||||
remain_at_all_count_for_uin: ret.atInfo.RemainAtAllCountForUin
|
||||
};
|
||||
return data;
|
||||
}
|
||||
}
|
21
src/onebot/action/go-cqhttp/GoCQHTTPCheckUrlSafely.ts
Normal file
21
src/onebot/action/go-cqhttp/GoCQHTTPCheckUrlSafely.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: ['url'],
|
||||
} as const satisfies JSONSchema;
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class GoCQHTTPCheckUrlSafely extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GoCQHTTP_CheckUrlSafely;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
return { level: 1 };
|
||||
}
|
||||
}
|
38
src/onebot/action/go-cqhttp/GoCQHTTPDeleteFriend.ts
Normal file
38
src/onebot/action/go-cqhttp/GoCQHTTPDeleteFriend.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
friend_id: { type: ['string', 'number'] },
|
||||
temp_block: { type: 'boolean' },
|
||||
temp_both_del: { type: 'boolean' },
|
||||
},
|
||||
required: ['friend_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class GoCQHTTPDeleteFriend extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GoCQHTTP_DeleteFriend;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
let uid = await this.core.apis.UserApi.getUidByUinV2(payload.friend_id.toString());
|
||||
|
||||
if (!uid) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '好友不存在',
|
||||
};
|
||||
}
|
||||
let isBuddy = await this.core.apis.FriendApi.isBuddy(uid);
|
||||
if (!isBuddy) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '不是好友',
|
||||
};
|
||||
}
|
||||
return await this.core.apis.FriendApi.delBuudy(uid, payload.temp_block, payload.temp_both_del);
|
||||
}
|
||||
}
|
28
src/onebot/action/go-cqhttp/GoCQHTTPGetModelShow.ts
Normal file
28
src/onebot/action/go-cqhttp/GoCQHTTPGetModelShow.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
model: { type: 'string' },
|
||||
}
|
||||
} as const satisfies JSONSchema;
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class GoCQHTTPGetModelShow extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GoCQHTTP_GetModelShow;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
if (!payload.model) {
|
||||
payload.model = 'napcat';
|
||||
}
|
||||
return [{
|
||||
variants: {
|
||||
model_show: "napcat",
|
||||
need_pay: false
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
19
src/onebot/action/go-cqhttp/GoCQHTTPSetModelShow.ts
Normal file
19
src/onebot/action/go-cqhttp/GoCQHTTPSetModelShow.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
} as const satisfies JSONSchema;
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
//兼容性代码
|
||||
export class GoCQHTTPSetModelShow extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GoCQHTTP_SetModelShow;
|
||||
payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
export default class SetModelShow extends BaseAction<null, null> {
|
||||
actionName = ActionName.SetModelShow;
|
||||
|
||||
async _handle(payload: null): Promise<null> {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -21,7 +21,14 @@ export class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const groupIdStr = payload.group_id.toString();
|
||||
const groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
||||
const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false;
|
||||
const memberCache = this.core.apis.GroupApi.groupMemberCache;
|
||||
let groupMembers;
|
||||
if (noCache) {
|
||||
groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
||||
} else {
|
||||
groupMembers = memberCache.get(groupIdStr) ?? await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
||||
}
|
||||
|
||||
const memberPromises = Array.from(groupMembers.values()).map(item =>
|
||||
OB11Entities.groupMember(groupIdStr, item)
|
||||
@@ -30,4 +37,7 @@ export class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
const MemberMap = new Map(_groupMembers.map(member => [member.user_id, member]));
|
||||
return Array.from(MemberMap.values());
|
||||
}
|
||||
}
|
||||
stringToBoolean(str: string | boolean): boolean {
|
||||
return typeof str === 'boolean' ? str : str.toLowerCase() === "true";
|
||||
}
|
||||
}
|
||||
|
@@ -71,7 +71,6 @@ import { FetchUserProfileLike } from './extends/FetchUserProfileLike';
|
||||
import { NapCatCore } from '@/core';
|
||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
import GetGuildProfile from './guild/GetGuildProfile';
|
||||
import SetModelShow from './go-cqhttp/SetModelShow';
|
||||
import { SetInputStatus } from './extends/SetInputStatus';
|
||||
import { GetCSRF } from './system/GetCSRF';
|
||||
import { DelGroupNotice } from './group/DelGroupNotice';
|
||||
@@ -94,6 +93,11 @@ import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
|
||||
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
|
||||
import { GetCredentials } from './system/GetCredentials';
|
||||
import { SetGroupSign } from './extends/SetGroupSign';
|
||||
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
||||
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
||||
import { GoCQHTTPGetModelShow } from './go-cqhttp/GoCQHTTPGetModelShow';
|
||||
import { GoCQHTTPSetModelShow } from './go-cqhttp/GoCQHTTPSetModelShow';
|
||||
import { GoCQHTTPDeleteFriend } from './go-cqhttp/GoCQHTTPDeleteFriend';
|
||||
|
||||
|
||||
export type ActionMap = Map<string, BaseAction<any, any>>;
|
||||
@@ -151,6 +155,8 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
||||
new GetRobotUinRange(obContext, core),
|
||||
new GetFriendWithCategory(obContext, core),
|
||||
//以下为go-cqhttp api
|
||||
new GoCQHTTPDeleteFriend(obContext, core),
|
||||
new GoCQHTTPCheckUrlSafely(obContext, core),
|
||||
new GetOnlineClient(obContext, core),
|
||||
new OCRImage(obContext, core),
|
||||
new IOCRImage(obContext, core),
|
||||
@@ -158,6 +164,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
||||
new SendGroupNotice(obContext, core),
|
||||
new GetGroupNotice(obContext, core),
|
||||
new GetGroupEssence(obContext, core),
|
||||
new GoCQHTTPGetGroupAtAllRemain(obContext, core),
|
||||
new GoCQHTTPSendForwardMsg(obContext, core),
|
||||
new GoCQHTTPSendGroupForwardMsg(obContext, core),
|
||||
new GoCQHTTPSendPrivateForwardMsg(obContext, core),
|
||||
@@ -180,7 +187,9 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
||||
new FetchCustomFace(obContext, core),
|
||||
new GoCQHTTPUploadPrivateFile(obContext, core),
|
||||
new GetGuildProfile(obContext, core),
|
||||
new SetModelShow(obContext, core),
|
||||
new GoCQHTTPGetModelShow(obContext, core),
|
||||
new GoCQHTTPSetModelShow(obContext, core),
|
||||
new GoCQHTTPCheckUrlSafely(obContext, core),
|
||||
new SetInputStatus(obContext, core),
|
||||
new GetCSRF(obContext, core),
|
||||
new GetCredentials(obContext, core),
|
||||
|
@@ -116,9 +116,17 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
|
||||
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||
const packetMode = this.core.apis.PacketApi.available;
|
||||
const returnMsgAndResId = packetMode
|
||||
? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[], payload.source, payload.news, payload.summary, payload.prompt)
|
||||
: await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
|
||||
let returnMsgAndResId: { message: RawMessage | null, res_id?: string } | null;
|
||||
try {
|
||||
returnMsgAndResId = packetMode
|
||||
? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[], payload.source, payload.news, payload.summary, payload.prompt)
|
||||
: await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
|
||||
} catch (e) {
|
||||
throw Error(packetMode ? `发送伪造合并转发消息失败: ${e}` : `发送合并转发消息失败: ${e}`);
|
||||
}
|
||||
if (!returnMsgAndResId) {
|
||||
throw Error('发送合并转发消息失败:returnMsgAndResId 为空!');
|
||||
}
|
||||
if (returnMsgAndResId.message) {
|
||||
const msgShortId = MessageUnique.createUniqueMsgId({
|
||||
guildId: '',
|
||||
@@ -129,7 +137,6 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
|
||||
throw Error(`发送转发消息(res_id:${returnMsgAndResId.res_id} 失败`);
|
||||
}
|
||||
throw Error('发送转发消息失败');
|
||||
} else {
|
||||
// if (getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||
// const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
|
||||
@@ -145,22 +152,42 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
return { message_id: returnMsg!.id! };
|
||||
}
|
||||
|
||||
// TODO: recursively handle forwarded nodes
|
||||
private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: { text: string }[], summary?: string, prompt?: string): Promise<{
|
||||
message: RawMessage | null,
|
||||
private async uploadForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: {
|
||||
text: string
|
||||
}[], summary?: string, prompt?: string, parentMeta?: {
|
||||
user_id: string,
|
||||
nickname: string,
|
||||
}, dp: number = 0): Promise<{
|
||||
finallySendElements: SendArkElement,
|
||||
res_id?: string
|
||||
}> {
|
||||
} | null> {
|
||||
const logger = this.core.context.logger;
|
||||
const packetMsg: PacketMsg[] = [];
|
||||
for (const node of messageNodes) {
|
||||
if (dp >= 3) {
|
||||
logger.logWarn('转发消息深度超过3层,将停止解析!');
|
||||
break;
|
||||
}
|
||||
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 OB11Data = normalize(node.type === OB11MessageDataType.node ? node.data.content : node);
|
||||
let sendElements: SendMessageElement[];
|
||||
|
||||
if (getSpecialMsgNum({message: OB11Data}, OB11MessageDataType.node)) {
|
||||
const uploadReturnData = await this.uploadForwardedNodesPacket(msgPeer, OB11Data as OB11MessageNode[], node.data.source, node.data.news, node.data.summary, node.data.prompt, {
|
||||
user_id: node.data.user_id?.toString() ?? parentMeta?.user_id ?? this.core.selfInfo.uin,
|
||||
nickname: node.data.nickname ?? parentMeta?.nickname ?? "QQ用户",
|
||||
}, dp + 1);
|
||||
sendElements = uploadReturnData?.finallySendElements ? [uploadReturnData.finallySendElements] : [];
|
||||
} else {
|
||||
const sendElementsCreateReturn = await this.obContext.apis.MsgApi.createSendElements(OB11Data, msgPeer);
|
||||
sendElements = sendElementsCreateReturn.sendElements;
|
||||
}
|
||||
|
||||
const packetMsgElements: rawMsgWithSendMsg = {
|
||||
senderUin: Number(node.data.user_id) ?? +this.core.selfInfo.uin,
|
||||
senderName: node.data.nickname,
|
||||
senderUin: Number(node.data.user_id ?? parentMeta?.user_id) || +this.core.selfInfo.uin,
|
||||
senderName: node.data.nickname ?? parentMeta?.nickname ?? "QQ用户",
|
||||
groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined,
|
||||
time: Date.now(),
|
||||
time: Number(node.data.time) || Date.now(),
|
||||
msg: sendElements,
|
||||
};
|
||||
logger.logDebug(`handleForwardedNodesPacket 开始转换 ${JSON.stringify(packetMsgElements)}`);
|
||||
@@ -171,22 +198,37 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${JSON.stringify(node)}`);
|
||||
}
|
||||
}
|
||||
if (packetMsg.length === 0) {
|
||||
logger.logWarn('handleForwardedNodesPacket 元素为空!');
|
||||
return null;
|
||||
}
|
||||
const resid = await this.core.apis.PacketApi.sendUploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
||||
const forwardJson = ForwardMsgBuilder.fromPacketMsg(resid, packetMsg, source, news, summary, prompt);
|
||||
const finallySendElements = {
|
||||
elementType: ElementType.ARK,
|
||||
elementId: "",
|
||||
arkElement: {
|
||||
bytesData: JSON.stringify(forwardJson),
|
||||
},
|
||||
} as SendArkElement;
|
||||
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 {
|
||||
finallySendElements: {
|
||||
elementType: ElementType.ARK,
|
||||
elementId: "",
|
||||
arkElement: {
|
||||
bytesData: JSON.stringify(forwardJson),
|
||||
},
|
||||
} as SendArkElement,
|
||||
res_id: resid,
|
||||
};
|
||||
}
|
||||
|
||||
private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: {
|
||||
text: string
|
||||
}[], summary?: string, prompt?: string): Promise<{
|
||||
message: RawMessage | null,
|
||||
res_id?: string
|
||||
}> {
|
||||
let returnMsg: RawMessage | undefined, res_id: string | undefined;
|
||||
const uploadReturnData = await this.uploadForwardedNodesPacket(msgPeer, messageNodes, source, news, summary, prompt);
|
||||
res_id = uploadReturnData?.res_id;
|
||||
const finallySendElements = uploadReturnData?.finallySendElements;
|
||||
if (!finallySendElements) throw Error('转发消息失败,生成节点为空');
|
||||
returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], [], true).catch(_ => undefined);
|
||||
return {message: returnMsg ?? null, res_id};
|
||||
}
|
||||
|
||||
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
|
||||
|
@@ -5,16 +5,14 @@ import { ActionName, BaseCheckResult } from '../types';
|
||||
export abstract class GetPacketStatusDepends<PT, RT> extends BaseAction<PT, RT> {
|
||||
actionName = ActionName.GetPacketStatus;
|
||||
|
||||
protected async check(): Promise<BaseCheckResult>{
|
||||
protected async check(payload: PT): Promise<BaseCheckResult>{
|
||||
if (!this.core.apis.PacketApi.available) {
|
||||
return {
|
||||
valid: false,
|
||||
message: "packetServer不可用,请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置!",
|
||||
};
|
||||
}
|
||||
return {
|
||||
valid: true,
|
||||
};
|
||||
return await super.check(payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -57,11 +57,11 @@ export enum ActionName {
|
||||
// go-cqhttp
|
||||
SetQQProfile = 'set_qq_profile',
|
||||
// QidianGetAccountInfo = 'qidian_get_account_info',
|
||||
// GetModelShow = '_get_model_show',
|
||||
// SetModelShow = '_set_model_show',
|
||||
GoCQHTTP_GetModelShow = '_get_model_show',
|
||||
GoCQHTTP_SetModelShow = '_set_model_show',
|
||||
GetOnlineClient = 'get_online_clients',
|
||||
// GetUnidirectionalFriendList = 'get_unidirectional_friend_list',
|
||||
// DeleteFriend = 'delete_friend',
|
||||
GoCQHTTP_DeleteFriend = 'delete_friend',
|
||||
// DeleteUnidirectionalFriendList = 'delete_unidirectional_friend',
|
||||
GoCQHTTP_MarkMsgAsRead = 'mark_msg_as_read',
|
||||
GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg',
|
||||
@@ -71,7 +71,7 @@ export enum ActionName {
|
||||
IOCRImage = '.ocr_image',
|
||||
GetGroupSystemMsg = 'get_group_system_msg',
|
||||
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list',
|
||||
// GetGroupAtAllRemain = 'get_group_at_all_remain',
|
||||
GoCQHTTP_GetGroupAtAllRemain = 'get_group_at_all_remain',
|
||||
SetGroupPortrait = 'set_group_portrait',
|
||||
SetEssenceMsg = 'set_essence_msg',
|
||||
DelEssenceMsg = 'delete_essence_msg',
|
||||
@@ -88,8 +88,8 @@ export enum ActionName {
|
||||
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
|
||||
// GOCQHTTP_ReloadEventFilter = 'reload_event_filter',
|
||||
GoCQHTTP_DownloadFile = 'download_file',
|
||||
// GoCQHTTP_CheckUrlSafely = 'check_url_safely',
|
||||
// GoCQHTTP_GetWordSlices = '.get_word_slices',
|
||||
GoCQHTTP_CheckUrlSafely = 'check_url_safely',
|
||||
GoCQHTTP_GetWordSlices = '.get_word_slices',
|
||||
GoCQHTTP_HandleQuickAction = '.handle_quick_operation',
|
||||
|
||||
// 以下为扩展napcat扩展
|
||||
|
@@ -152,6 +152,11 @@ export interface OB11MessageNode {
|
||||
user_id?: number | string // number
|
||||
nickname: string
|
||||
content: OB11MessageMixType
|
||||
source?: string,
|
||||
news?: { text: string }[],
|
||||
summary?: string,
|
||||
prompt?: string
|
||||
time?: string
|
||||
};
|
||||
}
|
||||
|
||||
@@ -225,6 +230,7 @@ export interface OB11PostSendMsg {
|
||||
news?: { text: string }[],
|
||||
summary?: string,
|
||||
prompt?: string
|
||||
time?: string
|
||||
}
|
||||
export interface OB11PostContext {
|
||||
message_type?: 'private' | 'group'
|
||||
|
@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
undefined,
|
||||
SettingButton('V3.1.6', 'napcat-update-button', 'secondary'),
|
||||
SettingButton('V3.1.8', 'napcat-update-button', 'secondary'),
|
||||
),
|
||||
]),
|
||||
SettingList([
|
||||
|
@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
void 0,
|
||||
SettingButton("V3.1.6", "napcat-update-button", "secondary")
|
||||
SettingButton("V3.1.8", "napcat-update-button", "secondary")
|
||||
)
|
||||
]),
|
||||
SettingList([
|
||||
|
Reference in New Issue
Block a user