Compare commits

...

28 Commits

Author SHA1 Message Date
手瓜一十雪
5d46f41348 fix: dep 2024-10-22 12:14:43 +08:00
手瓜一十雪
3c2c1963f4 release: 3.1.2 2024-10-22 12:11:02 +08:00
手瓜一十雪
4896ca9279 fix 2024-10-22 11:37:01 +08:00
手瓜一十雪
f0afba6cd9 fix: GetOnlineClient 2024-10-22 11:34:28 +08:00
手瓜一十雪
bd717c298a fix: get_online_clients 2024-10-22 11:17:39 +08:00
手瓜一十雪
baaa8a70dc release: 3.1.1 2024-10-22 11:08:24 +08:00
手瓜一十雪
6d561c6e6f Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-10-22 11:04:43 +08:00
手瓜一十雪
e6b6947d49 feat: 标准化凭据获取 2024-10-22 11:04:28 +08:00
手瓜一十雪
52e99a2175 Merge pull request #454 from huankong233/main
简单修复一些小问题
2024-10-22 10:17:08 +08:00
手瓜一十雪
052d17a46f fix: getfile 2024-10-22 10:15:16 +08:00
huankong233
1aa1f4c212 优化 getFile 处理逻辑 2024-10-22 09:48:55 +08:00
huankong233
c3a48e3344 修复标记好友/群聊信息已读逻辑 2024-10-21 19:51:17 +08:00
手瓜一十雪
1d5483dc28 Merge pull request #451 from huankong233/main
对接口顺序和文档同步
2024-10-21 16:47:46 +08:00
huankong233
54277fa0df CleanCache 未实现 2024-10-21 16:09:27 +08:00
huankong233
ab04bd262f 对接口顺序和文档同步 2024-10-21 15:49:57 +08:00
手瓜一十雪
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
pk5ls20
e7222653fa release: 3.0.6 2024-10-20 23:54:21 +08:00
pk5ls20
014f0758f5 chore: 部分回滚 https://github.com/NapNeko/NapCatQQ/commit/bb72d70b 2024-10-20 23:52:36 +08:00
pk5ls20
0e8b416f6d Merge pull request #448 from pk5ls20/feat/friend-poke
feat: add `friend_poke` OneBot11 API
2024-10-20 23:18:26 +08:00
pk5ls20
09a60a2204 feat: add friend_poke OneBot11 API 2024-10-20 23:09:38 +08:00
手瓜一十雪
b0eae307c2 release: 3.0.5 2024-10-20 22:18:57 +08:00
手瓜一十雪
f5d2b54cca fix: 兼容晚启动 2024-10-20 22:18:34 +08:00
手瓜一十雪
3eefec3899 release: v3.0.4 2024-10-20 19:52:23 +08:00
43 changed files with 641 additions and 500 deletions

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "3.0.3",
"version": "3.1.2",
"scripts": {
"build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell",
@@ -37,7 +37,7 @@
"commander": "^12.1.0",
"async-mutex": "^0.5.0",
"file-type": "^19.0.0",
"json-schema-to-ts": "^3.1.0",
"json-schema-to-ts": "^3.1.1",
"image-size": "^1.1.1",
"cors": "^2.8.5"
},
@@ -49,4 +49,4 @@
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
}
}
}

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.3';
export const napCatVersion = '3.1.2';

View File

@@ -318,7 +318,7 @@ export class NTQQGroupApi {
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
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();
const result = await this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, num);
if (result.errCode !== 0) {

View File

@@ -1,18 +1,19 @@
import * as os from 'os';
import {ChatType, 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} 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";
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]: {
@@ -26,7 +27,7 @@ const typedOffset: OffsetType = offset;
export class NTQQPacketApi {
context: InstanceContext;
core: NapCatCore;
logger: LogWrapper
logger: LogWrapper;
serverUrl: string | undefined;
qqVersion: string | undefined;
packetSession: PacketSession | undefined;
@@ -59,8 +60,12 @@ export class NTQQPacketApi {
if (!table) return false;
const url = 'ws://' + this.serverUrl + '/ws';
this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core));
await this.packetSession.client.connect();
await this.packetSession.client.init(process.pid, table.recv, table.send);
const cb = () => {
if (this.packetSession && this.packetSession.client) {
this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger));
}
};
await this.packetSession.client.connect(cb);
return true;
}
@@ -68,8 +73,8 @@ export class NTQQPacketApi {
return this.packetSession!.client.sendPacket(cmd, data, rsp);
}
async sendPokePacket(group: number, peer: number) {
const data = this.packetSession?.packer.packPokePacket(group, peer);
async sendPokePacket(peer: number, group?: number) {
const data = this.packetSession?.packer.packPokePacket(peer, group);
await this.sendPacket('OidbSvcTrpcTcp.0xed3_1', data!, false);
}
@@ -106,11 +111,11 @@ export class NTQQPacketApi {
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){
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
@@ -135,9 +140,9 @@ export class NTQQPacketApi {
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){
if (resp.download.retCode !== 0) {
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

@@ -338,4 +338,12 @@ export class NTQQWebApi {
}
return (hash & 0x7FFFFFFF).toString();
}
public getBknFromSKey(sKey: string) {
let hash = 5381;
for (let i = 0; i < sKey.length; i++) {
const code = sKey.charCodeAt(i);
hash = hash + (hash << 5) + code;
}
return (hash & 0x7FFFFFFF).toString();
}
}

View File

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

View File

@@ -22,7 +22,7 @@ export class PacketClient {
private websocket: WebSocket | undefined;
private isConnected: boolean = false;
private reconnectAttempts: number = 0;
private readonly maxReconnectAttempts: number = 5;//现在暂时不可配置
private readonly maxReconnectAttempts: number = 60;//现在暂时不可配置
private readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
private readonly clientUrl: string = '';
readonly napCatCore: NapCatCore;
@@ -47,16 +47,17 @@ export class PacketClient {
return text;
}
connect(): Promise<void> {
connect(cb: any): Promise<void> {
return new Promise((resolve, reject) => {
//this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`);
this.websocket = new WebSocket(this.clientUrl);
this.websocket.on('error', (err) => {}/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/);
this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/);
this.websocket.onopen = () => {
this.isConnected = true;
this.reconnectAttempts = 0;
this.logger.log.bind(this.logger)(`[Core] [Packet Server] Connected to ${this.clientUrl}`);
cb();
resolve();
};
@@ -74,17 +75,17 @@ export class PacketClient {
this.websocket.onclose = () => {
this.isConnected = false;
//this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`);
this.attemptReconnect();
this.attemptReconnect(cb);
};
});
}
private attemptReconnect(): void {
private attemptReconnect(cb: any): void {
try {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
this.connect().catch((error) => {
this.connect(cb).catch((error) => {
this.logger.logError.bind(this.logger)(`[Core] [Packet Server] Reconnecting attempt failed,${error.message}`);
});
}, 5000 * this.reconnectAttempts);

View File

@@ -1,8 +1,8 @@
import * as stream from 'node:stream';
import {ReadStream} from "node:fs";
import {PacketHighwaySig} from "@/core/packet/highway/session";
import {HighwayHttpUploader, HighwayTcpUploader} from "@/core/packet/highway/uploader";
import {LogWrapper} from "@/common/log";
import { ReadStream } from "node:fs";
import { PacketHighwaySig } from "@/core/packet/highway/session";
import { HighwayHttpUploader, HighwayTcpUploader } from "@/core/packet/highway/uploader";
import { LogWrapper } from "@/common/log";
export interface PacketHighwayTrans {
uin: string;

View File

@@ -1,16 +1,16 @@
import * as fs from "node:fs";
import {ChatType, Peer} from "@/core";
import {LogWrapper} from "@/common/log";
import {PacketClient} from "@/core/packet/client";
import {PacketPacker} from "@/core/packet/packer";
import {NapProtoMsg} from "@/core/packet/proto/NapProto";
import {HttpConn0x6ff_501Response} from "@/core/packet/proto/action/action";
import {PacketHighwayClient} from "@/core/packet/highway/client";
import {NTV2RichMediaResp} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
import {OidbSvcTrpcTcpBaseRsp} from "@/core/packet/proto/oidb/OidbBase";
import {PacketMsgPicElement} from "@/core/packet/msg/element";
import {NTV2RichMediaHighwayExt} from "@/core/packet/proto/highway/highway";
import {int32ip2str, oidbIpv4s2HighwayIpv4s} from "@/core/packet/highway/utils";
import { ChatType, Peer } from "@/core";
import { LogWrapper } from "@/common/log";
import { PacketClient } from "@/core/packet/client";
import { PacketPacker } from "@/core/packet/packer";
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action";
import { PacketHighwayClient } from "@/core/packet/highway/client";
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
import { OidbSvcTrpcTcpBaseRsp } from "@/core/packet/proto/oidb/OidbBase";
import { PacketMsgPicElement } from "@/core/packet/msg/element";
import { NTV2RichMediaHighwayExt } from "@/core/packet/proto/highway/highway";
import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils";
export const BlockSize = 1024 * 1024;
@@ -42,7 +42,7 @@ export class PacketHighwaySession {
sigSession: null,
sessionKey: null,
serverAddr: [],
}
};
this.packer = packer;
this.packetHighwayClient = new PacketHighwayClient(this.sig, this.logger);
}
@@ -69,8 +69,8 @@ export class PacketHighwaySession {
const rsp = new NapProtoMsg(HttpConn0x6ff_501Response).decode(
Buffer.from(req.hex_data, 'hex')
);
this.sig.sigSession = rsp.httpConn.sigSession
this.sig.sessionKey = rsp.httpConn.sessionKey
this.sig.sigSession = rsp.httpConn.sigSession;
this.sig.sessionKey = rsp.httpConn.sessionKey;
for (const info of rsp.httpConn.serverInfos) {
if (info.serviceType !== 1) continue;
for (const addr of info.serverAddrs) {
@@ -78,7 +78,7 @@ export class PacketHighwaySession {
this.sig.serverAddr.push({
ip: int32ip2str(addr.ip),
port: addr.port
})
});
}
}
}
@@ -118,10 +118,10 @@ export class PacketHighwaySession {
hash: {
fileSha1: [sha1]
}
})
});
await this.packetHighwayClient.upload(
1004,
fs.createReadStream(img.path, {highWaterMark: BlockSize}),
fs.createReadStream(img.path, { highWaterMark: BlockSize }),
img.size,
md5,
extend
@@ -157,10 +157,10 @@ export class PacketHighwaySession {
hash: {
fileSha1: [sha1]
}
})
});
await this.packetHighwayClient.upload(
1003,
fs.createReadStream(img.path, {highWaterMark: BlockSize}),
fs.createReadStream(img.path, { highWaterMark: BlockSize }),
img.size,
md5,
extend

View File

@@ -2,13 +2,13 @@ import * as net from "node:net";
import * as crypto from "node:crypto";
import * as http from "node:http";
import * as stream from "node:stream";
import {LogWrapper} from "@/common/log";
import { LogWrapper } from "@/common/log";
import * as tea from "@/core/packet/utils/crypto/tea";
import {NapProtoMsg} from "@/core/packet/proto/NapProto";
import {ReqDataHighwayHead, RespDataHighwayHead} from "@/core/packet/proto/highway/highway";
import {BlockSize} from "@/core/packet/highway/session";
import {PacketHighwayTrans} from "@/core/packet/highway/client";
import {Frame} from "@/core/packet/highway/frame";
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
import { ReqDataHighwayHead, RespDataHighwayHead } from "@/core/packet/proto/highway/highway";
import { BlockSize } from "@/core/packet/highway/session";
import { PacketHighwayTrans } from "@/core/packet/highway/client";
import { Frame } from "@/core/packet/highway/frame";
abstract class HighwayUploader {
readonly trans: PacketHighwayTrans;
@@ -53,7 +53,7 @@ abstract class HighwayUploader {
uint32LoginSigType: 8,
appId: 1600001604,
}
})
});
}
abstract upload(): Promise<void>;
@@ -89,8 +89,8 @@ export class HighwayTcpUploader extends HighwayUploader {
const highwayTransForm = new HighwayTcpUploaderTransform(this);
const upload = new Promise<void>((resolve, _) => {
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);
if (rsp.errorCode !== 0) {
@@ -111,25 +111,25 @@ export class HighwayTcpUploader extends HighwayUploader {
} catch (e) {
this.logger.logError(`[Highway] tcpUpload parse response error: ${e}`);
}
})
});
socket.on('close', () => {
this.logger.logDebug('[Highway] tcpUpload socket closed.');
resolve();
})
});
socket.on('error', (err) => {
this.logger.logError('[Highway] tcpUpload socket.on error:', err);
})
});
this.trans.data.on('error', (err) => {
this.logger.logError('[Highway] tcpUpload readable error:', err);
socket.end();
})
})
});
});
const timeout = new Promise<void>((_, reject) => {
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
)
})
);
});
await Promise.race([upload, timeout]);
}
}
@@ -139,7 +139,7 @@ export class HighwayHttpUploader extends HighwayUploader {
async upload(): Promise<void> {
let offset = 0;
for await (const chunk of this.trans.data) {
let block = chunk as Buffer;
const block = chunk as Buffer;
try {
await this.uploadBlock(block, offset);
} catch (err) {
@@ -153,7 +153,7 @@ export class HighwayHttpUploader extends HighwayUploader {
private async uploadBlock(block: Buffer, offset: number): Promise<void> {
const chunkMD5 = crypto.createHash('md5').update(block).digest();
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 [head, body] = Frame.unpack(resp);
const headData = new NapProtoMsg(RespDataHighwayHead).decode(head);

View File

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

View File

@@ -1,8 +1,8 @@
import * as crypto from "crypto";
import {PushMsgBody} from "@/core/packet/proto/message/message";
import {NapProtoEncodeStructType} from "@/core/packet/proto/NapProto";
import {LogWrapper} from "@/common/log";
import {PacketMsg} from "@/core/packet/msg/message";
import { PushMsgBody } from "@/core/packet/proto/message/message";
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
import { LogWrapper } from "@/common/log";
import { PacketMsg } from "@/core/packet/msg/message";
export class PacketMsgBuilder {
private logger: LogWrapper;

View File

@@ -28,8 +28,8 @@ import {
PacketMsgVideoElement,
PacketMultiMsgElement
} from "@/core/packet/msg/element";
import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
import {LogWrapper} from "@/common/log";
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message";
import { LogWrapper } from "@/common/log";
type SendMessageElementMap = {
textElement: SendTextElement,
@@ -82,50 +82,50 @@ export class PacketMsgConverter {
);
if (key) {
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;
}).filter((e) => e !== null)
}
};
}
private rawToPacketMsgConverters: RawToPacketMsgConverters = {
textElement: (element: SendTextElement) => {
if (element.textElement.atType) {
return new PacketMsgAtElement(element)
return new PacketMsgAtElement(element);
}
return new PacketMsgTextElement(element)
return new PacketMsgTextElement(element);
},
picElement: (element: SendPicElement) => {
return new PacketMsgPicElement(element)
return new PacketMsgPicElement(element);
},
replyElement: (element: SendReplyElement) => {
return new PacketMsgReplyElement(element)
return new PacketMsgReplyElement(element);
},
faceElement: (element: SendFaceElement) => {
return new PacketMsgFaceElement(element)
return new PacketMsgFaceElement(element);
},
marketFaceElement: (element: SendMarketFaceElement) => {
return new PacketMsgMarkFaceElement(element)
return new PacketMsgMarkFaceElement(element);
},
videoElement: (element: SendVideoElement) => {
return new PacketMsgVideoElement(element)
return new PacketMsgVideoElement(element);
},
fileElement: (element: SendFileElement) => {
return new PacketMsgFileElement(element)
return new PacketMsgFileElement(element);
},
pttElement: (element: SendPttElement) => {
return new PacketMsgPttElement(element)
return new PacketMsgPttElement(element);
},
arkElement: (element: SendArkElement) => {
return new PacketMsgLightAppElement(element)
return new PacketMsgLightAppElement(element);
},
markdownElement: (element: SendMarkdownElement) => {
return new PacketMsgMarkDownElement(element)
return new PacketMsgMarkDownElement(element);
},
// TODO: check this logic, move it in arkElement?
structLongMsgElement: (element: SendStructLongMsgElement) => {
return new PacketMultiMsgElement(element)
return new PacketMultiMsgElement(element);
}
}
};
}

View File

@@ -1,7 +1,6 @@
import assert from "node:assert";
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 {
CustomFace,
Elem,
@@ -26,12 +25,11 @@ import {
SendTextElement,
SendVideoElement
} from "@/core";
import {MsgInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
import { MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message";
import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
// raw <-> packet
// TODO: check ob11 -> raw impl!
// TODO: parse to raw element
// TODO: SendStructLongMsgElement
export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
protected constructor(rawElement: T) {
@@ -86,11 +84,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,
}
)
}
}];
@@ -103,7 +101,7 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
path: string;
name: string
name: string;
size: number;
md5: string;
width: number;
@@ -133,7 +131,7 @@ export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
pbElem: new NapProtoMsg(MsgInfo).encode(this.msgInfo),
businessType: 10,
}
}]
}];
}
toPreview(): string {
@@ -187,7 +185,7 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
uid: String(this.targetUid),
}),
} : undefined,
}]
}];
}
toPreview(): string {
@@ -221,7 +219,7 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
}),
businessType: 1
}
}]
}];
} else if (this.faceId < 260) {
return [{
face: {
@@ -239,7 +237,7 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
}),
businessType: 1
}
}]
}];
}
}
@@ -278,11 +276,11 @@ export class PacketMsgMarkFaceElement extends IPacketMsgElement<SendMarketFaceEl
field8: 1
}
}
}]
}];
}
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'))
])
}
}]
}];
}
toPreview(): string {
return "[小程序]";
return "[卡片消息]";
}
}
@@ -345,11 +343,11 @@ export class PacketMsgMarkDownElement extends IPacketMsgElement<SendMarkdownElem
}),
businessType: 1
}
}]
}];
}
toPreview(): string {
return this.content;
return `[Markdown消息 ${this.content}]`;
}
}
@@ -363,58 +361,15 @@ export class PacketMultiMsgElement extends IPacketMsgElement<SendStructLongMsgEl
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>[] {
return [{
lightAppElem: {
data: Buffer.concat([
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 {

View File

@@ -1,5 +1,5 @@
import {IPacketMsgElement} from "@/core/packet/msg/element";
import {SendMessageElement, SendStructLongMsgElement} from "@/core";
import { IPacketMsgElement } from "@/core/packet/msg/element";
import { SendMessageElement, SendStructLongMsgElement } from "@/core";
export type PacketSendMsgElement = SendMessageElement | SendStructLongMsgElement

View File

@@ -1,23 +1,23 @@
import * as zlib from "node:zlib";
import * as crypto from "node:crypto";
import {calculateSha1} from "@/core/packet/utils/crypto/hash"
import {NapProtoMsg} from "@/core/packet/proto/NapProto";
import {OidbSvcTrpcTcpBase} from "@/core/packet/proto/oidb/OidbBase";
import {OidbSvcTrpcTcp0X9067_202} from "@/core/packet/proto/oidb/Oidb.0x9067_202";
import {OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body} from "@/core/packet/proto/oidb/Oidb.0x8FC_2";
import {OidbSvcTrpcTcp0XFE1_2} from "@/core/packet/proto/oidb/Oidb.0XFE1_2";
import {OidbSvcTrpcTcp0XED3_1} from "@/core/packet/proto/oidb/Oidb.0xED3_1";
import {NTV2RichMediaReq} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
import {HttpConn0x6ff_501} from "@/core/packet/proto/action/action";
import {LongMsgResult, SendLongMsgReq} from "@/core/packet/proto/message/action";
import {PacketMsgBuilder} from "@/core/packet/msg/builder";
import {PacketMsgPicElement} from "@/core/packet/msg/element";
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";
import { calculateSha1 } from "@/core/packet/utils/crypto/hash";
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
import { OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body } from "@/core/packet/proto/oidb/Oidb.0x8FC_2";
import { OidbSvcTrpcTcp0XFE1_2 } from "@/core/packet/proto/oidb/Oidb.0XFE1_2";
import { OidbSvcTrpcTcp0XED3_1 } from "@/core/packet/proto/oidb/Oidb.0xED3_1";
import { NTV2RichMediaReq } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
import { PacketMsgBuilder } from "@/core/packet/msg/builder";
import { PacketMsgPicElement } from "@/core/packet/msg/element";
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 };
@@ -47,11 +47,11 @@ export class PacketPacker {
});
}
packPokePacket(group: number, peer: number): PacketHexStr {
packPokePacket(peer: number, group?: number): PacketHexStr {
const oidb_0xed3 = new NapProtoMsg(OidbSvcTrpcTcp0XED3_1).encode({
uin: peer,
groupUin: group,
friendUin: group,
friendUin: group ?? peer,
ext: 0
});
return this.toHexStr(this.packOidbPacket(0xed3, 1, oidb_0xed3));
@@ -97,7 +97,7 @@ export class PacketPacker {
packStatusPacket(uin: number): PacketHexStr {
const oidb_0xfe1_2 = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2).encode({
uin: uin,
key: [{key: 27372}]
key: [{ key: 27372 }]
});
return this.toHexStr(this.packOidbPacket(0xfe1, 2, oidb_0xfe1_2));
}
@@ -217,75 +217,75 @@ export class PacketPacker {
noNeedCompatMsg: false,
}
}
)
);
return this.toHexStr(this.packOidbPacket(0x11c4, 100, req, true, false));
}
async packUploadC2CImgReq(peerUin: string, img: PacketMsgPicElement): Promise<PacketHexStr> {
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: Number(img.size),
fileHash: img.md5,
fileSha1: this.toHexStr(await calculateSha1(img.path)),
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: Number(img.size),
fileHash: img.md5,
fileSha1: this.toHexStr(await calculateSha1(img.path)),
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.toHexStr(this.packOidbPacket(0x11c5, 100, req, true, false));
}
@@ -299,7 +299,7 @@ export class PacketPacker {
fileId: fileUUID
}
}), true, false)
)
);
}
packC2CFileDownloadReq(selfUid: string, fileUUID: string, fileHash: string): PacketHexStr {
@@ -319,6 +319,6 @@ export class PacketPacker {
field200: 1,
field99999: Buffer.from([0xc0, 0x85, 0x2c, 0x01])
})
)
);
}
}

View File

@@ -1,6 +1,6 @@
import { ScalarType } from "@protobuf-ts/runtime";
import { ProtoField } from "../NapProto";
import {ContentHead, MessageBody, MessageControl, RoutingHead} from "@/core/packet/proto/message/message";
import { ContentHead, MessageBody, MessageControl, RoutingHead } from "@/core/packet/proto/message/message";
export const FaceRoamRequest = {
comm: ProtoField(1, () => PlatInfo, true),

View File

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

View File

@@ -1,5 +1,5 @@
import {ScalarType} from "@protobuf-ts/runtime";
import {ProtoField} from "../NapProto";
import { ScalarType } from "@protobuf-ts/runtime";
import { ProtoField } from "../NapProto";
export const Elem = {
text: ProtoField(1, () => Text, true),
@@ -118,7 +118,7 @@ export const MarketFace = {
export const MarketFacePbRes = {
field8: ProtoField(8, ScalarType.INT32)
}
};
export const CustomFace = {
guid: ProtoField(1, ScalarType.BYTES),
@@ -315,7 +315,7 @@ export const SrcMsgPbRes = {
senderUid: ProtoField(6, ScalarType.STRING, true),
receiverUid: ProtoField(7, ScalarType.STRING, true),
friendSeq: ProtoField(8, ScalarType.UINT32, true),
}
};
export const LightAppElem = {
data: ProtoField(1, ScalarType.BYTES),
@@ -358,4 +358,4 @@ export const QSmallFaceExtra = {
export const MarkdownData = {
content: ProtoField(1, ScalarType.STRING)
}
};

View File

@@ -1,5 +1,5 @@
import {ScalarType} from "@protobuf-ts/runtime";
import {ProtoField} from "../../NapProto";
import { ScalarType } from "@protobuf-ts/runtime";
import { ProtoField } from "../../NapProto";
export const NTV2RichMediaReq = {
ReqHead: ProtoField(1, () => MultiMediaReqHead),
@@ -160,7 +160,7 @@ export const PicUrlExtInfo = {
export const VideoExtInfo = {
VideoCodecFormat: ProtoField(1, ScalarType.UINT32),
}
};
export const ExtBizInfo = {
Pic: ProtoField(1, () => PicExtBizInfo),

View File

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

View File

@@ -1,7 +1,7 @@
import { PacketClient } from "@/core/packet/client";
import { PacketHighwaySession } from "@/core/packet/highway/session";
import { LogWrapper } from "@/common/log";
import {PacketPacker} from "@/core/packet/packer";
import { PacketPacker } from "@/core/packet/packer";
export class PacketSession {
readonly logger: LogWrapper;

View File

@@ -76,7 +76,7 @@ export function decrypt(encrypted: Buffer, key: Buffer) {
encrypted.writeInt32BE(r1, i);
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;
return encrypted.subarray((encrypted[0] & 0x07) + 3, encrypted.length - 7);
// return encrypted.slice((encrypted[0] & 0x07) + 3, encrypted.length - 7);

View File

@@ -1,3 +1,4 @@
import { GroupNotifyMsgStatus } from '@/core';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
@@ -11,18 +12,22 @@ export default class GetGroupAddRequest extends BaseAction<null, OB11GroupReques
actionName = ActionName.GetGroupIgnoreAddRequest;
async _handle(payload: null): Promise<OB11GroupRequestNotify[] | null> {
// const data = await this.core.apis.GroupApi.getGroupIgnoreNotifies();
// log(data);
// const notifies: GroupNotify[] = data.notifies.filter(notify => notify.status === GroupNotifyStatus.WAIT_HANDLE);
// const returnData: OB11GroupRequestNotify[] = [];
// for (const notify of notifies) {
// const uin = || (await NTQQUserApi.getUserDetailInfo(notify.user1.uid))?.uin;
// returnData.push({
// group_id: parseInt(notify.group.groupCode),
// user_id: parseInt(uin),
// flag: notify.seq
// });
// }
return null;
const ignoredNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(true, 10);
const retData: any = {
join_requests: await Promise.all(
ignoredNotifies
.filter(notify => notify.type === 7)
.map(async SSNotify => ({
request_id: SSNotify.seq,
requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
}))),
};
return retData;
}
}

View File

@@ -1,6 +1,6 @@
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import {GetPacketStatusDepends} from "@/onebot/action/packet/GetPacketStatus";
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
// no_cache get时传字符串
const SchemaData = {
type: 'object',

View File

@@ -5,9 +5,9 @@ import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { OB11MessageImage, OB11MessageVideo } from '@/onebot/types';
export interface GetFilePayload {
file: string; // 文件名或者fileUuid
}
// interface GetFilePayload {
// file: string; // 文件名或者fileUuid
// }
export interface GetFileResponse {
file?: string; // path
@@ -16,19 +16,25 @@ export interface GetFileResponse {
file_name?: string;
base64?: string;
}
const GetFileBase_PayloadSchema = {
type: 'object',
properties: {
file: { type: 'string' },
file_id: { type: 'string' }
},
required: ['file'],
oneOf: [
{ required: ['file'] },
{ required: ['file_id'] }
]
} as const satisfies JSONSchema;
export type GetFilePayload = FromSchema<typeof GetFileBase_PayloadSchema>;
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
payloadSchema: any = GetFileBase_PayloadSchema;
payloadSchema = GetFileBase_PayloadSchema;
async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
payload.file ||= payload.file_id || '';
//接收消息标记模式
const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file);
if (contextMsgFile) {
@@ -115,27 +121,6 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
}
}
const GetFile_PayloadSchema = {
type: 'object',
properties: {
file_id: { type: 'string' },
file: { type: 'string' },
},
required: ['file_id'],
} as const satisfies JSONSchema;
type GetFile_Payload_Internal = FromSchema<typeof GetFile_PayloadSchema>;
interface GetFile_Payload extends GetFile_Payload_Internal {
file: string;
}
export default class GetFile extends GetFileBase {
actionName = ActionName.GetFile;
payloadSchema = GetFile_PayloadSchema;
async _handle(payload: GetFile_Payload): Promise<GetFileResponse> {
payload.file = payload.file_id;
return super._handle(payload);
}
}

View File

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

View File

@@ -1,5 +1,5 @@
import BaseAction from '../BaseAction';
import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNodePlain as OB11MessageNode} from '@/onebot';
import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNodePlain as OB11MessageNode } from '@/onebot';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/message-unique';

View File

@@ -1,6 +1,6 @@
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import {GetPacketStatusDepends} from "@/onebot/action/packet/GetPacketStatus";
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
// no_cache get时传字符串
const SchemaData = {
type: 'object',
@@ -18,6 +18,6 @@ export class GroupPoke extends GetPacketStatusDepends<Payload, any> {
payloadSchema = SchemaData;
async _handle(payload: Payload) {
await this.core.apis.PacketApi.sendPokePacket(+payload.group_id, +payload.user_id);
await this.core.apis.PacketApi.sendPokePacket(+payload.user_id, +payload.group_id);
}
}

View File

@@ -90,7 +90,9 @@ import { SetSpecialTittle } from './extends/SetSpecialTittle';
import { GetGroupShutList } from './group/GetGroupShutList';
import { GetGroupMemberList } from './group/GetGroupMemberList';
import { GetGroupFileUrl } from "@/onebot/action/file/GetGroupFileUrl";
import {GetPacketStatus} from "@/onebot/action/packet/GetPacketStatus";
import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
import { GetCredentials } from './system/GetCredentials';
export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -179,6 +181,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SetModelShow(obContext, core),
new SetInputStatus(obContext, core),
new GetCSRF(obContext, core),
new GetCredentials(obContext, core),
new DelGroupNotice(obContext, core),
new DeleteGroupFile(obContext, core),
new CreateGroupFileFolder(obContext, core),
@@ -189,6 +192,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new FetchUserProfileLike(obContext, core),
new GetPacketStatus(obContext, core),
new GroupPoke(obContext, core),
new FriendPoke(obContext, core),
new GetUserStatus(obContext, core),
new GetRkey(obContext, core),
new SetSpecialTittle(obContext, core),

View File

@@ -1,7 +1,7 @@
import { ChatType, Peer } from '@/core/entities';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
@@ -49,23 +49,14 @@ export class MarkGroupMsgAsRead extends MarkMsgAsRead {
actionName = ActionName.MarkGroupMsgAsRead;
}
interface Payload {
message_id: number;
}
export class GoCQHTTPMarkMsgAsRead extends BaseAction<Payload, null> {
export class GoCQHTTPMarkMsgAsRead extends MarkMsgAsRead {
actionName = ActionName.GoCQHTTP_MarkMsgAsRead;
async _handle(payload: Payload): Promise<null> {
return null;
}
}
export class MarkAllMsgAsRead extends BaseAction<Payload, null> {
export class MarkAllMsgAsRead extends BaseAction<any, null> {
actionName = ActionName._MarkAllMsgAsRead;
async _handle(payload: Payload): Promise<null> {
async _handle(): Promise<null> {
await this.core.apis.MsgApi.markAllMsgAsRead();
return null;
}

View File

@@ -6,14 +6,14 @@ 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, SendArkElement, 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";
import { rawMsgWithSendMsg } from "@/core/packet/msg/converter";
import { PacketMsg } from "@/core/packet/msg/message";
import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
export interface ReturnDataType {
message_id: number;
@@ -30,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];
}
@@ -100,7 +100,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
};
}
return {valid: true};
return { valid: true };
}
async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
@@ -115,7 +115,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
);
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const packetMode = this.core.apis.PacketApi.available
const packetMode = this.core.apis.PacketApi.available;
const returnMsgAndResId = packetMode
? await this.handleForwardedNodesPacket(peer, messages as OB11MessageNode[])
: await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
@@ -125,7 +125,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
peerUid: peer.peerUid,
chatType: peer.chatType,
}, (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('发送转发消息失败');
} else {
@@ -137,12 +139,13 @@ 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! };
}
// TODO: recursively handle forwarded nodes
private async handleForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
message: RawMessage | null,
res_id?: string
@@ -152,14 +155,14 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
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 { 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)}`);
@@ -169,23 +172,21 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
}
}
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 forwardJson = ForwardMsgBuilder.fromPacketMsg(resid, packetMsg);
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};
} 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 };
}
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<{
@@ -227,7 +228,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
//完成子卡片生成跳过后续
continue;
}
const {sendElements} = await this.obContext.apis.MsgApi
const { sendElements } = await this.obContext.apis.MsgApi
.createSendElements(OB11Data, destPeer);
//拆分消息
@@ -268,7 +269,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;
}

View File

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

View File

@@ -5,8 +5,12 @@ export class GetCSRF extends BaseAction<any, any> {
actionName = ActionName.GetCSRF;
async _handle(payload: any) {
const sKey = await this.core.apis.UserApi.getSKey();
if (!sKey) {
throw new Error('SKey is undefined');
}
return {
token: '',
token: +this.core.apis.WebApi.getBknFromSKey(sKey),
};
}
}

View File

@@ -0,0 +1,31 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
interface Response {
cookies: string,
token: number
}
const SchemaData = {
type: 'object',
properties: {
domain: { type: 'string' },
},
required: ['domain'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetCredentials extends BaseAction<Payload, Response> {
actionName = ActionName.GetCredentials;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const cookiesObject = await this.core.apis.UserApi.getCookies(payload.domain);
//把获取到的cookiesObject转换成 k=v; 格式字符串拼接在一起
const cookies = Object.entries(cookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const bkn = cookiesObject?.skey ? this.core.apis.WebApi.getBknFromCookie(cookiesObject) : '';
return { cookies: cookies, token: +bkn };
}
}

View File

@@ -1,4 +1,4 @@
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult;
export interface ValidCheckResult {
valid: true;
@@ -14,20 +14,93 @@ export interface InvalidCheckResult {
}
export enum ActionName {
// onebot 11
SendPrivateMsg = 'send_private_msg',
SendGroupMsg = 'send_group_msg',
SendMsg = 'send_msg',
DeleteMsg = 'delete_msg',
GetMsg = 'get_msg',
GoCQHTTP_GetForwardMsg = 'get_forward_msg',
SendLike = 'send_like',
SetGroupKick = 'set_group_kick',
SetGroupBan = 'set_group_ban',
// SetGroupAnoymousBan = 'set_group_anonymous_ban',
SetGroupWholeBan = 'set_group_whole_ban',
SetGroupAdmin = 'set_group_admin',
// SetGroupAnoymous = 'set_group_anonymous',
SetGroupCard = 'set_group_card',
SetGroupName = 'set_group_name',
SetGroupLeave = 'set_group_leave',
SetSpecialTittle = 'set_group_special_title',
SetFriendAddRequest = 'set_friend_add_request',
SetGroupAddRequest = 'set_group_add_request',
GetLoginInfo = 'get_login_info',
GoCQHTTP_GetStrangerInfo = 'get_stranger_info',
GetFriendList = 'get_friend_list',
GetGroupInfo = 'get_group_info',
GetGroupList = 'get_group_list',
GetGroupMemberInfo = 'get_group_member_info',
GetGroupMemberList = 'get_group_member_list',
GetGroupHonorInfo = 'get_group_honor_info',
GetCookies = 'get_cookies',
GetCSRF = 'get_csrf_token',
GetCredentials = 'get_credentials',
GetRecord = 'get_record',
GetImage = 'get_image',
CanSendImage = 'can_send_image',
CanSendRecord = 'can_send_record',
GetStatus = 'get_status',
GetVersionInfo = 'get_version_info',
// Reboot = 'set_restart',
// CleanCache = 'clean_cache',
// go-cqhttp
SetQQProfile = 'set_qq_profile',
// QidianGetAccountInfo = 'qidian_get_account_info',
// GetModelShow = '_get_model_show',
// SetModelShow = '_set_model_show',
GetOnlineClient = 'get_online_clients',
// GetUnidirectionalFriendList = 'get_unidirectional_friend_list',
// DeleteFriend = 'delete_friend',
// DeleteUnidirectionalFriendList = 'delete_unidirectional_friend',
GoCQHTTP_MarkMsgAsRead = 'mark_msg_as_read',
GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg',
GoCQHTTP_SendPrivateForwardMsg = 'send_private_forward_msg',
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history',
OCRImage = 'ocr_image',
IOCRImage = '.ocr_image',
GetGroupSystemMsg = 'get_group_system_msg',
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list',
// GetGroupAtAllRemain = 'get_group_at_all_remain',
SetGroupPortrait = 'set_group_portrait',
SetEssenceMsg = 'set_essence_msg',
DelEssenceMsg = 'delete_essence_msg',
GoCQHTTP_SendGroupNotice = '_send_group_notice',
GoCQHTTP_GetGroupNotice = '_get_group_notice',
GoCQHTTP_UploadGroupFile = 'upload_group_file',
GOCQHTTP_DeleteGroupFile = 'delete_group_file',
GoCQHTTP_CreateGroupFileFolder = 'create_group_file_folder',
GoCQHTTP_DeleteGroupFileFolder = 'delete_group_folder',
GoCQHTTP_GetGroupFileSystemInfo = 'get_group_file_system_info',
GoCQHTTP_GetGroupRootFiles = 'get_group_root_files',
GoCQHTTP_GetGroupFilesByFolder = 'get_group_files_by_folder',
GOCQHTTP_GetGroupFileUrl = 'get_group_file_url',
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_HandleQuickAction = '.handle_quick_operation',
// 以下为扩展napcat扩展
Unknown = 'unknown',
GroupPoke = 'group_poke',
SharePeer = 'ArkSharePeer',
ShareGroupEx = 'ArkShareGroup',
RebootNormal = 'reboot_normal',//无快速登录重新启动
// RebootNormal = 'reboot_normal', //无快速登录重新启动
GetRobotUinRange = 'get_robot_uin_range',
SetOnlineStatus = 'set_online_status',
GetFriendsWithCategory = 'get_friends_with_category',
GetGroupIgnoreAddRequest = 'get_group_ignore_add_request',
SetQQAvatar = 'set_qq_avatar',
GetConfig = 'get_config',
SetConfig = 'set_config',
Debug = 'debug',
GetFile = 'get_file',
ForwardFriendSingleMsg = 'forward_friend_single_msg',
ForwardGroupSingleMsg = 'forward_group_single_msg',
@@ -37,94 +110,35 @@ export enum ActionName {
SetGroupFileFolder = 'set_group_file_folder',
DelGroupFile = 'del_group_file',
DelGroupFileFolder = 'del_group_file_folder',
// onebot 11
Reboot = 'set_restart',
SendLike = 'send_like',
GetLoginInfo = 'get_login_info',
GetFriendList = 'get_friend_list',
GetGroupInfo = 'get_group_info',
GetGroupList = 'get_group_list',
GetGroupMemberInfo = 'get_group_member_info',
GetGroupMemberList = 'get_group_member_list',
GetMsg = 'get_msg',
SendMsg = 'send_msg',
SendGroupMsg = 'send_group_msg',
SendPrivateMsg = 'send_private_msg',
DeleteMsg = 'delete_msg',
SetMsgEmojiLike = 'set_msg_emoji_like',
SetGroupAddRequest = 'set_group_add_request',
SetFriendAddRequest = 'set_friend_add_request',
SetGroupLeave = 'set_group_leave',
GetVersionInfo = 'get_version_info',
GetStatus = 'get_status',
CanSendRecord = 'can_send_record',
CanSendImage = 'can_send_image',
SetGroupKick = 'set_group_kick',
SetGroupBan = 'set_group_ban',
SetGroupWholeBan = 'set_group_whole_ban',
SetGroupAdmin = 'set_group_admin',
SetGroupCard = 'set_group_card',
SetGroupName = 'set_group_name',
GetImage = 'get_image',
GetRecord = 'get_record',
CleanCache = 'clean_cache',
GetCookies = 'get_cookies',
// 以下为go-cqhttp api
GoCQHTTP_HandleQuickAction = '.handle_quick_operation',
GetGroupHonorInfo = 'get_group_honor_info',
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list',
GoCQHTTP_SendGroupNotice = '_send_group_notice',
GoCQHTTP_GetGroupNotice = '_get_group_notice',
GoCQHTTP_SendForwardMsg = 'send_forward_msg',
GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg',
GoCQHTTP_SendPrivateForwardMsg = 'send_private_forward_msg',
GoCQHTTP_GetStrangerInfo = 'get_stranger_info',
GoCQHTTP_MarkMsgAsRead = 'mark_msg_as_read',
GetGuildList = 'get_guild_list',
MarkPrivateMsgAsRead = 'mark_private_msg_as_read',
MarkGroupMsgAsRead = 'mark_group_msg_as_read',
GoCQHTTP_UploadGroupFile = 'upload_group_file',
GOCQHTTP_DeleteGroupFile = 'delete_group_file',
GOCQHTTP_GetGroupFileUrl = 'get_group_file_url',
GoCQHTTP_CreateGroupFileFolder = 'create_group_file_folder',
GoCQHTTP_DeleteGroupFileFolder = 'delete_group_file_folder',
GoCQHTTP_GetGroupFileSystemInfo = 'get_group_file_system_info',
GoCQHTTP_GetGroupRootFiles = 'get_group_root_files',
GoCQHTTP_GetGroupFilesByFolder = 'get_group_files_by_folder',
GoCQHTTP_DownloadFile = 'download_file',
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history',
GoCQHTTP_GetForwardMsg = 'get_forward_msg',
GetFriendMsgHistory = 'get_friend_msg_history',
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
GetOnlineClient = 'get_online_clients',
OCRImage = 'ocr_image',
IOCRImage = '.ocr_image',
SetQQProfile = 'set_qq_profile',
CreateCollection = 'create_collection',
GetCollectionList = 'get_collection_list',
SetLongNick = 'set_self_longnick',
SetEssenceMsg = 'set_essence_msg',
DelEssenceMsg = 'delete_essence_msg',
GetRecentContact = 'get_recent_contact',
_MarkAllMsgAsRead = '_mark_all_as_read',
GetProfileLike = 'get_profile_like',
SetGroupPortrait = 'set_group_portrait',
FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
TestApi01 = 'test_api_01',
FetchEmojiLike = 'fetch_emoji_like',
GetGuildProfile = 'get_guild_service_profile',
SetModelShow = '_set_model_show',
SetInputStatus = 'set_input_status',
GetCSRF = 'get_csrf_token',
GetGroupInfoEx = 'get_group_info_ex',
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
DelGroupNotice = '_del_group_notice',
GetGroupInfoEx = "get_group_info_ex",
GetGroupSystemMsg = 'get_group_system_msg',
FetchUserProfileLike = "fetch_user_profile_like",
FetchUserProfileLike = 'fetch_user_profile_like',
FriendPoke = 'friend_poke',
GroupPoke = 'group_poke',
GetPacketStatus = 'nc_get_packet_status',
GetUserStatus = "nc_get_user_status",
GetRkey = "nc_get_rkey",
SetSpecialTittle = "set_group_special_title",
GetUserStatus = 'nc_get_user_status',
GetRkey = 'nc_get_rkey',
GetGroupShutList = 'get_group_shut_list',
GetGuildList = 'get_guild_list',
GetGuildProfile = 'get_guild_service_profile',
GetGroupIgnoreAddRequest = 'get_group_ignore_add_request',
// Debug = 'debug',
// TestApi01 = 'test_api_01',
// UploadForwardMsg = "upload_forward_msg",
GetGroupShutList = "get_group_shut_list",
}

View File

@@ -0,0 +1,22 @@
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
const SchemaData = {
type: 'object',
properties: {
user_id: { type: ['number', 'string'] },
},
required: ['user_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class FriendPoke extends GetPacketStatusDepends<Payload, any> {
actionName = ActionName.FriendPoke;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
await this.core.apis.PacketApi.sendPokePacket(+payload.user_id);
}
}

View File

@@ -26,15 +26,16 @@ 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';
import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -600,7 +601,13 @@ export class OneBotMsgApi {
[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,

View File

@@ -304,10 +304,10 @@ export class NapCatOneBot11Adapter {
},
m.msgId,
);
if (m.sourceType == MsgSourceType.K_DOWN_SOURCETYPE_AIOINNER) {
await this.emitMsg(m)
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
}
// if (m.sourceType == MsgSourceType.K_DOWN_SOURCETYPE_AIOINNER) {
await this.emitMsg(m)
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
// }
}
};
@@ -576,8 +576,6 @@ export class NapCatOneBot11Adapter {
this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e));
console.log('message', message);
this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => {
if (groupEvent) {
// log("post group event", groupEvent);

View File

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

View File

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