Compare commits

...

91 Commits

Author SHA1 Message Date
手瓜一十雪
6024cabb69 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-25 22:36:53 +08:00
手瓜一十雪
08446e648e release: 2.2.8 2024-08-25 22:36:42 +08:00
Alen
14af7a3572 Merge pull request #302 from cnxysoft/upmain
fix: 多个问题
2024-08-25 22:23:58 +08:00
Alen
cdc4275f81 fix: 多个问题
修复group_increase事件上报
修复启动时加载群员信息失败
修复文件发送失败
2024-08-25 22:23:06 +08:00
手瓜一十雪
a9ade98315 style: remove unless 2024-08-25 22:12:38 +08:00
手瓜一十雪
f3ae6fa70f style: fix 2024-08-25 22:09:30 +08:00
手瓜一十雪
8f465e376e style: type 2024-08-25 21:59:07 +08:00
手瓜一十雪
adc366a959 style: 清理不规范代码 2024-08-25 21:54:20 +08:00
手瓜一十雪
b176fa66d4 style: 样式处理 2024-08-25 21:47:55 +08:00
手瓜一十雪
7b7609a068 style: 标准化样式 2024-08-25 21:37:36 +08:00
手瓜一十雪
136e27d655 fix: 消息组合 2024-08-25 19:38:19 +08:00
手瓜一十雪
e3ca5df713 Delete .github/workflows/codacy.yml 2024-08-25 12:52:31 +08:00
手瓜一十雪
bda32f3e8f chore:update 2024-08-25 12:08:36 +08:00
手瓜一十雪
a7c6e45a92 chore: codacy 2024-08-25 12:02:03 +08:00
手瓜一十雪
7c20ca9b64 style:lint 2024-08-25 11:45:50 +08:00
手瓜一十雪
a201461eff chore: lint 2024-08-25 11:18:11 +08:00
Seijo Cecilia
e5d9df37c5 Merge remote-tracking branch 'origin/main' 2024-08-25 10:14:42 +08:00
Seijo Cecilia
106fbaf086 refactor: make parseMessage an instance method 2024-08-25 10:14:11 +08:00
Alen
a0024c98d5 release: 2.2.7 2024-08-25 09:59:07 +08:00
Alen
684a702638 Merge pull request #300 from cnxysoft/upmain
fix: 多处修复
2024-08-25 09:47:07 +08:00
Alen
aec4a009d1 fix: 撤回重复上报 2024-08-25 09:44:40 +08:00
Alen
822af575c9 fix: 群相关
group_admin事件上报
群成员信息/群列表缓存
ProfileService新增事件
2024-08-25 02:08:14 +08:00
Alen
485efa7d44 Merge branch 'main' into upmain 2024-08-25 01:25:12 +08:00
手瓜一十雪
3d09d45423 build: 2.2.7
DelGroupNotice
2024-08-24 23:50:05 +08:00
手瓜一十雪
4c69c6d9fd fix: v2.2.6 2024-08-24 23:24:24 +08:00
手瓜一十雪
920a41acef build: 2.2.6-test 2024-08-24 23:22:32 +08:00
Alen
0cf13a284c Merge branch 'main' into upmain 2024-08-24 22:26:56 +08:00
手瓜一十雪
a89cdef436 chore: kickMemberV2Inner 2024-08-24 22:23:44 +08:00
手瓜一十雪
881d88f4ad feat: quitGroupV2 2024-08-24 22:12:14 +08:00
Alen
a72c96f56d Merge branch 'main' into upmain 2024-08-24 21:54:43 +08:00
手瓜一十雪
bc8235b209 fix: 移除无用代码 2024-08-24 21:54:11 +08:00
手瓜一十雪
0087495749 fix: 类型推断 2024-08-24 21:50:29 +08:00
手瓜一十雪
9560afd4a7 fix: 一处错误推断 2024-08-24 21:47:39 +08:00
手瓜一十雪
56ec8559a0 fix: type 2024-08-24 21:42:49 +08:00
手瓜一十雪
99ca79ac7d chore: 去掉无用注释 2024-08-24 12:07:40 +08:00
手瓜一十雪
24564f4c74 release: 2.2.5 2024-08-24 12:05:07 +08:00
手瓜一十雪
212c802a1e fix: BuddyReq 2024-08-24 11:52:50 +08:00
手瓜一十雪
984b5d6c40 fix: 好友申请重复推送 2024-08-24 11:26:05 +08:00
手瓜一十雪
0e3a4191a9 Merge pull request #298 from shengwang52005/readme
docs: 增强胡言乱语水平
2024-08-24 01:03:21 +08:00
Miaowing
570a34bca5 docs: 增强胡言乱语水平
增强胡言乱语水平
2024-08-24 00:16:16 +08:00
手瓜一十雪
c9a0c29286 build: 2.2.0 2024-08-23 20:46:45 +08:00
手瓜一十雪
b5f804ec22 build: CQ码回滚 提高兼容 2024-08-23 20:46:23 +08:00
手瓜一十雪
dadbb83271 docs: 提高胡言乱语水平 2024-08-23 12:54:04 +08:00
手瓜一十雪
848aacdbbf releas: 2.2.4 2024-08-23 11:34:26 +08:00
手瓜一十雪
da3665a167 release: 2.2.0
release: 2.2.1

release: 2.2.2

chore

chore: 扩大范围

release: 2.2.3
2024-08-23 11:33:22 +08:00
手瓜一十雪
dcf0a06217 chore: 扩大范围 2024-08-23 11:20:29 +08:00
手瓜一十雪
3b5e6553cd chore 2024-08-23 11:04:47 +08:00
手瓜一十雪
509390af20 release: 2.2.2 2024-08-23 11:01:24 +08:00
手瓜一十雪
9ad511a9c0 release: 2.2.1 2024-08-23 10:57:13 +08:00
手瓜一十雪
89c102513d release: 2.2.0 2024-08-23 10:55:19 +08:00
手瓜一十雪
5e65ae76ad chore: 移除无用代码 2024-08-22 15:58:23 +08:00
手瓜一十雪
b6c364cd78 fix: 误操作 2024-08-22 15:56:01 +08:00
手瓜一十雪
e086b8707f refactor: chattype 2024-08-22 15:53:27 +08:00
手瓜一十雪
90dddd10a9 chore: 移除类型 2024-08-22 15:42:45 +08:00
手瓜一十雪
2dd0907565 fix: type 2024-08-22 15:42:07 +08:00
手瓜一十雪
f31b0d0c71 chore: 进一步拉高版本 2024-08-22 15:40:10 +08:00
手瓜一十雪
a0825b75f7 chore: parseMsg 重构 2024-08-22 15:28:54 +08:00
手瓜一十雪
a3bd4c0f73 chore: 推动重构 2024-08-22 15:06:49 +08:00
手瓜一十雪
a3e8c9b28a chore: 重构 2024-08-22 14:58:05 +08:00
手瓜一十雪
d7fb850b4a chore: 丢弃空消息 2024-08-22 14:35:48 +08:00
手瓜一十雪
d084778a6e chore: re 2024-08-22 14:34:09 +08:00
手瓜一十雪
8ca30de760 chore: 兼容性提高 2024-08-22 14:26:49 +08:00
手瓜一十雪
8a10b81bd9 chore: fix 2024-08-22 14:15:29 +08:00
手瓜一十雪
4a93c4e584 chore: 结构性调整 2024-08-22 14:13:52 +08:00
手瓜一十雪
50177cd6bd chore: style&&lint 2024-08-22 14:12:24 +08:00
手瓜一十雪
71a2e52739 chore: 解耦 2024-08-22 14:11:20 +08:00
手瓜一十雪
4fac6d5aa3 chore: 解耦 2024-08-22 14:05:01 +08:00
手瓜一十雪
37d061b602 chore: 进一步解耦 2024-08-22 13:53:07 +08:00
手瓜一十雪
40193e4edc chore: 解耦 2024-08-22 13:46:47 +08:00
手瓜一十雪
b4e9d61871 chore: 解耦代码 2024-08-22 13:39:53 +08:00
Wesley F. Young
d44b589e55 docs: update version range 2024-08-21 09:44:11 +08:00
手瓜一十雪
68216415b6 release: 2.1.0 2024-08-21 08:16:59 +08:00
手瓜一十雪
ba53da18d1 release: 2.1.0 2024-08-21 08:13:53 +08:00
手瓜一十雪
9b76fa3582 chore: LLNC Deprecated 2024-08-21 08:12:52 +08:00
手瓜一十雪
13d8d10a7f Merge pull request #289 from NapNeko/extend
Support: 9.9.15-27254
2024-08-21 08:09:37 +08:00
手瓜一十雪
5c6c1bb09d Merge branch 'main' into extend 2024-08-20 20:36:53 +08:00
手瓜一十雪
12105d96ea release: v2.0.37 2024-08-20 20:33:06 +08:00
手瓜一十雪
4054756035 Merge branch 'main' into extend 2024-08-20 20:27:22 +08:00
手瓜一十雪
16769c7838 fix: 提高兼容性 2024-08-20 20:26:49 +08:00
手瓜一十雪
cd076c5959 fix 2024-08-20 20:22:44 +08:00
手瓜一十雪
f52e1aa131 Merge branch 'main' into extend 2024-08-20 20:07:02 +08:00
手瓜一十雪
cd496a22bf chore: 调整appid 2024-08-20 17:47:14 +08:00
手瓜一十雪
0200343780 chore: 调整appid 2024-08-20 17:12:07 +08:00
手瓜一十雪
47fb629d26 Merge branch 'main' into extend 2024-08-20 16:59:17 +08:00
手瓜一十雪
5b75e753a7 Util 2024-08-19 21:38:31 +08:00
手瓜一十雪
326e9b86ce Merge branch 'main' into extend 2024-08-19 20:44:49 +08:00
手瓜一十雪
e430cc54f2 Merge branch 'main' into extend 2024-08-19 19:03:31 +08:00
手瓜一十雪
fd26a9c698 fix 2024-08-19 18:53:47 +08:00
手瓜一十雪
e79b608f77 support: 27206 2024-08-19 18:47:12 +08:00
手瓜一十雪
03098ee024 chore: util 2024-08-17 15:21:47 +08:00
手瓜一十雪
a2bfdd003c fix: getNTUserDataInfoConfig 2024-08-17 15:18:33 +08:00
84 changed files with 1612 additions and 1542 deletions

View File

@@ -95,7 +95,13 @@ jobs:
steps: steps:
- name: Download All Artifact - name: Download All Artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
# - name: Compress subdirectories
# run: |
# cd ./NapCat.Shell/
# zip -q -r NapCat.Shell.zip *
# cd ..
# rm ./NapCat.Shell.zip -rf
# mv ./NapCat.Shell/NapCat.Shell.zip ./
- name: Compress subdirectories - name: Compress subdirectories
run: | run: |
cd ./NapCat.Shell/ cd ./NapCat.Shell/

View File

@@ -3,38 +3,33 @@
</div> </div>
--- ---
## To Be Continued ## 欢迎回来
当前版本请使用内核构建版本(版本号最后的五位数)为 26702 至 26909 的 PC NTQQ 运行 NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
高版本QQ NapCat已完成兼容 暂时不发布 直至版本2.0.x结束。 ## 猫猫技能
## 项目介绍
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
## 项目优势
- [x] **多种启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动 - [x] **多种启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行 - [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
- [x] **超多接口**在实现大部分Onebot接口上扩展了一套私有API - [x] **超多接口**在实现大部分Onebot接口上扩展了一套私有API
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷 - [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
## 如何使用 ## 使用猫猫
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本 可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。 **首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
## 相关链接 ## 回家旅途
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS) [QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl) [Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
## 鸣谢名单 ## 猫猫朋友
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础 感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
--- ---
## 使用许可 ## 约法三章
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 [core](./src/core) 部分代码开发。** 任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper'; import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
interface Internal_MapKey { interface InternalMapKey {
timeout: number; timeout: number;
createtime: number; createtime: number;
func: (...arg: any[]) => any; func: (...arg: any[]) => any;
@@ -11,36 +11,31 @@ interface Internal_MapKey {
export type ListenerClassBase = Record<string, string>; export type ListenerClassBase = Record<string, string>;
export interface ListenerIBase { export interface ListenerIBase {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: any): ListenerClassBase; new(listener: any): ListenerClassBase;
[key: string]: any;
} }
export class LegacyNTEventWrapper { export class LegacyNTEventWrapper {
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例 private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} private EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor( constructor(
listenerMapping: Record<string, ListenerIBase>, wrapperSession: NodeIQQNTWrapperSession
wrapperSession: NodeIQQNTWrapperSession,
) { ) {
this.listenerMapping = listenerMapping;
this.WrapperSession = wrapperSession; this.WrapperSession = wrapperSession;
} }
createProxyDispatch(ListenerMainName: string) { createProxyDispatch(ListenerMainName: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias const dispatcherListenerFunc = this.dispatcherListener.bind(this);
const current = this;
return new Proxy( return new Proxy(
{}, {},
{ {
get(target: any, prop: any, receiver: any) { get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') { if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod // 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: any[]) => { return (...args: any[]) => {
current.dispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then(); dispatcherListenerFunc(ListenerMainName, prop, ...args).then();
}; };
} }
// 如果方法存在,正常返回 // 如果方法存在,正常返回
@@ -58,8 +53,6 @@ export class LegacyNTEventWrapper {
if (eventNameArr.length > 1) { if (eventNameArr.length > 1) {
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', ''); const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
const eventName = eventNameArr[1]; const eventName = eventNameArr[1];
//getNodeIKernelGroupListener,GroupService
//console.log('2', eventName);
const services = (this.WrapperSession as unknown as eventType)[serviceName](); const services = (this.WrapperSession as unknown as eventType)[serviceName]();
let event = services[eventName]; let event = services[eventName];
//重新绑定this //重新绑定this
@@ -72,32 +65,29 @@ export class LegacyNTEventWrapper {
} }
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T { createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
const ListenerType = this.listenerMapping![listenerMainName]; const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
let Listener = this.listenerManager.get(listenerMainName + uniqueCode); if (!existListener) {
if (!Listener && ListenerType) { const Listener = this.createProxyDispatch(listenerMainName);
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1]; const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener'; const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service); const addfunc = this.createEventFunction<(listener: T) => number>(Service);
addfunc!(Listener as T); addfunc!(Listener as T);
//console.log(addfunc!(Listener as T));
this.listenerManager.set(listenerMainName + uniqueCode, Listener); this.listenerManager.set(listenerMainName + uniqueCode, Listener);
return Listener as T;
} }
return Listener as T; return existListener as T;
} }
//统一回调清理事件 //统一回调清理事件
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) { async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
this.EventTask.get(ListenerMainName) this.EventTask.get(ListenerMainName)
?.get(ListenerSubName) ?.get(ListenerSubName)
?.forEach((task, uuid) => { ?.forEach((task, uuid) => {
//console.log(task.func, uuid, task.createtime, task.timeout);
if (task.createtime + task.timeout < Date.now()) { if (task.createtime + task.timeout < Date.now()) {
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid); this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
return; return;
} }
if (task.checker && task.checker(...args)) { if (task?.checker?.(...args)) {
task.func(...args); task.func(...args);
} }
}); });
@@ -166,7 +156,88 @@ export class LegacyNTEventWrapper {
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
}); });
} }
async CallNormalEventV2<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
>(
EventName = '',
ListenerName = '',
waitTimes = 1,
timeout: number = 3000,
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
checkerListener: (...args: Parameters<ListenerType>) => boolean = () => true,
...args: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
if (complete == 0) {
reject(
new Error(
'Timeout: NTEvent EventName:' +
EventName +
' ListenerName:' +
ListenerName +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const ListenerNameList = ListenerName.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checkerListener,
func: (...args: any[]) => {
complete++;
retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
databack();
}
},
};
if (!this.EventTask.get(ListenerMainName)) {
this.EventTask.set(ListenerMainName, new Map());
}
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName);
retEvent = await EventFunc!(...(args as any[]));
if (!checkerEvent(retEvent)) {
clearTimeout(Timeouter);
reject(
new Error(
'EventChecker Failed: NTEvent EventName:' +
EventName +
' ListenerName:' +
ListenerName +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
}
},
);
}
async CallNormalEvent< async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>, EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void ListenerType extends (...args: any[]) => void
@@ -236,28 +307,3 @@ export class LegacyNTEventWrapper {
); );
} }
} }
// 示例代码 快速创建事件
// let NTEvent = new NTEventWrapper();
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest');
// if (TestEvent) {
// TestEvent(true);
// }
// 示例代码 快速创建监听Listener类
// let NTEvent = new NTEventWrapper();
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
// 调用接口
//let NTEvent = new NTEventWrapper();
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true);
// 注册监听 解除监听
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb);
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core');
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode);
// GetTest('test');
// always模式
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) });

View File

@@ -124,14 +124,7 @@ export class NTEventChannel extends EventEmitter {
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) { EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => { return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
const EventFunc = this.createEventFunction<EventType>(EventName); const EventFunc = this.createEventFunction<EventType>(EventName);
let complete = false;
const Timeouter = setTimeout(() => {
if (!complete) {
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
}
}, timeout);
const retData = await EventFunc!(...args); const retData = await EventFunc!(...args);
complete = true;
resolve(retData); resolve(retData);
}); });
} }

View File

@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import fs from 'fs'; import fs from 'fs';
export const napcat_version = '2.0.36'; export const napcat_version = '2.2.8';
export class NapCatPathWrapper { export class NapCatPathWrapper {
binaryPath: string; binaryPath: string;

View File

@@ -53,7 +53,7 @@ export abstract class ConfigBase<T> {
} }
save(newConfigData: T = this.configData as T) { save(newConfigData: T = this.configData) {
const logger = this.coreContext.context.logger; const logger = this.coreContext.context.logger;
const selfInfo = this.coreContext.selfInfo; const selfInfo = this.coreContext.selfInfo;
this.configData = newConfigData; this.configData = newConfigData;

View File

@@ -46,7 +46,7 @@ export class QQBasicInfoWrapper {
} }
requireMinNTQQBuild(buildStr: string) { requireMinNTQQBuild(buildStr: string) {
const currentBuild = parseInt(this.getQQBuildStr() || '0'); const currentBuild = +(this.getQQBuildStr() ?? '0');
if (currentBuild == 0) throw new Error('QQBuildStr获取失败'); if (currentBuild == 0) throw new Error('QQBuildStr获取失败');
return currentBuild >= parseInt(buildStr); return currentBuild >= parseInt(buildStr);
} }
@@ -74,8 +74,6 @@ export class QQBasicInfoWrapper {
this.context.logger.log( this.context.logger.log(
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`, `[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
); );
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() }; return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
} }
} }
export let QQBasicInfo: QQBasicInfoWrapper | undefined;

View File

@@ -4,6 +4,7 @@ import crypto, { randomUUID } from 'crypto';
import util from 'util'; import util from 'util';
import path from 'node:path'; import path from 'node:path';
import * as fileType from 'file-type'; import * as fileType from 'file-type';
import { solveAsyncProblem, solveProblem } from './helper';
export function isGIF(path: string) { export function isGIF(path: string) {
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
@@ -185,25 +186,28 @@ export enum FileUriType {
} }
export async function checkUriType(Uri: string) { export async function checkUriType(Uri: string) {
//先判断是否是本地文件
try { const LocalFileRet = await solveProblem((uri: string) => {
if (fs.existsSync(Uri)) return { Uri: Uri, Type: FileUriType.Local }; if (fs.existsSync(uri)) {
} catch (error) { return { Uri: uri, Type: FileUriType.Local };
} }
try { return undefined;
}, Uri);
if (LocalFileRet) return LocalFileRet;
const OtherFileRet = await solveProblem((uri: string) => {
//再判断是否是Http //再判断是否是Http
if (Uri.startsWith('http://') || Uri.startsWith('https://')) { if (uri.startsWith('http://') || uri.startsWith('https://')) {
return { Uri: Uri, Type: FileUriType.Remote }; return { Uri: uri, Type: FileUriType.Remote };
} }
//再判断是否是Base64 //再判断是否是Base64
if (Uri.startsWith('base64://')) { if (uri.startsWith('base64://')) {
return { Uri: Uri, Type: FileUriType.Base64 }; return { Uri: uri, Type: FileUriType.Base64 };
} }
if (Uri.startsWith('file://')) { if (uri.startsWith('file://')) {
let pathname: string;
let filePath: string; let filePath: string;
// await fs.copyFile(url.pathname, filePath); // await fs.copyFile(url.pathname, filePath);
pathname = decodeURIComponent(new URL(Uri).pathname); const pathname = decodeURIComponent(new URL(uri).pathname);
if (process.platform === 'win32') { if (process.platform === 'win32') {
filePath = pathname.slice(1); filePath = pathname.slice(1);
} else { } else {
@@ -211,8 +215,9 @@ export async function checkUriType(Uri: string) {
} }
return { Uri: filePath, Type: FileUriType.Local }; return { Uri: filePath, Type: FileUriType.Local };
} }
} catch (error) { }, Uri);
} if (OtherFileRet) return OtherFileRet;
return { Uri: Uri, Type: FileUriType.Unknown }; return { Uri: Uri, Type: FileUriType.Unknown };
} }
@@ -232,14 +237,14 @@ export async function uri2local(dir: string, uri: string, filename: string | und
//接下来都要有文件名 //接下来都要有文件名
if (!filename) filename = randomUUID(); if (!filename) filename = randomUUID();
//解析Http和Https协议 //解析Http和Https协议
if (UriType == FileUriType.Remote) { if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname)); const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) { if (pathInfo.name) {
filename = pathInfo.name; filename = pathInfo.name;
if (pathInfo.ext) { if (pathInfo.ext) {
filename += pathInfo.ext; filename += pathInfo.ext;
} }
} }
filename = filename.replace(/[/\\:*?"<>|]/g, '_'); filename = filename.replace(/[/\\:*?"<>|]/g, '_');
const fileExt = path.extname(HandledUri); const fileExt = path.extname(HandledUri);

View File

@@ -1,12 +1,30 @@
import crypto from 'node:crypto';
import path from 'node:path'; import path from 'node:path';
import fs from 'fs'; import fs from 'fs';
import * as fsPromise from 'node:fs/promises';
import os from 'node:os'; import os from 'node:os';
import { QQLevel } from '@/core'; import { QQLevel } from '@/core';
//下面这个类是用于将uid+msgid合并的类 export async function solveProblem<T extends (...arg: any[]) => any>(func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
return new Promise<ReturnType<T> | undefined>((resolve) => {
try {
const result = func(...args);
resolve(result);
} catch (e) {
resolve(undefined);
}
});
}
export async function solveAsyncProblem<T extends (...args: any[]) => Promise<any>>(func: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>> | undefined> {
return new Promise<Awaited<ReturnType<T>> | undefined>(async (resolve) => {
try {
const result = await func(...args);
resolve(result);
} catch (e) {
resolve(undefined);
}
});
}
//下面这个类是用于将uid+msgid合并的类
export class UUIDConverter { export class UUIDConverter {
static encode(highStr: string, lowStr: string): string { static encode(highStr: string, lowStr: string): string {
const high = BigInt(highStr); const high = BigInt(highStr);
@@ -97,19 +115,19 @@ export function isEqual(obj1: any, obj2: any) {
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType { export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
if (os.platform() === 'linux') { if (os.platform() === 'linux') {
return { return {
baseVersion: '3.2.12-26702', baseVersion: '3.2.12-27254',
curVersion: '3.2.12-26702', curVersion: '3.2.12-27254',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '26702', buildId: '27254',
}; };
} }
return { return {
baseVersion: '9.9.15-26702', baseVersion: '9.9.15-27254',
curVersion: '9.9.15-26702', curVersion: '9.9.15-27254',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '26702', buildId: '27254',
}; };
} }

View File

@@ -2,7 +2,7 @@ import log4js, { Configuration } from 'log4js';
import { truncateString } from '@/common/utils/helper'; import { truncateString } from '@/common/utils/helper';
import path from 'node:path'; import path from 'node:path';
import chalk from 'chalk'; import chalk from 'chalk';
import { AtType, ChatType, ElementType, ElementWrapper, RawMessage, SelfInfo } from '@/core'; import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
export enum LogLevel { export enum LogLevel {
DEBUG = 'debug', DEBUG = 'debug',
@@ -152,11 +152,11 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
const tokens: string[] = []; const tokens: string[] = [];
if (msg.chatType == ChatType.friend) { if (msg.chatType == ChatType.KCHATTYPEC2C) {
tokens.push(`私聊 (${msg.peerUin})`); tokens.push(`私聊 (${msg.peerUin})`);
} else if (msg.chatType == ChatType.group) { } else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
tokens.push(`群聊 (群 ${msg.peerUin}${msg.senderUin})`); tokens.push(`群聊 (群 ${msg.peerUin}${msg.senderUin})`);
} else if (msg.chatType == ChatType.chatDevice) { } else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
tokens.push('移动设备'); tokens.push('移动设备');
} else /* temp */ { } else /* temp */ {
tokens.push(`临时消息 (${msg.peerUin})`); tokens.push(`临时消息 (${msg.peerUin})`);
@@ -164,7 +164,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
// message content // message content
function msgElementToText(element: ElementWrapper) { function msgElementToText(element: MessageElement) {
if (element.textElement) { if (element.textElement) {
if (element.textElement.atType === AtType.notAt) { if (element.textElement.atType === AtType.notAt) {
return element.textElement.content; return element.textElement.content;

View File

@@ -3,7 +3,6 @@ import { LogWrapper } from './log';
export function proxyHandlerOf(logger: LogWrapper) { export function proxyHandlerOf(logger: LogWrapper) {
return { return {
get(target: any, prop: any, receiver: any) { get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') { if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod // 如果方法不存在返回一个函数这个函数调用existentMethod
return (..._args: unknown[]) => { return (..._args: unknown[]) => {

File diff suppressed because one or more lines are too long

View File

@@ -179,7 +179,7 @@ export class NTQQFileApi {
async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) { async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
let GroupData; let GroupData;
let BuddyData; let BuddyData;
if (peer.chatType === ChatType.group) { if (peer.chatType === ChatType.KCHATTYPEGROUP) {
GroupData = GroupData =
[{ [{
groupCode: peer.peerUid, groupCode: peer.peerUid,
@@ -189,7 +189,7 @@ export class NTQQFileApi {
groupName: 'NapCat.Cached', groupName: 'NapCat.Cached',
remark: 'NapCat.Cached', remark: 'NapCat.Cached',
}]; }];
} else if (peer.chatType === ChatType.friend) { } else if (peer.chatType === ChatType.KCHATTYPEC2C) {
BuddyData = [{ BuddyData = [{
category_name: 'NapCat.Cached', category_name: 'NapCat.Cached',
peerUid: peer.peerUid, peerUid: peer.peerUid,

View File

@@ -1,5 +1,5 @@
import { Friend, FriendV2, User } from '@/core/entities'; import { Friend, FriendV2, User } from '@/core/entities';
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core'; import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelBuddyListener, NodeIKernelBuddyService, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
import { LimitedHashTable } from '@/common/utils/MessageUnique'; import { LimitedHashTable } from '@/common/utils/MessageUnique';
export class NTQQFriendApi { export class NTQQFriendApi {
@@ -70,29 +70,19 @@ export class NTQQFriendApi {
async isBuddy(uid: string) { async isBuddy(uid: string) {
return this.context.session.getBuddyService().isBuddy(uid); return this.context.session.getBuddyService().isBuddy(uid);
} }
async clearBuddyReqUnreadCnt() {
/** return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
* @deprecated }
* @param forced async getBuddyReq() {
* @returns const [, ret] = await this.core.eventWrapper.CallNormalEventV2<
*/ NodeIKernelBuddyService['getBuddyReq'],
async getFriends(forced = false): Promise<User[]> { NodeIKernelBuddyListener['onBuddyReqChange']
const [_retData, _BuddyArg] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void> >(
( 'NodeIKernelBuddyService/getBuddyReq',
'NodeIKernelBuddyService/getBuddyList', 'NodeIKernelBuddyListener/onBuddyReqChange',
'NodeIKernelBuddyListener/onBuddyListChange',
1, 1,
5000, 5000);
() => true, return ret;
forced,
);
const friends: User[] = [];
for (const categoryItem of _BuddyArg) {
for (const friend of categoryItem.buddyList) {
friends.push(friend);
}
}
return friends;
} }
async handleFriendRequest(flag: string, accept: boolean) { async handleFriendRequest(flag: string, accept: boolean) {

View File

@@ -7,12 +7,14 @@ import {
GroupNotify, GroupNotify,
GroupRequestOperateTypes, GroupRequestOperateTypes,
InstanceContext, InstanceContext,
KickMemberInfo,
kickMemberV2Req,
MemberExtSourceType, MemberExtSourceType,
NapCatCore, NapCatCore,
NodeIKernelGroupListener, NodeIKernelGroupListener,
NodeIKernelGroupService, NodeIKernelGroupService,
} from '@/core'; } from '@/core';
import { isNumeric, runAllWithTimeout } from '@/common/utils/helper'; import { isNumeric, runAllWithTimeout, sleep } from '@/common/utils/helper';
export class NTQQGroupApi { export class NTQQGroupApi {
context: InstanceContext; context: InstanceContext;
@@ -23,7 +25,9 @@ export class NTQQGroupApi {
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
this.initCache().then().catch(context.logger.logError); sleep(1000).then(() => {
this.initCache().then().catch(context.logger.logError);
});
} }
async initCache() { async initCache() {
this.groups = await this.getGroups(); this.groups = await this.getGroups();
@@ -114,7 +118,7 @@ export class NTQQGroupApi {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', { return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: { chatInfo: {
peerUid: GroupCode, peerUid: GroupCode,
chatType: ChatType.group, chatType: ChatType.KCHATTYPEGROUP,
}, },
filterMsgType: [], filterMsgType: [],
filterSendersUid: uids, filterSendersUid: uids,
@@ -175,7 +179,7 @@ export class NTQQGroupApi {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', { return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: { chatInfo: {
peerUid: GroupCode, peerUid: GroupCode,
chatType: ChatType.group, chatType: ChatType.KCHATTYPEGROUP,
}, },
filterMsgType: [], filterMsgType: [],
filterSendersUid: uids, filterSendersUid: uids,
@@ -219,7 +223,21 @@ export class NTQQGroupApi {
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数 // GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
return this.context.session.getGroupService().addGroupEssence(param); return this.context.session.getGroupService().addGroupEssence(param);
} }
async kickMemberV2Inner(param: kickMemberV2Req) {
return this.context.session.getGroupService().kickMemberV2(param);
}
async deleteGroupBulletin(GroupCode: string, feedId: string) {
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, _Pskey, feedId);
}
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
const param = {
groupCode: GroupCode,
needDeleteLocalMsg: needDeleteLocalMsg
};
//应该是直接返回不需要Listener的 未经测试 需测试再发布
return this.context.session.getGroupService().quitGroupV2(param);
}
async removeGroupEssence(GroupCode: string, msgId: string) { async removeGroupEssence(GroupCode: string, msgId: string) {
// 代码没测过 // 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom // 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -253,19 +271,16 @@ export class NTQQGroupApi {
} }
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) { async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
//type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
type EventType = NodeIKernelGroupService['getMemberInfo']; type EventType = NodeIKernelGroupService['getMemberInfo'];
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void> const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
( (
'NodeIKernelGroupListener/onMemberInfoChange', 'NodeIKernelGroupListener/onMemberInfoChange',
1, 1,
forced ? 5000 : 250, forced ? 5000 : 250,
(params) => { (params) => {
return params === GroupCode; return params === GroupCode;
}, },
); );
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo'); const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
const retData = await EventFunc!(GroupCode, [uid], forced); const retData = await EventFunc!(GroupCode, [uid], forced);
if (retData.result !== 0) { if (retData.result !== 0) {
@@ -274,24 +289,10 @@ export class NTQQGroupApi {
const result = await Listener as unknown; const result = await Listener as unknown;
let member: GroupMember | undefined; let member: GroupMember | undefined;
if (Array.isArray(result) && result?.[2] instanceof Map) { if (Array.isArray(result) && result?.[2] instanceof Map) {
let members = result[2] as Map<string, GroupMember>; const members = result[2] as Map<string, GroupMember>;
member = members.get(uid); member = members.get(uid);
}; }
return member; return member;
// 原本的方法: (no_cache 下效率很高, cache 下效率一致)
// const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
// (
// 'NodeIKernelGroupService/getMemberInfo',
// 'NodeIKernelGroupListener/onMemberInfoChange',
// 1,
// 5000,
// (groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
// return groupCode == GroupCode && members.has(uid);
// },
// GroupCode, [uid], forced,
// );
// return _members.get(uid);
} }
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> { async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
@@ -299,10 +300,10 @@ export class NTQQGroupApi {
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow'); const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
const result = await groupService.getNextMemberList(sceneId!, undefined, num); const result = await groupService.getNextMemberList(sceneId!, undefined, num);
if (result.errCode !== 0) { if (result.errCode !== 0) {
throw ('获取群成员列表出错,' + result.errMsg); throw new Error('获取群成员列表出错,' + result.errMsg);
} }
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values())); this.context.logger.logDebug(`获取群(${groupQQ})成员列表结果:`, `members: ${result.result.infos.size}`); //, Array.from(result.result.infos.values()));
return result.result.infos; return result.result.infos;
/* /*
console.log(sceneId); console.log(sceneId);

View File

@@ -120,18 +120,18 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent< const data = await this.core.eventWrapper.CallNormalEvent<
(GroupCode: string, params: GetFileListParam) => Promise<unknown>, (GroupCode: string, params: GetFileListParam) => Promise<unknown>,
(groupFileListResult: onGroupFileInfoUpdateParamType) => void (groupFileListResult: onGroupFileInfoUpdateParamType) => void
>( >(
'NodeIKernelRichMediaService/getGroupFileList', 'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate', 'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1, 1,
5000, 5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => { (groupFileListResult: onGroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功 //Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true; return true;
}, },
GroupCode, GroupCode,
params, params,
); );
return data[1].item; return data[1].item;
} }
@@ -156,7 +156,7 @@ export class NTQQMsgApi {
peerOpenId: "", peerOpenId: "",
}; };
return this.context.session.getMsgService().prepareTempChat({ return this.context.session.getMsgService().prepareTempChat({
chatType: ChatType.temp, chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: toUserUid, peerUid: toUserUid,
peerNickname: nickname, peerNickname: nickname,
fromGroupCode: GroupCode, fromGroupCode: GroupCode,
@@ -171,7 +171,7 @@ export class NTQQMsgApi {
} }
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) { async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//唉? !我有个想法 //唉? !我有个想法
if (peer.chatType === ChatType.temp && peer.guildId && peer.guildId !== '') { if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') {
const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!); const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
if (member) { if (member) {
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick); await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
@@ -182,24 +182,24 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent< const data = await this.core.eventWrapper.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>, (msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void (msgList: RawMessage[]) => void
>( >(
'NodeIKernelMsgService/sendMsg', 'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate', 'NodeIKernelMsgListener/onMsgInfoListUpdate',
1, 1,
timeout, timeout,
(msgRecords: RawMessage[]) => { (msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) { for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) { if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true; return true;
}
} }
} return false;
return false; },
}, '0',
'0', peer,
peer, msgElements,
msgElements, new Map(),
new Map(), );
);
const retMsg = data[1].find(msgRecord => { const retMsg = data[1].find(msgRecord => {
if (msgRecord.guildId === msgId) { if (msgRecord.guildId === msgId) {
return true; return true;
@@ -227,25 +227,25 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent< const data = await this.core.eventWrapper.CallNormalEvent<
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>, (msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void (msgList: RawMessage[]) => void
>( >(
'NodeIKernelMsgService/multiForwardMsgWithComment', 'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate', 'NodeIKernelMsgListener/onMsgInfoListUpdate',
1, 1,
5000, 5000,
(msgRecords: RawMessage[]) => { (msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) { for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) { if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true; return true;
}
} }
} return false;
return false; },
}, msgInfos,
msgInfos, srcPeer,
srcPeer, destPeer,
destPeer, [],
[], new Map(),
new Map(), );
);
for (const msg of data[1]) { for (const msg of data[1]) {
const arkElement = msg.elements.find(ele => ele.arkElement); const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) { if (!arkElement) {

View File

@@ -3,6 +3,7 @@ import { NodeIKernelProfileListener } from '@/core/listeners';
import { RequestUtil } from '@/common/utils/request'; import { RequestUtil } from '@/common/utils/request';
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services'; import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
import { InstanceContext, NapCatCore } from '..'; import { InstanceContext, NapCatCore } from '..';
import { solveAsyncProblem } from '@/common/utils/helper';
export class NTQQUserApi { export class NTQQUserApi {
context: InstanceContext; context: InstanceContext;
@@ -128,12 +129,9 @@ export class NTQQUserApi {
} }
async getUserDetailInfo(uid: string): Promise<User> { async getUserDetailInfo(uid: string): Promise<User> {
try { const retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB));
let retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB); if (retUser && retUser.uin !== '0') {
if (retUser.uin !== '0') { return retUser;
return retUser;
}
} catch (e) {
} }
this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.'); this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.');
return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER); return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER);

View File

@@ -20,14 +20,13 @@ export class NTQQWebApi {
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) { async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${ const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
new URLSearchParams({ bkn: this.getBknFromCookie(cookieObject),
bkn: this.getBknFromCookie(cookieObject), group_code: groupCode,
group_code: groupCode, msg_seq: msgSeq,
msg_seq: msgSeq, msg_random: msgRandom,
msg_random: msgRandom, target_group_code: targetGroupCode,
target_group_code: targetGroupCode, }).toString()
}).toString()
}`; }`;
try { try {
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
@@ -38,13 +37,12 @@ export class NTQQWebApi {
async getGroupEssenceMsg(GroupCode: string, page_start: string) { async getGroupEssenceMsg(GroupCode: string, page_start: string) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${ const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
new URLSearchParams({ bkn: this.getBknFromCookie(cookieObject),
bkn: this.getBknFromCookie(cookieObject), group_code: GroupCode,
group_code: GroupCode, page_start,
page_start, page_limit: '20',
page_limit: '20', }).toString()
}).toString()
}`; }`;
let ret; let ret;
try { try {
@@ -65,14 +63,13 @@ export class NTQQWebApi {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const retList: Promise<WebApiGroupMemberRet>[] = []; const retList: Promise<WebApiGroupMemberRet>[] = [];
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet> const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${ (`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
new URLSearchParams({ st: '0',
st: '0', end: '40',
end: '40', sort: '1',
sort: '1', gc: GroupCode,
gc: GroupCode, bkn: this.getBknFromCookie(cookieObject),
bkn: this.getBknFromCookie(cookieObject), }).toString()
}).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) { if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
return []; return [];
@@ -86,14 +83,13 @@ export class NTQQWebApi {
//遍历批量请求 //遍历批量请求
for (let i = 2; i <= PageNum; i++) { for (let i = 2; i <= PageNum; i++) {
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet> const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${ (`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
new URLSearchParams({ st: ((i - 1) * 40).toString(),
st: ((i - 1) * 40).toString(), end: (i * 40).toString(),
end: (i * 40).toString(), sort: '1',
sort: '1', gc: GroupCode,
gc: GroupCode, bkn: this.getBknFromCookie(cookieObject),
bkn: this.getBknFromCookie(cookieObject), }).toString()
}).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
retList.push(ret); retList.push(ret);
} }
@@ -127,15 +123,14 @@ export class NTQQWebApi {
let ret: any = undefined; let ret: any = undefined;
try { try {
ret = await RequestUtil.HttpGetJson<any> ret = await RequestUtil.HttpGetJson<any>
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${ (`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({
new URLSearchParams({ bkn: this.getBknFromCookie(cookieObject),
bkn: this.getBknFromCookie(cookieObject), qid: GroupCode,
qid: GroupCode, text: Content,
text: Content, pinned: '0',
pinned: '0', type: '1',
type: '1', settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}', }).toString()
}).toString()
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) }); }`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
return ret; return ret;
} catch (e) { } catch (e) {
@@ -147,15 +142,10 @@ export class NTQQWebApi {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
let ret: WebApiGroupNoticeRet | undefined = undefined; let ret: WebApiGroupNoticeRet | undefined = undefined;
try { try {
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${ const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' +
new URLSearchParams({ this.getBknFromCookie(cookieObject) + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
bkn: this.getBknFromCookie(cookieObject),
qid: GroupCode, ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
type: '1',
start: '0',
num: '1',
}).toString()
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
if (ret?.ec !== 0) { if (ret?.ec !== 0) {
return undefined; return undefined;
} }
@@ -168,11 +158,10 @@ export class NTQQWebApi {
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) { async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => { const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
const url = `https://qun.qq.com/interactive/honorlist?${ const url = `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
new URLSearchParams({ gc: Internal_groupCode,
gc: Internal_groupCode, type: Internal_type.toString(),
type: Internal_type.toString(), }).toString()
}).toString()
}`; }`;
let resJson; let resJson;
try { try {

View File

@@ -44,8 +44,8 @@ export class NapCatCore {
constructor(context: InstanceContext, selfInfo: SelfInfo) { constructor(context: InstanceContext, selfInfo: SelfInfo) {
this.selfInfo = selfInfo; this.selfInfo = selfInfo;
this.context = context; this.context = context;
this.util = new this.context.wrapper.NodeQQNTWrapperUtil(); this.util = this.context.wrapper.NodeQQNTWrapperUtil;
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session); this.eventWrapper = new LegacyNTEventWrapper(context.session);
this.apis = { this.apis = {
FileApi: new NTQQFileApi(this.context, this), FileApi: new NTQQFileApi(this.context, this),
SystemApi: new NTQQSystemApi(this.context, this), SystemApi: new NTQQSystemApi(this.context, this),
@@ -79,7 +79,7 @@ export class NapCatCore {
} }
get dataPath(): string { get dataPath(): string {
let result = this.util.getNTUserDataInfoConfig(); let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!result) { if (!result) {
result = path.resolve(os.homedir(), './.config/QQ'); result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true }); fs.mkdirSync(result, { recursive: true });
@@ -98,7 +98,7 @@ export class NapCatCore {
}; };
//await sleep(2500); //await sleep(2500);
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)), proxiedListenerOf(msgListener, this.context.logger) as any
); );
const profileListener = new ProfileListener(); const profileListener = new ProfileListener();
@@ -113,7 +113,7 @@ export class NapCatCore {
// } // }
}; };
this.context.session.getProfileService().addKernelProfileListener( this.context.session.getProfileService().addKernelProfileListener(
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)), proxiedListenerOf(profileListener, this.context.logger),
); );
// 群相关 // 群相关
@@ -197,12 +197,12 @@ export class NapCatCore {
} }
}; };
this.context.session.getGroupService().addKernelGroupListener( this.context.session.getGroupService().addKernelGroupListener(
new this.context.wrapper.NodeIKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)), proxiedListenerOf(groupListener, this.context.logger) as any
); );
} }
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean { checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
if (memberNew.role !== memberOld?.role) { if (memberNew.role !== memberOld?.role) {
this.context.logger.log(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`); this.context.logger.logDebug(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
return true; return true;
} }
return false; return false;

View File

@@ -1,5 +1,17 @@
import { QQLevel, Sex, User } from './user'; import { QQLevel, Sex, User } from './user';
export interface KickMemberInfo {
optFlag: number,
optOperate: number,
optMemberUid: string,
optBytesMsg: string,
}
export interface kickMemberV2Req{
groupCode: string,
kickFlag: number,
kickList: Array<KickMemberInfo>,
kickListUids: Array<string>,
kickMsg: string
}
export enum GroupListUpdateType { export enum GroupListUpdateType {
REFRESHALL, REFRESHALL,
GETALL, GETALL,

View File

@@ -362,6 +362,7 @@ export interface SendPicElement {
} }
export interface ReplyElement { export interface ReplyElement {
sourceMsgIdInRecords?: string;
replayMsgSeq: string; replayMsgSeq: string;
replayMsgId: string; replayMsgId: string;
senderUin: string; senderUin: string;
@@ -476,7 +477,7 @@ export interface MessageElement {
extBufForUI: string,//"0x", extBufForUI: string,//"0x",
textElement?: TextElement; textElement?: TextElement;
faceElement?: FaceElement, faceElement?: FaceElement,
marketFaceElement?: MarkdownElement, marketFaceElement?: MarketFaceElement,
replyElement?: ReplyElement, replyElement?: ReplyElement,
picElement?: PicElement, picElement?: PicElement,
pttElement?: PttElement, pttElement?: PttElement,
@@ -510,17 +511,8 @@ export enum AtType {
atAll = 1, atAll = 1,
atUser = 2 atUser = 2
} }
export enum ChatType {
friend = 1,
group = 2,
chatDevice = 8, //移动设备?
temp = 100
}
// 来自Android分析 // 来自Android分析
export enum ChatType2 { export enum ChatType {
KCHATTYPEADELIE = 42, KCHATTYPEADELIE = 42,
KCHATTYPEBUDDYNOTIFY = 5, KCHATTYPEBUDDYNOTIFY = 5,
KCHATTYPEC2C = 1, KCHATTYPEC2C = 1,
@@ -856,41 +848,6 @@ export interface MultiForwardMsgElement {
resId: string; resId: string;
fileName: string; fileName: string;
} }
export enum NTSubMsgType {
KMSGSUBTYPEARKGROUPANNOUNCE = 3,
KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4,
KMSGSUBTYPEARKGROUPGIFTATME = 5,
KMSGSUBTYPEARKGROUPTASKATALL = 6,
KMSGSUBTYPEARKMULTIMSG = 7,
KMSGSUBTYPEARKNORMAL = 0,
KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1,
KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2,
KMSGSUBTYPEEMOTICON = 15,
KMSGSUBTYPEFILEAPP = 11,
KMSGSUBTYPEFILEAUDIO = 3,
KMSGSUBTYPEFILEDOC = 4,
KMSGSUBTYPEFILEEXCEL = 6,
KMSGSUBTYPEFILEFOLDER = 13,
KMSGSUBTYPEFILEHTML = 10,
KMSGSUBTYPEFILEIPA = 14,
KMSGSUBTYPEFILENORMAL = 0,
KMSGSUBTYPEFILEPDF = 7,
KMSGSUBTYPEFILEPIC = 1,
KMSGSUBTYPEFILEPPT = 5,
KMSGSUBTYPEFILEPSD = 12,
KMSGSUBTYPEFILETXT = 8,
KMSGSUBTYPEFILEVIDEO = 2,
KMSGSUBTYPEFILEZIP = 9,
KMSGSUBTYPELINK = 5,
KMSGSUBTYPEMARKETFACE = 1,
KMSGSUBTYPEMIXEMOTICON = 7,
KMSGSUBTYPEMIXFACE = 3,
KMSGSUBTYPEMIXMARKETFACE = 2,
KMSGSUBTYPEMIXPIC = 1,
KMSGSUBTYPEMIXREPLY = 4,
KMSGSUBTYPEMIXTEXT = 0,
KMSGSUBTYPETENCENTDOC = 6
}
export enum SendStatusType { export enum SendStatusType {
KSEND_STATUS_FAILED = 0, KSEND_STATUS_FAILED = 0,
KSEND_STATUS_SENDING = 1, KSEND_STATUS_SENDING = 1,
@@ -922,7 +879,7 @@ export interface RawMessage {
msgType: NTMsgType; msgType: NTMsgType;
subMsgType: NTSubMsgType; subMsgType: number;
senderUid: string; senderUid: string;
@@ -965,76 +922,5 @@ export interface RawMessage {
records: RawMessage[]; records: RawMessage[];
elements: ElementWrapper[]; elements: MessageElement[];
} }
/**
* 并非原生接口类型,故以 type 包装
*/
export type ElementWrapper = {
elementId: string;
elementType: ElementType;
replyElement?: {
sourceMsgIdInRecords: string;
/**
* 源消息发送者 QQ 号
*/
senderUid: string;
/**
* 源消息是否有图片
*/
sourceMsgIsIncPic: boolean;
/**
* 源消息文本
*/
sourceMsgText: string;
/**
* 源消息的 msgSeq可以通过这个找到源消息的 msgId
*/
replayMsgSeq: string;
};
textElement?: {
atType: AtType;
/**
* 被 @ 的 QQ 号
*/
atUid: string;
content: string;
/**
* 被 @ 的 UID从这里可以看出来 UID 的概念是 NT 才引入的)
*/
atNtUid: string;
};
picElement?: PicElement;
pttElement?: PttElement;
arkElement?: ArkElement;
grayTipElement?: GrayTipElement;
faceElement?: FaceElement;
videoElement?: VideoElement;
fileElement?: FileElement;
marketFaceElement?: MarketFaceElement;
inlineKeyboardElement?: InlineKeyboardElement;
markdownElement?: MarkdownElement;
multiForwardMsgElement?: MultiForwardMsgElement;
}

View File

@@ -1,40 +1,58 @@
export enum GroupNotifyTypes { export enum GroupNotifyMsgType {
INVITE_ME = 1, UN_SPECIFIED,
INVITED_JOIN = 4, // 有人接受了邀请入群 INVITED_BY_MEMBER,
JOIN_REQUEST = 7, REFUSE_INVITED,
ADMIN_SET = 8, REFUSED_BY_ADMINI_STRATOR,
KICK_MEMBER = 9, AGREED_TOJOIN_DIRECT,// 有人接受了邀请入群
MEMBER_EXIT = 11, // 主动退出 INVITED_NEED_ADMINI_STRATOR_PASS,
ADMIN_UNSET = 12, AGREED_TO_JOIN_BY_ADMINI_STRATOR,
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员 REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
SET_ADMIN,
KICK_MEMBER_NOTIFY_ADMIN,
KICK_MEMBER_NOTIFY_KICKED,
MEMBER_LEAVE_NOTIFY_ADMIN,// 主动退出
CANCEL_ADMIN_NOTIFY_CANCELED,
CANCEL_ADMIN_NOTIFY_ADMIN,// 其他人取消管理员
TRANSFER_GROUP_NOTIFY_OLDOWNER,
TRANSFER_GROUP_NOTIFY_ADMIN
} }
export interface GroupNotifies { export interface GroupNotifies {
doubt: boolean; doubt: boolean;
nextStartSeq: string; nextStartSeq: string;
notifies: GroupNotify[]; notifies: GroupNotify[];
} }
export enum GroupNotifyStatus { export enum GroupNotifyMsgStatus {
IGNORE = 0, KINIT,//初始化
WAIT_HANDLE = 1, KUNHANDLE,//未处理
APPROVE = 2, KAGREED,//同意
REJECT = 3 KREFUSED,//拒绝
KIGNORED//忽略
}
export enum GroupInviteStatus {
INIT,
WAIT_TO_APPROVE,
JOINED,
REFUSED_BY_ADMINI_STRATOR
}
export enum GroupInviteType {
BYBUDDY,
BYGROUPMEMBER,
BYDISCUSSMEMBER
} }
export interface GroupNotify { export interface GroupNotify {
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify seq: string; // 通知序列号
seq: string; // 唯一标识符转成数字再除以1000应该就是时间戳 type: GroupNotifyMsgType;
type: GroupNotifyTypes; status: GroupNotifyMsgStatus;
status: GroupNotifyStatus; // 0是已忽略1是未处理2是已同意
group: { groupCode: string; groupName: string }; group: { groupCode: string; groupName: string };
user1: { uid: string; nickName: string }; // 被设置管理员的人 user1: { uid: string; nickName: string }; // 被设置管理员的人
user2: { uid: string; nickName: string }; // 操作者 user2: { uid: string; nickName: string }; // 操作者
actionUser: { uid: string; nickName: string }; //未知 actionUser: { uid: string; nickName: string }; //未知
actionTime: string; actionTime: string;
invitationExt: { invitationExt: {
srcType: number; // 0?未知 srcType: GroupInviteType; // 邀请来源
groupCode: string; waitStatus: number groupCode: string;
waitStatus: GroupInviteStatus
}; };
postscript: string; // 加群用户填写的验证信息 postscript: string; // 加群用户填写的验证信息
repeatSeqs: []; repeatSeqs: [];
@@ -64,6 +82,7 @@ export enum BuddyReqType {
} }
export interface FriendRequest { export interface FriendRequest {
isBuddy?: boolean;
isInitiator?: boolean; isInitiator?: boolean;
isDecide: boolean; isDecide: boolean;
friendUid: string; friendUid: string;

View File

@@ -1,58 +1,30 @@
{ {
"3.1.2-13107": { "3.2.12-27187": {
"appid": 537146866, "appid": 537240645,
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B" "qua": "V1_LNX_NQ_3.2.12_27187_GW_B"
}, },
"3.2.10-25765": { "3.2.12-27206": {
"appid": 537234773, "appid": 537240645,
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B" "qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
}, },
"3.2.12-26702": { "3.2.12-27254":{
"appid": 537237950, "appid": 537240795,
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B" "qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
}, },
"3.2.12-26740": { "9.9.15-27187":{
"appid": 537237950, "appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B" "qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
}, },
"3.2.12-26909": { "9.9.15-27206":{
"appid": 537237923, "appid": 537240610,
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B" "qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
}, },
"9.9.11-24815": { "9.9.15-27254":{
"appid": 537226656, "appid": 537240709,
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B" "qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
}, },
"9.9.12-25493": { "9.9.15-27333": {
"appid": 537231759, "appid": 537240709,
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B" "qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
},
"9.9.12-25765": {
"appid": 537234702,
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
},
"9.9.12-26299": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
},
"9.9.12-26339": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
},
"9.9.12-26466": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
},
"9.9.15-26702": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26740": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26909": {
"appid": 537237802,
"qua": "V1_WIN_NQ_9.9.15_26909_GW_B"
} }
} }

View File

@@ -1,6 +1,6 @@
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities'; import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
interface IGroupListener { export interface IGroupListener {
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void; onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
onGroupExtListUpdate(...args: unknown[]): void; onGroupExtListUpdate(...args: unknown[]): void;

View File

@@ -43,4 +43,48 @@ export class ProfileListener implements IProfileListener {
onStrangerRemarkChanged(...args: unknown[]) { onStrangerRemarkChanged(...args: unknown[]) {
} }
onMemberListChange(...args: unknown[]){
}
onMemberInfoChange(...args: unknown[]){
}
onGroupListUpdate(...args: unknown[]){
}
onGroupAllInfoChange(...args: unknown[]){
}
onGroupDetailInfoChange(...args: unknown[]){
}
onGroupConfMemberChange(...args: unknown[]){
}
onGroupExtListUpdate(...args: unknown[]){
}
onGroupNotifiesUpdated(...args: unknown[]){
}
onGroupNotifiesUnreadCountUpdated(...args: unknown[]){
}
onGroupMemberLevelInfoChange(...args: unknown[]){
}
onGroupBulletinChange(...args: unknown[]){
}
} }

View File

@@ -61,11 +61,11 @@ export interface NodeIKernelBuddyService {
getBuddyReqUnreadCnt(): number; getBuddyReqUnreadCnt(): number;
getBuddyReq(): unknown; getBuddyReq(): Promise<GeneralCallResult>;
delBuddyReq(uid: number): void; delBuddyReq(uid: number): void;
clearBuddyReqUnreadCnt(): void; clearBuddyReqUnreadCnt(): Promise<GeneralCallResult>;
reqToAddFriends(uid: number, msg: string): void; reqToAddFriends(uid: number, msg: string): void;

View File

@@ -1,16 +1,20 @@
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener'; import { IGroupListener, NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import { import {
GroupExtParam, GroupExtParam,
GroupMember, GroupMember,
GroupMemberRole, GroupMemberRole,
GroupNotifyTypes, GroupNotifyMsgType,
GroupRequestOperateTypes, GroupRequestOperateTypes,
kickMemberV2Req,
} from '@/core/entities'; } from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底 //高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
export interface NodeIKernelGroupService { export interface NodeIKernelGroupService {
kickMemberV2(param: kickMemberV2Req): Promise<GeneralCallResult>;
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
getMemberCommonInfo(Req: { getMemberCommonInfo(Req: {
groupCode: string, groupCode: string,
startUin: string, startUin: string,
@@ -96,7 +100,7 @@ export interface NodeIKernelGroupService {
uid: string, uid: string,
index: number//0 index: number//0
}>, }>,
infos: {}, infos: unknown,
finish: true, finish: true,
hasRobot: false hasRobot: false
} }
@@ -104,7 +108,7 @@ export interface NodeIKernelGroupService {
setHeader(uid: string, path: string): unknown; setHeader(uid: string, path: string): unknown;
addKernelGroupListener(listener: NodeIKernelGroupListener): number; addKernelGroupListener(listener: IGroupListener): number;
removeKernelGroupListener(listenerId: unknown): void; removeKernelGroupListener(listenerId: unknown): void;
@@ -195,7 +199,7 @@ export interface NodeIKernelGroupService {
operateType: GroupRequestOperateTypes, // 2 拒绝 operateType: GroupRequestOperateTypes, // 2 拒绝
targetMsg: { targetMsg: {
seq: string, // 通知序列号 seq: string, // 通知序列号
type: GroupNotifyTypes, type: GroupNotifyMsgType,
groupCode: string, groupCode: string,
postscript: string postscript: string
} }
@@ -205,7 +209,7 @@ export interface NodeIKernelGroupService {
getGroupBulletin(groupCode: string): unknown; getGroupBulletin(groupCode: string): unknown;
deleteGroupBulletin(groupCode: string, seq: string): void; deleteGroupBulletin(groupCode: string, seq: string, feedId: string): void;
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>; publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;

View File

@@ -37,7 +37,7 @@ export interface NodeIKernelMsgService {
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>; recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
addKernelMsgImportToolListener(arg: Object): unknown; addKernelMsgImportToolListener(arg: unknown): unknown;
removeKernelMsgListener(args: unknown): unknown; removeKernelMsgListener(args: unknown): unknown;
@@ -51,7 +51,7 @@ export interface NodeIKernelMsgService {
getOnLineDev(): void; getOnLineDev(): void;
kickOffLine(DevInfo: Object): unknown; kickOffLine(DevInfo: unknown): unknown;
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>; setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>;
@@ -80,11 +80,11 @@ export interface NodeIKernelMsgService {
// this.voipToken = bArr2; // this.voipToken = bArr2;
// this.profileId = str; // this.profileId = str;
setToken(arg: Object): unknown; setToken(arg: unknown): unknown;
switchForeGround(): unknown; switchForeGround(): unknown;
switchBackGround(arg: Object): unknown; switchBackGround(arg: unknown): unknown;
//hex //hex
setTokenForMqq(token: string): unknown; setTokenForMqq(token: string): unknown;
@@ -384,7 +384,7 @@ export interface NodeIKernelMsgService {
getFileThumbSavePath(...args: unknown[]): unknown; getFileThumbSavePath(...args: unknown[]): unknown;
//猜测居多 //猜测居多
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown; translatePtt2Text(MsgId: string, Peer: Peer, MsgElement: unknown): unknown;
setPttPlayedState(...args: unknown[]): unknown; setPttPlayedState(...args: unknown[]): unknown;
@@ -668,7 +668,7 @@ export interface NodeIKernelMsgService {
recordEmoji(...args: unknown[]): unknown; recordEmoji(...args: unknown[]): unknown;
fetchGetHitEmotionsByWord(args: Object): Promise<unknown>;//表情推荐? fetchGetHitEmotionsByWord(args: unknown): Promise<unknown>;//表情推荐?
deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息? deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息?

View File

@@ -1,6 +1,6 @@
import { AnyCnameRecord } from 'node:dns'; import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities'; import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners'; import { NodeIKernelProfileListener, ProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource { export enum UserDetailSource {
@@ -35,7 +35,7 @@ export interface NodeIKernelProfileService {
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>; fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
addKernelProfileListener(listener: NodeIKernelProfileListener): number; addKernelProfileListener(listener: ProfileListener): number;
removeKernelProfileListener(listenerId: number): void; removeKernelProfileListener(listenerId: number): void;

View File

@@ -1,4 +1,4 @@
import { forceFetchClientKeyRetType } from './common'; import { ForceFetchClientKeyRetType } from './common';
export interface NodeIKernelTicketService { export interface NodeIKernelTicketService {
@@ -6,7 +6,7 @@ export interface NodeIKernelTicketService {
removeKernelTicketListener(listenerId: unknown): void; removeKernelTicketListener(listenerId: unknown): void;
forceFetchClientKey(arg: string): Promise<forceFetchClientKeyRetType>; forceFetchClientKey(arg: string): Promise<ForceFetchClientKeyRetType>;
isNull(): boolean; isNull(): boolean;
} }

View File

@@ -8,7 +8,7 @@ export interface GeneralCallResult {
errMsg: string errMsg: string
} }
export interface forceFetchClientKeyRetType extends GeneralCallResult { export interface ForceFetchClientKeyRetType extends GeneralCallResult {
url: string; url: string;
keyIndex: string; keyIndex: string;
clientKey: string; clientKey: string;

View File

@@ -35,6 +35,7 @@ import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPe
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService'; import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
export interface NodeQQNTWrapperUtil { export interface NodeQQNTWrapperUtil {
get(): unknown;
// eslint-disable-next-line @typescript-eslint/no-misused-new // eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil; new(): NodeQQNTWrapperUtil;
@@ -68,9 +69,9 @@ export interface NodeQQNTWrapperUtil {
genFileShaAndMd5Hex(path: string, unknown: number): unknown; //可能是错的 genFileShaAndMd5Hex(path: string, unknown: number): unknown; //可能是错的
setTraceInfo(unknown: Object): unknown; setTraceInfo(unknown: unknown): unknown;
encodeOffLine(unknown: Object): unknown; encodeOffLine(unknown: unknown): unknown;
decodeOffLine(arg: string): unknown; //可能是错的 传递hex decodeOffLine(arg: string): unknown; //可能是错的 传递hex
@@ -88,7 +89,7 @@ export interface NodeQQNTWrapperUtil {
runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown; runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown;
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown; calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown;
fullWordToHalfWord(arg0: string): unknown; fullWordToHalfWord(arg0: string): unknown;

File diff suppressed because one or more lines are too long

View File

@@ -41,8 +41,7 @@ export async function NCoreInitFramework(
online: true, online: true,
}); });
}; };
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
proxiedListenerOf(loginListener, logger)));
}); });
// 过早进入会导致addKernelMsgListener等Listener添加失败 // 过早进入会导致addKernelMsgListener等Listener添加失败
// await sleep(2500); // await sleep(2500);

View File

@@ -1,4 +1,4 @@
import { OB11Constructor } from '@/onebot/helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';

View File

@@ -24,16 +24,16 @@ export class SetInputStatus extends BaseAction<Payload, any> {
let peer: Peer; let peer: Peer;
if (payload.group_id) { if (payload.group_id) {
peer = { peer = {
chatType: ChatType.group, chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id peerUid: payload.group_id
} };
} else if (payload.user_id) { } else if (payload.user_id) {
let uid = await NTQQUserApi.getUidByUinV2(payload.user_id); const uid = await NTQQUserApi.getUidByUinV2(payload.user_id);
if (!uid) throw new Error('uid is empty'); if (!uid) throw new Error('uid is empty');
peer = { peer = {
chatType: ChatType.friend, chatType: ChatType.KCHATTYPEC2C,
peerUid: uid peerUid: uid
} };
} else { } else {
throw new Error('请指定 group_id 或 user_id'); throw new Error('请指定 group_id 或 user_id');
} }

View File

@@ -47,15 +47,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
let peer: Peer | undefined; let peer: Peer | undefined;
//识别Peer //识别Peer
if (isGroup) { if (isGroup) {
peer = { chatType: ChatType.group, peerUid: peerUin }; peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
} }
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin); const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
if (PeerUid) { if (PeerUid) {
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid); const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) { if (isBuddy) {
peer = { chatType: ChatType.friend, peerUid: PeerUid }; peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
} else { } else {
peer = { chatType: ChatType.temp, peerUid: PeerUid }; peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
} }
} }
if (!peer) { if (!peer) {
@@ -90,15 +90,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
return res; return res;
} }
} catch { } catch {
this.CoreContext.context.logger.logDebug('GetFileBase Mode - 1 Error');
} }
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems; const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
if (NTSearchNameResult.length !== 0) { if (NTSearchNameResult.length !== 0) {
const MsgId = NTSearchNameResult[0].msgId; const MsgId = NTSearchNameResult[0].msgId;
let peer: Peer | undefined = undefined; let peer: Peer | undefined = undefined;
if (NTSearchNameResult[0].chatType == ChatType.group) { if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) {
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode }; peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
} }
if (!peer) { if (!peer) {
throw new Error('chattype not support'); throw new Error('chattype not support');

View File

@@ -1,6 +1,5 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types'; import { OB11ForwardMessage, OB11Message, OB11MessageData } from '@/onebot';
import { OB11Constructor } from '../../helper/data';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique'; import { MessageUnique } from '@/common/utils/MessageUnique';
@@ -40,7 +39,8 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
} }
const msgList = data.msgList; const msgList = data.msgList;
const messages = (await Promise.all(msgList.map(async msg => { const messages = (await Promise.all(msgList.map(async msg => {
const resMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg); const resMsg = await this.OneBotContext.apiContext.MsgApi
.parseMessage(msg);
if (!resMsg) return; if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({ resMsg.message_id = MessageUnique.createMsg({
guildId: '', guildId: '',

View File

@@ -1,8 +1,7 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { OB11Message } from '../../types'; import { OB11Message } from '@/onebot';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { ChatType, RawMessage } from '@/core/entities'; import { ChatType, RawMessage } from '@/core/entities';
import { OB11Constructor } from '../../helper/data';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique'; import { MessageUnique } from '@/common/utils/MessageUnique';
@@ -37,7 +36,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
const isReverseOrder = payload.reverseOrder || true; const isReverseOrder = payload.reverseOrder || true;
if (!uid) throw `记录${payload.user_id}不存在`; if (!uid) throw `记录${payload.user_id}不存在`;
const friend = await NTQQFriendApi.isBuddy(uid); const friend = await NTQQFriendApi.isBuddy(uid);
const peer = { chatType: friend ? ChatType.friend : ChatType.temp, peerUid: uid }; const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
//拉取消息 //拉取消息
let msgList: RawMessage[]; let msgList: RawMessage[];
@@ -53,7 +52,9 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
//转换消息 //转换消息
const ob11MsgList = (await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList }; return { 'messages': ob11MsgList };
} }
} }

View File

@@ -1,8 +1,7 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { OB11Message } from '../../types'; import { OB11Message } from '@/onebot';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { ChatType, Peer, RawMessage } from '@/core/entities'; import { ChatType, Peer, RawMessage } from '@/core/entities';
import { OB11Constructor } from '../../helper/data';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique'; import { MessageUnique } from '@/common/utils/MessageUnique';
@@ -32,7 +31,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
//处理参数 //处理参数
const isReverseOrder = payload.reverseOrder || true; const isReverseOrder = payload.reverseOrder || true;
const MsgCount = payload.count || 20; const MsgCount = payload.count || 20;
const peer: Peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() }; const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
//拉取消息 //拉取消息
let msgList: RawMessage[]; let msgList: RawMessage[];
if (!payload.message_seq || payload.message_seq == 0) { if (!payload.message_seq || payload.message_seq == 0) {
@@ -48,7 +47,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
})); }));
//转换消息 //转换消息
const ob11MsgList = (await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList }; return { 'messages': ob11MsgList };
} }
} }

View File

@@ -1,6 +1,6 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { OB11User, OB11UserSex } from '../../types'; import { OB11User, OB11UserSex } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { calcQQLevel } from '@/common/utils/helper'; import { calcQQLevel } from '@/common/utils/helper';
@@ -37,7 +37,7 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0, level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0,
login_days: 0, login_days: 0,
uid: '' uid: ''
}; };
return ret; return ret;
} }
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) }; const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };

View File

@@ -44,9 +44,9 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
} }
// log(`头像设置返回:${JSON.stringify(ret)}`) // log(`头像设置返回:${JSON.stringify(ret)}`)
if (ret['result'] == 1004022) { if (ret['result'] == 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`; throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
} else if (ret['result'] != 0) { } else if (ret['result'] != 0) {
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`; throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
} }
return ret; return ret;
} else { } else {

View File

@@ -24,7 +24,7 @@ export class SetQQProfile extends BaseAction<Payload, any | null> {
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid); const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
const ret = await NTQQUserApi.modifySelfProfile({ const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nickname, nick: payload.nickname,
longNick: payload?.personal_note ?? OldProfile?.longNick!, longNick: (payload?.personal_note ?? OldProfile?.longNick) || '',
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()), sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() }, birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() },
location: undefined, location: undefined,

View File

@@ -5,7 +5,7 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg'; import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file'; import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/msg'; import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -36,7 +36,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
} }
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id); const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id);
await sendMsg(this.CoreContext, { await sendMsg(this.CoreContext, {
chatType: ChatType.group, chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(), peerUid: payload.group_id.toString(),
}, [sendFileEle], [], true); }, [sendFileEle], [], true);
return null; return null;

View File

@@ -5,7 +5,7 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg'; import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file'; import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/msg'; import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -32,7 +32,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
throw `私聊${payload.user_id}不存在`; throw `私聊${payload.user_id}不存在`;
} }
const isBuddy = await NTQQFriendApi.isBuddy(peerUid); const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid }; return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
} }
throw '缺少参数 user_id'; throw '缺少参数 user_id';
} }

View File

@@ -0,0 +1,28 @@
import { WebApiGroupNoticeFeed } from '@/core';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
feed_id: { type: 'string' },
},
required: ['group_id','feed_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class DelGroupNotice extends BaseAction<Payload, any> {
actionName = ActionName.DelGroupNotice;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const group = payload.group_id.toString();
const feedId = payload.feed_id;
return await NTQQGroupApi.deleteGroupBulletin(group, feedId);
}
}

View File

@@ -1,5 +1,5 @@
import { OB11Group } from '../../types'; import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@@ -1,5 +1,5 @@
import { OB11Group } from '../../types'; import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { Group } from '@/core/entities'; import { Group } from '@/core/entities';

View File

@@ -1,5 +1,5 @@
import { OB11GroupMember } from '../../types'; import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -26,12 +26,12 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
const NTQQGroupApi = this.CoreContext.apis.GroupApi; const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache; const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) throw (`Uin2Uid Error ${payload.user_id}不存在`); if (!uid) throw new Error (`Uin2Uid Error ${payload.user_id}不存在`);
const [member, info] = await Promise.allSettled([ const [member, info] = await Promise.allSettled([
NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache), NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
NTQQUserApi.getUserDetailInfo(uid), NTQQUserApi.getUserDetailInfo(uid),
]); ]);
if (member.status !== 'fulfilled') throw (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`); if (member.status !== 'fulfilled') throw new Error (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
if (info.status === 'fulfilled') { if (info.status === 'fulfilled') {
this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value); this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value);
Object.assign(member, info.value); Object.assign(member, info.value);

View File

@@ -1,5 +1,5 @@
import { OB11GroupMember } from '../../types'; import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@@ -1,3 +1,4 @@
import { GroupNotifyMsgStatus } from '@/core';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -28,7 +29,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
invitor_nick: SSNotify.user1?.nickName, invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode, group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true, checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
}); });
} else if (SSNotify.type == 7) { } else if (SSNotify.type == 7) {
@@ -38,7 +39,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
requester_nick: SSNotify.user1?.nickName, requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode, group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true, checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
}); });
} }

View File

@@ -61,7 +61,7 @@ import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
import { SetGroupFileFolder } from './file/SetGroupFileFolder'; import { SetGroupFileFolder } from './file/SetGroupFileFolder';
import { DelGroupFile } from './file/DelGroupFile'; import { DelGroupFile } from './file/DelGroupFile';
import { DelGroupFileFolder } from './file/DelGroupFileFolder'; import { DelGroupFileFolder } from './file/DelGroupFileFolder';
import { SetQQProfile } from './go-cqhttp/SetQQProfile' import { SetQQProfile } from './go-cqhttp/SetQQProfile';
import { ShareGroupEx, SharePeer } from './extends/ShareContact'; import { ShareGroupEx, SharePeer } from './extends/ShareContact';
import { CreateCollection } from './extends/CreateCollection'; import { CreateCollection } from './extends/CreateCollection';
import { SetLongNick } from './extends/SetLongNick'; import { SetLongNick } from './extends/SetLongNick';
@@ -79,6 +79,8 @@ import { NapCatOneBot11Adapter } from '@/onebot';
import GetGuildProfile from './guild/GetGuildProfile'; import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow'; import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus'; import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF';
import { DelGroupNotice } from './group/DelGroupNotice';
export type ActionMap = Map<string, BaseAction<any, any>>; export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -167,6 +169,8 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
new GetGuildProfile(onebotContext, coreContext), new GetGuildProfile(onebotContext, coreContext),
new SetModelShow(onebotContext, coreContext), new SetModelShow(onebotContext, coreContext),
new SetInputStatus(onebotContext, coreContext), new SetInputStatus(onebotContext, coreContext),
new GetCSRF(onebotContext, coreContext),
new DelGroupNotice(onebotContext, coreContext),
]; ];
const actionMap = new Map(); const actionMap = new Map();
for (const action of actionHandlers) { for (const action of actionHandlers) {

View File

@@ -24,9 +24,9 @@ class ForwardSingleMsg extends BaseAction<Payload, null> {
if (!peerUid) { if (!peerUid) {
throw new Error(`无法找到私聊对象${payload.user_id}`); throw new Error(`无法找到私聊对象${payload.user_id}`);
} }
return { chatType: ChatType.friend, peerUid }; return { chatType: ChatType.KCHATTYPEC2C, peerUid };
} }
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() }; return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id!.toString() };
} }
async _handle(payload: Payload): Promise<null> { async _handle(payload: Payload): Promise<null> {

View File

@@ -1,5 +1,4 @@
import { OB11Message } from '../../types'; import { OB11Message } from '@/onebot';
import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -31,19 +30,20 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString()); const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString());
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString())); const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
if (!msgIdWithPeer) { if (!msgIdWithPeer) {
throw ('消息不存在'); throw new Error('消息不存在');
} }
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType }; const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
const msg = await NTQQMsgApi.getMsgsByMsgId( const msg = await NTQQMsgApi.getMsgsByMsgId(
peer, peer,
[msgIdWithPeer?.MsgId || payload.message_id.toString()]); [msgIdWithPeer?.MsgId || payload.message_id.toString()]);
const retMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array'); const retMsg = await this.OneBotContext.apiContext.MsgApi.parseMessage(msg.msgList[0], 'array');
if (!retMsg) throw Error('消息为空'); if (!retMsg) throw Error('消息为空');
try { try {
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!; retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
retMsg.message_seq = retMsg.message_id; retMsg.message_seq = retMsg.message_id;
retMsg.real_id = retMsg.message_id; retMsg.real_id = retMsg.message_id;
} catch (e) { } catch (e) {
// ignored
} }
return retMsg; return retMsg;
} }

View File

@@ -23,12 +23,12 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
throw `私聊${payload.user_id}不存在`; throw `私聊${payload.user_id}不存在`;
} }
const isBuddy = await NTQQFriendApi.isBuddy(peerUid); const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid }; return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
} }
if (!payload.group_id) { if (!payload.group_id) {
throw '缺少参数 group_id 或 user_id'; throw '缺少参数 group_id 或 user_id';
} }
return { chatType: ChatType.group, peerUid: payload.group_id.toString() }; return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
} }
async _handle(payload: PlayloadType): Promise<null> { async _handle(payload: PlayloadType): Promise<null> {
@@ -36,7 +36,7 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
// 调用API // 调用API
const ret = await NTQQMsgApi.setMsgRead(await this.getPeer(payload)); const ret = await NTQQMsgApi.setMsgRead(await this.getPeer(payload));
if (ret.result != 0) { if (ret.result != 0) {
throw ('设置已读失败,' + ret.errMsg); throw new Error('设置已读失败,' + ret.errMsg);
} }
return null; return null;
} }

View File

@@ -2,8 +2,8 @@ import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/one
import { uri2local } from '@/common/utils/file'; import { uri2local } from '@/common/utils/file';
import { RequestUtil } from '@/common/utils/request'; import { RequestUtil } from '@/common/utils/request';
import { MessageUnique } from '@/common/utils/MessageUnique'; import { MessageUnique } from '@/common/utils/MessageUnique';
import { AtType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core'; import { AtType, ChatType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
import { SendMsgElementConstructor } from '@/onebot/helper/msg'; import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
export type MessageContext = { export type MessageContext = {
@@ -17,14 +17,21 @@ async function handleOb11FileLikeMessage(
{ data: inputdata }: OB11MessageFileBase, { data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: MessageContext, { deleteAfterSentFiles }: MessageContext,
) { ) {
//有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa //inputdata?.url || inputdata.file
const isBlankUrl = !inputdata.url || inputdata.url === '';
const isBlankFile = !inputdata.file || inputdata.file === '';
if (isBlankUrl && isBlankFile) {
coreContext.context.logger.logError('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数');
}
const fileOrUrl = (isBlankUrl ? inputdata.file : inputdata.url) || "";
const { const {
path, path,
isLocal, isLocal,
fileName, fileName,
errMsg, errMsg,
success, success,
} = (await uri2local(coreContext.NapCatTempPath, inputdata?.url || inputdata.file)); } = (await uri2local(coreContext.NapCatTempPath, fileOrUrl));
if (!success) { if (!success) {
coreContext.context.logger.logError('文件下载失败', errMsg); coreContext.context.logger.logError('文件下载失败', errMsg);
@@ -51,15 +58,18 @@ const _handlers: {
[OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text), [OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text),
[OB11MessageDataType.at]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { qq: atQQ } }, context) => { [OB11MessageDataType.at]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { qq: atQQ } }, context) => {
if (!context.peer) return undefined; if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined;
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员'); if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
const NTQQGroupApi = coreContext.apis.GroupApi;
// then the qq is a group member const NTQQUserApi = coreContext.apis.UserApi;
// Mlikiowa V2.0.36 Refactor Todo const atMember = await NTQQGroupApi.getGroupMember(context.peer.peerUid, atQQ);
const uid = await coreContext.apis.UserApi.getUidByUinV2(`${atQQ}`); if (atMember) {
return SendMsgElementConstructor.at(coreContext, atQQ, atMember.uid, AtType.atUser, atMember.nick || atMember.cardName);
}
const uid = await NTQQUserApi.getUidByUinV2(`${atQQ}`);
if (!uid) throw new Error('Get Uid Error'); if (!uid) throw new Error('Get Uid Error');
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, ''); const info = await NTQQUserApi.getUserDetailInfo(uid);
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, info.nick || '');
}, },
[OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => { [OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => {
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id)); const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
@@ -161,7 +171,7 @@ const _handlers: {
} else { } else {
postData = data; postData = data;
} }
// Mlikiowa V2.0.36 Refactor Todo // Mlikiowa V2.2.8 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl; const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) { if (!signUrl) {
if (data.type === 'qq') { if (data.type === 'qq') {
@@ -225,7 +235,7 @@ export default async function createSendElements(
callResultList.push(callResult); callResultList.push(callResult);
} }
const ret = await Promise.all(callResultList); const ret = await Promise.all(callResultList);
const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[]; const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
return { sendElements, deleteAfterSentFiles }; return { sendElements, deleteAfterSentFiles };
} }

View File

@@ -7,7 +7,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMessage | undefined> { async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMessage | undefined> {
const selfPeer = { const selfPeer = {
chatType: ChatType.friend, chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid, peerUid: coreContext.selfInfo.uid,
}; };
const logger = coreContext.context.logger; const logger = coreContext.context.logger;
@@ -34,7 +34,7 @@ async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMe
export async function handleForwardNode(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> { export async function handleForwardNode(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
const NTQQMsgApi = coreContext.apis.MsgApi; const NTQQMsgApi = coreContext.apis.MsgApi;
const selfPeer = { const selfPeer = {
chatType: ChatType.friend, chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid, peerUid: coreContext.selfInfo.uid,
}; };
let nodeMsgIds: string[] = []; let nodeMsgIds: string[] = [];
@@ -110,18 +110,21 @@ export async function handleForwardNode(coreContext: NapCatCore, obContext: NapC
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId); nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
let retMsgIds: string[] = []; let retMsgIds: string[] = [];
if (needSendSelf) { if (needSendSelf) {
for (const [index, msg] of nodeMsgArray.entries()) { for (const [, msg] of nodeMsgArray.entries()) {
if (msg.peerUid === coreContext.selfInfo.uid) continue; if (msg.peerUid === coreContext.selfInfo.uid){
retMsgIds.push(msg.msgId);
continue;
}
const ClonedMsg = await cloneMsg(coreContext, msg); const ClonedMsg = await cloneMsg(coreContext, msg);
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId); if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
} }
} else { } else {
retMsgIds = nodeMsgIds; retMsgIds = nodeMsgIds;
} }
if (nodeMsgIds.length === 0) throw Error('转发消息失败,生成节点为空'); if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
try { try {
logger.logDebug('开发转发', srcPeer, destPeer, nodeMsgIds); logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds); return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds);
} catch (e) { } catch (e) {
logger.logError('forward failed', e); logger.logError('forward failed', e);
return null; return null;

View File

@@ -40,7 +40,7 @@ export async function sendMsg(coreContext: NapCatCore, peer: Peer, sendElements:
const NTQQMsgApi = coreContext.apis.MsgApi; const NTQQMsgApi = coreContext.apis.MsgApi;
const logger = coreContext.context.logger; const logger = coreContext.context.logger;
if (!sendElements.length) { if (!sendElements.length) {
throw ('消息体无法解析, 请检查是否发送了不支持的消息类型'); throw new Error ('消息体无法解析, 请检查是否发送了不支持的消息类型');
} }
let totalSize = 0; let totalSize = 0;
let timeout = 10000; let timeout = 10000;
@@ -90,20 +90,43 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
// This redundant design of Ob11 here should be blamed. // This redundant design of Ob11 here should be blamed.
const NTQQFriendApi = coreContext.apis.FriendApi; const NTQQFriendApi = coreContext.apis.FriendApi;
const NTQQUserApi = coreContext.apis.UserApi; const NTQQUserApi = coreContext.apis.UserApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) { if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
return { return {
chatType: ChatType.group, chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(), peerUid: payload.group_id.toString(),
}; };
} }
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) { if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(Uid!); if (!Uid) throw '无法获取用户信息';
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy); const isBuddy = await NTQQFriendApi.isBuddy(Uid);
if (!isBuddy) {
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, Uid);
if (ret.tmpChatInfo?.groupCode) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: '',
};
}
if (payload.group_id) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: payload.group_id.toString(),
};
}
return {
chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!,
guildId: '',
};
}
return { return {
chatType: isBuddy ? ChatType.friend : ChatType.temp, chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!, peerUid: Uid!,
guildId: payload.group_id?.toString() || '', guildId: '',
}; };
} }
throw '请指定 group_id 或 user_id'; throw '请指定 group_id 或 user_id';
@@ -135,7 +158,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (payload.user_id && payload.message_type !== 'group') { if (payload.user_id && payload.message_type !== 'group') {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(uid!); const isBuddy = await NTQQFriendApi.isBuddy(uid!);
if (!isBuddy) {} //if (!isBuddy) { }
} }
return { valid: true }; return { valid: true };
} }

View File

@@ -0,0 +1,12 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class GetCSRF extends BaseAction<any, any> {
actionName = ActionName.GetCSRF;
async _handle(payload: any) {
return {
token: "",
};
}
}

View File

@@ -1,5 +1,5 @@
import { OB11User } from '../../types'; import { OB11User } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';

View File

@@ -107,5 +107,7 @@ export enum ActionName {
FetchEmojiLike = 'fetch_emoji_like', FetchEmojiLike = 'fetch_emoji_like',
GetGuildProfile = "get_guild_service_profile", GetGuildProfile = "get_guild_service_profile",
SetModelShow = "_set_model_show", SetModelShow = "_set_model_show",
SetInputStatus = "set_input_status" SetInputStatus = "set_input_status",
GetCSRF = "get_csrf_token",
DelGroupNotice = "_del_group_notice",
} }

View File

@@ -1,5 +1,5 @@
import { OB11User } from '../../types'; import { OB11User } from '../../types';
import { OB11Constructor } from '../../helper/data'; import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@@ -1,7 +1,6 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { OB11Constructor } from '@/onebot/helper/data';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -20,11 +19,11 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
const NTQQUserApi = this.CoreContext.apis.UserApi; const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi; const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString())); const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
const data = await Promise.all(ret.info.changedList.map(async (t) => { return await Promise.all(ret.info.changedList.map(async (t) => {
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]); const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0) { if (FastMsg.msgList.length > 0) {
//扩展ret.info.changedList //扩展ret.info.changedList
const lastestMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array'); const lastestMsg = await this.OneBotContext.apiContext.MsgApi.parseMessage(FastMsg.msgList[0], 'array');
return { return {
lastestMsg: lastestMsg, lastestMsg: lastestMsg,
peerUin: t.peerUin, peerUin: t.peerUin,
@@ -48,6 +47,5 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
peerName: t.peerName, peerName: t.peerName,
}; };
})); }));
return data;
} }
} }

View File

@@ -1,6 +1,7 @@
import { NapCatCore } from '@/core'; import { GrayTipElement, NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11FriendPokeEvent } from '../event/notice/OB11PokeEvent';
export class OneBotFriendApi { export class OneBotFriendApi {
obContext: NapCatOneBot11Adapter; obContext: NapCatOneBot11Adapter;
@@ -10,4 +11,22 @@ export class OneBotFriendApi {
this.obContext = obContext; this.obContext = obContext;
this.coreContext = coreContext; this.coreContext = coreContext;
} }
//使用前预先判断 busiId 1061
async parsePrivatePokeEvent(grayTipElement: GrayTipElement) {
const NTQQUserApi = this.coreContext.apis.UserApi;
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
let pokedetail: any[] = json.items;
//筛选item带有uid的元素
pokedetail = pokedetail.filter(item => item.uid);
//console.log("[NapCat] 群拍一拍 群:", pokedetail, parseInt(msg.peerUid), " ", await NTQQUserApi.getUinByUid(pokedetail[0].uid), "拍了拍", await NTQQUserApi.getUinByUid(pokedetail[1].uid));
if (pokedetail.length == 2) {
return new OB11FriendPokeEvent(
this.coreContext,
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[1].uid))!),
pokedetail
);
}
return undefined;
}
} }

View File

@@ -1,6 +1,11 @@
import { NapCatCore } from '@/core'; import { ChatType, GrayTipElement, NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent';
import fastXmlParser from 'fast-xml-parser';
import { OB11GroupMsgEmojiLikeEvent } from '../event/notice/OB11MsgEmojiLikeEvent';
import { MessageUnique } from '@/common/utils/MessageUnique';
export class OneBotGroupApi { export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter; obContext: NapCatOneBot11Adapter;
@@ -10,4 +15,133 @@ export class OneBotGroupApi {
this.obContext = obContext; this.obContext = obContext;
this.coreContext = coreContext; this.coreContext = coreContext;
} }
async parseGroupBanEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const groupElement = grayTipElement?.groupElement;
const NTQQGroupApi = this.coreContext.apis.GroupApi;
if (!groupElement?.shutUp) return undefined;
const memberUid = groupElement.shutUp!.member.uid;
const adminUid = groupElement.shutUp!.admin.uid;
let memberUin: string = '';
let duration = parseInt(groupElement.shutUp!.duration);
const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban';
if (memberUid) {
memberUin = (await NTQQGroupApi.getGroupMember(GroupCode, memberUid))?.uin || '';
} else {
memberUin = '0'; // 0表示全员禁言
if (duration > 0) {
duration = -1;
}
}
const adminUin = (await NTQQGroupApi.getGroupMember(GroupCode, adminUid))?.uin;
if (memberUin && adminUin) {
return new OB11GroupBanEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(memberUin),
parseInt(adminUin),
duration,
subType
);
}
return undefined;
}
async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
this.coreContext.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement);
const xmlElement = grayTipElement.xmlElement;
if (xmlElement?.content) {
const regex = /jp="(\d+)"/g;
const matches = [];
let match = null;
while ((match = regex.exec(xmlElement.content)) !== null) {
matches.push(match[1]);
}
// log("新人进群匹配到的QQ号", matches)
if (matches.length === 2) {
const [inviter, invitee] = matches;
return new OB11GroupIncreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(invitee),
parseInt(inviter),
'invite'
);
}
}
return undefined;
}
async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined;
const member = await NTQQGroupApi.getGroupMemberV2(GroupCode, groupElement.memberUid);
const memberUin = member?.uin;
const adminMember = await NTQQGroupApi.getGroupMember(GroupCode, groupElement.adminUid);
if (memberUin) {
const operatorUin = adminMember?.uin || memberUin;
return new OB11GroupIncreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(memberUin),
parseInt(operatorUin)
);
}
return undefined;
}
async parseGroupKickEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const NTQQUserApi = this.coreContext.apis.UserApi;
const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined;
const adminUin = (await NTQQGroupApi.getGroupMember(GroupCode, groupElement.adminUid))?.uin || (await NTQQUserApi.getUidByUinV2(groupElement.adminUid));
if (adminUin) {
return new OB11GroupDecreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(this.coreContext.selfInfo.uin),
parseInt(adminUin),
'kick_me'
);
}
return undefined;
}
async parseGroupEmjioLikeEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const emojiLikeData = new fastXmlParser.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
}).parse(grayTipElement.xmlElement.content);
this.coreContext.context.logger.logDebug('收到表情回应我的消息', emojiLikeData);
try {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
const peer = {
chatType: ChatType.KCHATTYPEGROUP,
guildId: '',
peerUid: GroupCode
};
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, msgSeq)).msgList;
if (replyMsgList.length < 1) {
return;
}
const replyMsg = replyMsgList.filter(e => e.msgSeq == msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
//console.log("表情回应消息长度检测", msgSeq, replyMsg.elements);
if (!replyMsg) throw new Error('找不到回应消息');
return new OB11GroupMsgEmojiLikeEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(senderUin),
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!,
[{
emoji_id: emojiId,
count: 1,
}],
);
} catch (e: any) {
this.coreContext.context.logger.logError('解析表情回应消息失败', e.stack);
}
return undefined;
}
} }

View File

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

468
src/onebot/api/msg.ts Normal file
View File

@@ -0,0 +1,468 @@
import { UUIDConverter } from '@/common/utils/helper';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { AtType, ChatType, FaceIndex, MessageElement, NapCatCore, RawMessage } from '@/core';
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType } from '@/onebot';
import { OB11Constructor } from '../helper';
import { EventType } from '@/onebot/event/OB11BaseEvent';
import { encodeCQCode } from '@/onebot/helper/cqcode';
export class OneBotMsgApi {
obContext: NapCatOneBot11Adapter;
coreContext: NapCatCore;
constructor(obContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
this.obContext = obContext;
this.coreContext = coreContext;
}
async parseMessage(
msg: RawMessage,
messagePostFormat: string = this.obContext.configLoader.configData.messagePostFormat
) {
if (msg.senderUin == "0" || msg.senderUin == "") return;
if (msg.peerUin == "0" || msg.peerUin == "") return;
//跳过空消息
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const NTQQUserApi = this.coreContext.apis.UserApi;
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const resMsg: OB11Message = {
self_id: parseInt(this.coreContext.selfInfo.uin),
user_id: parseInt(msg.senderUin!),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: this.coreContext.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (msg.chatType == ChatType.KCHATTYPEGROUP) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.KCHATTYPEC2C) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
} else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) {
resMsg.sub_type = 'group';
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid);
if (ret.result === 0) {
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
} else {
resMsg.group_id = 284840486; //兜底数据
resMsg.sender.nickname = "临时会话";
}
}
for (const element of msg.elements) {
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
const textAtMsgData = await this.obContext.apiContext.MsgApi.parseTextElemntWithAt(msg, element);
if (textAtMsgData) message_data = textAtMsgData;
} else if (element.textElement) {
const textMsgData = await this.obContext.apiContext.MsgApi.parseTextElement(msg, element);
if (textMsgData) message_data = textMsgData;
} else if (element.replyElement) {
const replyMsgData = await this.obContext.apiContext.MsgApi.parseReplyElement(msg, element);
if (replyMsgData) message_data = replyMsgData;
} else if (element.picElement) {
const PicMsgData = await this.obContext.apiContext.MsgApi.parsePicElement(msg, element);
if (PicMsgData) message_data = PicMsgData;
} else if (element.fileElement) {
const FileMsgData = await this.obContext.apiContext.MsgApi.parseFileElement(msg, element);
if (FileMsgData) message_data = FileMsgData;
} else if (element.videoElement) {
const videoMsgData = await this.obContext.apiContext.MsgApi.parseVideoElement(msg, element);
if (videoMsgData) message_data = videoMsgData;
} else if (element.pttElement) {
const pttMsgData = await this.obContext.apiContext.MsgApi.parsePTTElement(msg, element);
if (pttMsgData) message_data = pttMsgData;
} else if (element.arkElement) {
const arkMsgData = await this.obContext.apiContext.MsgApi.parseArkElement(msg, element);
if (arkMsgData) message_data = arkMsgData;
} else if (element.faceElement) {
const faceMsgData = await this.obContext.apiContext.MsgApi.parseFaceElement(msg, element);
if (faceMsgData) message_data = faceMsgData;
} else if (element.marketFaceElement) {
const marketFaceMsgData = await this.obContext.apiContext.MsgApi.parseMarketFaceElement(msg, element);
if (marketFaceMsgData) message_data = marketFaceMsgData;
} else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown;
message_data['data']['data'] = element.markdownElement.content;
} else if (element.multiForwardMsgElement) {
const multiForwardMsgData = await this.obContext.apiContext.MsgApi.parseMultForwardElement(msg, element, messagePostFormat);
if (multiForwardMsgData) message_data = multiForwardMsgData;
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {
(resMsg.message as string) += cqCode;
} else (resMsg.message as OB11MessageData[]).push(message_data);
resMsg.raw_message += cqCode;
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}
async parseFileElement(msg: RawMessage, element: MessageElement) {
const fileElement = element.fileElement;
if (!fileElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.file;
message_data['data']['file'] = fileElement.fileName;
message_data['data']['path'] = fileElement.filePath;
message_data['data']['url'] = fileElement.filePath;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = fileElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
fileElement.fileSize,
fileElement.fileName
);
return message_data;
}
async parseTextElemntWithAt(msg: RawMessage, element: MessageElement) {
const textElement = element.textElement;
if (!textElement) return undefined;
const NTQQUserApi = this.coreContext.apis.UserApi;
let qq: `${number}` | 'all';
// let name: string | undefined;
if (textElement.atType == AtType.atAll) {
qq = 'all';
} else {
const { atNtUid, content } = textElement;
let atQQ = textElement.atUid;
if (!atQQ || atQQ === '0') {
atQQ = await NTQQUserApi.getUinByUidV2(atNtUid);
}
if (atQQ) {
qq = atQQ as `${number}`;
// name = content.replace('@', '');
}
}
return {
type: OB11MessageDataType.at,
data: {
qq: qq!,
// name,
},
};
}
async parseTextElement(msg: RawMessage, element: MessageElement) {
const textElement = element.textElement;
if (!textElement) return undefined;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.text;
let text = textElement.content;
if (!text.trim()) {
return false;
}
// 兼容 9.7.x 换行符
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
text = text.replace(/\r/g, '\n');
}
message_data['data']['text'] = text;
return message_data;
}
async parsePicElement(msg: RawMessage, element: MessageElement) {
const picElement = element.picElement;
if (!picElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.image;
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = picElement.fileName;
message_data['data']['subType'] = picElement.picSubType;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
// message_data["data"]["path"] = element.picElement.sourcePath
try {
message_data['data']['url'] = await NTQQFileApi.getImageUrl(picElement);
} catch (e: any) {
this.coreContext.context.logger.logError('获取图片url失败', e.stack);
}
//console.log(message_data['data']['url'])
// message_data["data"]["file_id"] = element.picElement.fileUuid
message_data['data']['file_size'] = picElement.fileSize;
return message_data;
}
async parseMarketFaceElement(msg: RawMessage, element: MessageElement) {
const NTQQFileApi = this.coreContext.apis.FileApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.image;
message_data['data']['file'] = 'marketface';
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['path'] = element.elementId;
message_data['data']['url'] = element.elementId;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
'0',
'marketface'
);
return message_data;
}
async parseReplyElement(msg: RawMessage, element: MessageElement) {
const replyElement = element.replyElement;
if (!replyElement) return undefined;
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement);
try {
const records = msg.records.find(msgRecord => msgRecord.msgId === replyElement?.sourceMsgIdInRecords);
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
let replyMsg: RawMessage | undefined;
if (!records) throw new Error('找不到回复消息');
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom);
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, replyElement.replayMsgSeq)).msgList[0];
}
if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到
}
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) {
throw new Error('回复消息消息验证失败');
}
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
}
message_data['data']['id'] = MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId)?.toString();
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
} catch (e: any) {
message_data['type'] = 'unknown' as any;
message_data['data'] = undefined;
this.coreContext.context.logger.logError('获取不到引用的消息', e.stack, replyElement.replayMsgSeq);
return undefined;
}
return message_data;
}
async parseVideoElement(msg: RawMessage, element: MessageElement) {
const videoElement = element.videoElement;
if (!videoElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
//读取视频链接并兜底
let videoUrl; //Array
if (msg.peerUin === '284840486') {
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
}
try {
videoUrl = await NTQQFileApi.getVideoUrl({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '0',
}, msg.msgId, element.elementId);
} catch (error) {
videoUrl = undefined;
}
//读取在线URL
let videoDownUrl = undefined;
if (videoUrl) {
const videoDownUrlTemp = videoUrl.find((url) => {
return !!url.url;
});
if (videoDownUrlTemp) {
videoDownUrl = videoDownUrlTemp.url;
}
}
//开始兜底
if (!videoDownUrl) {
videoDownUrl = videoElement.filePath;
}
message_data['type'] = OB11MessageDataType.video;
message_data['data']['file'] = videoElement.fileName;
message_data['data']['path'] = videoDownUrl;
message_data['data']['url'] = videoDownUrl;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = videoElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName
);
return message_data;
}
async parsePTTElement(msg: RawMessage, element: MessageElement) {
const pttElement = element.pttElement;
if (!pttElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.voice;
message_data['data']['file'] = pttElement.fileName;
message_data['data']['path'] = pttElement.filePath;
//message_data['data']['file_id'] = element.pttElement.fileUuid;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = pttElement.fileSize;
await NTQQFileApi.addFileCache({
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
pttElement.fileSize || '0',
pttElement.fileUuid || ''
);
//以uuid作为文件名
return message_data;
}
async parseFaceElement(msg: RawMessage, element: MessageElement) {
const faceElement = element.faceElement;
if (!faceElement) return undefined;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
const faceId = faceElement.faceIndex;
if (faceId === FaceIndex.dice) {
message_data['type'] = OB11MessageDataType.dice;
message_data['data']['result'] = faceElement.resultId;
} else if (faceId === FaceIndex.RPS) {
message_data['type'] = OB11MessageDataType.RPS;
message_data['data']['result'] = faceElement.resultId;
} else {
message_data['type'] = OB11MessageDataType.face;
message_data['data']['id'] = faceElement.faceIndex.toString();
}
return message_data;
}
async parseMultForwardElement(msg: RawMessage, element: MessageElement, messagePostFormat: any) {
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const faceElement = element.multiForwardMsgElement;
if (!faceElement) return undefined;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
const ParentMsgPeer = msg.parentMsgPeer ?? {
chatType: msg.chatType,
guildId: '',
peerUid: msg.peerUid,
};
//判断是否在合并消息内
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
//首次列表不存在则开始创建
msg.parentMsgIdList.push(msg.msgId);
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
//加入自身MsgId
const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
//拉取下级消息
if (!MultiMsgs) return undefined;
//拉取失败则跳过
message_data['data']['content'] = [];
for (const MultiMsg of MultiMsgs) {
//对每条拉取的消息传递ParentMsgPeer修正Peer
MultiMsg.parentMsgPeer = ParentMsgPeer;
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId); //该ID仅用查看 无法调用
const msgList = await this.parseMessage(MultiMsg, messagePostFormat);
if (!msgList) continue;
message_data['data']['content'].push(msgList);
//console.log("合并消息", msgList);
}
return message_data;
}
async parseArkElement(msg: RawMessage, element: MessageElement) {
const arkElement = element.arkElement;
if (!arkElement) return undefined;
const message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.json;
message_data['data']['data'] = arkElement.bytesData;
return message_data;
}
}

View File

@@ -0,0 +1,113 @@
import { calcQQLevel } from '@/common/utils/helper';
import { SelfInfo, FriendV2, Friend, Sex, GroupMember, User, Group } from '@/core';
import { OB11User, OB11GroupMemberRole, OB11UserSex, OB11GroupMember, OB11Group } from '../types';
export class OB11Constructor {
static selfInfo(selfInfo: SelfInfo): OB11User {
return {
user_id: parseInt(selfInfo.uin),
nickname: selfInfo.nick,
};
}
static friendsV2(friends: FriendV2[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.baseInfo.sex!);
data.push({
...friend.baseInfo,
...friend.coreInfo,
user_id: parseInt(friend.coreInfo.uin),
nickname: friend.coreInfo.nick,
remark: friend.coreInfo.nick,
sex: sexValue,
level: 0,
});
});
return data;
}
static friends(friends: Friend[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.sex!);
data.push({
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: sexValue,
level: 0,
});
});
return data;
}
static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
return {
4: OB11GroupMemberRole.owner,
3: OB11GroupMemberRole.admin,
2: OB11GroupMemberRole.member,
}[role];
}
static sex(sex: Sex): OB11UserSex {
const sexMap = {
[Sex.male]: OB11UserSex.male,
[Sex.female]: OB11UserSex.female,
[Sex.unknown]: OB11UserSex.unknown,
};
return sexMap[sex] || OB11UserSex.unknown;
}
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
return {
group_id: parseInt(group_id),
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName,
sex: OB11Constructor.sex(member.sex!),
age: member.age ?? 0,
area: '',
level: '0',
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
join_time: 0, // 暂时没法获取
last_sent_time: 0, // 暂时没法获取
title_expire_time: 0,
unfriendly: false,
card_changeable: true,
is_robot: member.isRobot,
shut_up_timestamp: member.shutUpTime,
role: OB11Constructor.groupMemberRole(member.role),
title: member.memberSpecialTitle || '',
};
}
static stranger(user: User): OB11User {
//logDebug('construct ob11 stranger', user);
return {
...user,
user_id: parseInt(user.uin),
nickname: user.nick,
sex: OB11Constructor.sex(user.sex!),
age: 0,
qid: user.qid,
login_days: 0,
level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
};
}
static group(group: Group): OB11Group {
return {
group_id: parseInt(group.groupCode),
group_name: group.groupName,
member_count: group.memberCount,
max_member_count: group.maxMember,
};
}
static groups(groups: Group[]): OB11Group[] {
return groups.map(OB11Constructor.group);
}
}

View File

@@ -79,8 +79,4 @@ export function encodeCQCode(data: OB11MessageData) {
} }
result += ']'; result += ']';
return result; return result;
} }
// const result = parseCQCode("[CQ:at,qq=114514]早上好啊[CQ:image,file=http://baidu.com/1.jpg,type=show,id=40004]")
// const result = parseCQCode("好好好")
// console.log(JSON.stringify(result))

View File

@@ -1,759 +0,0 @@
import fastXmlParser from 'fast-xml-parser';
import {
OB11Group,
OB11GroupMember,
OB11GroupMemberRole,
OB11Message,
OB11MessageData,
OB11MessageDataType,
OB11User,
OB11UserSex,
} from '../types';
import {
AtType,
ChatType,
FaceIndex,
Friend,
FriendV2,
Group,
GroupMember,
NTGrayTipElementSubTypeV2,
Peer,
RawMessage,
SelfInfo,
Sex,
TipGroupElementType,
User,
VideoElement,
} from '@/core/entities';
import { EventType } from '../event/OB11BaseEvent';
import { encodeCQCode } from './cqcode';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent';
import { OB11GroupCardEvent } from '../event/notice/OB11GroupCardEvent';
import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent';
import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent';
import { calcQQLevel, sleep, UUIDConverter } from '@/common/utils/helper';
import { OB11GroupTitleEvent } from '../event/notice/OB11GroupTitleEvent';
import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent';
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent';
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from '../event/notice/OB11PokeEvent';
import { OB11FriendAddNoticeEvent } from '../event/notice/OB11FriendAddNoticeEvent';
import { OB11BaseNoticeEvent } from '../event/notice/OB11BaseNoticeEvent';
import { OB11GroupEssenceEvent } from '../event/notice/OB11GroupEssenceEvent';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '..';
export class OB11Constructor {
static async message(
core: NapCatCore,
obcore: NapCatOneBot11Adapter,
msg: RawMessage,
messagePostFormat: string = obcore.configLoader.configData.messagePostFormat
): Promise<OB11Message | undefined> {
if (msg.senderUin == "0" || msg.senderUin == "") return;
if (msg.peerUin == "0" || msg.peerUin == "") return;
//跳过空消息
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQFileApi = core.apis.FileApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
const resMsg: OB11Message = {
self_id: parseInt(core.selfInfo.uin),
user_id: parseInt(msg.senderUin!),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.group ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (msg.chatType == ChatType.group) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
//直接去QQNative取
let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.friend) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
//const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!);
//resMsg.sender.nickname = user.info.nick;
} else if (msg.chatType == ChatType.temp) {
resMsg.sub_type = 'group';
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.temp, msg.senderUid);
if (ret.result === 0) {
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
} else {
resMsg.group_id = 284840486;//兜底数据
resMsg.sender.nickname = "临时会话";
}
}
for (const element of msg.elements) {
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
let qq: `${number}` | 'all';
let name: string | undefined;
if (element.textElement.atType == AtType.atAll) {
qq = 'all';
} else {
const { atNtUid, content } = element.textElement;
let atQQ = element.textElement.atUid;
if (!atQQ || atQQ === '0') {
atQQ = await NTQQUserApi.getUinByUidV2(atNtUid);
}
if (atQQ) {
qq = atQQ as `${number}`;
name = content.replace('@', '');
}
}
message_data = {
type: OB11MessageDataType.at,
data: {
qq: qq!,
name,
},
};
} else if (element.textElement) {
message_data['type'] = OB11MessageDataType.text;
let text = element.textElement.content;
if (!text.trim()) {
continue;
}
// 兼容 9.7.x 换行符
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
text = text.replace(/\r/g, '\n');
}
message_data['data']['text'] = text;
} else if (element.replyElement) {
message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement);
try {
const records = msg.records.find(msgRecord => msgRecord.msgId === element?.replyElement?.sourceMsgIdInRecords);
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
let replyMsg: RawMessage | undefined;
if (!records) throw new Error('找不到回复消息');
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, element.replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom);
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
}
if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到
}
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) {
throw new Error('回复消息消息验证失败');
}
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
}
message_data['data']['id'] = MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId)?.toString();
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
} catch (e: any) {
message_data['type'] = 'unknown' as any;
message_data['data'] = undefined;
logger.logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq);
}
} else if (element.picElement) {
message_data['type'] = OB11MessageDataType.image;
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = element.picElement.fileName;
message_data['data']['subType'] = element.picElement.picSubType;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
// message_data["data"]["path"] = element.picElement.sourcePath
try {
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement);
} catch (e: any) {
logger.logError('获取图片url失败', e.stack);
}
//console.log(message_data['data']['url'])
// message_data["data"]["file_id"] = element.picElement.fileUuid
message_data['data']['file_size'] = element.picElement.fileSize;
} else if (element.fileElement) {
const FileElement = element.fileElement;
message_data['type'] = OB11MessageDataType.file;
message_data['data']['file'] = FileElement.fileName;
message_data['data']['path'] = FileElement.filePath;
message_data['data']['url'] = FileElement.filePath;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = FileElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
FileElement.fileSize,
FileElement.fileName,
);
} else if (element.videoElement) {
const videoElement: VideoElement = element.videoElement;
//读取视频链接并兜底
let videoUrl;//Array
if (msg.peerUin === '284840486') {
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
}
try {
videoUrl = await NTQQFileApi.getVideoUrl({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '0',
}, msg.msgId, element.elementId);
} catch (error) {
videoUrl = undefined;
}
//读取在线URL
let videoDownUrl = undefined;
if (videoUrl) {
const videoDownUrlTemp = videoUrl.find((url) => {
return !!url.url;
});
if (videoDownUrlTemp) {
videoDownUrl = videoDownUrlTemp.url;
}
}
//开始兜底
if (!videoDownUrl) {
videoDownUrl = videoElement.filePath;
}
message_data['type'] = OB11MessageDataType.video;
message_data['data']['file'] = videoElement.fileName;
message_data['data']['path'] = videoDownUrl;
message_data['data']['url'] = videoDownUrl;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = videoElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName,
);
} else if (element.pttElement) {
message_data['type'] = OB11MessageDataType.voice;
message_data['data']['file'] = element.pttElement.fileName;
message_data['data']['path'] = element.pttElement.filePath;
//message_data['data']['file_id'] = element.pttElement.fileUuid;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = element.pttElement.fileSize;
await NTQQFileApi.addFileCache({
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
element.pttElement.fileSize || '0',
element.pttElement.fileUuid || '',
);
//以uuid作为文件名
} else if (element.arkElement) {
message_data['type'] = OB11MessageDataType.json;
message_data['data']['data'] = element.arkElement.bytesData;
} else if (element.faceElement) {
const faceId = element.faceElement.faceIndex;
if (faceId === FaceIndex.dice) {
message_data['type'] = OB11MessageDataType.dice;
message_data['data']['result'] = element.faceElement.resultId;
} else if (faceId === FaceIndex.RPS) {
message_data['type'] = OB11MessageDataType.RPS;
message_data['data']['result'] = element.faceElement.resultId;
} else {
message_data['type'] = OB11MessageDataType.face;
message_data['data']['id'] = element.faceElement.faceIndex.toString();
}
} else if (element.marketFaceElement) {
message_data['type'] = OB11MessageDataType.mface;
message_data['data']['summary'] = element.marketFaceElement.faceName;
const md5 = element.marketFaceElement.emojiId;
// 取md5的前两位
const dir = md5.substring(0, 2);
// 获取组装url
// const url = `https://p.qpic.cn/CDN_STATIC/0/data/imgcache/htdocs/club/item/parcel/item/${dir}/${md5}/300x300.gif?max_age=31536000`;
message_data['data']['url'] = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${md5}/raw300.gif`;
message_data['data']['emoji_id'] = element.marketFaceElement.emojiId;
message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId);
message_data['data']['key'] = element.marketFaceElement.key;
//mFaceCache.set(md5, element.marketFaceElement.faceName);
} else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown;
message_data['data']['data'] = element.markdownElement.content;
} else if (element.multiForwardMsgElement) {
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
const ParentMsgPeer = msg.parentMsgPeer ?? {
chatType: msg.chatType,
guildId: '',
peerUid: msg.peerUid,
};
//判断是否在合并消息内
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
//首次列表不存在则开始创建
msg.parentMsgIdList.push(msg.msgId);
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
//加入自身MsgId
const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
//拉取下级消息
if (!MultiMsgs) continue;
//拉取失败则跳过
message_data['data']['content'] = [];
for (const MultiMsg of MultiMsgs) {
//对每条拉取的消息传递ParentMsgPeer修正Peer
MultiMsg.parentMsgPeer = ParentMsgPeer;
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId);//该ID仅用查看 无法调用
const msgList = await OB11Constructor.message(core, obcore, MultiMsg, messagePostFormat);
if (!msgList) continue;
message_data['data']['content'].push(msgList);
//console.log("合并消息", msgList);
}
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {
(resMsg.message as string) += cqCode;
} else (resMsg.message as OB11MessageData[]).push(message_data);
resMsg.raw_message += cqCode;
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}
static async PrivateEvent(core: NapCatCore, msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
const NTQQUserApi = core.apis.UserApi;
if (msg.chatType !== ChatType.friend) {
return;
}
for (const element of msg.elements) {
if (element.grayTipElement) {
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
let pokedetail: any[] = json.items;
//筛选item带有uid的元素
pokedetail = pokedetail.filter(item => item.uid);
//console.log("[NapCat] 群拍一拍 群:", pokedetail, parseInt(msg.peerUid), " ", await NTQQUserApi.getUinByUid(pokedetail[0].uid), "拍了拍", await NTQQUserApi.getUinByUid(pokedetail[1].uid));
if (pokedetail.length == 2) {
return new OB11FriendPokeEvent(
core,
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[1].uid))!),
pokedetail
);
}
}
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
//好友添加成功事件
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') {
return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin));
}
}
}
}
}
static async GroupEvent(core: NapCatCore, msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
if (msg.chatType !== ChatType.group) {
return;
}
//log("group msg", msg);
if (msg.senderUin && msg.senderUin !== '0') {
const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName || '';
const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
member.cardName = newCardName;
return event;
}
}
for (const element of msg.elements) {
const grayTipElement = element.grayTipElement;
const groupElement = grayTipElement?.groupElement;
if (groupElement) {
// log("收到群提示消息", groupElement)
if (groupElement.type == TipGroupElementType.memberIncrease) {
logger.logDebug('收到群成员增加消息', groupElement);
await sleep(1000);
const member = await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.memberUid);
const memberUin = member?.uin;
// if (!memberUin) {
// memberUin = (await NTQQUserApi.getUserDetailInfo(groupElement.memberUid)).uin
// }
// log("获取新群成员QQ", memberUin)
const adminMember = await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.adminUid);
// log("获取同意新成员入群的管理员", adminMember)
if (memberUin) {
const operatorUin = adminMember?.uin || memberUin;
// log("构造群增加事件", event)
return new OB11GroupIncreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
parseInt(operatorUin)
);
}
} else if (groupElement.type === TipGroupElementType.ban) {
logger.logDebug('收到群群员禁言提示', groupElement);
const memberUid = groupElement.shutUp!.member.uid;
const adminUid = groupElement.shutUp!.admin.uid;
let memberUin: string = '';
let duration = parseInt(groupElement.shutUp!.duration);
const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban';
// log('OB11被禁言事件', adminUid);
if (memberUid) {
memberUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, memberUid))?.uin || ''; // || (await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
} else {
memberUin = '0'; // 0表示全员禁言
if (duration > 0) {
duration = -1;
}
}
const adminUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, adminUid))?.uin; // || (await NTQQUserApi.getUserDetailInfo(adminUid))?.uin
// log('OB11被禁言事件', memberUin, adminUin, duration, subType);
if (memberUin && adminUin) {
return new OB11GroupBanEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
parseInt(adminUin),
duration,
subType
);
}
} else if (groupElement.type == TipGroupElementType.kicked) {
logger.logDebug(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement);
NTQQGroupApi.quitGroup(msg.peerUid).then();
try {
const adminUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || (await NTQQUserApi.getUidByUinV2(groupElement.adminUid));
if (adminUin) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
parseInt(adminUin),
'kick_me'
);
}
} catch (e) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
0,
'leave'
);
}
}
} else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(
core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''),
{
id: element.fileElement.fileUuid!,
name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0,
}
);
}
if (grayTipElement) {
//console.log('收到群提示消息', grayTipElement);
if (grayTipElement.xmlElement?.templId === '10382') {
const emojiLikeData = new fastXmlParser.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
}).parse(grayTipElement.xmlElement.content);
logger.logDebug('收到表情回应我的消息', emojiLikeData);
try {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
const peer = {
chatType: ChatType.group,
guildId: '',
peerUid: msg.peerUid
}
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, msgSeq)).msgList;
if (replyMsgList.length < 1) {
return;
}
const replyMsg = replyMsgList.filter(e => e.msgSeq == msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
//console.log("表情回应消息长度检测", msgSeq, replyMsg.elements);
if (!replyMsg) throw new Error('找不到回应消息');
return new OB11GroupMsgEmojiLikeEvent(
core,
parseInt(msg.peerUid),
parseInt(senderUin),
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!,
[{
emoji_id: emojiId,
count: 1,
}],
);
} catch (e: any) {
logger.logError('解析表情回应消息失败', e.stack);
}
}
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
logger.logDebug('收到新人被邀请进群消息', grayTipElement);
const xmlElement = grayTipElement.xmlElement;
if (xmlElement?.content) {
const regex = /jp="(\d+)"/g;
const matches = [];
let match = null;
while ((match = regex.exec(xmlElement.content)) !== null) {
matches.push(match[1]);
}
// log("新人进群匹配到的QQ号", matches)
if (matches.length === 2) {
const [inviter, invitee] = matches;
return new OB11GroupIncreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(invitee),
parseInt(inviter),
'invite'
);
}
}
}
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
else if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: any[] = json.items;
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid);
if (poke_uid.length == 2) {
return new OB11GroupPokeEvent(
core,
parseInt(msg.peerUid),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!),
pokedetail
);
}
}
if (grayTipElement.jsonGrayTipElement.busiId == 2401) {
const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode');
// const businessId = searchParams.get('businessid');
const Peer: Peer = {
guildId: '',
chatType: ChatType.group,
peerUid: Group!,
};
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
return new OB11GroupEssenceEvent(
core,
parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!,
parseInt(msgData.msgList[0].senderUin)
);
// 获取MsgSeq+Peer可获取具体消息
}
if (grayTipElement.jsonGrayTipElement.busiId == 2407) {
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
const memberUin = json.items[1].param[0];
const title = json.items[3].txt;
logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
title
);
}
}
}
}
}
static friend(friend: User): OB11User {
return {
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: OB11Constructor.sex(friend.sex!),
level: friend.qqLevel && calcQQLevel(friend.qqLevel) || 0,
};
}
static selfInfo(selfInfo: SelfInfo): OB11User {
return {
user_id: parseInt(selfInfo.uin),
nickname: selfInfo.nick,
};
}
static friendsV2(friends: FriendV2[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.baseInfo.sex!);
data.push({
...friend.baseInfo,
...friend.coreInfo,
user_id: parseInt(friend.coreInfo.uin),
nickname: friend.coreInfo.nick,
remark: friend.coreInfo.nick,
sex: sexValue,
level: 0,
});
});
return data;
}
static friends(friends: Friend[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.sex!);
data.push({
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: sexValue,
level: 0,
});
});
return data;
}
static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
return {
4: OB11GroupMemberRole.owner,
3: OB11GroupMemberRole.admin,
2: OB11GroupMemberRole.member,
}[role];
}
static sex(sex: Sex): OB11UserSex {
const sexMap = {
[Sex.male]: OB11UserSex.male,
[Sex.female]: OB11UserSex.female,
[Sex.unknown]: OB11UserSex.unknown,
};
return sexMap[sex] || OB11UserSex.unknown;
}
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
return {
group_id: parseInt(group_id),
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName,
sex: OB11Constructor.sex(member.sex!),
age: member.age ?? 0,
area: '',
level: '0',
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
join_time: 0, // 暂时没法获取
last_sent_time: 0, // 暂时没法获取
title_expire_time: 0,
unfriendly: false,
card_changeable: true,
is_robot: member.isRobot,
shut_up_timestamp: member.shutUpTime,
role: OB11Constructor.groupMemberRole(member.role),
title: member.memberSpecialTitle || '',
};
}
static stranger(user: User): OB11User {
//logDebug('construct ob11 stranger', user);
return {
...user,
user_id: parseInt(user.uin),
nickname: user.nick,
sex: OB11Constructor.sex(user.sex!),
age: 0,
qid: user.qid,
login_days: 0,
level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
};
}
static group(group: Group): OB11Group {
return {
group_id: parseInt(group.groupCode),
group_name: group.groupName,
member_count: group.memberCount,
max_member_count: group.maxMember,
};
}
static groups(groups: Group[]): OB11Group[] {
return groups.map(OB11Constructor.group);
}
}

154
src/onebot/helper/event.ts Normal file
View File

@@ -0,0 +1,154 @@
import { NapCatOneBot11Adapter } from "..";
import { OB11BaseNoticeEvent } from "../event/notice/OB11BaseNoticeEvent";
import { OB11FriendAddNoticeEvent } from "../event/notice/OB11FriendAddNoticeEvent";
import { OB11GroupNoticeEvent } from "../event/notice/OB11GroupNoticeEvent";
import { OB11GroupCardEvent } from "../event/notice/OB11GroupCardEvent";
import { OB11GroupDecreaseEvent } from "../event/notice/OB11GroupDecreaseEvent";
import { OB11GroupUploadNoticeEvent } from "../event/notice/OB11GroupUploadNoticeEvent";
import { OB11GroupPokeEvent } from "../event/notice/OB11PokeEvent";
import { OB11GroupEssenceEvent } from "../event/notice/OB11GroupEssenceEvent";
import { MessageUnique } from "@/common/utils/MessageUnique";
import { OB11GroupTitleEvent } from "../event/notice/OB11GroupTitleEvent";
import { NapCatCore, RawMessage, ChatType, NTGrayTipElementSubTypeV2, TipGroupElementType, Peer } from '@/core';
export async function NT2PrivateEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
if (msg.chatType !== ChatType.KCHATTYPEC2C) {
return;
}
for (const element of msg.elements) {
if (element.grayTipElement) {
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
const PokeEvent = await obContext.apiContext.FriendApi.parsePrivatePokeEvent(element.grayTipElement);
if (PokeEvent) return PokeEvent;
}
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
//好友添加成功事件
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') {
return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin));
}
}
}
}
}
export async function NT2GroupEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
if (msg.chatType !== ChatType.KCHATTYPEGROUP) {
return;
}
//log("group msg", msg);
if (msg.senderUin && msg.senderUin !== '0') {
const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName || '';
const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
member.cardName = newCardName;
return event;
}
}
for (const element of msg.elements) {
if (element.grayTipElement && element.grayTipElement.groupElement) {
const groupElement = element.grayTipElement.groupElement;
if (groupElement.type == TipGroupElementType.memberIncrease) {
const MemberIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement);
if (MemberIncreaseEvent) return MemberIncreaseEvent;
} else if (groupElement.type === TipGroupElementType.ban) {
const BanEvent = await obContext.apiContext.GroupApi.parseGroupBanEvent(msg.peerUid, element.grayTipElement);
if (BanEvent) return BanEvent;
} else if (groupElement.type == TipGroupElementType.kicked) {
NTQQGroupApi.quitGroup(msg.peerUid).then();
try {
const KickEvent = await obContext.apiContext.GroupApi.parseGroupKickEvent(msg.peerUid, element.grayTipElement);
if (KickEvent) return KickEvent;
} catch (e) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
0,
'leave'
);
}
}
} else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(
core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''),
{
id: element.fileElement.fileUuid!,
name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0,
}
);
}
if (element.grayTipElement) {
if (element.grayTipElement.xmlElement?.templId === '10382') {
const emojiLikeEvent = await obContext.apiContext.GroupApi.parseGroupEmjioLikeEvent(msg.peerUid, element.grayTipElement);
if (emojiLikeEvent) return emojiLikeEvent;
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
const GroupIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupIncreaseEvent(msg.peerUid, element.grayTipElement);
if (GroupIncreaseEvent) return GroupIncreaseEvent;
}
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
else if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: any[] = json.items;
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid);
if (poke_uid.length == 2) {
return new OB11GroupPokeEvent(
core,
parseInt(msg.peerUid),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!),
pokedetail
);
}
}
if (element.grayTipElement.jsonGrayTipElement.busiId == 2401) {
const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode');
// const businessId = searchParams.get('businessid');
const Peer: Peer = {
guildId: '',
chatType: ChatType.KCHATTYPEGROUP,
peerUid: Group!,
};
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
return new OB11GroupEssenceEvent(
core,
parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!,
parseInt(msgData.msgList[0].senderUin)
);
// 获取MsgSeq+Peer可获取具体消息
}
if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) {
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
const memberUin = json.items[1].param[0];
const title = json.items[3].txt;
logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
title
);
}
}
}
}
}

View File

@@ -0,0 +1,5 @@
export * from './config';
export * from './converter';
export * from './quick';
export * from './genMessage';
export * from './event';

View File

@@ -20,15 +20,15 @@ async function handleMsg(coreContext: NapCatCore, obContext: NapCatOneBot11Adapt
msg = msg as OB11Message; msg = msg as OB11Message;
const reply = quickAction.reply; const reply = quickAction.reply;
const peer: Peer = { const peer: Peer = {
chatType: ChatType.friend, chatType: ChatType.KCHATTYPEC2C,
peerUid: await coreContext.apis.UserApi.getUidByUinV2(msg.user_id.toString()) as string, peerUid: await coreContext.apis.UserApi.getUidByUinV2(msg.user_id.toString()) as string,
}; };
if (msg.message_type == 'private') { if (msg.message_type == 'private') {
if (msg.sub_type === 'group') { if (msg.sub_type === 'group') {
peer.chatType = ChatType.temp; peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
} }
} else { } else {
peer.chatType = ChatType.group; peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
peer.peerUid = msg.group_id!.toString(); peer.peerUid = msg.group_id!.toString();
} }
if (reply) { if (reply) {

View File

@@ -3,13 +3,14 @@ import {
BuddyReqType, BuddyReqType,
ChatType, ChatType,
GroupListener, GroupListener,
GroupNotifyTypes,
InstanceContext, InstanceContext,
MsgListener, MsgListener,
NapCatCore, NapCatCore,
RawMessage, RawMessage,
SendStatusType, SendStatusType,
GroupMemberRole, GroupMemberRole,
GroupNotifyMsgType,
GroupNotifyMsgStatus,
} from '@/core'; } from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config'; import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
import { OneBotApiContextType } from '@/onebot/types'; import { OneBotApiContextType } from '@/onebot/types';
@@ -21,12 +22,11 @@ import {
OB11PassiveWebSocketAdapter, OB11PassiveWebSocketAdapter,
} from '@/onebot/network'; } from '@/onebot/network';
import { NapCatPathWrapper } from '@/common/framework/napcat'; import { NapCatPathWrapper } from '@/common/framework/napcat';
import { OneBotFriendApi, OneBotGroupApi, OneBotUserApi } from '@/onebot/api'; import { OneBotFriendApi, OneBotGroupApi, OneBotMsgApi, OneBotUserApi } from '@/onebot/api';
import { ActionMap, createActionMap } from '@/onebot/action'; import { ActionMap, createActionMap } from '@/onebot/action';
import { WebUiDataRuntime } from '@/webui/src/helper/Data'; import { WebUiDataRuntime } from '@/webui/src/helper/Data';
import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent'; import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent';
import { MessageUnique } from '@/common/utils/MessageUnique'; import { MessageUnique } from '@/common/utils/MessageUnique';
import { OB11Constructor } from '@/onebot/helper/data';
import { proxiedListenerOf } from '@/common/utils/proxy-handler'; import { proxiedListenerOf } from '@/common/utils/proxy-handler';
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest'; import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent'; import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent';
@@ -35,6 +35,7 @@ import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent'; import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent'; import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
import { LRUCache } from '@/common/utils/LRU'; import { LRUCache } from '@/common/utils/LRU';
import { NT2GroupEvent, NT2PrivateEvent } from './helper';
//OneBot实现类 //OneBot实现类
export class NapCatOneBot11Adapter { export class NapCatOneBot11Adapter {
@@ -56,6 +57,7 @@ export class NapCatOneBot11Adapter {
GroupApi: new OneBotGroupApi(this, core), GroupApi: new OneBotGroupApi(this, core),
UserApi: new OneBotUserApi(this, core), UserApi: new OneBotUserApi(this, core),
FriendApi: new OneBotFriendApi(this, core), FriendApi: new OneBotFriendApi(this, core),
MsgApi: new OneBotMsgApi(this, core),
}; };
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
@@ -247,15 +249,16 @@ export class NapCatOneBot11Adapter {
} }
}; };
const msgIdSend = new LRUCache<string, boolean>(100); const msgIdSend = new LRUCache<string, boolean>(100);
const recallMsgs = new LRUCache<string, boolean>(100);
msgListener.onMsgInfoListUpdate = async msgList => { msgListener.onMsgInfoListUpdate = async msgList => {
this.emitRecallMsg(msgList) this.emitRecallMsg(msgList, recallMsgs)
.catch(e => this.context.logger.logError('处理消息失败', e)); .catch(e => this.context.logger.logError('处理消息失败', e));
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) { for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
msgIdSend.put(msg.msgId, true); msgIdSend.put(msg.msgId, true);
// 完成后再post // 完成后再post
OB11Constructor.message(this.core, this, msg) this.apiContext.MsgApi.parseMessage(msg)
.then((ob11Msg) => { .then((ob11Msg) => {
if (!ob11Msg) return; if (!ob11Msg) return;
ob11Msg.target_id = parseInt(msg.peerUin); ob11Msg.target_id = parseInt(msg.peerUin);
@@ -275,15 +278,23 @@ export class NapCatOneBot11Adapter {
}; };
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)), proxiedListenerOf(msgListener, this.context.logger) as any
); );
} }
private initBuddyListener() { private initBuddyListener() {
const buddyListener = new BuddyListener(); const buddyListener = new BuddyListener();
buddyListener.onBuddyReqChange = reqs => { buddyListener.onBuddyReqChange = async reqs => {
reqs.buddyReqs.forEach(async req => { this.core.apis.FriendApi.clearBuddyReqUnreadCnt();
for (let i = 0; i < reqs.unreadNums; i++) {
const req = reqs.buddyReqs[i];
//req.isBuddy === false是单向好友 null为常规情况
// if (req.isBuddy === false && ) {
// const NTQQFriendApi = this.core.apis.FriendApi;
// await NTQQFriendApi.handleFriendRequest(req.friendUid + '|' + req.reqTime, true);
// }
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
return; return;
} }
@@ -298,11 +309,11 @@ export class NapCatOneBot11Adapter {
} catch (e) { } catch (e) {
this.context.logger.logDebug('获取加好友者QQ号失败', e); this.context.logger.logDebug('获取加好友者QQ号失败', e);
} }
}); }
}; };
this.context.session.getBuddyService().addKernelBuddyListener( this.context.session.getBuddyService().addKernelBuddyListener(
new this.context.wrapper.NodeIKernelBuddyListener(proxiedListenerOf(buddyListener, this.context.logger)), proxiedListenerOf(buddyListener, this.context.logger) as any
); );
} }
@@ -312,12 +323,11 @@ export class NapCatOneBot11Adapter {
groupListener.onGroupNotifiesUpdated = async (_, notifies) => { groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]); //console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
if (![ if (![
GroupNotifyTypes.ADMIN_SET, GroupNotifyMsgType.SET_ADMIN,
GroupNotifyTypes.ADMIN_UNSET, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyTypes.ADMIN_UNSET_OTHER, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notifies[0]?.type)) { ].includes(notifies[0]?.type)) {
for (const notify of notifies) { for (const notify of notifies) {
notify.time = Date.now();
const notifyTime = parseInt(notify.seq) / 1000 / 1000; const notifyTime = parseInt(notify.seq) / 1000 / 1000;
// log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`); // log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`);
if (notifyTime < this.bootTime) { if (notifyTime < this.bootTime) {
@@ -328,9 +338,9 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('收到群通知', notify); this.context.logger.logDebug('收到群通知', notify);
if ([ if ([
GroupNotifyTypes.ADMIN_SET, GroupNotifyMsgType.SET_ADMIN,
GroupNotifyTypes.ADMIN_UNSET, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyTypes.ADMIN_UNSET_OTHER, GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notify.type)) { ].includes(notify.type)) {
const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid); const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid);
this.context.logger.logDebug('有管理员变动通知'); this.context.logger.logDebug('有管理员变动通知');
@@ -339,20 +349,21 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('开始获取变动的管理员'); this.context.logger.logDebug('开始获取变动的管理员');
if (member1) { if (member1) {
this.context.logger.logDebug('变动管理员获取成功'); this.context.logger.logDebug('变动管理员获取成功');
// member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent( const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core, this.core,
parseInt(notify.group.groupCode), parseInt(notify.group.groupCode),
parseInt(member1.uin), parseInt(member1.uin),
[GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(notify.type) ? 'unset' : 'set', [
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN
].includes(notify.type) ? 'unset' : 'set',
); );
this.networkManager.emitEvent(groupAdminNoticeEvent) this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e)); .catch(e => this.context.logger.logError('处理群管理员变动失败', e));
} else { } else {
this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode)); this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode));
} }
} else if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) { } else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) {
this.context.logger.logDebug('有成员退出通知', notify); this.context.logger.logDebug('有成员退出通知', notify);
const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
let operatorId = member1Uin; let operatorId = member1Uin;
@@ -376,8 +387,8 @@ export class NapCatOneBot11Adapter {
.catch(e => this.context.logger.logError('处理群成员退出失败', e)); .catch(e => this.context.logger.logError('处理群成员退出失败', e));
// notify.status == 1 表示未处理 2表示处理完成 // notify.status == 1 表示未处理 2表示处理完成
} else if ([ } else if ([
GroupNotifyTypes.JOIN_REQUEST, GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
].includes(notify.type) && notify.status == 1) { ].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug('有加群请求'); this.context.logger.logDebug('有加群请求');
try { try {
let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
@@ -397,7 +408,7 @@ export class NapCatOneBot11Adapter {
} catch (e) { } catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e); this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
} }
} else if (notify.type == GroupNotifyTypes.INVITE_ME && notify.status == 1) { } else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent( const groupInviteEvent = new OB11GroupRequestEvent(
this.core, this.core,
@@ -430,7 +441,7 @@ export class NapCatOneBot11Adapter {
member.role === GroupMemberRole.admin ? 'set' : 'unset', member.role === GroupMemberRole.admin ? 'set' : 'unset',
); );
this.networkManager.emitEvent(groupAdminNoticeEvent) this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e)); .catch(e => this.context.logger.logError('处理群管理员变动失败', e));
existMember.isChangeRole = false; existMember.isChangeRole = false;
this.context.logger.logDebug('群管理员变动处理完毕'); this.context.logger.logDebug('群管理员变动处理完毕');
}); });
@@ -438,14 +449,14 @@ export class NapCatOneBot11Adapter {
}; };
this.context.session.getGroupService().addKernelGroupListener( this.context.session.getGroupService().addKernelGroupListener(
new this.context.wrapper.NodeIKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)), proxiedListenerOf(groupListener, this.context.logger)
); );
} }
private async emitMsg(message: RawMessage) { private async emitMsg(message: RawMessage) {
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData; const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
this.context.logger.logDebug('收到新消息 RawMessage', message); this.context.logger.logDebug('收到新消息 RawMessage', message);
OB11Constructor.message(this.core, this, message, messagePostFormat).then((ob11Msg) => { this.apiContext.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => {
if (!ob11Msg) return; if (!ob11Msg) return;
this.context.logger.logDebug('转化为 OB11Message', ob11Msg); this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
if (debug) { if (debug) {
@@ -467,14 +478,14 @@ export class NapCatOneBot11Adapter {
this.networkManager.emitEvent(ob11Msg); this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError('constructMessage error: ', e)); }).catch(e => this.context.logger.logError('constructMessage error: ', e));
OB11Constructor.GroupEvent(this.core, message).then(groupEvent => { NT2GroupEvent(this.core, this, message).then(groupEvent => {
if (groupEvent) { if (groupEvent) {
// log("post group event", groupEvent); // log("post group event", groupEvent);
this.networkManager.emitEvent(groupEvent); this.networkManager.emitEvent(groupEvent);
} }
}).catch(e => this.context.logger.logError('constructGroupEvent error: ', e)); }).catch(e => this.context.logger.logError('constructGroupEvent error: ', e));
OB11Constructor.PrivateEvent(this.core, message).then(privateEvent => { NT2PrivateEvent(this.core, this, message).then(privateEvent => {
if (privateEvent) { if (privateEvent) {
// log("post private event", privateEvent); // log("post private event", privateEvent);
this.networkManager.emitEvent(privateEvent); this.networkManager.emitEvent(privateEvent);
@@ -482,16 +493,17 @@ export class NapCatOneBot11Adapter {
}).catch(e => this.context.logger.logError('constructPrivateEvent error: ', e)); }).catch(e => this.context.logger.logError('constructPrivateEvent error: ', e));
} }
private async emitRecallMsg(msgList: RawMessage[]) { private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache<string, boolean>) {
for (const message of msgList) { for (const message of msgList) {
// log("message update", message.sendStatus, message.msgId, message.msgSeq) // log("message update", message.sendStatus, message.msgId, message.msgSeq)
if (message.recallTime != '0') { //todo: 这个判断方法不太好,应该使用灰色消息元素来判断? if (message.recallTime != '0' && !cache.get(message.msgId)) { //todo: 这个判断方法不太好,应该使用灰色消息元素来判断?
cache.put(message.msgId, true);
// 撤回消息上报 // 撤回消息上报
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId); const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId);
if (!oriMessageId) { if (!oriMessageId) {
continue; continue;
} }
if (message.chatType == ChatType.friend) { if (message.chatType == ChatType.KCHATTYPEC2C) {
const friendRecallEvent = new OB11FriendRecallNoticeEvent( const friendRecallEvent = new OB11FriendRecallNoticeEvent(
this.core, this.core,
parseInt(message!.senderUin), parseInt(message!.senderUin),
@@ -499,7 +511,7 @@ export class NapCatOneBot11Adapter {
); );
this.networkManager.emitEvent(friendRecallEvent) this.networkManager.emitEvent(friendRecallEvent)
.catch(e => this.context.logger.logError('处理好友消息撤回失败', e)); .catch(e => this.context.logger.logError('处理好友消息撤回失败', e));
} else if (message.chatType == ChatType.group) { } else if (message.chatType == ChatType.KCHATTYPEGROUP) {
let operatorId = message.senderUin; let operatorId = message.senderUin;
for (const element of message.elements) { for (const element of message.elements) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid; const operatorUid = element.grayTipElement?.revokeElement.operatorUid;

View File

@@ -71,6 +71,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
this.connection = new WebSocket(this.url, { this.connection = new WebSocket(this.url, {
maxPayload: 1024 * 1024 * 1024, maxPayload: 1024 * 1024 * 1024,
handshakeTimeout: 2000,
perMessageDeflate: false,
headers: { headers: {
'X-Self-ID': this.coreContext.selfInfo.uin, 'X-Self-ID': this.coreContext.selfInfo.uin,
'Authorization': `Bearer ${this.token}`, 'Authorization': `Bearer ${this.token}`,

View File

@@ -36,7 +36,11 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
this.logger = coreContext.context.logger; this.logger = coreContext.context.logger;
this.heartbeatInterval = heartbeatInterval; this.heartbeatInterval = heartbeatInterval;
this.wsServer = new WebSocketServer({ port: port, host: ip, maxPayload: 1024 * 1024 * 1024, }); this.wsServer = new WebSocketServer({
port: port,
host: ip,
maxPayload: 1024 * 1024 * 1024,
});
const core = coreContext; const core = coreContext;
this.wsServer.on('connection', async (wsClient, wsReq) => { this.wsServer.on('connection', async (wsClient, wsReq) => {
if (!this.isOpen) { if (!this.isOpen) {
@@ -151,6 +155,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
//this.logger.logDebug('收到正向Websocket消息', receiveData); //this.logger.logDebug('收到正向Websocket消息', receiveData);
} catch (e) { } catch (e) {
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient); this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient);
return;
} }
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || ''); const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || '');

View File

@@ -1,7 +1,8 @@
import { OneBotFriendApi, OneBotGroupApi, OneBotUserApi } from '../api'; import { OneBotFriendApi, OneBotGroupApi, OneBotMsgApi, OneBotUserApi } from '../api';
export interface OneBotApiContextType { export interface OneBotApiContextType {
FriendApi: OneBotFriendApi; FriendApi: OneBotFriendApi;
UserApi: OneBotUserApi; UserApi: OneBotUserApi;
GroupApi: OneBotGroupApi; GroupApi: OneBotGroupApi;
MsgApi: OneBotMsgApi;
} }

View File

@@ -156,7 +156,7 @@ export interface OB11MessageCustomMusic {
export interface OB11MessageJson { export interface OB11MessageJson {
type: OB11MessageDataType.json; type: OB11MessageDataType.json;
data: { config: { token: string } } & any; data: { config: { token: string }, data: string };
} }
export interface OB11MessageDice { export interface OB11MessageDice {

View File

@@ -43,12 +43,12 @@ export async function NCoreInitShell() {
// from constructor // from constructor
const engine = new wrapper.NodeIQQNTWrapperEngine(); const engine = new wrapper.NodeIQQNTWrapperEngine();
const util = new wrapper.NodeQQNTWrapperUtil(); //const util = wrapper.NodeQQNTWrapperUtil.get();
const loginService = new wrapper.NodeIKernelLoginService(); const loginService = new wrapper.NodeIKernelLoginService();
const session = new wrapper.NodeIQQNTWrapperSession(); const session = new wrapper.NodeIQQNTWrapperSession();
// from get dataPath // from get dataPath
let dataPath = util.getNTUserDataInfoConfig(); let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!dataPath) { if (!dataPath) {
dataPath = path.resolve(os.homedir(), './.config/QQ'); dataPath = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(dataPath, { recursive: true }); fs.mkdirSync(dataPath, { recursive: true });
@@ -70,7 +70,7 @@ export async function NCoreInitShell() {
}, },
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }, thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
}, },
new wrapper.NodeIGlobalAdapter(new GlobalAdapter()), new GlobalAdapter() as any,
); );
loginService.initConfig({ loginService.initConfig({
machineId: '', machineId: '',
@@ -140,8 +140,7 @@ export async function NCoreInitShell() {
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args); logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
}; };
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
proxiedListenerOf(loginListener, logger)));
// 实现WebUi快速登录 // 实现WebUi快速登录
loginService.getLoginList().then((res) => { loginService.getLoginList().then((res) => {
@@ -188,10 +187,9 @@ export async function NCoreInitShell() {
} else { } else {
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式'); logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
if (historyLoginList.length > 0) { if (historyLoginList.length > 0) {
logger.log(`可用于快速登录的 QQ\n${ logger.log(`可用于快速登录的 QQ\n${historyLoginList
historyLoginList .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`) .join('\n')
.join('\n')
}`); }`);
} }
loginService.getQRCodePicture(); loginService.getQRCodePicture();
@@ -220,9 +218,9 @@ export async function NCoreInitShell() {
}; };
session.init( session.init(
sessionConfig, sessionConfig,
new wrapper.NodeIDependsAdapter(new DependsAdapter()), new DependsAdapter() as any,
new wrapper.NodeIDispatcherAdapter(new DispatcherAdapter()), new DispatcherAdapter() as any,
new wrapper.NodeIKernelSessionListener(sessionListener), sessionListener as any,
); );
try { try {
session.startNT(0); session.startNT(0);

View File

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

View File

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