Compare commits

...

99 Commits

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 08:37:11 +00:00
Mlikiowa
e89a2266ec release: v4.7.60 2025-05-11 03:32:29 +00:00
手瓜一十雪
6607533311 fix: #996 2025-05-11 11:31:52 +08:00
Mlikiowa
4057054220 release: v4.7.58 2025-05-11 03:24:02 +00:00
手瓜一十雪
055e43845e feat: ffmpeg下载来源更换 2025-05-11 11:23:43 +08:00
Mlikiowa
d67270f2f8 release: v4.7.57 2025-05-10 13:15:37 +00:00
手瓜一十雪
d061b6c190 feat: 34958 2025-05-10 21:15:07 +08:00
Mlikiowa
945f87d77f release: v4.7.56 2025-05-09 11:28:05 +00:00
手瓜一十雪
6c9be52d39 feat: 34740 2025-05-09 19:16:25 +08:00
手瓜一十雪
98e347f010 fix: 过滤掉已读 2025-05-09 18:51:00 +08:00
手瓜一十雪
607e367bb1 fix 2025-05-09 13:17:47 +08:00
Mlikiowa
7a25dc1ef1 release: v4.7.55 2025-05-08 10:11:36 +00:00
手瓜一十雪
e22ec4be09 Merge pull request #991 from NapNeko/fix-upload-foward-cahce-del
fix: upload-foward-cache-del
2025-05-08 18:10:45 +08:00
手瓜一十雪
51a06622f9 fix: typo
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-05-08 18:04:40 +08:00
手瓜一十雪
22faf5b831 fix 2025-05-08 17:58:12 +08:00
手瓜一十雪
e781c662b2 fix 2025-05-08 15:20:55 +08:00
手瓜一十雪
5744698d24 docs: 清空不好的影响&推荐一下 2025-05-08 15:20:06 +08:00
Mlikiowa
2c2ab3cd48 release: v4.7.51 2025-05-07 15:16:50 +00:00
手瓜一十雪
cfae4f5acd fix: 增强 2025-05-07 22:26:25 +08:00
Mlikiowa
de541e3249 release: v4.5.50 2025-05-07 14:10:15 +00:00
手瓜一十雪
f5187c5c01 Merge pull request #989 from NapNeko/file-url-feat
feat: 消息上报Url重构
2025-05-07 22:09:29 +08:00
手瓜一十雪
9936279443 fix 2025-05-07 22:03:00 +08:00
手瓜一十雪
2818773fd4 fix 2025-05-07 20:53:36 +08:00
手瓜一十雪
b9293cbcd0 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:50 +08:00
手瓜一十雪
5b9e44ddfc fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:28 +08:00
手瓜一十雪
1791accab7 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:51:12 +08:00
手瓜一十雪
08081360f3 fix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-07 20:50:51 +08:00
手瓜一十雪
e933a95e97 fix 2025-05-07 18:10:58 +08:00
手瓜一十雪
4ef457fe6f feat: fileUrl Get 2025-05-07 18:10:49 +08:00
Mlikiowa
bd9cae8921 release: v4.7.49 2025-05-07 09:16:28 +00:00
手瓜一十雪
303a74f8fd feat: 背压问题 2025-05-07 17:14:57 +08:00
Mlikiowa
0b7f126ce1 release: v4.7.48 2025-05-07 08:21:34 +00:00
Clansty
308b5c027f fix: at 变成负数 2025-05-07 03:46:17 +08:00
手瓜一十雪
ed3abc4b43 feat 2025-05-04 21:11:34 +08:00
Mlikiowa
87ecb3b380 release: v4.7.47 2025-05-03 14:27:49 +00:00
手瓜一十雪
7e31763a25 fix 2025-05-03 22:26:41 +08:00
Mlikiowa
c9df57d16a release: v4.7.46 2025-05-03 08:08:25 +00:00
手瓜一十雪
3d0f8ee657 fix 2025-05-03 16:06:51 +08:00
手瓜一十雪
6421bb4f5c feat: normalize 2025-05-02 15:10:31 +08:00
Mlikiowa
3919743885 release: v4.7.45 2025-04-30 13:43:59 +00:00
pk5ls20
a5a57b9e20 fix: fxxking fake forward element
- close #972, #977, #666
2025-04-30 20:31:03 +08:00
手瓜一十雪
e31d2810ad fix 2025-04-29 22:06:01 +08:00
Mlikiowa
140e62fdcd release: v4.7.44 2025-04-28 14:04:40 +00:00
手瓜一十雪
014b4deb87 feat: 34740 2025-04-28 22:04:20 +08:00
Mlikiowa
956b6cd172 release: v4.7.43 2025-04-26 11:10:37 +00:00
手瓜一十雪
bbaca3f044 fix 2025-04-26 19:10:00 +08:00
Mlikiowa
bb8a44b918 release: v4.7.42 2025-04-26 11:02:25 +00:00
手瓜一十雪
b5574d5999 fix: #976 2025-04-26 19:00:31 +08:00
手瓜一十雪
06dde072da Merge pull request #975 from pohgxz/main
接口 _get_model_show 的 model 设置为可选属性
2025-04-26 18:31:10 +08:00
Nepenthe
8e92a81bb9 接口 _get_model_show 的 model 设置为可选属性 2025-04-26 14:48:35 +08:00
Nepenthe
2c7345ae88 Merge branch 'NapNeko:main' into main 2025-04-26 14:40:38 +08:00
Mlikiowa
33d4696155 release: v4.7.41 2025-04-24 09:43:32 +00:00
手瓜一十雪
7d2dcc10e5 fix 2025-04-24 17:43:13 +08:00
Mlikiowa
e82687454c release: v4.7.40 2025-04-24 07:57:16 +00:00
手瓜一十雪
84382caebc fix 2025-04-24 15:56:55 +08:00
Mlikiowa
662530e507 release: v4.7.36 2025-04-24 07:53:59 +00:00
手瓜一十雪
edf81d0a2e feat: 34606 2025-04-24 15:37:44 +08:00
手瓜一十雪
7cbae86941 Revert "fix: 私聊撤回"
This reverts commit 8ff7420a5e.
2025-04-24 11:34:07 +08:00
手瓜一十雪
8ff7420a5e fix: 私聊撤回 2025-04-24 11:33:11 +08:00
手瓜一十雪
7ae59b1419 Merge pull request #971 from Sn0wo2/main
fix: temp_source
2025-04-24 09:54:29 +08:00
手瓜一十雪
41036f8ee8 fix: 969 2025-04-24 09:50:26 +08:00
Me0wo
380777ca04 fix: #970 2025-04-24 04:11:31 +08:00
Mlikiowa
c658cd1096 release: v4.7.35 2025-04-23 08:52:43 +00:00
手瓜一十雪
c7b9946d2f feat: doubt friends支持 2025-04-23 16:46:09 +08:00
手瓜一十雪
0caca473d6 feat: 34566 2025-04-23 16:18:48 +08:00
手瓜一十雪
3e5d35957d fix 2025-04-23 16:12:56 +08:00
手瓜一十雪
6b8b14aba2 fix: #963 2025-04-23 11:47:58 +08:00
手瓜一十雪
5db7a90a24 feat: 301 302自动跟随下载 2025-04-21 18:43:44 +08:00
Mlikiowa
88b86611a3 release: v4.7.34 2025-04-20 14:12:47 +00:00
手瓜一十雪
886fe2052e feat: 避免危险信息 2025-04-20 22:12:12 +08:00
手瓜一十雪
e4dd194d4a fix: #960
神经设计
2025-04-20 22:10:24 +08:00
Nepenthe
faf390bb18 Merge branch 'NapNeko:main' into main 2025-02-21 21:19:02 +08:00
Nepenthe
941b30847b Merge branch 'NapNeko:main' into main 2025-01-25 23:14:26 +08:00
Nepenthe
4c5a26698e Merge branch 'NapNeko:main' into main 2025-01-15 21:33:43 +08:00
Nepenthe
d14a1dd948 Merge branch 'NapNeko:main' into main 2024-11-17 17:28:31 +08:00
Nepenthe
1c0b434f47 Merge branch 'NapNeko:main' into main 2024-10-31 19:15:25 +08:00
Nepenthe
573451bade 修复<get_record>接口 2024-10-30 21:07:01 +08:00
151 changed files with 1895 additions and 1053 deletions

View File

@@ -1,8 +1,9 @@
<img src="https://napneko.github.io/assets/newnewlogo.png" width = "305" height = "411" alt="NapCat" align=right />
<div align="center">
# NapCat
![NapCatQQ](https://socialify.git.ci/NapNeko/NapCatQQ/image?font=Jost&logo=https%3A%2F%2Fnapneko.github.io%2Fassets%2Fnewlogo.png&name=1&owner=1&pattern=Diagonal+Stripes&stargazers=1&theme=Auto)
_Modern protocol-side framework implemented based on NTQQ._
@@ -40,13 +41,20 @@ _Modern protocol-side framework implemented based on NTQQ._
| Docs | [![Cloudflare.Pages](https://img.shields.io/badge/docs%20on-Cloudflare.Pages-blue)](https://napneko.pages.dev/) | [![Server.Other](https://img.shields.io/badge/docs%20on-Server.Other-green)](https://napcat.cyou/) | [![NapCat.Wiki](https://img.shields.io/badge/docs%20on-NapCat.Wiki-red)](https://www.napcat.wiki) |
|:-:|:-:|:-:|:-:|
| Contact | [![QQ Group#1](https://img.shields.io/badge/QQ%20Group%231-Join-blue)](https://qm.qq.com/q/I6LU87a0Yq) | [![QQ Group#2](https://img.shields.io/badge/QQ%20Group%232-Join-blue)](https://qm.qq.com/q/HaRcfrHpUk) | [![Telegram](https://img.shields.io/badge/Telegram-MelodicMoonlight-blue)](https://t.me/MelodicMoonlight) |
|:-:|:-:|:-:|:-:|
| QQ Group | [![QQ Group#4](https://img.shields.io/badge/QQ%20Group%234-Join-blue)](https://qm.qq.com/q/CMmPbGw0jA) | [![QQ Group#3](https://img.shields.io/badge/QQ%20Group%233-Join-blue)](https://qm.qq.com/q/8zJMLjqy2Y) | [![QQ Group#2](https://img.shields.io/badge/QQ%20Group%232-Join-blue)](https://qm.qq.com/q/HaRcfrHpUk) | [![QQ Group#1](https://img.shields.io/badge/QQ%20Group%231-Join-blue)](https://qm.qq.com/q/I6LU87a0Yq) |
|:-:|:-:|:-:|:-:|:-:|
| Telegram | [![Telegram](https://img.shields.io/badge/Telegram-MelodicMoonlight-blue)](https://t.me/MelodicMoonlight) |
|:-:|:-:|
## Thanks
+ [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).
**本仓库仅用于提高易用性,实现消息推送类功能,此外,禁止任何项目未经仓库主作者授权基于 NapCat 代码开发。使用请遵守当地法律法规,由此造成的问题由使用者和提供违规使用教程者负责。**
## Warnings
[某框架抄袭部分分析](https://napneko.github.io/other/about-copy)

Binary file not shown.

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.

View File

@@ -1,9 +1,9 @@
{
"name": "qq-chat",
"version": "9.9.18-32869",
"verHash": "e735296c",
"linuxVersion": "3.2.16-32869",
"linuxVerHash": "4c192ba9",
"version": "9.9.19-34740",
"verHash": "f31348f2",
"linuxVersion": "3.2.17-34740",
"linuxVerHash": "5aa2d8d6",
"private": true,
"description": "QQ",
"productName": "QQ",
@@ -16,27 +16,10 @@
"bin": {
"qd": "externals/devtools/cli/index.js"
},
"appid": {
"win32": "537258389",
"darwin": "537258412",
"linux": "537258424"
},
"main": "./loadNapCat.js",
"peerDependenciesMeta": {
"*": {
"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",
"buildVersion": "34740",
"isPureShell": true,
"isByteCodeShell": true,
"platform": "win32",
"eleArch": "x64"
}
}

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.7.33",
"version": "4.7.63",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
@@ -42,7 +42,7 @@
"async-mutex": "^0.5.0",
"commander": "^13.0.0",
"cors": "^2.8.5",
"esbuild": "0.25.0",
"esbuild": "0.25.4",
"eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^4.0.0",
"eslint-plugin-import": "^2.29.1",
@@ -50,7 +50,6 @@
"fast-xml-parser": "^4.3.6",
"file-type": "^20.0.0",
"globals": "^16.0.0",
"image-size": "^1.1.1",
"json5": "^2.2.3",
"multer": "^1.4.5-lts.1",
"typescript": "^5.3.3",

View File

@@ -8,11 +8,12 @@ import { pipeline } from 'stream/promises';
import { fileURLToPath } from 'url';
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 = [
"https://github.moeyy.xyz/" + downloadOri,
"https://ghp.ci/" + downloadOri,
"https://gh.api.99988866.xyz/" + downloadOri,
'https://github.moeyy.xyz/' + downloadOri,
'https://ghp.ci/' + downloadOri,
'https://gh.api.99988866.xyz/' + downloadOri,
'https://gh.api.99988866.xyz/' + downloadOri,
downloadOri
];
@@ -336,17 +337,24 @@ export async function downloadFFmpegIfNotExists(log: LogWrapper) {
const ffprobe_exist = fs.existsSync(path.join(currentPath, 'ffmpeg', 'ffprobe.exe'));
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}`);
});
if (!url) {
log.log('[FFmpeg] [Error] 下载FFmpeg失败');
return {
path: null,
reset: false
};
}
return {
path: path.join(currentPath, 'ffmpeg'),
reset: true
}
};
}
return {
path: path.join(currentPath, 'ffmpeg'),
reset: true
}
};
}

View File

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

View File

@@ -115,7 +115,7 @@ async function tryDownload(options: string | HttpDownloadOptions, useReferer: bo
if (useReferer && !headers['Referer']) {
headers['Referer'] = url;
}
const fetchRes = await fetch(url, { headers }).catch((err) => {
const fetchRes = await fetch(url, { headers, redirect: 'follow' }).catch((err) => {
if (err.cause) {
throw err.cause;
}
@@ -145,8 +145,8 @@ export enum FileUriType {
export async function checkUriType(Uri: string) {
const LocalFileRet = await solveProblem((uri: string) => {
if (fs.existsSync(uri)) {
return { Uri: uri, Type: FileUriType.Local };
if (fs.existsSync(path.normalize(uri))) {
return { Uri: path.normalize(uri), Type: FileUriType.Local };
}
return undefined;
}, Uri);
@@ -182,28 +182,28 @@ export async function uriToLocalFile(dir: string, uri: string, filename: string
const filePath = path.join(dir, filename);
switch (UriType) {
case FileUriType.Local: {
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
}
case FileUriType.Local: {
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
}
case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
}
}

View File

@@ -1,6 +1,4 @@
import { Peer } from '@/core';
import crypto from 'crypto';
export class LimitedHashTable<K, V> {
private readonly keyToValue: Map<K, V> = new Map();
private readonly valueToKey: Map<V, K> = new Map();
@@ -78,65 +76,19 @@ export class LimitedHashTable<K, V> {
}
class MessageUniqueWrapper {
private readonly msgDataMap: LimitedHashTable<string, number>;
private readonly msgIdMap: LimitedHashTable<string, number>;
constructor(maxMap: number = 5000) {
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
constructor() {
}
getRecentMsgIds(Peer: Peer, size: number): string[] {
const heads = this.msgIdMap.getHeads(size);
if (!heads) {
return [];
getOutputData(peer: Peer, msg_id: string, seq: string): string {
return `${peer.chatType}|${msg_id}|${peer.peerUid}|${seq}`;
}
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));
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);
return { MsgId: msgId, Peer: { chatType: parseInt(chatType), peerUid, guildId: '' }, seq: seq };
}
}

View File

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

View File

@@ -9,7 +9,7 @@ export async function runTask<T, R>(workerScript: string, taskData: T): Promise<
console.error('Worker Log--->:', (result as { log: string }).log);
}
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);
});

View File

@@ -17,8 +17,6 @@ import fs from 'fs';
import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
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 { calculateFileMD5 } from '@/common/file';
import pathLib from 'node:path';
@@ -28,6 +26,9 @@ import { SendMessageContext } from '@/onebot/api';
import { getFileTypeForSendType } from '../helper/msg';
import { FFmpegService } from '@/common/ffmpeg';
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 {
context: InstanceContext;
@@ -44,7 +45,7 @@ export class NTQQFileApi {
'https://secret-service.bietiaop.com/rkeys',
'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) {
await this.core.util.copyFile(filePath, destPath);
@@ -137,7 +208,7 @@ export class NTQQFileApi {
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const imageSize = await this.core.apis.FileApi.getImageSize(picPath);
const imageSize = await imageSizeFallBack(picPath);
context.deleteAfterSentFiles.push(path);
return {
elementType: ElementType.PIC,
@@ -308,23 +379,24 @@ export class NTQQFileApi {
element.elementType === ElementType.FILE
) {
switch (element.elementType) {
case ElementType.PIC:
case ElementType.PIC:
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.VIDEO:
break;
case ElementType.VIDEO:
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.PTT:
break;
case ElementType.PTT:
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.FILE:
break;
case ElementType.FILE:
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
break;
}
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) {
@@ -345,6 +417,7 @@ export class NTQQFileApi {
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[{
fileModelId: '0',
downSourceType: 0,
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
@@ -363,19 +436,6 @@ export class NTQQFileApi {
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> {
const randomResultId = 100000 + Math.floor(Math.random() * 10000);

View File

@@ -86,4 +86,31 @@ export class NTQQFriendApi {
accept,
});
}
async handleDoubtFriendRequest(friendUid: string, str1: string = '', str2: string = '') {
this.context.session.getBuddyService().approvalDoubtBuddyReq(friendUid, str1, str2);
}
async getDoubtFriendRequest(count: number) {
let date = Date.now().toString();
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelBuddyService/getDoubtBuddyReq',
'NodeIKernelBuddyListener/onDoubtBuddyReqChange',
[date, count, ''],
() => true,
(data) => data.reqId === date
);
let requests = Promise.all(ret.doubtList.map(async (item) => {
return {
flag: item.uid, //注意强制String 非isNumeric 不遵守则不符合设计
uin: await this.core.apis.UserApi.getUinByUidV2(item.uid) ?? 0,// 信息字段
nick: item.nick, // 信息字段 这个不是nickname 可能是来源的群内的昵称
source: item.source, // 信息字段
reason: item.reason, // 信息字段
msg: item.msg, // 信息字段
group_code: item.groupCode, // 信息字段
time: item.reqTime, // 信息字段
type: 'doubt' //保留字段
};
}));
return requests;
}
}

View File

@@ -71,6 +71,7 @@ export class NTQQMsgApi {
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,
//searchFields: 3,
filterMsgType: [],
filterSendersUid: [],
filterMsgToTime: '0',
@@ -84,6 +85,7 @@ export class NTQQMsgApi {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,
filterMsgType: [],
//searchFields: 3,
filterSendersUid: SendersUid,
filterMsgToTime: MsgTime,
filterMsgFromTime: MsgTime,
@@ -100,6 +102,7 @@ export class NTQQMsgApi {
filterMsgToTime: '0',
filterMsgFromTime: '0',
isReverseOrder: false,
//searchFields: 3,
isIncludeCurrent: true,
pageLimit: 1,
});
@@ -110,6 +113,7 @@ export class NTQQMsgApi {
filterMsgType: [],
filterSendersUid: [],
filterMsgToTime: '0',
//searchFields: 3,
filterMsgFromTime: '0',
isReverseOrder: true,
isIncludeCurrent: true,
@@ -128,6 +132,7 @@ export class NTQQMsgApi {
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
//searchFields: 3,
filterMsgToTime: filterMsgToTime,
filterMsgFromTime: filterMsgFromTime,
isReverseOrder: false,
@@ -142,6 +147,7 @@ export class NTQQMsgApi {
chatInfo: peer,
filterMsgType: [],
filterSendersUid: SendersUid,
//searchFields: 3,
filterMsgToTime: '0',
filterMsgFromTime: '0',
isReverseOrder: true,

View File

@@ -258,5 +258,41 @@
"3.2.17-34467": {
"appid": 537282292,
"qua": "V1_LNX_NQ_3.2.17_34467_GW_B"
},
"9.9.19-34566": {
"appid": 537282307,
"qua": "V1_WIN_NQ_9.9.19_34566_GW_B"
},
"3.2.17-34566": {
"appid": 537282343,
"qua": "V1_LNX_NQ_3.2.17_34566_GW_B"
},
"3.2.17-34606": {
"appid": 537282343,
"qua": "V1_LNX_NQ_3.2.17_34606_GW_B"
},
"9.9.19-34606": {
"appid": 537282307,
"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"
}
}

View File

@@ -327,12 +327,56 @@
"send": "770CDC0",
"recv": "77106F0"
},
"9.9.19-34362-x64":{
"9.9.19-34362-x64": {
"send": "3BD80D0",
"recv": "3BDC8D0"
},
"9.9.19-34467-x64": {
"send": "3BD8690",
"recv": "3BDCE90"
},
"9.9.19-34566-x64": {
"send": "3BDA110",
"recv": "3BDE910"
},
"9.9.19-34606-x64": {
"send": "3BDA110",
"recv": "3BDE910"
},
"3.2.17-34606-x64": {
"send": "AD7DC60",
"recv": "AD81680"
},
"3.2.17-34606-arm64": {
"send": "7711270",
"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"
}
}

View File

@@ -3,57 +3,75 @@ import { BuddyCategoryType, FriendRequestNotify } from '@/core/types';
export type OnBuddyChangeParams = BuddyCategoryType[];
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);
}
onDoubtBuddyReqChange(arg: unknown): any {
onDoubtBuddyReqChange(_arg:
{
reqId: string;
cookie: string;
doubtList: Array<{
uid: string;
nick: string;
age: number,
sex: number;
commFriendNum: number;
reqTime: string;
msg: string;
source: string;
reason: string;
groupCode: string;
nameMore?: null;
}>;
}): void | Promise<void> {
}
onDoubtBuddyReqUnreadNumChange(arg: unknown): any {
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 {
}
}

View File

@@ -21,7 +21,8 @@ export interface OnRichMediaDownloadCompleteParams {
clientMsg: string,
businessId: number,
userTotalSpacePerDay: unknown,
userUsedSpacePerDay: unknown
userUsedSpacePerDay: unknown,
chatType: number,
}
export interface GroupFileInfoUpdateParamType {
@@ -97,112 +98,112 @@ export interface TempOnRecvParams {
}
export class NodeIKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage): any {
onAddSendMsg(_msgRecord: RawMessage): any {
}
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): any {
onBroadcastHelperDownloadComplete(_broadcastHelperTransNotifyInfo: unknown): any {
}
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): any {
onBroadcastHelperProgressUpdate(_broadcastHelperTransNotifyInfo: unknown): any {
}
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): any {
onChannelFreqLimitInfoUpdate(_contact: unknown, _z: unknown, _freqLimitInfo: unknown): any {
}
onContactUnreadCntUpdate(hashMap: unknown): any {
onContactUnreadCntUpdate(_hashMap: unknown): any {
}
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): any {
onCustomWithdrawConfigUpdate(_customWithdrawConfig: unknown): any {
}
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): any {
onDraftUpdate(_contact: unknown, _arrayList: unknown, _j2: unknown): any {
}
onEmojiDownloadComplete(emojiNotifyInfo: unknown): any {
onEmojiDownloadComplete(_emojiNotifyInfo: unknown): any {
}
onEmojiResourceUpdate(emojiResourceInfo: unknown): any {
onEmojiResourceUpdate(_emojiResourceInfo: unknown): any {
}
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
onFeedEventUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
}
onFileMsgCome(arrayList: unknown): any {
onFileMsgCome(_arrayList: unknown): any {
}
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
onFirstViewDirectMsgUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
}
onFirstViewGroupGuildMapping(arrayList: unknown): any {
onFirstViewGroupGuildMapping(_arrayList: unknown): any {
}
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): any {
onGrabPasswordRedBag(_i2: unknown, _str: unknown, _i3: unknown, _recvdOrder: unknown, _msgRecord: unknown): any {
}
onGroupFileInfoAdd(groupItem: unknown): any {
onGroupFileInfoAdd(_groupItem: unknown): any {
}
onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType): any {
onGroupFileInfoUpdate(_groupFileListResult: GroupFileInfoUpdateParamType): any {
}
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): any {
onGroupGuildUpdate(_groupGuildNotifyInfo: unknown): any {
}
onGroupTransferInfoAdd(groupItem: unknown): any {
onGroupTransferInfoAdd(_groupItem: unknown): any {
}
onGroupTransferInfoUpdate(groupFileListResult: unknown): any {
onGroupTransferInfoUpdate(_groupFileListResult: unknown): any {
}
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): any {
onGuildInteractiveUpdate(_guildInteractiveNotificationItem: unknown): any {
}
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): any {
onGuildMsgAbFlagChanged(_guildMsgAbFlag: unknown): any {
}
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): any {
onGuildNotificationAbstractUpdate(_guildNotificationAbstractInfo: unknown): any {
}
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): any {
onHitCsRelatedEmojiResult(_downloadRelateEmojiResultInfo: unknown): any {
}
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): any {
onHitEmojiKeywordResult(_hitRelatedEmojiWordsResult: unknown): any {
}
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): any {
onHitRelatedEmojiResult(_relatedWordEmojiInfo: unknown): any {
}
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): any {
onImportOldDbProgressUpdate(_importOldDbMsgNotifyInfo: unknown): any {
}
onInputStatusPush(inputStatusInfo: {
onInputStatusPush(_inputStatusInfo: {
chatType: number;
eventType: number;
fromUin: string;
@@ -215,55 +216,55 @@ export class NodeIKernelMsgListener {
}
onKickedOffLine(kickedInfo: KickedOffLineInfo): any {
onKickedOffLine(_kickedInfo: KickedOffLineInfo): any {
}
onLineDev(arrayList: unknown): any {
onLineDev(_arrayList: unknown): any {
}
onLogLevelChanged(j2: unknown): any {
onLogLevelChanged(_j2: unknown): any {
}
onMsgAbstractUpdate(arrayList: unknown): any {
onMsgAbstractUpdate(_arrayList: unknown): any {
}
onMsgBoxChanged(arrayList: unknown): any {
onMsgBoxChanged(_arrayList: unknown): any {
}
onMsgDelete(contact: unknown, arrayList: unknown): any {
onMsgDelete(_contact: unknown, _arrayList: unknown): any {
}
onMsgEventListUpdate(hashMap: unknown): any {
onMsgEventListUpdate(_hashMap: unknown): any {
}
onMsgInfoListAdd(arrayList: unknown): any {
onMsgInfoListAdd(_arrayList: unknown): any {
}
onMsgInfoListUpdate(msgList: RawMessage[]): any {
onMsgInfoListUpdate(_msgList: RawMessage[]): any {
}
onMsgQRCodeStatusChanged(i2: unknown): any {
onMsgQRCodeStatusChanged(_i2: unknown): any {
}
onMsgRecall(chatType: ChatType, uid: string, msgSeq: string): any {
onMsgRecall(_chatType: ChatType, _uid: string, _msgSeq: string): any {
}
onMsgSecurityNotify(msgRecord: unknown): any {
onMsgSecurityNotify(_msgRecord: unknown): any {
}
onMsgSettingUpdate(msgSetting: unknown): any {
onMsgSettingUpdate(_msgSetting: unknown): any {
}
@@ -279,108 +280,108 @@ export class NodeIKernelMsgListener {
}
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
onReadFeedEventUpdate(_firstViewDirectMsgNotifyInfo: unknown): any {
}
onRecvGroupGuildFlag(i2: unknown): any {
onRecvGroupGuildFlag(_i2: unknown): any {
}
onRecvMsg(arrayList: RawMessage[]): any {
onRecvMsg(_arrayList: RawMessage[]): any {
}
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): any {
onRecvMsgSvrRspTransInfo(_j2: unknown, _contact: unknown, _i2: unknown, _i3: unknown, _str: unknown, _bArr: unknown): any {
}
onRecvOnlineFileMsg(arrayList: unknown): any {
onRecvOnlineFileMsg(_arrayList: unknown): any {
}
onRecvS2CMsg(arrayList: unknown): any {
onRecvS2CMsg(_arrayList: unknown): any {
}
onRecvSysMsg(arrayList: Array<number>): any {
onRecvSysMsg(_arrayList: Array<number>): any {
}
onRecvUDCFlag(i2: unknown): any {
onRecvUDCFlag(_i2: unknown): any {
}
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): any {
onRichMediaDownloadComplete(_fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): any {
}
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): any {
onRichMediaProgerssUpdate(_fileTransNotifyInfo: unknown): any {
}
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): any {
onRichMediaUploadComplete(_fileTransNotifyInfo: unknown): any {
}
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): any {
onSearchGroupFileInfoUpdate(_searchGroupFileResult: unknown): any {
}
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): any {
onSendMsgError(_j2: unknown, _contact: unknown, _i2: unknown, _str: unknown): any {
}
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): any {
onSysMsgNotification(_i2: unknown, _j2: unknown, _j3: unknown, _arrayList: unknown): any {
}
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): any {
onTempChatInfoUpdate(_tempChatInfo: TempOnRecvParams): any {
}
onUnreadCntAfterFirstView(hashMap: unknown): any {
onUnreadCntAfterFirstView(_hashMap: unknown): any {
}
onUnreadCntUpdate(hashMap: unknown): any {
onUnreadCntUpdate(_hashMap: unknown): any {
}
onUserChannelTabStatusChanged(z: unknown): any {
onUserChannelTabStatusChanged(_z: unknown): any {
}
onUserOnlineStatusChanged(z: unknown): any {
onUserOnlineStatusChanged(_z: unknown): any {
}
onUserTabStatusChanged(arrayList: unknown): any {
onUserTabStatusChanged(_arrayList: unknown): any {
}
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
onlineStatusBigIconDownloadPush(_i2: unknown, _j2: unknown, _str: unknown): any {
}
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
onlineStatusSmallIconDownloadPush(_i2: unknown, _j2: unknown, _str: unknown): any {
}
// 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]): any {
onUserSecQualityChanged(..._args: unknown[]): any {
}
onMsgWithRichLinkInfoUpdate(...args: unknown[]): any {
onMsgWithRichLinkInfoUpdate(..._args: unknown[]): any {
}
onRedTouchChanged(...args: unknown[]): any {
onRedTouchChanged(..._args: unknown[]): any {
}
// 第一次发现于Win 9.9.9-23159
onBroadcastHelperProgerssUpdate(...args: unknown[]): any {
onBroadcastHelperProgerssUpdate(..._args: unknown[]): any {
}
}

View File

@@ -6,13 +6,14 @@ import {
PacketMsgFileElement,
PacketMsgPicElement,
PacketMsgPttElement,
PacketMsgVideoElement
PacketMsgReplyElement,
PacketMsgVideoElement,
} from '@/core/packet/message/element';
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
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 { ImageOcrResult } from '@/core/packet/entities/ocrResult';
import { gunzipSync } from 'zlib';
@@ -76,22 +77,24 @@ export class PacketOperationContext {
async UploadResources(msg: PacketMsg[], groupUin: number = 0) {
const chatType = groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C;
const peerUid = groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid;
const reqList = msg.flatMap(m =>
m.msg.map(e => {
if (e instanceof PacketMsgPicElement) {
return this.context.highway.uploadImage({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgVideoElement) {
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgPttElement) {
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgFileElement) {
return this.context.highway.uploadFile({ chatType, peerUid }, e);
}
return null;
}).filter(Boolean)
const reqList = msg.flatMap((m) =>
m.msg
.map((e) => {
if (e instanceof PacketMsgPicElement) {
return this.context.highway.uploadImage({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgVideoElement) {
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgPttElement) {
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
} else if (e instanceof PacketMsgFileElement) {
return this.context.highway.uploadFile({ chatType, peerUid }, e);
}
return null;
})
.filter(Boolean)
);
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) => {
if (result.status === 'rejected') {
this.context.logger.error(`上传第${index + 1}个资源失败:${result.reason.stack}`);
@@ -100,10 +103,13 @@ export class PacketOperationContext {
}
async UploadImage(img: PacketMsgPicElement) {
await this.context.highway.uploadImage({
chatType: ChatType.KCHATTYPEC2C,
peerUid: this.context.napcore.basicInfo.uid
}, img);
await this.context.highway.uploadImage(
{
chatType: ChatType.KCHATTYPEC2C,
peerUid: this.context.napcore.basicInfo.uid,
},
img
);
const index = img.msgInfo?.msgInfoBody?.at(0)?.index;
if (!index) {
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}`;
}
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>) {
const req = trans.DownloadGroupImage.build(groupUin, node);
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}`;
}
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) {
const req = trans.ImageOCR.build(imgUrl);
const resp = await this.context.client.sendOidbPacket(req, true);
@@ -137,24 +172,66 @@ export class PacketOperationContext {
coordinates: item.polygon.coordinates.map((c) => {
return {
x: c.x,
y: c.y
y: c.y,
};
}),
};
}),
language: res.ocrRspBody.language
language: res.ocrRspBody.language,
} 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);
}
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 resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.UploadForwardMsg.parse(resp);
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 resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.MoveGroupFile.parse(resp);
@@ -174,6 +251,7 @@ export class PacketOperationContext {
const res = trans.DownloadGroupFile.parse(resp);
return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`;
}
async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) {
const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5);
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`;
}
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) {
const req = trans.GetMiniAppAdaptShareInfo.build(param);
const resp = await this.context.client.sendOidbPacket(req, true);
@@ -203,12 +274,17 @@ export class PacketOperationContext {
return res.content.map((item) => {
return {
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;
const reqMaxTime = 30;
const sessionId = crypto.randomBytes(4).readUInt32BE(0);
@@ -236,6 +312,7 @@ export class PacketOperationContext {
if (!main?.actionData.msgBody) {
throw new Error('msgBody is empty');
}
this.context.logger.debug('rawChains ', inflate.toString('hex'));
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
if (!msg?.body?.richText?.elems) {
@@ -251,12 +328,12 @@ export class PacketOperationContext {
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
element.picElement = {
...element.picElement,
originImageUrl: await this.GetGroupImageUrl(groupUin, index!)
originImageUrl: await this.GetGroupImageUrl(groupUin, index!),
};
} else {
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;
@@ -269,7 +346,7 @@ export class PacketOperationContext {
elements: elements,
guildId: '',
isOnlineMsg: false,
msgId: '7467703692092974645', // TODO: no necessary
msgId: '7467703692092974645', // TODO: no necessary
msgRandom: '0',
msgSeq: String(msg.contentHead.sequence ?? 0),
msgTime: String(msg.contentHead.timeStamp ?? 0),

View File

@@ -24,12 +24,15 @@ export class PacketMsgBuilder {
}
return {
responseHead: {
fromUid: '',
fromUin: node.senderUin,
toUid: node.groupId ? undefined : selfUid,
type: 0,
sigMap: 0,
toUin: 0,
fromUid: '',
forward: node.groupId ? undefined : {
friendName: node.senderName,
},
toUid: node.groupId ? undefined : selfUid,
grp: node.groupId ? {
groupUin: node.groupId,
memberName: node.senderName,
@@ -40,16 +43,13 @@ export class PacketMsgBuilder {
type: node.groupId ? 82 : 9,
subType: node.groupId ? undefined : 4,
divSeq: node.groupId ? undefined : 4,
msgId: crypto.randomBytes(4).readUInt32LE(0),
autoReply: 0,
sequence: crypto.randomBytes(4).readUInt32LE(0),
timeStamp: +node.time.toString().substring(0, 10),
field7: BigInt(1),
field8: 0,
field9: 0,
forward: {
field1: 0,
field2: 0,
field3: node.groupId ? 0 : 2,
field3: node.groupId ? 1 : 2,
unknownBase64: avatar,
avatar: avatar
}

View File

@@ -10,6 +10,7 @@ import {
MsgInfo,
NotOnlineImage,
OidbSvcTrpcTcp0XE37_800Response,
PushMsgBody,
QBigFaceExtra,
QSmallFaceExtra,
} from '@/core/packet/transformer/proto';
@@ -29,7 +30,8 @@ import {
SendReplyElement,
SendMultiForwardMsgElement,
SendTextElement,
SendVideoElement
SendVideoElement,
Peer
} from '@/core';
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
@@ -146,41 +148,40 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
}
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
messageId: bigint;
messageSeq: number;
messageClientSeq: number;
time: number;
targetMessageId: bigint;
targetMessageSeq: number;
targetMessageClientSeq: number;
targetUin: number;
targetUid: string;
time: number;
elems: PacketMsg[];
targetElems?: NapProtoEncodeStructType<typeof Elem>[];
targetSourceMsg?: NapProtoEncodeStructType<typeof PushMsgBody>;
targetPeer?: Peer;
constructor(element: SendReplyElement) {
super(element);
this.messageId = BigInt(element.replyElement.replayMsgId ?? 0);
this.messageSeq = +(element.replyElement.replayMsgSeq ?? 0);
this.messageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
this.time = +(element.replyElement.replyMsgTime ?? Math.floor(Date.now() / 1000));
this.targetMessageId = BigInt(element.replyElement.replayMsgId ?? 0);
this.targetMessageSeq = +(element.replyElement.replayMsgSeq ?? 0);
this.targetMessageClientSeq = +(element.replyElement.replyMsgClientSeq ?? 0);
this.targetUin = +(element.replyElement.senderUin ?? 0);
this.targetUid = element.replyElement.senderUidStr ?? '';
this.time = +(element.replyElement.replyMsgTime ?? 0);
this.elems = []; // TODO: in replyElement.sourceMsgTextElems
this.targetPeer = element.replyElement._replyMsgPeer;
}
get isGroupReply(): boolean {
return this.messageClientSeq === 0;
return this.targetMessageClientSeq === 0;
}
override buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
return [{
srcMsg: {
origSeqs: [this.isGroupReply ? this.messageClientSeq : this.messageSeq],
origSeqs: [this.isGroupReply ? this.targetMessageSeq : this.targetMessageClientSeq],
senderUin: BigInt(this.targetUin),
time: this.time,
elems: [], // TODO: in replyElement.sourceMsgTextElems
pbReserve: {
messageId: this.messageId,
},
toUin: BigInt(this.targetUin),
type: 1,
elems: this.targetElems ?? [],
sourceMsg: new NapProtoMsg(PushMsgBody).encode(this.targetSourceMsg ?? {}),
toUin: BigInt(0),
}
}];
}

View 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();

View 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();

View 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();

View File

@@ -13,3 +13,6 @@ export { default as UploadPrivatePtt } from './UploadPrivatePtt';
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
export { default as DownloadImage } from './DownloadImage';
export { default as DownloadGroupImage } from './DownloadGroupImage';
export { default as DownloadVideo } from './DownloadVideo';
export { default as DownloadGroupVideo } from './DownloadGroupVideo';
export { default as DownloadPtt } from './DownloadPtt';

View 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();

View 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();

View File

@@ -1,2 +1,4 @@
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';

View File

@@ -13,13 +13,15 @@ import {
export const ContentHead = {
type: ProtoField(1, ScalarType.UINT32),
subType: ProtoField(2, ScalarType.UINT32, true),
divSeq: ProtoField(3, ScalarType.UINT32, true),
msgId: ProtoField(4, ScalarType.UINT32, true),
c2cCmd: ProtoField(3, ScalarType.UINT32, true),
ranDom: ProtoField(4, ScalarType.UINT32, true),
sequence: ProtoField(5, ScalarType.UINT32, true),
timeStamp: ProtoField(6, ScalarType.UINT32, true),
field7: ProtoField(7, ScalarType.UINT64, true),
field8: ProtoField(8, ScalarType.UINT32, true),
field9: ProtoField(9, ScalarType.UINT32, true),
pkgNum: ProtoField(7, ScalarType.UINT64, true),
pkgIndex: ProtoField(8, 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),
forward: ProtoField(15, () => ForwardHead, true),
};

View 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),
};

View File

@@ -106,15 +106,15 @@ export interface NodeIKernelBuddyService {
getAddMeSetting(): unknown;
getDoubtBuddyReq(): unknown;
getDoubtBuddyReq(reqId: string, num: number,uk:string): Promise<GeneralCallResult>;
getDoubtBuddyUnreadNum(): number;
approvalDoubtBuddyReq(uid: number, isAgree: boolean): void;
approvalDoubtBuddyReq(uid: string, str1: string, str2: string): void;
delDoubtBuddyReq(uid: number): void;
delAllDoubtBuddyReq(): void;
delAllDoubtBuddyReq(): Promise<GeneralCallResult>;
reportDoubtBuddyReqUnread(): void;

View File

@@ -249,7 +249,7 @@ export interface NodeIKernelGroupService {
reqToJoinGroup(groupCode: string, arg: unknown): void;
setGroupShutUp(groupCode: string, shutUp: boolean): void;
setGroupShutUp(groupCode: string, shutUp: boolean): Promise<GeneralCallResult>;
getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult>;

View File

@@ -148,10 +148,11 @@ export interface NodeIKernelMsgService {
msgList: RawMessage[]
}>;
//@deprecated
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
// getMsgService/getMsgs { chatType: 2, peerUid: '975206796', privilegeFlag: 336068800 } 0 20 true
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 & {
msgList: RawMessage[]
}>;
@@ -425,7 +426,20 @@ export interface NodeIKernelMsgService {
switchToOfflineGetRichMediaElement(...args: unknown[]): unknown;
downloadRichMedia(...args: unknown[]): unknown;
downloadRichMedia(args: {
fileModelId: string,
downSourceType: number,
triggerType: number,
msgId: string,
chatType: number,
peerUid: string,
elementId: string,
thumbSize: number,
downloadType: number,
filePath: string
} & {
downloadSourceType: number, //33800左右一下的老版本 新版34606已经完全上面格式
}): unknown;
getFirstUnreadMsgSeq(args: {
peerUid: string

View File

@@ -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'>;
@@ -213,6 +224,9 @@ export interface ReplyElement {
senderUidStr?: string;
replyMsgTime?: 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 {

View File

@@ -403,7 +403,7 @@ export interface NTGroupGrayMember {
}
/**
* 群灰色提示邀请者和被邀请者接口
*
*
* */
export interface NTGroupGrayInviterAndInvite {
invited: NTGroupGrayMember;
@@ -477,7 +477,7 @@ export enum SendStatusType {
export interface RawMessage {
parentMsgPeer: Peer; // 父消息的Peer
parentMsgIdList: string[];// 父消息 ID 列表
id?: number;// 扩展字段,与 Ob11 msg ID 有关
id?: string;// 扩展字段,与 Ob11 msg ID 有关
guildId: string;// 频道ID
msgRandom: string;// 消息ID相关
msgId: string;// 雪花ID
@@ -501,13 +501,15 @@ export interface RawMessage {
elements: MessageElement[];// 消息元素
sourceType: MsgSourceType;// 消息来源类型
isOnlineMsg: boolean;// 是否为在线消息
clientSeq?: string;
}
/**
* 查询消息参数接口
*/
export interface QueryMsgsParams {
chatInfo: Peer;
chatInfo: Peer & { privilegeFlag?: number };
//searchFields: number;
filterMsgType: Array<{ type: NTMsgType, subType: Array<number> }>;
filterSendersUid: string[];
filterMsgFromTime: string;
@@ -565,4 +567,4 @@ export enum FaceType {
AniSticke = 3, // 动画贴纸
Lottie = 4,// 新格式表情
Poke = 5 // 可变Poke
}
}

View File

@@ -132,18 +132,26 @@ export enum BuddyReqType {
KMEINITIATORWAITPEERCONFIRM = 13
}
// 其中 ? 代表新版本参数
export interface FriendRequest {
isBuddy?: boolean;
isInitiator?: boolean;
isDecide: boolean;
friendUid: string;
reqType: BuddyReqType,
reqTime: string; // 时间戳 秒
flag?: number; // 0
preGroupingId?: number; // 0
commFriendNum?: number; // 共同好友数
extWords: string; // 申请人填写的验证消息
isUnread: boolean;
isDoubt?: boolean; // 是否是可疑的好友请求
nameMore?: string;
friendNick: string;
sourceId: number;
groupCode: string
groupCode: string;
isBuddy?: boolean;
isAgreed?: boolean;
relation?: number;
}
export interface FriendRequestNotify {

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

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { MessageUnique } from '@/common/message-unique';
import { type NTQQMsgApi } from '@/core/apis';
const SchemaData = Type.Object({
message_id: Type.Union([Type.Number(), Type.String()]),
message_id: Type.String(),
emojiId: Type.Union([Type.Number(), Type.String()]),
emojiType: Type.Union([Type.Number(), Type.String()]),
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;
async _handle(payload: Payload) {
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
const msgIdPeer = MessageUnique.getInnerData(payload.message_id);
if (!msgIdPeer) throw new Error('消息不存在');
const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
if (!msg) throw new Error('消息不存在');

View File

@@ -4,7 +4,7 @@ import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
import { Type, Static } from '@sinclair/typebox';
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 }),
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ interface Response {
}
const SchemaData = Type.Object({
user_id: Type.String(),
message_seq: Type.Optional(Type.String()),
message_id: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false })
});
@@ -24,24 +24,24 @@ export default class GetFriendMsgHistory extends OneBotAction<Payload, Response>
override actionName = ActionName.GetFriendMsgHistory;
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());
if (!uid) throw new Error(`记录${payload.user_id}不存在`);
const friend = await this.core.apis.FriendApi.isBuddy(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 startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
const msgList = hasMessageSeq ?
const hasMessageId = !payload.message_id ? !!payload.message_id : !(payload.message_id?.toString() === '' || payload.message_id?.toString() === '0');
const startMsgId = hasMessageId ? (MessageUnique.getInnerData(payload.message_id!)?.MsgId ?? payload.message_id!.toString()) : '0';
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;
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 => {
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(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,15 +4,13 @@ import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer } from '@/core/types';
import { MessageUnique } from '@/common/message-unique';
import { Static, Type } from '@sinclair/typebox';
import { NetworkAdapterConfig } from '@/onebot/config/config';
interface Response {
messages: OB11Message[];
}
const SchemaData = Type.Object({
group_id: Type.String(),
message_seq: Type.Optional(Type.String()),
message_id: Type.Optional(Type.String()),
count: Type.Number({ default: 20 }),
reverseOrder: Type.Boolean({ default: false })
});
@@ -25,21 +23,21 @@ export default class GoCQHTTPGetGroupMsgHistory extends OneBotAction<Payload, Re
override actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
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 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 ?
(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 => {
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(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

@@ -6,7 +6,7 @@ import { OB11Construct } from '@/onebot/helper/data';
import { Static, Type } from '@sinclair/typebox';
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 }),
});

View File

@@ -6,7 +6,7 @@ import { calcQQLevel } from '@/common/helper';
import { Static, Type } from '@sinclair/typebox';
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 }),
});
@@ -28,7 +28,7 @@ export default class GoCQHTTPGetStrangerInfo extends OneBotAction<Payload, OB11U
...extendData.detail.simpleInfo.baseInfo,
...extendData.detail.simpleInfo.relationFlags ?? {},
...extendData.detail.simpleInfo.status ?? {},
user_id: parseInt(extendData.detail.uin) ?? 0,
user_id: extendData.detail.uin ?? 0,
uid: info.uid ?? uid,
nickname: extendData.detail.simpleInfo.coreInfo.nick ?? '',
age: extendData.detail.simpleInfo.baseInfo.age ?? info.age,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ import { SendMessageContext } from '@/onebot/api';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]),
group_id: Type.String(),
file: Type.String(),
name: Type.String(),
folder: Type.Optional(Type.String()),
@@ -38,6 +38,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction<Payload, null>
deleteAfterSentFiles: []
};
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
msgContext.deleteAfterSentFiles.push(downloadResult.path);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], msgContext.deleteAfterSentFiles);
return null;
}

View File

@@ -8,7 +8,7 @@ import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()]),
user_id: Type.String(),
file: Type.String(),
name: Type.String(),
});
@@ -23,7 +23,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
if (payload.user_id) {
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
if (!peerUid) {
throw new Error( `私聊${payload.user_id}不存在`);
throw new Error(`私聊${payload.user_id}不存在`);
}
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
@@ -48,6 +48,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
deleteAfterSentFiles: []
};
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
msgContext.deleteAfterSentFiles.push(downloadResult.path);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], msgContext.deleteAfterSentFiles);
return null;
}

View File

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

View File

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

View File

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

View File

@@ -4,10 +4,8 @@ import { ActionName } from '@/onebot/action/router';
import { MessageUnique } from '@/common/message-unique';
import crypto from 'crypto';
import { Static, Type } from '@sinclair/typebox';
import { NetworkAdapterConfig } from '@/onebot/config/config';
const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]),
group_id: Type.String(),
});
type Payload = Static<typeof SchemaData>;
@@ -22,12 +20,12 @@ export class GetGroupEssence extends OneBotAction<Payload, unknown> {
return undefined;
}
return {
id: MessageUnique.createUniqueMsgId(peer, replyMsgList.msgId),
id: MessageUnique.getOutputData(peer, replyMsgList.msgId, replyMsgList.msgSeq),
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);
if (!msglist) {
throw new Error('获取失败');
@@ -48,7 +46,7 @@ export class GetGroupEssence extends OneBotAction<Payload, unknown> {
operator_nick: msg.add_digest_nick,
message_id: message_id,
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({

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import { Static, Type } from '@sinclair/typebox';
import { GroupMember } from '@/core';
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()]))
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
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()])),
});

View File

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

View File

@@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
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()])),
});
@@ -15,7 +15,10 @@ export default class SetGroupWholeBan extends OneBotAction<Payload, null> {
async _handle(payload: Payload): Promise<null> {
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;
}
}

View File

@@ -30,7 +30,6 @@ import SetGroupAdmin from './group/SetGroupAdmin';
import SetGroupCard from './group/SetGroupCard';
import GetImage from './file/GetImage';
import GetRecord from './file/GetRecord';
import { GoCQHTTPMarkMsgAsRead, MarkAllMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead';
import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile';
import SetQQAvatar from '@/onebot/action/extends/SetQQAvatar';
import GoCQHTTPDownloadFile from './go-cqhttp/DownloadFile';
@@ -48,7 +47,6 @@ import { ForwardFriendSingleMsg, ForwardGroupSingleMsg } from '@/onebot/action/m
import { GetFriendWithCategory } from './extends/GetFriendWithCategory';
import { SendGroupNotice } from './go-cqhttp/SendGroupNotice';
import { GetGroupHonorInfo } from './go-cqhttp/GetGroupHonorInfo';
import { GoCQHTTPHandleQuickAction } from './go-cqhttp/QuickAction';
import { GetGroupIgnoredNotifies } from './group/GetGroupIgnoredNotifies';
import { GetOnlineClient } from './go-cqhttp/GetOnlineClient';
import { IOCRImage, OCRImage } from './extends/OCRImage';
@@ -115,10 +113,16 @@ import { RenameGroupFile } from './extends/RenameGroupFile';
import { GetRkeyServer } from './packet/GetRkeyServer';
import { GetRkeyEx } from './packet/GetRkeyEx';
import { CleanCache } from './system/CleanCache';
import SetFriendRemark from './user/SetFriendRemark';
import { SetDoubtFriendsAddRequest } from './new/SetDoubtFriendsAddRequest';
import { GetDoubtFriendsAddRequest } from './new/GetDoubtFriendsAddRequest';
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
const actionHandlers = [
new SetDoubtFriendsAddRequest(obContext, core),
new GetDoubtFriendsAddRequest(obContext, core),
new SetFriendRemark(obContext, core),
new GetRkeyEx(obContext, core),
new GetRkeyServer(obContext, core),
new SetGroupRemark(obContext, core),
@@ -132,8 +136,6 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new SetLongNick(obContext, core),
new ForwardFriendSingleMsg(obContext, core),
new ForwardGroupSingleMsg(obContext, core),
new MarkGroupMsgAsRead(obContext, core),
new MarkPrivateMsgAsRead(obContext, core),
new SetQQAvatar(obContext, core),
new TranslateEnWordToZn(obContext, core),
new GetGroupRootFiles(obContext, core),
@@ -193,17 +195,14 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GoCQHTTPGetStrangerInfo(obContext, core),
new GoCQHTTPDownloadFile(obContext, core),
new GetGuildList(obContext, core),
new GoCQHTTPMarkMsgAsRead(obContext, core),
new GoCQHTTPUploadGroupFile(obContext, core),
new GoCQHTTPGetGroupMsgHistory(obContext, core),
new GoCQHTTPGetForwardMsgAction(obContext, core),
new GetFriendMsgHistory(obContext, core),
new GoCQHTTPHandleQuickAction(obContext, core),
new GetGroupIgnoredNotifies(obContext, core),
new DelEssenceMsg(obContext, core),
new SetEssenceMsg(obContext, core),
new GetRecentContact(obContext, core),
new MarkAllMsgAsRead(obContext, core),
new GetProfileLike(obContext, core),
new SetGroupPortrait(obContext, core),
new FetchCustomFace(obContext, core),

View File

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

View File

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

View File

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

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