diff --git a/README.md b/README.md index b08ecd5..353dbc7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LLOneBot -LiteLoaderQQNT 插件,实现 OneBot 11 协议,用以 QQ 机器人开发 +LiteLoaderQQNT 插件,实现 OneBot 11 协议,用于 QQ 机器人开发 > [!CAUTION]\ > **请不要在 QQ 官方群聊和任何影响力较大的简中互联网平台(包括但不限于: 哔哩哔哩,微博,知乎,抖音等)发布和讨论*任何*与本插件存在相关性的信息** diff --git a/scripts/gen-manifest.ts b/scripts/gen-manifest.ts index fe48785..2794a65 100644 --- a/scripts/gen-manifest.ts +++ b/scripts/gen-manifest.ts @@ -6,7 +6,7 @@ const manifest = { type: 'extension', name: 'LLOneBot', slug: 'LLOneBot', - description: '实现 OneBot 11 协议,用以 QQ 机器人开发', + description: '实现 OneBot 11 协议,用于 QQ 机器人开发', version, icon: './icon.webp', authors: [ diff --git a/src/common/data.ts b/src/common/data.ts index 60b031e..ea9fcd2 100644 --- a/src/common/data.ts +++ b/src/common/data.ts @@ -66,6 +66,7 @@ export async function getGroupMember(groupCode: string | number, memberUinOrUid: let member = getMember() if (!member) { members = await NTQQGroupApi.getGroupMembers(groupCodeStr) + groupMembers.set(groupCodeStr, members) member = getMember() } return member diff --git a/src/common/utils/request.ts b/src/common/utils/request.ts index 0487ecc..0c471d4 100644 --- a/src/common/utils/request.ts +++ b/src/common/utils/request.ts @@ -16,7 +16,7 @@ export class RequestUtil { const redirectUrl = new URL(res.headers.location, url); RequestUtil.HttpsGetCookies(redirectUrl.href).then((redirectCookies) => { // 合并重定向过程中的cookies - log('redirectCookies', redirectCookies) + //log('redirectCookies', redirectCookies) cookies = { ...cookies, ...redirectCookies }; resolve(cookies); }); @@ -33,7 +33,7 @@ export class RequestUtil { }); if (res.headers['set-cookie']) { // console.log(res.headers['set-cookie']); - log('set-cookie', url, res.headers['set-cookie']); + //log('set-cookie', url, res.headers['set-cookie']); res.headers['set-cookie'].forEach((cookie) => { const parts = cookie.split(';')[0].split('='); const key = parts[0]; diff --git a/src/ntqqapi/api/webapi.ts b/src/ntqqapi/api/webapi.ts index d3f1aaf..e3d2623 100644 --- a/src/ntqqapi/api/webapi.ts +++ b/src/ntqqapi/api/webapi.ts @@ -138,45 +138,47 @@ export class WebApi { return ret } - @CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members') static async getGroupMembers(GroupCode: string, cached: boolean = true): Promise { - //logDebug('webapi 获取群成员', GroupCode) - let MemberData: Array = new Array() - try { - const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com') - const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ') - const Bkn = WebApi.genBkn(CookiesObject.skey) - const retList: Promise[] = [] - const fastRet = await RequestUtil.HttpGetJson('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=0&end=40&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue }); - if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) { - return [] - } else { - for (const key in fastRet.mems) { - MemberData.push(fastRet.mems[key]) - } + const memberData: Array = new Array() + const cookieObject = await NTQQUserApi.getCookies('qun.qq.com') + const cookieStr = Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ') + const retList: Promise[] = [] + const params = new URLSearchParams({ + st: '0', + end: '40', + sort: '1', + gc: GroupCode, + bkn: WebApi.genBkn(cookieObject.skey) + }) + const fastRet = await RequestUtil.HttpGetJson(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${params}`, 'POST', '', { 'Cookie': cookieStr }) + if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) { + return [] + } else { + for (const member of fastRet.mems) { + memberData.push(member) } - //初始化获取PageNum - const PageNum = Math.ceil(fastRet.count / 40) - //遍历批量请求 - for (let i = 2; i <= PageNum; i++) { - const ret: Promise = RequestUtil.HttpGetJson('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=' + (i - 1) * 40 + '&end=' + i * 40 + '&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue }); - retList.push(ret) - } - //批量等待 - for (let i = 1; i <= PageNum; i++) { - const ret = await (retList[i]) - if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) { - continue - } - for (const key in ret.mems) { - MemberData.push(ret.mems[key]) - } - } - } catch { - return MemberData } - return MemberData + const pageNum = Math.ceil(fastRet.count / 40) + //遍历批量请求 + for (let i = 2; i <= pageNum; i++) { + params.set('st', String((i - 1) * 40)) + params.set('end', String(i * 40)) + const ret = RequestUtil.HttpGetJson(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${params}`, 'POST', '', { 'Cookie': cookieStr }) + retList.push(ret) + } + //批量等待 + for (let i = 1; i <= pageNum; i++) { + const ret = await (retList[i]) + if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) { + continue + } + for (const member of ret.mems) { + memberData.push(member) + } + } + return memberData } + // public static async addGroupDigest(groupCode: string, msgSeq: string) { // const url = `https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&msg_seq=${msgSeq}&msg_random=444021292`; // const res = await this.request(url); diff --git a/src/onebot11/action/OB11Response.ts b/src/onebot11/action/OB11Response.ts index 25a394e..0ce6b81 100644 --- a/src/onebot11/action/OB11Response.ts +++ b/src/onebot11/action/OB11Response.ts @@ -10,7 +10,7 @@ export class OB11Response { data: data, message: message, wording: message, - echo: null, + echo: undefined, } } diff --git a/src/onebot11/action/group/GetGroupMemberInfo.ts b/src/onebot11/action/group/GetGroupMemberInfo.ts index 2273f23..e3cafd3 100644 --- a/src/onebot11/action/group/GetGroupMemberInfo.ts +++ b/src/onebot11/action/group/GetGroupMemberInfo.ts @@ -1,33 +1,45 @@ import { OB11GroupMember } from '../../types' -import { getGroupMember } from '../../../common/data' +import { getGroupMember, getSelfUid } from '@/common/data' import { OB11Constructor } from '../../constructor' import BaseAction from '../BaseAction' import { ActionName } from '../types' -import { NTQQUserApi } from '../../../ntqqapi/api/user' +import { NTQQUserApi, WebApi } from '@/ntqqapi/api' +import { isNull } from '@/common/utils/helper' import { log } from '../../../common/utils/log' -import { isNull } from '../../../common/utils/helper' -export interface PayloadType { - group_id: number - user_id: number +interface Payload { + group_id: number | string + user_id: number | string } -class GetGroupMemberInfo extends BaseAction { +class GetGroupMemberInfo extends BaseAction { actionName = ActionName.GetGroupMemberInfo - protected async _handle(payload: PayloadType) { + protected async _handle(payload: Payload) { const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString()) if (member) { if (isNull(member.sex)) { - log('获取群成员详细信息') - let info = await NTQQUserApi.getUserDetailInfo(member.uid, true) - log('群成员详细信息结果', info) + //log('获取群成员详细信息') + const info = await NTQQUserApi.getUserDetailInfo(member.uid, true) + //log('群成员详细信息结果', info) Object.assign(member, info) } const ret = OB11Constructor.groupMember(payload.group_id.toString(), member) + const self = await getGroupMember(payload.group_id.toString(), getSelfUid()) + if (self?.role === 3 || self?.role === 4) { + const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString()) + const target = webGroupMembers.find(e => e?.uin && e.uin === ret.user_id) + log(target) + if (target) { + ret.join_time = target.join_time + ret.last_sent_time = target.last_speak_time + ret.qage = target.qage + ret.level = target.lv.level.toString() + } + } const date = Math.round(Date.now() / 1000) - ret.last_sent_time = Number(member.lastSpeakTime || date) - ret.join_time = Number(member.joinTime || date) + ret.last_sent_time ||= Number(member.lastSpeakTime || date) + ret.join_time ||= Number(member.joinTime || date) return ret } else { throw `群成员${payload.user_id}不存在` diff --git a/src/onebot11/action/msg/SendMsg.ts b/src/onebot11/action/msg/SendMsg.ts index f7f9b7d..a03d04b 100644 --- a/src/onebot11/action/msg/SendMsg.ts +++ b/src/onebot11/action/msg/SendMsg.ts @@ -289,12 +289,12 @@ export async function sendMsg( log('文件大小计算失败', e, fileElement) } } - log('发送消息总大小', totalSize, 'bytes') - let timeout = ((totalSize / 1024 / 100) * 1000) + 5000 // 100kb/s - log('设置消息超时时间', timeout) + //log('发送消息总大小', totalSize, 'bytes') + const timeout = 10000 + (totalSize / 1024 / 256 * 1000) // 10s Basic Timeout + PredictTime( For File 512kb/s ) + //log('设置消息超时时间', timeout) const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout) - log('消息发送结果', returnMsg) returnMsg.msgShortId = MessageUnique.createMsg(peer, returnMsg.msgId) + log('消息发送', returnMsg.msgShortId) deleteAfterSentFiles.map(path => fsPromise.unlink(path)) return returnMsg } diff --git a/src/onebot11/server/post-ob11-event.ts b/src/onebot11/server/post-ob11-event.ts index f979dd4..7ebc1d4 100644 --- a/src/onebot11/server/post-ob11-event.ts +++ b/src/onebot11/server/post-ob11-event.ts @@ -61,13 +61,15 @@ export function postOb11Event(msg: PostEventType, reportSelf = false, postWs = t body: msgStr, }).then( async (res) => { - log(`新消息事件HTTP上报成功: ${host} `, msgStr) + if (msg.post_type) { + log(`HTTP 事件上报: ${host} `, msg.post_type) + } try { const resJson = await res.json() log(`新消息事件HTTP上报返回快速操作: `, JSON.stringify(resJson)) handleQuickOperation(msg as QuickOperationEvent, resJson).then().catch(log); } catch (e) { - log(`新消息事件HTTP上报没有返回快速操作,不需要处理`) + //log(`新消息事件HTTP上报没有返回快速操作,不需要处理`) return } }, diff --git a/src/onebot11/server/ws/reply.ts b/src/onebot11/server/ws/reply.ts index 7837a2c..2be847b 100644 --- a/src/onebot11/server/ws/reply.ts +++ b/src/onebot11/server/ws/reply.ts @@ -1,18 +1,15 @@ import { WebSocket as WebSocketClass } from 'ws' -import { OB11Response } from '../../action/OB11Response' import { PostEventType } from '../post-ob11-event' -import { log } from '../../../common/utils/log' -import { isNull } from '../../../common/utils/helper' +import { log } from '@/common/utils/log' +import { OB11Return } from '../../types' -export function wsReply(wsClient: WebSocketClass, data: OB11Response | PostEventType) { +export function wsReply(wsClient: WebSocketClass, data: OB11Return | PostEventType) { try { - const packet = Object.assign({}, data) - if (isNull(packet['echo'])) { - delete packet['echo'] + wsClient.send(JSON.stringify(data)) + if (data['post_type']) { + log('WebSocket 事件上报', wsClient.url ?? '', data['post_type']) } - wsClient.send(JSON.stringify(packet)) - //log('ws 消息上报', wsClient.url || '', data) } catch (e: any) { - log('websocket 回复失败', e.stack, data) + log('WebSocket 上报失败', e.stack, data) } }