mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
1 Commits
v4.7.51
...
forward-fi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
acccb7fce8 |
10
README.md
10
README.md
@@ -1,9 +1,8 @@
|
||||
<img src="https://napneko.github.io/assets/newnewlogo.png" width = "305" height = "411" alt="NapCat" align=right />
|
||||
<div align="center">
|
||||
|
||||
# NapCat
|
||||
|
||||
|
||||

|
||||
|
||||
_Modern protocol-side framework implemented based on NTQQ._
|
||||
|
||||
@@ -41,11 +40,8 @@ _Modern protocol-side framework implemented based on NTQQ._
|
||||
| Docs | [](https://napneko.pages.dev/) | [](https://napcat.cyou/) | [](https://www.napcat.wiki) |
|
||||
|:-:|:-:|:-:|:-:|
|
||||
|
||||
| QQ Group | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/8zJMLjqy2Y) | [](https://qm.qq.com/q/HaRcfrHpUk) | [](https://qm.qq.com/q/I6LU87a0Yq) |
|
||||
|:-:|:-:|:-:|:-:|:-:|
|
||||
|
||||
| Telegram | [](https://t.me/MelodicMoonlight) |
|
||||
|:-:|:-:|
|
||||
| Contact | [](https://qm.qq.com/q/I6LU87a0Yq) | [](https://qm.qq.com/q/HaRcfrHpUk) | [](https://t.me/MelodicMoonlight) |
|
||||
|:-:|:-:|:-:|:-:|
|
||||
|
||||
## Thanks
|
||||
|
||||
|
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
"name": "NapCatQQ",
|
||||
"slug": "NapCat.Framework",
|
||||
"description": "高性能的 OneBot 11 协议实现",
|
||||
"version": "4.5.50",
|
||||
"version": "4.7.33",
|
||||
"icon": "./logo.png",
|
||||
"authors": [
|
||||
{
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "4.5.50",
|
||||
"version": "4.7.33",
|
||||
"scripts": {
|
||||
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
||||
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
||||
|
@@ -115,7 +115,7 @@ async function tryDownload(options: string | HttpDownloadOptions, useReferer: bo
|
||||
if (useReferer && !headers['Referer']) {
|
||||
headers['Referer'] = url;
|
||||
}
|
||||
const fetchRes = await fetch(url, { headers, redirect: 'follow' }).catch((err) => {
|
||||
const fetchRes = await fetch(url, { headers }).catch((err) => {
|
||||
if (err.cause) {
|
||||
throw err.cause;
|
||||
}
|
||||
@@ -145,8 +145,8 @@ export enum FileUriType {
|
||||
|
||||
export async function checkUriType(Uri: string) {
|
||||
const LocalFileRet = await solveProblem((uri: string) => {
|
||||
if (fs.existsSync(path.normalize(uri))) {
|
||||
return { Uri: path.normalize(uri), Type: FileUriType.Local };
|
||||
if (fs.existsSync(uri)) {
|
||||
return { Uri: uri, Type: FileUriType.Local };
|
||||
}
|
||||
return undefined;
|
||||
}, Uri);
|
||||
|
@@ -1 +1 @@
|
||||
export const napCatVersion = '4.5.50';
|
||||
export const napCatVersion = '4.7.33';
|
||||
|
@@ -28,8 +28,6 @@ import { SendMessageContext } from '@/onebot/api';
|
||||
import { getFileTypeForSendType } from '../helper/msg';
|
||||
import { FFmpegService } from '@/common/ffmpeg';
|
||||
import { rkeyDataType } from '../types/file';
|
||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { FileId } from '../packet/transformer/proto/misc/fileid';
|
||||
|
||||
export class NTQQFileApi {
|
||||
context: InstanceContext;
|
||||
@@ -65,76 +63,6 @@ export class NTQQFileApi {
|
||||
}
|
||||
}
|
||||
|
||||
async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined) {
|
||||
if (this.core.apis.PacketApi.available) {
|
||||
try {
|
||||
if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetGroupFileUrl(+peer, fileUUID);
|
||||
} else if (file10MMd5 && fileUUID) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetPrivateFileUrl(peer, fileUUID, file10MMd5);
|
||||
}
|
||||
} catch (error) {
|
||||
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||
}
|
||||
}
|
||||
throw new Error('fileUUID or file10MMd5 is undefined');
|
||||
}
|
||||
|
||||
async getPttUrl(peer: string, fileUUID?: string) {
|
||||
if (this.core.apis.PacketApi.available && fileUUID) {
|
||||
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
|
||||
try {
|
||||
if (appid && appid === 1403) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+peer, {
|
||||
fileUuid: fileUUID,
|
||||
storeId: 1,
|
||||
uploadTime: 0,
|
||||
ttl: 0,
|
||||
subType: 0,
|
||||
});
|
||||
} else if (fileUUID) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetPttUrl(peer, {
|
||||
fileUuid: fileUUID,
|
||||
storeId: 1,
|
||||
uploadTime: 0,
|
||||
ttl: 0,
|
||||
subType: 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||
}
|
||||
}
|
||||
throw new Error('packet cant get ptt url');
|
||||
}
|
||||
|
||||
async getVideoUrlPacket(peer: string, fileUUID?: string) {
|
||||
if (this.core.apis.PacketApi.available && fileUUID) {
|
||||
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
|
||||
try {
|
||||
if (appid && appid === 1415) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetGroupVideoUrl(+peer, {
|
||||
fileUuid: fileUUID,
|
||||
storeId: 1,
|
||||
uploadTime: 0,
|
||||
ttl: 0,
|
||||
subType: 0,
|
||||
});
|
||||
} else if (fileUUID) {
|
||||
return this.core.apis.PacketApi.pkt.operation.GetVideoUrl(peer, {
|
||||
fileUuid: fileUUID,
|
||||
storeId: 1,
|
||||
uploadTime: 0,
|
||||
ttl: 0,
|
||||
subType: 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||
}
|
||||
}
|
||||
throw new Error('packet cant get video url');
|
||||
}
|
||||
|
||||
async copyFile(filePath: string, destPath: string) {
|
||||
await this.core.util.copyFile(filePath, destPath);
|
||||
@@ -417,7 +345,6 @@ export class NTQQFileApi {
|
||||
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||
[{
|
||||
fileModelId: '0',
|
||||
downSourceType: 0,
|
||||
downloadSourceType: 0,
|
||||
triggerType: 1,
|
||||
msgId: msgId,
|
||||
|
@@ -86,31 +86,4 @@ export class NTQQFriendApi {
|
||||
accept,
|
||||
});
|
||||
}
|
||||
async handleDoubtFriendRequest(friendUid: string, str1: string = '', str2: string = '') {
|
||||
this.context.session.getBuddyService().approvalDoubtBuddyReq(friendUid, str1, str2);
|
||||
}
|
||||
async getDoubtFriendRequest(count: number) {
|
||||
let date = Date.now().toString();
|
||||
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
|
||||
'NodeIKernelBuddyService/getDoubtBuddyReq',
|
||||
'NodeIKernelBuddyListener/onDoubtBuddyReqChange',
|
||||
[date, count, ''],
|
||||
() => true,
|
||||
(data) => data.reqId === date
|
||||
);
|
||||
let requests = Promise.all(ret.doubtList.map(async (item) => {
|
||||
return {
|
||||
flag: item.uid, //注意强制String 非isNumeric 不遵守则不符合设计
|
||||
uin: await this.core.apis.UserApi.getUinByUidV2(item.uid) ?? 0,// 信息字段
|
||||
nick: item.nick, // 信息字段 这个不是nickname 可能是来源的群内的昵称
|
||||
source: item.source, // 信息字段
|
||||
reason: item.reason, // 信息字段
|
||||
msg: item.msg, // 信息字段
|
||||
group_code: item.groupCode, // 信息字段
|
||||
time: item.reqTime, // 信息字段
|
||||
type: 'doubt' //保留字段
|
||||
};
|
||||
}))
|
||||
return requests;
|
||||
}
|
||||
}
|
||||
|
@@ -71,7 +71,6 @@ export class NTQQMsgApi {
|
||||
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||
chatInfo: peer,
|
||||
//searchFields: 3,
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: '0',
|
||||
@@ -85,7 +84,6 @@ export class NTQQMsgApi {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||
chatInfo: peer,
|
||||
filterMsgType: [],
|
||||
//searchFields: 3,
|
||||
filterSendersUid: SendersUid,
|
||||
filterMsgToTime: MsgTime,
|
||||
filterMsgFromTime: MsgTime,
|
||||
@@ -102,7 +100,6 @@ export class NTQQMsgApi {
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: false,
|
||||
//searchFields: 3,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 1,
|
||||
});
|
||||
@@ -113,7 +110,6 @@ export class NTQQMsgApi {
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: '0',
|
||||
//searchFields: 3,
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: true,
|
||||
isIncludeCurrent: true,
|
||||
@@ -132,7 +128,6 @@ export class NTQQMsgApi {
|
||||
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
//searchFields: 3,
|
||||
filterMsgToTime: filterMsgToTime,
|
||||
filterMsgFromTime: filterMsgFromTime,
|
||||
isReverseOrder: false,
|
||||
@@ -147,7 +142,6 @@ export class NTQQMsgApi {
|
||||
chatInfo: peer,
|
||||
filterMsgType: [],
|
||||
filterSendersUid: SendersUid,
|
||||
//searchFields: 3,
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: true,
|
||||
|
24
src/core/external/appid.json
vendored
24
src/core/external/appid.json
vendored
@@ -258,29 +258,5 @@
|
||||
"3.2.17-34467": {
|
||||
"appid": 537282292,
|
||||
"qua": "V1_LNX_NQ_3.2.17_34467_GW_B"
|
||||
},
|
||||
"9.9.19-34566": {
|
||||
"appid": 537282307,
|
||||
"qua": "V1_WIN_NQ_9.9.19_34566_GW_B"
|
||||
},
|
||||
"3.2.17-34566": {
|
||||
"appid": 537282343,
|
||||
"qua": "V1_LNX_NQ_3.2.17_34566_GW_B"
|
||||
},
|
||||
"3.2.17-34606": {
|
||||
"appid": 537282343,
|
||||
"qua": "V1_LNX_NQ_3.2.17_34606_GW_B"
|
||||
},
|
||||
"9.9.19-34606": {
|
||||
"appid": 537282307,
|
||||
"qua": "V1_WIN_NQ_9.9.19_34606_GW_B"
|
||||
},
|
||||
"9.9.19-34740": {
|
||||
"appid": 537290691,
|
||||
"qua": "V1_WIN_NQ_9.9.19_34740_GW_B"
|
||||
},
|
||||
"3.2.17-34740": {
|
||||
"appid": 537290727,
|
||||
"qua": "V1_LNX_NQ_3.2.17_34740_GW_B"
|
||||
}
|
||||
}
|
22
src/core/external/offset.json
vendored
22
src/core/external/offset.json
vendored
@@ -327,32 +327,12 @@
|
||||
"send": "770CDC0",
|
||||
"recv": "77106F0"
|
||||
},
|
||||
"9.9.19-34362-x64": {
|
||||
"9.9.19-34362-x64":{
|
||||
"send": "3BD80D0",
|
||||
"recv": "3BDC8D0"
|
||||
},
|
||||
"9.9.19-34467-x64": {
|
||||
"send": "3BD8690",
|
||||
"recv": "3BDCE90"
|
||||
},
|
||||
"9.9.19-34566-x64": {
|
||||
"send": "3BDA110",
|
||||
"recv": "3BDE910"
|
||||
},
|
||||
"9.9.19-34606-x64": {
|
||||
"send": "3BDA110",
|
||||
"recv": "3BDE910"
|
||||
},
|
||||
"3.2.17-34606-x64": {
|
||||
"send": "AD7DC60",
|
||||
"recv": "AD81680"
|
||||
},
|
||||
"3.2.17-34606-arm64": {
|
||||
"send": "7711270",
|
||||
"recv": "7714BA0"
|
||||
},
|
||||
"9.9.19-34740-x64": {
|
||||
"send": "3BDD8D0",
|
||||
"recv": "3BE20D0"
|
||||
}
|
||||
}
|
@@ -40,30 +40,12 @@ export class NodeIKernelBuddyListener {
|
||||
}
|
||||
|
||||
onDelBatchBuddyInfos(arg: unknown): any {
|
||||
console.log('onDelBatchBuddyInfos not implemented', ...arguments);
|
||||
}
|
||||
|
||||
onDoubtBuddyReqChange(_arg:
|
||||
{
|
||||
reqId: string;
|
||||
cookie: string;
|
||||
doubtList: Array<{
|
||||
uid: string;
|
||||
nick: string;
|
||||
age: number,
|
||||
sex: number;
|
||||
commFriendNum: number;
|
||||
reqTime: string;
|
||||
msg: string;
|
||||
source: string;
|
||||
reason: string;
|
||||
groupCode: string;
|
||||
nameMore?: null;
|
||||
}>;
|
||||
}): void | Promise<void> {
|
||||
onDoubtBuddyReqChange(arg: unknown): any {
|
||||
}
|
||||
|
||||
onDoubtBuddyReqUnreadNumChange(_num: number): void | Promise<void> {
|
||||
onDoubtBuddyReqUnreadNumChange(arg: unknown): any {
|
||||
}
|
||||
|
||||
onNickUpdated(arg: unknown): any {
|
||||
|
@@ -21,8 +21,7 @@ export interface OnRichMediaDownloadCompleteParams {
|
||||
clientMsg: string,
|
||||
businessId: number,
|
||||
userTotalSpacePerDay: unknown,
|
||||
userUsedSpacePerDay: unknown,
|
||||
chatType: number,
|
||||
userUsedSpacePerDay: unknown
|
||||
}
|
||||
|
||||
export interface GroupFileInfoUpdateParamType {
|
||||
@@ -98,112 +97,112 @@ export interface TempOnRecvParams {
|
||||
}
|
||||
|
||||
export class NodeIKernelMsgListener {
|
||||
onAddSendMsg(_msgRecord: RawMessage): any {
|
||||
onAddSendMsg(msgRecord: RawMessage): any {
|
||||
|
||||
}
|
||||
|
||||
onBroadcastHelperDownloadComplete(_broadcastHelperTransNotifyInfo: unknown): any {
|
||||
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onBroadcastHelperProgressUpdate(_broadcastHelperTransNotifyInfo: unknown): any {
|
||||
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onChannelFreqLimitInfoUpdate(_contact: unknown, _z: unknown, _freqLimitInfo: unknown): any {
|
||||
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onContactUnreadCntUpdate(_hashMap: unknown): any {
|
||||
onContactUnreadCntUpdate(hashMap: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onCustomWithdrawConfigUpdate(_customWithdrawConfig: unknown): any {
|
||||
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onDraftUpdate(_contact: unknown, _arrayList: unknown, _j2: unknown): any {
|
||||
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onEmojiDownloadComplete(_emojiNotifyInfo: unknown): any {
|
||||
onEmojiDownloadComplete(emojiNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onEmojiResourceUpdate(_emojiResourceInfo: unknown): any {
|
||||
onEmojiResourceUpdate(emojiResourceInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onFeedEventUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onFileMsgCome(_arrayList: unknown): any {
|
||||
onFileMsgCome(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onFirstViewDirectMsgUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onFirstViewGroupGuildMapping(_arrayList: unknown): any {
|
||||
onFirstViewGroupGuildMapping(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGrabPasswordRedBag(_i2: unknown, _str: unknown, _i3: unknown, _recvdOrder: unknown, _msgRecord: unknown): any {
|
||||
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupFileInfoAdd(_groupItem: unknown): any {
|
||||
onGroupFileInfoAdd(groupItem: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupFileInfoUpdate(_groupFileListResult: GroupFileInfoUpdateParamType): any {
|
||||
onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupGuildUpdate(_groupGuildNotifyInfo: unknown): any {
|
||||
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
|
||||
onGroupTransferInfoAdd(_groupItem: unknown): any {
|
||||
onGroupTransferInfoAdd(groupItem: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupTransferInfoUpdate(_groupFileListResult: unknown): any {
|
||||
onGroupTransferInfoUpdate(groupFileListResult: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGuildInteractiveUpdate(_guildInteractiveNotificationItem: unknown): any {
|
||||
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGuildMsgAbFlagChanged(_guildMsgAbFlag: unknown): any {
|
||||
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onGuildNotificationAbstractUpdate(_guildNotificationAbstractInfo: unknown): any {
|
||||
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onHitCsRelatedEmojiResult(_downloadRelateEmojiResultInfo: unknown): any {
|
||||
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onHitEmojiKeywordResult(_hitRelatedEmojiWordsResult: unknown): any {
|
||||
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onHitRelatedEmojiResult(_relatedWordEmojiInfo: unknown): any {
|
||||
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onImportOldDbProgressUpdate(_importOldDbMsgNotifyInfo: unknown): any {
|
||||
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onInputStatusPush(_inputStatusInfo: {
|
||||
onInputStatusPush(inputStatusInfo: {
|
||||
chatType: number;
|
||||
eventType: number;
|
||||
fromUin: string;
|
||||
@@ -216,55 +215,55 @@ export class NodeIKernelMsgListener {
|
||||
|
||||
}
|
||||
|
||||
onKickedOffLine(_kickedInfo: KickedOffLineInfo): any {
|
||||
onKickedOffLine(kickedInfo: KickedOffLineInfo): any {
|
||||
|
||||
}
|
||||
|
||||
onLineDev(_arrayList: unknown): any {
|
||||
onLineDev(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onLogLevelChanged(_j2: unknown): any {
|
||||
onLogLevelChanged(j2: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgAbstractUpdate(_arrayList: unknown): any {
|
||||
onMsgAbstractUpdate(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgBoxChanged(_arrayList: unknown): any {
|
||||
onMsgBoxChanged(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgDelete(_contact: unknown, _arrayList: unknown): any {
|
||||
onMsgDelete(contact: unknown, arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgEventListUpdate(_hashMap: unknown): any {
|
||||
onMsgEventListUpdate(hashMap: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgInfoListAdd(_arrayList: unknown): any {
|
||||
onMsgInfoListAdd(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgInfoListUpdate(_msgList: RawMessage[]): any {
|
||||
onMsgInfoListUpdate(msgList: RawMessage[]): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgQRCodeStatusChanged(_i2: unknown): any {
|
||||
onMsgQRCodeStatusChanged(i2: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgRecall(_chatType: ChatType, _uid: string, _msgSeq: string): any {
|
||||
onMsgRecall(chatType: ChatType, uid: string, msgSeq: string): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgSecurityNotify(_msgRecord: unknown): any {
|
||||
onMsgSecurityNotify(msgRecord: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgSettingUpdate(_msgSetting: unknown): any {
|
||||
onMsgSettingUpdate(msgSetting: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
@@ -280,108 +279,108 @@ export class NodeIKernelMsgListener {
|
||||
|
||||
}
|
||||
|
||||
onReadFeedEventUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvGroupGuildFlag(_i2: unknown): any {
|
||||
onRecvGroupGuildFlag(i2: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvMsg(_arrayList: RawMessage[]): any {
|
||||
onRecvMsg(arrayList: RawMessage[]): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvMsgSvrRspTransInfo(_j2: unknown, _contact: unknown, _i2: unknown, _i3: unknown, _str: unknown, _bArr: unknown): any {
|
||||
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvOnlineFileMsg(_arrayList: unknown): any {
|
||||
onRecvOnlineFileMsg(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvS2CMsg(_arrayList: unknown): any {
|
||||
onRecvS2CMsg(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvSysMsg(_arrayList: Array<number>): any {
|
||||
onRecvSysMsg(arrayList: Array<number>): any {
|
||||
|
||||
}
|
||||
|
||||
onRecvUDCFlag(_i2: unknown): any {
|
||||
onRecvUDCFlag(i2: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRichMediaDownloadComplete(_fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): any {
|
||||
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): any {
|
||||
}
|
||||
|
||||
onRichMediaProgerssUpdate(_fileTransNotifyInfo: unknown): any {
|
||||
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onRichMediaUploadComplete(_fileTransNotifyInfo: unknown): any {
|
||||
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onSearchGroupFileInfoUpdate(_searchGroupFileResult: unknown): any {
|
||||
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onSendMsgError(_j2: unknown, _contact: unknown, _i2: unknown, _str: unknown): any {
|
||||
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onSysMsgNotification(_i2: unknown, _j2: unknown, _j3: unknown, _arrayList: unknown): any {
|
||||
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onTempChatInfoUpdate(_tempChatInfo: TempOnRecvParams): any {
|
||||
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): any {
|
||||
|
||||
}
|
||||
|
||||
onUnreadCntAfterFirstView(_hashMap: unknown): any {
|
||||
onUnreadCntAfterFirstView(hashMap: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onUnreadCntUpdate(_hashMap: unknown): any {
|
||||
onUnreadCntUpdate(hashMap: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onUserChannelTabStatusChanged(_z: unknown): any {
|
||||
onUserChannelTabStatusChanged(z: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onUserOnlineStatusChanged(_z: unknown): any {
|
||||
onUserOnlineStatusChanged(z: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onUserTabStatusChanged(_arrayList: unknown): any {
|
||||
onUserTabStatusChanged(arrayList: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onlineStatusBigIconDownloadPush(_i2: unknown, _j2: unknown, _str: unknown): any {
|
||||
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
onlineStatusSmallIconDownloadPush(_i2: unknown, _j2: unknown, _str: unknown): any {
|
||||
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
|
||||
|
||||
}
|
||||
|
||||
// 第一次发现于Linux
|
||||
onUserSecQualityChanged(..._args: unknown[]): any {
|
||||
onUserSecQualityChanged(...args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onMsgWithRichLinkInfoUpdate(..._args: unknown[]): any {
|
||||
onMsgWithRichLinkInfoUpdate(...args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onRedTouchChanged(..._args: unknown[]): any {
|
||||
onRedTouchChanged(...args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
// 第一次发现于Win 9.9.9-23159
|
||||
onBroadcastHelperProgerssUpdate(..._args: unknown[]): any {
|
||||
onBroadcastHelperProgerssUpdate(...args: unknown[]): any {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -6,14 +6,13 @@ import {
|
||||
PacketMsgFileElement,
|
||||
PacketMsgPicElement,
|
||||
PacketMsgPttElement,
|
||||
PacketMsgReplyElement,
|
||||
PacketMsgVideoElement,
|
||||
PacketMsgVideoElement
|
||||
} from '@/core/packet/message/element';
|
||||
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
||||
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
||||
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
||||
import { NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { IndexNode, LongMsgResult, MsgInfo, PushMsgBody } from '@/core/packet/transformer/proto';
|
||||
import { IndexNode, LongMsgResult, MsgInfo } from '@/core/packet/transformer/proto';
|
||||
import { OidbPacket } from '@/core/packet/transformer/base';
|
||||
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
||||
import { gunzipSync } from 'zlib';
|
||||
@@ -77,24 +76,22 @@ export class PacketOperationContext {
|
||||
async UploadResources(msg: PacketMsg[], groupUin: number = 0) {
|
||||
const chatType = groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C;
|
||||
const peerUid = groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid;
|
||||
const reqList = msg.flatMap((m) =>
|
||||
m.msg
|
||||
.map((e) => {
|
||||
if (e instanceof PacketMsgPicElement) {
|
||||
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgVideoElement) {
|
||||
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgPttElement) {
|
||||
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgFileElement) {
|
||||
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
const reqList = msg.flatMap(m =>
|
||||
m.msg.map(e => {
|
||||
if (e instanceof PacketMsgPicElement) {
|
||||
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgVideoElement) {
|
||||
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgPttElement) {
|
||||
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
||||
} else if (e instanceof PacketMsgFileElement) {
|
||||
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean)
|
||||
);
|
||||
const res = await Promise.allSettled(reqList);
|
||||
this.context.logger.info(`上传资源${res.length}个,失败${res.filter((r) => r.status === 'rejected').length}个`);
|
||||
this.context.logger.info(`上传资源${res.length}个,失败${res.filter(r => r.status === 'rejected').length}个`);
|
||||
res.forEach((result, index) => {
|
||||
if (result.status === 'rejected') {
|
||||
this.context.logger.error(`上传第${index + 1}个资源失败:${result.reason.stack}`);
|
||||
@@ -103,13 +100,10 @@ export class PacketOperationContext {
|
||||
}
|
||||
|
||||
async UploadImage(img: PacketMsgPicElement) {
|
||||
await this.context.highway.uploadImage(
|
||||
{
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: this.context.napcore.basicInfo.uid,
|
||||
},
|
||||
img
|
||||
);
|
||||
await this.context.highway.uploadImage({
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: this.context.napcore.basicInfo.uid
|
||||
}, img);
|
||||
const index = img.msgInfo?.msgInfoBody?.at(0)?.index;
|
||||
if (!index) {
|
||||
throw new Error('img.msgInfo?.msgInfoBody![0].index! is undefined');
|
||||
@@ -124,20 +118,6 @@ export class PacketOperationContext {
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetPttUrl(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadPtt.build(selfUid, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadPtt.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetVideoUrl(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadVideo.build(selfUid, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadVideo.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadGroupImage.build(groupUin, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
@@ -145,21 +125,6 @@ export class PacketOperationContext {
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadImage.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetGroupVideoUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadGroupVideo.build(groupUin, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadImage.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
|
||||
async ImageOCR(imgUrl: string) {
|
||||
const req = trans.ImageOCR.build(imgUrl);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
@@ -172,66 +137,24 @@ export class PacketOperationContext {
|
||||
coordinates: item.polygon.coordinates.map((c) => {
|
||||
return {
|
||||
x: c.x,
|
||||
y: c.y,
|
||||
y: c.y
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
language: res.ocrRspBody.language,
|
||||
language: res.ocrRspBody.language
|
||||
} as ImageOcrResult;
|
||||
}
|
||||
|
||||
private async SendPreprocess(msg: PacketMsg[], groupUin: number = 0) {
|
||||
const ps = msg.map((m) => {
|
||||
return m.msg.map(async (e) => {
|
||||
if (e instanceof PacketMsgReplyElement && !e.targetElems) {
|
||||
this.context.logger.debug(`Cannot find reply element's targetElems, prepare to fetch it...`);
|
||||
if (!e.targetPeer?.peerUid) {
|
||||
this.context.logger.error(`targetPeer is undefined!`);
|
||||
}
|
||||
let targetMsg: NapProtoEncodeStructType<typeof PushMsgBody>[] | undefined;
|
||||
if (e.isGroupReply) {
|
||||
targetMsg = await this.FetchGroupMessage(+(e.targetPeer?.peerUid ?? 0), e.targetMessageSeq, e.targetMessageSeq);
|
||||
} else {
|
||||
targetMsg = await this.FetchC2CMessage(await this.context.napcore.basicInfo.uin2uid(e.targetUin), e.targetMessageSeq, e.targetMessageSeq);
|
||||
}
|
||||
e.targetElems = targetMsg.at(0)?.body?.richText?.elems;
|
||||
e.targetSourceMsg = targetMsg.at(0);
|
||||
}
|
||||
});
|
||||
}).flat();
|
||||
await Promise.all(ps)
|
||||
await this.UploadResources(msg, groupUin);
|
||||
}
|
||||
|
||||
async FetchGroupMessage(groupUin: number, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
|
||||
const req = trans.FetchGroupMessage.build(groupUin, startSeq, endSeq);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.FetchGroupMessage.parse(resp);
|
||||
return res.body.messages
|
||||
}
|
||||
|
||||
async FetchC2CMessage(targetUid: string, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
|
||||
const req = trans.FetchC2CMessage.build(targetUid, startSeq, endSeq);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.FetchC2CMessage.parse(resp);
|
||||
return res.messages
|
||||
}
|
||||
|
||||
async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {
|
||||
await this.SendPreprocess(msg, groupUin);
|
||||
await this.UploadResources(msg, groupUin);
|
||||
const req = trans.UploadForwardMsg.build(this.context.napcore.basicInfo.uid, msg, groupUin);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.UploadForwardMsg.parse(resp);
|
||||
return res.result.resId;
|
||||
}
|
||||
|
||||
async MoveGroupFile(
|
||||
groupUin: number,
|
||||
fileUUID: string,
|
||||
currentParentDirectory: string,
|
||||
targetParentDirectory: string
|
||||
) {
|
||||
async MoveGroupFile(groupUin: number, fileUUID: string, currentParentDirectory: string, targetParentDirectory: string) {
|
||||
const req = trans.MoveGroupFile.build(groupUin, fileUUID, currentParentDirectory, targetParentDirectory);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.MoveGroupFile.parse(resp);
|
||||
@@ -251,7 +174,6 @@ export class PacketOperationContext {
|
||||
const res = trans.DownloadGroupFile.parse(resp);
|
||||
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
|
||||
}
|
||||
|
||||
async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) {
|
||||
const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
@@ -259,6 +181,13 @@ export class PacketOperationContext {
|
||||
return `http://${res.body?.result?.server}:${res.body?.result?.port}${res.body?.result?.url?.slice(8)}&isthumb=0`;
|
||||
}
|
||||
|
||||
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadGroupPtt.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetMiniAppAdaptShareInfo(param: MiniAppReqParams) {
|
||||
const req = trans.GetMiniAppAdaptShareInfo.build(param);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
@@ -274,17 +203,12 @@ export class PacketOperationContext {
|
||||
return res.content.map((item) => {
|
||||
return {
|
||||
category: item.category,
|
||||
voices: item.voices,
|
||||
voices: item.voices
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async GetAiVoice(
|
||||
groupUin: number,
|
||||
voiceId: string,
|
||||
text: string,
|
||||
chatType: AIVoiceChatType
|
||||
): Promise<NapProtoDecodeStructType<typeof MsgInfo>> {
|
||||
async GetAiVoice(groupUin: number, voiceId: string, text: string, chatType: AIVoiceChatType): Promise<NapProtoDecodeStructType<typeof MsgInfo>> {
|
||||
let reqTime = 0;
|
||||
const reqMaxTime = 30;
|
||||
const sessionId = crypto.randomBytes(4).readUInt32BE(0);
|
||||
@@ -312,7 +236,6 @@ export class PacketOperationContext {
|
||||
if (!main?.actionData.msgBody) {
|
||||
throw new Error('msgBody is empty');
|
||||
}
|
||||
this.context.logger.debug('rawChains ', inflate.toString('hex'));
|
||||
|
||||
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
|
||||
if (!msg?.body?.richText?.elems) {
|
||||
@@ -328,12 +251,12 @@ export class PacketOperationContext {
|
||||
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
|
||||
element.picElement = {
|
||||
...element.picElement,
|
||||
originImageUrl: await this.GetGroupImageUrl(groupUin, index!),
|
||||
originImageUrl: await this.GetGroupImageUrl(groupUin, index!)
|
||||
};
|
||||
} else {
|
||||
element.picElement = {
|
||||
...element.picElement,
|
||||
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!),
|
||||
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!)
|
||||
};
|
||||
}
|
||||
return element;
|
||||
@@ -346,7 +269,7 @@ export class PacketOperationContext {
|
||||
elements: elements,
|
||||
guildId: '',
|
||||
isOnlineMsg: false,
|
||||
msgId: '7467703692092974645', // TODO: no necessary
|
||||
msgId: '7467703692092974645', // TODO: no necessary
|
||||
msgRandom: '0',
|
||||
msgSeq: String(msg.contentHead.sequence ?? 0),
|
||||
msgTime: String(msg.contentHead.timeStamp ?? 0),
|
||||
|
@@ -24,15 +24,12 @@ export class PacketMsgBuilder {
|
||||
}
|
||||
return {
|
||||
responseHead: {
|
||||
fromUin: node.senderUin,
|
||||
type: 0,
|
||||
sigMap: 0,
|
||||
toUin: 0,
|
||||
fromUid: '',
|
||||
fromUin: node.senderUin,
|
||||
toUid: node.groupId ? undefined : selfUid,
|
||||
forward: node.groupId ? undefined : {
|
||||
friendName: node.senderName,
|
||||
},
|
||||
toUid: node.groupId ? undefined : selfUid,
|
||||
grp: node.groupId ? {
|
||||
groupUin: node.groupId,
|
||||
memberName: node.senderName,
|
||||
@@ -43,13 +40,16 @@ export class PacketMsgBuilder {
|
||||
type: node.groupId ? 82 : 9,
|
||||
subType: node.groupId ? undefined : 4,
|
||||
divSeq: node.groupId ? undefined : 4,
|
||||
autoReply: 0,
|
||||
msgId: crypto.randomBytes(4).readUInt32LE(0),
|
||||
sequence: crypto.randomBytes(4).readUInt32LE(0),
|
||||
timeStamp: +node.time.toString().substring(0, 10),
|
||||
field7: BigInt(1),
|
||||
field8: 0,
|
||||
field9: 0,
|
||||
forward: {
|
||||
field1: 0,
|
||||
field2: 0,
|
||||
field3: node.groupId ? 1 : 2,
|
||||
field3: node.groupId ? 0 : 2,
|
||||
unknownBase64: avatar,
|
||||
avatar: avatar
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ import {
|
||||
MsgInfo,
|
||||
NotOnlineImage,
|
||||
OidbSvcTrpcTcp0XE37_800Response,
|
||||
PushMsgBody,
|
||||
QBigFaceExtra,
|
||||
QSmallFaceExtra,
|
||||
} from '@/core/packet/transformer/proto';
|
||||
@@ -30,8 +29,7 @@ import {
|
||||
SendReplyElement,
|
||||
SendMultiForwardMsgElement,
|
||||
SendTextElement,
|
||||
SendVideoElement,
|
||||
Peer
|
||||
SendVideoElement
|
||||
} from '@/core';
|
||||
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||
@@ -148,40 +146,41 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||
}
|
||||
|
||||
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||
time: number;
|
||||
targetMessageId: bigint;
|
||||
targetMessageSeq: number;
|
||||
targetMessageClientSeq: number;
|
||||
messageId: bigint;
|
||||
messageSeq: number;
|
||||
messageClientSeq: number;
|
||||
targetUin: number;
|
||||
targetUid: string;
|
||||
targetElems?: NapProtoEncodeStructType<typeof Elem>[];
|
||||
targetSourceMsg?: NapProtoEncodeStructType<typeof PushMsgBody>;
|
||||
targetPeer?: Peer;
|
||||
time: number;
|
||||
elems: PacketMsg[];
|
||||
|
||||
constructor(element: SendReplyElement) {
|
||||
super(element);
|
||||
this.time = +(element.replyElement.replyMsgTime ?? Math.floor(Date.now() / 1000));
|
||||
this.targetMessageId = BigInt(element.replyElement.replayMsgId ?? 0);
|
||||
this.targetMessageSeq = +(element.replyElement.replayMsgSeq ?? 0);
|
||||
this.targetMessageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
|
||||
this.messageId = BigInt(element.replyElement.replayMsgId ?? 0);
|
||||
this.messageSeq = +(element.replyElement.replayMsgSeq ?? 0);
|
||||
this.messageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
|
||||
this.targetUin = +(element.replyElement.senderUin ?? 0);
|
||||
this.targetUid = element.replyElement.senderUidStr ?? '';
|
||||
this.targetPeer = element.replyElement._replyMsgPeer;
|
||||
this.time = +(element.replyElement.replyMsgTime ?? 0);
|
||||
this.elems = []; // TODO: in replyElement.sourceMsgTextElems
|
||||
}
|
||||
|
||||
get isGroupReply(): boolean {
|
||||
return this.targetMessageClientSeq === 0;
|
||||
return this.messageClientSeq === 0;
|
||||
}
|
||||
|
||||
override buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||
return [{
|
||||
srcMsg: {
|
||||
origSeqs: [this.isGroupReply ? this.targetMessageSeq : this.targetMessageClientSeq],
|
||||
origSeqs: [this.isGroupReply ? this.messageClientSeq : this.messageSeq],
|
||||
senderUin: BigInt(this.targetUin),
|
||||
time: this.time,
|
||||
elems: this.targetElems ?? [],
|
||||
sourceMsg: new NapProtoMsg(PushMsgBody).encode(this.targetSourceMsg ?? {}),
|
||||
toUin: BigInt(0),
|
||||
elems: [], // TODO: in replyElement.sourceMsgTextElems
|
||||
pbReserve: {
|
||||
messageId: this.messageId,
|
||||
},
|
||||
toUin: BigInt(this.targetUin),
|
||||
type: 1,
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@@ -1,50 +0,0 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||
|
||||
class DownloadGroupVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 200
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 2,
|
||||
sceneType: 2,
|
||||
group: {
|
||||
groupUin: groupUin
|
||||
}
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
}
|
||||
},
|
||||
download: {
|
||||
node: node,
|
||||
download: {
|
||||
video: {
|
||||
busiType: 0,
|
||||
sceneType: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return OidbBase.build(0x11EA, 200, body, true, false);
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
const oidbBody = OidbBase.parse(data).body;
|
||||
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DownloadGroupVideo();
|
@@ -1,51 +0,0 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||
|
||||
class DownloadPtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 200
|
||||
},
|
||||
scene: {
|
||||
requestType: 1,
|
||||
businessType: 3,
|
||||
sceneType: 1,
|
||||
c2C: {
|
||||
accountType: 2,
|
||||
targetUid: selfUid
|
||||
},
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
}
|
||||
},
|
||||
download: {
|
||||
node: node,
|
||||
download: {
|
||||
video: {
|
||||
busiType: 0,
|
||||
sceneType: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return OidbBase.build(0x126D, 200, body, true, false);
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
const oidbBody = OidbBase.parse(data).body;
|
||||
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DownloadPtt();
|
@@ -1,51 +0,0 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||
|
||||
class DownloadVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 200
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 2,
|
||||
sceneType: 1,
|
||||
c2C: {
|
||||
accountType: 2,
|
||||
targetUid: selfUid
|
||||
},
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
}
|
||||
},
|
||||
download: {
|
||||
node: node,
|
||||
download: {
|
||||
video: {
|
||||
busiType: 0,
|
||||
sceneType: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return OidbBase.build(0x11E9, 200, body, true, false);
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
const oidbBody = OidbBase.parse(data).body;
|
||||
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DownloadVideo();
|
@@ -13,6 +13,3 @@ export { default as UploadPrivatePtt } from './UploadPrivatePtt';
|
||||
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
||||
export { default as DownloadImage } from './DownloadImage';
|
||||
export { default as DownloadGroupImage } from './DownloadGroupImage';
|
||||
export { default as DownloadVideo } from './DownloadVideo';
|
||||
export { default as DownloadGroupVideo } from './DownloadGroupVideo';
|
||||
export { default as DownloadPtt } from './DownloadPtt';
|
@@ -1,27 +0,0 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
|
||||
class FetchC2CMessage extends PacketTransformer<typeof proto.SsoGetC2cMsgResponse> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(targetUid: string, startSeq: number, endSeq: number): OidbPacket {
|
||||
const req = new NapProtoMsg(proto.SsoGetC2cMsg).encode({
|
||||
friendUid: targetUid,
|
||||
startSequence: startSeq,
|
||||
endSequence: endSeq,
|
||||
});
|
||||
return {
|
||||
cmd: 'trpc.msg.register_proxy.RegisterProxy.SsoGetC2cMsg',
|
||||
data: PacketHexStrBuilder(req)
|
||||
};
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
return new NapProtoMsg(proto.SsoGetC2cMsgResponse).decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
export default new FetchC2CMessage();
|
@@ -1,30 +0,0 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
|
||||
class FetchGroupMessage extends PacketTransformer<typeof proto.SsoGetGroupMsgResponse> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(groupUin: number, startSeq: number, endSeq: number): OidbPacket {
|
||||
const req = new NapProtoMsg(proto.SsoGetGroupMsg).encode({
|
||||
info: {
|
||||
groupUin: groupUin,
|
||||
startSequence: startSeq,
|
||||
endSequence: endSeq
|
||||
},
|
||||
direction: true
|
||||
});
|
||||
return {
|
||||
cmd: 'trpc.msg.register_proxy.RegisterProxy.SsoGetGroupMsg',
|
||||
data: PacketHexStrBuilder(req)
|
||||
};
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
return new NapProtoMsg(proto.SsoGetGroupMsgResponse).decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
export default new FetchGroupMessage();
|
@@ -1,4 +1,2 @@
|
||||
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
||||
export { default as FetchGroupMessage } from './FetchGroupMessage';
|
||||
export { default as FetchC2CMessage } from './FetchC2CMessage';
|
||||
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
||||
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
@@ -13,15 +13,13 @@ import {
|
||||
export const ContentHead = {
|
||||
type: ProtoField(1, ScalarType.UINT32),
|
||||
subType: ProtoField(2, ScalarType.UINT32, true),
|
||||
c2cCmd: ProtoField(3, ScalarType.UINT32, true),
|
||||
ranDom: ProtoField(4, ScalarType.UINT32, true),
|
||||
divSeq: ProtoField(3, ScalarType.UINT32, true),
|
||||
msgId: ProtoField(4, ScalarType.UINT32, true),
|
||||
sequence: ProtoField(5, ScalarType.UINT32, true),
|
||||
timeStamp: ProtoField(6, ScalarType.UINT32, true),
|
||||
pkgNum: ProtoField(7, ScalarType.UINT64, true),
|
||||
pkgIndex: ProtoField(8, ScalarType.UINT32, true),
|
||||
divSeq: ProtoField(9, ScalarType.UINT32, true),
|
||||
autoReply: ProtoField(10, ScalarType.UINT32),
|
||||
ntMsgSeq: ProtoField(10, ScalarType.UINT32, true),
|
||||
field7: ProtoField(7, ScalarType.UINT64, true),
|
||||
field8: ProtoField(8, ScalarType.UINT32, true),
|
||||
field9: ProtoField(9, ScalarType.UINT32, true),
|
||||
newId: ProtoField(12, ScalarType.UINT64, true),
|
||||
forward: ProtoField(15, () => ForwardHead, true),
|
||||
};
|
||||
|
@@ -1,6 +0,0 @@
|
||||
import { ProtoField, ScalarType } from '@napneko/nap-proto-core';
|
||||
|
||||
export const FileId = {
|
||||
appid: ProtoField(4, ScalarType.UINT32, true),
|
||||
ttl: ProtoField(10, ScalarType.UINT32, true),
|
||||
};
|
@@ -106,15 +106,15 @@ export interface NodeIKernelBuddyService {
|
||||
|
||||
getAddMeSetting(): unknown;
|
||||
|
||||
getDoubtBuddyReq(reqId: string, num: number,uk:string): Promise<GeneralCallResult>;
|
||||
getDoubtBuddyReq(): unknown;
|
||||
|
||||
getDoubtBuddyUnreadNum(): number;
|
||||
|
||||
approvalDoubtBuddyReq(uid: string, str1: string, str2: string): void;
|
||||
approvalDoubtBuddyReq(uid: number, isAgree: boolean): void;
|
||||
|
||||
delDoubtBuddyReq(uid: number): void;
|
||||
|
||||
delAllDoubtBuddyReq(): Promise<GeneralCallResult>;
|
||||
delAllDoubtBuddyReq(): void;
|
||||
|
||||
reportDoubtBuddyReqUnread(): void;
|
||||
|
||||
|
@@ -148,11 +148,10 @@ export interface NodeIKernelMsgService {
|
||||
msgList: RawMessage[]
|
||||
}>;
|
||||
|
||||
// getMsgService/getMsgs { chatType: 2, peerUid: '975206796', privilegeFlag: 336068800 } 0 20 true
|
||||
getMsgs(peer: Peer & { privilegeFlag: number }, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[]
|
||||
}>;
|
||||
//@deprecated
|
||||
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
||||
|
||||
//@deprecated
|
||||
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[]
|
||||
}>;
|
||||
@@ -426,20 +425,7 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
switchToOfflineGetRichMediaElement(...args: unknown[]): unknown;
|
||||
|
||||
downloadRichMedia(args: {
|
||||
fileModelId: string,
|
||||
downSourceType: number,
|
||||
triggerType: number,
|
||||
msgId: string,
|
||||
chatType: number,
|
||||
peerUid: string,
|
||||
elementId: string,
|
||||
thumbSize: number,
|
||||
downloadType: number,
|
||||
filePath: string
|
||||
} & {
|
||||
downloadSourceType: number, //33800左右一下的老版本 新版34606已经完全上面格式
|
||||
}): unknown;
|
||||
downloadRichMedia(...args: unknown[]): unknown;
|
||||
|
||||
getFirstUnreadMsgSeq(args: {
|
||||
peerUid: string
|
||||
|
@@ -1,15 +1,4 @@
|
||||
import {
|
||||
ElementType,
|
||||
MessageElement,
|
||||
NTGrayTipElementSubTypeV2,
|
||||
PicSubType,
|
||||
PicType,
|
||||
TipAioOpGrayTipElement,
|
||||
TipGroupElement,
|
||||
NTVideoType,
|
||||
FaceType,
|
||||
Peer
|
||||
} from './msg';
|
||||
import { ElementType, MessageElement, NTGrayTipElementSubTypeV2, PicSubType, PicType, TipAioOpGrayTipElement, TipGroupElement, NTVideoType, FaceType } from './msg';
|
||||
|
||||
type ElementFullBase = Omit<MessageElement, 'elementType' | 'elementId' | 'extBufForUI'>;
|
||||
|
||||
@@ -224,9 +213,6 @@ export interface ReplyElement {
|
||||
senderUidStr?: string;
|
||||
replyMsgTime?: string;
|
||||
replyMsgClientSeq?: string;
|
||||
// HACK: Attributes that were not originally available,
|
||||
// but were added due to NTQQ and NapCat's internal implementation, are used to supplement NapCat
|
||||
_replyMsgPeer?: Peer;
|
||||
}
|
||||
|
||||
export interface CalendarElement {
|
||||
|
@@ -403,7 +403,7 @@ export interface NTGroupGrayMember {
|
||||
}
|
||||
/**
|
||||
* 群灰色提示邀请者和被邀请者接口
|
||||
*
|
||||
*
|
||||
* */
|
||||
export interface NTGroupGrayInviterAndInvite {
|
||||
invited: NTGroupGrayMember;
|
||||
@@ -501,15 +501,13 @@ export interface RawMessage {
|
||||
elements: MessageElement[];// 消息元素
|
||||
sourceType: MsgSourceType;// 消息来源类型
|
||||
isOnlineMsg: boolean;// 是否为在线消息
|
||||
clientSeq?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询消息参数接口
|
||||
*/
|
||||
export interface QueryMsgsParams {
|
||||
chatInfo: Peer & { privilegeFlag?: number };
|
||||
//searchFields: number;
|
||||
chatInfo: Peer;
|
||||
filterMsgType: Array<{ type: NTMsgType, subType: Array<number> }>;
|
||||
filterSendersUid: string[];
|
||||
filterMsgFromTime: string;
|
||||
@@ -567,4 +565,4 @@ export enum FaceType {
|
||||
AniSticke = 3, // 动画贴纸
|
||||
Lottie = 4,// 新格式表情
|
||||
Poke = 5 // 可变Poke
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
|
||||
const SchemaData = Type.Object({
|
||||
model: Type.Optional(Type.String()),
|
||||
model: Type.String(),
|
||||
});
|
||||
|
||||
type Payload = Static<typeof SchemaData>;
|
||||
|
@@ -38,7 +38,6 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction<Payload, null>
|
||||
deleteAfterSentFiles: []
|
||||
};
|
||||
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
|
||||
msgContext.deleteAfterSentFiles.push(downloadResult.path);
|
||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], msgContext.deleteAfterSentFiles);
|
||||
return null;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
|
||||
if (payload.user_id) {
|
||||
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!peerUid) {
|
||||
throw new Error(`私聊${payload.user_id}不存在`);
|
||||
throw new Error( `私聊${payload.user_id}不存在`);
|
||||
}
|
||||
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
|
||||
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
|
||||
@@ -48,7 +48,6 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
|
||||
deleteAfterSentFiles: []
|
||||
};
|
||||
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
|
||||
msgContext.deleteAfterSentFiles.push(downloadResult.path);
|
||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], msgContext.deleteAfterSentFiles);
|
||||
return null;
|
||||
}
|
||||
|
@@ -115,16 +115,10 @@ import { RenameGroupFile } from './extends/RenameGroupFile';
|
||||
import { GetRkeyServer } from './packet/GetRkeyServer';
|
||||
import { GetRkeyEx } from './packet/GetRkeyEx';
|
||||
import { CleanCache } from './system/CleanCache';
|
||||
import SetFriendRemark from './user/SetFriendRemark';
|
||||
import { SetDoubtFriendsAddRequest } from './new/SetDoubtFriendsAddRequest';
|
||||
import { GetDoubtFriendsAddRequest } from './new/GetDoubtFriendsAddRequest';
|
||||
|
||||
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
|
||||
|
||||
const actionHandlers = [
|
||||
new SetDoubtFriendsAddRequest(obContext, core),
|
||||
new GetDoubtFriendsAddRequest(obContext, core),
|
||||
new SetFriendRemark(obContext, core),
|
||||
new GetRkeyEx(obContext, core),
|
||||
new GetRkeyServer(obContext, core),
|
||||
new SetGroupRemark(obContext, core),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
OB11MessageData,
|
||||
OB11MessageDataType,
|
||||
OB11MessageForward,
|
||||
OB11MessageMixType,
|
||||
OB11MessageNode,
|
||||
OB11PostContext,
|
||||
@@ -12,7 +13,7 @@ import { MessageUnique } from '@/common/message-unique';
|
||||
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, SendMessageElement } from '@/core';
|
||||
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||
import { ForwardMsgBuilder } from '@/common/forward-msg-builder';
|
||||
import { stringifyWithBigInt } from '@/common/helper';
|
||||
import { isNumeric, stringifyWithBigInt } from '@/common/helper';
|
||||
import { PacketMsg } from '@/core/packet/message/message';
|
||||
import { rawMsgWithSendMsg } from '@/core/packet/message/converter';
|
||||
|
||||
@@ -152,21 +153,25 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
||||
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
|
||||
throw Error(`发送转发消息(res_id:${returnMsgAndResId.res_id} 失败`);
|
||||
}
|
||||
} else {
|
||||
// if (getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||
// const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
|
||||
// if (music) {
|
||||
// }
|
||||
// }
|
||||
} else if (await this.getIsFowardOneMsg(messages)) {
|
||||
let onebot_inner_forward = await this.getIsFowardOneMsg(messages);
|
||||
if (!onebot_inner_forward) throw Error('转发消息失败,未找到消息');
|
||||
const real_msgid = MessageUnique.getMsgIdAndPeerByShortId(+onebot_inner_forward.data.id)?.MsgId || onebot_inner_forward.data.id;
|
||||
await this.core.apis.MsgApi.forwardMsg(peer,
|
||||
peer,
|
||||
[real_msgid]
|
||||
);
|
||||
// 暂时没办法筛选的委屈办法
|
||||
return { message_id: +onebot_inner_forward.data.id };
|
||||
}
|
||||
// log("send msg:", peer, sendElements)
|
||||
|
||||
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! };
|
||||
}
|
||||
|
||||
private async getIsFowardOneMsg(message: OB11MessageData[]): Promise<OB11MessageForward | undefined> {
|
||||
return message.find(msg => msg.type === OB11MessageDataType.forward && isNumeric(msg.data.id)) as OB11MessageForward | undefined;
|
||||
}
|
||||
private async uploadForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: {
|
||||
text: string
|
||||
}[], summary?: string, prompt?: string, parentMeta?: {
|
||||
|
@@ -1,18 +0,0 @@
|
||||
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
|
||||
const SchemaData = Type.Object({
|
||||
count: Type.Number({ default: 50 }),
|
||||
});
|
||||
|
||||
type Payload = Static<typeof SchemaData>;
|
||||
|
||||
export class GetDoubtFriendsAddRequest extends OneBotAction<Payload, unknown> {
|
||||
override actionName = ActionName.GetDoubtFriendsAddRequest;
|
||||
override payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
return await this.core.apis.FriendApi.getDoubtFriendRequest(payload.count);
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
|
||||
const SchemaData = Type.Object({
|
||||
flag: Type.String(),
|
||||
//注意强制String 非isNumeric 不遵守则不符合设计
|
||||
approve: Type.Boolean({ default: true }),
|
||||
//该字段没有语义 仅做保留 强制为True
|
||||
});
|
||||
|
||||
type Payload = Static<typeof SchemaData>;
|
||||
|
||||
export class SetDoubtFriendsAddRequest extends OneBotAction<Payload, unknown> {
|
||||
override actionName = ActionName.SetDoubtFriendsAddRequest;
|
||||
override payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
return await this.core.apis.FriendApi.handleDoubtFriendRequest(payload.flag);
|
||||
}
|
||||
}
|
@@ -10,10 +10,6 @@ export interface InvalidCheckResult {
|
||||
}
|
||||
|
||||
export const ActionName = {
|
||||
// new extends 完全差异OneBot类别
|
||||
GetDoubtFriendsAddRequest: 'get_doubt_friends_add_request',
|
||||
SetDoubtFriendsAddRequest: 'set_doubt_friends_add_request',
|
||||
// napcat
|
||||
GetRkeyEx: 'get_rkey',
|
||||
GetRkeyServer: 'get_rkey_server',
|
||||
SetGroupRemark: 'set_group_remark',
|
||||
@@ -39,7 +35,6 @@ export const ActionName = {
|
||||
SetGroupLeave: 'set_group_leave',
|
||||
SetSpecialTitle: 'set_group_special_title',
|
||||
SetFriendAddRequest: 'set_friend_add_request',
|
||||
SetFriendRemark: 'set_friend_remark',
|
||||
SetGroupAddRequest: 'set_group_add_request',
|
||||
GetLoginInfo: 'get_login_info',
|
||||
GoCQHTTP_GetStrangerInfo: 'get_stranger_info',
|
||||
|
@@ -1,25 +0,0 @@
|
||||
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||
import { ActionName } from '@/onebot/action/router';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
|
||||
const SchemaData = Type.Object({
|
||||
user_id: Type.String(),
|
||||
remark: Type.String()
|
||||
});
|
||||
|
||||
type Payload = Static<typeof SchemaData>;
|
||||
|
||||
export default class SetFriendRemark extends OneBotAction<Payload, null> {
|
||||
override actionName = ActionName.SetFriendRemark;
|
||||
override payloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload): Promise<null> {
|
||||
let friendUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id);
|
||||
let is_friend = await this.core.apis.FriendApi.isBuddy(friendUid);
|
||||
if (!is_friend) {
|
||||
throw new Error(`用户 ${payload.user_id} 不是好友`);
|
||||
}
|
||||
await this.core.apis.FriendApi.setBuddyRemark(friendUid, payload.remark);
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -250,34 +250,7 @@ export class OneBotGroupApi {
|
||||
'invite'
|
||||
);
|
||||
}
|
||||
async parse51TypeEvent(msg: RawMessage, grayTipElement: GrayTipElement) {
|
||||
// 神经腾讯 没了妈妈想出来的
|
||||
// Warn 下面存在高并发危险
|
||||
if (grayTipElement.jsonGrayTipElement.jsonStr) {
|
||||
const json: {
|
||||
align: string,
|
||||
items: Array<{ txt: string, type: string }>
|
||||
} = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
||||
if (json.items.length === 1 && json.items[0]?.txt.endsWith('加入群')) {
|
||||
let old_members = structuredClone(this.core.apis.GroupApi.groupMemberCache.get(msg.peerUid));
|
||||
if (!old_members) return;
|
||||
let new_members_map = await this.core.apis.GroupApi.refreshGroupMemberCache(msg.peerUid, true);
|
||||
if (!new_members_map) return;
|
||||
let new_members = Array.from(new_members_map.values());
|
||||
// 对比members查找新成员
|
||||
let new_member = new_members.find((member) => old_members.get(member.uid) == undefined);
|
||||
if (!new_member) return;
|
||||
return new OB11GroupIncreaseEvent(
|
||||
this.core,
|
||||
+msg.peerUid,
|
||||
+new_member.uin,
|
||||
0,
|
||||
'invite',
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async parseGrayTipElement(msg: RawMessage, grayTipElement: GrayTipElement) {
|
||||
if (grayTipElement.subElementType === NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_GROUP) {
|
||||
// 解析群组事件 由sysmsg解析
|
||||
@@ -309,9 +282,6 @@ export class OneBotGroupApi {
|
||||
return await this.parsePaiYiPai(msg, grayTipElement.jsonGrayTipElement.jsonStr);
|
||||
} else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) {
|
||||
return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr);
|
||||
} else if (+(grayTipElement.jsonGrayTipElement.busiId ?? 0) == 51) {
|
||||
// 51是什么?{"align":"center","items":[{"txt":"下一秒起床通过王者荣耀加入群","type":"nor"}]
|
||||
return await this.parse51TypeEvent(msg, grayTipElement);
|
||||
} else {
|
||||
return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context);
|
||||
}
|
||||
|
@@ -100,7 +100,7 @@ export class OneBotMsgApi {
|
||||
let qq: string = 'all';
|
||||
if (element.atType !== NTMsgAtType.ATTYPEALL) {
|
||||
const { atNtUid, atUid } = element;
|
||||
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : String(Number(atUid) >>> 0);
|
||||
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : atUid;
|
||||
}
|
||||
return {
|
||||
type: OB11MessageDataType.at,
|
||||
@@ -150,31 +150,12 @@ export class OneBotMsgApi {
|
||||
};
|
||||
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileUuid);
|
||||
FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName);
|
||||
if (this.core.apis.PacketApi.available) {
|
||||
let url;
|
||||
try {
|
||||
url = await this.core.apis.FileApi.getFileUrl(msg.chatType, msg.peerUid, element.fileUuid, element.file10MMd5)
|
||||
} catch (error) {
|
||||
url = '';
|
||||
}
|
||||
if (url) {
|
||||
return {
|
||||
type: OB11MessageDataType.file,
|
||||
data: {
|
||||
file: element.fileName,
|
||||
file_id: element.fileUuid,
|
||||
file_size: element.fileSize,
|
||||
url: url,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: OB11MessageDataType.file,
|
||||
data: {
|
||||
file: element.fileName,
|
||||
file_id: element.fileUuid,
|
||||
file_size: element.fileSize
|
||||
file_size: element.fileSize,
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -244,13 +225,17 @@ export class OneBotMsgApi {
|
||||
},
|
||||
|
||||
replyElement: async (element, msg) => {
|
||||
const records = msg.records.find(msgRecord => msgRecord.msgId === element?.sourceMsgIdInRecords);
|
||||
const peer = {
|
||||
chatType: msg.chatType,
|
||||
peerUid: msg.peerUid,
|
||||
guildId: '',
|
||||
};
|
||||
if (!records || !element.replyMsgTime || !element.senderUidStr) {
|
||||
this.core.context.logger.logError('似乎是旧版客户端,获取不到引用的消息', element.replayMsgSeq);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 创建回复数据的通用方法
|
||||
const createReplyData = (msgId: string): OB11MessageData => ({
|
||||
type: OB11MessageDataType.reply,
|
||||
data: {
|
||||
@@ -258,96 +243,48 @@ export class OneBotMsgApi {
|
||||
},
|
||||
});
|
||||
|
||||
// 查找记录
|
||||
const records = msg.records.find(msgRecord => msgRecord.msgId === element?.sourceMsgIdInRecords);
|
||||
|
||||
// 特定账号的特殊处理
|
||||
if (records && (records.peerUin === '284840486' || records.peerUin === '1094950020')) {
|
||||
if (records.peerUin === '284840486' || records.peerUin === '1094950020') {
|
||||
return createReplyData(records.msgId);
|
||||
}
|
||||
let replyMsgList = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV2(peer, element.replayMsgSeq, records.msgTime, [element.senderUidStr])).msgList;
|
||||
let replyMsg = replyMsgList.find(msg => msg.msgRandom === records.msgRandom);
|
||||
|
||||
// 获取消息的通用方法组
|
||||
const tryFetchMethods = async (msgSeq: string, senderUid?: string, msgTime?: string, msgRandom?: string): Promise<RawMessage | undefined> => {
|
||||
try {
|
||||
// 方法1:通过序号和时间筛选
|
||||
if (senderUid && msgTime) {
|
||||
const replyMsgList = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV2(
|
||||
peer, msgSeq, msgTime, [senderUid]
|
||||
)).msgList;
|
||||
|
||||
const replyMsg = msgRandom
|
||||
? replyMsgList.find(msg => msg.msgRandom === msgRandom)
|
||||
: replyMsgList.find(msg => msg.msgSeq === msgSeq);
|
||||
|
||||
if (replyMsg) return replyMsg;
|
||||
|
||||
this.core.context.logger.logWarn(`方法1查询失败,序号: ${msgSeq}, 消息数: ${replyMsgList.length}`);
|
||||
}
|
||||
|
||||
// 方法2:直接通过序号获取
|
||||
const replyMsgList = (await this.core.apis.MsgApi.getMsgsBySeqAndCount(
|
||||
peer, msgSeq, 1, true, true
|
||||
)).msgList;
|
||||
|
||||
const replyMsg = msgRandom
|
||||
? replyMsgList.find(msg => msg.msgRandom === msgRandom)
|
||||
: replyMsgList.find(msg => msg.msgSeq === msgSeq);
|
||||
|
||||
if (replyMsg) return replyMsg;
|
||||
|
||||
this.core.context.logger.logWarn(`方法2查询失败,序号: ${msgSeq}, 消息数: ${replyMsgList.length}`);
|
||||
|
||||
// 方法3:另一种筛选方式
|
||||
if (senderUid) {
|
||||
const replyMsgList = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV3(
|
||||
peer, msgSeq, [senderUid]
|
||||
)).msgList;
|
||||
|
||||
const replyMsg = msgRandom
|
||||
? replyMsgList.find(msg => msg.msgRandom === msgRandom)
|
||||
: replyMsgList.find(msg => msg.msgSeq === msgSeq);
|
||||
|
||||
if (replyMsg) return replyMsg;
|
||||
|
||||
this.core.context.logger.logWarn(`方法3查询失败,序号: ${msgSeq}, 消息数: ${replyMsgList.length}`);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
this.core.context.logger.logError('查询回复消息出错', error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// 有记录情况下,使用完整信息查询
|
||||
if (records && element.replyMsgTime && element.senderUidStr) {
|
||||
const replyMsg = await tryFetchMethods(
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
this.core.context.logger.logError(
|
||||
'筛选结果,筛选消息失败,将使用Fallback-1 Seq: ',
|
||||
element.replayMsgSeq,
|
||||
element.senderUidStr,
|
||||
records.msgTime,
|
||||
records.msgRandom
|
||||
',消息长度:',
|
||||
replyMsgList.length
|
||||
);
|
||||
|
||||
if (replyMsg) {
|
||||
return createReplyData(replyMsg.msgId);
|
||||
}
|
||||
|
||||
this.core.context.logger.logError('所有查找方法均失败,获取不到带记录的引用消息', element.replayMsgSeq);
|
||||
} else {
|
||||
// 旧版客户端或不完整记录的情况,也尝试使用相同流程
|
||||
this.core.context.logger.logWarn('似乎是旧版客户端,尝试仅通过序号获取引用消息', element.replayMsgSeq);
|
||||
|
||||
const replyMsg = await tryFetchMethods(element.replayMsgSeq);
|
||||
|
||||
if (replyMsg) {
|
||||
return createReplyData(replyMsg.msgId);
|
||||
}
|
||||
|
||||
this.core.context.logger.logError('所有查找方法均失败,获取不到旧客户端的引用消息', element.replayMsgSeq);
|
||||
replyMsgList = (await this.core.apis.MsgApi.getMsgsBySeqAndCount(peer, element.replayMsgSeq, 1, true, true)).msgList;
|
||||
replyMsg = replyMsgList.find(msg => msg.msgRandom === records.msgRandom);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
this.core.context.logger.logWarn(
|
||||
'筛选消息失败,将使用Fallback-2 Seq:',
|
||||
element.replayMsgSeq,
|
||||
',消息长度:',
|
||||
replyMsgList.length
|
||||
);
|
||||
replyMsgList = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV3(peer, element.replayMsgSeq, [element.senderUidStr])).msgList;
|
||||
replyMsg = replyMsgList.find(msg => msg.msgRandom === records.msgRandom);
|
||||
}
|
||||
|
||||
|
||||
// 丢弃该消息段
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
this.core.context.logger.logError(
|
||||
'最终筛选结果,筛选消息失败,获取不到引用的消息 Seq: ',
|
||||
element.replayMsgSeq,
|
||||
',消息长度:',
|
||||
replyMsgList.length
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return createReplyData(replyMsg.msgId);
|
||||
},
|
||||
|
||||
videoElement: async (element, msg, elementWrapper) => {
|
||||
const peer = {
|
||||
chatType: msg.chatType,
|
||||
@@ -394,17 +331,7 @@ export class OneBotMsgApi {
|
||||
|
||||
//开始兜底
|
||||
if (!videoDownUrl) {
|
||||
if (this.core.apis.PacketApi.available) {
|
||||
try {
|
||||
videoDownUrl = await this.core.apis.FileApi.getVideoUrlPacket(msg.peerUid, element.fileUuid);
|
||||
} catch (e) {
|
||||
this.core.context.logger.logError('获取视频url失败', (e as Error).stack);
|
||||
videoDownUrl = element.filePath;
|
||||
}
|
||||
} else {
|
||||
videoDownUrl = element.filePath;
|
||||
}
|
||||
|
||||
videoDownUrl = element.filePath;
|
||||
}
|
||||
const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName);
|
||||
return {
|
||||
@@ -424,28 +351,6 @@ export class OneBotMsgApi {
|
||||
guildId: '',
|
||||
};
|
||||
const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, '', element.fileName);
|
||||
let pttUrl = '';
|
||||
if (this.core.apis.PacketApi.available) {
|
||||
try {
|
||||
pttUrl = await this.core.apis.FileApi.getPttUrl(msg.peerUid, element.fileUuid);
|
||||
} catch (e) {
|
||||
this.core.context.logger.logError('获取语音url失败', (e as Error).stack);
|
||||
pttUrl = element.filePath;
|
||||
}
|
||||
} else {
|
||||
pttUrl = element.filePath;
|
||||
}
|
||||
if (pttUrl) {
|
||||
return {
|
||||
type: OB11MessageDataType.voice,
|
||||
data: {
|
||||
file: fileCode,
|
||||
path: element.filePath,
|
||||
url: pttUrl,
|
||||
file_size: element.fileSize,
|
||||
},
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: OB11MessageDataType.voice,
|
||||
data: {
|
||||
@@ -562,8 +467,6 @@ export class OneBotMsgApi {
|
||||
replayMsgId: replyMsg.msgId, // raw.msgId
|
||||
senderUin: replyMsg.senderUin,
|
||||
senderUinStr: replyMsg.senderUin,
|
||||
replyMsgClientSeq: replyMsg.clientSeq,
|
||||
_replyMsgPeer: replyMsgM.Peer
|
||||
},
|
||||
} :
|
||||
undefined;
|
||||
@@ -1004,10 +907,10 @@ export class OneBotMsgApi {
|
||||
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
|
||||
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
|
||||
resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话';
|
||||
resMsg.temp_source = 0;
|
||||
resMsg.temp_source = resMsg.group_id;
|
||||
} else {
|
||||
resMsg.group_id = 284840486;
|
||||
resMsg.temp_source = 0;
|
||||
resMsg.temp_source = resMsg.group_id;
|
||||
resMsg.sender.nickname = '临时会话';
|
||||
}
|
||||
}
|
||||
|
@@ -39,11 +39,8 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
||||
wsClient.close();
|
||||
return;
|
||||
}
|
||||
// 鉴权 close 不会立刻销毁 当前返回可避免挂载message事件 close 并未立刻关闭 而是存在timer操作后关闭
|
||||
// 引发高危漏洞
|
||||
if (!this.authorize(this.config.token, wsClient, wsReq)) {
|
||||
return;
|
||||
}
|
||||
//鉴权
|
||||
this.authorize(this.config.token, wsClient, wsReq);
|
||||
const paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url;
|
||||
const isApiConnect = paramUrl === '/api' || paramUrl === '/api/';
|
||||
if (!isApiConnect) {
|
||||
@@ -148,16 +145,15 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter<WebsocketSer
|
||||
}
|
||||
|
||||
private authorize(token: string | undefined, wsClient: WebSocket, wsReq: IncomingMessage) {
|
||||
if (!token || token.length == 0) return true;//客户端未设置密钥
|
||||
if (!token || token.length == 0) return;//客户端未设置密钥
|
||||
const QueryClientToken = urlParse.parse(wsReq?.url || '', true).query['access_token'];
|
||||
const HeaderClientToken = wsReq.headers.authorization?.split('Bearer ').pop() || '';
|
||||
const ClientToken = typeof (QueryClientToken) === 'string' && QueryClientToken !== '' ? QueryClientToken : HeaderClientToken;
|
||||
if (ClientToken === token) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
wsClient.send(JSON.stringify(OB11Response.res(null, 'failed', 1403, 'token验证失败')));
|
||||
wsClient.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
private checkStateAndReply<T>(data: T, wsClient: WebSocket) {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { LogWrapper } from '@/common/log';
|
||||
import * as net from 'net';
|
||||
import * as process from 'process';
|
||||
import { Writable } from 'stream';
|
||||
|
||||
/**
|
||||
* 连接到命名管道并重定向stdout
|
||||
@@ -26,50 +25,12 @@ export function connectToNamedPipe(logger: LogWrapper, timeoutMs: number = 5000)
|
||||
}, timeoutMs);
|
||||
|
||||
try {
|
||||
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
||||
let originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
||||
const pipeSocket = net.connect(pipePath, () => {
|
||||
// 清除超时
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// 优化网络性能设置
|
||||
pipeSocket.setNoDelay(true); // 减少延迟
|
||||
|
||||
// 设置更高的高水位线,允许更多数据缓冲
|
||||
|
||||
logger.log(`[StdOut] 已重定向到命名管道: ${pipePath}`);
|
||||
|
||||
// 创建拥有更优雅背压处理的 Writable 流
|
||||
const pipeWritable = new Writable({
|
||||
highWaterMark: 1024 * 64, // 64KB 高水位线
|
||||
write(chunk, encoding, callback) {
|
||||
if (!pipeSocket.writable) {
|
||||
// 如果管道不可写,退回到原始stdout
|
||||
logger.log('[StdOut] 管道不可写,回退到控制台输出');
|
||||
return originalStdoutWrite(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
// 尝试写入数据到管道
|
||||
const canContinue = pipeSocket.write(chunk, encoding, () => {
|
||||
// 数据已被发送或放入内部缓冲区
|
||||
});
|
||||
|
||||
if (canContinue) {
|
||||
// 如果返回true,表示可以继续写入更多数据
|
||||
// 立即通知写入流可以继续
|
||||
process.nextTick(callback);
|
||||
} else {
|
||||
// 如果返回false,表示内部缓冲区已满
|
||||
// 等待drain事件再恢复写入
|
||||
pipeSocket.once('drain', () => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
// 明确返回true,表示写入已处理
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 重定向stdout
|
||||
process.stdout.write = (
|
||||
chunk: any,
|
||||
encoding?: BufferEncoding | (() => void),
|
||||
@@ -79,11 +40,8 @@ export function connectToNamedPipe(logger: LogWrapper, timeoutMs: number = 5000)
|
||||
cb = encoding;
|
||||
encoding = undefined;
|
||||
}
|
||||
|
||||
// 使用优化的writable流处理写入
|
||||
return pipeWritable.write(chunk, encoding as BufferEncoding, cb as () => void);
|
||||
return pipeSocket.write(chunk, encoding as BufferEncoding, cb);
|
||||
};
|
||||
|
||||
// 提供断开连接的方法
|
||||
const disconnect = () => {
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
@@ -95,7 +53,6 @@ export function connectToNamedPipe(logger: LogWrapper, timeoutMs: number = 5000)
|
||||
resolve({ disconnect });
|
||||
});
|
||||
|
||||
// 管道错误处理
|
||||
pipeSocket.on('error', (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
@@ -103,18 +60,11 @@ export function connectToNamedPipe(logger: LogWrapper, timeoutMs: number = 5000)
|
||||
reject(err);
|
||||
});
|
||||
|
||||
// 管道关闭处理
|
||||
pipeSocket.on('end', () => {
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
logger.log('命名管道连接已关闭');
|
||||
});
|
||||
|
||||
// 确保在连接意外关闭时恢复stdout
|
||||
pipeSocket.on('close', () => {
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
logger.log('命名管道连接已关闭');
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
logger.log(`尝试连接命名管道 ${pipePath} 时发生异常:`, error);
|
||||
|
Reference in New Issue
Block a user