Compare commits

..

49 Commits

Author SHA1 Message Date
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
51 changed files with 956 additions and 328 deletions

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "2.2.20",
"version": "2.2.27",
"scripts": {
"build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell",

View File

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

View File

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

View File

@@ -1 +1 @@
export const napCatVersion = '2.2.20';
export const napCatVersion = '2.2.27';

View File

@@ -17,7 +17,7 @@ import {
import path from 'path';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore } from '@/core';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
import * as fileType from 'file-type';
import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
@@ -302,7 +302,18 @@ export class NTQQFileApi {
async downloadMediaByUuid() {
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
}
async downloadFileForModelId(peer: Peer, modelId: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[peer, [modelId]],
() => true,
(arg) => arg?.commonFileInfo?.fileModelId === modelId,
1,
timeout,
);
return fileTransNotifyInfo.filePath;
}
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
// 用于下载收到的消息中的图片等
@@ -342,20 +353,15 @@ export class NTQQFileApi {
chatType: chatType,
peerUid: peerUid,
}, [msgId]);
if (msg.msgList.length === 0) {
return fileTransNotifyInfo.filePath;
const mixElement = msg.msgList.find((msg) => msg.msgId === msgId)?.elements.find((e) => e.elementId === elementId);
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];
}
//获取原始消息
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;
return realPath;
}
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
@@ -429,17 +435,44 @@ export class NTQQFileApi {
});
}
async searchfile(keys: string[]) {
const Event = this.core.eventWrapper.createEventFunction('NodeIKernelSearchService/searchFileWithKeywords');
const id = await Event!(keys, 12);
const Listener = this.core.eventWrapper.registerListen(
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
1,
20000,
(params) => id !== '' && params.searchId == id,
async searchForFile(keys: string[]): Promise<SearchResultItem | undefined> {
const randomResultId = 100000 + Math.floor(Math.random() * 10000);
let searchId = 0;
const [, searchResult] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelFileAssistantService/searchFile',
'NodeIKernelFileAssistantListener/onFileSearch',
[
keys,
{
resultType: 2,
pageLimit: 1,
},
randomResultId
],
(ret) => {
searchId = ret;
return true;
},
result => result.searchId === searchId && result.resultId === randomResultId,
);
const [ret] = (await Listener);
return ret;
return searchResult.resultItems[0];
}
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) {

View File

@@ -2,7 +2,6 @@ import {
ChatType,
GeneralCallResult,
Group,
GroupInfoSource,
GroupMember,
GroupMemberRole,
GroupRequestOperateTypes,
@@ -12,6 +11,7 @@ import {
NapCatCore,
} from '@/core';
import { isNumeric, runAllWithTimeout } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique';
export class NTQQGroupApi {
context: InstanceContext;
@@ -19,6 +19,7 @@ export class NTQQGroupApi {
groupCache: Map<string, Group> = new Map<string, Group>();
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = [];
essenceLRU = new LimitedHashTable<number, string>(1000);
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
@@ -33,6 +34,17 @@ export class NTQQGroupApi {
}
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) {
return this.context.session.getGroupService().setHeader(gc, filePath);
}
@@ -270,7 +282,15 @@ export class NTQQGroupApi {
//应该是直接返回不需要Listener的 未经测试 需测试再发布
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) {
// 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -288,12 +308,12 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().removeGroupEssence(param);
}
async getSingleScreenNotifies(num: number) {
async getSingleScreenNotifies(doubt: boolean, num: number) {
const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
[
false,
doubt,
'',
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);
}

View File

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

View File

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

View File

@@ -1,5 +1,13 @@
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[]) {
@@ -11,6 +19,42 @@ export class NodeIKernelFileAssistantListener {
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 { CommonFileInfo } from '@/core';
export interface OnRichMediaDownloadCompleteParams {
fileModelId: string,
@@ -15,7 +16,7 @@ export interface OnRichMediaDownloadCompleteParams {
totalSize: string,
trasferStatus: number,
step: number,
commonFileInfo: unknown | null,
commonFileInfo?: CommonFileInfo,
fileSrvErrCode: string,
clientMsg: string,
businessId: number,
@@ -28,7 +29,47 @@ export interface GroupFileInfoUpdateParamType {
retMsg: string;
clientWording: string;
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;
nextIndex: string;
reqId: string;

View File

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

View File

@@ -1,15 +1,15 @@
// @generated by protobuf-ts 2.9.4
// @generated from protobuf file "EmojiLikeToOthers.proto" (package "SysMessage", syntax proto3)
// tslint:disable
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { IBinaryWriter } from "@protobuf-ts/runtime";
import { WireType } from "@protobuf-ts/runtime";
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import type {
BinaryReadOptions,
BinaryWriteOptions,
IBinaryReader,
IBinaryWriter,
PartialMessage,
} from '@protobuf-ts/runtime';
import { MessageType, reflectionMergePartial, UnknownFieldHandler, WireType } from '@protobuf-ts/runtime';
/**
* @generated from protobuf message SysMessage.EmojiLikeToOthersWrapper1
*/
@@ -62,6 +62,27 @@ export interface EmojiLikeToOthersAttributes {
* @generated from protobuf field: string senderUid = 4;
*/
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
class EmojiLikeToOthersWrapper1$Type extends MessageType<EmojiLikeToOthersWrapper1> {
@@ -260,13 +281,15 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
constructor() {
super("SysMessage.EmojiLikeToOthersAttributes", [
{ 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 {
const message = globalThis.Object.create((this.messagePrototype!));
message.emojiId = "";
message.senderUid = "";
message.operation = 0;
if (value !== undefined)
reflectionMergePartial<EmojiLikeToOthersAttributes>(this, message, value);
return message;
@@ -282,6 +305,9 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
case /* string senderUid */ 4:
message.senderUid = reader.string();
break;
case /* SysMessage.EmojiLikeToOthersAttributes.Operation operation */ 5:
message.operation = reader.int32();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
@@ -300,6 +326,9 @@ class EmojiLikeToOthersAttributes$Type extends MessageType<EmojiLikeToOthersAttr
/* string senderUid = 4; */
if (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;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
import { NodeIKernelFileAssistantListener } from '@/core';
export interface NodeIKernelFileAssistantService {
addKernelFileAssistantListener(arg1: unknown[]): unknown;
addKernelFileAssistantListener(listener: NodeIKernelFileAssistantListener): unknown;
removeKernelFileAssistantListener(arg1: unknown[]): unknown;
@@ -9,7 +11,7 @@ export interface NodeIKernelFileAssistantService {
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;
@@ -17,7 +19,7 @@ export interface NodeIKernelFileAssistantService {
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;
@@ -32,4 +34,4 @@ export interface NodeIKernelFileAssistantService {
saveAsWithRename(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
isNull(): boolean;
}
}

View File

@@ -182,14 +182,13 @@ export interface NodeIKernelGroupService {
destroyGroup(groupCode: string): void;
//获取单屏群通知列表
getSingleScreenNotifies(force: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
getSingleScreenNotifies(doubted: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
clearGroupNotifies(groupCode: string): void;
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>;
clearGroupNotifiesUnreadCount(groupCode: string): void;
clearGroupNotifiesUnreadCount(unknown: boolean): void;
operateSysNotify(
doubt: boolean,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,15 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FileNapCatOneBotUUID } from '@/common/helper';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['string', 'number'] },
start_index: { type: 'number' },
file_count: { type: 'number' },
start_index: { type: ['string', 'number'] },
file_count: { type: ['string', 'number'] },
folder_id: { type: ['string', 'number'] },
},
required: ['group_id', 'start_index', 'file_count'],
} as const satisfies JSONSchema;
@@ -20,15 +22,27 @@ export class GetGroupFileList extends BaseAction<Payload, { FileList: Array<any>
async _handle(payload: Payload) {
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(), {
sortType: 1,
fileCount: payload.file_count,
startIndex: payload.start_index,
fileCount: +payload.file_count,
startIndex: +payload.start_index,
sortOrder: 2,
showOnlinedocFolder: 0,
}).catch((e) => {
...param
}).catch(() => {
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 };
}
}

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
.parseMessage(msg);
if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({
resMsg.message_id = MessageUnique.createUniqueMsgId({
guildId: '',
chatType: msg.chatType,
peerUid: msg.peerUid,

View File

@@ -47,7 +47,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
if (isReverseOrder) msgList.reverse();
//转换序号
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(

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();
//转换序号
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(

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> {
const NTQQGroupApi = this.core.apis.GroupApi;
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(
msg.Peer.peerUid,
msg.MsgId,

View File

@@ -1,29 +1,99 @@
import { GroupEssenceMsgRet } from '@/core';
import { ChatType, Peer } from '@/core';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/message-unique';
import crypto from 'crypto';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
pages: { type: ['number', 'string'] },
group_id: { type: ['number', 'string'] }
},
required: ['group_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> {
export class GetGroupEssence extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_GetEssenceMsg;
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) {
const NTQQWebApi = this.core.apis.WebApi;
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), (+(payload.pages ?? 0)).toString());
if (!ret) {
const NTQQGroupApi = this.core.apis.GroupApi;
const msglist = (await NTQQWebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
if (!msglist) {
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

@@ -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 { GetGroupHonorInfo } from './go-cqhttp/GetGroupHonorInfo';
import { GoCQHTTPHandleQuickAction } from './go-cqhttp/QuickAction';
import { GetGroupSystemMsg } from './group/GetGroupSystemMsg';
import { GetGroupIgnoredNotifies } from './group/GetGroupIgnoredNotifies';
import { GetOnlineClient } from './go-cqhttp/GetOnlineClient';
import { IOCRImage, OCRImage } from './extends/OCRImage';
import { GetGroupFileCount } from './file/GetGroupFileCount';
@@ -82,10 +82,22 @@ import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF';
import { DelGroupNotice } from './group/DelGroupNotice';
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 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 = [
new GetGroupInfoEx(obContext, core),
new FetchEmojiLike(obContext, core),
@@ -101,11 +113,11 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new MarkPrivateMsgAsRead(obContext, core),
new SetQQAvatar(obContext, core),
new TranslateEnWordToZn(obContext, core),
new GetGroupFileCount(obContext, core),
ncGetGroupFileCount,
new GetGroupFileList(obContext, core),
new SetGroupFileFolder(obContext, core),
new DelGroupFile(obContext, core),
new DelGroupFileFolder(obContext, core),
ncSetGroupFileFolder,
ncDelGroupFile,
ncDelGroupFileFolder,
// onebot11
new SendLike(obContext, core),
new GetMsg(obContext, core),
@@ -159,7 +171,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GoCQHTTPGetForwardMsgAction(obContext, core),
new GetFriendMsgHistory(obContext, core),
new GoCQHTTPHandleQuickAction(obContext, core),
new GetGroupSystemMsg(obContext, core),
new GetGroupIgnoredNotifies(obContext, core),
new DelEssenceMsg(obContext, core),
new SetEssenceMsg(obContext, core),
new GetRecentContact(obContext, core),
@@ -173,6 +185,12 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SetInputStatus(obContext, core),
new GetCSRF(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();
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');
if (!retMsg) throw Error('消息为空');
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.real_id = retMsg.message_id;
} catch (e) {

View File

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

View File

@@ -83,11 +83,17 @@ export enum ActionName {
MarkPrivateMsgAsRead = 'mark_private_msg_as_read',
MarkGroupMsgAsRead = 'mark_group_msg_as_read',
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_GetGroupMsgHistory = 'get_group_msg_history',
GoCQHTTP_GetForwardMsg = 'get_forward_msg',
GetFriendMsgHistory = 'get_friend_msg_history',
GetGroupSystemMsg = 'get_group_system_msg',
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
GetOnlineClient = 'get_online_clients',
OCRImage = 'ocr_image',
IOCRImage = '.ocr_image',

View File

@@ -116,18 +116,22 @@ export class OneBotGroupApi {
const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode');
if (!Group) return;
// const businessId = searchParams.get('businessid');
const Peer = {
guildId: '',
chatType: ChatType.KCHATTYPEGROUP,
peerUid: Group!,
peerUid: Group,
};
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
let msgList = (await this.core.apis.WebApi.getGroupEssenceMsgAll(Group)).flatMap((e) => e.data.msg_list);
let realMsg = msgList.find((e) => e.msg_seq.toString() == msgSeq);
return new OB11GroupEssenceEvent(
this.core,
parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!,
parseInt(msgData.msgList[0].senderUin),
parseInt(realMsg?.add_digest_uin ?? '0'),
);
// 获取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 {
AtType,
@@ -9,7 +9,8 @@ import {
FaceType,
IdMusicSignPostData,
MessageElement,
NapCatCore, NTGrayTipElementSubTypeV2,
NapCatCore,
NTGrayTipElementSubTypeV2,
Peer,
RawMessage,
SendMessageElement,
@@ -98,14 +99,19 @@ export class OneBotMsgApi {
}
},
picElement: async (element, msg) => {
picElement: async (element, msg, elementWrapper) => {
try {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
return {
type: OB11MessageDataType.image,
data: {
file: element.fileName,
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),
file_size: element.fileSize,
},
@@ -117,6 +123,11 @@ export class OneBotMsgApi {
},
fileElement: async (element, msg, elementWrapper) => {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
await this.core.apis.FileApi.addFileCache(
{
peerUid: msg.peerUid,
@@ -137,7 +148,7 @@ export class OneBotMsgApi {
file: element.fileName,
path: 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,
},
};
@@ -184,11 +195,16 @@ export class OneBotMsgApi {
'0',
'marketface',
);
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
return {
type: OB11MessageDataType.image,
data: {
file: 'marketface',
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
path: elementWrapper.elementId,
url: elementWrapper.elementId,
},
@@ -207,13 +223,21 @@ export class OneBotMsgApi {
this.core.context.logger.logError('获取不到引用的消息', element.replayMsgSeq);
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;
// Attempt 1
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, element.replayMsgSeq, 1, true, true))
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount(peer,element.replayMsgSeq, 1, true, true))
.msgList
.find(msg => msg.msgRandom === records.msgRandom);
@@ -221,7 +245,7 @@ export class OneBotMsgApi {
// Attempt 2
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
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) {
@@ -233,21 +257,16 @@ export class OneBotMsgApi {
}
}
return {
type: OB11MessageDataType.reply,
data: {
id: MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId).toString(),
},
};
return createReplyData(replyMsg.msgId);
},
videoElement: async (element, msg, elementWrapper) => {
const NTQQFileApi = this.core.apis.FileApi;
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
//读取视频链接并兜底
let videoUrlWrappers: Awaited<ReturnType<typeof NTQQFileApi.getVideoUrl>> | undefined;
@@ -302,13 +321,18 @@ export class OneBotMsgApi {
file: element.fileName,
path: videoDownUrl,
url: videoDownUrl,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
file_size: element.fileSize,
},
};
},
pttElement: async (element, msg, elementWrapper) => {
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
await this.core.apis.FileApi.addFileCache(
{
peerUid: msg.peerUid,
@@ -328,7 +352,7 @@ export class OneBotMsgApi {
data: {
file: element.fileName,
path: element.filePath,
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId),
file_size: element.fileSize,
},
};
@@ -365,7 +389,7 @@ export class OneBotMsgApi {
async multiMsgItem => {
multiMsgItem.parentMsgPeer = parentMsgPeer;
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);
},
))).filter(item => item !== undefined),
@@ -823,7 +847,7 @@ export class OneBotMsgApi {
}
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
if (!returnMsg) throw new Error('发送消息失败');
returnMsg.id = MessageUnique.createMsg({
returnMsg.id = MessageUnique.createUniqueMsgId({
chatType: peer.chatType,
guildId: '',
peerUid: peer.peerUid,

View File

@@ -10,12 +10,12 @@ import {
QuickActionGroupMessage,
QuickActionGroupRequest,
} from '@/onebot';
import { ChatType, GroupRequestOperateTypes, NapCatCore, Peer } from '@/core';
import { GroupRequestOperateTypes, NapCatCore, Peer } from '@/core';
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
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 { createContext } from '@/onebot/action/msg/SendMsg';
export class OneBotQuickActionApi {
constructor(
public obContext: NapCatOneBot11Adapter,
@@ -78,7 +78,7 @@ export class OneBotQuickActionApi {
sendElements,
deleteAfterSentFiles,
} = 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,
quickAction.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
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) {
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 { Friend, FriendV2, Group, GroupMember, SelfInfo, Sex, User } from '@/core';
import { OB11Group, OB11GroupMember, OB11GroupMemberRole, OB11User, OB11UserSex } from './types';
import { calcQQLevel, FileNapCatOneBotUUID } from '@/common/helper';
import { Friend, FriendV2, Group, GroupFileInfoUpdateParamType, GroupMember, SelfInfo, Sex, User } from '@/core';
import {
OB11Group,
OB11GroupFile,
OB11GroupFileFolder,
OB11GroupMember,
OB11GroupMemberRole,
OB11User,
OB11UserSex,
} from './types';
export class OB11Entities {
static selfInfo(selfInfo: SelfInfo): OB11User {
@@ -97,4 +105,32 @@ export class OB11Entities {
static groups(groups: Group[]): OB11Group[] {
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';
message_id: number;
sender_id: number;
operator_id: number;
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);
this.group_id = groupId;
this.operator_id = operator_id;
this.message_id = message_id;
this.sender_id = sender_id;
}

View File

@@ -139,7 +139,7 @@ export class NapCatOneBot11Adapter {
initRecentContactListener() {
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) => {
if (msg.chatType == ChatType.KCHATTYPEGROUP) {
// log("recent contact", msgList, arg0, arg1);
@@ -248,19 +248,23 @@ export class NapCatOneBot11Adapter {
return;
}
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)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
@@ -283,7 +287,7 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug(`消息时间${m.msgTime}早于启动时间${this.bootTime},忽略上报`);
continue;
}
m.id = MessageUnique.createMsg(
m.id = MessageUnique.createUniqueMsgId(
{
chatType: m.chatType,
peerUid: m.peerUid,
@@ -311,7 +315,7 @@ export class NapCatOneBot11Adapter {
if (!ob11Msg) return;
ob11Msg.target_id = parseInt(msg.peerUin);
if (this.configLoader.configData.reportSelfMessage) {
msg.id = MessageUnique.createMsg({
msg.id = MessageUnique.createUniqueMsgId({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
@@ -344,7 +348,7 @@ export class NapCatOneBot11Adapter {
// }
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
return;
continue;
}
try {
const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid);
@@ -370,6 +374,7 @@ export class NapCatOneBot11Adapter {
groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false);
if (![
GroupNotifyMsgType.SET_ADMIN,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
@@ -468,6 +473,18 @@ export class NapCatOneBot11Adapter {
);
this.networkManager.emitEvent(groupInviteEvent)
.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, // 群等级
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 { NapCatPathWrapper } from '@/common/path';
import {
InstanceContext,
genSessionConfig,
InstanceContext,
loadQQWrapper,
NapCatCore,
NapCatCoreWorkingEnv,

View File

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

View File

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