Compare commits

..

58 Commits

Author SHA1 Message Date
Alen
efc1875e35 release: 2.2.29 2024-08-31 12:53:50 +08:00
Alen
df063e6762 Merge pull request #326 from cnxysoft/upmain
fix: 群成员信息
2024-08-31 11:55:56 +08:00
Alen
e5c55b4339 fix: 群成员信息 2024-08-31 11:53:07 +08:00
Wesley F. Young
bee9095d6f release: 2.2.28 2024-08-31 10:35:33 +08:00
Wesley F. Young
92f8eaaac9 fix: get file by msgId and elemId 2024-08-31 10:34:19 +08:00
Wesley F. Young
f5e7288fe5 fix: report encoded msgId+elemId in upload event 2024-08-30 20:47:48 +08:00
Seijo Cecilia
214aa7b6e4 update(workflow): 'build' can only be triggered manually 2024-08-30 16:00:33 +08:00
Seijo Cecilia
5b5d5b41f5 build: snapshot-fix-get-file 2024-08-30 15:47:18 +08:00
Seijo Cecilia
23d613321e Revert "fix: arg3 no longer needed for downloadFileForModelId"
This reverts commit e1e4d038d9.
2024-08-30 15:41:50 +08:00
Wesley F. Young
0b6be0923f release: 2.2.27 2024-08-30 12:02:50 +08:00
Wesley F. Young
aba748ea13 Merge pull request #323 from LingLambda/main
fix: 规范 setSelfOnlineStatus 接口的参数命名
2024-08-30 11:55:22 +08:00
Wesley F. Young
f1f1ac582d chore: optimize imports 2024-08-30 11:45:58 +08:00
Wesley F. Young
54a7cbc3f4 feat: go-cqhttp style group file apis 2024-08-30 11:44:15 +08:00
ling
2f4dbaec4c fix: 规范setSelfOnlineStatus接口参数命名 2024-08-30 11:38:15 +08:00
Wesley F. Young
578f518aaf fix: others invited by others 2024-08-30 10:04:48 +08:00
Wesley F. Young
077ba74b22 fix: missing parameter for file searching 2024-08-30 09:31:18 +08:00
手瓜一十雪
e0efe635c7 release: 2.2.26 2024-08-29 22:47:24 +08:00
手瓜一十雪
1a06841de0 feat: notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS 2024-08-29 22:46:55 +08:00
手瓜一十雪
3987e0ee0b feat: 2.2.25 2024-08-29 22:38:48 +08:00
Wesley F. Young
9f53bea02f fix: duplicate type definition 2024-08-29 22:30:19 +08:00
Wesley F. Young
737709f9e7 Merge remote-tracking branch 'origin/main' 2024-08-29 22:27:57 +08:00
Wesley F. Young
39477aa6a0 fix: try to fix '搜索名字模式' of GetFile 2024-08-29 22:27:51 +08:00
手瓜一十雪
f097050b56 chore: 移除测试 2024-08-29 21:57:05 +08:00
手瓜一十雪
f14726ed1a fix: getfile 2024-08-29 21:55:44 +08:00
Wesley F. Young
e1e4d038d9 fix: arg3 no longer needed for downloadFileForModelId 2024-08-29 21:21:45 +08:00
Wesley F. Young
d2db4cf887 fix: 有笨蛋塞了 console.log 忘记删掉 2024-08-29 20:58:39 +08:00
手瓜一十雪
2f3ece9ca3 build: 2.2.25-test 2024-08-29 20:50:09 +08:00
手瓜一十雪
9f82007116 revert: eslint 2024-08-29 20:40:33 +08:00
手瓜一十雪
f79198a472 release: 2.2.24 2024-08-29 20:35:30 +08:00
手瓜一十雪
ce3d35d7ec fix: modelId 2024-08-29 20:34:24 +08:00
手瓜一十雪
f4d40f0466 release: 2.2.23 2024-08-29 20:14:40 +08:00
手瓜一十雪
a2fa085d5f fix: getfile 2024-08-29 20:14:20 +08:00
手瓜一十雪
a598266a6e fix: #320 2024-08-29 19:32:37 +08:00
手瓜一十雪
f5fe33cee7 fix: GroupEssenceMsg 2024-08-29 19:25:16 +08:00
手瓜一十雪
200c7226ef fix: getGroupEssenceMsgAll 2024-08-29 19:21:03 +08:00
Wesley F. Young
53475a6a0e fix: filter emoji un-like by operation 2024-08-29 18:15:52 +08:00
Wesley F. Young
b4ec1ad6c0 Merge remote-tracking branch 'origin/main' 2024-08-29 17:10:27 +08:00
Wesley F. Young
ef511a729d fix: solve export conflict 2024-08-29 17:10:14 +08:00
Wesley F. Young
275c4ce226 feat: GetGroupIgnoredNotifies 2024-08-29 17:08:36 +08:00
手瓜一十雪
45f9c029c8 Merge pull request #319 from NapNeko/dependabot/npm_and_yarn/eslint-9.9.1
build(deps-dev): bump eslint from 8.57.0 to 9.9.1
2024-08-29 17:01:34 +08:00
dependabot[bot]
db5e4ad5d9 build(deps-dev): bump eslint from 8.57.0 to 9.9.1
Bumps [eslint](https://github.com/eslint/eslint) from 8.57.0 to 9.9.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.0...v9.9.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-29 08:54:04 +00:00
Wesley F. Young
f05d0a9727 chore: run eslint 2024-08-29 08:37:10 +08:00
Wesley F. Young
04593e9d9a feat: code style 2024-08-29 00:28:53 +08:00
手瓜一十雪
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
53 changed files with 972 additions and 337 deletions

View File

@@ -1,15 +1,11 @@
name: "Build Action" name: "Build Action"
on: on:
workflow_dispatch: workflow_dispatch:
push:
branches:
- main
permissions: write-all permissions: write-all
jobs: jobs:
Build-LiteLoader: Build-LiteLoader:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Clone Main Repository - name: Clone Main Repository
@@ -37,7 +33,6 @@ jobs:
name: NapCat.Framework name: NapCat.Framework
path: dist path: dist
Build-Shell: Build-Shell:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Clone Main Repository - name: Clone Main Repository
@@ -63,4 +58,4 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: NapCat.Shell name: NapCat.Shell
path: dist path: dist

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.20", "version": "2.2.29",
"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.20", "version": "2.2.29",
"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",

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) => {
@@ -24,25 +24,49 @@ export async function solveAsyncProblem<T extends (...args: any[]) => Promise<an
}); });
} }
//下面这个类是用于将uid+msgid合并的类 export class FileNapCatOneBotUUID {
export class UUIDConverter { static encodeModelId(peer: Peer, modelId: string): string {
static encode(highStr: string, lowStr: string): string { return `NapCatOneBot-ModelIdFile-${peer.chatType}-${peer.peerUid}-${modelId}`;
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 decode(uuid: string): { high: string; low: string } { static decodeModelId(uuid: string): undefined | {
const hex = uuid.replace(/-/g, ''); peer: Peer,
const high = BigInt('0x' + hex.substring(0, 16)); modelId: string
const low = BigInt('0x' + hex.substring(16)); } {
return { high: high.toString(), low: low.toString() }; if (!uuid.startsWith('NapCatOneBot-ModelIdFile-')) return undefined;
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-MsgFile-')) 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,
};
} }
} }

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.20'; export const napCatVersion = '2.2.29';

View File

@@ -17,7 +17,7 @@ import {
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import fsPromises from 'fs/promises'; import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore } from '@/core'; import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
import * as fileType from 'file-type'; import * as fileType from 'file-type';
import imageSize from 'image-size'; import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface'; import { ISizeCalculationResult } from 'image-size/dist/types/interface';
@@ -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, unknown: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[peer, [modelId], unknown],
() => 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);
// 用于下载收到的消息中的图片等 // 用于下载收到的消息中的图片等
@@ -342,20 +353,15 @@ export class NTQQFileApi {
chatType: chatType, chatType: chatType,
peerUid: peerUid, peerUid: peerUid,
}, [msgId]); }, [msgId]);
if (msg.msgList.length === 0) { const mixElement = msg.msgList.find((msg) => msg.msgId === msgId)?.elements.find((e) => e.elementId === elementId);
return fileTransNotifyInfo.filePath; const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
let realPath = mixElementInner?.filePath;
if (!realPath) {
const picThumbPath: Map<number, string> = (mixElementInner as any)?.picThumbPath;
const picThumbPathList = Array.from(picThumbPath.values());
if (picThumbPathList.length > 0) realPath = picThumbPathList[0];
} }
//获取原始消息 return realPath;
const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
if (!FileElements) {
//失败则就乱来 Todo
return fileTransNotifyInfo.filePath;
}
//从原始消息获取文件路径
return FileElements?.fileElement?.filePath ??
FileElements?.pttElement?.filePath ??
FileElements?.videoElement?.filePath ??
FileElements?.picElement?.sourcePath;
} }
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> { async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
@@ -429,17 +435,44 @@ export class NTQQFileApi {
}); });
} }
async searchfile(keys: string[]) { async searchForFile(keys: string[]): Promise<SearchResultItem | undefined> {
const Event = this.core.eventWrapper.createEventFunction('NodeIKernelSearchService/searchFileWithKeywords'); const randomResultId = 100000 + Math.floor(Math.random() * 10000);
const id = await Event!(keys, 12); let searchId = 0;
const Listener = this.core.eventWrapper.registerListen( const [, searchResult] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelSearchListener/onSearchFileKeywordsResult', 'NodeIKernelFileAssistantService/searchFile',
1, 'NodeIKernelFileAssistantListener/onFileSearch',
20000, [
(params) => id !== '' && params.searchId == id, keys,
{
resultType: 2,
pageLimit: 1,
},
randomResultId
],
(ret) => {
searchId = ret;
return true;
},
result => result.searchId === searchId && result.resultId === randomResultId,
); );
const [ret] = (await Listener); return searchResult.resultItems[0];
return ret; }
async downloadFileById(
fileId: string,
fileSize: number = 1024576,
estimatedTime: number = (fileSize * 1000 / 1024576) + 5000,
) {
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelFileAssistantService/downloadFile',
'NodeIKernelFileAssistantListener/onFileStatusChanged',
[[fileId]],
ret => ret.result === 0,
status => status.fileStatus === 2 && status.fileProgress === '0',
1,
estimatedTime, // estimate 1MB/s
);
return ret.filePath!;
} }
async getImageUrl(element: PicElement) { async getImageUrl(element: PicElement) {

View File

@@ -2,7 +2,6 @@ import {
ChatType, ChatType,
GeneralCallResult, GeneralCallResult,
Group, Group,
GroupInfoSource,
GroupMember, GroupMember,
GroupMemberRole, GroupMemberRole,
GroupRequestOperateTypes, GroupRequestOperateTypes,
@@ -12,6 +11,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;
@@ -19,6 +19,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;
@@ -33,6 +34,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 +282,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
@@ -288,12 +308,12 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().removeGroupEssence(param); return this.context.session.getGroupService().removeGroupEssence(param);
} }
async getSingleScreenNotifies(num: number) { async getSingleScreenNotifies(doubt: boolean, num: number) {
const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2( const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getSingleScreenNotifies', 'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies', 'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
[ [
false, doubt,
'', '',
num, num,
], ],
@@ -381,7 +401,7 @@ export class NTQQGroupApi {
} }
async GetGroupFileCount(Gids: Array<string>) { async getGroupFileCount(Gids: Array<string>) {
return this.context.session.getRichMediaService().batchGetGroupFileCount(Gids); return this.context.session.getRichMediaService().batchGetGroupFileCount(Gids);
} }

View File

@@ -27,27 +27,40 @@ 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) {
return undefined; return undefined;
} }
} }
async getGroupEssenceMsgAll(GroupCode: string) {
let ret: GroupEssenceMsgRet[] = [];
for (let i = 0; i < 4; i++) {
let data = await this.getGroupEssenceMsg(GroupCode, i, 50);
if (!data) break;
if (data.data.is_end) {
ret.push(data);
break;
}
ret.push(data);
async getGroupEssenceMsg(GroupCode: string, page_start: string) { }
return ret;
}
async getGroupEssenceMsg(GroupCode: string, page_start: number = 0, page_limit: number = 50) {
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),
page_start: page_start.toString(),
page_limit: page_limit.toString(),
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 +76,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 +96,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 +136,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 +175,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

@@ -22,6 +22,7 @@ export interface GetFileListParam {
startIndex: number; startIndex: number;
sortOrder: number; sortOrder: number;
showOnlinedocFolder: number; showOnlinedocFolder: number;
folderId?: string;
} }
export enum ElementType { export enum ElementType {
@@ -593,6 +594,7 @@ export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn';
export interface PicElement { export interface PicElement {
md5HexStr?: string; md5HexStr?: string;
filePath?: string;
fileSize: number | string;//number fileSize: number | string;//number
picWidth: number; picWidth: number;
picHeight: number; picHeight: number;

View File

@@ -1,5 +1,13 @@
export class NodeIKernelFileAssistantListener { export class NodeIKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]) { onFileStatusChanged(fileStatus: {
id: string,
fileStatus: number,
fileProgress: `${number}`,
fileSize: `${number}`,
fileSpeed: number,
thumbPath: string | null,
filePath: string | null,
}) {
} }
onSessionListChanged(...args: unknown[]) { onSessionListChanged(...args: unknown[]) {
@@ -11,6 +19,42 @@ export class NodeIKernelFileAssistantListener {
onFileListChanged(...args: unknown[]) { onFileListChanged(...args: unknown[]) {
} }
onFileSearch(...args: unknown[]) { onFileSearch(searchResult: SearchResultWrapper) {
} }
} }
export type SearchResultWrapper = {
searchId: number,
resultId: number,
hasMore: boolean,
resultItems: SearchResultItem[],
};
export type SearchResultItem = {
id: string,
fileName: string,
fileNameHits: string[],
fileStatus: number,
fileSize: string,
isSend: boolean,
source: number,
fileTime: string,
expTime: string,
session: {
context: null,
uid: string,
nick: string,
remark: string,
memberCard: string,
groupCode: string,
groupName: string,
groupRemark: string,
count: number,
},
thumbPath: string,
filePath: string,
msgId: string,
chatType: number,
peerUid: string,
fileType: number,
};

View File

@@ -1,4 +1,5 @@
import { ChatType, RawMessage } from '@/core/entities'; import { ChatType, RawMessage } from '@/core/entities';
import { CommonFileInfo } from '@/core';
export interface OnRichMediaDownloadCompleteParams { export interface OnRichMediaDownloadCompleteParams {
fileModelId: string, fileModelId: string,
@@ -15,7 +16,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,
@@ -28,7 +29,47 @@ export interface GroupFileInfoUpdateParamType {
retMsg: string; retMsg: string;
clientWording: string; clientWording: string;
isEnd: boolean; isEnd: boolean;
item: Array<any>; item: Array<{
peerId: string;
type: number;
folderInfo?: {
folderId: string;
parentFolderId: string;
folderName: string;
createTime: number;
modifyTime: number;
createUin: string;
creatorName: string;
totalFileCount: number;
modifyUin: string;
modifyName: string;
usedSpace: string;
},
fileInfo?: {
fileModelId: string;
fileId: string;
fileName: string;
fileSize: string;
busId: number;
uploadedSize: string;
uploadTime: number;
deadTime: number;
modifyTime: number;
downloadTimes: number;
sha: string;
sha3: string;
md5: string;
uploaderLocalPath: string;
uploaderName: string;
uploaderUin: string;
parentFolderId: string;
localPath: string;
transStatus: number;
transType: number;
elementId: string;
isFolder: boolean;
},
}>;
allFileCount: string; allFileCount: string;
nextIndex: string; nextIndex: string;
reqId: string; reqId: string;

View File

@@ -19,6 +19,13 @@ message EmojiLikeToOthersMsgSpec {
} }
message EmojiLikeToOthersAttributes { message EmojiLikeToOthersAttributes {
enum Operation {
FALLBACK = 0;
LIKE = 1;
UNLIKE = 2;
}
string emojiId = 1; string emojiId = 1;
string senderUid = 4; string senderUid = 4;
Operation operation = 5;
} }

View File

@@ -1,15 +1,15 @@
// @generated by protobuf-ts 2.9.4 // @generated by protobuf-ts 2.9.4
// @generated from protobuf file "EmojiLikeToOthers.proto" (package "SysMessage", syntax proto3) // @generated from protobuf file "EmojiLikeToOthers.proto" (package "SysMessage", syntax proto3)
// tslint:disable // tslint:disable
import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; import type {
import type { IBinaryWriter } from "@protobuf-ts/runtime"; BinaryReadOptions,
import { WireType } from "@protobuf-ts/runtime"; BinaryWriteOptions,
import type { BinaryReadOptions } from "@protobuf-ts/runtime"; IBinaryReader,
import type { IBinaryReader } from "@protobuf-ts/runtime"; IBinaryWriter,
import { UnknownFieldHandler } from "@protobuf-ts/runtime"; PartialMessage,
import type { PartialMessage } from "@protobuf-ts/runtime"; } from '@protobuf-ts/runtime';
import { reflectionMergePartial } from "@protobuf-ts/runtime"; import { MessageType, reflectionMergePartial, UnknownFieldHandler, WireType } from '@protobuf-ts/runtime';
import { MessageType } from "@protobuf-ts/runtime";
/** /**
* @generated from protobuf message SysMessage.EmojiLikeToOthersWrapper1 * @generated from protobuf message SysMessage.EmojiLikeToOthersWrapper1
*/ */
@@ -62,6 +62,27 @@ export interface EmojiLikeToOthersAttributes {
* @generated from protobuf field: string senderUid = 4; * @generated from protobuf field: string senderUid = 4;
*/ */
senderUid: string; senderUid: string;
/**
* @generated from protobuf field: SysMessage.EmojiLikeToOthersAttributes.Operation operation = 5;
*/
operation: EmojiLikeToOthersAttributes_Operation;
}
/**
* @generated from protobuf enum SysMessage.EmojiLikeToOthersAttributes.Operation
*/
export enum EmojiLikeToOthersAttributes_Operation {
/**
* @generated from protobuf enum value: FALLBACK = 0;
*/
FALLBACK = 0,
/**
* @generated from protobuf enum value: LIKE = 1;
*/
LIKE = 1,
/**
* @generated from protobuf enum value: UNLIKE = 2;
*/
UNLIKE = 2
} }
// @generated message type with reflection information, may provide speed optimized methods // @generated message type with reflection information, may provide speed optimized methods
class EmojiLikeToOthersWrapper1$Type extends MessageType<EmojiLikeToOthersWrapper1> { class EmojiLikeToOthersWrapper1$Type extends MessageType<EmojiLikeToOthersWrapper1> {
@@ -260,13 +281,15 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
constructor() { constructor() {
super("SysMessage.EmojiLikeToOthersAttributes", [ super("SysMessage.EmojiLikeToOthersAttributes", [
{ no: 1, name: "emojiId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 1, name: "emojiId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "senderUid", kind: "scalar", T: 9 /*ScalarType.STRING*/ } { no: 4, name: "senderUid", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 5, name: "operation", kind: "enum", T: () => ["SysMessage.EmojiLikeToOthersAttributes.Operation", EmojiLikeToOthersAttributes_Operation] }
]); ]);
} }
create(value?: PartialMessage<EmojiLikeToOthersAttributes>): EmojiLikeToOthersAttributes { create(value?: PartialMessage<EmojiLikeToOthersAttributes>): EmojiLikeToOthersAttributes {
const message = globalThis.Object.create((this.messagePrototype!)); const message = globalThis.Object.create((this.messagePrototype!));
message.emojiId = ""; message.emojiId = "";
message.senderUid = ""; message.senderUid = "";
message.operation = 0;
if (value !== undefined) if (value !== undefined)
reflectionMergePartial<EmojiLikeToOthersAttributes>(this, message, value); reflectionMergePartial<EmojiLikeToOthersAttributes>(this, message, value);
return message; return message;
@@ -282,6 +305,9 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
case /* string senderUid */ 4: case /* string senderUid */ 4:
message.senderUid = reader.string(); message.senderUid = reader.string();
break; break;
case /* SysMessage.EmojiLikeToOthersAttributes.Operation operation */ 5:
message.operation = reader.int32();
break;
default: default:
let u = options.readUnknownField; let u = options.readUnknownField;
if (u === "throw") if (u === "throw")
@@ -300,6 +326,9 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
/* string senderUid = 4; */ /* string senderUid = 4; */
if (message.senderUid !== "") if (message.senderUid !== "")
writer.tag(4, WireType.LengthDelimited).string(message.senderUid); writer.tag(4, WireType.LengthDelimited).string(message.senderUid);
/* SysMessage.EmojiLikeToOthersAttributes.Operation operation = 5; */
if (message.operation !== 0)
writer.tag(5, WireType.Varint).int32(message.operation);
let u = options.writeUnknownFields; let u = options.writeUnknownFields;
if (u !== false) if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);

View File

@@ -1,15 +1,15 @@
// @generated by protobuf-ts 2.9.4 // @generated by protobuf-ts 2.9.4
// @generated from protobuf file "GreyTipWrapper.proto" (package "SysMessage", syntax proto3) // @generated from protobuf file "GreyTipWrapper.proto" (package "SysMessage", syntax proto3)
// tslint:disable // tslint:disable
import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; import type {
import type { IBinaryWriter } from "@protobuf-ts/runtime"; BinaryReadOptions,
import { WireType } from "@protobuf-ts/runtime"; BinaryWriteOptions,
import type { BinaryReadOptions } from "@protobuf-ts/runtime"; IBinaryReader,
import type { IBinaryReader } from "@protobuf-ts/runtime"; IBinaryWriter,
import { UnknownFieldHandler } from "@protobuf-ts/runtime"; PartialMessage,
import type { PartialMessage } from "@protobuf-ts/runtime"; } from '@protobuf-ts/runtime';
import { reflectionMergePartial } from "@protobuf-ts/runtime"; import { MessageType, reflectionMergePartial, UnknownFieldHandler, WireType } from '@protobuf-ts/runtime';
import { MessageType } from "@protobuf-ts/runtime";
/** /**
* @generated from protobuf message SysMessage.GreyTipWrapper * @generated from protobuf message SysMessage.GreyTipWrapper
*/ */

View File

@@ -1,15 +1,15 @@
// @generated by protobuf-ts 2.9.4 // @generated by protobuf-ts 2.9.4
// @generated from protobuf file "SysMessage.proto" (package "SysMessage", syntax proto3) // @generated from protobuf file "SysMessage.proto" (package "SysMessage", syntax proto3)
// tslint:disable // tslint:disable
import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; import type {
import type { IBinaryWriter } from "@protobuf-ts/runtime"; BinaryReadOptions,
import { WireType } from "@protobuf-ts/runtime"; BinaryWriteOptions,
import type { BinaryReadOptions } from "@protobuf-ts/runtime"; IBinaryReader,
import type { IBinaryReader } from "@protobuf-ts/runtime"; IBinaryWriter,
import { UnknownFieldHandler } from "@protobuf-ts/runtime"; PartialMessage,
import type { PartialMessage } from "@protobuf-ts/runtime"; } from '@protobuf-ts/runtime';
import { reflectionMergePartial } from "@protobuf-ts/runtime"; import { MessageType, reflectionMergePartial, UnknownFieldHandler, WireType } from '@protobuf-ts/runtime';
import { MessageType } from "@protobuf-ts/runtime";
/** /**
* @generated from protobuf message SysMessage.SysMessage * @generated from protobuf message SysMessage.SysMessage
*/ */

View File

@@ -1,5 +1,7 @@
import { NodeIKernelFileAssistantListener } from '@/core';
export interface NodeIKernelFileAssistantService { export interface NodeIKernelFileAssistantService {
addKernelFileAssistantListener(arg1: unknown[]): unknown; addKernelFileAssistantListener(listener: NodeIKernelFileAssistantListener): unknown;
removeKernelFileAssistantListener(arg1: unknown[]): unknown; removeKernelFileAssistantListener(arg1: unknown[]): unknown;
@@ -9,7 +11,7 @@ export interface NodeIKernelFileAssistantService {
getFileSessionList(): unknown; getFileSessionList(): unknown;
searchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown; searchFile(keywords: string[], params: { resultType: number, pageLimit: number }, resultId: number): number;
resetSearchFileSortType(arg1: unknown, arg2: unknown, arg3: unknown): unknown; resetSearchFileSortType(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
@@ -17,7 +19,7 @@ export interface NodeIKernelFileAssistantService {
cancelSearchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown; cancelSearchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
downloadFile(arg1: unknown[]): unknown; downloadFile(fileIds: string[]): { result: number, errMsg: string };
forwardFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown; forwardFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
@@ -32,4 +34,4 @@ export interface NodeIKernelFileAssistantService {
saveAsWithRename(arg1: unknown, arg2: unknown, arg3: unknown): unknown; saveAsWithRename(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
isNull(): boolean; isNull(): boolean;
} }

View File

@@ -182,14 +182,13 @@ export interface NodeIKernelGroupService {
destroyGroup(groupCode: string): void; destroyGroup(groupCode: string): void;
//获取单屏群通知列表 getSingleScreenNotifies(doubted: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
getSingleScreenNotifies(force: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
clearGroupNotifies(groupCode: string): void; clearGroupNotifies(groupCode: string): void;
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[], unknown: string): Promise<unknown>;
//第三个参数 Array<Type> //第三个参数 Array<Type>
// this.fileId = ""; // this.fileId = "";

View File

@@ -2,8 +2,7 @@ import { NapCatPathWrapper } from '@/common/path';
import { LogWrapper } from '@/common/log'; import { LogWrapper } from '@/common/log';
import { proxiedListenerOf } from '@/common/proxy-handler'; import { proxiedListenerOf } from '@/common/proxy-handler';
import { QQBasicInfoWrapper } from '@/common/qq-basic-info'; import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
import { loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from '@/core'; import { InstanceContext, loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from '@/core';
import { InstanceContext } from '@/core';
import { SelfInfo } from '@/core/entities'; import { SelfInfo } from '@/core/entities';
import { NodeIKernelLoginListener } from '@/core/listeners'; import { NodeIKernelLoginListener } from '@/core/listeners';
import { NodeIKernelLoginService } from '@/core/services'; import { NodeIKernelLoginService } from '@/core/services';

View File

@@ -1,4 +1,5 @@
import { createServer } from 'node:net'; import { createServer } from 'node:net';
export class NewAdapterNetwork { export class NewAdapterNetwork {
constructor(public host: number, public port: number) { } constructor(public host: number, public port: number) { }
async open() { async open() {
@@ -11,8 +12,8 @@ export class NewAdapterNetwork {
}); });
socket.on('connect', () => { socket.on('connect', () => {
}) });
}); });
server.listen(this.port, this.host); server.listen(this.port, this.host);
} }
} }

View File

@@ -7,10 +7,10 @@ const SchemaData = {
type: 'object', type: 'object',
properties: { properties: {
status: { type: ['number', 'string'] }, status: { type: ['number', 'string'] },
extStatus: { type: ['number', 'string'] }, ext_status: { type: ['number', 'string'] },
batteryStatus: { type: ['number', 'string'] }, battery_status: { type: ['number', 'string'] },
}, },
required: ['status', 'extStatus', 'batteryStatus'], required: ['status', 'ext_status', 'battery_status'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
@@ -23,8 +23,8 @@ export class SetOnlineStatus extends BaseAction<Payload, null> {
const NTQQUserApi = this.core.apis.UserApi; const NTQQUserApi = this.core.apis.UserApi;
const ret = await NTQQUserApi.setSelfOnlineStatus( const ret = await NTQQUserApi.setSelfOnlineStatus(
parseInt(payload.status.toString()), parseInt(payload.status.toString()),
parseInt(payload.extStatus.toString()), parseInt(payload.ext_status.toString()),
parseInt(payload.batteryStatus.toString()), parseInt(payload.battery_status.toString()),
); );
if (ret.result !== 0) { if (ret.result !== 0) {
throw new Error('设置在线状态失败'); throw new Error('设置在线状态失败');

View File

@@ -1,8 +1,7 @@
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 { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
export interface GetFilePayload { export interface GetFilePayload {
@@ -29,100 +28,80 @@ 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 peerUin = uuidData.high; const contextMsgFile = FileNapCatOneBotUUID.decode(payload.file);
const msgId = uuidData.low; if (contextMsgFile) {
const isGroup: boolean = !!(await NTQQGroupApi.getGroups(false)).find(e => e.groupCode == peerUin); const { peer, msgId, elementId } = contextMsgFile;
let peer: Peer | undefined; const downloadPath = await NTQQFileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
//识别Peer const mixElement = (await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]))?.msgList
if (isGroup) { .find(msg => msg.msgId === msgId)?.elements.find(e => e.elementId === elementId);
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin }; const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
} if (!mixElementInner) throw new Error('element not found');
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin); const fileSize = mixElementInner.fileSize?.toString() ?? '';
if (PeerUid) { const fileName = mixElementInner.fileName ?? '';
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) {
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
} else {
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 { 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; //群文件模式
if (NTSearchNameResult.length !== 0) { const contextModelIdFile = FileNapCatOneBotUUID.decodeModelId(payload.file);
const MsgId = NTSearchNameResult[0].msgId; if (contextModelIdFile) {
let peer: Peer | undefined = undefined; const { peer, modelId } = contextModelIdFile;
if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) { const downloadPath = await NTQQFileApi.downloadFileForModelId(peer, modelId, '');
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
}
if (!peer) {
throw new Error('chattype not support');
}
const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList;
if (!msgList || msgList.length == 0) {
throw new Error('msg not found');
}
const msg = msgList[0];
const file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType);
if (file.length == 0) {
throw new Error('file not found');
}
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, file[0].elementId, '', '');
const res: GetFileResponse = { const res: GetFileResponse = {
file: downloadPath, file: downloadPath,
url: downloadPath, url: downloadPath,
file_size: NTSearchNameResult[0].fileSize.toString(), file_size: '',
file_name: NTSearchNameResult[0].fileName, file_name: '',
}; };
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;
} }
//搜索名字模式
const searchResult = (await NTQQFileApi.searchForFile([payload.file]));
if (searchResult) {
const downloadPath = await NTQQFileApi.downloadFileById(searchResult.id, parseInt(searchResult.fileSize));
const res: GetFileResponse = {
file: downloadPath,
url: downloadPath,
file_size: searchResult.fileSize.toString(),
file_name: searchResult.fileName,
};
if (this.obContext.configLoader.configData.enableLocalFile2Url && downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
throw new Error('文件下载失败. ' + e);
}
}
return res;
}
throw new Error('file not found'); throw new Error('file not found');
} }
} }

View File

@@ -17,8 +17,7 @@ export class GetGroupFileCount extends BaseAction<Payload, { count: number }> {
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const NTQQGroupApi = this.core.apis.GroupApi; const ret = await this.core.apis.GroupApi.getGroupFileCount([payload.group_id?.toString()]);
const ret = await NTQQGroupApi.GetGroupFileCount([payload.group_id?.toString()]);
return { count: ret.groupFileCounts[0] }; return { count: ret.groupFileCounts[0] };
} }
} }

View File

@@ -1,13 +1,15 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FileNapCatOneBotUUID } from '@/common/helper';
const SchemaData = { 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,15 +22,30 @@ 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,
}).catch((e) => { ...param
}).catch(() => {
return []; return [];
}); });
ret.forEach((e) => {
const fileModelId = e?.fileInfo?.fileModelId;
if (fileModelId)
e.fileInfo!.fileId = FileNapCatOneBotUUID.encodeModelId({
chatType: 2,
peerUid: payload.group_id.toString()
}, fileModelId);
});
return { FileList: ret }; return { FileList: ret };
} }
} }

View File

@@ -0,0 +1,32 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NapCatOneBot11Adapter } from '@/onebot';
import { NapCatCore } from '@/core';
import { SetGroupFileFolder } from '@/onebot/action/file/SetGroupFileFolder';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
folder_name: { type: 'string' },
},
required: ['group_id', 'folder_name'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class CreateGroupFileFolder extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_CreateGroupFileFolder;
payloadSchema = SchemaData;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore,
private ncSetGroupFileFolderImpl: SetGroupFileFolder) {
super(obContext, core);
}
async _handle(payload: Payload) {
await this.ncSetGroupFileFolderImpl._handle(payload);
return null;
}
}

View File

@@ -0,0 +1,32 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import { DelGroupFile } from '@/onebot/action/file/DelGroupFile';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
file_id: { type: 'string' },
},
required: ['group_id', 'file_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class DeleteGroupFile extends BaseAction<Payload, null> {
actionName = ActionName.GOCQHTTP_DeleteGroupFile;
payloadSchema = SchemaData;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore,
private ncDelGroupFileImpl: DelGroupFile) {
super(obContext, core);
}
async _handle(payload: Payload) {
await this.ncDelGroupFileImpl._handle(payload);
return null;
}
}

View File

@@ -0,0 +1,32 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import { DelGroupFileFolder } from '@/onebot/action/file/DelGroupFileFolder';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' },
},
required: ['group_id', 'folder_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class DeleteGroupFileFolder extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder;
payloadSchema = SchemaData;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore,
private ncDelGroupFileFolderImpl: DelGroupFileFolder) {
super(obContext, core);
}
async _handle(payload: Payload) {
await this.ncDelGroupFileFolderImpl._handle(payload);
return null;
}
}

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

@@ -0,0 +1,35 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
},
required: ['group_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupFileSystemInfo extends BaseAction<Payload, {
file_count: number,
limit_count: number, // unimplemented
used_space: number, // todo: unimplemented, but can be implemented later
total_space: number, // unimplemented, 10 GB by default
}> {
actionName = ActionName.GoCQHTTP_GetGroupFileSystemInfo;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
return {
file_count:
(await this.core.apis.GroupApi
.getGroupFileCount([payload.group_id.toString()]))
.groupFileCounts[0],
limit_count: 10000,
used_space: 0,
total_space: 10 * 1024 * 1024 * 1024,
};
}
}

View File

@@ -0,0 +1,52 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NapCatOneBot11Adapter, OB11GroupFile } from '@/onebot';
import { NapCatCore } from '@/core';
import { GetGroupRootFiles } from '@/onebot/action/go-cqhttp/GetGroupRootFiles';
import { OB11Entities } from '@/onebot/entities';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' },
},
required: ['group_id', 'folder_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupFilesByFolder extends BaseAction<Payload, {
files: OB11GroupFile[],
folders: [] // QQ does not allow nested folders
}> {
actionName = ActionName.GoCQHTTP_GetGroupFilesByFolder;
payloadSchema = SchemaData;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore,
private getGroupRootFilesImpl: GetGroupRootFiles) {
super(obContext, core);
}
async _handle(payload: Payload) {
const folder = (await this.getGroupRootFilesImpl._handle({ group_id: payload.group_id }))
.folders.find(folder => folder.folder_id === payload.folder_id);
if (!folder) {
throw new Error('Folder not found');
}
const ret = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: folder.total_file_count,
startIndex: 0,
sortOrder: 2,
showOnlinedocFolder: 0,
folderId: payload.folder_id,
}).catch(() => []);
return {
files: ret.filter(item => item.fileInfo)
.map(item => OB11Entities.file(item.peerId, item.fileInfo!)),
folders: [] as [],
};
}
}

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

@@ -0,0 +1,47 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { NapCatOneBot11Adapter, OB11GroupFile, OB11GroupFileFolder } from '@/onebot';
import { NapCatCore } from '@/core';
import { GetGroupFileCount } from '@/onebot/action/file/GetGroupFileCount';
import { OB11Entities } from '@/onebot/entities';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
},
required: ['group_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupRootFiles extends BaseAction<Payload, {
files: OB11GroupFile[],
folders: OB11GroupFileFolder[],
}> {
actionName = ActionName.GoCQHTTP_GetGroupRootFiles;
payloadSchema = SchemaData;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore,
private ncGetGroupFileCountImpl: GetGroupFileCount) {
super(obContext, core);
}
async _handle(payload: Payload) {
const ret = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: (await this.ncGetGroupFileCountImpl._handle({ group_id: payload.group_id.toString() })).count,
startIndex: 0,
sortOrder: 2,
showOnlinedocFolder: 0,
}).catch(() => []);
return {
files: ret.filter(item => item.fileInfo)
.map(item => OB11Entities.file(item.peerId, item.fileInfo!)),
folders: ret.filter(item => item.folderInfo)
.map(item => OB11Entities.folder(item.peerId, item.folderInfo!)),
};
}
}

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,99 @@
import { GroupEssenceMsgRet } from '@/core'; import { ChatType, 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;
private async msgSeqToMsgId(peer: Peer, msgSeq: string, msgRandom: string) {
const replyMsgList = (await this.core.apis.MsgApi.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;
if (!ret) { const msglist = (await NTQQWebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
if (!msglist) {
throw new Error('获取失败'); throw new Error('获取失败');
} }
return ret; return await Promise.all(msglist.map(async (msg) => {
const msgOriginData = await this.msgSeqToMsgId({
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
}, 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: msg.msg_content.map((msg) => {
if (msg.msg_type === 1) {
return {
type: 'text',
data: {
text: msg?.text
}
};
} else if (msg.msg_type === 3) {
return {
type: 'image',
data: {
url: msg?.image_url,
}
};
}
return undefined;
}).filter(e => e !== undefined),
};
}));
} }
} }

View File

@@ -0,0 +1,39 @@
import { GroupNotifyMsgStatus } from '@/core';
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'] },
},
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupIgnoredNotifies extends BaseAction<void, any> {
actionName = ActionName.GetGroupIgnoredNotifies;
async _handle(payload: void) {
const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi;
const ignoredNotifies = await NTQQGroupApi.getSingleScreenNotifies(true, 10);
const retData: any = {
join_requests: await Promise.all(
ignoredNotifies
.filter(notify => notify.type === 7)
.map(async SSNotify => ({
request_id: SSNotify.seq,
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
}))),
};
return retData;
}
}

View File

@@ -31,18 +31,18 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache), NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
NTQQUserApi.getUserDetailInfo(uid), NTQQUserApi.getUserDetailInfo(uid),
]); ]);
if (member.status !== 'fulfilled') throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`); if (member.status !== 'fulfilled') throw new Error(`群(${payload.group_id})成员${payload.user_id}获取失败 ${member.reason}`);
if (!member.value) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
if (info.status === 'fulfilled') { if (info.status === 'fulfilled') {
this.core.context.logger.logDebug('群成员详细信息结果', info.value); Object.assign(member.value, info.value);
Object.assign(member, info.value);
} else { } else {
this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息 ${info.reason}`); this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息 ${info.reason}`);
} }
const date = Math.round(Date.now() / 1000); const date = Math.round(Date.now() / 1000);
const retMember = OB11Entities.groupMember(payload.group_id.toString(), member.value as GroupMember); const retMember = OB11Entities.groupMember(payload.group_id.toString(), member.value as GroupMember);
const Member = await this.core.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id); const Member = await this.core.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id);
retMember.last_sent_time = parseInt(Member?.lastSpeakTime || date.toString()); retMember.last_sent_time = parseInt(Member?.lastSpeakTime ?? date.toString());
retMember.join_time = parseInt(Member?.joinTime || date.toString()); retMember.join_time = parseInt(Member?.joinTime ?? date.toString());
return retMember; return retMember;
} }
} }

View File

@@ -1,50 +0,0 @@
import { GroupNotifyMsgStatus } from '@/core';
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'] },
},
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupSystemMsg extends BaseAction<void, any> {
actionName = ActionName.GetGroupSystemMsg;
async _handle(payload: void) {
const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi;
// 默认10条 该api未完整实现 包括响应数据规范化 类型规范化
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(10);
const retData: any = { InvitedRequest: [], join_requests: [] };
for (const SSNotify of SingleScreenNotifies) {
if (SSNotify.type == 1) {
retData.InvitedRequest.push({
request_id: SSNotify.seq,
invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
} else if (SSNotify.type == 7) {
retData.join_requests.push({
request_id: SSNotify.seq,
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
}
}
return retData;
}
}

View File

@@ -52,7 +52,7 @@ import { GetFriendWithCategory } from './extends/GetFriendWithCategory';
import { SendGroupNotice } from './go-cqhttp/SendGroupNotice'; import { SendGroupNotice } from './go-cqhttp/SendGroupNotice';
import { GetGroupHonorInfo } from './go-cqhttp/GetGroupHonorInfo'; import { GetGroupHonorInfo } from './go-cqhttp/GetGroupHonorInfo';
import { GoCQHTTPHandleQuickAction } from './go-cqhttp/QuickAction'; import { GoCQHTTPHandleQuickAction } from './go-cqhttp/QuickAction';
import { GetGroupSystemMsg } from './group/GetGroupSystemMsg'; import { GetGroupIgnoredNotifies } from './group/GetGroupIgnoredNotifies';
import { GetOnlineClient } from './go-cqhttp/GetOnlineClient'; import { GetOnlineClient } from './go-cqhttp/GetOnlineClient';
import { IOCRImage, OCRImage } from './extends/OCRImage'; import { IOCRImage, OCRImage } from './extends/OCRImage';
import { GetGroupFileCount } from './file/GetGroupFileCount'; import { GetGroupFileCount } from './file/GetGroupFileCount';
@@ -82,10 +82,22 @@ 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'; import { GetGroupInfoEx } from './extends/GetGroupInfoEx';
import { DeleteGroupFile } from '@/onebot/action/go-cqhttp/DeleteGroupFile';
import { CreateGroupFileFolder } from '@/onebot/action/go-cqhttp/CreateGroupFileFolder';
import { DeleteGroupFileFolder } from '@/onebot/action/go-cqhttp/DeleteGroupFileFolder';
import { GetGroupFileSystemInfo } from '@/onebot/action/go-cqhttp/GetGroupFileSystemInfo';
import { GetGroupRootFiles } from '@/onebot/action/go-cqhttp/GetGroupRootFiles';
import { GetGroupFilesByFolder } from '@/onebot/action/go-cqhttp/GetGroupFilesByFolder';
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 ncDelGroupFile = new DelGroupFile(obContext, core);
const ncSetGroupFileFolder = new SetGroupFileFolder(obContext, core);
const ncDelGroupFileFolder = new DelGroupFileFolder(obContext, core);
const ncGetGroupFileCount = new GetGroupFileCount(obContext, core);
const goCqHttpGetGroupRootFiles = new GetGroupRootFiles(obContext, core, ncGetGroupFileCount);
const actionHandlers = [ const actionHandlers = [
new GetGroupInfoEx(obContext, core), new GetGroupInfoEx(obContext, core),
new FetchEmojiLike(obContext, core), new FetchEmojiLike(obContext, core),
@@ -101,11 +113,11 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new MarkPrivateMsgAsRead(obContext, core), new MarkPrivateMsgAsRead(obContext, core),
new SetQQAvatar(obContext, core), new SetQQAvatar(obContext, core),
new TranslateEnWordToZn(obContext, core), new TranslateEnWordToZn(obContext, core),
new GetGroupFileCount(obContext, core), ncGetGroupFileCount,
new GetGroupFileList(obContext, core), new GetGroupFileList(obContext, core),
new SetGroupFileFolder(obContext, core), ncSetGroupFileFolder,
new DelGroupFile(obContext, core), ncDelGroupFile,
new DelGroupFileFolder(obContext, core), ncDelGroupFileFolder,
// onebot11 // onebot11
new SendLike(obContext, core), new SendLike(obContext, core),
new GetMsg(obContext, core), new GetMsg(obContext, core),
@@ -159,7 +171,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GoCQHTTPGetForwardMsgAction(obContext, core), new GoCQHTTPGetForwardMsgAction(obContext, core),
new GetFriendMsgHistory(obContext, core), new GetFriendMsgHistory(obContext, core),
new GoCQHTTPHandleQuickAction(obContext, core), new GoCQHTTPHandleQuickAction(obContext, core),
new GetGroupSystemMsg(obContext, core), new GetGroupIgnoredNotifies(obContext, core),
new DelEssenceMsg(obContext, core), new DelEssenceMsg(obContext, core),
new SetEssenceMsg(obContext, core), new SetEssenceMsg(obContext, core),
new GetRecentContact(obContext, core), new GetRecentContact(obContext, core),
@@ -173,6 +185,12 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SetInputStatus(obContext, core), new SetInputStatus(obContext, core),
new GetCSRF(obContext, core), new GetCSRF(obContext, core),
new DelGroupNotice(obContext, core), new DelGroupNotice(obContext, core),
new DeleteGroupFile(obContext, core, ncDelGroupFile),
new CreateGroupFileFolder(obContext, core, ncSetGroupFileFolder),
new DeleteGroupFileFolder(obContext, core, ncDelGroupFileFolder),
new GetGroupFileSystemInfo(obContext, core),
goCqHttpGetGroupRootFiles,
new GetGroupFilesByFolder(obContext, core, goCqHttpGetGroupRootFiles),
]; ];
const actionMap = new Map(); const actionMap = new Map();
for (const action of actionHandlers) { for (const action of actionHandlers) {

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

@@ -83,11 +83,17 @@ export enum ActionName {
MarkPrivateMsgAsRead = 'mark_private_msg_as_read', MarkPrivateMsgAsRead = 'mark_private_msg_as_read',
MarkGroupMsgAsRead = 'mark_group_msg_as_read', MarkGroupMsgAsRead = 'mark_group_msg_as_read',
GoCQHTTP_UploadGroupFile = 'upload_group_file', GoCQHTTP_UploadGroupFile = 'upload_group_file',
GOCQHTTP_DeleteGroupFile = 'delete_group_file',
GoCQHTTP_CreateGroupFileFolder = 'create_group_file_folder',
GoCQHTTP_DeleteGroupFileFolder = 'delete_group_file_folder',
GoCQHTTP_GetGroupFileSystemInfo = 'get_group_file_system_info',
GoCQHTTP_GetGroupRootFiles = 'get_group_root_files',
GoCQHTTP_GetGroupFilesByFolder = 'get_group_files_by_folder',
GoCQHTTP_DownloadFile = 'download_file', GoCQHTTP_DownloadFile = 'download_file',
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history', GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history',
GoCQHTTP_GetForwardMsg = 'get_forward_msg', GoCQHTTP_GetForwardMsg = 'get_forward_msg',
GetFriendMsgHistory = 'get_friend_msg_history', GetFriendMsgHistory = 'get_friend_msg_history',
GetGroupSystemMsg = 'get_group_system_msg', GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
GetOnlineClient = 'get_online_clients', GetOnlineClient = 'get_online_clients',
OCRImage = 'ocr_image', OCRImage = 'ocr_image',
IOCRImage = '.ocr_image', IOCRImage = '.ocr_image',

View File

@@ -18,6 +18,7 @@ import { OB11GroupUploadNoticeEvent } from '@/onebot/event/notice/OB11GroupUploa
import { OB11GroupPokeEvent } from '@/onebot/event/notice/OB11PokeEvent'; import { OB11GroupPokeEvent } from '@/onebot/event/notice/OB11PokeEvent';
import { OB11GroupEssenceEvent } from '@/onebot/event/notice/OB11GroupEssenceEvent'; import { OB11GroupEssenceEvent } from '@/onebot/event/notice/OB11GroupEssenceEvent';
import { OB11GroupTitleEvent } from '@/onebot/event/notice/OB11GroupTitleEvent'; import { OB11GroupTitleEvent } from '@/onebot/event/notice/OB11GroupTitleEvent';
import { FileNapCatOneBotUUID } from '@/common/helper';
export class OneBotGroupApi { export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter; obContext: NapCatOneBot11Adapter;
@@ -76,7 +77,10 @@ export class OneBotGroupApi {
this.core, this.core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''), parseInt(msg.peerUid), parseInt(msg.senderUin || ''),
{ {
id: element.fileElement.fileUuid!, id: FileNapCatOneBotUUID.encode({
chatType: ChatType.KCHATTYPEGROUP,
peerUid: msg.peerUid,
}, msg.msgId, element.elementId),
name: element.fileElement.fileName, name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize), size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0, busid: element.fileElement.fileBizId || 0,
@@ -116,18 +120,22 @@ export class OneBotGroupApi {
const searchParams = new URL(json.items[0].jp).searchParams; const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!; const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode'); const Group = searchParams.get('groupCode');
if (!Group) return;
// const businessId = searchParams.get('businessid'); // const businessId = searchParams.get('businessid');
const Peer = { const Peer = {
guildId: '', guildId: '',
chatType: ChatType.KCHATTYPEGROUP, chatType: ChatType.KCHATTYPEGROUP,
peerUid: Group!, peerUid: Group,
}; };
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true); const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
const msgList = (await this.core.apis.WebApi.getGroupEssenceMsgAll(Group)).flatMap((e) => e.data.msg_list);
const realMsg = msgList.find((e) => e.msg_seq.toString() == msgSeq);
return new OB11GroupEssenceEvent( return new OB11GroupEssenceEvent(
this.core, this.core,
parseInt(msg.peerUid), parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!,
parseInt(msgData.msgList[0].senderUin), parseInt(msgData.msgList[0].senderUin),
parseInt(realMsg?.add_digest_uin ?? '0'),
); );
// 获取MsgSeq+Peer可获取具体消息 // 获取MsgSeq+Peer可获取具体消息
} }

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,
@@ -9,7 +9,8 @@ import {
FaceType, FaceType,
IdMusicSignPostData, IdMusicSignPostData,
MessageElement, MessageElement,
NapCatCore, NTGrayTipElementSubTypeV2, NapCatCore,
NTGrayTipElementSubTypeV2,
Peer, Peer,
RawMessage, RawMessage,
SendMessageElement, SendMessageElement,
@@ -98,14 +99,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 +123,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 +148,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 +195,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 +223,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 +245,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 +257,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 +321,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 +352,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 +389,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 +847,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

@@ -10,12 +10,12 @@ import {
QuickActionGroupMessage, QuickActionGroupMessage,
QuickActionGroupRequest, QuickActionGroupRequest,
} from '@/onebot'; } from '@/onebot';
import { ChatType, GroupRequestOperateTypes, NapCatCore, Peer } from '@/core'; import { GroupRequestOperateTypes, NapCatCore, Peer } from '@/core';
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest'; import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest'; import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { ContextMode, normalize } from '@/onebot/action/msg/SendMsg'; import { ContextMode, createContext, normalize } from '@/onebot/action/msg/SendMsg';
import { isNull } from '@/common/helper'; import { isNull } from '@/common/helper';
import { createContext } from '@/onebot/action/msg/SendMsg';
export class OneBotQuickActionApi { export class OneBotQuickActionApi {
constructor( constructor(
public obContext: NapCatOneBot11Adapter, public obContext: NapCatOneBot11Adapter,
@@ -78,7 +78,7 @@ export class OneBotQuickActionApi {
sendElements, sendElements,
deleteAfterSentFiles, deleteAfterSentFiles,
} = await this.obContext.apis.MsgApi.createSendElements(replyMessage, peer); } = await this.obContext.apis.MsgApi.createSendElements(replyMessage, peer);
this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(this.core.context.logger.logError); this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles, false).then().catch(this.core.context.logger.logError.bind(this.core.context.logger));
} }
} }
@@ -88,13 +88,13 @@ export class OneBotQuickActionApi {
request.flag, request.flag,
quickAction.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject, quickAction.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
quickAction.reason, quickAction.reason,
).catch(this.core.context.logger.logError); ).catch(this.core.context.logger.logError.bind(this.core.context.logger));
} }
} }
async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) { async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) {
if (!isNull(quickAction.approve)) { if (!isNull(quickAction.approve)) {
this.core.apis.FriendApi.handleFriendRequest(request.flag, !!quickAction.approve).then().catch(this.core.context.logger.logError); this.core.apis.FriendApi.handleFriendRequest(request.flag, !!quickAction.approve).then().catch(this.core.context.logger.logError.bind(this.core.context.logger));
} }
} }
} }

View File

@@ -1,6 +1,14 @@
import { calcQQLevel } from '@/common/helper'; import { calcQQLevel, FileNapCatOneBotUUID } from '@/common/helper';
import { Friend, FriendV2, Group, GroupMember, SelfInfo, Sex, User } from '@/core'; import { Friend, FriendV2, Group, GroupFileInfoUpdateParamType, GroupMember, SelfInfo, Sex, User } from '@/core';
import { OB11Group, OB11GroupMember, OB11GroupMemberRole, OB11User, OB11UserSex } from './types'; import {
OB11Group,
OB11GroupFile,
OB11GroupFileFolder,
OB11GroupMember,
OB11GroupMemberRole,
OB11User,
OB11UserSex,
} from './types';
export class OB11Entities { export class OB11Entities {
static selfInfo(selfInfo: SelfInfo): OB11User { static selfInfo(selfInfo: SelfInfo): OB11User {
@@ -97,4 +105,32 @@ export class OB11Entities {
static groups(groups: Group[]): OB11Group[] { static groups(groups: Group[]): OB11Group[] {
return groups.map(OB11Entities.group); return groups.map(OB11Entities.group);
} }
static file(peerId: string, file: Exclude<GroupFileInfoUpdateParamType['item'][0]['fileInfo'], undefined>): OB11GroupFile {
return {
group_id: parseInt(peerId),
file_id: FileNapCatOneBotUUID.encodeModelId({ chatType: 2, peerUid: peerId }, file.fileModelId),
file_name: file.fileName,
busid: file.busId,
size: parseInt(file.fileSize),
upload_time: file.uploadTime,
dead_time: file.deadTime,
modify_time: file.modifyTime,
download_times: file.downloadTimes,
uploader: parseInt(file.uploaderUin),
uploader_name: file.uploaderName,
};
}
static folder(peerId: string, folder: Exclude<GroupFileInfoUpdateParamType['item'][0]['folderInfo'], undefined>): OB11GroupFileFolder {
return {
group_id: parseInt(peerId),
folder_id: folder.folderId,
folder_name: folder.folderName,
create_time: folder.createTime,
creator: parseInt(folder.createUin),
creator_name: folder.creatorName,
total_file_count: folder.totalFileCount,
};
}
} }

View File

@@ -5,11 +5,14 @@ export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent {
notice_type = 'essence'; notice_type = 'essence';
message_id: number; message_id: number;
sender_id: number; sender_id: number;
operator_id: number;
sub_type: 'add' | 'delete' = 'add'; sub_type: 'add' | 'delete' = 'add';
constructor(core: NapCatCore, groupId: number, message_id: number, sender_id: number) {
constructor(core: NapCatCore, groupId: number, message_id: number, sender_id: number, operator_id: number) {
super(core, groupId, sender_id); super(core, groupId, sender_id);
this.group_id = groupId; this.group_id = groupId;
this.operator_id = operator_id;
this.message_id = message_id; this.message_id = message_id;
this.sender_id = sender_id; this.sender_id = sender_id;
} }

View File

@@ -139,7 +139,7 @@ export class NapCatOneBot11Adapter {
initRecentContactListener() { initRecentContactListener() {
const recentContactListener = new NodeIKernelRecentContactListener(); const recentContactListener = new NodeIKernelRecentContactListener();
recentContactListener.onRecentContactNotification = function(msgList: any[] /* arg0: { msgListUnreadCnt: string }, arg1: number */) { recentContactListener.onRecentContactNotification = function (msgList: any[] /* arg0: { msgListUnreadCnt: string }, arg1: number */) {
msgList.forEach((msg) => { msgList.forEach((msg) => {
if (msg.chatType == ChatType.KCHATTYPEGROUP) { if (msg.chatType == ChatType.KCHATTYPEGROUP) {
// log("recent contact", msgList, arg0, arg1); // log("recent contact", msgList, arg0, arg1);
@@ -248,19 +248,23 @@ export class NapCatOneBot11Adapter {
return; return;
} }
const { msgType, subType, subSubType } = sysMsg.msgSpec[0]; const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
if (msgType === 732 && subType === 16 && subSubType === 16 ) { if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7))); const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) { if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1 const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest) .fromBinary(greyTip.rest)
.wrapper! .wrapper!
.body!; .body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent( const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(), greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid), await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
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 +287,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 +315,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 +348,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);
@@ -370,6 +374,7 @@ export class NapCatOneBot11Adapter {
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,
@@ -468,6 +473,18 @@ export class NapCatOneBot11Adapter {
); );
this.networkManager.emitEvent(groupInviteEvent) this.networkManager.emitEvent(groupInviteEvent)
.catch(e => this.context.logger.logError('处理邀请本人加群失败', e)); .catch(e => this.context.logger.logError('处理邀请本人加群失败', e));
} else if (notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent(
this.core,
parseInt(notify.group.groupCode),
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)),
'add',
notify.postscript,
flag,
);
this.networkManager.emitEvent(groupInviteEvent)
.catch(e => this.context.logger.logError('处理邀请本人加群失败', e));
} }
} }
} }

View File

@@ -63,3 +63,27 @@ export interface OB11Sender {
level?: string, // 群等级 level?: string, // 群等级
role?: OB11GroupMemberRole role?: OB11GroupMemberRole
} }
export interface OB11GroupFile {
group_id: number,
file_id: string,
file_name: string,
busid: number,
size: number,
upload_time: number,
dead_time: number,
modify_time: number,
download_times: number,
uploader: number,
uploader_name: string
}
export interface OB11GroupFileFolder {
group_id: number,
folder_id: string,
folder_name: string,
create_time: number,
creator: number,
creator_name: string,
total_file_count: number,
}

View File

@@ -5,8 +5,8 @@ import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/lis
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters'; import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '@/core/adapters';
import { NapCatPathWrapper } from '@/common/path'; import { NapCatPathWrapper } from '@/common/path';
import { import {
InstanceContext,
genSessionConfig, genSessionConfig,
InstanceContext,
loadQQWrapper, loadQQWrapper,
NapCatCore, NapCatCore,
NapCatCoreWorkingEnv, NapCatCoreWorkingEnv,

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.20', 'napcat-update-button', 'secondary'), SettingButton('V2.2.29', '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.20", "napcat-update-button", "secondary") SettingButton("V2.2.29", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([