Compare commits

...

21 Commits

Author SHA1 Message Date
手瓜一十雪
b1ecf13f8e release: 2.2.22 2024-08-29 00:10:52 +08:00
手瓜一十雪
e91e054f20 refactor: GetGroupEssence 2024-08-29 00:10:29 +08:00
手瓜一十雪
130ff7517e refactor: parseEssence 2024-08-29 00:02:24 +08:00
手瓜一十雪
c7042d9684 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-29 00:01:48 +08:00
手瓜一十雪
5752e45dd1 refactor: GetFile 2024-08-28 23:45:33 +08:00
Alen
1a034ecb53 Merge pull request #317 from cnxysoft/upmain
fix: 转发消息报错
2024-08-28 23:30:04 +08:00
手瓜一十雪
025da8fb76 refactor: getFile 2024-08-28 23:27:29 +08:00
Alen
2027da1db5 chore: 优化代码 2024-08-28 23:24:44 +08:00
Alen
7732f28ca8 fix: reply消息转换 2024-08-28 22:51:54 +08:00
手瓜一十雪
7f9da8cc2d feat: support folder_id 2024-08-28 21:19:17 +08:00
手瓜一十雪
c6342b80a7 release: 2.2.21 2024-08-28 20:51:59 +08:00
Wesley F. Young
f99c82de4b fix: unexpected return in buddy req parsing 2024-08-28 19:32:07 +08:00
Wesley F. Young
56fa57ea02 refactor: rename createMsg -> createUniqueMsgId to prevent ambiguity 2024-08-28 19:03:19 +08:00
手瓜一十雪
cc85985d08 feat: 设置noify已读 2024-08-28 18:18:40 +08:00
手瓜一十雪
bd1751903e chore: clearGroupNotifiesUnreadCount 2024-08-28 18:01:32 +08:00
手瓜一十雪
03a298a70f release: 2.2.20 2024-08-28 17:48:51 +08:00
手瓜一十雪
2722ca2b0e feat: getGroupInfoEx 2024-08-28 17:05:00 +08:00
手瓜一十雪
179c4b800e Merge pull request #314 from NapNeko/dependabot/npm_and_yarn/typescript-eslint/parser-8.3.0
build(deps-dev): bump @typescript-eslint/parser from 7.18.0 to 8.3.0
2024-08-28 17:04:37 +08:00
dependabot[bot]
6bdf14223d build(deps-dev): bump @typescript-eslint/parser from 7.18.0 to 8.3.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 7.18.0 to 8.3.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.3.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-28 08:58:48 +00:00
dependabot[bot]
1b8252aa4f Merge pull request #312 from NapNeko/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.3.0 2024-08-28 08:27:34 +00:00
dependabot[bot]
8219889154 build(deps-dev): bump @typescript-eslint/eslint-plugin
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 7.18.0 to 8.3.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.3.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-28 08:26:00 +00:00
28 changed files with 397 additions and 183 deletions

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "2.2.19", "version": "2.2.22",
"scripts": { "scripts": {
"build:framework": "vite build --mode framework", "build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell", "build:shell": "vite build --mode shell",
@@ -27,8 +27,8 @@
"@types/node": "^22.0.1", "@types/node": "^22.0.1",
"@types/qrcode-terminal": "^0.12.2", "@types/qrcode-terminal": "^0.12.2",
"@types/ws": "^8.5.12", "@types/ws": "^8.5.12",
"@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/eslint-plugin": "^8.3.0",
"@typescript-eslint/parser": "^7.4.0", "@typescript-eslint/parser": "^8.3.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",

View File

@@ -1,7 +1,7 @@
import path from 'node:path'; import path from 'node:path';
import fs from 'fs'; import fs from 'fs';
import os from 'node:os'; import os from 'node:os';
import { QQLevel } from '@/core'; import { Peer, QQLevel } from '@/core';
export async function solveProblem<T extends (...arg: any[]) => any>(func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> { export async function solveProblem<T extends (...arg: any[]) => any>(func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
return new Promise<ReturnType<T> | undefined>((resolve) => { return new Promise<ReturnType<T> | undefined>((resolve) => {
@@ -23,29 +23,48 @@ export async function solveAsyncProblem<T extends (...args: any[]) => Promise<an
}); });
}); });
} }
export class FileNapCatOneBotUUID {
//下面这个类是用于将uid+msgid合并的类 static encodeModelId(peer: Peer, modelId: string): string {
export class UUIDConverter { return `NapCatOneBot-ModeldFile-${peer.chatType}-${peer.peerUid}-${modelId}`;
static encode(highStr: string, lowStr: string): string {
const high = BigInt(highStr);
const low = BigInt(lowStr);
const highHex = high.toString(16).padStart(16, '0');
const lowHex = low.toString(16).padStart(16, '0');
const combinedHex = highHex + lowHex;
return `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
12,
16,
)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
} }
static decodeModelId(uuid: string): undefined | {
static decode(uuid: string): { high: string; low: string } { peer: Peer,
const hex = uuid.replace(/-/g, ''); modelId: string
const high = BigInt('0x' + hex.substring(0, 16)); } {
const low = BigInt('0x' + hex.substring(16)); if (!uuid.startsWith('NapCatOneBot-ModeldFile-')) return undefined;
return { high: high.toString(), low: low.toString() }; const data = uuid.split('-');
if (data.length !== 5) return undefined;
const [, , chatType, peerUid, modelId] = data;
return {
peer: {
chatType: chatType as any,
peerUid: peerUid
},
modelId,
};
}
static encode(peer: Peer, msgId: string, elementId: string): string {
return `NapCatOneBot-MsgFile-${peer.chatType}-${peer.peerUid}-${msgId}-${elementId}`;
}
static decode(uuid: string): undefined | {
peer: Peer,
msgId: string,
elementId: string
} {
if (!uuid.startsWith('NapCatOneBot-File-')) return undefined;
const data = uuid.split('-');
if (data.length !== 6) return undefined;
const [, , chatType, peerUid, msgId, elementId] = data;
return {
peer: {
chatType: chatType as any,
peerUid: peerUid
},
msgId,
elementId,
};
} }
} }
export function sleep(ms: number): Promise<void> { export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }

View File

@@ -91,7 +91,7 @@ class MessageUniqueWrapper {
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined); return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
} }
createMsg(peer: Peer, msgId: string) { createUniqueMsgId(peer: Peer, msgId: string) {
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`; const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
const hash = crypto.createHash('md5').update(key).digest(); const hash = crypto.createHash('md5').update(key).digest();
//设置第一个bit为0 保证shortId为正数 //设置第一个bit为0 保证shortId为正数

View File

@@ -1 +1 @@
export const napCatVersion = '2.2.19'; export const napCatVersion = '2.2.22';

View File

@@ -302,7 +302,18 @@ export class NTQQFileApi {
async downloadMediaByUuid() { async downloadMediaByUuid() {
//napCatCore.session.getRichMediaService().downloadFileForFileUuid(); //napCatCore.session.getRichMediaService().downloadFileForFileUuid();
} }
async downloadFileForModelId(peer: Peer, modelId: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[peer, [modelId]],
() => true,
(arg) => arg?.commonFileInfo?.fileModelId === modelId,
1,
timeout,
);
return fileTransNotifyInfo.filePath;
}
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) { async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force); //logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
// 用于下载收到的消息中的图片等 // 用于下载收到的消息中的图片等

View File

@@ -2,6 +2,7 @@ import {
ChatType, ChatType,
GeneralCallResult, GeneralCallResult,
Group, Group,
GroupInfoSource,
GroupMember, GroupMember,
GroupMemberRole, GroupMemberRole,
GroupRequestOperateTypes, GroupRequestOperateTypes,
@@ -11,6 +12,7 @@ import {
NapCatCore, NapCatCore,
} from '@/core'; } from '@/core';
import { isNumeric, runAllWithTimeout } from '@/common/helper'; import { isNumeric, runAllWithTimeout } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique';
export class NTQQGroupApi { export class NTQQGroupApi {
context: InstanceContext; context: InstanceContext;
@@ -18,6 +20,7 @@ export class NTQQGroupApi {
groupCache: Map<string, Group> = new Map<string, Group>(); groupCache: Map<string, Group> = new Map<string, Group>();
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>(); groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = []; groups: Group[] = [];
essenceLRU = new LimitedHashTable<number, string>(1000);
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
@@ -32,7 +35,17 @@ export class NTQQGroupApi {
} }
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
} }
async fetchGroupEssenceList(groupCode: string) {
const pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().fetchGroupEssenceList({
groupCode: groupCode,
pageStart: 0,
pageLimit: 300
}, pskey);
}
async clearGroupNotifiesUnreadCount(unk: boolean) {
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(unk);
}
async setGroupAvatar(gc: string, filePath: string) { async setGroupAvatar(gc: string, filePath: string) {
return this.context.session.getGroupService().setHeader(gc, filePath); return this.context.session.getGroupService().setHeader(gc, filePath);
} }
@@ -270,7 +283,15 @@ export class NTQQGroupApi {
//应该是直接返回不需要Listener的 未经测试 需测试再发布 //应该是直接返回不需要Listener的 未经测试 需测试再发布
return this.context.session.getGroupService().quitGroupV2(param); return this.context.session.getGroupService().quitGroupV2(param);
} }
async removeGroupEssenceBySeq(GroupCode: string, msgRandom: string, msgSeq: string) {
const param = {
groupCode: GroupCode,
msgRandom: parseInt(msgRandom),
msgSeq: parseInt(msgSeq),
};
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
return this.context.session.getGroupService().removeGroupEssence(param);
}
async removeGroupEssence(GroupCode: string, msgId: string) { async removeGroupEssence(GroupCode: string, msgId: string) {
// 代码没测过 // 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom // 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -340,7 +361,7 @@ export class NTQQGroupApi {
]); ]);
if (membersFromFunc.status === 'fulfilled' && membersFromListener.status === 'fulfilled') { if (membersFromFunc.status === 'fulfilled' && membersFromListener.status === 'fulfilled') {
return new Map([ return new Map([
...membersFromFunc.value.result.infos, ...membersFromFunc.value.result.infos,
...membersFromListener.value[0].infos ...membersFromListener.value[0].infos
]); ]);
} }
@@ -355,7 +376,7 @@ export class NTQQGroupApi {
groupService.destroyMemberListScene(sceneId); groupService.destroyMemberListScene(sceneId);
} }
} }
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> { async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const groupService = this.context.session.getGroupService(); const groupService = this.context.session.getGroupService();
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow'); const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');

View File

@@ -27,7 +27,7 @@ export class NTQQWebApi {
msg_random: msgRandom, msg_random: msgRandom,
target_group_code: targetGroupCode, target_group_code: targetGroupCode,
}).toString() }).toString()
}`; }`;
try { try {
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
} catch (e) { } catch (e) {
@@ -35,19 +35,17 @@ export class NTQQWebApi {
} }
} }
async getGroupEssenceMsg(GroupCode: string, page_start: string) { async getGroupEssenceMsg(GroupCode: string) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({ const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject), bkn: this.getBknFromCookie(cookieObject),
group_code: GroupCode, group_code: GroupCode,
page_start,
page_limit: '20',
}).toString() }).toString()
}`; }`;
let ret; let ret;
try { try {
ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet> ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>
(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); (url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
} catch { } catch {
return undefined; return undefined;
} }
@@ -63,14 +61,14 @@ export class NTQQWebApi {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const retList: Promise<WebApiGroupMemberRet>[] = []; const retList: Promise<WebApiGroupMemberRet>[] = [];
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet> const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({ (`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
st: '0', st: '0',
end: '40', end: '40',
sort: '1', sort: '1',
gc: GroupCode, gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject), bkn: this.getBknFromCookie(cookieObject),
}).toString() }).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) { if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
return []; return [];
} else { } else {
@@ -83,14 +81,14 @@ export class NTQQWebApi {
//遍历批量请求 //遍历批量请求
for (let i = 2; i <= PageNum; i++) { for (let i = 2; i <= PageNum; i++) {
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet> const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({ (`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
st: ((i - 1) * 40).toString(), st: ((i - 1) * 40).toString(),
end: (i * 40).toString(), end: (i * 40).toString(),
sort: '1', sort: '1',
gc: GroupCode, gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject), bkn: this.getBknFromCookie(cookieObject),
}).toString() }).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
retList.push(ret); retList.push(ret);
} }
//批量等待 //批量等待
@@ -123,15 +121,15 @@ export class NTQQWebApi {
let ret: any = undefined; let ret: any = undefined;
try { try {
ret = await RequestUtil.HttpGetJson<any> ret = await RequestUtil.HttpGetJson<any>
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({ (`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject), bkn: this.getBknFromCookie(cookieObject),
qid: GroupCode, qid: GroupCode,
text: Content, text: Content,
pinned: '0', pinned: '0',
type: '1', type: '1',
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}', settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
}).toString() }).toString()
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
return ret; return ret;
} catch (e) { } catch (e) {
return undefined; return undefined;
@@ -162,7 +160,7 @@ export class NTQQWebApi {
gc: Internal_groupCode, gc: Internal_groupCode,
type: Internal_type.toString(), type: Internal_type.toString(),
}).toString() }).toString()
}`; }`;
let resJson; let resJson;
try { try {
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });

View File

@@ -1,5 +1,25 @@
import { ChatType, RawMessage } from '@/core/entities'; import { ChatType, RawMessage } from '@/core/entities';
export interface CommonFileInfo {
bizType: null;
chatType: number;
elemId: string;
favId: null;
fileModelId: string;
fileName: string;
fileSize: string;
md5: string;
md510m: string;
msgId: string;
msgTime: string;
parent: null;
peerUid: string;
picThumbPath: null;
sha: string;
sha3: string;
subId: string;
uuid: string;
[property: string]: any;
}
export interface OnRichMediaDownloadCompleteParams { export interface OnRichMediaDownloadCompleteParams {
fileModelId: string, fileModelId: string,
msgElementId: string, msgElementId: string,
@@ -15,7 +35,7 @@ export interface OnRichMediaDownloadCompleteParams {
totalSize: string, totalSize: string,
trasferStatus: number, trasferStatus: number,
step: number, step: number,
commonFileInfo: unknown | null, commonFileInfo?: CommonFileInfo,
fileSrvErrCode: string, fileSrvErrCode: string,
clientMsg: string, clientMsg: string,
businessId: number, businessId: number,

View File

@@ -2,6 +2,7 @@ import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListe
import { import {
GroupExt0xEF0InfoFilter, GroupExt0xEF0InfoFilter,
GroupExtParam, GroupExtParam,
GroupInfoSource,
GroupMember, GroupMember,
GroupMemberRole, GroupMemberRole,
GroupNotifyMsgType, GroupNotifyMsgType,
@@ -13,9 +14,9 @@ import { GeneralCallResult } from '@/core/services/common';
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底 //高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
export interface NodeIKernelGroupService { export interface NodeIKernelGroupService {
//getGroupExt0xEF0Info(this.$enableGroupCodes, this.$bannedGroupCodes, this.$filter, this.$forceFetch
getGroupExt0xEF0Info(enableGroupCodes: string[], bannedGroupCodes: string[], filter: GroupExt0xEF0InfoFilter, forceFetch: boolean): getGroupExt0xEF0Info(enableGroupCodes: string[], bannedGroupCodes: string[], filter: GroupExt0xEF0InfoFilter, forceFetch: boolean):
Promise<GeneralCallResult & { result: { groupExtInfos: Map<string, any> }}>; Promise<GeneralCallResult & { result: { groupExtInfos: Map<string, any> } }>;
kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>; kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>; quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
@@ -39,11 +40,11 @@ export interface NodeIKernelGroupService {
realSpecialTitleFlag: number realSpecialTitleFlag: number
}): Promise<unknown>; }): Promise<unknown>;
//26702
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>; getGroupMemberLevelInfo(groupCode: string): Promise<unknown>;
//26702 getGroupInfoForJoinGroup(groupCode: string, needPrivilegeFlag: boolean, serviceType: number): Promise<unknown>;
getGroupHonorList(groupCodes: Array<string>): unknown;
getGroupHonorList(req: { groupCodes: Array<string> }): Promise<unknown>;
getUinByUids(uins: string[]): Promise<{ getUinByUids(uins: string[]): Promise<{
errCode: number, errCode: number,
@@ -57,13 +58,10 @@ export interface NodeIKernelGroupService {
uids: Map<string, string> uids: Map<string, string>
}>; }>;
//26702(其实更早 但是我不知道)
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>; checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>;
//26702(其实更早 但是我不知道)
getGroupLatestEssenceList(groupCode: string): Promise<unknown>; getGroupLatestEssenceList(groupCode: string): Promise<unknown>;
//26702(其实更早 但是我不知道)
shareDigest(Req: { shareDigest(Req: {
appId: string, appId: string,
appType: number, appType: number,
@@ -83,20 +81,17 @@ export interface NodeIKernelGroupService {
} }
}): Promise<unknown>; }): Promise<unknown>;
//26702(其实更早 但是我不知道)
isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>; isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
//26702(其实更早 但是我不知道)
queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>; queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
//26702(其实更早 但是我不知道)
fetchGroupEssenceList(Req: { fetchGroupEssenceList(Req: {
groupCode: string, groupCode: string,
pageStart: number, pageStart: number,
pageLimit: number pageLimit: number
}, Arg: unknown): Promise<unknown>; }, Arg: unknown): Promise<unknown>;
//26702
getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{ getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{
errCode: number, errCode: number,
errMsg: string, errMsg: string,
@@ -135,8 +130,6 @@ export interface NodeIKernelGroupService {
getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>; getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>;
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>; kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
modifyMemberRole(groupCode: string, uid: string, role: GroupMemberRole): void; modifyMemberRole(groupCode: string, uid: string, role: GroupMemberRole): void;
@@ -151,7 +144,7 @@ export interface NodeIKernelGroupService {
getGroupExtList(force: boolean): Promise<GeneralCallResult>; getGroupExtList(force: boolean): Promise<GeneralCallResult>;
getGroupDetailInfo(groupCode: string): unknown; getGroupDetailInfo(groupCode: string, groupInfoSource: GroupInfoSource): Promise<unknown>;
getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req
@@ -196,7 +189,7 @@ export interface NodeIKernelGroupService {
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>; getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>;
clearGroupNotifiesUnreadCount(groupCode: string): void; clearGroupNotifiesUnreadCount(unknown: boolean): void;
operateSysNotify( operateSysNotify(
doubt: boolean, doubt: boolean,

View File

@@ -155,7 +155,7 @@ export interface NodeIKernelRichMediaService {
}): unknown; }): unknown;
//arg3为“” //arg3为“”
downloadFileForModelId(peer: Peer, ModelId: string[], arg3: string): unknown; downloadFileForModelId(peer: Peer, ModelId: string[]): Promise<unknown>;
//第三个参数 Array<Type> //第三个参数 Array<Type>
// this.fileId = ""; // this.fileId = "";

View File

@@ -0,0 +1,24 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
},
required: ['group_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupInfoEx extends BaseAction<Payload, any> {
actionName = ActionName.GetGroupInfoEx;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.core.apis.GroupApi;
const groupInfoEx = (await NTQQGroupApi.getGroupExtFE0Info([payload.group_id.toString()])).result.groupExtInfos.get(payload.group_id.toString());
return groupInfoEx;
}
}

View File

@@ -1,8 +1,8 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import fs from 'fs/promises'; import fs from 'fs/promises';
import { UUIDConverter } from '@/common/helper'; import { FileNapCatOneBotUUID } from '@/common/helper';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { ChatType, ElementType, Peer, RawMessage } from '@/core/entities'; import { ChatType, Peer, RawMessage } from '@/core/entities';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
export interface GetFilePayload { export interface GetFilePayload {
@@ -29,64 +29,64 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
payloadSchema: any = GetFileBase_PayloadSchema; payloadSchema: any = GetFileBase_PayloadSchema;
async _handle(payload: GetFilePayload): Promise<GetFileResponse> { async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const NTQQFriendApi = this.core.apis.FriendApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQMsgApi = this.core.apis.MsgApi; const NTQQMsgApi = this.core.apis.MsgApi;
const NTQQGroupApi = this.core.apis.GroupApi;
const NTQQFileApi = this.core.apis.FileApi; const NTQQFileApi = this.core.apis.FileApi;
try {
const uuidData = UUIDConverter.decode(payload.file); const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file);
const peerUin = uuidData.high;
const msgId = uuidData.low; //接收消息标记模式
const isGroup: boolean = !!(await NTQQGroupApi.getGroups(false)).find(e => e.groupCode == peerUin); if (contextMsgFile) {
let peer: Peer | undefined; const { peer, msgId, elementId } = contextMsgFile;
//识别Peer
if (isGroup) { const downloadPath = await NTQQFileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
} const mixElement = (await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]))?.msgList
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin); .find(msg => msg.msgId === msgId)?.elements.find(e => e.elementId === elementId);
if (PeerUid) { const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid); if (!mixElementInner) throw new Error('element not found');
if (isBuddy) {
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid }; const fileSize = mixElementInner.fileSize?.toString() || '';
} else { const fileName = mixElementInner.fileName || '';
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
}
}
if (!peer) {
throw new Error('chattype not support');
}
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
if (msgList.msgList.length == 0) {
throw new Error('msg not found');
}
const msg = msgList.msgList[0];
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
if (!findEle) {
throw new Error('element not found');
}
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
const res: GetFileResponse = { const res: GetFileResponse = {
file: downloadPath, file: downloadPath,
url: downloadPath, url: downloadPath,
file_size: fileSize, file_size: fileSize,
file_name: fileName, file_name: fileName,
}; };
if (/* enableLocalFile2Url && */ downloadPath) {
if (this.obContext.configLoader.configData.enableLocalFile2Url && downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
throw new Error('文件下载失败. ' + e);
}
}
return res;
}
//群文件模式
const contextModelIdFile = FileNapCatOneBotUUID.decodeModelId(payload.file);
if (contextModelIdFile) {
const { peer, modelId } = contextModelIdFile;
const downloadPath = await NTQQFileApi.downloadFileForModelId(peer, modelId);
const res: GetFileResponse = {
file: downloadPath,
url: downloadPath,
file_size: '',
file_name: '',
};
if (this.obContext.configLoader.configData.enableLocalFile2Url && downloadPath) {
try { try {
res.base64 = await fs.readFile(downloadPath, 'base64'); res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) { } catch (e) {
throw new Error('文件下载失败. ' + e); throw new Error('文件下载失败. ' + e);
} }
} }
//不手动删除?文件持久化了
return res; return res;
} catch {
this.core.context.logger.logDebug('GetFileBase Mode - 1 Error');
} }
//搜索名字模式
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems; const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
if (NTSearchNameResult.length !== 0) { if (NTSearchNameResult.length !== 0) {
const MsgId = NTSearchNameResult[0].msgId; const MsgId = NTSearchNameResult[0].msgId;
@@ -94,9 +94,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) { if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) {
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode }; peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
} }
if (!peer) { if (!peer) throw new Error('chattype not support');
throw new Error('chattype not support');
}
const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList; const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList;
if (!msgList || msgList.length == 0) { if (!msgList || msgList.length == 0) {
throw new Error('msg not found'); throw new Error('msg not found');
@@ -113,16 +111,16 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
file_size: NTSearchNameResult[0].fileSize.toString(), file_size: NTSearchNameResult[0].fileSize.toString(),
file_name: NTSearchNameResult[0].fileName, file_name: NTSearchNameResult[0].fileName,
}; };
if (/* enableLocalFile2Url && */ downloadPath) { if (this.obContext.configLoader.configData.enableLocalFile2Url && downloadPath) {
try { try {
res.base64 = await fs.readFile(downloadPath, 'base64'); res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) { } catch (e) {
throw new Error('文件下载失败. ' + e); throw new Error('文件下载失败. ' + e);
} }
} }
//不手动删除?文件持久化了
return res; return res;
} }
throw new Error('file not found'); throw new Error('file not found');
} }
} }

View File

@@ -6,8 +6,9 @@ const SchemaData = {
type: 'object', type: 'object',
properties: { properties: {
group_id: { type: ['string', 'number'] }, group_id: { type: ['string', 'number'] },
start_index: { type: 'number' }, start_index: { type: ['string', 'number'] },
file_count: { type: 'number' }, file_count: { type: ['string', 'number'] },
folder_id: { type: ['string', 'number'] },
}, },
required: ['group_id', 'start_index', 'file_count'], required: ['group_id', 'start_index', 'file_count'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
@@ -20,12 +21,19 @@ export class GetGroupFileList extends BaseAction<Payload, { FileList: Array<any>
async _handle(payload: Payload) { async _handle(payload: Payload) {
const NTQQMsgApi = this.core.apis.MsgApi; const NTQQMsgApi = this.core.apis.MsgApi;
let param = {};
if (payload.folder_id) {
param = {
folderId: payload.folder_id.toString(),
};
}
const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), { const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1, sortType: 1,
fileCount: payload.file_count, fileCount: +payload.file_count,
startIndex: payload.start_index, startIndex: +payload.start_index,
sortOrder: 2, sortOrder: 2,
showOnlinedocFolder: 0, showOnlinedocFolder: 0,
...param
}).catch((e) => { }).catch((e) => {
return []; return [];
}); });

View File

@@ -38,7 +38,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
const resMsg = await this.obContext.apis.MsgApi const resMsg = await this.obContext.apis.MsgApi
.parseMessage(msg); .parseMessage(msg);
if (!resMsg) return; if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({ resMsg.message_id = MessageUnique.createUniqueMsgId({
guildId: '', guildId: '',
chatType: msg.chatType, chatType: msg.chatType,
peerUid: msg.peerUid, peerUid: msg.peerUid,

View File

@@ -47,7 +47,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
if (isReverseOrder) msgList.reverse(); if (isReverseOrder) msgList.reverse();
//转换序号 //转换序号
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(

View File

@@ -42,7 +42,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
if (isReverseOrder) msgList.reverse(); if (isReverseOrder) msgList.reverse();
//转换序号 //转换序号
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(

View File

@@ -20,7 +20,13 @@ export default class DelEssenceMsg extends BaseAction<Payload, any> {
async _handle(payload: Payload): Promise<any> { async _handle(payload: Payload): Promise<any> {
const NTQQGroupApi = this.core.apis.GroupApi; const NTQQGroupApi = this.core.apis.GroupApi;
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
if (!msg) throw new Error('msg not found'); const NTQQWebApi = this.core.apis.WebApi;
if (!msg) {
const data = NTQQGroupApi.essenceLRU.getValue(+payload.message_id);
if(!data) throw new Error('消息不存在');
const { msg_seq, msg_random, group_id } = JSON.parse(data) as { msg_seq: string, msg_random: string, group_id: string };
return await NTQQGroupApi.removeGroupEssenceBySeq(group_id, msg_seq, msg_random);
}
return await NTQQGroupApi.removeGroupEssence( return await NTQQGroupApi.removeGroupEssence(
msg.Peer.peerUid, msg.Peer.peerUid,
msg.MsgId, msg.MsgId,

View File

@@ -1,29 +1,117 @@
import { GroupEssenceMsgRet } from '@/core'; import { ChatType, GroupEssenceMsgRet, Peer } from '@/core';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/message-unique';
import crypto from 'crypto';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
properties: { properties: {
group_id: { type: ['number', 'string'] }, group_id: { type: ['number', 'string'] }
pages: { type: ['number', 'string'] },
}, },
required: ['group_id'], required: ['group_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> { export class GetGroupEssence extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_GetEssenceMsg; actionName = ActionName.GoCQHTTP_GetEssenceMsg;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async parseEssenceMsgImage(ele: any) {
return {
type: 'image',
data: {
url: ele?.image_url,
}
};
}
async parseEssenceMsgText(ele: any) {
return {
type: 'text',
data: {
text: ele?.text
}
};
}
async parseEssenceMsg(msgs: any) {
let handledMsg: any[] = [];
for (let msg of msgs) {
switch (msg.msg_type) {
case 1:
handledMsg.push(await this.parseEssenceMsgText(msg));
break;
case 3:
handledMsg.push(await this.parseEssenceMsgImage(msg));
break;
default:
break;
}
}
return handledMsg;
}
async msgSeqToMsgId(peer: Peer, msgSeq: string, msgRandom: string) {
const NTQQMsgApi = this.core.apis.MsgApi;
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount(peer, msgSeq, 1, true, true)).msgList.find((msg) => msg.msgSeq === msgSeq && msg.msgRandom === msgRandom);
if (!replyMsgList) {
return undefined;
}
return {
id: MessageUnique.createUniqueMsgId(peer, replyMsgList.msgId),
msg: replyMsgList
}
}
async _handle(payload: Payload) { async _handle(payload: Payload) {
const NTQQWebApi = this.core.apis.WebApi; const NTQQWebApi = this.core.apis.WebApi;
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), (+(payload.pages ?? 0)).toString()); const NTQQGroupApi = this.core.apis.GroupApi;
//await NTQQGroupApi.fetchGroupEssenceList(payload.group_id.toString());
let peer = {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
};
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString());
if (!ret) { if (!ret) {
throw new Error('获取失败'); throw new Error('获取失败');
} }
return ret; const Ob11Ret = await Promise.all(ret.data.msg_list.map(async (msg) => {
let msgOriginData = await this.msgSeqToMsgId(peer, msg.msg_seq.toString(), msg.msg_random.toString());
if (msgOriginData) {
const { id: message_id, msg: rawMessage } = msgOriginData;
return {
msg_seq: msg.msg_seq,
msg_random: msg.msg_random,
sender_id: +msg.sender_uin,
sender_nick: msg.sender_nick,
operator_id: +msg.add_digest_uin,
operator_nick: msg.add_digest_nick,
message_id: message_id,
operator_time: msg.add_digest_time,
content: (await this.obContext.apis.MsgApi.parseMessage(rawMessage, 'array'))?.message
};
}
const msgTempData = JSON.stringify({
msg_seq: msg.msg_seq.toString(),
msg_random: msg.msg_random.toString(),
group_id: payload.group_id.toString(),
});
const hash = crypto.createHash('md5').update(msgTempData).digest();
//设置第一个bit为0 保证shortId为正数
hash[0] &= 0x7f;
const shortId = hash.readInt32BE(0);
NTQQGroupApi.essenceLRU.set(shortId, msgTempData);
return {
msg_seq: msg.msg_seq,
msg_random: msg.msg_random,
sender_id: +msg.sender_uin,
sender_nick: msg.sender_nick,
operator_id: +msg.add_digest_uin,
operator_nick: msg.add_digest_nick,
message_id: shortId,
operator_time: msg.add_digest_time,
content: await this.parseEssenceMsg(msg.msg_content)
};
}));
return Ob11Ret;
} }
} }

View File

@@ -81,11 +81,13 @@ import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus'; import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF'; import { GetCSRF } from './system/GetCSRF';
import { DelGroupNotice } from './group/DelGroupNotice'; import { DelGroupNotice } from './group/DelGroupNotice';
import { GetGroupInfoEx } from './extends/GetGroupInfoEx';
export type ActionMap = Map<string, BaseAction<any, any>>; export type ActionMap = Map<string, BaseAction<any, any>>;
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore): ActionMap { export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore): ActionMap {
const actionHandlers = [ const actionHandlers = [
new GetGroupInfoEx(obContext, core),
new FetchEmojiLike(obContext, core), new FetchEmojiLike(obContext, core),
new GetFile(obContext, core), new GetFile(obContext, core),
new SetQQProfile(obContext, core), new SetQQProfile(obContext, core),

View File

@@ -39,7 +39,7 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg.msgList[0], 'array'); const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg.msgList[0], 'array');
if (!retMsg) throw Error('消息为空'); if (!retMsg) throw Error('消息为空');
try { try {
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!; retMsg.message_id = MessageUnique.createUniqueMsgId(peer, msg.msgList[0].msgId)!;
retMsg.message_seq = retMsg.message_id; retMsg.message_seq = retMsg.message_id;
retMsg.real_id = retMsg.message_id; retMsg.real_id = retMsg.message_id;
} catch (e) { } catch (e) {

View File

@@ -114,7 +114,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) { if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const returnMsg = await this.handleForwardedNodes(peer, messages as OB11MessageNode[]); const returnMsg = await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
if (returnMsg) { if (returnMsg) {
const msgShortId = MessageUnique.createMsg({ const msgShortId = MessageUnique.createUniqueMsgId({
guildId: '', guildId: '',
peerUid: peer.peerUid, peerUid: peer.peerUid,
chatType: peer.chatType, chatType: peer.chatType,
@@ -170,7 +170,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node)); const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
if (nodeMsg) { if (nodeMsg) {
nodeMsgIds.push(nodeMsg.msgId); nodeMsgIds.push(nodeMsg.msgId);
MessageUnique.createMsg(selfPeer, nodeMsg.msgId); MessageUnique.createUniqueMsgId(selfPeer, nodeMsg.msgId);
} }
//完成子卡片生成跳过后续 //完成子卡片生成跳过后续
continue; continue;
@@ -188,7 +188,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
(await Promise.allSettled(MsgNodeList)).map((result) => { (await Promise.allSettled(MsgNodeList)).map((result) => {
if (result.status === 'fulfilled' && result.value) { if (result.status === 'fulfilled' && result.value) {
nodeMsgIds.push(result.value.msgId); nodeMsgIds.push(result.value.msgId);
MessageUnique.createMsg(selfPeer, result.value.msgId); MessageUnique.createUniqueMsgId(selfPeer, result.value.msgId);
} }
}); });
} catch (e) { } catch (e) {

View File

@@ -110,4 +110,5 @@ export enum ActionName {
SetInputStatus = 'set_input_status', SetInputStatus = 'set_input_status',
GetCSRF = 'get_csrf_token', GetCSRF = 'get_csrf_token',
DelGroupNotice = '_del_group_notice', DelGroupNotice = '_del_group_notice',
GetGroupInfoEx = "get_group_info_ex"
} }

View File

@@ -1,4 +1,4 @@
import { UUIDConverter } from '@/common/helper'; import { FileNapCatOneBotUUID } from '@/common/helper';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import { import {
AtType, AtType,
@@ -98,14 +98,19 @@ export class OneBotMsgApi {
} }
}, },
picElement: async (element, msg) => { picElement: async (element, msg, elementWrapper) => {
try { try {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
return { return {
type: OB11MessageDataType.image, type: OB11MessageDataType.image,
data: { data: {
file: element.fileName, file: element.fileName,
sub_type: element.picSubType, sub_type: element.picSubType,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
url: await this.core.apis.FileApi.getImageUrl(element), url: await this.core.apis.FileApi.getImageUrl(element),
file_size: element.fileSize, file_size: element.fileSize,
}, },
@@ -117,6 +122,11 @@ export class OneBotMsgApi {
}, },
fileElement: async (element, msg, elementWrapper) => { fileElement: async (element, msg, elementWrapper) => {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
await this.core.apis.FileApi.addFileCache( await this.core.apis.FileApi.addFileCache(
{ {
peerUid: msg.peerUid, peerUid: msg.peerUid,
@@ -137,7 +147,7 @@ export class OneBotMsgApi {
file: element.fileName, file: element.fileName,
path: element.filePath, path: element.filePath,
url: element.filePath, url: element.filePath,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
file_size: element.fileSize, file_size: element.fileSize,
}, },
}; };
@@ -184,11 +194,16 @@ export class OneBotMsgApi {
'0', '0',
'marketface', 'marketface',
); );
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
return { return {
type: OB11MessageDataType.image, type: OB11MessageDataType.image,
data: { data: {
file: 'marketface', file: 'marketface',
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
path: elementWrapper.elementId, path: elementWrapper.elementId,
url: elementWrapper.elementId, url: elementWrapper.elementId,
}, },
@@ -207,13 +222,21 @@ export class OneBotMsgApi {
this.core.context.logger.logError('获取不到引用的消息', element.replayMsgSeq); this.core.context.logger.logError('获取不到引用的消息', element.replayMsgSeq);
return null; return null;
} }
const createReplyData = (msgId: string): OB11MessageData => ({
type: OB11MessageDataType.reply,
data: {
id: MessageUnique.createUniqueMsgId(peer, msgId).toString(),
},
});
if (records.peerUin === '284840486') {
return createReplyData(records.msgId);
}
let replyMsg: RawMessage | undefined; let replyMsg: RawMessage | undefined;
// Attempt 1 // Attempt 1
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({ replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount(peer,element.replayMsgSeq, 1, true, true))
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, element.replayMsgSeq, 1, true, true))
.msgList .msgList
.find(msg => msg.msgRandom === records.msgRandom); .find(msg => msg.msgRandom === records.msgRandom);
@@ -221,7 +244,7 @@ export class OneBotMsgApi {
// Attempt 2 // Attempt 2
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replayMsgSeq)).msgList[0]; replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replayMsgSeq)).msgList[0];
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') { if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
// Attempt 3 // Attempt 3
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList; const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) { if (replyMsgList.length < 1) {
@@ -233,21 +256,16 @@ export class OneBotMsgApi {
} }
} }
return { return createReplyData(replyMsg.msgId);
type: OB11MessageDataType.reply,
data: {
id: MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId).toString(),
},
};
}, },
videoElement: async (element, msg, elementWrapper) => { videoElement: async (element, msg, elementWrapper) => {
const NTQQFileApi = this.core.apis.FileApi; const NTQQFileApi = this.core.apis.FileApi;
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
//读取视频链接并兜底 //读取视频链接并兜底
let videoUrlWrappers: Awaited<ReturnType<typeof NTQQFileApi.getVideoUrl>> | undefined; let videoUrlWrappers: Awaited<ReturnType<typeof NTQQFileApi.getVideoUrl>> | undefined;
@@ -302,13 +320,18 @@ export class OneBotMsgApi {
file: element.fileName, file: element.fileName,
path: videoDownUrl, path: videoDownUrl,
url: videoDownUrl, url: videoDownUrl,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
file_size: element.fileSize, file_size: element.fileSize,
}, },
}; };
}, },
pttElement: async (element, msg, elementWrapper) => { pttElement: async (element, msg, elementWrapper) => {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
await this.core.apis.FileApi.addFileCache( await this.core.apis.FileApi.addFileCache(
{ {
peerUid: msg.peerUid, peerUid: msg.peerUid,
@@ -328,7 +351,7 @@ export class OneBotMsgApi {
data: { data: {
file: element.fileName, file: element.fileName,
path: element.filePath, path: element.filePath,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
file_size: element.fileSize, file_size: element.fileSize,
}, },
}; };
@@ -365,7 +388,7 @@ export class OneBotMsgApi {
async multiMsgItem => { async multiMsgItem => {
multiMsgItem.parentMsgPeer = parentMsgPeer; multiMsgItem.parentMsgPeer = parentMsgPeer;
multiMsgItem.parentMsgIdList = msg.parentMsgIdList; multiMsgItem.parentMsgIdList = msg.parentMsgIdList;
multiMsgItem.id = MessageUnique.createMsg(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用 multiMsgItem.id = MessageUnique.createUniqueMsgId(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用
return await this.parseMessage(multiMsgItem); return await this.parseMessage(multiMsgItem);
}, },
))).filter(item => item !== undefined), ))).filter(item => item !== undefined),
@@ -823,7 +846,7 @@ export class OneBotMsgApi {
} }
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout); const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
if (!returnMsg) throw new Error('发送消息失败'); if (!returnMsg) throw new Error('发送消息失败');
returnMsg.id = MessageUnique.createMsg({ returnMsg.id = MessageUnique.createUniqueMsgId({
chatType: peer.chatType, chatType: peer.chatType,
guildId: '', guildId: '',
peerUid: peer.peerUid, peerUid: peer.peerUid,

View File

@@ -1,6 +1,6 @@
{ {
"http": { "http": {
"enable": true, "enable": false,
"host": "", "host": "",
"port": 3000, "port": 3000,
"secret": "", "secret": "",

View File

@@ -261,6 +261,7 @@ export class NapCatOneBot11Adapter {
emojiLikeToOthers.msgSpec!.msgSeq.toString(), emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId, emojiLikeToOthers.attributes!.emojiId,
); );
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty); eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
} }
} }
@@ -283,7 +284,7 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug(`消息时间${m.msgTime}早于启动时间${this.bootTime},忽略上报`); this.context.logger.logDebug(`消息时间${m.msgTime}早于启动时间${this.bootTime},忽略上报`);
continue; continue;
} }
m.id = MessageUnique.createMsg( m.id = MessageUnique.createUniqueMsgId(
{ {
chatType: m.chatType, chatType: m.chatType,
peerUid: m.peerUid, peerUid: m.peerUid,
@@ -311,7 +312,7 @@ export class NapCatOneBot11Adapter {
if (!ob11Msg) return; if (!ob11Msg) return;
ob11Msg.target_id = parseInt(msg.peerUin); ob11Msg.target_id = parseInt(msg.peerUin);
if (this.configLoader.configData.reportSelfMessage) { if (this.configLoader.configData.reportSelfMessage) {
msg.id = MessageUnique.createMsg({ msg.id = MessageUnique.createUniqueMsgId({
chatType: msg.chatType, chatType: msg.chatType,
peerUid: msg.peerUid, peerUid: msg.peerUid,
guildId: '', guildId: '',
@@ -344,7 +345,7 @@ export class NapCatOneBot11Adapter {
// } // }
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
return; continue;
} }
try { try {
const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid); const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid);
@@ -367,9 +368,10 @@ export class NapCatOneBot11Adapter {
private initGroupListener() { private initGroupListener() {
const groupListener = new NodeIKernelGroupListener(); const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupNotifiesUpdated = async (_, notifies) => { groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]); //console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false);
if (![ if (![
GroupNotifyMsgType.SET_ADMIN, GroupNotifyMsgType.SET_ADMIN,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,

View File

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

View File

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