mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
67 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c45f0dc289 | ||
![]() |
c1a0f8915b | ||
![]() |
dcdab8e5a1 | ||
![]() |
eb3278fdab | ||
![]() |
433ce9caef | ||
![]() |
ba1acd624e | ||
![]() |
a586796339 | ||
![]() |
2016a90198 | ||
![]() |
b4bc4da7fc | ||
![]() |
52c828192e | ||
![]() |
34db3af48d | ||
![]() |
198da960dd | ||
![]() |
cb83918fb3 | ||
![]() |
f59a48540b | ||
![]() |
ccf9c1a5fb | ||
![]() |
ba6a85142a | ||
![]() |
440baccd2a | ||
![]() |
690c073328 | ||
![]() |
3f0730ed4f | ||
![]() |
01d5663bc8 | ||
![]() |
49806cd00e | ||
![]() |
935b0848e5 | ||
![]() |
5ca20a89a2 | ||
![]() |
e89a2266ec | ||
![]() |
6607533311 | ||
![]() |
4057054220 | ||
![]() |
055e43845e | ||
![]() |
d67270f2f8 | ||
![]() |
d061b6c190 | ||
![]() |
945f87d77f | ||
![]() |
6c9be52d39 | ||
![]() |
98e347f010 | ||
![]() |
607e367bb1 | ||
![]() |
7a25dc1ef1 | ||
![]() |
e22ec4be09 | ||
![]() |
51a06622f9 | ||
![]() |
22faf5b831 | ||
![]() |
e781c662b2 | ||
![]() |
5744698d24 | ||
![]() |
2c2ab3cd48 | ||
![]() |
cfae4f5acd | ||
![]() |
de541e3249 | ||
![]() |
f5187c5c01 | ||
![]() |
9936279443 | ||
![]() |
2818773fd4 | ||
![]() |
b9293cbcd0 | ||
![]() |
5b9e44ddfc | ||
![]() |
1791accab7 | ||
![]() |
08081360f3 | ||
![]() |
e933a95e97 | ||
![]() |
4ef457fe6f | ||
![]() |
bd9cae8921 | ||
![]() |
303a74f8fd | ||
![]() |
0b7f126ce1 | ||
![]() |
308b5c027f | ||
![]() |
ed3abc4b43 | ||
![]() |
87ecb3b380 | ||
![]() |
7e31763a25 | ||
![]() |
c9df57d16a | ||
![]() |
3d0f8ee657 | ||
![]() |
6421bb4f5c | ||
![]() |
3919743885 | ||
![]() |
a5a57b9e20 | ||
![]() |
e31d2810ad | ||
![]() |
140e62fdcd | ||
![]() |
014b4deb87 | ||
![]() |
956b6cd172 |
18
README.md
18
README.md
@@ -1,8 +1,9 @@
|
|||||||
|
<img src="https://napneko.github.io/assets/newnewlogo.png" width = "305" height = "411" alt="NapCat" align=right />
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
# NapCat
|
# NapCat
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
_Modern protocol-side framework implemented based on NTQQ._
|
_Modern protocol-side framework implemented based on NTQQ._
|
||||||
|
|
||||||
@@ -40,13 +41,20 @@ _Modern protocol-side framework implemented based on NTQQ._
|
|||||||
| Docs | [](https://napneko.pages.dev/) | [](https://napcat.cyou/) | [](https://www.napcat.wiki) |
|
| Docs | [](https://napneko.pages.dev/) | [](https://napcat.cyou/) | [](https://www.napcat.wiki) |
|
||||||
|:-:|:-:|:-:|:-:|
|
|:-:|:-:|:-:|:-:|
|
||||||
|
|
||||||
| Contact | [](https://qm.qq.com/q/I6LU87a0Yq) | [](https://qm.qq.com/q/HaRcfrHpUk) | [](https://t.me/MelodicMoonlight) |
|
| QQ Group | [](https://qm.qq.com/q/CMmPbGw0jA) | [](https://qm.qq.com/q/8zJMLjqy2Y) | [](https://qm.qq.com/q/HaRcfrHpUk) | [](https://qm.qq.com/q/I6LU87a0Yq) |
|
||||||
|:-:|:-:|:-:|:-:|
|
|:-:|:-:|:-:|:-:|:-:|
|
||||||
|
|
||||||
|
| Telegram | [](https://t.me/MelodicMoonlight) |
|
||||||
|
|:-:|:-:|
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
+ [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
|
+ [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
|
||||||
|
|
||||||
|
+ [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下
|
||||||
|
|
||||||
|
+ [MaiBot](https://github.com/MaiM-with-u/MaiBot) 一只赛博群友 麦麦 Bot框架 在此推荐一下
|
||||||
|
|
||||||
+ 不过最最重要的 还是需要感谢屏幕前的你哦~
|
+ 不过最最重要的 还是需要感谢屏幕前的你哦~
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -58,7 +66,3 @@ _Modern protocol-side framework implemented based on NTQQ._
|
|||||||
2. 项目其余逻辑代码采用[本仓库开源许可](./LICENSE).
|
2. 项目其余逻辑代码采用[本仓库开源许可](./LICENSE).
|
||||||
|
|
||||||
**本仓库仅用于提高易用性,实现消息推送类功能,此外,禁止任何项目未经仓库主作者授权基于 NapCat 代码开发。使用请遵守当地法律法规,由此造成的问题由使用者和提供违规使用教程者负责。**
|
**本仓库仅用于提高易用性,实现消息推送类功能,此外,禁止任何项目未经仓库主作者授权基于 NapCat 代码开发。使用请遵守当地法律法规,由此造成的问题由使用者和提供违规使用教程者负责。**
|
||||||
|
|
||||||
## Warnings
|
|
||||||
|
|
||||||
[某框架抄袭部分分析](https://napneko.github.io/other/about-copy)
|
|
||||||
|
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
BIN
external/logo.png
vendored
BIN
external/logo.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 250 KiB |
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "qq-chat",
|
"name": "qq-chat",
|
||||||
"version": "9.9.18-32869",
|
"version": "9.9.19-34740",
|
||||||
"verHash": "e735296c",
|
"verHash": "f31348f2",
|
||||||
"linuxVersion": "3.2.16-32869",
|
"linuxVersion": "3.2.17-34740",
|
||||||
"linuxVerHash": "4c192ba9",
|
"linuxVerHash": "5aa2d8d6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "QQ",
|
"description": "QQ",
|
||||||
"productName": "QQ",
|
"productName": "QQ",
|
||||||
@@ -16,27 +16,10 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"qd": "externals/devtools/cli/index.js"
|
"qd": "externals/devtools/cli/index.js"
|
||||||
},
|
},
|
||||||
"appid": {
|
|
||||||
"win32": "537258389",
|
|
||||||
"darwin": "537258412",
|
|
||||||
"linux": "537258424"
|
|
||||||
},
|
|
||||||
"main": "./loadNapCat.js",
|
"main": "./loadNapCat.js",
|
||||||
"peerDependenciesMeta": {
|
"buildVersion": "34740",
|
||||||
"*": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"patchedDependencies": {
|
|
||||||
"@vue/runtime-dom@3.5.12": "patches/@vue__runtime-dom@3.5.12.patch",
|
|
||||||
"@swc/helpers@0.5.3": "patches/@swc__helpers@0.5.3.patch",
|
|
||||||
"vuex@4.1.0": "patches/vuex@4.1.0.patch"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"buildVersion": "32869",
|
|
||||||
"isPureShell": true,
|
"isPureShell": true,
|
||||||
"isByteCodeShell": true,
|
"isByteCodeShell": true,
|
||||||
"platform": "win32",
|
"platform": "win32",
|
||||||
"eleArch": "x64"
|
"eleArch": "x64"
|
||||||
}
|
}
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.7.42",
|
"version": "4.7.63",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.7.42",
|
"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",
|
||||||
|
@@ -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
|
||||||
}
|
};
|
||||||
}
|
}
|
@@ -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',
|
||||||
|
@@ -145,8 +145,8 @@ export enum FileUriType {
|
|||||||
|
|
||||||
export async function checkUriType(Uri: string) {
|
export async function checkUriType(Uri: string) {
|
||||||
const LocalFileRet = await solveProblem((uri: string) => {
|
const LocalFileRet = await solveProblem((uri: string) => {
|
||||||
if (fs.existsSync(uri)) {
|
if (fs.existsSync(path.normalize(uri))) {
|
||||||
return { Uri: uri, Type: FileUriType.Local };
|
return { Uri: path.normalize(uri), Type: FileUriType.Local };
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}, Uri);
|
}, Uri);
|
||||||
@@ -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: '' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.7.42';
|
export const napCatVersion = '4.7.63';
|
||||||
|
@@ -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);
|
||||||
});
|
});
|
||||||
|
@@ -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';
|
||||||
@@ -28,6 +26,9 @@ import { SendMessageContext } from '@/onebot/api';
|
|||||||
import { getFileTypeForSendType } from '../helper/msg';
|
import { getFileTypeForSendType } from '../helper/msg';
|
||||||
import { FFmpegService } from '@/common/ffmpeg';
|
import { FFmpegService } from '@/common/ffmpeg';
|
||||||
import { rkeyDataType } from '../types/file';
|
import { rkeyDataType } from '../types/file';
|
||||||
|
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { FileId } from '../packet/transformer/proto/misc/fileid';
|
||||||
|
import { imageSizeFallBack } from '@/image-size';
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
@@ -44,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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,76 @@ export class NTQQFileApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined) {
|
||||||
|
if (this.core.apis.PacketApi.available) {
|
||||||
|
try {
|
||||||
|
if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetGroupFileUrl(+peer, fileUUID);
|
||||||
|
} else if (file10MMd5 && fileUUID) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetPrivateFileUrl(peer, fileUUID, file10MMd5);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('fileUUID or file10MMd5 is undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPttUrl(peer: string, fileUUID?: string) {
|
||||||
|
if (this.core.apis.PacketApi.available && fileUUID) {
|
||||||
|
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
|
||||||
|
try {
|
||||||
|
if (appid && appid === 1403) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+peer, {
|
||||||
|
fileUuid: fileUUID,
|
||||||
|
storeId: 1,
|
||||||
|
uploadTime: 0,
|
||||||
|
ttl: 0,
|
||||||
|
subType: 0,
|
||||||
|
});
|
||||||
|
} else if (fileUUID) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetPttUrl(peer, {
|
||||||
|
fileUuid: fileUUID,
|
||||||
|
storeId: 1,
|
||||||
|
uploadTime: 0,
|
||||||
|
ttl: 0,
|
||||||
|
subType: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('packet cant get ptt url');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoUrlPacket(peer: string, fileUUID?: string) {
|
||||||
|
if (this.core.apis.PacketApi.available && fileUUID) {
|
||||||
|
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
|
||||||
|
try {
|
||||||
|
if (appid && appid === 1415) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetGroupVideoUrl(+peer, {
|
||||||
|
fileUuid: fileUUID,
|
||||||
|
storeId: 1,
|
||||||
|
uploadTime: 0,
|
||||||
|
ttl: 0,
|
||||||
|
subType: 0,
|
||||||
|
});
|
||||||
|
} else if (fileUUID) {
|
||||||
|
return this.core.apis.PacketApi.pkt.operation.GetVideoUrl(peer, {
|
||||||
|
fileUuid: fileUUID,
|
||||||
|
storeId: 1,
|
||||||
|
uploadTime: 0,
|
||||||
|
ttl: 0,
|
||||||
|
subType: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.context.logger.logError('获取文件URL失败', (error as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('packet cant get video url');
|
||||||
|
}
|
||||||
|
|
||||||
async copyFile(filePath: string, destPath: string) {
|
async copyFile(filePath: string, destPath: string) {
|
||||||
await this.core.util.copyFile(filePath, destPath);
|
await this.core.util.copyFile(filePath, destPath);
|
||||||
@@ -137,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,
|
||||||
@@ -308,23 +379,24 @@ 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++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return res.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
||||||
@@ -364,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);
|
||||||
|
@@ -110,7 +110,7 @@ export class NTQQFriendApi {
|
|||||||
time: item.reqTime, // 信息字段
|
time: item.reqTime, // 信息字段
|
||||||
type: 'doubt' //保留字段
|
type: 'doubt' //保留字段
|
||||||
};
|
};
|
||||||
}))
|
}));
|
||||||
return requests;
|
return requests;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -71,6 +71,7 @@ export class NTQQMsgApi {
|
|||||||
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||||
chatInfo: peer,
|
chatInfo: peer,
|
||||||
|
//searchFields: 3,
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: [],
|
||||||
filterMsgToTime: '0',
|
filterMsgToTime: '0',
|
||||||
@@ -84,6 +85,7 @@ export class NTQQMsgApi {
|
|||||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||||
chatInfo: peer,
|
chatInfo: peer,
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
|
//searchFields: 3,
|
||||||
filterSendersUid: SendersUid,
|
filterSendersUid: SendersUid,
|
||||||
filterMsgToTime: MsgTime,
|
filterMsgToTime: MsgTime,
|
||||||
filterMsgFromTime: MsgTime,
|
filterMsgFromTime: MsgTime,
|
||||||
@@ -100,6 +102,7 @@ export class NTQQMsgApi {
|
|||||||
filterMsgToTime: '0',
|
filterMsgToTime: '0',
|
||||||
filterMsgFromTime: '0',
|
filterMsgFromTime: '0',
|
||||||
isReverseOrder: false,
|
isReverseOrder: false,
|
||||||
|
//searchFields: 3,
|
||||||
isIncludeCurrent: true,
|
isIncludeCurrent: true,
|
||||||
pageLimit: 1,
|
pageLimit: 1,
|
||||||
});
|
});
|
||||||
@@ -110,6 +113,7 @@ export class NTQQMsgApi {
|
|||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: [],
|
||||||
filterMsgToTime: '0',
|
filterMsgToTime: '0',
|
||||||
|
//searchFields: 3,
|
||||||
filterMsgFromTime: '0',
|
filterMsgFromTime: '0',
|
||||||
isReverseOrder: true,
|
isReverseOrder: true,
|
||||||
isIncludeCurrent: true,
|
isIncludeCurrent: true,
|
||||||
@@ -128,6 +132,7 @@ export class NTQQMsgApi {
|
|||||||
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: [],
|
||||||
|
//searchFields: 3,
|
||||||
filterMsgToTime: filterMsgToTime,
|
filterMsgToTime: filterMsgToTime,
|
||||||
filterMsgFromTime: filterMsgFromTime,
|
filterMsgFromTime: filterMsgFromTime,
|
||||||
isReverseOrder: false,
|
isReverseOrder: false,
|
||||||
@@ -142,6 +147,7 @@ export class NTQQMsgApi {
|
|||||||
chatInfo: peer,
|
chatInfo: peer,
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: SendersUid,
|
filterSendersUid: SendersUid,
|
||||||
|
//searchFields: 3,
|
||||||
filterMsgToTime: '0',
|
filterMsgToTime: '0',
|
||||||
filterMsgFromTime: '0',
|
filterMsgFromTime: '0',
|
||||||
isReverseOrder: true,
|
isReverseOrder: true,
|
||||||
|
20
src/core/external/appid.json
vendored
20
src/core/external/appid.json
vendored
@@ -274,5 +274,25 @@
|
|||||||
"9.9.19-34606": {
|
"9.9.19-34606": {
|
||||||
"appid": 537282307,
|
"appid": 537282307,
|
||||||
"qua": "V1_WIN_NQ_9.9.19_34606_GW_B"
|
"qua": "V1_WIN_NQ_9.9.19_34606_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.19-34740": {
|
||||||
|
"appid": 537290691,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.19_34740_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.17-34740": {
|
||||||
|
"appid": 537290727,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.17_34740_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.19-34958": {
|
||||||
|
"appid": 537290742,
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
28
src/core/external/offset.json
vendored
28
src/core/external/offset.json
vendored
@@ -350,5 +350,33 @@
|
|||||||
"3.2.17-34606-arm64": {
|
"3.2.17-34606-arm64": {
|
||||||
"send": "7711270",
|
"send": "7711270",
|
||||||
"recv": "7714BA0"
|
"recv": "7714BA0"
|
||||||
|
},
|
||||||
|
"9.9.19-34740-x64": {
|
||||||
|
"send": "3BDD8D0",
|
||||||
|
"recv": "3BE20D0"
|
||||||
|
},
|
||||||
|
"3.2.17-34740-x64": {
|
||||||
|
"send": "ADDF0A0",
|
||||||
|
"recv": "ADE2AC0"
|
||||||
|
},
|
||||||
|
"3.2.17-34740-arm64": {
|
||||||
|
"send": "7753BB8",
|
||||||
|
"recv": "77574E8"
|
||||||
|
},
|
||||||
|
"9.9.19-34958-x64": {
|
||||||
|
"send": "3BDD8D0",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,43 +3,43 @@ import { BuddyCategoryType, FriendRequestNotify } from '@/core/types';
|
|||||||
export type OnBuddyChangeParams = BuddyCategoryType[];
|
export type OnBuddyChangeParams = BuddyCategoryType[];
|
||||||
|
|
||||||
export class NodeIKernelBuddyListener {
|
export class NodeIKernelBuddyListener {
|
||||||
onBuddyListChangedV2(arg: unknown): any {
|
onBuddyListChangedV2(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddBuddyNeedVerify(arg: unknown): any {
|
onAddBuddyNeedVerify(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddMeSettingChanged(arg: unknown): any {
|
onAddMeSettingChanged(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAvatarUrlUpdated(arg: unknown): any {
|
onAvatarUrlUpdated(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBlockChanged(arg: unknown): any {
|
onBlockChanged(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyDetailInfoChange(arg: unknown): any {
|
onBuddyDetailInfoChange(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyInfoChange(arg: unknown): any {
|
onBuddyInfoChange(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyListChange(arg: OnBuddyChangeParams): any {
|
onBuddyListChange(_arg: OnBuddyChangeParams): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyRemarkUpdated(arg: unknown): any {
|
onBuddyRemarkUpdated(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyReqChange(arg: FriendRequestNotify): any {
|
onBuddyReqChange(_arg: FriendRequestNotify): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuddyReqUnreadCntChange(arg: unknown): any {
|
onBuddyReqUnreadCntChange(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheckBuddySettingResult(arg: unknown): any {
|
onCheckBuddySettingResult(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelBatchBuddyInfos(arg: unknown): any {
|
onDelBatchBuddyInfos(_arg: unknown): any {
|
||||||
console.log('onDelBatchBuddyInfos not implemented', ...arguments);
|
console.log('onDelBatchBuddyInfos not implemented', ...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +66,12 @@ export class NodeIKernelBuddyListener {
|
|||||||
onDoubtBuddyReqUnreadNumChange(_num: number): void | Promise<void> {
|
onDoubtBuddyReqUnreadNumChange(_num: number): void | Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onNickUpdated(arg: unknown): any {
|
onNickUpdated(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSmartInfos(arg: unknown): any {
|
onSmartInfos(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSpacePermissionInfos(arg: unknown): any {
|
onSpacePermissionInfos(_arg: unknown): any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,13 +6,14 @@ import {
|
|||||||
PacketMsgFileElement,
|
PacketMsgFileElement,
|
||||||
PacketMsgPicElement,
|
PacketMsgPicElement,
|
||||||
PacketMsgPttElement,
|
PacketMsgPttElement,
|
||||||
PacketMsgVideoElement
|
PacketMsgReplyElement,
|
||||||
|
PacketMsgVideoElement,
|
||||||
} from '@/core/packet/message/element';
|
} from '@/core/packet/message/element';
|
||||||
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
||||||
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
||||||
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
||||||
import { NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
import { NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
import { IndexNode, LongMsgResult, MsgInfo } from '@/core/packet/transformer/proto';
|
import { IndexNode, LongMsgResult, MsgInfo, PushMsgBody } from '@/core/packet/transformer/proto';
|
||||||
import { OidbPacket } from '@/core/packet/transformer/base';
|
import { OidbPacket } from '@/core/packet/transformer/base';
|
||||||
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
||||||
import { gunzipSync } from 'zlib';
|
import { gunzipSync } from 'zlib';
|
||||||
@@ -76,22 +77,24 @@ export class PacketOperationContext {
|
|||||||
async UploadResources(msg: PacketMsg[], groupUin: number = 0) {
|
async UploadResources(msg: PacketMsg[], groupUin: number = 0) {
|
||||||
const chatType = groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C;
|
const chatType = groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C;
|
||||||
const peerUid = groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid;
|
const peerUid = groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid;
|
||||||
const reqList = msg.flatMap(m =>
|
const reqList = msg.flatMap((m) =>
|
||||||
m.msg.map(e => {
|
m.msg
|
||||||
if (e instanceof PacketMsgPicElement) {
|
.map((e) => {
|
||||||
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
if (e instanceof PacketMsgPicElement) {
|
||||||
} else if (e instanceof PacketMsgVideoElement) {
|
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
||||||
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
} else if (e instanceof PacketMsgVideoElement) {
|
||||||
} else if (e instanceof PacketMsgPttElement) {
|
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
||||||
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
} else if (e instanceof PacketMsgPttElement) {
|
||||||
} else if (e instanceof PacketMsgFileElement) {
|
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
||||||
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
} else if (e instanceof PacketMsgFileElement) {
|
||||||
}
|
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
||||||
return null;
|
}
|
||||||
}).filter(Boolean)
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
);
|
);
|
||||||
const res = await Promise.allSettled(reqList);
|
const res = await Promise.allSettled(reqList);
|
||||||
this.context.logger.info(`上传资源${res.length}个,失败${res.filter(r => r.status === 'rejected').length}个`);
|
this.context.logger.info(`上传资源${res.length}个,失败${res.filter((r) => r.status === 'rejected').length}个`);
|
||||||
res.forEach((result, index) => {
|
res.forEach((result, index) => {
|
||||||
if (result.status === 'rejected') {
|
if (result.status === 'rejected') {
|
||||||
this.context.logger.error(`上传第${index + 1}个资源失败:${result.reason.stack}`);
|
this.context.logger.error(`上传第${index + 1}个资源失败:${result.reason.stack}`);
|
||||||
@@ -100,10 +103,13 @@ export class PacketOperationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async UploadImage(img: PacketMsgPicElement) {
|
async UploadImage(img: PacketMsgPicElement) {
|
||||||
await this.context.highway.uploadImage({
|
await this.context.highway.uploadImage(
|
||||||
chatType: ChatType.KCHATTYPEC2C,
|
{
|
||||||
peerUid: this.context.napcore.basicInfo.uid
|
chatType: ChatType.KCHATTYPEC2C,
|
||||||
}, img);
|
peerUid: this.context.napcore.basicInfo.uid,
|
||||||
|
},
|
||||||
|
img
|
||||||
|
);
|
||||||
const index = img.msgInfo?.msgInfoBody?.at(0)?.index;
|
const index = img.msgInfo?.msgInfoBody?.at(0)?.index;
|
||||||
if (!index) {
|
if (!index) {
|
||||||
throw new Error('img.msgInfo?.msgInfoBody![0].index! is undefined');
|
throw new Error('img.msgInfo?.msgInfoBody![0].index! is undefined');
|
||||||
@@ -118,6 +124,20 @@ export class PacketOperationContext {
|
|||||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async GetPttUrl(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const req = trans.DownloadPtt.build(selfUid, node);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadPtt.parse(resp);
|
||||||
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async GetVideoUrl(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const req = trans.DownloadVideo.build(selfUid, node);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadVideo.parse(resp);
|
||||||
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
async GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
async GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
const req = trans.DownloadGroupImage.build(groupUin, node);
|
const req = trans.DownloadGroupImage.build(groupUin, node);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
@@ -125,6 +145,21 @@ export class PacketOperationContext {
|
|||||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadImage.parse(resp);
|
||||||
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async GetGroupVideoUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const req = trans.DownloadGroupVideo.build(groupUin, node);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.DownloadImage.parse(resp);
|
||||||
|
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async ImageOCR(imgUrl: string) {
|
async ImageOCR(imgUrl: string) {
|
||||||
const req = trans.ImageOCR.build(imgUrl);
|
const req = trans.ImageOCR.build(imgUrl);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
@@ -137,24 +172,66 @@ export class PacketOperationContext {
|
|||||||
coordinates: item.polygon.coordinates.map((c) => {
|
coordinates: item.polygon.coordinates.map((c) => {
|
||||||
return {
|
return {
|
||||||
x: c.x,
|
x: c.x,
|
||||||
y: c.y
|
y: c.y,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
language: res.ocrRspBody.language
|
language: res.ocrRspBody.language,
|
||||||
} as ImageOcrResult;
|
} as ImageOcrResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {
|
private async SendPreprocess(msg: PacketMsg[], groupUin: number = 0) {
|
||||||
|
const ps = msg.map((m) => {
|
||||||
|
return m.msg.map(async (e) => {
|
||||||
|
if (e instanceof PacketMsgReplyElement && !e.targetElems) {
|
||||||
|
this.context.logger.debug('Cannot find reply element\'s targetElems, prepare to fetch it...');
|
||||||
|
if (!e.targetPeer?.peerUid) {
|
||||||
|
this.context.logger.error('targetPeer is undefined!');
|
||||||
|
}
|
||||||
|
let targetMsg: NapProtoEncodeStructType<typeof PushMsgBody>[] | undefined;
|
||||||
|
if (e.isGroupReply) {
|
||||||
|
targetMsg = await this.FetchGroupMessage(+(e.targetPeer?.peerUid ?? 0), e.targetMessageSeq, e.targetMessageSeq);
|
||||||
|
} else {
|
||||||
|
targetMsg = await this.FetchC2CMessage(await this.context.napcore.basicInfo.uin2uid(e.targetUin), e.targetMessageSeq, e.targetMessageSeq);
|
||||||
|
}
|
||||||
|
e.targetElems = targetMsg.at(0)?.body?.richText?.elems;
|
||||||
|
e.targetSourceMsg = targetMsg.at(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).flat();
|
||||||
|
await Promise.all(ps);
|
||||||
await this.UploadResources(msg, groupUin);
|
await this.UploadResources(msg, groupUin);
|
||||||
|
}
|
||||||
|
|
||||||
|
async FetchGroupMessage(groupUin: number, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
|
||||||
|
const req = trans.FetchGroupMessage.build(groupUin, startSeq, endSeq);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.FetchGroupMessage.parse(resp);
|
||||||
|
return res.body.messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
async FetchC2CMessage(targetUid: string, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
|
||||||
|
const req = trans.FetchC2CMessage.build(targetUid, startSeq, endSeq);
|
||||||
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
|
const res = trans.FetchC2CMessage.parse(resp);
|
||||||
|
return res.messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {
|
||||||
|
await this.SendPreprocess(msg, groupUin);
|
||||||
const req = trans.UploadForwardMsg.build(this.context.napcore.basicInfo.uid, msg, groupUin);
|
const req = trans.UploadForwardMsg.build(this.context.napcore.basicInfo.uid, msg, groupUin);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
const res = trans.UploadForwardMsg.parse(resp);
|
const res = trans.UploadForwardMsg.parse(resp);
|
||||||
return res.result.resId;
|
return res.result.resId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async MoveGroupFile(groupUin: number, fileUUID: string, currentParentDirectory: string, targetParentDirectory: string) {
|
async MoveGroupFile(
|
||||||
|
groupUin: number,
|
||||||
|
fileUUID: string,
|
||||||
|
currentParentDirectory: string,
|
||||||
|
targetParentDirectory: string
|
||||||
|
) {
|
||||||
const req = trans.MoveGroupFile.build(groupUin, fileUUID, currentParentDirectory, targetParentDirectory);
|
const req = trans.MoveGroupFile.build(groupUin, fileUUID, currentParentDirectory, targetParentDirectory);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
const res = trans.MoveGroupFile.parse(resp);
|
const res = trans.MoveGroupFile.parse(resp);
|
||||||
@@ -174,6 +251,7 @@ export class PacketOperationContext {
|
|||||||
const res = trans.DownloadGroupFile.parse(resp);
|
const res = trans.DownloadGroupFile.parse(resp);
|
||||||
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
|
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) {
|
async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) {
|
||||||
const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5);
|
const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
@@ -181,13 +259,6 @@ export class PacketOperationContext {
|
|||||||
return `http://${res.body?.result?.server}:${res.body?.result?.port}${res.body?.result?.url?.slice(8)}&isthumb=0`;
|
return `http://${res.body?.result?.server}:${res.body?.result?.port}${res.body?.result?.url?.slice(8)}&isthumb=0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
|
||||||
const req = trans.DownloadGroupPtt.build(groupUin, node);
|
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
|
||||||
const res = trans.DownloadGroupPtt.parse(resp);
|
|
||||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async GetMiniAppAdaptShareInfo(param: MiniAppReqParams) {
|
async GetMiniAppAdaptShareInfo(param: MiniAppReqParams) {
|
||||||
const req = trans.GetMiniAppAdaptShareInfo.build(param);
|
const req = trans.GetMiniAppAdaptShareInfo.build(param);
|
||||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||||
@@ -203,12 +274,17 @@ export class PacketOperationContext {
|
|||||||
return res.content.map((item) => {
|
return res.content.map((item) => {
|
||||||
return {
|
return {
|
||||||
category: item.category,
|
category: item.category,
|
||||||
voices: item.voices
|
voices: item.voices,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async GetAiVoice(groupUin: number, voiceId: string, text: string, chatType: AIVoiceChatType): Promise<NapProtoDecodeStructType<typeof MsgInfo>> {
|
async GetAiVoice(
|
||||||
|
groupUin: number,
|
||||||
|
voiceId: string,
|
||||||
|
text: string,
|
||||||
|
chatType: AIVoiceChatType
|
||||||
|
): Promise<NapProtoDecodeStructType<typeof MsgInfo>> {
|
||||||
let reqTime = 0;
|
let reqTime = 0;
|
||||||
const reqMaxTime = 30;
|
const reqMaxTime = 30;
|
||||||
const sessionId = crypto.randomBytes(4).readUInt32BE(0);
|
const sessionId = crypto.randomBytes(4).readUInt32BE(0);
|
||||||
@@ -236,6 +312,7 @@ export class PacketOperationContext {
|
|||||||
if (!main?.actionData.msgBody) {
|
if (!main?.actionData.msgBody) {
|
||||||
throw new Error('msgBody is empty');
|
throw new Error('msgBody is empty');
|
||||||
}
|
}
|
||||||
|
this.context.logger.debug('rawChains ', inflate.toString('hex'));
|
||||||
|
|
||||||
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
|
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
|
||||||
if (!msg?.body?.richText?.elems) {
|
if (!msg?.body?.richText?.elems) {
|
||||||
@@ -251,12 +328,12 @@ export class PacketOperationContext {
|
|||||||
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
|
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
|
||||||
element.picElement = {
|
element.picElement = {
|
||||||
...element.picElement,
|
...element.picElement,
|
||||||
originImageUrl: await this.GetGroupImageUrl(groupUin, index!)
|
originImageUrl: await this.GetGroupImageUrl(groupUin, index!),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
element.picElement = {
|
element.picElement = {
|
||||||
...element.picElement,
|
...element.picElement,
|
||||||
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!)
|
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
@@ -269,7 +346,7 @@ export class PacketOperationContext {
|
|||||||
elements: elements,
|
elements: elements,
|
||||||
guildId: '',
|
guildId: '',
|
||||||
isOnlineMsg: false,
|
isOnlineMsg: false,
|
||||||
msgId: '7467703692092974645', // TODO: no necessary
|
msgId: '7467703692092974645', // TODO: no necessary
|
||||||
msgRandom: '0',
|
msgRandom: '0',
|
||||||
msgSeq: String(msg.contentHead.sequence ?? 0),
|
msgSeq: String(msg.contentHead.sequence ?? 0),
|
||||||
msgTime: String(msg.contentHead.timeStamp ?? 0),
|
msgTime: String(msg.contentHead.timeStamp ?? 0),
|
||||||
|
@@ -24,12 +24,15 @@ export class PacketMsgBuilder {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
responseHead: {
|
responseHead: {
|
||||||
fromUid: '',
|
|
||||||
fromUin: node.senderUin,
|
fromUin: node.senderUin,
|
||||||
toUid: node.groupId ? undefined : selfUid,
|
type: 0,
|
||||||
|
sigMap: 0,
|
||||||
|
toUin: 0,
|
||||||
|
fromUid: '',
|
||||||
forward: node.groupId ? undefined : {
|
forward: node.groupId ? undefined : {
|
||||||
friendName: node.senderName,
|
friendName: node.senderName,
|
||||||
},
|
},
|
||||||
|
toUid: node.groupId ? undefined : selfUid,
|
||||||
grp: node.groupId ? {
|
grp: node.groupId ? {
|
||||||
groupUin: node.groupId,
|
groupUin: node.groupId,
|
||||||
memberName: node.senderName,
|
memberName: node.senderName,
|
||||||
@@ -40,16 +43,13 @@ export class PacketMsgBuilder {
|
|||||||
type: node.groupId ? 82 : 9,
|
type: node.groupId ? 82 : 9,
|
||||||
subType: node.groupId ? undefined : 4,
|
subType: node.groupId ? undefined : 4,
|
||||||
divSeq: node.groupId ? undefined : 4,
|
divSeq: node.groupId ? undefined : 4,
|
||||||
msgId: crypto.randomBytes(4).readUInt32LE(0),
|
autoReply: 0,
|
||||||
sequence: crypto.randomBytes(4).readUInt32LE(0),
|
sequence: crypto.randomBytes(4).readUInt32LE(0),
|
||||||
timeStamp: +node.time.toString().substring(0, 10),
|
timeStamp: +node.time.toString().substring(0, 10),
|
||||||
field7: BigInt(1),
|
|
||||||
field8: 0,
|
|
||||||
field9: 0,
|
|
||||||
forward: {
|
forward: {
|
||||||
field1: 0,
|
field1: 0,
|
||||||
field2: 0,
|
field2: 0,
|
||||||
field3: node.groupId ? 0 : 2,
|
field3: node.groupId ? 1 : 2,
|
||||||
unknownBase64: avatar,
|
unknownBase64: avatar,
|
||||||
avatar: avatar
|
avatar: avatar
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
MsgInfo,
|
MsgInfo,
|
||||||
NotOnlineImage,
|
NotOnlineImage,
|
||||||
OidbSvcTrpcTcp0XE37_800Response,
|
OidbSvcTrpcTcp0XE37_800Response,
|
||||||
|
PushMsgBody,
|
||||||
QBigFaceExtra,
|
QBigFaceExtra,
|
||||||
QSmallFaceExtra,
|
QSmallFaceExtra,
|
||||||
} from '@/core/packet/transformer/proto';
|
} from '@/core/packet/transformer/proto';
|
||||||
@@ -29,7 +30,8 @@ import {
|
|||||||
SendReplyElement,
|
SendReplyElement,
|
||||||
SendMultiForwardMsgElement,
|
SendMultiForwardMsgElement,
|
||||||
SendTextElement,
|
SendTextElement,
|
||||||
SendVideoElement
|
SendVideoElement,
|
||||||
|
Peer
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||||
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||||
@@ -146,41 +148,40 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||||
messageId: bigint;
|
time: number;
|
||||||
messageSeq: number;
|
targetMessageId: bigint;
|
||||||
messageClientSeq: number;
|
targetMessageSeq: number;
|
||||||
|
targetMessageClientSeq: number;
|
||||||
targetUin: number;
|
targetUin: number;
|
||||||
targetUid: string;
|
targetUid: string;
|
||||||
time: number;
|
targetElems?: NapProtoEncodeStructType<typeof Elem>[];
|
||||||
elems: PacketMsg[];
|
targetSourceMsg?: NapProtoEncodeStructType<typeof PushMsgBody>;
|
||||||
|
targetPeer?: Peer;
|
||||||
|
|
||||||
constructor(element: SendReplyElement) {
|
constructor(element: SendReplyElement) {
|
||||||
super(element);
|
super(element);
|
||||||
this.messageId = BigInt(element.replyElement.replayMsgId ?? 0);
|
this.time = +(element.replyElement.replyMsgTime ?? Math.floor(Date.now() / 1000));
|
||||||
this.messageSeq = +(element.replyElement.replayMsgSeq ?? 0);
|
this.targetMessageId = BigInt(element.replyElement.replayMsgId ?? 0);
|
||||||
this.messageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
|
this.targetMessageSeq = +(element.replyElement.replayMsgSeq ?? 0);
|
||||||
|
this.targetMessageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
|
||||||
this.targetUin = +(element.replyElement.senderUin ?? 0);
|
this.targetUin = +(element.replyElement.senderUin ?? 0);
|
||||||
this.targetUid = element.replyElement.senderUidStr ?? '';
|
this.targetUid = element.replyElement.senderUidStr ?? '';
|
||||||
this.time = +(element.replyElement.replyMsgTime ?? 0);
|
this.targetPeer = element.replyElement._replyMsgPeer;
|
||||||
this.elems = []; // TODO: in replyElement.sourceMsgTextElems
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isGroupReply(): boolean {
|
get isGroupReply(): boolean {
|
||||||
return this.messageClientSeq === 0;
|
return this.targetMessageClientSeq === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
override buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
override buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||||
return [{
|
return [{
|
||||||
srcMsg: {
|
srcMsg: {
|
||||||
origSeqs: [this.isGroupReply ? this.messageClientSeq : this.messageSeq],
|
origSeqs: [this.isGroupReply ? this.targetMessageSeq : this.targetMessageClientSeq],
|
||||||
senderUin: BigInt(this.targetUin),
|
senderUin: BigInt(this.targetUin),
|
||||||
time: this.time,
|
time: this.time,
|
||||||
elems: [], // TODO: in replyElement.sourceMsgTextElems
|
elems: this.targetElems ?? [],
|
||||||
pbReserve: {
|
sourceMsg: new NapProtoMsg(PushMsgBody).encode(this.targetSourceMsg ?? {}),
|
||||||
messageId: this.messageId,
|
toUin: BigInt(0),
|
||||||
},
|
|
||||||
toUin: BigInt(this.targetUin),
|
|
||||||
type: 1,
|
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
50
src/core/packet/transformer/highway/DownloadGroupVideo.ts
Normal file
50
src/core/packet/transformer/highway/DownloadGroupVideo.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||||
|
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
|
class DownloadGroupVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||||
|
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||||
|
reqHead: {
|
||||||
|
common: {
|
||||||
|
requestId: 1,
|
||||||
|
command: 200
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
requestType: 2,
|
||||||
|
businessType: 2,
|
||||||
|
sceneType: 2,
|
||||||
|
group: {
|
||||||
|
groupUin: groupUin
|
||||||
|
}
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
agentType: 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
node: node,
|
||||||
|
download: {
|
||||||
|
video: {
|
||||||
|
busiType: 0,
|
||||||
|
sceneType: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return OidbBase.build(0x11EA, 200, body, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
const oidbBody = OidbBase.parse(data).body;
|
||||||
|
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DownloadGroupVideo();
|
51
src/core/packet/transformer/highway/DownloadPtt.ts
Normal file
51
src/core/packet/transformer/highway/DownloadPtt.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||||
|
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
|
class DownloadPtt extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||||
|
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||||
|
reqHead: {
|
||||||
|
common: {
|
||||||
|
requestId: 1,
|
||||||
|
command: 200
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
requestType: 1,
|
||||||
|
businessType: 3,
|
||||||
|
sceneType: 1,
|
||||||
|
c2C: {
|
||||||
|
accountType: 2,
|
||||||
|
targetUid: selfUid
|
||||||
|
},
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
agentType: 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
node: node,
|
||||||
|
download: {
|
||||||
|
video: {
|
||||||
|
busiType: 0,
|
||||||
|
sceneType: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return OidbBase.build(0x126D, 200, body, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
const oidbBody = OidbBase.parse(data).body;
|
||||||
|
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DownloadPtt();
|
51
src/core/packet/transformer/highway/DownloadVideo.ts
Normal file
51
src/core/packet/transformer/highway/DownloadVideo.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||||
|
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||||
|
|
||||||
|
class DownloadVideo extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||||
|
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||||
|
reqHead: {
|
||||||
|
common: {
|
||||||
|
requestId: 1,
|
||||||
|
command: 200
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
requestType: 2,
|
||||||
|
businessType: 2,
|
||||||
|
sceneType: 1,
|
||||||
|
c2C: {
|
||||||
|
accountType: 2,
|
||||||
|
targetUid: selfUid
|
||||||
|
},
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
agentType: 2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
node: node,
|
||||||
|
download: {
|
||||||
|
video: {
|
||||||
|
busiType: 0,
|
||||||
|
sceneType: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return OidbBase.build(0x11E9, 200, body, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
const oidbBody = OidbBase.parse(data).body;
|
||||||
|
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DownloadVideo();
|
@@ -13,3 +13,6 @@ export { default as UploadPrivatePtt } from './UploadPrivatePtt';
|
|||||||
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
||||||
export { default as DownloadImage } from './DownloadImage';
|
export { default as DownloadImage } from './DownloadImage';
|
||||||
export { default as DownloadGroupImage } from './DownloadGroupImage';
|
export { default as DownloadGroupImage } from './DownloadGroupImage';
|
||||||
|
export { default as DownloadVideo } from './DownloadVideo';
|
||||||
|
export { default as DownloadGroupVideo } from './DownloadGroupVideo';
|
||||||
|
export { default as DownloadPtt } from './DownloadPtt';
|
27
src/core/packet/transformer/message/FetchC2CMessage.ts
Normal file
27
src/core/packet/transformer/message/FetchC2CMessage.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
|
||||||
|
class FetchC2CMessage extends PacketTransformer<typeof proto.SsoGetC2cMsgResponse> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(targetUid: string, startSeq: number, endSeq: number): OidbPacket {
|
||||||
|
const req = new NapProtoMsg(proto.SsoGetC2cMsg).encode({
|
||||||
|
friendUid: targetUid,
|
||||||
|
startSequence: startSeq,
|
||||||
|
endSequence: endSeq,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cmd: 'trpc.msg.register_proxy.RegisterProxy.SsoGetC2cMsg',
|
||||||
|
data: PacketHexStrBuilder(req)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
return new NapProtoMsg(proto.SsoGetC2cMsgResponse).decode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new FetchC2CMessage();
|
30
src/core/packet/transformer/message/FetchGroupMessage.ts
Normal file
30
src/core/packet/transformer/message/FetchGroupMessage.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import * as proto from '@/core/packet/transformer/proto';
|
||||||
|
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||||
|
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||||
|
|
||||||
|
class FetchGroupMessage extends PacketTransformer<typeof proto.SsoGetGroupMsgResponse> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(groupUin: number, startSeq: number, endSeq: number): OidbPacket {
|
||||||
|
const req = new NapProtoMsg(proto.SsoGetGroupMsg).encode({
|
||||||
|
info: {
|
||||||
|
groupUin: groupUin,
|
||||||
|
startSequence: startSeq,
|
||||||
|
endSequence: endSeq
|
||||||
|
},
|
||||||
|
direction: true
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cmd: 'trpc.msg.register_proxy.RegisterProxy.SsoGetGroupMsg',
|
||||||
|
data: PacketHexStrBuilder(req)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: Buffer) {
|
||||||
|
return new NapProtoMsg(proto.SsoGetGroupMsgResponse).decode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new FetchGroupMessage();
|
@@ -1,2 +1,4 @@
|
|||||||
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
||||||
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
export { default as FetchGroupMessage } from './FetchGroupMessage';
|
||||||
|
export { default as FetchC2CMessage } from './FetchC2CMessage';
|
||||||
|
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
||||||
|
@@ -13,13 +13,15 @@ import {
|
|||||||
export const ContentHead = {
|
export const ContentHead = {
|
||||||
type: ProtoField(1, ScalarType.UINT32),
|
type: ProtoField(1, ScalarType.UINT32),
|
||||||
subType: ProtoField(2, ScalarType.UINT32, true),
|
subType: ProtoField(2, ScalarType.UINT32, true),
|
||||||
divSeq: ProtoField(3, ScalarType.UINT32, true),
|
c2cCmd: ProtoField(3, ScalarType.UINT32, true),
|
||||||
msgId: ProtoField(4, ScalarType.UINT32, true),
|
ranDom: ProtoField(4, ScalarType.UINT32, true),
|
||||||
sequence: ProtoField(5, ScalarType.UINT32, true),
|
sequence: ProtoField(5, ScalarType.UINT32, true),
|
||||||
timeStamp: ProtoField(6, ScalarType.UINT32, true),
|
timeStamp: ProtoField(6, ScalarType.UINT32, true),
|
||||||
field7: ProtoField(7, ScalarType.UINT64, true),
|
pkgNum: ProtoField(7, ScalarType.UINT64, true),
|
||||||
field8: ProtoField(8, ScalarType.UINT32, true),
|
pkgIndex: ProtoField(8, ScalarType.UINT32, true),
|
||||||
field9: ProtoField(9, ScalarType.UINT32, true),
|
divSeq: ProtoField(9, ScalarType.UINT32, true),
|
||||||
|
autoReply: ProtoField(10, ScalarType.UINT32),
|
||||||
|
ntMsgSeq: ProtoField(10, ScalarType.UINT32, true),
|
||||||
newId: ProtoField(12, ScalarType.UINT64, true),
|
newId: ProtoField(12, ScalarType.UINT64, true),
|
||||||
forward: ProtoField(15, () => ForwardHead, true),
|
forward: ProtoField(15, () => ForwardHead, true),
|
||||||
};
|
};
|
||||||
|
6
src/core/packet/transformer/proto/misc/fileid.ts
Normal file
6
src/core/packet/transformer/proto/misc/fileid.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ProtoField, ScalarType } from '@napneko/nap-proto-core';
|
||||||
|
|
||||||
|
export const FileId = {
|
||||||
|
appid: ProtoField(4, ScalarType.UINT32, true),
|
||||||
|
ttl: ProtoField(10, ScalarType.UINT32, true),
|
||||||
|
};
|
@@ -249,7 +249,7 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
reqToJoinGroup(groupCode: string, arg: unknown): void;
|
reqToJoinGroup(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
setGroupShutUp(groupCode: string, shutUp: boolean): void;
|
setGroupShutUp(groupCode: string, shutUp: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult>;
|
getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
@@ -148,10 +148,11 @@ export interface NodeIKernelMsgService {
|
|||||||
msgList: RawMessage[]
|
msgList: RawMessage[]
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
//@deprecated
|
// getMsgService/getMsgs { chatType: 2, peerUid: '975206796', privilegeFlag: 336068800 } 0 20 true
|
||||||
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
getMsgs(peer: Peer & { privilegeFlag: number }, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
//@deprecated
|
|
||||||
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
||||||
msgList: RawMessage[]
|
msgList: RawMessage[]
|
||||||
}>;
|
}>;
|
||||||
|
@@ -1,4 +1,15 @@
|
|||||||
import { ElementType, MessageElement, NTGrayTipElementSubTypeV2, PicSubType, PicType, TipAioOpGrayTipElement, TipGroupElement, NTVideoType, FaceType } from './msg';
|
import {
|
||||||
|
ElementType,
|
||||||
|
MessageElement,
|
||||||
|
NTGrayTipElementSubTypeV2,
|
||||||
|
PicSubType,
|
||||||
|
PicType,
|
||||||
|
TipAioOpGrayTipElement,
|
||||||
|
TipGroupElement,
|
||||||
|
NTVideoType,
|
||||||
|
FaceType,
|
||||||
|
Peer
|
||||||
|
} from './msg';
|
||||||
|
|
||||||
type ElementFullBase = Omit<MessageElement, 'elementType' | 'elementId' | 'extBufForUI'>;
|
type ElementFullBase = Omit<MessageElement, 'elementType' | 'elementId' | 'extBufForUI'>;
|
||||||
|
|
||||||
@@ -213,6 +224,9 @@ export interface ReplyElement {
|
|||||||
senderUidStr?: string;
|
senderUidStr?: string;
|
||||||
replyMsgTime?: string;
|
replyMsgTime?: string;
|
||||||
replyMsgClientSeq?: string;
|
replyMsgClientSeq?: string;
|
||||||
|
// HACK: Attributes that were not originally available,
|
||||||
|
// but were added due to NTQQ and NapCat's internal implementation, are used to supplement NapCat
|
||||||
|
_replyMsgPeer?: Peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CalendarElement {
|
export interface CalendarElement {
|
||||||
|
@@ -403,7 +403,7 @@ export interface NTGroupGrayMember {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 群灰色提示邀请者和被邀请者接口
|
* 群灰色提示邀请者和被邀请者接口
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
export interface NTGroupGrayInviterAndInvite {
|
export interface NTGroupGrayInviterAndInvite {
|
||||||
invited: NTGroupGrayMember;
|
invited: NTGroupGrayMember;
|
||||||
@@ -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
|
||||||
@@ -501,13 +501,15 @@ export interface RawMessage {
|
|||||||
elements: MessageElement[];// 消息元素
|
elements: MessageElement[];// 消息元素
|
||||||
sourceType: MsgSourceType;// 消息来源类型
|
sourceType: MsgSourceType;// 消息来源类型
|
||||||
isOnlineMsg: boolean;// 是否为在线消息
|
isOnlineMsg: boolean;// 是否为在线消息
|
||||||
|
clientSeq?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询消息参数接口
|
* 查询消息参数接口
|
||||||
*/
|
*/
|
||||||
export interface QueryMsgsParams {
|
export interface QueryMsgsParams {
|
||||||
chatInfo: Peer;
|
chatInfo: Peer & { privilegeFlag?: number };
|
||||||
|
//searchFields: number;
|
||||||
filterMsgType: Array<{ type: NTMsgType, subType: Array<number> }>;
|
filterMsgType: Array<{ type: NTMsgType, subType: Array<number> }>;
|
||||||
filterSendersUid: string[];
|
filterSendersUid: string[];
|
||||||
filterMsgFromTime: string;
|
filterMsgFromTime: string;
|
||||||
@@ -565,4 +567,4 @@ export enum FaceType {
|
|||||||
AniSticke = 3, // 动画贴纸
|
AniSticke = 3, // 动画贴纸
|
||||||
Lottie = 4,// 新格式表情
|
Lottie = 4,// 新格式表情
|
||||||
Poke = 5 // 可变Poke
|
Poke = 5 // 可变Poke
|
||||||
}
|
}
|
||||||
|
@@ -132,18 +132,26 @@ export enum BuddyReqType {
|
|||||||
KMEINITIATORWAITPEERCONFIRM = 13
|
KMEINITIATORWAITPEERCONFIRM = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 其中 ? 代表新版本参数
|
||||||
export interface FriendRequest {
|
export interface FriendRequest {
|
||||||
isBuddy?: boolean;
|
|
||||||
isInitiator?: boolean;
|
isInitiator?: boolean;
|
||||||
isDecide: boolean;
|
isDecide: boolean;
|
||||||
friendUid: string;
|
friendUid: string;
|
||||||
reqType: BuddyReqType,
|
reqType: BuddyReqType,
|
||||||
reqTime: string; // 时间戳 秒
|
reqTime: string; // 时间戳 秒
|
||||||
|
flag?: number; // 0
|
||||||
|
preGroupingId?: number; // 0
|
||||||
|
commFriendNum?: number; // 共同好友数
|
||||||
extWords: string; // 申请人填写的验证消息
|
extWords: string; // 申请人填写的验证消息
|
||||||
isUnread: boolean;
|
isUnread: boolean;
|
||||||
|
isDoubt?: boolean; // 是否是可疑的好友请求
|
||||||
|
nameMore?: string;
|
||||||
friendNick: string;
|
friendNick: string;
|
||||||
sourceId: number;
|
sourceId: number;
|
||||||
groupCode: string
|
groupCode: string;
|
||||||
|
isBuddy?: boolean;
|
||||||
|
isAgreed?: boolean;
|
||||||
|
relation?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FriendRequestNotify {
|
export interface FriendRequestNotify {
|
||||||
|
426
src/image-size/index.ts
Normal file
426
src/image-size/index.ts
Normal 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;
|
||||||
|
}
|
@@ -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
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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('消息不存在');
|
||||||
|
@@ -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 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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');
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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(),
|
||||||
|
@@ -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(),
|
||||||
|
@@ -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('设置在线状态失败');
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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('设置在线状态失败');
|
||||||
|
@@ -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: '' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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()),
|
||||||
});
|
});
|
||||||
|
@@ -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. 获取消息内容
|
||||||
|
@@ -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 };
|
||||||
}
|
}
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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 }),
|
||||||
|
@@ -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))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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 };
|
||||||
}
|
}
|
||||||
|
@@ -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 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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,
|
||||||
|
@@ -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()),
|
||||||
});
|
});
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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 }),
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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()),
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
@@ -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('消息不存在');
|
||||||
|
@@ -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()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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({
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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()]))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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');
|
||||||
}
|
}
|
||||||
|
@@ -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()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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>;
|
||||||
|
@@ -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())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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()])),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -15,7 +15,10 @@ export default class SetGroupWholeBan extends OneBotAction<Payload, null> {
|
|||||||
|
|
||||||
async _handle(payload: Payload): Promise<null> {
|
async _handle(payload: Payload): Promise<null> {
|
||||||
const enable = payload.enable?.toString() !== 'false';
|
const enable = payload.enable?.toString() !== 'false';
|
||||||
await this.core.apis.GroupApi.banGroup(payload.group_id.toString(), enable);
|
let res = await this.core.apis.GroupApi.banGroup(payload.group_id.toString(), enable);
|
||||||
|
if (res.result !== 0) {
|
||||||
|
throw new Error(`SetGroupWholeBan failed: ${res.errMsg} ${res.result}`);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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),
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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}`);
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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} 失败`);
|
||||||
@@ -174,9 +171,11 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
nickname: string,
|
nickname: string,
|
||||||
}, dp: number = 0): Promise<{
|
}, dp: number = 0): Promise<{
|
||||||
finallySendElements: SendArkElement,
|
finallySendElements: SendArkElement,
|
||||||
res_id?: string
|
res_id?: string,
|
||||||
|
deleteAfterSentFiles: string[],
|
||||||
} | null> {
|
} | null> {
|
||||||
const packetMsg: PacketMsg[] = [];
|
const packetMsg: PacketMsg[] = [];
|
||||||
|
let delFiles: string[] = [];
|
||||||
for (const node of messageNodes) {
|
for (const node of messageNodes) {
|
||||||
if (dp >= 3) {
|
if (dp >= 3) {
|
||||||
this.core.context.logger.logWarn('转发消息深度超过3层,将停止解析!');
|
this.core.context.logger.logWarn('转发消息深度超过3层,将停止解析!');
|
||||||
@@ -192,9 +191,11 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
nickname: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? 'QQ用户',
|
nickname: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? 'QQ用户',
|
||||||
}, dp + 1);
|
}, dp + 1);
|
||||||
sendElements = uploadReturnData?.finallySendElements ? [uploadReturnData.finallySendElements] : [];
|
sendElements = uploadReturnData?.finallySendElements ? [uploadReturnData.finallySendElements] : [];
|
||||||
|
delFiles.push(...(uploadReturnData?.deleteAfterSentFiles || []));
|
||||||
} else {
|
} else {
|
||||||
const sendElementsCreateReturn = await this.obContext.apis.MsgApi.createSendElements(OB11Data, msgPeer);
|
const sendElementsCreateReturn = await this.obContext.apis.MsgApi.createSendElements(OB11Data, msgPeer);
|
||||||
sendElements = sendElementsCreateReturn.sendElements;
|
sendElements = sendElementsCreateReturn.sendElements;
|
||||||
|
delFiles.push(...sendElementsCreateReturn.deleteAfterSentFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
const packetMsgElements: rawMsgWithSendMsg = {
|
const packetMsgElements: rawMsgWithSendMsg = {
|
||||||
@@ -210,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;
|
||||||
@@ -218,7 +219,8 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0];
|
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0];
|
||||||
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${stringifyWithBigInt(msg)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${stringifyWithBigInt(msg)}`);
|
||||||
if (msg) {
|
if (msg) {
|
||||||
await this.core.apis.FileApi.downloadRawMsgMedia([msg]);
|
let msgCache = await this.core.apis.FileApi.downloadRawMsgMedia([msg]);
|
||||||
|
delFiles.push(...msgCache);
|
||||||
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer);
|
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer);
|
||||||
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
this.core.context.logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
|
||||||
packetMsg.push(transformedMsg);
|
packetMsg.push(transformedMsg);
|
||||||
@@ -234,6 +236,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const resid = await this.core.apis.PacketApi.pkt.operation.UploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
const resid = await this.core.apis.PacketApi.pkt.operation.UploadForwardMsg(packetMsg, msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : 0);
|
||||||
const forwardJson = ForwardMsgBuilder.fromPacketMsg(resid, packetMsg, source, news, summary, prompt);
|
const forwardJson = ForwardMsgBuilder.fromPacketMsg(resid, packetMsg, source, news, summary, prompt);
|
||||||
return {
|
return {
|
||||||
|
deleteAfterSentFiles: delFiles,
|
||||||
finallySendElements: {
|
finallySendElements: {
|
||||||
elementType: ElementType.ARK,
|
elementType: ElementType.ARK,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
@@ -255,7 +258,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const res_id = uploadReturnData?.res_id;
|
const res_id = uploadReturnData?.res_id;
|
||||||
const finallySendElements = uploadReturnData?.finallySendElements;
|
const finallySendElements = uploadReturnData?.finallySendElements;
|
||||||
if (!finallySendElements) throw Error('转发消息失败,生成节点为空');
|
if (!finallySendElements) throw Error('转发消息失败,生成节点为空');
|
||||||
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], []).catch(() => undefined);
|
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(msgPeer, [finallySendElements], uploadReturnData.deleteAfterSentFiles || []).catch(() => undefined);
|
||||||
return { message: returnMsg ?? null, res_id: res_id! };
|
return { message: returnMsg ?? null, res_id: res_id! };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,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 {
|
||||||
@@ -291,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;
|
||||||
@@ -318,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) {
|
||||||
@@ -331,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()) {
|
||||||
@@ -358,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 {
|
||||||
|
@@ -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');
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user