mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
Merge branch 'main' into event-via-emitter
This commit is contained in:
commit
ce81429902
@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "2.2.20",
|
"version": "2.2.21",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.2.20",
|
"version": "2.2.21",
|
||||||
"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",
|
||||||
|
@ -1 +1 @@
|
|||||||
export const napCatVersion = '2.2.20';
|
export const napCatVersion = '2.2.21';
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
NapCatCore,
|
NapCatCore,
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import { isNumeric, runAllWithTimeout } from '@/common/helper';
|
import { isNumeric, runAllWithTimeout } from '@/common/helper';
|
||||||
|
import { LimitedHashTable } from '@/common/message-unique';
|
||||||
|
|
||||||
export class NTQQGroupApi {
|
export class NTQQGroupApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
@ -19,6 +20,7 @@ export class NTQQGroupApi {
|
|||||||
groupCache: Map<string, Group> = new Map<string, Group>();
|
groupCache: Map<string, Group> = new Map<string, Group>();
|
||||||
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||||
groups: Group[] = [];
|
groups: Group[] = [];
|
||||||
|
essenceLRU = new LimitedHashTable<number, string>(1000);
|
||||||
|
|
||||||
constructor(context: InstanceContext, core: NapCatCore) {
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -33,6 +35,14 @@ export class NTQQGroupApi {
|
|||||||
}
|
}
|
||||||
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
|
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
|
||||||
}
|
}
|
||||||
|
async fetchGroupEssenceList(groupCode: string) {
|
||||||
|
const pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||||
|
return this.context.session.getGroupService().fetchGroupEssenceList({
|
||||||
|
groupCode: groupCode,
|
||||||
|
pageStart: 0,
|
||||||
|
pageLimit: 300
|
||||||
|
}, pskey);
|
||||||
|
}
|
||||||
async clearGroupNotifiesUnreadCount(unk: boolean) {
|
async clearGroupNotifiesUnreadCount(unk: boolean) {
|
||||||
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(unk);
|
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(unk);
|
||||||
}
|
}
|
||||||
@ -273,7 +283,15 @@ export class NTQQGroupApi {
|
|||||||
//应该是直接返回不需要Listener的 未经测试 需测试再发布
|
//应该是直接返回不需要Listener的 未经测试 需测试再发布
|
||||||
return this.context.session.getGroupService().quitGroupV2(param);
|
return this.context.session.getGroupService().quitGroupV2(param);
|
||||||
}
|
}
|
||||||
|
async removeGroupEssenceBySeq(GroupCode: string, msgRandom: string, msgSeq: string) {
|
||||||
|
const param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(msgRandom),
|
||||||
|
msgSeq: parseInt(msgSeq),
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return this.context.session.getGroupService().removeGroupEssence(param);
|
||||||
|
}
|
||||||
async removeGroupEssence(GroupCode: string, msgId: string) {
|
async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||||
// 代码没测过
|
// 代码没测过
|
||||||
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
@ -27,7 +27,7 @@ export class NTQQWebApi {
|
|||||||
msg_random: msgRandom,
|
msg_random: msgRandom,
|
||||||
target_group_code: targetGroupCode,
|
target_group_code: targetGroupCode,
|
||||||
}).toString()
|
}).toString()
|
||||||
}`;
|
}`;
|
||||||
try {
|
try {
|
||||||
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -35,19 +35,17 @@ export class NTQQWebApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
async getGroupEssenceMsg(GroupCode: 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?${new URLSearchParams({
|
||||||
bkn: this.getBknFromCookie(cookieObject),
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
group_code: GroupCode,
|
group_code: GroupCode,
|
||||||
page_start,
|
|
||||||
page_limit: '20',
|
|
||||||
}).toString()
|
}).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 +61,14 @@ 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 +81,14 @@ 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,15 +121,15 @@ 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,
|
||||||
pinned: '0',
|
pinned: '0',
|
||||||
type: '1',
|
type: '1',
|
||||||
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
||||||
}).toString()
|
}).toString()
|
||||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
return ret;
|
return ret;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -162,7 +160,7 @@ export class NTQQWebApi {
|
|||||||
gc: Internal_groupCode,
|
gc: Internal_groupCode,
|
||||||
type: Internal_type.toString(),
|
type: Internal_type.toString(),
|
||||||
}).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) });
|
||||||
|
@ -6,8 +6,9 @@ const SchemaData = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: ['string', 'number'] },
|
group_id: { type: ['string', 'number'] },
|
||||||
start_index: { type: 'number' },
|
start_index: { type: ['string', 'number'] },
|
||||||
file_count: { type: 'number' },
|
file_count: { type: ['string', 'number'] },
|
||||||
|
folder_id: { type: ['string', 'number'] },
|
||||||
},
|
},
|
||||||
required: ['group_id', 'start_index', 'file_count'],
|
required: ['group_id', 'start_index', 'file_count'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@ -20,12 +21,19 @@ export class GetGroupFileList extends BaseAction<Payload, { FileList: Array<any>
|
|||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const NTQQMsgApi = this.core.apis.MsgApi;
|
const NTQQMsgApi = this.core.apis.MsgApi;
|
||||||
|
let param = {};
|
||||||
|
if (payload.folder_id) {
|
||||||
|
param = {
|
||||||
|
folderId: payload.folder_id.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), {
|
const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), {
|
||||||
sortType: 1,
|
sortType: 1,
|
||||||
fileCount: payload.file_count,
|
fileCount: +payload.file_count,
|
||||||
startIndex: payload.start_index,
|
startIndex: +payload.start_index,
|
||||||
sortOrder: 2,
|
sortOrder: 2,
|
||||||
showOnlinedocFolder: 0,
|
showOnlinedocFolder: 0,
|
||||||
|
...param
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,13 @@ export default class DelEssenceMsg extends BaseAction<Payload, any> {
|
|||||||
async _handle(payload: Payload): Promise<any> {
|
async _handle(payload: Payload): Promise<any> {
|
||||||
const NTQQGroupApi = this.core.apis.GroupApi;
|
const NTQQGroupApi = this.core.apis.GroupApi;
|
||||||
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
|
const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id);
|
||||||
if (!msg) throw new Error('msg not found');
|
const NTQQWebApi = this.core.apis.WebApi;
|
||||||
|
if (!msg) {
|
||||||
|
const data = NTQQGroupApi.essenceLRU.getValue(+payload.message_id);
|
||||||
|
if(!data) throw new Error('消息不存在');
|
||||||
|
const { msg_seq, msg_random, group_id } = JSON.parse(data) as { msg_seq: string, msg_random: string, group_id: string };
|
||||||
|
return await NTQQGroupApi.removeGroupEssenceBySeq(group_id, msg_seq, msg_random);
|
||||||
|
}
|
||||||
return await NTQQGroupApi.removeGroupEssence(
|
return await NTQQGroupApi.removeGroupEssence(
|
||||||
msg.Peer.peerUid,
|
msg.Peer.peerUid,
|
||||||
msg.MsgId,
|
msg.MsgId,
|
||||||
|
@ -1,29 +1,70 @@
|
|||||||
import { GroupEssenceMsgRet } from '@/core';
|
import { ChatType, GroupEssenceMsgRet, Peer } 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';
|
||||||
|
import { MessageUnique } from '@/common/message-unique';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: ['number', 'string'] },
|
group_id: { type: ['number', 'string'] }
|
||||||
pages: { type: ['number', 'string'] },
|
|
||||||
},
|
},
|
||||||
required: ['group_id'],
|
required: ['group_id'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
type Payload = FromSchema<typeof SchemaData>;
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> {
|
export class GetGroupEssence extends BaseAction<Payload, any> {
|
||||||
actionName = ActionName.GoCQHTTP_GetEssenceMsg;
|
actionName = ActionName.GoCQHTTP_GetEssenceMsg;
|
||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
async msgSeqToMsgId(peer: Peer, msgSeq: string, msgRandom: string) {
|
||||||
|
const NTQQMsgApi = this.core.apis.MsgApi;
|
||||||
|
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount(peer, msgSeq, 1, true, true)).msgList.find((msg) => msg.msgSeq === msgSeq && msg.msgRandom === msgRandom);
|
||||||
|
if (!replyMsgList) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return MessageUnique.createUniqueMsgId(peer, replyMsgList.msgId);
|
||||||
|
}
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const NTQQWebApi = this.core.apis.WebApi;
|
const NTQQWebApi = this.core.apis.WebApi;
|
||||||
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), (+(payload.pages ?? 0)).toString());
|
const NTQQGroupApi = this.core.apis.GroupApi;
|
||||||
|
//await NTQQGroupApi.fetchGroupEssenceList(payload.group_id.toString());
|
||||||
|
let peer = {
|
||||||
|
chatType: ChatType.KCHATTYPEGROUP,
|
||||||
|
peerUid: payload.group_id.toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString());
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
throw new Error('获取失败');
|
throw new Error('获取失败');
|
||||||
}
|
}
|
||||||
return ret;
|
const Ob11Ret = await Promise.all(ret.data.msg_list.map(async (msg) => {
|
||||||
|
let message_id = await this.msgSeqToMsgId(peer, msg.msg_seq.toString(), msg.msg_random.toString());
|
||||||
|
if (message_id === 0) {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
msg_seq: msg.msg_seq.toString(),
|
||||||
|
msg_random: msg.msg_random.toString(),
|
||||||
|
group_id: payload.group_id.toString(),
|
||||||
|
});
|
||||||
|
const hash = crypto.createHash('md5').update(data).digest();
|
||||||
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
hash[0] &= 0x7f;
|
||||||
|
const shortId = hash.readInt32BE(0);
|
||||||
|
NTQQGroupApi.essenceLRU.set(shortId, data);
|
||||||
|
message_id = shortId;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
msg_seq: msg.msg_seq,
|
||||||
|
msg_random: msg.msg_random,
|
||||||
|
sender_id: +msg.sender_uin,
|
||||||
|
sender_nick: msg.sender_nick,
|
||||||
|
operator_id: +msg.add_digest_uin,
|
||||||
|
operator_nick: msg.add_digest_nick,
|
||||||
|
message_id: message_id,
|
||||||
|
operator_time: msg.add_digest_time,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
return Ob11Ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.20', 'napcat-update-button', 'secondary'),
|
SettingButton('V2.2.21', 'napcat-update-button', 'secondary'),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@ -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.20", "napcat-update-button", "secondary")
|
SettingButton("V2.2.21", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user