mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a0024c98d5 | ||
![]() |
684a702638 | ||
![]() |
aec4a009d1 | ||
![]() |
822af575c9 | ||
![]() |
485efa7d44 | ||
![]() |
3d09d45423 | ||
![]() |
4c69c6d9fd | ||
![]() |
920a41acef | ||
![]() |
0cf13a284c | ||
![]() |
a89cdef436 | ||
![]() |
881d88f4ad | ||
![]() |
a72c96f56d | ||
![]() |
bc8235b209 | ||
![]() |
0087495749 | ||
![]() |
9560afd4a7 | ||
![]() |
56ec8559a0 | ||
![]() |
99ca79ac7d | ||
![]() |
24564f4c74 | ||
![]() |
212c802a1e | ||
![]() |
984b5d6c40 | ||
![]() |
0e3a4191a9 | ||
![]() |
570a34bca5 | ||
![]() |
c9a0c29286 | ||
![]() |
b5f804ec22 | ||
![]() |
dadbb83271 | ||
![]() |
848aacdbbf | ||
![]() |
da3665a167 | ||
![]() |
dcf0a06217 | ||
![]() |
3b5e6553cd | ||
![]() |
509390af20 | ||
![]() |
9ad511a9c0 | ||
![]() |
89c102513d | ||
![]() |
5e65ae76ad | ||
![]() |
b6c364cd78 | ||
![]() |
e086b8707f | ||
![]() |
90dddd10a9 | ||
![]() |
2dd0907565 | ||
![]() |
f31b0d0c71 | ||
![]() |
a0825b75f7 | ||
![]() |
a3bd4c0f73 | ||
![]() |
a3e8c9b28a | ||
![]() |
d7fb850b4a | ||
![]() |
d084778a6e | ||
![]() |
8ca30de760 | ||
![]() |
8a10b81bd9 | ||
![]() |
4a93c4e584 | ||
![]() |
50177cd6bd | ||
![]() |
71a2e52739 | ||
![]() |
4fac6d5aa3 | ||
![]() |
37d061b602 | ||
![]() |
40193e4edc | ||
![]() |
b4e9d61871 | ||
![]() |
d44b589e55 | ||
![]() |
68216415b6 | ||
![]() |
ba53da18d1 | ||
![]() |
9b76fa3582 | ||
![]() |
13d8d10a7f | ||
![]() |
5c6c1bb09d | ||
![]() |
12105d96ea | ||
![]() |
4054756035 | ||
![]() |
16769c7838 | ||
![]() |
cd076c5959 | ||
![]() |
f52e1aa131 | ||
![]() |
fdc1ef7e9a | ||
![]() |
9cccf2d47b | ||
![]() |
0796f27f2a | ||
![]() |
6c84014e0d | ||
![]() |
cd496a22bf | ||
![]() |
0200343780 | ||
![]() |
47fb629d26 | ||
![]() |
5b75e753a7 | ||
![]() |
326e9b86ce | ||
![]() |
e430cc54f2 | ||
![]() |
fd26a9c698 | ||
![]() |
e79b608f77 | ||
![]() |
03098ee024 | ||
![]() |
a2bfdd003c |
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -95,7 +95,13 @@ jobs:
|
||||
steps:
|
||||
- name: Download All Artifact
|
||||
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
|
||||
run: |
|
||||
cd ./NapCat.Shell/
|
||||
|
22
README.md
22
README.md
@@ -3,37 +3,33 @@
|
||||
</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] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||
- [x] **超多接口**:在实现大部分Onebot接口上扩展了一套私有API
|
||||
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
||||
|
||||
## 如何使用
|
||||
## 使用猫猫
|
||||
|
||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||
|
||||
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
|
||||
|
||||
## 相关链接
|
||||
## 回家旅途
|
||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
||||
|
||||
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||
|
||||
## 鸣谢名单
|
||||
## 猫猫朋友
|
||||
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
|
||||
|
||||
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
|
||||
|
||||
---
|
||||
|
||||
## 使用许可
|
||||
## 约法三章
|
||||
|
||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 [core](./src/core) 部分代码开发。**
|
||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**
|
||||
|
@@ -4,7 +4,7 @@
|
||||
"name": "NapCatQQ",
|
||||
"slug": "NapCat.Framework",
|
||||
"description": "高性能的 OneBot 11 协议实现",
|
||||
"version": "2.0.35",
|
||||
"version": "2.2.7",
|
||||
"icon": "./logo.png",
|
||||
"authors": [
|
||||
{
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "2.0.35",
|
||||
"version": "2.2.7",
|
||||
"scripts": {
|
||||
"build:framework": "vite build --mode framework",
|
||||
"build:shell": "vite build --mode shell",
|
||||
|
@@ -16,16 +16,13 @@ export interface ListenerIBase {
|
||||
}
|
||||
|
||||
export class LegacyNTEventWrapper {
|
||||
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
|
||||
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
|
||||
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}
|
||||
|
||||
constructor(
|
||||
listenerMapping: Record<string, ListenerIBase>,
|
||||
wrapperSession: NodeIQQNTWrapperSession,
|
||||
wrapperSession: NodeIQQNTWrapperSession
|
||||
) {
|
||||
this.listenerMapping = listenerMapping;
|
||||
this.WrapperSession = wrapperSession;
|
||||
}
|
||||
|
||||
@@ -72,18 +69,17 @@ export class LegacyNTEventWrapper {
|
||||
}
|
||||
|
||||
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||
const ListenerType = this.listenerMapping![listenerMainName];
|
||||
let Listener = this.listenerManager.get(listenerMainName + uniqueCode);
|
||||
if (!Listener && ListenerType) {
|
||||
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
|
||||
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
|
||||
if (!existListener) {
|
||||
const Listener = this.createProxyDispatch(listenerMainName);
|
||||
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
|
||||
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
|
||||
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
|
||||
addfunc!(Listener as T);
|
||||
//console.log(addfunc!(Listener as T));
|
||||
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
|
||||
return Listener as T;
|
||||
}
|
||||
return Listener as T;
|
||||
return existListener as T;
|
||||
}
|
||||
|
||||
//统一回调清理事件
|
||||
@@ -166,7 +162,89 @@ export class LegacyNTEventWrapper {
|
||||
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++;
|
||||
//console.log('func', ...args);
|
||||
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<
|
||||
EventType extends (...args: any[]) => Promise<any>,
|
||||
ListenerType extends (...args: any[]) => void
|
||||
|
@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
|
||||
export const napcat_version = '2.0.35';
|
||||
export const napcat_version = '2.2.7';
|
||||
|
||||
export class NapCatPathWrapper {
|
||||
binaryPath: string;
|
||||
|
@@ -74,7 +74,7 @@ export class QQBasicInfoWrapper {
|
||||
this.context.logger.log(
|
||||
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
|
||||
);
|
||||
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() };
|
||||
return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -237,9 +237,9 @@ export async function uri2local(dir: string, uri: string, filename: string | und
|
||||
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
|
||||
if (pathInfo.name) {
|
||||
filename = pathInfo.name;
|
||||
if (pathInfo.ext) {
|
||||
filename += pathInfo.ext;
|
||||
}
|
||||
if (pathInfo.ext) {
|
||||
filename += pathInfo.ext;
|
||||
}
|
||||
}
|
||||
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
|
||||
const fileExt = path.extname(HandledUri);
|
||||
@@ -259,7 +259,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
|
||||
if (success) {
|
||||
filePath = fileTypePath;
|
||||
fileExt = ext;
|
||||
filename = path.basename(filePath, fileExt);
|
||||
filename = filename + '.' + ext;
|
||||
}
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import fs from 'fs';
|
||||
import * as fsPromise from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import { QQLevel } from '@/core';
|
||||
|
||||
@@ -97,19 +95,19 @@ export function isEqual(obj1: any, obj2: any) {
|
||||
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
||||
if (os.platform() === 'linux') {
|
||||
return {
|
||||
baseVersion: '3.2.12-26702',
|
||||
curVersion: '3.2.12-26702',
|
||||
baseVersion: '3.2.12-27254',
|
||||
curVersion: '3.2.12-27254',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702',
|
||||
buildId: '27254',
|
||||
};
|
||||
}
|
||||
return {
|
||||
baseVersion: '9.9.15-26702',
|
||||
curVersion: '9.9.15-26702',
|
||||
baseVersion: '9.9.15-27254',
|
||||
curVersion: '9.9.15-27254',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702',
|
||||
buildId: '27254',
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import log4js, { Configuration } from 'log4js';
|
||||
import { truncateString } from '@/common/utils/helper';
|
||||
import path from 'node:path';
|
||||
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 {
|
||||
DEBUG = 'debug',
|
||||
@@ -152,11 +152,11 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
||||
|
||||
const tokens: string[] = [];
|
||||
|
||||
if (msg.chatType == ChatType.friend) {
|
||||
if (msg.chatType == ChatType.KCHATTYPEC2C) {
|
||||
tokens.push(`私聊 (${msg.peerUin})`);
|
||||
} else if (msg.chatType == ChatType.group) {
|
||||
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
|
||||
tokens.push(`群聊 (群 ${msg.peerUin} 的 ${msg.senderUin})`);
|
||||
} else if (msg.chatType == ChatType.chatDevice) {
|
||||
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
||||
tokens.push('移动设备');
|
||||
} else /* temp */ {
|
||||
tokens.push(`临时消息 (${msg.peerUin})`);
|
||||
@@ -164,7 +164,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
||||
|
||||
// message content
|
||||
|
||||
function msgElementToText(element: ElementWrapper) {
|
||||
function msgElementToText(element: MessageElement) {
|
||||
if (element.textElement) {
|
||||
if (element.textElement.atType === AtType.notAt) {
|
||||
return element.textElement.content;
|
||||
|
File diff suppressed because one or more lines are too long
@@ -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) {
|
||||
let GroupData;
|
||||
let BuddyData;
|
||||
if (peer.chatType === ChatType.group) {
|
||||
if (peer.chatType === ChatType.KCHATTYPEGROUP) {
|
||||
GroupData =
|
||||
[{
|
||||
groupCode: peer.peerUid,
|
||||
@@ -189,7 +189,7 @@ export class NTQQFileApi {
|
||||
groupName: 'NapCat.Cached',
|
||||
remark: 'NapCat.Cached',
|
||||
}];
|
||||
} else if (peer.chatType === ChatType.friend) {
|
||||
} else if (peer.chatType === ChatType.KCHATTYPEC2C) {
|
||||
BuddyData = [{
|
||||
category_name: 'NapCat.Cached',
|
||||
peerUid: peer.peerUid,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
export class NTQQFriendApi {
|
||||
@@ -70,29 +70,14 @@ export class NTQQFriendApi {
|
||||
async isBuddy(uid: string) {
|
||||
return this.context.session.getBuddyService().isBuddy(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param forced
|
||||
* @returns
|
||||
*/
|
||||
async getFriends(forced = false): Promise<User[]> {
|
||||
const [_retData, _BuddyArg] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||
(
|
||||
'NodeIKernelBuddyService/getBuddyList',
|
||||
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
forced,
|
||||
);
|
||||
const friends: User[] = [];
|
||||
for (const categoryItem of _BuddyArg) {
|
||||
for (const friend of categoryItem.buddyList) {
|
||||
friends.push(friend);
|
||||
}
|
||||
}
|
||||
return friends;
|
||||
async clearBuddyReqUnreadCnt() {
|
||||
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
|
||||
}
|
||||
async getBuddyReq() {
|
||||
const [, ret] = await this.core.eventWrapper.CallNormalEventV2
|
||||
<NodeIKernelBuddyService['getBuddyReq'], NodeIKernelBuddyListener['onBuddyReqChange']>
|
||||
('NodeIKernelBuddyService/getBuddyReq', 'NodeIKernelBuddyListener/onBuddyReqChange', 1, 5000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async handleFriendRequest(flag: string, accept: boolean) {
|
||||
|
@@ -7,6 +7,8 @@ import {
|
||||
GroupNotify,
|
||||
GroupRequestOperateTypes,
|
||||
InstanceContext,
|
||||
KickMemberInfo,
|
||||
kickMemberV2Req,
|
||||
MemberExtSourceType,
|
||||
NapCatCore,
|
||||
NodeIKernelGroupListener,
|
||||
@@ -42,12 +44,12 @@ export class NTQQGroupApi {
|
||||
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
|
||||
const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getGroupList',
|
||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
forced,
|
||||
'NodeIKernelGroupService/getGroupList',
|
||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
forced,
|
||||
);
|
||||
return groupList;
|
||||
}
|
||||
@@ -114,7 +116,7 @@ export class NTQQGroupApi {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
@@ -175,7 +177,7 @@ export class NTQQGroupApi {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
@@ -219,7 +221,21 @@ export class NTQQGroupApi {
|
||||
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||
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) {
|
||||
let param = {
|
||||
groupCode: GroupCode,
|
||||
needDeleteLocalMsg: needDeleteLocalMsg
|
||||
};
|
||||
//应该是直接返回不需要Listener的 未经测试 需测试再发布
|
||||
return this.context.session.getGroupService().quitGroupV2(param);
|
||||
}
|
||||
async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||
// 代码没测过
|
||||
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||
@@ -240,14 +256,14 @@ export class NTQQGroupApi {
|
||||
async getSingleScreenNotifies(num: number) {
|
||||
const [_retData, _doubt, _seq, notifies] = await this.core.eventWrapper.CallNormalEvent<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||
(
|
||||
'NodeIKernelGroupService/getSingleScreenNotifies',
|
||||
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
false,
|
||||
'',
|
||||
num,
|
||||
'NodeIKernelGroupService/getSingleScreenNotifies',
|
||||
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
false,
|
||||
'',
|
||||
num,
|
||||
);
|
||||
return notifies;
|
||||
}
|
||||
@@ -258,14 +274,14 @@ export class NTQQGroupApi {
|
||||
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
|
||||
(
|
||||
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||
1,
|
||||
forced ? 5000 : 250,
|
||||
(params) => {
|
||||
return params === GroupCode;
|
||||
},
|
||||
);
|
||||
(
|
||||
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||
1,
|
||||
forced ? 5000 : 250,
|
||||
(params) => {
|
||||
return params === GroupCode;
|
||||
},
|
||||
);
|
||||
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
|
||||
const retData = await EventFunc!(GroupCode, [uid], forced);
|
||||
if (retData.result !== 0) {
|
||||
@@ -274,9 +290,9 @@ export class NTQQGroupApi {
|
||||
const result = await Listener as unknown;
|
||||
let member: GroupMember | undefined;
|
||||
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);
|
||||
};
|
||||
}
|
||||
return member;
|
||||
|
||||
// 原本的方法: (no_cache 下效率很高, cache 下效率一致)
|
||||
@@ -302,7 +318,7 @@ export class NTQQGroupApi {
|
||||
throw ('获取群成员列表出错,' + 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;
|
||||
/*
|
||||
console.log(sceneId);
|
||||
@@ -333,7 +349,7 @@ export class NTQQGroupApi {
|
||||
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
|
||||
5000,
|
||||
GroupCode,
|
||||
);
|
||||
);
|
||||
return ret.arkJson;
|
||||
}
|
||||
|
||||
|
@@ -120,18 +120,18 @@ export class NTQQMsgApi {
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||
>(
|
||||
'NodeIKernelRichMediaService/getGroupFileList',
|
||||
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||
1,
|
||||
5000,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||
>(
|
||||
'NodeIKernelRichMediaService/getGroupFileList',
|
||||
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||
1,
|
||||
5000,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
|
||||
return true;
|
||||
},
|
||||
GroupCode,
|
||||
params,
|
||||
);
|
||||
return true;
|
||||
},
|
||||
GroupCode,
|
||||
params,
|
||||
);
|
||||
return data[1].item;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ export class NTQQMsgApi {
|
||||
peerOpenId: "",
|
||||
};
|
||||
return this.context.session.getMsgService().prepareTempChat({
|
||||
chatType: ChatType.temp,
|
||||
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
|
||||
peerUid: toUserUid,
|
||||
peerNickname: nickname,
|
||||
fromGroupCode: GroupCode,
|
||||
@@ -171,7 +171,7 @@ export class NTQQMsgApi {
|
||||
}
|
||||
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!);
|
||||
if (member) {
|
||||
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
|
||||
@@ -182,24 +182,24 @@ export class NTQQMsgApi {
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
|
||||
return true;
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
'0',
|
||||
peer,
|
||||
msgElements,
|
||||
new Map(),
|
||||
);
|
||||
return false;
|
||||
},
|
||||
'0',
|
||||
peer,
|
||||
msgElements,
|
||||
new Map(),
|
||||
);
|
||||
const retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.guildId === msgId) {
|
||||
return true;
|
||||
@@ -227,25 +227,25 @@ export class NTQQMsgApi {
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
|
||||
return true;
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map(),
|
||||
);
|
||||
return false;
|
||||
},
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map(),
|
||||
);
|
||||
for (const msg of data[1]) {
|
||||
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||
if (!arkElement) {
|
||||
|
@@ -129,7 +129,7 @@ export class NTQQUserApi {
|
||||
|
||||
async getUserDetailInfo(uid: string): Promise<User> {
|
||||
try {
|
||||
let retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB);
|
||||
const retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB);
|
||||
if (retUser.uin !== '0') {
|
||||
return retUser;
|
||||
}
|
||||
|
@@ -20,15 +20,14 @@ export class NTQQWebApi {
|
||||
|
||||
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: groupCode,
|
||||
msg_seq: msgSeq,
|
||||
msg_random: msgRandom,
|
||||
target_group_code: targetGroupCode,
|
||||
}).toString()
|
||||
}`;
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: groupCode,
|
||||
msg_seq: msgSeq,
|
||||
msg_random: msgRandom,
|
||||
target_group_code: targetGroupCode,
|
||||
}).toString()
|
||||
}`;
|
||||
try {
|
||||
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
} catch (e) {
|
||||
@@ -38,18 +37,17 @@ export class NTQQWebApi {
|
||||
|
||||
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: GroupCode,
|
||||
page_start,
|
||||
page_limit: '20',
|
||||
}).toString()
|
||||
}`;
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: GroupCode,
|
||||
page_start,
|
||||
page_limit: '20',
|
||||
}).toString()
|
||||
}`;
|
||||
let ret;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>
|
||||
(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
@@ -65,15 +63,14 @@ export class NTQQWebApi {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
||||
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||
new URLSearchParams({
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||
st: '0',
|
||||
end: '40',
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
||||
return [];
|
||||
} else {
|
||||
@@ -86,15 +83,14 @@ export class NTQQWebApi {
|
||||
//遍历批量请求
|
||||
for (let i = 2; i <= PageNum; i++) {
|
||||
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||
new URLSearchParams({
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||
st: ((i - 1) * 40).toString(),
|
||||
end: (i * 40).toString(),
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
retList.push(ret);
|
||||
}
|
||||
//批量等待
|
||||
@@ -127,8 +123,7 @@ export class NTQQWebApi {
|
||||
let ret: any = undefined;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<any>
|
||||
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${
|
||||
new URLSearchParams({
|
||||
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
qid: GroupCode,
|
||||
text: Content,
|
||||
@@ -136,7 +131,7 @@ export class NTQQWebApi {
|
||||
type: '1',
|
||||
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
||||
}).toString()
|
||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
return ret;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
@@ -147,15 +142,10 @@ export class NTQQWebApi {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
let ret: WebApiGroupNoticeRet | undefined = undefined;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
qid: GroupCode,
|
||||
type: '1',
|
||||
start: '0',
|
||||
num: '1',
|
||||
}).toString()
|
||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' +
|
||||
this.getBknFromCookie(cookieObject) + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
|
||||
|
||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
if (ret?.ec !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -168,12 +158,11 @@ export class NTQQWebApi {
|
||||
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
|
||||
const url = `https://qun.qq.com/interactive/honorlist?${
|
||||
new URLSearchParams({
|
||||
gc: Internal_groupCode,
|
||||
type: Internal_type.toString(),
|
||||
}).toString()
|
||||
}`;
|
||||
const url = `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
|
||||
gc: Internal_groupCode,
|
||||
type: Internal_type.toString(),
|
||||
}).toString()
|
||||
}`;
|
||||
let resJson;
|
||||
try {
|
||||
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
|
@@ -44,8 +44,8 @@ export class NapCatCore {
|
||||
constructor(context: InstanceContext, selfInfo: SelfInfo) {
|
||||
this.selfInfo = selfInfo;
|
||||
this.context = context;
|
||||
this.util = new this.context.wrapper.NodeQQNTWrapperUtil();
|
||||
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
||||
this.util = this.context.wrapper.NodeQQNTWrapperUtil;
|
||||
this.eventWrapper = new LegacyNTEventWrapper(context.session);
|
||||
this.apis = {
|
||||
FileApi: new NTQQFileApi(this.context, this),
|
||||
SystemApi: new NTQQSystemApi(this.context, this),
|
||||
@@ -79,7 +79,7 @@ export class NapCatCore {
|
||||
}
|
||||
|
||||
get dataPath(): string {
|
||||
let result = this.util.getNTUserDataInfoConfig();
|
||||
let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
|
||||
if (!result) {
|
||||
result = path.resolve(os.homedir(), './.config/QQ');
|
||||
fs.mkdirSync(result, { recursive: true });
|
||||
@@ -98,7 +98,7 @@ export class NapCatCore {
|
||||
};
|
||||
//await sleep(2500);
|
||||
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();
|
||||
@@ -113,7 +113,7 @@ export class NapCatCore {
|
||||
// }
|
||||
};
|
||||
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(
|
||||
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 {
|
||||
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 false;
|
||||
|
@@ -1,5 +1,17 @@
|
||||
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 {
|
||||
REFRESHALL,
|
||||
GETALL,
|
||||
|
@@ -362,6 +362,7 @@ export interface SendPicElement {
|
||||
}
|
||||
|
||||
export interface ReplyElement {
|
||||
sourceMsgIdInRecords?: string;
|
||||
replayMsgSeq: string;
|
||||
replayMsgId: string;
|
||||
senderUin: string;
|
||||
@@ -476,7 +477,7 @@ export interface MessageElement {
|
||||
extBufForUI: string,//"0x",
|
||||
textElement?: TextElement;
|
||||
faceElement?: FaceElement,
|
||||
marketFaceElement?: MarkdownElement,
|
||||
marketFaceElement?: MarketFaceElement,
|
||||
replyElement?: ReplyElement,
|
||||
picElement?: PicElement,
|
||||
pttElement?: PttElement,
|
||||
@@ -510,17 +511,8 @@ export enum AtType {
|
||||
atAll = 1,
|
||||
atUser = 2
|
||||
}
|
||||
|
||||
export enum ChatType {
|
||||
friend = 1,
|
||||
group = 2,
|
||||
chatDevice = 8, //移动设备?
|
||||
temp = 100
|
||||
|
||||
}
|
||||
|
||||
// 来自Android分析
|
||||
export enum ChatType2 {
|
||||
export enum ChatType {
|
||||
KCHATTYPEADELIE = 42,
|
||||
KCHATTYPEBUDDYNOTIFY = 5,
|
||||
KCHATTYPEC2C = 1,
|
||||
@@ -965,76 +957,5 @@ export interface RawMessage {
|
||||
|
||||
records: RawMessage[];
|
||||
|
||||
elements: ElementWrapper[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 并非原生接口类型,故以 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;
|
||||
}
|
||||
elements: MessageElement[];
|
||||
}
|
@@ -1,40 +1,58 @@
|
||||
export enum GroupNotifyTypes {
|
||||
INVITE_ME = 1,
|
||||
INVITED_JOIN = 4, // 有人接受了邀请入群
|
||||
JOIN_REQUEST = 7,
|
||||
ADMIN_SET = 8,
|
||||
KICK_MEMBER = 9,
|
||||
MEMBER_EXIT = 11, // 主动退出
|
||||
ADMIN_UNSET = 12,
|
||||
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员
|
||||
export enum GroupNotifyMsgType {
|
||||
UN_SPECIFIED,
|
||||
INVITED_BY_MEMBER,
|
||||
REFUSE_INVITED,
|
||||
REFUSED_BY_ADMINI_STRATOR,
|
||||
AGREED_TOJOIN_DIRECT,// 有人接受了邀请入群
|
||||
INVITED_NEED_ADMINI_STRATOR_PASS,
|
||||
AGREED_TO_JOIN_BY_ADMINI_STRATOR,
|
||||
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 {
|
||||
doubt: boolean;
|
||||
nextStartSeq: string;
|
||||
notifies: GroupNotify[];
|
||||
}
|
||||
|
||||
export enum GroupNotifyStatus {
|
||||
IGNORE = 0,
|
||||
WAIT_HANDLE = 1,
|
||||
APPROVE = 2,
|
||||
REJECT = 3
|
||||
export enum GroupNotifyMsgStatus {
|
||||
KINIT,//初始化
|
||||
KUNHANDLE,//未处理
|
||||
KAGREED,//同意
|
||||
KREFUSED,//拒绝
|
||||
KIGNORED//忽略
|
||||
}
|
||||
export enum GroupInviteStatus {
|
||||
INIT,
|
||||
WAIT_TO_APPROVE,
|
||||
JOINED,
|
||||
REFUSED_BY_ADMINI_STRATOR
|
||||
}
|
||||
export enum GroupInviteType {
|
||||
BYBUDDY,
|
||||
BYGROUPMEMBER,
|
||||
BYDISCUSSMEMBER
|
||||
}
|
||||
|
||||
export interface GroupNotify {
|
||||
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify
|
||||
seq: string; // 唯一标识符,转成数字再除以1000应该就是时间戳?
|
||||
type: GroupNotifyTypes;
|
||||
status: GroupNotifyStatus; // 0是已忽略?,1是未处理,2是已同意
|
||||
seq: string; // 通知序列号
|
||||
type: GroupNotifyMsgType;
|
||||
status: GroupNotifyMsgStatus;
|
||||
group: { groupCode: string; groupName: string };
|
||||
user1: { uid: string; nickName: string }; // 被设置管理员的人
|
||||
user2: { uid: string; nickName: string }; // 操作者
|
||||
actionUser: { uid: string; nickName: string }; //未知
|
||||
actionTime: string;
|
||||
invitationExt: {
|
||||
srcType: number; // 0?未知
|
||||
groupCode: string; waitStatus: number
|
||||
srcType: GroupInviteType; // 邀请来源
|
||||
groupCode: string;
|
||||
waitStatus: GroupInviteStatus
|
||||
};
|
||||
postscript: string; // 加群用户填写的验证信息
|
||||
repeatSeqs: [];
|
||||
@@ -64,6 +82,7 @@ export enum BuddyReqType {
|
||||
}
|
||||
|
||||
export interface FriendRequest {
|
||||
isBuddy?: boolean;
|
||||
isInitiator?: boolean;
|
||||
isDecide: boolean;
|
||||
friendUid: string;
|
||||
|
70
src/core/external/appid.json
vendored
70
src/core/external/appid.json
vendored
@@ -1,58 +1,30 @@
|
||||
{
|
||||
"3.1.2-13107": {
|
||||
"appid": 537146866,
|
||||
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
|
||||
"3.2.12-27187": {
|
||||
"appid": 537240645,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27187_GW_B"
|
||||
},
|
||||
"3.2.10-25765": {
|
||||
"appid": 537234773,
|
||||
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
|
||||
"3.2.12-27206": {
|
||||
"appid": 537240645,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
|
||||
},
|
||||
"3.2.12-26702": {
|
||||
"appid": 537237950,
|
||||
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||
"3.2.12-27254":{
|
||||
"appid": 537240795,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
|
||||
},
|
||||
"3.2.12-26740": {
|
||||
"appid": 537237950,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
|
||||
"9.9.15-27187":{
|
||||
"appid": 537240610,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
|
||||
},
|
||||
"3.2.12-26909": {
|
||||
"appid": 537237923,
|
||||
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B"
|
||||
"9.9.15-27206":{
|
||||
"appid": 537240610,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
|
||||
},
|
||||
"9.9.11-24815": {
|
||||
"appid": 537226656,
|
||||
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||
"9.9.15-27254":{
|
||||
"appid": 537240709,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
|
||||
},
|
||||
"9.9.12-25493": {
|
||||
"appid": 537231759,
|
||||
"qua": "V1_WIN_NQ_9.9.12_25493_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"
|
||||
"9.9.15-27333": {
|
||||
"appid": 537240709,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||
|
||||
interface IGroupListener {
|
||||
export interface IGroupListener {
|
||||
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
|
@@ -43,4 +43,48 @@ export class ProfileListener implements IProfileListener {
|
||||
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[]){
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -61,11 +61,11 @@ export interface NodeIKernelBuddyService {
|
||||
|
||||
getBuddyReqUnreadCnt(): number;
|
||||
|
||||
getBuddyReq(): unknown;
|
||||
getBuddyReq(): Promise<GeneralCallResult>;
|
||||
|
||||
delBuddyReq(uid: number): void;
|
||||
|
||||
clearBuddyReqUnreadCnt(): void;
|
||||
clearBuddyReqUnreadCnt(): Promise<GeneralCallResult>;
|
||||
|
||||
reqToAddFriends(uid: number, msg: string): void;
|
||||
|
||||
|
@@ -1,16 +1,20 @@
|
||||
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
|
||||
import { IGroupListener, NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
|
||||
import {
|
||||
GroupExtParam,
|
||||
GroupMember,
|
||||
GroupMemberRole,
|
||||
GroupNotifyTypes,
|
||||
GroupNotifyMsgType,
|
||||
GroupRequestOperateTypes,
|
||||
kickMemberV2Req,
|
||||
} from '@/core/entities';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
|
||||
|
||||
export interface NodeIKernelGroupService {
|
||||
kickMemberV2(param: kickMemberV2Req): Promise<GeneralCallResult>;
|
||||
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
|
||||
|
||||
getMemberCommonInfo(Req: {
|
||||
groupCode: string,
|
||||
startUin: string,
|
||||
@@ -104,7 +108,7 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
setHeader(uid: string, path: string): unknown;
|
||||
|
||||
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
||||
addKernelGroupListener(listener: IGroupListener): number;
|
||||
|
||||
removeKernelGroupListener(listenerId: unknown): void;
|
||||
|
||||
@@ -195,7 +199,7 @@ export interface NodeIKernelGroupService {
|
||||
operateType: GroupRequestOperateTypes, // 2 拒绝
|
||||
targetMsg: {
|
||||
seq: string, // 通知序列号
|
||||
type: GroupNotifyTypes,
|
||||
type: GroupNotifyMsgType,
|
||||
groupCode: string,
|
||||
postscript: string
|
||||
}
|
||||
@@ -205,7 +209,7 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
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>;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { AnyCnameRecord } from 'node:dns';
|
||||
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
|
||||
import { NodeIKernelProfileListener } from '../listeners';
|
||||
import { NodeIKernelProfileListener, ProfileListener } from '../listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
export enum UserDetailSource {
|
||||
@@ -35,7 +35,7 @@ export interface NodeIKernelProfileService {
|
||||
|
||||
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
|
||||
|
||||
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
|
||||
addKernelProfileListener(listener: ProfileListener): number;
|
||||
|
||||
removeKernelProfileListener(listenerId: number): void;
|
||||
|
||||
|
@@ -35,6 +35,7 @@ import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPe
|
||||
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
|
||||
|
||||
export interface NodeQQNTWrapperUtil {
|
||||
get(): unknown;
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(): NodeQQNTWrapperUtil;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -41,8 +41,7 @@ export async function NCoreInitFramework(
|
||||
online: true,
|
||||
});
|
||||
};
|
||||
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
|
||||
proxiedListenerOf(loginListener, logger)));
|
||||
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
|
||||
});
|
||||
// 过早进入会导致addKernelMsgListener等Listener添加失败
|
||||
// await sleep(2500);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { OB11Constructor } from '@/onebot/helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
|
@@ -24,16 +24,16 @@ export class SetInputStatus extends BaseAction<Payload, any> {
|
||||
let peer: Peer;
|
||||
if (payload.group_id) {
|
||||
peer = {
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_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');
|
||||
peer = {
|
||||
chatType: ChatType.friend,
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: uid
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error('请指定 group_id 或 user_id');
|
||||
}
|
||||
|
@@ -47,15 +47,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
let peer: Peer | undefined;
|
||||
//识别Peer
|
||||
if (isGroup) {
|
||||
peer = { chatType: ChatType.group, peerUid: peerUin };
|
||||
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
|
||||
}
|
||||
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
|
||||
if (PeerUid) {
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
|
||||
if (isBuddy) {
|
||||
peer = { chatType: ChatType.friend, peerUid: PeerUid };
|
||||
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
|
||||
} else {
|
||||
peer = { chatType: ChatType.temp, peerUid: PeerUid };
|
||||
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
|
||||
}
|
||||
}
|
||||
if (!peer) {
|
||||
@@ -97,8 +97,8 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
if (NTSearchNameResult.length !== 0) {
|
||||
const MsgId = NTSearchNameResult[0].msgId;
|
||||
let peer: Peer | undefined = undefined;
|
||||
if (NTSearchNameResult[0].chatType == ChatType.group) {
|
||||
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||
if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) {
|
||||
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||
}
|
||||
if (!peer) {
|
||||
throw new Error('chattype not support');
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { RawNTMsg2Onebot } from '@/onebot/helper';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -40,7 +41,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
||||
}
|
||||
const msgList = data.msgList;
|
||||
const messages = (await Promise.all(msgList.map(async msg => {
|
||||
const resMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg);
|
||||
const resMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg);
|
||||
if (!resMsg) return;
|
||||
resMsg.message_id = MessageUnique.createMsg({
|
||||
guildId: '',
|
||||
|
@@ -2,9 +2,10 @@ import BaseAction from '../BaseAction';
|
||||
import { OB11Message } from '../../types';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, RawMessage } from '@/core/entities';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { RawNTMsg2Onebot } from '@/onebot/helper';
|
||||
|
||||
interface Response {
|
||||
messages: OB11Message[];
|
||||
@@ -37,7 +38,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
||||
const isReverseOrder = payload.reverseOrder || true;
|
||||
if (!uid) throw `记录${payload.user_id}不存在`;
|
||||
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[];
|
||||
@@ -53,7 +54,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
||||
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 => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
|
||||
return { 'messages': ob11MsgList };
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,10 @@ import BaseAction from '../BaseAction';
|
||||
import { OB11Message } from '../../types';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, Peer, RawMessage } from '@/core/entities';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { RawNTMsg2Onebot } from '@/onebot/helper';
|
||||
|
||||
interface Response {
|
||||
messages: OB11Message[];
|
||||
@@ -32,7 +33,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
||||
//处理参数
|
||||
const isReverseOrder = payload.reverseOrder || true;
|
||||
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[];
|
||||
if (!payload.message_seq || payload.message_seq == 0) {
|
||||
@@ -48,7 +49,7 @@ 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 => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
|
||||
return { 'messages': ob11MsgList };
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11User, OB11UserSex } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
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,
|
||||
login_days: 0,
|
||||
uid: ''
|
||||
};
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||
|
@@ -44,9 +44,9 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
|
||||
}
|
||||
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||
if (ret['result'] == 1004022) {
|
||||
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
|
||||
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
|
||||
} else if (ret['result'] != 0) {
|
||||
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
|
@@ -5,7 +5,7 @@ import fs from 'fs';
|
||||
import { sendMsg } from '@/onebot/action/msg/SendMsg';
|
||||
import { uri2local } from '@/common/utils/file';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
|
||||
|
||||
const SchemaData = {
|
||||
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);
|
||||
await sendMsg(this.CoreContext, {
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id.toString(),
|
||||
}, [sendFileEle], [], true);
|
||||
return null;
|
||||
|
@@ -5,7 +5,7 @@ import fs from 'fs';
|
||||
import { sendMsg } from '@/onebot/action/msg/SendMsg';
|
||||
import { uri2local } from '@/common/utils/file';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -32,7 +32,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
||||
throw `私聊${payload.user_id}不存在`;
|
||||
}
|
||||
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';
|
||||
}
|
||||
|
28
src/onebot/action/group/DelGroupNotice.ts
Normal file
28
src/onebot/action/group/DelGroupNotice.ts
Normal 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);
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
import { OB11Group } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11Group } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { Group } from '@/core/entities';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { GroupNotifyMsgStatus } from '@/core';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
@@ -28,7 +29,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
|
||||
invitor_nick: SSNotify.user1?.nickName,
|
||||
group_id: SSNotify.group?.groupCode,
|
||||
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,
|
||||
});
|
||||
} else if (SSNotify.type == 7) {
|
||||
@@ -38,7 +39,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
|
||||
requester_nick: SSNotify.user1?.nickName,
|
||||
group_id: SSNotify.group?.groupCode,
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
|
||||
import { SetGroupFileFolder } from './file/SetGroupFileFolder';
|
||||
import { DelGroupFile } from './file/DelGroupFile';
|
||||
import { DelGroupFileFolder } from './file/DelGroupFileFolder';
|
||||
import { SetQQProfile } from './go-cqhttp/SetQQProfile'
|
||||
import { SetQQProfile } from './go-cqhttp/SetQQProfile';
|
||||
import { ShareGroupEx, SharePeer } from './extends/ShareContact';
|
||||
import { CreateCollection } from './extends/CreateCollection';
|
||||
import { SetLongNick } from './extends/SetLongNick';
|
||||
@@ -79,6 +79,8 @@ import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
import GetGuildProfile from './guild/GetGuildProfile';
|
||||
import SetModelShow from './go-cqhttp/SetModelShow';
|
||||
import { SetInputStatus } from './extends/SetInputStatus';
|
||||
import { GetCSRF } from './system/GetCSRF';
|
||||
import { DelGroupNotice } from './group/DelGroupNotice';
|
||||
|
||||
export type ActionMap = Map<string, BaseAction<any, any>>;
|
||||
|
||||
@@ -167,6 +169,8 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
|
||||
new GetGuildProfile(onebotContext, coreContext),
|
||||
new SetModelShow(onebotContext, coreContext),
|
||||
new SetInputStatus(onebotContext, coreContext),
|
||||
new GetCSRF(onebotContext, coreContext),
|
||||
new DelGroupNotice(onebotContext, coreContext),
|
||||
];
|
||||
const actionMap = new Map();
|
||||
for (const action of actionHandlers) {
|
||||
|
@@ -24,9 +24,9 @@ class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||
if (!peerUid) {
|
||||
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> {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { OB11Message } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { RawNTMsg2Onebot } from '@/onebot/helper';
|
||||
|
||||
|
||||
export type ReturnDataType = OB11Message
|
||||
@@ -37,7 +38,7 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
|
||||
const msg = await NTQQMsgApi.getMsgsByMsgId(
|
||||
peer,
|
||||
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
|
||||
const retMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array');
|
||||
const retMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array');
|
||||
if (!retMsg) throw Error('消息为空');
|
||||
try {
|
||||
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
|
||||
|
@@ -23,12 +23,12 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
||||
throw `私聊${payload.user_id}不存在`;
|
||||
}
|
||||
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) {
|
||||
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> {
|
||||
|
@@ -2,8 +2,8 @@ import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/one
|
||||
import { uri2local } from '@/common/utils/file';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { AtType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
|
||||
import { AtType, ChatType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
|
||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
|
||||
export type MessageContext = {
|
||||
@@ -17,14 +17,21 @@ async function handleOb11FileLikeMessage(
|
||||
{ data: inputdata }: OB11MessageFileBase,
|
||||
{ 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 {
|
||||
path,
|
||||
isLocal,
|
||||
fileName,
|
||||
errMsg,
|
||||
success,
|
||||
} = (await uri2local(coreContext.NapCatTempPath, inputdata?.url || inputdata.file));
|
||||
} = (await uri2local(coreContext.NapCatTempPath, fileOrUrl));
|
||||
|
||||
if (!success) {
|
||||
coreContext.context.logger.logError('文件下载失败', errMsg);
|
||||
@@ -51,15 +58,18 @@ const _handlers: {
|
||||
[OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text),
|
||||
|
||||
[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, '全体成员');
|
||||
|
||||
// then the qq is a group member
|
||||
// Mlikiowa V2.0.35 Refactor Todo
|
||||
const uid = await coreContext.apis.UserApi.getUidByUinV2(`${atQQ}`);
|
||||
const NTQQGroupApi = coreContext.apis.GroupApi;
|
||||
const NTQQUserApi = coreContext.apis.UserApi;
|
||||
const atMember = await NTQQGroupApi.getGroupMember(context.peer.peerUid, 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');
|
||||
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
|
||||
let info = await NTQQUserApi.getUserDetailInfo(uid);
|
||||
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, info.nick || '');
|
||||
},
|
||||
[OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => {
|
||||
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||
@@ -161,7 +171,7 @@ const _handlers: {
|
||||
} else {
|
||||
postData = data;
|
||||
}
|
||||
// Mlikiowa V2.0.35 Refactor Todo
|
||||
// Mlikiowa V2.2.7 Refactor Todo
|
||||
const signUrl = obContext.configLoader.configData.musicSignUrl;
|
||||
if (!signUrl) {
|
||||
if (data.type === 'qq') {
|
||||
@@ -225,7 +235,7 @@ export default async function createSendElements(
|
||||
callResultList.push(callResult);
|
||||
}
|
||||
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 };
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
|
||||
async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMessage | undefined> {
|
||||
const selfPeer = {
|
||||
chatType: ChatType.friend,
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: coreContext.selfInfo.uid,
|
||||
};
|
||||
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> {
|
||||
const NTQQMsgApi = coreContext.apis.MsgApi;
|
||||
const selfPeer = {
|
||||
chatType: ChatType.friend,
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: coreContext.selfInfo.uid,
|
||||
};
|
||||
let nodeMsgIds: string[] = [];
|
||||
|
@@ -90,20 +90,43 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
|
||||
// This redundant design of Ob11 here should be blamed.
|
||||
const NTQQFriendApi = coreContext.apis.FriendApi;
|
||||
const NTQQUserApi = coreContext.apis.UserApi;
|
||||
const NTQQMsgApi = coreContext.apis.MsgApi;
|
||||
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
|
||||
return {
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id.toString(),
|
||||
};
|
||||
}
|
||||
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
||||
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
||||
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
|
||||
if (!Uid) throw '无法获取用户信息';
|
||||
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 {
|
||||
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: Uid!,
|
||||
guildId: payload.group_id?.toString() || '',
|
||||
guildId: '',
|
||||
};
|
||||
}
|
||||
throw '请指定 group_id 或 user_id';
|
||||
@@ -135,7 +158,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
if (payload.user_id && payload.message_type !== 'group') {
|
||||
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
|
||||
if (!isBuddy) {}
|
||||
if (!isBuddy) { }
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
12
src/onebot/action/system/GetCSRF.ts
Normal file
12
src/onebot/action/system/GetCSRF.ts
Normal 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: "",
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
import { OB11User } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
|
@@ -107,5 +107,7 @@ export enum ActionName {
|
||||
FetchEmojiLike = 'fetch_emoji_like',
|
||||
GetGuildProfile = "get_guild_service_profile",
|
||||
SetModelShow = "_set_model_show",
|
||||
SetInputStatus = "set_input_status"
|
||||
SetInputStatus = "set_input_status",
|
||||
GetCSRF = "get_csrf_token",
|
||||
DelGroupNotice = "_del_group_notice",
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11User } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { OB11Constructor } from '@/onebot/helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { RawNTMsg2Onebot } from '@/onebot/helper';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -24,7 +25,7 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
|
||||
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
||||
if (FastMsg.msgList.length > 0) {
|
||||
//扩展ret.info.changedList
|
||||
const lastestMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array');
|
||||
const lastestMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array');
|
||||
return {
|
||||
lastestMsg: lastestMsg,
|
||||
peerUin: t.peerUin,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { NapCatCore } from '@/core';
|
||||
import { GrayTipElement, NapCatCore } from '@/core';
|
||||
|
||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
import { OB11FriendPokeEvent } from '../event/notice/OB11PokeEvent';
|
||||
|
||||
export class OneBotFriendApi {
|
||||
obContext: NapCatOneBot11Adapter;
|
||||
@@ -10,4 +11,22 @@ export class OneBotFriendApi {
|
||||
this.obContext = obContext;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { NapCatCore } from '@/core';
|
||||
|
||||
import { ChatType, GrayTipElement, NapCatCore } from '@/core';
|
||||
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 {
|
||||
obContext: NapCatOneBot11Adapter;
|
||||
@@ -10,4 +15,133 @@ export class OneBotGroupApi {
|
||||
this.obContext = obContext;
|
||||
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.getGroupMember(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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
export * from './friend';
|
||||
export * from './group';
|
||||
export * from './user';
|
||||
export * from './user';
|
||||
export * from './msg';
|
361
src/onebot/api/msg.ts
Normal file
361
src/onebot/api/msg.ts
Normal file
@@ -0,0 +1,361 @@
|
||||
import { UUIDConverter } from '@/common/utils/helper';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { AtType, FaceIndex, MessageElement, NapCatCore, RawMessage } from '@/core';
|
||||
import { NapCatOneBot11Adapter, OB11MessageData, OB11MessageDataType } from '@/onebot';
|
||||
import { RawNTMsg2Onebot } from '../helper';
|
||||
|
||||
export class OneBotMsgApi {
|
||||
obContext: NapCatOneBot11Adapter;
|
||||
coreContext: NapCatCore;
|
||||
|
||||
constructor(obContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
|
||||
this.obContext = obContext;
|
||||
this.coreContext = coreContext;
|
||||
}
|
||||
async parseFileElement(msg: RawMessage, element: MessageElement) {
|
||||
const fileElement = element.fileElement;
|
||||
if (!fileElement) return undefined;
|
||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
||||
let 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 message_data: OB11MessageData = {
|
||||
data: {} as any,
|
||||
type: 'unknown' as any,
|
||||
};
|
||||
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('@', '');
|
||||
}
|
||||
}
|
||||
|
||||
message_data = {
|
||||
type: OB11MessageDataType.at,
|
||||
data: {
|
||||
qq: qq!,
|
||||
//name,
|
||||
},
|
||||
};
|
||||
return message_data;
|
||||
}
|
||||
async parseTextElement(msg: RawMessage, element: MessageElement) {
|
||||
const textElement = element.textElement;
|
||||
if (!textElement) return undefined;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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;
|
||||
let 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 RawNTMsg2Onebot(this.coreContext, this.obContext, 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;
|
||||
let message_data: OB11MessageData = {
|
||||
data: {} as any,
|
||||
type: 'unknown' as any,
|
||||
};
|
||||
message_data['type'] = OB11MessageDataType.json;
|
||||
message_data['data']['data'] = arkElement.bytesData;
|
||||
return message_data;
|
||||
}
|
||||
}
|
113
src/onebot/helper/converter.ts
Normal file
113
src/onebot/helper/converter.ts
Normal 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);
|
||||
}
|
||||
}
|
@@ -79,8 +79,4 @@ export function encodeCQCode(data: OB11MessageData) {
|
||||
}
|
||||
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))
|
||||
}
|
@@ -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
154
src/onebot/helper/event.ts
Normal 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.parseGroupMemberIncreaseEvent(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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
src/onebot/helper/index.ts
Normal file
6
src/onebot/helper/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from './config';
|
||||
export * from './parseMessage';
|
||||
export * from './converter';
|
||||
export * from './quick';
|
||||
export * from './genMessage';
|
||||
export * from './event';
|
117
src/onebot/helper/parseMessage.ts
Normal file
117
src/onebot/helper/parseMessage.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType } from '..';
|
||||
import { AtType, ChatType, NapCatCore, RawMessage } from '@/core';
|
||||
import { EventType } from '../event/OB11BaseEvent';
|
||||
import { OB11Constructor } from './converter';
|
||||
import { encodeCQCode } from './cqcode';
|
||||
|
||||
|
||||
export async function RawNTMsg2Onebot(
|
||||
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 NTQQMsgApi = core.apis.MsgApi;
|
||||
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.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: core.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) {
|
||||
let textAtMsgData = await obcore.apiContext.MsgApi.parseTextElemntWithAt(msg, element);
|
||||
if (textAtMsgData) message_data = textAtMsgData
|
||||
} else if (element.textElement) {
|
||||
let textMsgData = await obcore.apiContext.MsgApi.parseTextElement(msg, element);
|
||||
if (textMsgData) message_data = textMsgData;
|
||||
} else if (element.replyElement) {
|
||||
let replyMsgData = await obcore.apiContext.MsgApi.parseReplyElement(msg, element);
|
||||
if (replyMsgData) message_data = replyMsgData;
|
||||
} else if (element.picElement) {
|
||||
let PicMsgData = await obcore.apiContext.MsgApi.parsePicElement(msg, element);
|
||||
if (PicMsgData) message_data = PicMsgData;
|
||||
} else if (element.fileElement) {
|
||||
let FileMsgData = await obcore.apiContext.MsgApi.parseFileElement(msg, element);
|
||||
if (FileMsgData) message_data = FileMsgData;
|
||||
} else if (element.videoElement) {
|
||||
let videoMsgData = await obcore.apiContext.MsgApi.parseVideoElement(msg, element);
|
||||
if (videoMsgData) message_data = videoMsgData;
|
||||
} else if (element.pttElement) {
|
||||
let pttMsgData = await obcore.apiContext.MsgApi.parsePTTElement(msg, element);
|
||||
if (pttMsgData) message_data = pttMsgData;
|
||||
} else if (element.arkElement) {
|
||||
let arkMsgData = await obcore.apiContext.MsgApi.parseArkElement(msg, element);
|
||||
if (arkMsgData) message_data = arkMsgData;
|
||||
} else if (element.faceElement) {
|
||||
let faceMsgData = await obcore.apiContext.MsgApi.parseFaceElement(msg, element);
|
||||
if (faceMsgData) message_data = faceMsgData;
|
||||
} else if (element.marketFaceElement) {
|
||||
let marketFaceMsgData = await obcore.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) {
|
||||
let multiForwardMsgData = await obcore.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;
|
||||
}
|
@@ -20,15 +20,15 @@ async function handleMsg(coreContext: NapCatCore, obContext: NapCatOneBot11Adapt
|
||||
msg = msg as OB11Message;
|
||||
const reply = quickAction.reply;
|
||||
const peer: Peer = {
|
||||
chatType: ChatType.friend,
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: await coreContext.apis.UserApi.getUidByUinV2(msg.user_id.toString()) as string,
|
||||
};
|
||||
if (msg.message_type == 'private') {
|
||||
if (msg.sub_type === 'group') {
|
||||
peer.chatType = ChatType.temp;
|
||||
peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
|
||||
}
|
||||
} else {
|
||||
peer.chatType = ChatType.group;
|
||||
peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
|
||||
peer.peerUid = msg.group_id!.toString();
|
||||
}
|
||||
if (reply) {
|
||||
|
@@ -3,13 +3,14 @@ import {
|
||||
BuddyReqType,
|
||||
ChatType,
|
||||
GroupListener,
|
||||
GroupNotifyTypes,
|
||||
InstanceContext,
|
||||
MsgListener,
|
||||
NapCatCore,
|
||||
RawMessage,
|
||||
SendStatusType,
|
||||
GroupMemberRole,
|
||||
GroupNotifyMsgType,
|
||||
GroupNotifyMsgStatus,
|
||||
} from '@/core';
|
||||
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
|
||||
import { OneBotApiContextType } from '@/onebot/types';
|
||||
@@ -21,12 +22,11 @@ import {
|
||||
OB11PassiveWebSocketAdapter,
|
||||
} from '@/onebot/network';
|
||||
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 { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
||||
import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { OB11Constructor } from '@/onebot/helper/data';
|
||||
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
|
||||
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
|
||||
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 { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
|
||||
import { LRUCache } from '@/common/utils/LRU';
|
||||
import { NT2GroupEvent, NT2PrivateEvent, RawNTMsg2Onebot } from './helper';
|
||||
|
||||
//OneBot实现类
|
||||
export class NapCatOneBot11Adapter {
|
||||
@@ -56,6 +57,7 @@ export class NapCatOneBot11Adapter {
|
||||
GroupApi: new OneBotGroupApi(this, core),
|
||||
UserApi: new OneBotUserApi(this, core),
|
||||
FriendApi: new OneBotFriendApi(this, core),
|
||||
MsgApi: new OneBotMsgApi(this, core),
|
||||
};
|
||||
this.actions = createActionMap(this, core);
|
||||
this.networkManager = new OB11NetworkManager();
|
||||
@@ -247,15 +249,16 @@ export class NapCatOneBot11Adapter {
|
||||
}
|
||||
};
|
||||
const msgIdSend = new LRUCache<string, boolean>(100);
|
||||
const recallMsgs = new LRUCache<string, boolean>(100);
|
||||
msgListener.onMsgInfoListUpdate = async msgList => {
|
||||
this.emitRecallMsg(msgList)
|
||||
this.emitRecallMsg(msgList, recallMsgs)
|
||||
.catch(e => this.context.logger.logError('处理消息失败', e));
|
||||
|
||||
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
|
||||
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
|
||||
msgIdSend.put(msg.msgId, true);
|
||||
// 完成后再post
|
||||
OB11Constructor.message(this.core, this, msg)
|
||||
RawNTMsg2Onebot(this.core, this, msg)
|
||||
.then((ob11Msg) => {
|
||||
if (!ob11Msg) return;
|
||||
ob11Msg.target_id = parseInt(msg.peerUin);
|
||||
@@ -275,15 +278,23 @@ export class NapCatOneBot11Adapter {
|
||||
};
|
||||
|
||||
this.context.session.getMsgService().addKernelMsgListener(
|
||||
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
|
||||
proxiedListenerOf(msgListener, this.context.logger) as any
|
||||
);
|
||||
}
|
||||
|
||||
private initBuddyListener() {
|
||||
const buddyListener = new BuddyListener();
|
||||
|
||||
buddyListener.onBuddyReqChange = reqs => {
|
||||
reqs.buddyReqs.forEach(async req => {
|
||||
buddyListener.onBuddyReqChange = async reqs => {
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
@@ -298,11 +309,11 @@ export class NapCatOneBot11Adapter {
|
||||
} catch (e) {
|
||||
this.context.logger.logDebug('获取加好友者QQ号失败', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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) => {
|
||||
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
|
||||
if (![
|
||||
GroupNotifyTypes.ADMIN_SET,
|
||||
GroupNotifyTypes.ADMIN_UNSET,
|
||||
GroupNotifyTypes.ADMIN_UNSET_OTHER,
|
||||
GroupNotifyMsgType.SET_ADMIN,
|
||||
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
|
||||
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
|
||||
].includes(notifies[0]?.type)) {
|
||||
for (const notify of notifies) {
|
||||
notify.time = Date.now();
|
||||
const notifyTime = parseInt(notify.seq) / 1000 / 1000;
|
||||
// log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`);
|
||||
if (notifyTime < this.bootTime) {
|
||||
@@ -328,9 +338,9 @@ export class NapCatOneBot11Adapter {
|
||||
this.context.logger.logDebug('收到群通知', notify);
|
||||
|
||||
if ([
|
||||
GroupNotifyTypes.ADMIN_SET,
|
||||
GroupNotifyTypes.ADMIN_UNSET,
|
||||
GroupNotifyTypes.ADMIN_UNSET_OTHER,
|
||||
GroupNotifyMsgType.SET_ADMIN,
|
||||
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
|
||||
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
|
||||
].includes(notify.type)) {
|
||||
const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid);
|
||||
this.context.logger.logDebug('有管理员变动通知');
|
||||
@@ -339,20 +349,21 @@ export class NapCatOneBot11Adapter {
|
||||
this.context.logger.logDebug('开始获取变动的管理员');
|
||||
if (member1) {
|
||||
this.context.logger.logDebug('变动管理员获取成功');
|
||||
// member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
|
||||
|
||||
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
|
||||
this.core,
|
||||
parseInt(notify.group.groupCode),
|
||||
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)
|
||||
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
|
||||
} else {
|
||||
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);
|
||||
const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
|
||||
let operatorId = member1Uin;
|
||||
@@ -376,8 +387,8 @@ export class NapCatOneBot11Adapter {
|
||||
.catch(e => this.context.logger.logError('处理群成员退出失败', e));
|
||||
// notify.status == 1 表示未处理 2表示处理完成
|
||||
} else if ([
|
||||
GroupNotifyTypes.JOIN_REQUEST,
|
||||
].includes(notify.type) && notify.status == 1) {
|
||||
GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
|
||||
].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
|
||||
this.context.logger.logDebug('有加群请求');
|
||||
try {
|
||||
let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
|
||||
@@ -397,7 +408,7 @@ export class NapCatOneBot11Adapter {
|
||||
} catch (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}`);
|
||||
const groupInviteEvent = new OB11GroupRequestEvent(
|
||||
this.core,
|
||||
@@ -430,7 +441,7 @@ export class NapCatOneBot11Adapter {
|
||||
member.role === GroupMemberRole.admin ? 'set' : 'unset',
|
||||
);
|
||||
this.networkManager.emitEvent(groupAdminNoticeEvent)
|
||||
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
|
||||
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
|
||||
existMember.isChangeRole = false;
|
||||
this.context.logger.logDebug('群管理员变动处理完毕');
|
||||
});
|
||||
@@ -438,14 +449,14 @@ export class NapCatOneBot11Adapter {
|
||||
};
|
||||
|
||||
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) {
|
||||
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
|
||||
this.context.logger.logDebug('收到新消息 RawMessage', message);
|
||||
OB11Constructor.message(this.core, this, message, messagePostFormat).then((ob11Msg) => {
|
||||
RawNTMsg2Onebot(this.core, this, message, messagePostFormat).then((ob11Msg) => {
|
||||
if (!ob11Msg) return;
|
||||
this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
|
||||
if (debug) {
|
||||
@@ -467,14 +478,14 @@ export class NapCatOneBot11Adapter {
|
||||
this.networkManager.emitEvent(ob11Msg);
|
||||
}).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) {
|
||||
// log("post group event", groupEvent);
|
||||
this.networkManager.emitEvent(groupEvent);
|
||||
}
|
||||
}).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) {
|
||||
// log("post private event", privateEvent);
|
||||
this.networkManager.emitEvent(privateEvent);
|
||||
@@ -482,16 +493,17 @@ export class NapCatOneBot11Adapter {
|
||||
}).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) {
|
||||
// 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);
|
||||
if (!oriMessageId) {
|
||||
continue;
|
||||
}
|
||||
if (message.chatType == ChatType.friend) {
|
||||
if (message.chatType == ChatType.KCHATTYPEC2C) {
|
||||
const friendRecallEvent = new OB11FriendRecallNoticeEvent(
|
||||
this.core,
|
||||
parseInt(message!.senderUin),
|
||||
@@ -499,7 +511,7 @@ export class NapCatOneBot11Adapter {
|
||||
);
|
||||
this.networkManager.emitEvent(friendRecallEvent)
|
||||
.catch(e => this.context.logger.logError('处理好友消息撤回失败', e));
|
||||
} else if (message.chatType == ChatType.group) {
|
||||
} else if (message.chatType == ChatType.KCHATTYPEGROUP) {
|
||||
let operatorId = message.senderUin;
|
||||
for (const element of message.elements) {
|
||||
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
|
||||
|
@@ -71,6 +71,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
||||
|
||||
this.connection = new WebSocket(this.url, {
|
||||
maxPayload: 1024 * 1024 * 1024,
|
||||
handshakeTimeout: 2000,
|
||||
perMessageDeflate: false,
|
||||
headers: {
|
||||
'X-Self-ID': this.coreContext.selfInfo.uin,
|
||||
'Authorization': `Bearer ${this.token}`,
|
||||
@@ -134,6 +136,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
|
||||
this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData);
|
||||
} catch (e) {
|
||||
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo));
|
||||
return;
|
||||
}
|
||||
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
|
||||
const retdata = await this.actions.get(receiveData.action)
|
||||
|
@@ -36,7 +36,11 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
||||
this.logger = coreContext.context.logger;
|
||||
|
||||
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;
|
||||
this.wsServer.on('connection', async (wsClient, wsReq) => {
|
||||
if (!this.isOpen) {
|
||||
@@ -151,6 +155,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
||||
//this.logger.logDebug('收到正向Websocket消息', receiveData);
|
||||
} catch (e) {
|
||||
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient);
|
||||
return;
|
||||
}
|
||||
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
|
||||
const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || '');
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { OneBotFriendApi, OneBotGroupApi, OneBotUserApi } from '../api';
|
||||
import { OneBotFriendApi, OneBotGroupApi, OneBotMsgApi, OneBotUserApi } from '../api';
|
||||
|
||||
export interface OneBotApiContextType {
|
||||
FriendApi: OneBotFriendApi;
|
||||
UserApi: OneBotUserApi;
|
||||
GroupApi: OneBotGroupApi;
|
||||
MsgApi: OneBotMsgApi;
|
||||
}
|
||||
|
@@ -43,12 +43,12 @@ export async function NCoreInitShell() {
|
||||
|
||||
// from constructor
|
||||
const engine = new wrapper.NodeIQQNTWrapperEngine();
|
||||
const util = new wrapper.NodeQQNTWrapperUtil();
|
||||
//const util = wrapper.NodeQQNTWrapperUtil.get();
|
||||
const loginService = new wrapper.NodeIKernelLoginService();
|
||||
const session = new wrapper.NodeIQQNTWrapperSession();
|
||||
|
||||
// from get dataPath
|
||||
let dataPath = util.getNTUserDataInfoConfig();
|
||||
let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
|
||||
if (!dataPath) {
|
||||
dataPath = path.resolve(os.homedir(), './.config/QQ');
|
||||
fs.mkdirSync(dataPath, { recursive: true });
|
||||
@@ -70,7 +70,7 @@ export async function NCoreInitShell() {
|
||||
},
|
||||
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
|
||||
},
|
||||
new wrapper.NodeIGlobalAdapter(new GlobalAdapter()),
|
||||
new GlobalAdapter() as any,
|
||||
);
|
||||
loginService.initConfig({
|
||||
machineId: '',
|
||||
@@ -140,8 +140,7 @@ export async function NCoreInitShell() {
|
||||
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
|
||||
};
|
||||
|
||||
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
|
||||
proxiedListenerOf(loginListener, logger)));
|
||||
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
|
||||
|
||||
// 实现WebUi快速登录
|
||||
loginService.getLoginList().then((res) => {
|
||||
@@ -188,10 +187,9 @@ export async function NCoreInitShell() {
|
||||
} else {
|
||||
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
|
||||
if (historyLoginList.length > 0) {
|
||||
logger.log(`可用于快速登录的 QQ:\n${
|
||||
historyLoginList
|
||||
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
||||
.join('\n')
|
||||
logger.log(`可用于快速登录的 QQ:\n${historyLoginList
|
||||
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
||||
.join('\n')
|
||||
}`);
|
||||
}
|
||||
loginService.getQRCodePicture();
|
||||
@@ -220,9 +218,9 @@ export async function NCoreInitShell() {
|
||||
};
|
||||
session.init(
|
||||
sessionConfig,
|
||||
new wrapper.NodeIDependsAdapter(new DependsAdapter()),
|
||||
new wrapper.NodeIDispatcherAdapter(new DispatcherAdapter()),
|
||||
new wrapper.NodeIKernelSessionListener(sessionListener),
|
||||
new DependsAdapter() as any,
|
||||
new DispatcherAdapter() as any,
|
||||
sessionListener as any,
|
||||
);
|
||||
try {
|
||||
session.startNT(0);
|
||||
|
@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
undefined,
|
||||
SettingButton('V2.0.35', 'napcat-update-button', 'secondary'),
|
||||
SettingButton('V2.2.7', 'napcat-update-button', 'secondary'),
|
||||
),
|
||||
]),
|
||||
SettingList([
|
||||
|
@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
void 0,
|
||||
SettingButton("V2.0.35", "napcat-update-button", "secondary")
|
||||
SettingButton("V2.2.7", "napcat-update-button", "secondary")
|
||||
)
|
||||
]),
|
||||
SettingList([
|
||||
|
Reference in New Issue
Block a user