Compare commits

..

4 Commits

Author SHA1 Message Date
手瓜一十雪
3b5e6553cd chore 2024-08-23 11:04:47 +08:00
手瓜一十雪
509390af20 release: 2.2.2 2024-08-23 11:01:24 +08:00
手瓜一十雪
9ad511a9c0 release: 2.2.1 2024-08-23 10:57:13 +08:00
手瓜一十雪
89c102513d release: 2.2.0 2024-08-23 10:55:19 +08:00
20 changed files with 315 additions and 293 deletions

View File

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

View File

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

View File

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

View File

@@ -162,89 +162,7 @@ export class LegacyNTEventWrapper {
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
}); });
} }
async CallNormalEventV2<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
>(
EventName = '',
ListenerName = '',
waitTimes = 1,
timeout: number = 3000,
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
checkerListener: (...args: Parameters<ListenerType>) => boolean = () => true,
...args: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
if (complete == 0) {
reject(
new Error(
'Timeout: NTEvent EventName:' +
EventName +
' ListenerName:' +
ListenerName +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const ListenerNameList = ListenerName.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checkerListener,
func: (...args: any[]) => {
complete++;
//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< async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>, EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void ListenerType extends (...args: any[]) => void

View File

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

View File

@@ -1,5 +1,5 @@
import { Friend, FriendV2, User } from '@/core/entities'; import { Friend, FriendV2, User } from '@/core/entities';
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelBuddyListener, NodeIKernelBuddyService, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core'; import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
import { LimitedHashTable } from '@/common/utils/MessageUnique'; import { LimitedHashTable } from '@/common/utils/MessageUnique';
export class NTQQFriendApi { export class NTQQFriendApi {
@@ -70,14 +70,29 @@ export class NTQQFriendApi {
async isBuddy(uid: string) { async isBuddy(uid: string) {
return this.context.session.getBuddyService().isBuddy(uid); return this.context.session.getBuddyService().isBuddy(uid);
} }
async clearBuddyReqUnreadCnt() {
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt(); /**
} * @deprecated
async getBuddyReq() { * @param forced
const [, ret] = await this.core.eventWrapper.CallNormalEventV2 * @returns
<NodeIKernelBuddyService['getBuddyReq'], NodeIKernelBuddyListener['onBuddyReqChange']> */
('NodeIKernelBuddyService/getBuddyReq', 'NodeIKernelBuddyListener/onBuddyReqChange', 1, 5000); async getFriends(forced = false): Promise<User[]> {
return ret; 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 handleFriendRequest(flag: string, accept: boolean) { async handleFriendRequest(flag: string, accept: boolean) {

View File

@@ -7,8 +7,6 @@ import {
GroupNotify, GroupNotify,
GroupRequestOperateTypes, GroupRequestOperateTypes,
InstanceContext, InstanceContext,
KickMemberInfo,
kickMemberV2Req,
MemberExtSourceType, MemberExtSourceType,
NapCatCore, NapCatCore,
NodeIKernelGroupListener, NodeIKernelGroupListener,
@@ -44,12 +42,12 @@ export class NTQQGroupApi {
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate']; type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType> const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType>
( (
'NodeIKernelGroupService/getGroupList', 'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate', 'NodeIKernelGroupListener/onGroupListUpdate',
1, 1,
5000, 5000,
() => true, () => true,
forced, forced,
); );
return groupList; return groupList;
} }
@@ -221,17 +219,7 @@ export class NTQQGroupApi {
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数 // GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
return this.context.session.getGroupService().addGroupEssence(param); return this.context.session.getGroupService().addGroupEssence(param);
} }
async kickMemberV2Inner(param: kickMemberV2Req) {
return this.context.session.getGroupService().kickMemberV2(param);
}
async 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) { async removeGroupEssence(GroupCode: string, msgId: string) {
// 代码没测过 // 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom // 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -252,14 +240,14 @@ export class NTQQGroupApi {
async getSingleScreenNotifies(num: number) { 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> 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', 'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies', 'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
1, 1,
5000, 5000,
() => true, () => true,
false, false,
'', '',
num, num,
); );
return notifies; return notifies;
} }
@@ -271,12 +259,12 @@ export class NTQQGroupApi {
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced); //return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void> const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
( (
'NodeIKernelGroupListener/onMemberInfoChange', 'NodeIKernelGroupListener/onMemberInfoChange',
1, 1,
forced ? 5000 : 250, forced ? 5000 : 250,
(params) => { (params) => {
return params === GroupCode; return params === GroupCode;
}, },
); );
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo'); const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
const retData = await EventFunc!(GroupCode, [uid], forced); const retData = await EventFunc!(GroupCode, [uid], forced);
@@ -345,7 +333,7 @@ export class NTQQGroupApi {
'NodeIKernelGroupService/getGroupRecommendContactArkJson', 'NodeIKernelGroupService/getGroupRecommendContactArkJson',
5000, 5000,
GroupCode, GroupCode,
); );
return ret.arkJson; return ret.arkJson;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,18 +3,14 @@ import {
GroupExtParam, GroupExtParam,
GroupMember, GroupMember,
GroupMemberRole, GroupMemberRole,
GroupNotifyMsgType, GroupNotifyTypes,
GroupRequestOperateTypes, GroupRequestOperateTypes,
kickMemberV2Req,
} from '@/core/entities'; } from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底 //高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
export interface NodeIKernelGroupService { export interface NodeIKernelGroupService {
kickMemberV2(param: kickMemberV2Req): Promise<GeneralCallResult>;
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
getMemberCommonInfo(Req: { getMemberCommonInfo(Req: {
groupCode: string, groupCode: string,
startUin: string, startUin: string,
@@ -199,7 +195,7 @@ export interface NodeIKernelGroupService {
operateType: GroupRequestOperateTypes, // 2 拒绝 operateType: GroupRequestOperateTypes, // 2 拒绝
targetMsg: { targetMsg: {
seq: string, // 通知序列号 seq: string, // 通知序列号
type: GroupNotifyMsgType, type: GroupNotifyTypes,
groupCode: string, groupCode: string,
postscript: string postscript: string
} }

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -171,7 +171,7 @@ const _handlers: {
} else { } else {
postData = data; postData = data;
} }
// Mlikiowa V2.2.6 Refactor Todo // Mlikiowa V2.2.2 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl; const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) { if (!signUrl) {
if (data.type === 'qq') { if (data.type === 'qq') {

View File

@@ -90,7 +90,6 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
// This redundant design of Ob11 here should be blamed. // This redundant design of Ob11 here should be blamed.
const NTQQFriendApi = coreContext.apis.FriendApi; const NTQQFriendApi = coreContext.apis.FriendApi;
const NTQQUserApi = coreContext.apis.UserApi; const NTQQUserApi = coreContext.apis.UserApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) { if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
return { return {
chatType: ChatType.KCHATTYPEGROUP, chatType: ChatType.KCHATTYPEGROUP,
@@ -99,34 +98,12 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
} }
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) { if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!Uid) throw '无法获取用户信息'; const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
const isBuddy = await NTQQFriendApi.isBuddy(Uid); //console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
if (!isBuddy) {
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, Uid);
if (ret.tmpChatInfo?.groupCode) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: '',
};
}
if (payload.group_id) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: payload.group_id.toString(),
};
}
return {
chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!,
guildId: '',
};
}
return { return {
chatType: ChatType.KCHATTYPEC2C, chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid!, peerUid: Uid!,
guildId: '', guildId: payload.group_id?.toString() || '',
}; };
} }
throw '请指定 group_id 或 user_id'; throw '请指定 group_id 或 user_id';
@@ -158,7 +135,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (payload.user_id && payload.message_type !== 'group') { if (payload.user_id && payload.message_type !== 'group') {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(uid!); const isBuddy = await NTQQFriendApi.isBuddy(uid!);
if (!isBuddy) { } if (!isBuddy) {}
} }
return { valid: true }; return { valid: true };
} }

View File

@@ -65,12 +65,11 @@ export class OneBotMsgApi {
name = content.replace('@', ''); name = content.replace('@', '');
} }
} }
message_data = { message_data = {
type: OB11MessageDataType.at, type: OB11MessageDataType.at,
data: { data: {
qq: qq!, qq: qq!,
//name, name,
}, },
}; };
return message_data; return message_data;

View File

@@ -3,14 +3,13 @@ import {
BuddyReqType, BuddyReqType,
ChatType, ChatType,
GroupListener, GroupListener,
GroupNotifyTypes,
InstanceContext, InstanceContext,
MsgListener, MsgListener,
NapCatCore, NapCatCore,
RawMessage, RawMessage,
SendStatusType, SendStatusType,
GroupMemberRole, GroupMemberRole,
GroupNotifyMsgType,
GroupNotifyMsgStatus,
} from '@/core'; } from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config'; import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
import { OneBotApiContextType } from '@/onebot/types'; import { OneBotApiContextType } from '@/onebot/types';
@@ -284,16 +283,8 @@ export class NapCatOneBot11Adapter {
private initBuddyListener() { private initBuddyListener() {
const buddyListener = new BuddyListener(); const buddyListener = new BuddyListener();
buddyListener.onBuddyReqChange = async reqs => { buddyListener.onBuddyReqChange = reqs => {
this.core.apis.FriendApi.clearBuddyReqUnreadCnt(); reqs.buddyReqs.forEach(async req => {
for (let i = 0; i < reqs.unreadNums; i++) {
const req = reqs.buddyReqs[i];
//req.isBuddy === false是单向好友 null为常规情况
// if (req.isBuddy === false && ) {
// const NTQQFriendApi = this.core.apis.FriendApi;
// await NTQQFriendApi.handleFriendRequest(req.friendUid + '|' + req.reqTime, true);
// }
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
return; return;
} }
@@ -308,7 +299,7 @@ export class NapCatOneBot11Adapter {
} catch (e) { } catch (e) {
this.context.logger.logDebug('获取加好友者QQ号失败', e); this.context.logger.logDebug('获取加好友者QQ号失败', e);
} }
} });
}; };
this.context.session.getBuddyService().addKernelBuddyListener( this.context.session.getBuddyService().addKernelBuddyListener(
@@ -322,11 +313,12 @@ export class NapCatOneBot11Adapter {
groupListener.onGroupNotifiesUpdated = async (_, notifies) => { groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]); //console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
if (![ if (![
GroupNotifyMsgType.SET_ADMIN, GroupNotifyTypes.ADMIN_SET,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, GroupNotifyTypes.ADMIN_UNSET,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, GroupNotifyTypes.ADMIN_UNSET_OTHER,
].includes(notifies[0]?.type)) { ].includes(notifies[0]?.type)) {
for (const notify of notifies) { for (const notify of notifies) {
notify.time = Date.now();
const notifyTime = parseInt(notify.seq) / 1000 / 1000; const notifyTime = parseInt(notify.seq) / 1000 / 1000;
// log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`); // log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`);
if (notifyTime < this.bootTime) { if (notifyTime < this.bootTime) {
@@ -337,9 +329,9 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('收到群通知', notify); this.context.logger.logDebug('收到群通知', notify);
if ([ if ([
GroupNotifyMsgType.SET_ADMIN, GroupNotifyTypes.ADMIN_SET,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED, GroupNotifyTypes.ADMIN_UNSET,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN, GroupNotifyTypes.ADMIN_UNSET_OTHER,
].includes(notify.type)) { ].includes(notify.type)) {
const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid); const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid);
this.context.logger.logDebug('有管理员变动通知'); this.context.logger.logDebug('有管理员变动通知');
@@ -348,21 +340,20 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('开始获取变动的管理员'); this.context.logger.logDebug('开始获取变动的管理员');
if (member1) { if (member1) {
this.context.logger.logDebug('变动管理员获取成功'); this.context.logger.logDebug('变动管理员获取成功');
// member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent( const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core, this.core,
parseInt(notify.group.groupCode), parseInt(notify.group.groupCode),
parseInt(member1.uin), parseInt(member1.uin),
[ [GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(notify.type) ? 'unset' : 'set',
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN
].includes(notify.type) ? 'unset' : 'set',
); );
this.networkManager.emitEvent(groupAdminNoticeEvent) this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e)); .catch(e => this.context.logger.logError('处理群管理员变动失败', e));
} else { } else {
this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode)); this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode));
} }
} else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) { } else if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) {
this.context.logger.logDebug('有成员退出通知', notify); this.context.logger.logDebug('有成员退出通知', notify);
const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
let operatorId = member1Uin; let operatorId = member1Uin;
@@ -386,8 +377,8 @@ export class NapCatOneBot11Adapter {
.catch(e => this.context.logger.logError('处理群成员退出失败', e)); .catch(e => this.context.logger.logError('处理群成员退出失败', e));
// notify.status == 1 表示未处理 2表示处理完成 // notify.status == 1 表示未处理 2表示处理完成
} else if ([ } else if ([
GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS, GroupNotifyTypes.JOIN_REQUEST,
].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { ].includes(notify.type) && notify.status == 1) {
this.context.logger.logDebug('有加群请求'); this.context.logger.logDebug('有加群请求');
try { try {
let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
@@ -407,7 +398,7 @@ export class NapCatOneBot11Adapter {
} catch (e) { } catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e); this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
} }
} else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { } else if (notify.type == GroupNotifyTypes.INVITE_ME && notify.status == 1) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent( const groupInviteEvent = new OB11GroupRequestEvent(
this.core, this.core,

View File

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

View File

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