Compare commits

...

53 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
46 changed files with 881 additions and 314 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.21", "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.21", "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

@@ -1 +1 @@
export const napCatVersion = '2.2.21'; 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,
@@ -309,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,
], ],
@@ -402,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

@@ -34,11 +34,26 @@ export class NTQQWebApi {
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) { }
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,
}).toString() }).toString()
}`; }`;

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,8 +182,7 @@ 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;

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

@@ -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

@@ -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

@@ -1,4 +1,4 @@
import { ChatType, GroupEssenceMsgRet, Peer } 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';
@@ -18,42 +18,54 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetGroupEssence extends BaseAction<Payload, any> { export class GetGroupEssence extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_GetEssenceMsg; actionName = ActionName.GoCQHTTP_GetEssenceMsg;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async msgSeqToMsgId(peer: Peer, msgSeq: string, msgRandom: string) {
const NTQQMsgApi = this.core.apis.MsgApi; private async msgSeqToMsgId(peer: Peer, msgSeq: string, msgRandom: string) {
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount(peer, msgSeq, 1, true, true)).msgList.find((msg) => msg.msgSeq === msgSeq && msg.msgRandom === msgRandom); const replyMsgList = (await this.core.apis.MsgApi.getMsgsBySeqAndCount(peer, msgSeq, 1, true, true)).msgList.find((msg) => msg.msgSeq === msgSeq && msg.msgRandom === msgRandom);
if (!replyMsgList) { if (!replyMsgList) {
return 0; return undefined;
} }
return MessageUnique.createUniqueMsgId(peer, replyMsgList.msgId); 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 NTQQGroupApi = this.core.apis.GroupApi; const NTQQGroupApi = this.core.apis.GroupApi;
//await NTQQGroupApi.fetchGroupEssenceList(payload.group_id.toString()); const msglist = (await NTQQWebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
let peer = { if (!msglist) {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
};
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString());
if (!ret) {
throw new Error('获取失败'); throw new Error('获取失败');
} }
const Ob11Ret = await Promise.all(ret.data.msg_list.map(async (msg) => { return await Promise.all(msglist.map(async (msg) => {
let message_id = await this.msgSeqToMsgId(peer, msg.msg_seq.toString(), msg.msg_random.toString()); const msgOriginData = await this.msgSeqToMsgId({
if (message_id === 0) { chatType: ChatType.KCHATTYPEGROUP,
const data = JSON.stringify({ peerUid: payload.group_id.toString(),
msg_seq: msg.msg_seq.toString(), }, msg.msg_seq.toString(), msg.msg_random.toString());
msg_random: msg.msg_random.toString(), if (msgOriginData) {
group_id: payload.group_id.toString(), const { id: message_id, msg: rawMessage } = msgOriginData;
}); return {
const hash = crypto.createHash('md5').update(data).digest(); msg_seq: msg.msg_seq,
//设置第一个bit为0 保证shortId为正数 msg_random: msg.msg_random,
hash[0] &= 0x7f; sender_id: +msg.sender_uin,
const shortId = hash.readInt32BE(0); sender_nick: msg.sender_nick,
NTQQGroupApi.essenceLRU.set(shortId, data); operator_id: +msg.add_digest_uin,
message_id = shortId; 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 { return {
msg_seq: msg.msg_seq, msg_seq: msg.msg_seq,
msg_random: msg.msg_random, msg_random: msg.msg_random,
@@ -61,10 +73,27 @@ export class GetGroupEssence extends BaseAction<Payload, any> {
sender_nick: msg.sender_nick, sender_nick: msg.sender_nick,
operator_id: +msg.add_digest_uin, operator_id: +msg.add_digest_uin,
operator_nick: msg.add_digest_nick, operator_nick: msg.add_digest_nick,
message_id: message_id, message_id: shortId,
operator_time: msg.add_digest_time, 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),
}; };
})); }));
return Ob11Ret;
} }
} }

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

@@ -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.createUniqueMsgId({
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,
}, },
}; };

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,13 +248,16 @@ 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),
@@ -368,7 +371,7 @@ export class NapCatOneBot11Adapter {
private initGroupListener() { private initGroupListener() {
const groupListener = new NodeIKernelGroupListener(); const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupNotifiesUpdated = async (_, notifies) => { groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]); //console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false); await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false);
@@ -470,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.21', '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.21", "napcat-update-button", "secondary") SettingButton("V2.2.29", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([