Compare commits

...

28 Commits

Author SHA1 Message Date
手瓜一十雪
c45f0dc289 Merge branch 'main' into newApi 2025-05-18 18:23:37 +08:00
手瓜一十雪
c1a0f8915b docs: mai 2025-05-17 17:54:53 +08:00
Mlikiowa
dcdab8e5a1 release: v4.7.63 2025-05-17 05:09:36 +00:00
手瓜一十雪
eb3278fdab feat: 35184 2025-05-17 11:16:39 +08:00
手瓜一十雪
433ce9caef fix 2025-05-16 21:29:16 +08:00
手瓜一十雪
ba1acd624e feat: 迁移at 消息段 2025-05-16 21:21:22 +08:00
手瓜一十雪
a586796339 feat: 彻底扬了没用的 2025-05-16 21:19:04 +08:00
手瓜一十雪
2016a90198 feat: 测试代码 2025-05-16 21:12:28 +08:00
手瓜一十雪
b4bc4da7fc feat: 测试 2025-05-16 21:04:46 +08:00
手瓜一十雪
52c828192e feat: 斩杀快速操作 CQCode 调整消息和会话Id类型 2025-05-16 20:59:14 +08:00
手瓜一十雪
34db3af48d fix: #1007 2025-05-15 21:10:21 +08:00
Mlikiowa
198da960dd release: v4.7.62 2025-05-12 12:37:01 +00:00
手瓜一十雪
cb83918fb3 fix 2025-05-12 20:36:39 +08:00
Mlikiowa
f59a48540b release: v4.7.61 2025-05-12 12:34:18 +00:00
手瓜一十雪
ccf9c1a5fb Merge pull request #1005 from NapNeko/fix-1001
refactor: remove image-size
2025-05-12 20:22:35 +08:00
手瓜一十雪
ba6a85142a fix 2025-05-12 19:29:40 +08:00
手瓜一十雪
440baccd2a fix 2025-05-12 19:27:46 +08:00
手瓜一十雪
690c073328 fix 2025-05-12 19:27:01 +08:00
手瓜一十雪
3f0730ed4f fix 2025-05-12 19:25:03 +08:00
手瓜一十雪
01d5663bc8 fix: image size 2025-05-12 19:18:33 +08:00
手瓜一十雪
49806cd00e Create index.ts 2025-05-12 19:17:17 +08:00
手瓜一十雪
935b0848e5 Merge pull request #1004 from NapNeko/dependabot/npm_and_yarn/esbuild-0.25.4
build(deps-dev): bump esbuild from 0.25.0 to 0.25.4
2025-05-12 18:45:23 +08:00
dependabot[bot]
5ca20a89a2 build(deps-dev): bump esbuild from 0.25.0 to 0.25.4
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.0 to 0.25.4.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.0...v0.25.4)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 08:37:11 +00:00
Mlikiowa
e89a2266ec release: v4.7.60 2025-05-11 03:32:29 +00:00
手瓜一十雪
6607533311 fix: #996 2025-05-11 11:31:52 +08:00
Mlikiowa
4057054220 release: v4.7.58 2025-05-11 03:24:02 +00:00
手瓜一十雪
055e43845e feat: ffmpeg下载来源更换 2025-05-11 11:23:43 +08:00
Mlikiowa
d67270f2f8 release: v4.7.57 2025-05-10 13:15:37 +00:00
122 changed files with 875 additions and 808 deletions

View File

@@ -53,6 +53,8 @@ _Modern protocol-side framework implemented based on NTQQ._
+ [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下 + [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下
+ [MaiBot](https://github.com/MaiM-with-u/MaiBot) 一只赛博群友 麦麦 Bot框架 在此推荐一下
+ 不过最最重要的 还是需要感谢屏幕前的你哦~ + 不过最最重要的 还是需要感谢屏幕前的你哦~
--- ---

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "4.7.56", "version": "4.7.63",
"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": "4.7.56", "version": "4.7.63",
"scripts": { "scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1",
@@ -42,7 +42,7 @@
"async-mutex": "^0.5.0", "async-mutex": "^0.5.0",
"commander": "^13.0.0", "commander": "^13.0.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"esbuild": "0.25.0", "esbuild": "0.25.4",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^4.0.0", "eslint-import-resolver-typescript": "^4.0.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
@@ -50,7 +50,6 @@
"fast-xml-parser": "^4.3.6", "fast-xml-parser": "^4.3.6",
"file-type": "^20.0.0", "file-type": "^20.0.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"image-size": "^1.1.1",
"json5": "^2.2.3", "json5": "^2.2.3",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"typescript": "^5.3.3", "typescript": "^5.3.3",

View File

@@ -8,11 +8,12 @@ import { pipeline } from 'stream/promises';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { LogWrapper } from './log'; import { LogWrapper } from './log';
const downloadOri = "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2025-04-16-12-54/ffmpeg-n7.1.1-6-g48c0f071d4-win64-lgpl-7.1.zip" const downloadOri = 'https://github.com/NapNeko/ffmpeg-build/releases/download/v1.0.0/ffmpeg-7.1.1-win64.zip';
const urls = [ const urls = [
"https://github.moeyy.xyz/" + downloadOri, 'https://github.moeyy.xyz/' + downloadOri,
"https://ghp.ci/" + downloadOri, 'https://ghp.ci/' + downloadOri,
"https://gh.api.99988866.xyz/" + downloadOri, 'https://gh.api.99988866.xyz/' + downloadOri,
'https://gh.api.99988866.xyz/' + downloadOri,
downloadOri downloadOri
]; ];
@@ -336,17 +337,24 @@ export async function downloadFFmpegIfNotExists(log: LogWrapper) {
const ffprobe_exist = fs.existsSync(path.join(currentPath, 'ffmpeg', 'ffprobe.exe')); const ffprobe_exist = fs.existsSync(path.join(currentPath, 'ffmpeg', 'ffprobe.exe'));
if (!ffmpeg_exist || !ffprobe_exist) { if (!ffmpeg_exist || !ffprobe_exist) {
await downloadFFmpeg(path.join(currentPath, 'ffmpeg'), path.join(currentPath, 'cache'), (percentage: number, message: string) => { let url = await downloadFFmpeg(path.join(currentPath, 'ffmpeg'), path.join(currentPath, 'cache'), (percentage: number, message: string) => {
log.log(`[FFmpeg] [Download] ${percentage}% - ${message}`); log.log(`[FFmpeg] [Download] ${percentage}% - ${message}`);
}); });
if (!url) {
log.log('[FFmpeg] [Error] 下载FFmpeg失败');
return {
path: null,
reset: false
};
}
return { return {
path: path.join(currentPath, 'ffmpeg'), path: path.join(currentPath, 'ffmpeg'),
reset: true reset: true
} };
} }
return { return {
path: path.join(currentPath, 'ffmpeg'), path: path.join(currentPath, 'ffmpeg'),
reset: true reset: true
} };
} }

View File

@@ -4,10 +4,10 @@ import { execFile } from 'child_process';
import { promisify } from 'util'; import { promisify } from 'util';
import type { VideoInfo } from './video'; import type { VideoInfo } from './video';
import { fileTypeFromFile } from 'file-type'; import { fileTypeFromFile } from 'file-type';
import imageSize from 'image-size';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { platform } from 'node:os'; import { platform } from 'node:os';
import { LogWrapper } from './log'; import { LogWrapper } from './log';
import { imageSizeFallBack } from '@/image-size';
const currentPath = dirname(fileURLToPath(import.meta.url)); const currentPath = dirname(fileURLToPath(import.meta.url));
const execFileAsync = promisify(execFile); const execFileAsync = promisify(execFile);
const getFFmpegPath = (tool: string): string => { const getFFmpegPath = (tool: string): string => {
@@ -157,7 +157,7 @@ export class FFmpegService {
try { try {
await this.extractThumbnail(videoPath, thumbnailPath); await this.extractThumbnail(videoPath, thumbnailPath);
// 获取图片尺寸 // 获取图片尺寸
const dimensions = imageSize(thumbnailPath); const dimensions = await imageSizeFallBack(thumbnailPath);
return { return {
format: fileType?.ext ?? 'mp4', format: fileType?.ext ?? 'mp4',

View File

@@ -182,28 +182,28 @@ export async function uriToLocalFile(dir: string, uri: string, filename: string
const filePath = path.join(dir, filename); const filePath = path.join(dir, filename);
switch (UriType) { switch (UriType) {
case FileUriType.Local: { case FileUriType.Local: {
const fileExt = path.extname(HandledUri); const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt; const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt); const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath); fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath }; return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
} }
case FileUriType.Remote: { case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} }); const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer); fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath }; return { success: true, errMsg: '', fileName: filename, path: filePath };
} }
case FileUriType.Base64: { case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, ''); const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64'); const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer); fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath }; return { success: true, errMsg: '', fileName: filename, path: filePath };
} }
default: default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' }; return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
} }
} }

View File

@@ -1,6 +1,4 @@
import { Peer } from '@/core'; import { Peer } from '@/core';
import crypto from 'crypto';
export class LimitedHashTable<K, V> { export class LimitedHashTable<K, V> {
private readonly keyToValue: Map<K, V> = new Map(); private readonly keyToValue: Map<K, V> = new Map();
private readonly valueToKey: Map<V, K> = new Map(); private readonly valueToKey: Map<V, K> = new Map();
@@ -78,65 +76,19 @@ export class LimitedHashTable<K, V> {
} }
class MessageUniqueWrapper { class MessageUniqueWrapper {
private readonly msgDataMap: LimitedHashTable<string, number>; constructor() {
private readonly msgIdMap: LimitedHashTable<string, number>;
constructor(maxMap: number = 5000) {
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
} }
getRecentMsgIds(Peer: Peer, size: number): string[] { getOutputData(peer: Peer, msg_id: string, seq: string): string {
const heads = this.msgIdMap.getHeads(size); return `${peer.chatType}|${msg_id}|${peer.peerUid}|${seq}`;
if (!heads) { }
return [];
getInnerData(shortId: string): { MsgId: string; Peer: Peer, seq: string } | undefined {
const [chatType, msgId, peerUid, seq] = shortId.split('|');
if (!chatType || !msgId || !peerUid || !seq) {
return undefined;
} }
const data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value)); return { MsgId: msgId, Peer: { chatType: parseInt(chatType), peerUid, guildId: '' }, seq: seq };
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid);
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
}
createUniqueMsgId(peer: Peer, msgId: string) {
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
const hash = crypto.createHash('md5').update(key).digest();
if (hash[0]) {
//设置第一个bit为0 保证shortId为正数
hash[0] &= 0x7f;
}
const shortId = hash.readInt32BE(0);
//减少性能损耗
this.msgIdMap.set(msgId, shortId);
this.msgDataMap.set(key, shortId);
return shortId;
}
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
const data = this.msgDataMap.getKey(shortId);
if (data) {
const [msgId, chatTypeStr, peerUid] = data.split('|');
const peer: Peer = {
chatType: parseInt(chatTypeStr ?? '0'),
peerUid: peerUid ?? '',
guildId: '',
};
return { MsgId: msgId ?? '0', Peer: peer };
}
return undefined;
}
getShortIdByMsgId(msgId: string): number | undefined {
return this.msgIdMap.getValue(msgId);
}
getPeerByMsgId(msgId: string) {
const shortId = this.msgIdMap.getValue(msgId);
if (!shortId) return undefined;
return this.getMsgIdAndPeerByShortId(shortId);
}
resize(maxSize: number): void {
this.msgIdMap.resize(maxSize);
this.msgDataMap.resize(maxSize);
} }
} }

View File

@@ -1 +1 @@
export const napCatVersion = '4.7.56'; export const napCatVersion = '4.7.63';

View File

@@ -9,7 +9,7 @@ export async function runTask<T, R>(workerScript: string, taskData: T): Promise<
console.error('Worker Log--->:', (result as { log: string }).log); console.error('Worker Log--->:', (result as { log: string }).log);
} }
if ((result as any)?.error) { if ((result as any)?.error) {
reject(new Error("Worker error: " + (result as { error: string }).error)); reject(new Error('Worker error: ' + (result as { error: string }).error));
} }
resolve(result); resolve(result);
}); });

View File

@@ -17,8 +17,6 @@ import fs from 'fs';
import fsPromises from 'fs/promises'; import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/core'; import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
import { fileTypeFromFile } from 'file-type'; import { fileTypeFromFile } from 'file-type';
import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { RkeyManager } from '@/core/helper/rkey'; import { RkeyManager } from '@/core/helper/rkey';
import { calculateFileMD5 } from '@/common/file'; import { calculateFileMD5 } from '@/common/file';
import pathLib from 'node:path'; import pathLib from 'node:path';
@@ -30,6 +28,7 @@ import { FFmpegService } from '@/common/ffmpeg';
import { rkeyDataType } from '../types/file'; import { rkeyDataType } from '../types/file';
import { NapProtoMsg } from '@napneko/nap-proto-core'; import { NapProtoMsg } from '@napneko/nap-proto-core';
import { FileId } from '../packet/transformer/proto/misc/fileid'; import { FileId } from '../packet/transformer/proto/misc/fileid';
import { imageSizeFallBack } from '@/image-size';
export class NTQQFileApi { export class NTQQFileApi {
context: InstanceContext; context: InstanceContext;
@@ -46,7 +45,7 @@ export class NTQQFileApi {
'https://secret-service.bietiaop.com/rkeys', 'https://secret-service.bietiaop.com/rkeys',
'http://ss.xingzhige.com/music_card/rkey', 'http://ss.xingzhige.com/music_card/rkey',
], ],
this.context.logger this.context.logger
); );
} }
@@ -209,7 +208,7 @@ export class NTQQFileApi {
if (fileSize === 0) { if (fileSize === 0) {
throw new Error('文件异常大小为0'); throw new Error('文件异常大小为0');
} }
const imageSize = await this.core.apis.FileApi.getImageSize(picPath); const imageSize = await imageSizeFallBack(picPath);
context.deleteAfterSentFiles.push(path); context.deleteAfterSentFiles.push(path);
return { return {
elementType: ElementType.PIC, elementType: ElementType.PIC,
@@ -380,18 +379,18 @@ export class NTQQFileApi {
element.elementType === ElementType.FILE element.elementType === ElementType.FILE
) { ) {
switch (element.elementType) { switch (element.elementType) {
case ElementType.PIC: case ElementType.PIC:
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? ''; element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
break; break;
case ElementType.VIDEO: case ElementType.VIDEO:
element.videoElement!.filePath = elementResults?.[elementIndex] ?? ''; element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
break; break;
case ElementType.PTT: case ElementType.PTT:
element.pttElement!.filePath = elementResults?.[elementIndex] ?? ''; element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
break; break;
case ElementType.FILE: case ElementType.FILE:
element.fileElement!.filePath = elementResults?.[elementIndex] ?? ''; element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
break; break;
} }
elementIndex++; elementIndex++;
} }
@@ -437,19 +436,6 @@ export class NTQQFileApi {
return completeRetData.filePath; return completeRetData.filePath;
} }
async getImageSize(filePath: string): Promise<ISizeCalculationResult> {
return new Promise((resolve, reject) => {
imageSize(filePath, (err: Error | null, dimensions) => {
if (err) {
reject(new Error(err.message));
} else if (!dimensions) {
reject(new Error('获取图片尺寸失败'));
} else {
resolve(dimensions);
}
});
});
}
async searchForFile(keys: string[]): Promise<SearchResultItem | undefined> { async searchForFile(keys: string[]): Promise<SearchResultItem | undefined> {
const randomResultId = 100000 + Math.floor(Math.random() * 10000); const randomResultId = 100000 + Math.floor(Math.random() * 10000);

View File

@@ -110,7 +110,7 @@ export class NTQQFriendApi {
time: item.reqTime, // 信息字段 time: item.reqTime, // 信息字段
type: 'doubt' //保留字段 type: 'doubt' //保留字段
}; };
})) }));
return requests; return requests;
} }
} }

View File

@@ -286,5 +286,13 @@
"9.9.19-34958": { "9.9.19-34958": {
"appid": 537290742, "appid": 537290742,
"qua": "V1_WIN_NQ_9.9.19_34958_GW_B" "qua": "V1_WIN_NQ_9.9.19_34958_GW_B"
},
"3.2.17-35184": {
"appid": 537291084,
"qua": "V1_LNX_NQ_3.2.17_35184_GW_B"
},
"9.9.19-35184": {
"appid": 537291048,
"qua": "V1_WIN_NQ_9.9.19_35184_GW_B"
} }
} }

View File

@@ -366,5 +366,17 @@
"9.9.19-34958-x64": { "9.9.19-34958-x64": {
"send": "3BDD8D0", "send": "3BDD8D0",
"recv": "3BE20D0" "recv": "3BE20D0"
},
"3.2.17-35184-x64": {
"send": "AE0DDE0",
"recv": "AE11800"
},
"3.2.17-35184-arm64": {
"send": "7776028",
"recv": "7779958"
},
"9.9.19-35184-x64": {
"send": "3BE5A10",
"recv": "3BEA210"
} }
} }

View File

@@ -185,9 +185,9 @@ export class PacketOperationContext {
const ps = msg.map((m) => { const ps = msg.map((m) => {
return m.msg.map(async (e) => { return m.msg.map(async (e) => {
if (e instanceof PacketMsgReplyElement && !e.targetElems) { if (e instanceof PacketMsgReplyElement && !e.targetElems) {
this.context.logger.debug(`Cannot find reply element's targetElems, prepare to fetch it...`); this.context.logger.debug('Cannot find reply element\'s targetElems, prepare to fetch it...');
if (!e.targetPeer?.peerUid) { if (!e.targetPeer?.peerUid) {
this.context.logger.error(`targetPeer is undefined!`); this.context.logger.error('targetPeer is undefined!');
} }
let targetMsg: NapProtoEncodeStructType<typeof PushMsgBody>[] | undefined; let targetMsg: NapProtoEncodeStructType<typeof PushMsgBody>[] | undefined;
if (e.isGroupReply) { if (e.isGroupReply) {
@@ -200,7 +200,7 @@ export class PacketOperationContext {
} }
}); });
}).flat(); }).flat();
await Promise.all(ps) await Promise.all(ps);
await this.UploadResources(msg, groupUin); await this.UploadResources(msg, groupUin);
} }
@@ -208,14 +208,14 @@ export class PacketOperationContext {
const req = trans.FetchGroupMessage.build(groupUin, startSeq, endSeq); const req = trans.FetchGroupMessage.build(groupUin, startSeq, endSeq);
const resp = await this.context.client.sendOidbPacket(req, true); const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.FetchGroupMessage.parse(resp); const res = trans.FetchGroupMessage.parse(resp);
return res.body.messages return res.body.messages;
} }
async FetchC2CMessage(targetUid: string, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> { async FetchC2CMessage(targetUid: string, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
const req = trans.FetchC2CMessage.build(targetUid, startSeq, endSeq); const req = trans.FetchC2CMessage.build(targetUid, startSeq, endSeq);
const resp = await this.context.client.sendOidbPacket(req, true); const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.FetchC2CMessage.parse(resp); const res = trans.FetchC2CMessage.parse(resp);
return res.messages return res.messages;
} }
async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) { async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {

View File

@@ -477,7 +477,7 @@ export enum SendStatusType {
export interface RawMessage { export interface RawMessage {
parentMsgPeer: Peer; // 父消息的Peer parentMsgPeer: Peer; // 父消息的Peer
parentMsgIdList: string[];// 父消息 ID 列表 parentMsgIdList: string[];// 父消息 ID 列表
id?: number;// 扩展字段,与 Ob11 msg ID 有关 id?: string;// 扩展字段,与 Ob11 msg ID 有关
guildId: string;// 频道ID guildId: string;// 频道ID
msgRandom: string;// 消息ID相关 msgRandom: string;// 消息ID相关
msgId: string;// 雪花ID msgId: string;// 雪花ID

426
src/image-size/index.ts Normal file
View File

@@ -0,0 +1,426 @@
import * as fs from 'fs';
import { ReadStream } from 'fs';
export interface ImageSize {
width: number;
height: number;
}
export enum ImageType {
JPEG = 'jpeg',
PNG = 'png',
BMP = 'bmp',
GIF = 'gif',
WEBP = 'webp',
UNKNOWN = 'unknown',
}
interface ImageParser {
readonly type: ImageType;
canParse(buffer: Buffer): boolean;
parseSize(stream: ReadStream): Promise<ImageSize | undefined>;
}
// 魔术匹配
function matchMagic(buffer: Buffer, magic: number[], offset = 0): boolean {
if (buffer.length < offset + magic.length) {
return false;
}
for (let i = 0; i < magic.length; i++) {
if (buffer[offset + i] !== magic[i]) {
return false;
}
}
return true;
}
// PNG解析器
class PngParser implements ImageParser {
readonly type = ImageType.PNG;
// PNG 魔术头89 50 4E 47 0D 0A 1A 0A
private readonly PNG_SIGNATURE = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
canParse(buffer: Buffer): boolean {
return matchMagic(buffer, this.PNG_SIGNATURE);
}
async parseSize(stream: ReadStream): Promise<ImageSize | undefined> {
return new Promise((resolve, reject) => {
stream.once('error', reject);
stream.once('readable', () => {
const buf = stream.read(24) as Buffer;
if (!buf || buf.length < 24) {
return resolve(undefined);
}
if (this.canParse(buf)) {
const width = buf.readUInt32BE(16);
const height = buf.readUInt32BE(20);
resolve({ width, height });
} else {
resolve(undefined);
}
});
});
}
}
// JPEG解析器
class JpegParser implements ImageParser {
readonly type = ImageType.JPEG;
// JPEG 魔术头FF D8
private readonly JPEG_SIGNATURE = [0xFF, 0xD8];
// JPEG标记常量
private readonly SOF_MARKERS = {
SOF0: 0xC0, // 基线DCT
SOF1: 0xC1, // 扩展顺序DCT
SOF2: 0xC2, // 渐进式DCT
SOF3: 0xC3, // 无损
} as const;
// 非SOF标记
private readonly NON_SOF_MARKERS: number[] = [
0xC4, // DHT
0xC8, // JPEG扩展
0xCC, // DAC
] as const;
canParse(buffer: Buffer): boolean {
return matchMagic(buffer, this.JPEG_SIGNATURE);
}
isSOFMarker(marker: number): boolean {
return (
marker === this.SOF_MARKERS.SOF0 ||
marker === this.SOF_MARKERS.SOF1 ||
marker === this.SOF_MARKERS.SOF2 ||
marker === this.SOF_MARKERS.SOF3
);
}
isNonSOFMarker(marker: number): boolean {
return this.NON_SOF_MARKERS.includes(marker);
}
async parseSize(stream: ReadStream): Promise<ImageSize | undefined> {
return new Promise<ImageSize | undefined>((resolve, reject) => {
const BUFFER_SIZE = 1024; // 读取块大小,可以根据需要调整
let buffer = Buffer.alloc(0);
let offset = 0;
let found = false;
// 处理错误
stream.on('error', (err) => {
stream.destroy();
reject(err);
});
// 处理数据块
stream.on('data', (chunk: Buffer | string) => {
// 追加新数据到缓冲区
const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
buffer = Buffer.concat([buffer.subarray(offset), chunkBuffer]);
offset = 0;
// 保持缓冲区在合理大小内,只保留最后的部分用于跨块匹配
const bufferSize = buffer.length;
const MIN_REQUIRED_BYTES = 10; // SOF段最低字节数
// 从JPEG头部后开始扫描
while (offset < bufferSize - MIN_REQUIRED_BYTES) {
// 寻找FF标记
if (buffer[offset] === 0xFF && buffer[offset + 1]! >= 0xC0 && buffer[offset + 1]! <= 0xCF) {
const marker = buffer[offset + 1];
if (!marker) {
break;
}
// 跳过非SOF标记
if (this.isNonSOFMarker(marker)) {
offset += 2;
continue;
}
// 处理SOF标记 (包含尺寸信息)
if (this.isSOFMarker(marker)) {
// 确保缓冲区中有足够数据读取尺寸
if (offset + 9 < bufferSize) {
// 解析尺寸: FF XX YY YY PP HH HH WW WW ...
// XX = 标记, YY YY = 段长度, PP = 精度, HH HH = 高, WW WW = 宽
const height = buffer.readUInt16BE(offset + 5);
const width = buffer.readUInt16BE(offset + 7);
found = true;
stream.destroy();
resolve({ width, height });
return;
} else {
// 如果缓冲区内数据不够,保留当前位置等待更多数据
break;
}
}
}
offset++;
}
// 缓冲区管理: 如果处理了许多数据但没找到标记,
// 保留最后N字节用于跨块匹配丢弃之前的数据
if (offset > BUFFER_SIZE) {
const KEEP_BYTES = 20; // 保留足够数据以处理跨块边界的情况
if (offset > KEEP_BYTES) {
buffer = buffer.subarray(offset - KEEP_BYTES);
offset = KEEP_BYTES;
}
}
});
// 处理流结束
stream.on('end', () => {
if (!found) {
resolve(undefined);
}
});
});
}
}
// BMP解析器
class BmpParser implements ImageParser {
readonly type = ImageType.BMP;
// BMP 魔术头42 4D (BM)
private readonly BMP_SIGNATURE = [0x42, 0x4D];
canParse(buffer: Buffer): boolean {
return matchMagic(buffer, this.BMP_SIGNATURE);
}
async parseSize(stream: ReadStream): Promise<ImageSize | undefined> {
return new Promise((resolve, reject) => {
stream.once('error', reject);
stream.once('readable', () => {
const buf = stream.read(26) as Buffer;
if (!buf || buf.length < 26) {
return resolve(undefined);
}
if (this.canParse(buf)) {
const width = buf.readUInt32LE(18);
const height = buf.readUInt32LE(22);
resolve({ width, height });
} else {
resolve(undefined);
}
});
});
}
}
// GIF解析器
class GifParser implements ImageParser {
readonly type = ImageType.GIF;
// GIF87a 魔术头47 49 46 38 37 61
private readonly GIF87A_SIGNATURE = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61];
// GIF89a 魔术头47 49 46 38 39 61
private readonly GIF89A_SIGNATURE = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61];
canParse(buffer: Buffer): boolean {
return (
matchMagic(buffer, this.GIF87A_SIGNATURE) ||
matchMagic(buffer, this.GIF89A_SIGNATURE)
);
}
async parseSize(stream: ReadStream): Promise<ImageSize | undefined> {
return new Promise((resolve, reject) => {
stream.once('error', reject);
stream.once('readable', () => {
const buf = stream.read(10) as Buffer;
if (!buf || buf.length < 10) {
return resolve(undefined);
}
if (this.canParse(buf)) {
const width = buf.readUInt16LE(6);
const height = buf.readUInt16LE(8);
resolve({ width, height });
} else {
resolve(undefined);
}
});
});
}
}
// WEBP解析器 - 完整支持VP8, VP8L, VP8X格式
class WebpParser implements ImageParser {
readonly type = ImageType.WEBP;
// WEBP RIFF 头52 49 46 46 (RIFF)
private readonly RIFF_SIGNATURE = [0x52, 0x49, 0x46, 0x46];
// WEBP 魔术头57 45 42 50 (WEBP)
private readonly WEBP_SIGNATURE = [0x57, 0x45, 0x42, 0x50];
// WEBP 块头
private readonly CHUNK_VP8 = [0x56, 0x50, 0x38, 0x20]; // "VP8 "
private readonly CHUNK_VP8L = [0x56, 0x50, 0x38, 0x4C]; // "VP8L"
private readonly CHUNK_VP8X = [0x56, 0x50, 0x38, 0x58]; // "VP8X"
canParse(buffer: Buffer): boolean {
return (
buffer.length >= 12 &&
matchMagic(buffer, this.RIFF_SIGNATURE, 0) &&
matchMagic(buffer, this.WEBP_SIGNATURE, 8)
);
}
isChunkType(buffer: Buffer, offset: number, chunkType: number[]): boolean {
return buffer.length >= offset + 4 && matchMagic(buffer, chunkType, offset);
}
async parseSize(stream: ReadStream): Promise<ImageSize | undefined> {
return new Promise((resolve, reject) => {
// 需要读取足够的字节来检测所有三种格式
const MAX_HEADER_SIZE = 32;
let totalBytes = 0;
let buffer = Buffer.alloc(0);
stream.on('error', reject);
stream.on('data', (chunk: Buffer | string) => {
const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
buffer = Buffer.concat([buffer, chunkBuffer]);
totalBytes += chunk.length;
// 检查是否有足够的字节进行格式检测
if (totalBytes >= MAX_HEADER_SIZE) {
stream.destroy();
// 检查基本的WEBP签名
if (!this.canParse(buffer)) {
return resolve(undefined);
}
// 检查chunk头部位于字节12-15
if (this.isChunkType(buffer, 12, this.CHUNK_VP8)) {
// VP8格式 - 标准WebP
// 宽度和高度在帧头中
const width = buffer.readUInt16LE(26) & 0x3FFF;
const height = buffer.readUInt16LE(28) & 0x3FFF;
return resolve({ width, height });
} else if (this.isChunkType(buffer, 12, this.CHUNK_VP8L)) {
// VP8L格式 - 无损WebP
// 1字节标记后是14位宽度和14位高度
const bits = buffer.readUInt32LE(21);
const width = 1 + (bits & 0x3FFF);
const height = 1 + ((bits >> 14) & 0x3FFF);
return resolve({ width, height });
} else if (this.isChunkType(buffer, 12, this.CHUNK_VP8X)) {
// VP8X格式 - 扩展WebP
// 24位宽度和高度(减去1)
if (!buffer[24] || !buffer[25] || !buffer[26] || !buffer[27] || !buffer[28] || !buffer[29]) {
return resolve(undefined);
}
const width = 1 + ((buffer[24] | (buffer[25] << 8) | (buffer[26] << 16)) & 0xFFFFFF);
const height = 1 + ((buffer[27] | (buffer[28] << 8) | (buffer[29] << 16)) & 0xFFFFFF);
return resolve({ width, height });
} else {
// 未知的WebP子格式
return resolve(undefined);
}
}
});
stream.on('end', () => {
// 如果没有读到足够的字节
if (totalBytes < MAX_HEADER_SIZE) {
resolve(undefined);
}
});
});
}
}
const parsers: ReadonlyArray<ImageParser> = [
new PngParser(),
new JpegParser(),
new BmpParser(),
new GifParser(),
new WebpParser(),
];
export async function detectImageType(filePath: string): Promise<ImageType> {
return new Promise((resolve, reject) => {
const stream = fs.createReadStream(filePath, {
highWaterMark: 64, // 优化读取buffer大小
start: 0,
end: 63
});
let buffer: Buffer | null = null;
stream.once('error', (err) => {
stream.destroy();
reject(err);
});
stream.once('readable', () => {
buffer = stream.read(64) as Buffer;
stream.destroy();
if (!buffer) {
return resolve(ImageType.UNKNOWN);
}
for (const parser of parsers) {
if (parser.canParse(buffer)) {
return resolve(parser.type);
}
}
resolve(ImageType.UNKNOWN);
});
stream.once('end', () => {
if (!buffer) {
resolve(ImageType.UNKNOWN);
}
});
});
}
export async function imageSizeFromFile(filePath: string): Promise<ImageSize | undefined> {
try {
// 先检测类型
const type = await detectImageType(filePath);
const parser = parsers.find(p => p.type === type);
if (!parser) {
return undefined;
}
// 用流式方式解析尺寸
const stream = fs.createReadStream(filePath);
try {
return await parser.parseSize(stream);
} catch (err) {
console.error(`解析图片尺寸出错: ${err}`);
return undefined;
} finally {
if (!stream.destroyed) {
stream.destroy();
}
}
} catch (err) {
console.error(`检测图片类型出错: ${err}`);
return undefined;
}
}
export async function imageSizeFallBack(
filePath: string,
fallback: ImageSize = {
width: 1024,
height: 1024,
}
): Promise<ImageSize> {
return await imageSizeFromFile(filePath) ?? fallback;
}

View File

@@ -3,7 +3,7 @@ import { OneBotAction } from '../OneBotAction';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
bot_appid: Type.String(), bot_appid: Type.String(),
button_id: Type.String({ default: '' }), button_id: Type.String({ default: '' }),
callback_data: Type.String({ default: '' }), callback_data: Type.String({ default: '' }),
@@ -25,6 +25,6 @@ export class ClickInlineKeyboardButton extends OneBotAction<Payload, unknown> {
callback_data: payload.callback_data, callback_data: payload.callback_data,
dmFlag: 0, dmFlag: 0,
chatType: 2 chatType: 2
}) });
} }
} }

View File

@@ -3,7 +3,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
count: Type.Union([Type.Number(), Type.String()], { default: 48 }), count: Type.Number({ default: 48 }),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -5,7 +5,7 @@ import { MessageUnique } from '@/common/message-unique';
import { type NTQQMsgApi } from '@/core/apis'; import { type NTQQMsgApi } from '@/core/apis';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
emojiId: Type.Union([Type.Number(), Type.String()]), emojiId: Type.Union([Type.Number(), Type.String()]),
emojiType: Type.Union([Type.Number(), Type.String()]), emojiType: Type.Union([Type.Number(), Type.String()]),
count: Type.Union([Type.Number(), Type.String()], { default: 20 }), count: Type.Union([Type.Number(), Type.String()], { default: 20 }),
@@ -18,7 +18,7 @@ export class FetchEmojiLike extends OneBotAction<Payload, Awaited<ReturnType<NTQ
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msgIdPeer = MessageUnique.getInnerData(payload.message_id);
if (!msgIdPeer) throw new Error('消息不存在'); if (!msgIdPeer) throw new Error('消息不存在');
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0]; const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
if (!msg) throw new Error('消息不存在'); if (!msg) throw new Error('消息不存在');

View File

@@ -4,7 +4,7 @@ import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
import { Type, Static } from '@sinclair/typebox'; import { Type, Static } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
chat_type: Type.Union([Type.Union([Type.Number(), Type.String()])], { default: 1 }), chat_type: Type.Union([Type.Union([Type.Number(), Type.String()])], { default: 1 }),
}); });

View File

@@ -4,8 +4,8 @@ import { ActionName } from '@/onebot/action/router';
import { Type, Static } from '@sinclair/typebox'; import { Type, Static } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
category: Type.Union([Type.Number(), Type.String()]), category: Type.Number({ default: 0 }),
count: Type.Union([Type.Union([Type.Number(), Type.String()])], { default: 1 }), count: Type.Number({ default: 1 }),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { Type, Static } from '@sinclair/typebox'; import { Type, Static } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -4,9 +4,9 @@ import { ActionName } from '@/onebot/action/router';
import { Type, Static } from '@sinclair/typebox'; import { Type, Static } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), user_id: Type.Optional(Type.String()),
start: Type.Union([Type.Number(), Type.String()], { default: 0 }), start: Type.String({ default: '0' }),
count: Type.Union([Type.Number(), Type.String()], { default: 10 }) count: Type.String({ default: '10' })
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -36,7 +36,7 @@ export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
uint64_uin: self_id, uint64_uin: self_id,
uint64_top: 0, uint64_top: 0,
uint32_req_num: 99, uint32_req_num: 99,
bytes_cookies: "" bytes_cookies: ''
}; };
const packed_data = await this.pack_data(JSON.stringify(req_json)); const packed_data = await this.pack_data(JSON.stringify(req_json));
const data = Buffer.from(packed_data).toString('hex'); const data = Buffer.from(packed_data).toString('hex');

View File

@@ -3,7 +3,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -4,7 +4,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_id: Type.String(), file_id: Type.String(),
current_parent_directory: Type.String(), current_parent_directory: Type.String(),
target_parent_directory: Type.String(), target_parent_directory: Type.String(),

View File

@@ -4,7 +4,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_id: Type.String(), file_id: Type.String(),
current_parent_directory: Type.String(), current_parent_directory: Type.String(),
new_name: Type.String(), new_name: Type.String(),

View File

@@ -3,8 +3,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
face_id: Type.Union([Type.Number(), Type.String()]),// 参考 face_config.json 的 QSid face_id: Type.String(),// 参考 face_config.json 的 QSid
face_type: Type.Union([Type.Number(), Type.String()], { default: '1' }), face_type: Type.String({ default: '1' }),
wording: Type.String({ default: ' ' }), wording: Type.String({ default: ' ' }),
}); });
@@ -16,9 +16,9 @@ export class SetDiyOnlineStatus extends OneBotAction<Payload, string> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const ret = await this.core.apis.UserApi.setDiySelfOnlineStatus( const ret = await this.core.apis.UserApi.setDiySelfOnlineStatus(
payload.face_id.toString(), payload.face_id,
payload.wording, payload.wording,
payload.face_type.toString(), payload.face_type,
); );
if (ret.result !== 0) { if (ret.result !== 0) {
throw new Error('设置在线状态失败'); throw new Error('设置在线状态失败');

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -4,7 +4,7 @@ import { ChatType } from '@/core';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
event_type: Type.Number(), event_type: Type.Number(),
}); });

View File

@@ -3,9 +3,9 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
status: Type.Union([Type.Number(), Type.String()]), status: Type.Number(),
ext_status: Type.Union([Type.Number(), Type.String()]), ext_status: Type.Number(),
battery_status: Type.Union([Type.Number(), Type.String()]), battery_status: Type.Number(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -16,9 +16,9 @@ export class SetOnlineStatus extends OneBotAction<Payload, null> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const ret = await this.core.apis.UserApi.setSelfOnlineStatus( const ret = await this.core.apis.UserApi.setSelfOnlineStatus(
+payload.status, payload.status,
+payload.ext_status, payload.ext_status,
+payload.battery_status, payload.battery_status,
); );
if (ret.result !== 0) { if (ret.result !== 0) {
throw new Error('设置在线状态失败'); throw new Error('设置在线状态失败');

View File

@@ -3,8 +3,8 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
special_title: Type.String({ default: '' }), special_title: Type.String({ default: '' }),
}); });

View File

@@ -4,8 +4,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), user_id: Type.Optional(Type.String()),
group_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), group_id: Type.Optional(Type.String()),
phoneNumber: Type.String({ default: '' }), phoneNumber: Type.String({ default: '' }),
}); });
@@ -29,7 +29,7 @@ export class SharePeer extends OneBotAction<Payload, GeneralCallResult & {
} }
const SchemaDataGroupEx = Type.Object({ const SchemaDataGroupEx = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type PayloadGroupEx = Static<typeof SchemaDataGroupEx>; type PayloadGroupEx = Static<typeof SchemaDataGroupEx>;

View File

@@ -4,7 +4,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_id: Type.String(), file_id: Type.String(),
}); });

View File

@@ -4,7 +4,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_id: Type.String(), file_id: Type.String(),
}); });

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
folder_name: Type.String(), folder_name: Type.String(),
}); });

View File

@@ -6,7 +6,7 @@ import { Static, Type } from '@sinclair/typebox';
import { NTQQGroupApi } from '@/core/apis'; import { NTQQGroupApi } from '@/core/apis';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_id: Type.String(), file_id: Type.String(),
}); });

View File

@@ -4,7 +4,7 @@ import { Static, Type } from '@sinclair/typebox';
import { NTQQGroupApi } from '@/core/apis'; import { NTQQGroupApi } from '@/core/apis';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
folder_id: Type.Optional(Type.String()), folder_id: Type.Optional(Type.String()),
folder: Type.Optional(Type.String()), folder: Type.Optional(Type.String()),
}); });

View File

@@ -117,8 +117,7 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<Payload, {
} }
throw new Error('ResId无效: 找不到相关的聊天记录'); throw new Error('ResId无效: 找不到相关的聊天记录');
} }
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString()); const rootMsg = MessageUnique.getInnerData(msgId);
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
if (rootMsg) { if (rootMsg) {
// 5. 获取消息内容 // 5. 获取消息内容

View File

@@ -12,7 +12,7 @@ interface Response {
} }
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.String(), user_id: Type.String(),
message_seq: Type.Optional(Type.String()), message_id: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }), count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false }) reverseOrder: Type.Boolean({ default: false })
}); });
@@ -24,24 +24,24 @@ export default class GetFriendMsgHistory extends OneBotAction<Payload, Response>
override actionName = ActionName.GetFriendMsgHistory; override actionName = ActionName.GetFriendMsgHistory;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig): Promise<Response> { async _handle(payload: Payload, _adapter: string, _config: NetworkAdapterConfig): Promise<Response> {
//处理参数 //处理参数
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) throw new Error(`记录${payload.user_id}不存在`); if (!uid) throw new Error(`记录${payload.user_id}不存在`);
const friend = await this.core.apis.FriendApi.isBuddy(uid); const friend = await this.core.apis.FriendApi.isBuddy(uid);
const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid }; const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0'); const hasMessageId = !payload.message_id ? !!payload.message_id : !(payload.message_id?.toString() === '' || payload.message_id?.toString() === '0');
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0'; const startMsgId = hasMessageId ? (MessageUnique.getInnerData(payload.message_id!)?.MsgId ?? payload.message_id!.toString()) : '0';
const msgList = hasMessageSeq ? const msgList = hasMessageId ?
(await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverseOrder)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList; (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverseOrder)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList;
if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`); if (msgList.length === 0) throw new Error(`消息${payload.message_id}不存在`);
//转换序号 //转换序号
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.getOutputData({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId, msg.msgSeq);
})); }));
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat))) msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined); ).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList }; return { 'messages': ob11MsgList };
} }

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]) group_id: Type.String()
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]) group_id: Type.String()
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -5,7 +5,7 @@ import { OB11Construct } from '@/onebot/helper/data';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
folder_id: Type.Optional(Type.String()), folder_id: Type.Optional(Type.String()),
folder: Type.Optional(Type.String()), folder: Type.Optional(Type.String()),
file_count: Type.Union([Type.Number(), Type.String()], { default: 50 }), file_count: Type.Union([Type.Number(), Type.String()], { default: 50 }),

View File

@@ -4,7 +4,7 @@ import { WebHonorType } from '@/core/types';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
type: Type.Optional(Type.Enum(WebHonorType)) type: Type.Optional(Type.Enum(WebHonorType))
}); });

View File

@@ -4,15 +4,13 @@ import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer } from '@/core/types'; import { ChatType, Peer } from '@/core/types';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
import { NetworkAdapterConfig } from '@/onebot/config/config';
interface Response { interface Response {
messages: OB11Message[]; messages: OB11Message[];
} }
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.String(), group_id: Type.String(),
message_seq: Type.Optional(Type.String()), message_id: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }), count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false }) reverseOrder: Type.Boolean({ default: false })
}); });
@@ -25,21 +23,21 @@ export default class GoCQHTTPGetGroupMsgHistory extends OneBotAction<Payload, Re
override actionName = ActionName.GoCQHTTP_GetGroupMsgHistory; override actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig): Promise<Response> { async _handle(payload: Payload, _adapter: string): Promise<Response> {
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() }; const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0'); const hasMessageSeq = !payload.message_id ? !!payload.message_id : !(payload.message_id?.toString() === '' || payload.message_id?.toString() === '0');
//拉取消息 //拉取消息
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0'; const startMsgId = hasMessageSeq ? (MessageUnique.getInnerData(payload.message_id!)?.MsgId ?? payload.message_id!.toString()) : '0';
const msgList = hasMessageSeq ? const msgList = hasMessageSeq ?
(await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverseOrder)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList; (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, +payload.count, payload.reverseOrder)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, +payload.count)).msgList;
if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`); if (msgList.length === 0) throw new Error(`消息${payload.message_id}不存在`);
//转换序号 //转换序号
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.getOutputData({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId, msg.msgSeq);
})); }));
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat))) msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined); ).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList }; return { 'messages': ob11MsgList };
} }

View File

@@ -6,7 +6,7 @@ import { OB11Construct } from '@/onebot/helper/data';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file_count: Type.Union([Type.Number(), Type.String()], { default: 50 }), file_count: Type.Union([Type.Number(), Type.String()], { default: 50 }),
}); });

View File

@@ -6,7 +6,7 @@ import { calcQQLevel } from '@/common/helper';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
no_cache: Type.Union([Type.Boolean(), Type.String()], { default: false }), no_cache: Type.Union([Type.Boolean(), Type.String()], { default: false }),
}); });
@@ -28,7 +28,7 @@ export default class GoCQHTTPGetStrangerInfo extends OneBotAction<Payload, OB11U
...extendData.detail.simpleInfo.baseInfo, ...extendData.detail.simpleInfo.baseInfo,
...extendData.detail.simpleInfo.relationFlags ?? {}, ...extendData.detail.simpleInfo.relationFlags ?? {},
...extendData.detail.simpleInfo.status ?? {}, ...extendData.detail.simpleInfo.status ?? {},
user_id: parseInt(extendData.detail.uin) ?? 0, user_id: extendData.detail.uin ?? 0,
uid: info.uid ?? uid, uid: info.uid ?? uid,
nickname: extendData.detail.simpleInfo.coreInfo.nick ?? '', nickname: extendData.detail.simpleInfo.coreInfo.nick ?? '',
age: extendData.detail.simpleInfo.baseInfo.age ?? info.age, age: extendData.detail.simpleInfo.baseInfo.age ?? info.age,

View File

@@ -3,8 +3,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
friend_id: Type.Optional(Type.Union([Type.String(), Type.Number()])), friend_id: Type.Optional(Type.String()),
user_id: Type.Optional(Type.Union([Type.String(), Type.Number()])), user_id: Type.Optional(Type.String()),
temp_block: Type.Optional(Type.Boolean()), temp_block: Type.Optional(Type.Boolean()),
temp_both_del: Type.Optional(Type.Boolean()), temp_both_del: Type.Optional(Type.Boolean()),
}); });

View File

@@ -1,19 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { QuickAction, QuickActionEvent } from '@/onebot/types';
interface Payload {
context: QuickActionEvent,
operation: QuickAction
}
export class GoCQHTTPHandleQuickAction extends OneBotAction<Payload, null> {
override actionName = ActionName.GoCQHTTP_HandleQuickAction;
async _handle(payload: Payload): Promise<null> {
this.obContext.apis.QuickActionApi
.handleQuickOperation(payload.context, payload.operation)
.catch(e => this.core.context.logger.logError(e));
return null;
}
}

View File

@@ -5,7 +5,7 @@ import { unlink } from 'node:fs/promises';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
content: Type.String(), content: Type.String(),
image: Type.Optional(Type.String()), image: Type.Optional(Type.String()),
pinned: Type.Union([Type.Number(), Type.String()], { default: 0 }), pinned: Type.Union([Type.Number(), Type.String()], { default: 0 }),

View File

@@ -6,7 +6,7 @@ import fs from 'node:fs/promises';
import { GeneralCallResult } from '@/core'; import { GeneralCallResult } from '@/core';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
file: Type.String(), file: Type.String(),
group_id: Type.Union([Type.Number(), Type.String()]) group_id: Type.String()
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -7,7 +7,7 @@ import { SendMessageContext } from '@/onebot/api';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
file: Type.String(), file: Type.String(),
name: Type.String(), name: Type.String(),
folder: Type.Optional(Type.String()), folder: Type.Optional(Type.String()),

View File

@@ -8,7 +8,7 @@ import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
file: Type.String(), file: Type.String(),
name: Type.String(), name: Type.String(),
}); });

View File

@@ -4,7 +4,7 @@ import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -13,7 +13,7 @@ export default class DelEssenceMsg extends OneBotAction<Payload, unknown> {
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<unknown> { async _handle(payload: Payload): Promise<unknown> {
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msg = MessageUnique.getInnerData(payload.message_id);
if (!msg) { if (!msg) {
const data = this.core.apis.GroupApi.essenceLRU.getValue(+payload.message_id); const data = this.core.apis.GroupApi.essenceLRU.getValue(+payload.message_id);
if(!data) throw new Error('消息不存在'); if(!data) throw new Error('消息不存在');

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
notice_id: Type.String() notice_id: Type.String()
}); });

View File

@@ -5,7 +5,7 @@ import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
character: Type.String(), character: Type.String(),
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
text: Type.String(), text: Type.String(),
}); });

View File

@@ -4,10 +4,8 @@ import { ActionName } from '@/onebot/action/router';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import crypto from 'crypto'; import crypto from 'crypto';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
import { NetworkAdapterConfig } from '@/onebot/config/config';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -22,12 +20,12 @@ export class GetGroupEssence extends OneBotAction<Payload, unknown> {
return undefined; return undefined;
} }
return { return {
id: MessageUnique.createUniqueMsgId(peer, replyMsgList.msgId), id: MessageUnique.getOutputData(peer, replyMsgList.msgId, replyMsgList.msgSeq),
msg: replyMsgList msg: replyMsgList
}; };
} }
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig) { async _handle(payload: Payload, _adapter: string) {
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list); const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
if (!msglist) { if (!msglist) {
throw new Error('获取失败'); throw new Error('获取失败');
@@ -48,7 +46,7 @@ export class GetGroupEssence extends OneBotAction<Payload, unknown> {
operator_nick: msg.add_digest_nick, operator_nick: msg.add_digest_nick,
message_id: message_id, message_id: message_id,
operator_time: msg.add_digest_time, operator_time: msg.add_digest_time,
content: (await this.obContext.apis.MsgApi.parseMessage(rawMessage, config.messagePostFormat))?.message content: (await this.obContext.apis.MsgApi.parseMessage(rawMessage))?.message
}; };
} }
const msgTempData = JSON.stringify({ const msgTempData = JSON.stringify({

View File

@@ -5,7 +5,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -5,8 +5,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
no_cache: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), no_cache: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
}); });

View File

@@ -6,7 +6,7 @@ import { Static, Type } from '@sinclair/typebox';
import { GroupMember } from '@/core'; import { GroupMember } from '@/core';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
no_cache: Type.Optional(Type.Union([Type.Boolean(), Type.String()])) no_cache: Type.Optional(Type.Union([Type.Boolean(), Type.String()]))
}); });

View File

@@ -17,7 +17,7 @@ interface GroupNotice {
} }
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -4,7 +4,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -3,8 +3,8 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -5,7 +5,7 @@ import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
character: Type.String(), character: Type.String(),
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
text: Type.String(), text: Type.String(),
}); });

View File

@@ -4,7 +4,7 @@ import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -14,7 +14,7 @@ export default class SetEssenceMsg extends OneBotAction<Payload, unknown> {
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msg = MessageUnique.getInnerData(payload.message_id);
if (!msg) { if (!msg) {
throw new Error('msg not found'); throw new Error('msg not found');
} }

View File

@@ -4,8 +4,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
enable: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), enable: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
}); });

View File

@@ -3,9 +3,9 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
duration: Type.Union([Type.Number(), Type.String()], { default: 0 }), duration: Type.String({ default: '0' }),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -3,8 +3,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
card: Type.Optional(Type.String()) card: Type.Optional(Type.String())
}); });

View File

@@ -3,8 +3,8 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
reject_add_request: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), reject_add_request: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
}); });

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
is_dismiss: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), is_dismiss: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
}); });

View File

@@ -4,7 +4,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
group_name: Type.String(), group_name: Type.String(),
}); });

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.String(),
enable: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), enable: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
}); });

View File

@@ -30,7 +30,6 @@ import SetGroupAdmin from './group/SetGroupAdmin';
import SetGroupCard from './group/SetGroupCard'; import SetGroupCard from './group/SetGroupCard';
import GetImage from './file/GetImage'; import GetImage from './file/GetImage';
import GetRecord from './file/GetRecord'; import GetRecord from './file/GetRecord';
import { GoCQHTTPMarkMsgAsRead, MarkAllMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead';
import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile'; import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile';
import SetQQAvatar from '@/onebot/action/extends/SetQQAvatar'; import SetQQAvatar from '@/onebot/action/extends/SetQQAvatar';
import GoCQHTTPDownloadFile from './go-cqhttp/DownloadFile'; import GoCQHTTPDownloadFile from './go-cqhttp/DownloadFile';
@@ -48,7 +47,6 @@ import { ForwardFriendSingleMsg, ForwardGroupSingleMsg } from '@/onebot/action/m
import { GetFriendWithCategory } from './extends/GetFriendWithCategory'; 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 { GetGroupIgnoredNotifies } from './group/GetGroupIgnoredNotifies'; 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';
@@ -138,8 +136,6 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SetLongNick(obContext, core), new SetLongNick(obContext, core),
new ForwardFriendSingleMsg(obContext, core), new ForwardFriendSingleMsg(obContext, core),
new ForwardGroupSingleMsg(obContext, core), new ForwardGroupSingleMsg(obContext, core),
new MarkGroupMsgAsRead(obContext, core),
new MarkPrivateMsgAsRead(obContext, core),
new SetQQAvatar(obContext, core), new SetQQAvatar(obContext, core),
new TranslateEnWordToZn(obContext, core), new TranslateEnWordToZn(obContext, core),
new GetGroupRootFiles(obContext, core), new GetGroupRootFiles(obContext, core),
@@ -199,17 +195,14 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GoCQHTTPGetStrangerInfo(obContext, core), new GoCQHTTPGetStrangerInfo(obContext, core),
new GoCQHTTPDownloadFile(obContext, core), new GoCQHTTPDownloadFile(obContext, core),
new GetGuildList(obContext, core), new GetGuildList(obContext, core),
new GoCQHTTPMarkMsgAsRead(obContext, core),
new GoCQHTTPUploadGroupFile(obContext, core), new GoCQHTTPUploadGroupFile(obContext, core),
new GoCQHTTPGetGroupMsgHistory(obContext, core), new GoCQHTTPGetGroupMsgHistory(obContext, core),
new GoCQHTTPGetForwardMsgAction(obContext, core), new GoCQHTTPGetForwardMsgAction(obContext, core),
new GetFriendMsgHistory(obContext, core), new GetFriendMsgHistory(obContext, core),
new GoCQHTTPHandleQuickAction(obContext, core),
new GetGroupIgnoredNotifies(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),
new MarkAllMsgAsRead(obContext, core),
new GetProfileLike(obContext, core), new GetProfileLike(obContext, core),
new SetGroupPortrait(obContext, core), new SetGroupPortrait(obContext, core),
new FetchCustomFace(obContext, core), new FetchCustomFace(obContext, core),

View File

@@ -4,7 +4,7 @@ import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -14,7 +14,7 @@ class DeleteMsg extends OneBotAction<Payload, void> {
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const msg = MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id)); const msg = MessageUnique.getInnerData(payload.message_id);
if (msg) { if (msg) {
await this.core.apis.MsgApi.recallMsg(msg.Peer, msg.MsgId); await this.core.apis.MsgApi.recallMsg(msg.Peer, msg.MsgId);
} else { } else {

View File

@@ -5,9 +5,9 @@ import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
group_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), group_id: Type.Optional(Type.String()),
user_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), user_id: Type.Optional(Type.String()),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -25,7 +25,7 @@ class ForwardSingleMsg extends OneBotAction<Payload, null> {
} }
async _handle(payload: Payload): Promise<null> { async _handle(payload: Payload): Promise<null> {
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msg = MessageUnique.getInnerData(payload.message_id);
if (!msg) { if (!msg) {
throw new Error(`无法找到消息${payload.message_id}`); throw new Error(`无法找到消息${payload.message_id}`);
} }

View File

@@ -4,12 +4,10 @@ import { ActionName } from '@/onebot/action/router';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import { RawMessage } from '@/core'; import { RawMessage } from '@/core';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
import { NetworkAdapterConfig } from '@/onebot/config/config';
export type ReturnDataType = OB11Message export type ReturnDataType = OB11Message
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -18,30 +16,27 @@ class GetMsg extends OneBotAction<Payload, OB11Message> {
override actionName = ActionName.GetMsg; override actionName = ActionName.GetMsg;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig) { async _handle(payload: Payload, _adapter: string) {
if (!payload.message_id) { if (!payload.message_id) {
throw Error('参数message_id不能为空'); throw Error('参数message_id不能为空');
} }
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString()); const msgIdWithPeer = MessageUnique.getInnerData(payload.message_id);
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId ?? +payload.message_id);
if (!msgIdWithPeer) { if (!msgIdWithPeer) {
throw new Error('消息不存在'); throw new Error('消息不存在');
} }
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType }; const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
const orimsg = this.obContext.recallMsgCache.get(msgIdWithPeer.MsgId); const orimsg = this.obContext.recallMsgCache.get(msgIdWithPeer.MsgId);
let msg: RawMessage|undefined; let msg: RawMessage | undefined;
if (orimsg) { if (orimsg) {
msg = orimsg; msg = orimsg;
} else { } else {
msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0]; msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0];
} }
if (!msg) throw Error('消息不存在'); if (!msg) throw Error('消息不存在');
const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat); const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg);
if (!retMsg) throw Error('消息为空'); if (!retMsg) throw Error('消息为空');
try { try {
retMsg.message_id = MessageUnique.createUniqueMsgId(peer, msg.msgId)!; retMsg.message_id = MessageUnique.getOutputData(peer, msg.msgId, msg.msgSeq)!;
retMsg.message_seq = retMsg.message_id;
retMsg.real_id = retMsg.message_id;
} catch { } catch {
// ignored // ignored
} }

View File

@@ -1,72 +0,0 @@
import { ChatType, Peer } from '@/core/types';
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
user_id: Type.Optional(Type.Union([Type.String(), Type.Number()])),
group_id: Type.Optional(Type.Union([Type.String(), Type.Number()])),
message_id: Type.Optional(Type.Union([Type.String(), Type.Number()])),
});
type PlayloadType = Static<typeof SchemaData>;
class MarkMsgAsRead extends OneBotAction<PlayloadType, null> {
async getPeer(payload: PlayloadType): Promise<Peer> {
if (payload.message_id) {
const s_peer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)?.Peer;
if (s_peer) {
return s_peer;
}
const l_peer = MessageUnique.getPeerByMsgId(payload.message_id.toString())?.Peer;
if (l_peer) {
return l_peer;
}
}
if (payload.user_id) {
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
if (!peerUid) {
throw new Error( `私聊${payload.user_id}不存在`);
}
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
if (!payload.group_id) {
throw new Error('缺少参数 group_id 或 user_id');
}
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
}
async _handle(payload: PlayloadType): Promise<null> {
const ret = await this.core.apis.MsgApi.setMsgRead(await this.getPeer(payload));
if (ret.result != 0) {
throw new Error('设置已读失败,' + ret.errMsg);
}
return null;
}
}
// 以下为非标准实现
export class MarkPrivateMsgAsRead extends MarkMsgAsRead {
override payloadSchema = SchemaData;
override actionName = ActionName.MarkPrivateMsgAsRead;
}
export class MarkGroupMsgAsRead extends MarkMsgAsRead {
override payloadSchema = SchemaData;
override actionName = ActionName.MarkGroupMsgAsRead;
}
export class GoCQHTTPMarkMsgAsRead extends MarkMsgAsRead {
override actionName = ActionName.GoCQHTTP_MarkMsgAsRead;
}
export class MarkAllMsgAsRead extends OneBotAction<void, null> {
override actionName = ActionName._MarkAllMsgAsRead;
async _handle(): Promise<null> {
await this.core.apis.MsgApi.markAllMsgAsRead();
return null;
}
}

View File

@@ -7,7 +7,6 @@ import {
OB11PostSendMsg, OB11PostSendMsg,
} from '@/onebot/types'; } from '@/onebot/types';
import { ActionName, BaseCheckResult } from '@/onebot/action/router'; import { ActionName, BaseCheckResult } from '@/onebot/action/router';
import { decodeCQCode } from '@/onebot/helper/cqcode';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, SendMessageElement } from '@/core'; import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, SendMessageElement } from '@/core';
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
@@ -17,7 +16,7 @@ import { PacketMsg } from '@/core/packet/message/message';
import { rawMsgWithSendMsg } from '@/core/packet/message/converter'; import { rawMsgWithSendMsg } from '@/core/packet/message/converter';
export interface ReturnDataType { export interface ReturnDataType {
message_id: number; message_id: string;
res_id?: string; res_id?: string;
} }
@@ -28,11 +27,9 @@ export enum ContextMode {
} }
// Normalizes a mixed type (CQCode/a single segment/segment array) into a segment array. // Normalizes a mixed type (CQCode/a single segment/segment array) into a segment array.
export function normalize(message: OB11MessageMixType, autoEscape = false): OB11MessageData[] { export function normalize(message: OB11MessageMixType): OB11MessageData[] {
return typeof message === 'string' ? ( return typeof message === 'string' ? (
autoEscape ? [{ type: OB11MessageDataType.text, data: { text: message } }]
[{ type: OB11MessageDataType.text, data: { text: message } }] :
decodeCQCode(message)
) : Array.isArray(message) ? message : [message]; ) : Array.isArray(message) ? message : [message];
} }
@@ -54,7 +51,7 @@ export async function createContext(core: NapCatCore, payload: OB11PostContext |
chatType: ChatType.KCHATTYPEGROUP, chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(), peerUid: payload.group_id.toString(),
guildId: '' guildId: ''
} };
} }
throw new Error('无法获取用户信息'); throw new Error('无法获取用户信息');
} }
@@ -124,10 +121,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
if (payload.message_type === 'private') this.contextMode = ContextMode.Private; if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
const peer = await createContext(this.core, payload, this.contextMode); const peer = await createContext(this.core, payload, this.contextMode);
const messages = normalize( const messages = normalize(payload.message);
payload.message,
typeof payload.auto_escape === 'string' ? payload.auto_escape === 'true' : !!payload.auto_escape,
);
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) { if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const packetMode = this.core.apis.PacketApi.available; const packetMode = this.core.apis.PacketApi.available;
@@ -143,11 +137,14 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
throw Error('发送合并转发消息失败returnMsgAndResId 为空!'); throw Error('发送合并转发消息失败returnMsgAndResId 为空!');
} }
if (returnMsgAndResId.message) { if (returnMsgAndResId.message) {
const msgShortId = MessageUnique.createUniqueMsgId({ const msgShortId = MessageUnique.getOutputData({
guildId: '', guildId: '',
peerUid: peer.peerUid, peerUid: peer.peerUid,
chatType: peer.chatType, chatType: peer.chatType,
}, (returnMsgAndResId.message).msgId); },
(returnMsgAndResId.message).msgId,
(returnMsgAndResId.message).msgSeq
);
return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id! }; return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id! };
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) { } else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`); throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`);
@@ -214,7 +211,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
packetMsg.push(transformedMsg); packetMsg.push(transformedMsg);
} else if (node.data.id) { } else if (node.data.id) {
const id = node.data.id; const id = node.data.id;
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id); const nodeMsg = MessageUnique.getInnerData(id);
if (!nodeMsg) { if (!nodeMsg) {
this.core.context.logger.logError('转发消息失败,未找到消息', id); this.core.context.logger.logError('转发消息失败,未找到消息', id);
continue; continue;
@@ -273,17 +270,17 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
chatType: ChatType.KCHATTYPEC2C, chatType: ChatType.KCHATTYPEC2C,
peerUid: this.core.selfInfo.uid, peerUid: this.core.selfInfo.uid,
}; };
let nodeMsgIds: string[] = []; let nodeMsgIds: { MsgId: string, Peer: Peer }[] = [];
for (const messageNode of messageNodes) { for (const messageNode of messageNodes) {
const nodeId = messageNode.data.id; const nodeId = messageNode.data.id;
if (nodeId) { if (nodeId) {
// 对Msgid和OB11ID混用情况兜底 // 对Msgid和OB11ID混用情况兜底
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId); const nodeMsg = MessageUnique.getInnerData(nodeId);
if (!nodeMsg) { if (!nodeMsg) {
this.core.context.logger.logError('转发消息失败,未找到消息', nodeId); this.core.context.logger.logError('转发消息失败,未找到消息', nodeId);
continue; continue;
} }
nodeMsgIds.push(nodeMsg.MsgId); nodeMsgIds.push({ MsgId: nodeMsg.MsgId, Peer: nodeMsg.Peer });
} else { } else {
// 自定义的消息 // 自定义的消息
try { try {
@@ -297,8 +294,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
} }
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node)); const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
if (nodeMsg) { if (nodeMsg) {
nodeMsgIds.push(nodeMsg.message!.msgId); nodeMsgIds.push({ MsgId: nodeMsg.message!.msgId, Peer: selfPeer });
MessageUnique.createUniqueMsgId(selfPeer, nodeMsg.message!.msgId);
} }
//完成子卡片生成跳过后续 //完成子卡片生成跳过后续
continue; continue;
@@ -324,8 +320,8 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
} }
(await Promise.allSettled(MsgNodeList)).map((result) => { (await Promise.allSettled(MsgNodeList)).map((result) => {
if (result.status === 'fulfilled' && result.value) { if (result.status === 'fulfilled' && result.value) {
nodeMsgIds.push(result.value.msgId); nodeMsgIds.push({ MsgId: result.value.msgId, Peer: selfPeer });
MessageUnique.createUniqueMsgId(selfPeer, result.value.msgId); MessageUnique.getOutputData(selfPeer, result.value.msgId, result.value.msgSeq);
} }
}); });
} catch (e: unknown) { } catch (e: unknown) {
@@ -337,22 +333,18 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
let srcPeer: Peer | undefined = undefined; let srcPeer: Peer | undefined = undefined;
let needSendSelf = false; let needSendSelf = false;
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送 //检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
for (const msgId of nodeMsgIds) { let new_nodeMsgIds: { MsgId: string, Peer: Peer }[] = [];
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId); for (const data of nodeMsgIds) {
if (!nodeMsgPeer) { const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(data.Peer, [data.MsgId])).msgList[0];
this.core.context.logger.logError('转发消息失败,未找到消息', msgId);
continue;
}
const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
if (nodeMsg) { if (nodeMsg) {
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid }; srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
if (srcPeer.peerUid !== nodeMsg.peerUid) { if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true; needSendSelf = true;
} }
nodeMsgArray.push(nodeMsg); new_nodeMsgIds.push({ MsgId: nodeMsg.msgId, Peer: data.Peer });
} }
} }
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId); nodeMsgIds = new_nodeMsgIds;
let retMsgIds: string[] = []; let retMsgIds: string[] = [];
if (needSendSelf) { if (needSendSelf) {
for (const [, msg] of nodeMsgArray.entries()) { for (const [, msg] of nodeMsgArray.entries()) {
@@ -364,7 +356,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId); if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
} }
} else { } else {
retMsgIds = nodeMsgIds; retMsgIds = nodeMsgIds.map((msg) => msg.MsgId);
} }
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空'); if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
try { try {

View File

@@ -4,7 +4,7 @@ import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]), message_id: Type.String(),
emoji_id: Type.Union([Type.Number(), Type.String()]), emoji_id: Type.Union([Type.Number(), Type.String()]),
set: Type.Optional(Type.Union([Type.Boolean(), Type.String()])) set: Type.Optional(Type.Union([Type.Boolean(), Type.String()]))
}); });
@@ -16,7 +16,7 @@ export class SetMsgEmojiLike extends OneBotAction<Payload, unknown> {
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); const msg = MessageUnique.getInnerData(payload.message_id);
if (!msg) { if (!msg) {
throw new Error('msg not found'); throw new Error('msg not found');
} }

View File

@@ -8,7 +8,7 @@ export class GetRkeyEx extends GetPacketStatusDepends<void, unknown> {
let rkeys = await this.core.apis.PacketApi.pkt.operation.FetchRkey(); let rkeys = await this.core.apis.PacketApi.pkt.operation.FetchRkey();
return rkeys.map(rkey => { return rkeys.map(rkey => {
return { return {
type: rkey.type === 10 ? "private" : "group", type: rkey.type === 10 ? 'private' : 'group',
rkey: rkey.rkey, rkey: rkey.rkey,
created_at: rkey.time, created_at: rkey.time,
ttl: rkey.ttl, ttl: rkey.ttl,

View File

@@ -30,7 +30,7 @@ export class GetRkeyServer extends GetPacketStatusDepends<void, { private_rkey?:
private_rkey: privateRkeyItem ? privateRkeyItem.rkey : undefined, private_rkey: privateRkeyItem ? privateRkeyItem.rkey : undefined,
group_rkey: groupRkeyItem ? groupRkeyItem.rkey : undefined, group_rkey: groupRkeyItem ? groupRkeyItem.rkey : undefined,
expired_time: this.expiryTime, expired_time: this.expiryTime,
name: "NapCat 4" name: 'NapCat 4'
}; };
return this.rkeyCache; return this.rkeyCache;

View File

@@ -3,8 +3,8 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Optional(Type.Union([Type.Number(), Type.String()])), group_id: Type.Optional(Type.String()),
user_id: Type.Union([Type.Number(), Type.String()]), user_id: Type.String(),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -3,7 +3,7 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]) user_id: Type.String()
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -1,7 +1,6 @@
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { NetworkAdapterConfig } from '@/onebot/config/config';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
@@ -14,14 +13,14 @@ export default class GetRecentContact extends OneBotAction<Payload, unknown> {
override actionName = ActionName.GetRecentContact; override actionName = ActionName.GetRecentContact;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig) { async _handle(payload: Payload, _adapter: string) {
const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+payload.count); const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+payload.count);
//烘焙消息 //烘焙消息
return await Promise.all(ret.info.changedList.map(async (t) => { return await Promise.all(ret.info.changedList.map(async (t) => {
const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]); const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0 && FastMsg.msgList[0]) { if (FastMsg.msgList.length > 0 && FastMsg.msgList[0]) {
//扩展ret.info.changedList //扩展ret.info.changedList
const lastestMsg = await this.obContext.apis.MsgApi.parseMessage(FastMsg.msgList[0], config.messagePostFormat); const lastestMsg = await this.obContext.apis.MsgApi.parseMessage(FastMsg.msgList[0]);
return { return {
lastestMsg: lastestMsg, lastestMsg: lastestMsg,
peerUin: t.peerUin, peerUin: t.peerUin,

View File

@@ -4,7 +4,7 @@ import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
times: Type.Union([Type.Number(), Type.String()], { default: 1 }), times: Type.Union([Type.Number(), Type.String()], { default: 1 }),
user_id: Type.Union([Type.Number(), Type.String()]) user_id: Type.String()
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;

View File

@@ -19,9 +19,9 @@ export class OneBotFriendApi {
if (poke_uid.length == 2 && poke_uid[0]?.uid && poke_uid[1]?.uid) { if (poke_uid.length == 2 && poke_uid[0]?.uid && poke_uid[1]?.uid) {
return new OB11FriendPokeEvent( return new OB11FriendPokeEvent(
this.core, this.core,
uin, uin.toString(),
parseInt((await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid))), await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid),
parseInt((await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid))), await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid),
pokedetail, pokedetail,
); );
} }

View File

@@ -54,9 +54,9 @@ export class OneBotGroupApi {
if (memberUin && adminUin) { if (memberUin && adminUin) {
return new OB11GroupBanEvent( return new OB11GroupBanEvent(
this.core, this.core,
parseInt(GroupCode), GroupCode,
parseInt(memberUin), memberUin,
parseInt(adminUin), adminUin,
duration, duration,
subType, subType,
); );
@@ -101,9 +101,9 @@ export class OneBotGroupApi {
} }
return new OB11GroupMsgEmojiLikeEvent( return new OB11GroupMsgEmojiLikeEvent(
this.core, this.core,
parseInt(groupCode), groupCode,
parseInt(senderUin), senderUin,
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!, MessageUnique.getOutputData(peer, replyMsg.msgId, replyMsg.msgSeq),
[{ [{
emoji_id: emojiId, emoji_id: emojiId,
count: 1, count: 1,
@@ -116,7 +116,7 @@ export class OneBotGroupApi {
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin); const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) { if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName ?? ''; const newCardName = msg.sendMemberName ?? '';
const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); const event = new OB11GroupCardEvent(this.core, msg.peerUid, msg.senderUin, newCardName, member.cardName);
member.cardName = newCardName; member.cardName = newCardName;
return event; return event;
} }
@@ -137,9 +137,9 @@ export class OneBotGroupApi {
if (poke_uid.length == 2 && poke_uid[0]?.uid && poke_uid[1]?.uid) { if (poke_uid.length == 2 && poke_uid[0]?.uid && poke_uid[1]?.uid) {
return new OB11GroupPokeEvent( return new OB11GroupPokeEvent(
this.core, this.core,
parseInt(msg.peerUid), msg.peerUid,
+await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid), await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid),
+await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid), await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid),
pokedetail, pokedetail,
); );
} }
@@ -156,8 +156,8 @@ export class OneBotGroupApi {
context.logger.logDebug('收到群成员新头衔消息', json); context.logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent( return new OB11GroupTitleEvent(
this.core, this.core,
+msg.peerUid, msg.peerUid,
+memberUin, memberUin,
title, title,
); );
} else if (type === '移出') { } else if (type === '移出') {
@@ -187,10 +187,10 @@ export class OneBotGroupApi {
if (msgData.msgList[0]) { if (msgData.msgList[0]) {
return new OB11GroupEssenceEvent( return new OB11GroupEssenceEvent(
this.core, this.core,
parseInt(msg.peerUid), msg.peerUid,
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, MessageUnique.getOutputData(Peer, msgData.msgList[0].msgId, msgData.msgList[0].msgSeq)!,
parseInt(msgData.msgList[0].senderUin), msgData.msgList[0].senderUin,
parseInt(realMsg?.add_digest_uin ?? '0'), realMsg?.add_digest_uin ?? '0',
); );
} }
return; return;
@@ -200,7 +200,8 @@ export class OneBotGroupApi {
async parseGroupUploadFileEvene(msg: RawMessage, element: FileElement, elementWrapper: MessageElement) { async parseGroupUploadFileEvene(msg: RawMessage, element: FileElement, elementWrapper: MessageElement) {
return new OB11GroupUploadNoticeEvent( return new OB11GroupUploadNoticeEvent(
this.core, this.core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''), msg.peerUid,
msg.senderUin,
{ {
id: FileNapCatOneBotUUID.encode({ id: FileNapCatOneBotUUID.encode({
chatType: ChatType.KCHATTYPEGROUP, chatType: ChatType.KCHATTYPEGROUP,
@@ -218,8 +219,8 @@ export class OneBotGroupApi {
this.core.context.logger.logDebug('收到群名称变更事件', element); this.core.context.logger.logDebug('收到群名称变更事件', element);
return new OB11GroupNameEvent( return new OB11GroupNameEvent(
this.core, this.core,
parseInt(msg.peerUid), msg.peerUid,
parseInt(await this.core.apis.UserApi.getUinByUidV2(element.memberUid)), await this.core.apis.UserApi.getUinByUidV2(element.memberUid),
element.groupName, element.groupName,
); );
} else if (element.type === TipGroupElementType.KSHUTUP) { } else if (element.type === TipGroupElementType.KSHUTUP) {
@@ -231,9 +232,9 @@ export class OneBotGroupApi {
await this.core.apis.GroupApi.refreshGroupMemberCache(msg.peerUid, true); await this.core.apis.GroupApi.refreshGroupMemberCache(msg.peerUid, true);
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
parseInt(msg.peerUid), msg.peerUid,
+this.core.selfInfo.uin, this.core.selfInfo.uin,
element.adminUid ? +await this.core.apis.UserApi.getUinByUidV2(element.adminUid) : 0, element.adminUid ? await this.core.apis.UserApi.getUinByUidV2(element.adminUid) : '0',
'approve' 'approve'
); );
} }
@@ -244,9 +245,9 @@ export class OneBotGroupApi {
async parseSelfInviteEvent(msg: RawMessage, inviterUin: string, inviteeUin: string) { async parseSelfInviteEvent(msg: RawMessage, inviterUin: string, inviteeUin: string) {
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
parseInt(msg.peerUid), msg.peerUid,
+inviteeUin, inviteeUin,
+inviterUin, inviterUin,
'invite' 'invite'
); );
} }
@@ -269,9 +270,9 @@ export class OneBotGroupApi {
if (!new_member) return; if (!new_member) return;
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
+msg.peerUid, msg.peerUid,
+new_member.uin, new_member.uin,
0, '0',
'invite', 'invite',
); );
} }

View File

@@ -1,5 +1,4 @@
export * from './friend'; export * from './friend';
export * from './group'; export * from './group';
export * from './user'; export * from './user';
export * from './msg'; export * from './msg';
export * from './quick-action';

View File

@@ -31,7 +31,6 @@ import {
} from '@/onebot'; } from '@/onebot';
import { OB11Construct } from '@/onebot/helper/data'; import { OB11Construct } from '@/onebot/helper/data';
import { EventType } from '@/onebot/event/OneBotEvent'; import { EventType } from '@/onebot/event/OneBotEvent';
import { encodeCQCode } from '@/onebot/helper/cqcode';
import { uriToLocalFile } from '@/common/file'; import { uriToLocalFile } from '@/common/file';
import { RequestUtil } from '@/common/request'; import { RequestUtil } from '@/common/request';
import fsPromise from 'node:fs/promises'; import fsPromise from 'node:fs/promises';
@@ -85,9 +84,6 @@ export class OneBotMsgApi {
textElement: async element => { textElement: async element => {
if (element.atType === NTMsgAtType.ATTYPEUNKNOWN) { if (element.atType === NTMsgAtType.ATTYPEUNKNOWN) {
let text = element.content; let text = element.content;
if (!text.trim()) {
return null;
}
// 兼容 9.7.x 换行符 // 兼容 9.7.x 换行符
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) { if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
text = text.replace(/\r/g, '\n'); text = text.replace(/\r/g, '\n');
@@ -98,15 +94,16 @@ export class OneBotMsgApi {
}; };
} else { } else {
let qq: string = 'all'; let qq: string = 'all';
const { atNtUid, atUid, content } = element;
if (element.atType !== NTMsgAtType.ATTYPEALL) { if (element.atType !== NTMsgAtType.ATTYPEALL) {
const { atNtUid, atUid } = element;
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : String(Number(atUid) >>> 0); qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : String(Number(atUid) >>> 0);
} }
return { return {
type: OB11MessageDataType.at, type: OB11MessageDataType.at,
data: { data: {
qq: qq, id: qq,
// name: content.slice(1); name: content.slice(1)
}, },
}; };
} }
@@ -153,7 +150,7 @@ export class OneBotMsgApi {
if (this.core.apis.PacketApi.available) { if (this.core.apis.PacketApi.available) {
let url; let url;
try { try {
url = await this.core.apis.FileApi.getFileUrl(msg.chatType, msg.peerUid, element.fileUuid, element.file10MMd5) url = await this.core.apis.FileApi.getFileUrl(msg.chatType, msg.peerUid, element.fileUuid, element.file10MMd5);
} catch (error) { } catch (error) {
url = ''; url = '';
} }
@@ -166,7 +163,7 @@ export class OneBotMsgApi {
file_size: element.fileSize, file_size: element.fileSize,
url: url, url: url,
}, },
} };
} }
} }
return { return {
@@ -251,10 +248,10 @@ export class OneBotMsgApi {
}; };
// 创建回复数据的通用方法 // 创建回复数据的通用方法
const createReplyData = (msgId: string): OB11MessageData => ({ const createReplyData = (msgId: string, msgSeq: string): OB11MessageData => ({
type: OB11MessageDataType.reply, type: OB11MessageDataType.reply,
data: { data: {
id: MessageUnique.createUniqueMsgId(peer, msgId).toString(), id: MessageUnique.getOutputData(peer, msgId, msgSeq).toString(),
}, },
}); });
@@ -263,7 +260,7 @@ export class OneBotMsgApi {
// 特定账号的特殊处理 // 特定账号的特殊处理
if (records && (records.peerUin === '284840486' || records.peerUin === '1094950020')) { if (records && (records.peerUin === '284840486' || records.peerUin === '1094950020')) {
return createReplyData(records.msgId); return createReplyData(records.msgId, records.msgSeq);
} }
// 获取消息的通用方法组 // 获取消息的通用方法组
@@ -329,7 +326,7 @@ export class OneBotMsgApi {
); );
if (replyMsg) { if (replyMsg) {
return createReplyData(replyMsg.msgId); return createReplyData(replyMsg.msgId, replyMsg.msgSeq);
} }
this.core.context.logger.logError('所有查找方法均失败,获取不到带记录的引用消息', element.replayMsgSeq); this.core.context.logger.logError('所有查找方法均失败,获取不到带记录的引用消息', element.replayMsgSeq);
@@ -340,13 +337,13 @@ export class OneBotMsgApi {
const replyMsg = await tryFetchMethods(element.replayMsgSeq); const replyMsg = await tryFetchMethods(element.replayMsgSeq);
if (replyMsg) { if (replyMsg) {
return createReplyData(replyMsg.msgId); return createReplyData(replyMsg.msgId, replyMsg.msgSeq);
} }
this.core.context.logger.logError('所有查找方法均失败,获取不到旧客户端的引用消息', element.replayMsgSeq); //this.core.context.logger.logError('所有查找方法均失败,获取不到旧客户端的引用消息', element.replayMsgSeq);
} }
return null; return createReplyData('-1', element.replayMsgSeq);
}, },
videoElement: async (element, msg, elementWrapper) => { videoElement: async (element, msg, elementWrapper) => {
const peer = { const peer = {
@@ -444,7 +441,7 @@ export class OneBotMsgApi {
url: pttUrl, url: pttUrl,
file_size: element.fileSize, file_size: element.fileSize,
}, },
} };
} }
return { return {
type: OB11MessageDataType.voice, type: OB11MessageDataType.voice,
@@ -518,7 +515,7 @@ export class OneBotMsgApi {
}, },
}), }),
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => { [OB11MessageDataType.at]: async ({ data: { id: atQQ } }, context) => {
function at(atUid: string, atNtUid: string, atType: NTMsgAtType, atName: string): SendTextElement { function at(atUid: string, atNtUid: string, atType: NTMsgAtType, atName: string): SendTextElement {
return { return {
elementType: ElementType.TEXT, elementType: ElementType.TEXT,
@@ -546,7 +543,7 @@ export class OneBotMsgApi {
}, },
[OB11MessageDataType.reply]: async ({ data: { id } }) => { [OB11MessageDataType.reply]: async ({ data: { id } }) => {
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id)); const replyMsgM = MessageUnique.getInnerData(id);
if (!replyMsgM) { if (!replyMsgM) {
this.core.context.logger.logWarn('回复消息不存在', id); this.core.context.logger.logWarn('回复消息不存在', id);
return undefined; return undefined;
@@ -871,7 +868,7 @@ export class OneBotMsgApi {
} }
; ;
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') { } else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid))); return new OB11FriendAddNoticeEvent(this.core, await this.core.apis.UserApi.getUinByUidV2(msg.peerUid));
} }
} }
return; return;
@@ -901,21 +898,17 @@ export class OneBotMsgApi {
const parsed = await Promise.all(multiMsgs.map(async msg => { const parsed = await Promise.all(multiMsgs.map(async msg => {
msg.parentMsgPeer = parentMsgPeer; msg.parentMsgPeer = parentMsgPeer;
msg.parentMsgIdList = parentMsgIdList; msg.parentMsgIdList = parentMsgIdList;
msg.id = MessageUnique.createUniqueMsgId(parentMsgPeer, msg.msgId); msg.id = MessageUnique.getOutputData(parentMsgPeer, msg.msgId, msg.msgSeq);
//该ID仅用查看 无法调用 //该ID仅用查看 无法调用
return await this.parseMessage(msg, 'array', true); return await this.parseMessage(msg, true);
})); }));
return parsed.filter(item => item !== undefined); return parsed.filter(item => item !== undefined);
} }
async parseMessage( async parseMessage(
msg: RawMessage, msg: RawMessage,
messagePostFormat: string,
parseMultMsg: boolean = true parseMultMsg: boolean = true
) { ) {
if (messagePostFormat === 'string') {
return (await this.parseMessageV2(msg, parseMultMsg))?.stringMsg;
}
return (await this.parseMessageV2(msg, parseMultMsg))?.arrayMsg; return (await this.parseMessageV2(msg, parseMultMsg))?.arrayMsg;
} }
@@ -944,39 +937,32 @@ export class OneBotMsgApi {
const validSegments = await this.parseMessageSegments(msg, parseMultMsg); const validSegments = await this.parseMessageSegments(msg, parseMultMsg);
resMsg.message = validSegments; resMsg.message = validSegments;
resMsg.raw_message = validSegments.map(msg => encodeCQCode(msg)).join('').trim(); return { arrayMsg: resMsg };
const stringMsg = await this.convertArrayToStringMessage(resMsg);
return { stringMsg, arrayMsg: resMsg };
} }
private initializeMessage(msg: RawMessage): OB11Message { private initializeMessage(msg: RawMessage): OB11Message {
return { return {
self_id: parseInt(this.core.selfInfo.uin), self_id: this.core.selfInfo.uin,
user_id: parseInt(msg.senderUin), user_id: msg.senderUin,
time: parseInt(msg.msgTime) || Date.now(), time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!, message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
real_seq: msg.msgSeq, real_seq: msg.msgSeq,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private', message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: { sender: {
user_id: +(msg.senderUin ?? 0), user_id: (msg.senderUin ?? 0).toString(),
nickname: msg.sendNickName, nickname: msg.sendNickName,
card: msg.sendMemberName ?? '', card: msg.sendMemberName ?? '',
}, },
raw_message: '',
font: 14, font: 14,
sub_type: 'friend', sub_type: 'friend',
message: [], message: [],
message_format: 'array',
post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
}; };
} }
private async handleGroupMessage(resMsg: OB11Message, msg: RawMessage) { private async handleGroupMessage(resMsg: OB11Message, msg: RawMessage) {
resMsg.sub_type = 'normal'; resMsg.sub_type = 'normal';
resMsg.group_id = parseInt(msg.peerUin); resMsg.group_id = msg.peerUin;
let member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); let member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); if (!member) member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) { if (member) {
@@ -1002,11 +988,11 @@ export class OneBotMsgApi {
const ret = await this.core.apis.MsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid); const ret = await this.core.apis.MsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid);
if (ret.result === 0) { if (ret.result === 0) {
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode); resMsg.group_id = ret.tmpChatInfo!.groupCode;
resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话'; resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话';
resMsg.temp_source = 0; resMsg.temp_source = 0;
} else { } else {
resMsg.group_id = 284840486; resMsg.group_id = '284840486';
resMsg.temp_source = 0; resMsg.temp_source = 0;
resMsg.sender.nickname = '临时会话'; resMsg.sender.nickname = '临时会话';
} }
@@ -1049,19 +1035,7 @@ export class OneBotMsgApi {
}).map((entry) => (<PromiseFulfilledResult<OB11MessageData>>entry).value).filter(value => value != null); }).map((entry) => (<PromiseFulfilledResult<OB11MessageData>>entry).value).filter(value => value != null);
} }
private async convertArrayToStringMessage(originMsg: OB11Message): Promise<OB11Message> {
const msg = structuredClone(originMsg);
msg.message_format = 'string';
msg.message = msg.raw_message;
return msg;
}
async importArrayTostringMsg(originMsg: OB11Message) {
const msg = structuredClone(originMsg);
msg.message_format = 'string';
msg.message = msg.raw_message;
return msg;
}
async createSendElements( async createSendElements(
messageData: OB11MessageData[], messageData: OB11MessageData[],
@@ -1122,11 +1096,11 @@ export class OneBotMsgApi {
try { try {
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, timeout); const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, timeout);
if (!returnMsg) throw new Error('发送消息失败'); if (!returnMsg) throw new Error('发送消息失败');
returnMsg.id = MessageUnique.createUniqueMsgId({ returnMsg.id = MessageUnique.getOutputData({
chatType: peer.chatType, chatType: peer.chatType,
guildId: '', guildId: '',
peerUid: peer.peerUid, peerUid: peer.peerUid,
}, returnMsg.msgId); }, returnMsg.msgId, returnMsg.msgSeq);
return returnMsg; return returnMsg;
} catch (error) { } catch (error) {
throw new Error((error as Error).message); throw new Error((error as Error).message);
@@ -1246,9 +1220,9 @@ export class OneBotMsgApi {
); );
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
groupChange.groupUin, groupChange.groupUin.toString(),
groupChange.memberUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.memberUid) : 0, groupChange.memberUid ? await this.core.apis.UserApi.getUinByUidV2(groupChange.memberUid) : '0',
operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(operatorUid) : 0, operatorUid ? await this.core.apis.UserApi.getUinByUidV2(operatorUid) : '0',
groupChange.decreaseType == 131 ? 'invite' : 'approve', groupChange.decreaseType == 131 ? 'invite' : 'approve',
); );
@@ -1272,9 +1246,9 @@ export class OneBotMsgApi {
} }
return new OB11GroupDecreaseEvent( return new OB11GroupDecreaseEvent(
this.core, this.core,
groupChange.groupUin, groupChange.groupUin.toString(),
groupChange.memberUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.memberUid) : 0, groupChange.memberUid ? await this.core.apis.UserApi.getUinByUidV2(groupChange.memberUid) : '0',
operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(operatorUid) : 0, operatorUid ? await this.core.apis.UserApi.getUinByUidV2(operatorUid) : '0',
this.groupChangDecreseType2String(groupChange.decreaseType), this.groupChangDecreseType2String(groupChange.decreaseType),
); );
} else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) {
@@ -1291,8 +1265,8 @@ export class OneBotMsgApi {
} }
return new OB11GroupAdminNoticeEvent( return new OB11GroupAdminNoticeEvent(
this.core, this.core,
groupAmin.groupUin, groupAmin.groupUin.toString(),
+await this.core.apis.UserApi.getUinByUidV2(uid), await this.core.apis.UserApi.getUinByUidV2(uid),
enabled ? 'set' : 'unset' enabled ? 'set' : 'unset'
); );
} else if (SysMessage.contentHead.type == 87 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 87 && SysMessage.body?.msgContent) {
@@ -1355,8 +1329,8 @@ export class OneBotMsgApi {
}); });
return new OB11GroupRequestEvent( return new OB11GroupRequestEvent(
this.core, this.core,
+groupInvite.groupUin, groupInvite.groupUin.toString(),
+await this.core.apis.UserApi.getUinByUidV2(groupInvite.invitorUid), await this.core.apis.UserApi.getUinByUidV2(groupInvite.invitorUid),
'invite', 'invite',
'', '',
request_seq request_seq

View File

@@ -1,113 +0,0 @@
import type {
NapCatOneBot11Adapter,
OB11Message,
OB11MessageAt,
OB11MessageData,
OB11MessageReply,
QuickAction,
QuickActionEvent,
QuickActionFriendRequest,
QuickActionGroupMessage,
QuickActionGroupRequest,
} from '@/onebot';
import { NTGroupRequestOperateTypes, type NapCatCore, type Peer } from '@/core';
import type { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
import type { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { ContextMode, createContext, normalize } from '@/onebot/action/msg/SendMsg';
import { isNull } from '@/common/helper';
export class OneBotQuickActionApi {
obContext: NapCatOneBot11Adapter;
core: NapCatCore;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
this.obContext = obContext;
this.core = core;
}
async handleQuickOperation(eventContext: QuickActionEvent, quickAction: QuickAction) {
if (eventContext.post_type === 'message') {
await this.handleMsg(eventContext as OB11Message, quickAction)
.catch(e => this.core.context.logger.logError(e));
}
if (eventContext.post_type === 'request') {
const friendRequest = eventContext as OB11FriendRequestEvent;
const groupRequest = eventContext as OB11GroupRequestEvent;
if ((friendRequest).request_type === 'friend') {
await this.handleFriendRequest(friendRequest, quickAction)
.catch(e => this.core.context.logger.logError(e));
} else if (groupRequest.request_type === 'group') {
await this.handleGroupRequest(groupRequest, quickAction)
.catch(e => this.core.context.logger.logError(e));
}
}
}
async handleMsg(msg: OB11Message, quickAction: QuickAction) {
const reply = quickAction.reply;
const peerContextMode = msg.message_type == 'private' ? ContextMode.Private : ContextMode.Group;
const peer: Peer = await createContext(this.core, {
group_id: msg.group_id?.toString(),
user_id: msg.user_id?.toString(),
}, peerContextMode);
if (reply) {
// let group: Group | undefined;
let replyMessage: OB11MessageData[] = [];
if (msg.message_type == 'group') {
// group = await core.apis.GroupApi.getGroup(msg.group_id!.toString());
replyMessage.push({
type: 'reply',
data: {
id: msg.message_id.toString(),
},
} as OB11MessageReply);
if ((quickAction as QuickActionGroupMessage).at_sender) {
replyMessage.push({
type: 'at',
data: {
qq: msg.user_id.toString(),
},
} as OB11MessageAt);
}
}
replyMessage = replyMessage.concat(normalize(reply, quickAction.auto_escape));
const {
sendElements,
deleteAfterSentFiles,
} = await this.obContext.apis.MsgApi.createSendElements(replyMessage, peer);
this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles).then().catch(e => this.core.context.logger.logError(e));
}
}
async findNotify(flag: string) {
let notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(false, 100)).find(e => e.seq == flag);
if (!notify) {
notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(true, 100)).find(e => e.seq == flag);
return { doubt: true, notify };
}
return { doubt: false, notify };
}
async handleGroupRequest(request: OB11GroupRequestEvent, quickAction: QuickActionGroupRequest) {
const invite_notify = this.obContext.apis.MsgApi.notifyGroupInvite.get(request.flag);
const { doubt, notify } = invite_notify ? { doubt: false, notify: invite_notify } : await this.findNotify(request.flag);
if (!isNull(quickAction.approve) && notify) {
this.core.apis.GroupApi.handleGroupRequest(
doubt,
notify,
quickAction.approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE,
quickAction.reason,
).catch(e => this.core.context.logger.logError(e));
}
}
async handleFriendRequest(request: OB11FriendRequestEvent, quickAction: QuickActionFriendRequest) {
const notify = (await this.core.apis.FriendApi.getBuddyReq()).buddyReqs.find(e => e.reqTime == request.flag.toString());
if (!isNull(quickAction.approve) && notify) {
this.core.apis.FriendApi.handleFriendRequest(notify, !!quickAction.approve).then().catch(e => this.core.context.logger.logError(e));
}
}
}

View File

@@ -23,7 +23,7 @@ export class OneBotUserApi {
const times = detail.txt.match(/\d+/) ?? '0'; const times = detail.txt.match(/\d+/) ?? '0';
return new OB11ProfileLikeEvent( return new OB11ProfileLikeEvent(
this.core, this.core,
Number(detail.uin), detail.uin.toString(),
detail.nickname, detail.nickname,
parseInt(times[0], 10), parseInt(times[0], 10),
likeMsg.time, likeMsg.time,

View File

@@ -3,13 +3,13 @@ import { NapCatCore } from '@/core';
export class BotOfflineEvent extends OB11BaseNoticeEvent { export class BotOfflineEvent extends OB11BaseNoticeEvent {
notice_type = 'bot_offline'; notice_type = 'bot_offline';
user_id: number; user_id: string;
tag: string = 'BotOfflineEvent'; tag: string = 'BotOfflineEvent';
message: string = 'BotOfflineEvent'; message: string = 'BotOfflineEvent';
public constructor(core: NapCatCore, tag: string, message: string) { public constructor(core: NapCatCore, tag: string, message: string) {
super(core); super(core);
this.user_id = +core.selfInfo.uin; this.user_id = core.selfInfo.uin;
this.tag = tag; this.tag = tag;
this.message = message; this.message = message;
} }

View File

@@ -3,9 +3,9 @@ import { NapCatCore } from '@/core';
export class OB11FriendAddNoticeEvent extends OB11BaseNoticeEvent { export class OB11FriendAddNoticeEvent extends OB11BaseNoticeEvent {
notice_type = 'friend_add'; notice_type = 'friend_add';
user_id: number; user_id: string;
public constructor(core: NapCatCore, userId: number) { public constructor(core: NapCatCore, userId: string) {
super(core); super(core);
this.user_id = userId; this.user_id = userId;
} }

View File

@@ -3,10 +3,10 @@ import { NapCatCore } from '@/core';
export class OB11FriendRecallNoticeEvent extends OB11BaseNoticeEvent { export class OB11FriendRecallNoticeEvent extends OB11BaseNoticeEvent {
notice_type = 'friend_recall'; notice_type = 'friend_recall';
user_id: number; user_id: string;
message_id: number; message_id: string;
public constructor(core: NapCatCore, userId: number, messageId: number) { public constructor(core: NapCatCore, userId: string, messageId: string) {
super(core); super(core);
this.user_id = userId; this.user_id = userId;
this.message_id = messageId; this.message_id = messageId;

View File

@@ -5,7 +5,7 @@ export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent {
notice_type = 'group_admin'; notice_type = 'group_admin';
sub_type: 'set' | 'unset'; sub_type: 'set' | 'unset';
constructor(core: NapCatCore, group_id: number, user_id: number, sub_type: 'set' | 'unset') { constructor(core: NapCatCore, group_id: string, user_id: string, sub_type: 'set' | 'unset') {
super(core, group_id, user_id); super(core, group_id, user_id);
this.sub_type = sub_type; this.sub_type = sub_type;
} }

View File

@@ -3,11 +3,11 @@ import { NapCatCore } from '@/core';
export class OB11GroupBanEvent extends OB11GroupNoticeEvent { export class OB11GroupBanEvent extends OB11GroupNoticeEvent {
notice_type = 'group_ban'; notice_type = 'group_ban';
operator_id: number; operator_id: string;
duration: number; duration: number;
sub_type: 'ban' | 'lift_ban'; sub_type: 'ban' | 'lift_ban';
constructor(core: NapCatCore, groupId: number, userId: number, operatorId: number, duration: number, sub_type: 'ban' | 'lift_ban') { constructor(core: NapCatCore, groupId: string, userId: string, operatorId: string, duration: number, sub_type: 'ban' | 'lift_ban') {
super(core, groupId, userId); super(core, groupId, userId);
this.group_id = groupId; this.group_id = groupId;
this.operator_id = operatorId; this.operator_id = operatorId;

View File

@@ -7,7 +7,7 @@ export class OB11GroupCardEvent extends OB11GroupNoticeEvent {
card_old: string; card_old: string;
constructor(core: NapCatCore, groupId: number, userId: number, cardNew: string, cardOld: string) { constructor(core: NapCatCore, groupId: string, userId: string, cardNew: string, cardOld: string) {
super(core, groupId, userId); super(core, groupId, userId);
this.group_id = groupId; this.group_id = groupId;
this.user_id = userId; this.user_id = userId;

View File

@@ -6,9 +6,9 @@ export type GroupDecreaseSubType = 'leave' | 'kick' | 'kick_me' | 'disband';
export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent { export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent {
notice_type = 'group_decrease'; notice_type = 'group_decrease';
sub_type: GroupDecreaseSubType = 'leave'; sub_type: GroupDecreaseSubType = 'leave';
operator_id: number; operator_id: string;
constructor(core: NapCatCore, groupId: number, userId: number, operatorId: number, subType: GroupDecreaseSubType = 'leave') { constructor(core: NapCatCore, groupId: string, userId: string, operatorId: string, subType: GroupDecreaseSubType = 'leave') {
super(core, groupId, userId); super(core, groupId, userId);
this.group_id = groupId; this.group_id = groupId;
this.operator_id = operatorId; this.operator_id = operatorId;

View File

@@ -3,13 +3,13 @@ import { NapCatCore } from '@/core';
export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent { export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent {
notice_type = 'essence'; notice_type = 'essence';
message_id: number; message_id: string;
sender_id: number; sender_id: string;
operator_id: number; operator_id: string;
sub_type: 'add' | 'delete' = 'add'; sub_type: 'add' | 'delete' = 'add';
constructor(core: NapCatCore, groupId: number, message_id: number, sender_id: number, operator_id: number) { constructor(core: NapCatCore, groupId: string, message_id: string, sender_id: string, operator_id: string) {
super(core, groupId, sender_id); super(core, groupId, sender_id);
this.group_id = groupId; this.group_id = groupId;
this.operator_id = operator_id; this.operator_id = operator_id;

View File

@@ -5,10 +5,10 @@ type GroupIncreaseSubType = 'approve' | 'invite';
export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent { export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent {
notice_type = 'group_increase'; notice_type = 'group_increase';
operator_id: number; operator_id: string;
sub_type: GroupIncreaseSubType; sub_type: GroupIncreaseSubType;
constructor(core: NapCatCore, groupId: number, userId: number, operatorId: number, subType: GroupIncreaseSubType = 'approve') { constructor(core: NapCatCore, groupId: string, userId: string, operatorId: string, subType: GroupIncreaseSubType = 'approve') {
super(core, groupId, userId); super(core, groupId, userId);
this.group_id = groupId; this.group_id = groupId;
this.operator_id = operatorId; this.operator_id = operatorId;

Some files were not shown because too many files have changed in this diff Show More