From 02e5222f923a519f8a2b74fa18bac9a9dd0dcfd8 Mon Sep 17 00:00:00 2001 From: idranme Date: Wed, 4 Sep 2024 15:42:10 +0800 Subject: [PATCH] feat: SendGroupNotice --- src/ntqqapi/api/webapi.ts | 185 +++++++++--------- src/ntqqapi/core.ts | 2 +- .../action/go-cqhttp/SendGroupNotice.ts | 37 ++++ src/onebot11/action/index.ts | 4 +- src/onebot11/action/types.ts | 3 +- src/onebot11/adapter.ts | 2 +- 6 files changed, 139 insertions(+), 94 deletions(-) create mode 100644 src/onebot11/action/go-cqhttp/SendGroupNotice.ts diff --git a/src/ntqqapi/api/webapi.ts b/src/ntqqapi/api/webapi.ts index 3f8b930..aa4ad4b 100644 --- a/src/ntqqapi/api/webapi.ts +++ b/src/ntqqapi/api/webapi.ts @@ -1,5 +1,6 @@ import { RequestUtil } from '@/common/utils/request' import { Service, Context } from 'cordis' +import { Dict } from 'cosmokit' declare module 'cordis' { interface Context { @@ -49,56 +50,6 @@ interface WebApiGroupMemberRet { extmode: number } -export interface WebApiGroupNoticeFeed { - u: number//发送者 - fid: string//fid - pubt: number//时间 - msg: { - text: string - text_face: string - title: string, - pics?: { - id: string, - w: string, - h: string - }[] - } - type: number - fn: number - cn: number - vn: number - settings: { - is_show_edit_card: number - remind_ts: number - tip_window_type: number - confirm_required: number - } - read_num: number - is_read: number - is_all_confirm: number -} - -export interface WebApiGroupNoticeRet { - ec: number - em: string - ltsm: number - srv_code: number - read_only: number - role: number - feeds: WebApiGroupNoticeFeed[] - group: { - group_id: number - class_ext: number - } - sta: number, - gln: number - tst: number, - ui: any - server_time: number - svrt: number - ad: number -} - interface GroupEssenceMsg { group_code: string msg_seq: number @@ -124,6 +75,30 @@ export interface GroupEssenceMsgRet { } } +interface SetGroupNoticeParams { + groupCode: string + content: string + pinned: number + type: number + isShowEditCard: number + tipWindowType: number + confirmRequired: number + picId: string + imgWidth?: number + imgHeight?: number +} + +interface SetGroupNoticeRet { + ec: number + em: string + id: number + ltsm: number + new_fid: string + read_only: number + role: number + srv_code: number +} + export class NTQQWebApi extends Service { static inject = ['ntUserApi'] @@ -134,7 +109,7 @@ export class NTQQWebApi extends Service { async getGroupMembers(GroupCode: string, cached: boolean = true): Promise { const memberData: Array = new Array() const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com') - const cookieStr = Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ') + const cookieStr = this.cookieToString(cookieObject) const retList: Promise[] = [] const params = new URLSearchParams({ st: '0', @@ -173,49 +148,47 @@ export class NTQQWebApi extends Service { } genBkn(sKey: string) { - sKey = sKey || ''; - let hash = 5381; - + sKey = sKey || '' + let hash = 5381 for (let i = 0; i < sKey.length; i++) { - const code = sKey.charCodeAt(i); - hash = hash + (hash << 5) + code; + const code = sKey.charCodeAt(i) + hash = hash + (hash << 5) + code } - - return (hash & 0x7FFFFFFF).toString(); + return (hash & 0x7FFFFFFF).toString() } //实现未缓存 考虑2h缓存 async getGroupHonorInfo(groupCode: string, getType: WebHonorType) { const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => { - let url = 'https://qun.qq.com/interactive/honorlist?gc=' + Internal_groupCode + '&type=' + Internal_type.toString(); - let res = ''; - let resJson; + let url = 'https://qun.qq.com/interactive/honorlist?gc=' + Internal_groupCode + '&type=' + Internal_type.toString() + let res = '' + let resJson try { - res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': cookieStr }); - const match = res.match(/window\.__INITIAL_STATE__=(.*?);/); + res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': cookieStr }) + const match = res.match(/window\.__INITIAL_STATE__=(.*?);/) if (match) { - resJson = JSON.parse(match[1].trim()); + resJson = JSON.parse(match[1].trim()) } if (Internal_type === 1) { - return resJson?.talkativeList; + return resJson?.talkativeList } else { - return resJson?.actorList; + return resJson?.actorList } } catch (e) { - this.ctx.logger.error('获取当前群荣耀失败', url, e); + this.ctx.logger.error('获取当前群荣耀失败', url, e) } - return undefined; + return undefined } - let HonorInfo: any = { group_id: groupCode }; + let HonorInfo: any = { group_id: groupCode } const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com') - const cookieStr = Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ') + const cookieStr = this.cookieToString(cookieObject) if (getType === WebHonorType.TALKACTIVE || getType === WebHonorType.ALL) { try { - let RetInternal = await getDataInternal(groupCode, 1); + let RetInternal = await getDataInternal(groupCode, 1) if (!RetInternal) { - throw new Error('获取龙王信息失败'); + throw new Error('获取龙王信息失败') } HonorInfo.current_talkative = { user_id: RetInternal[0]?.uin, @@ -232,17 +205,17 @@ export class NTQQWebApi extends Service { description: talkative_ele?.desc, day_count: 0, nickname: talkative_ele?.name - }); + }) } } catch (e) { - this.ctx.logger.error(e); + this.ctx.logger.error(e) } } if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) { try { - let RetInternal = await getDataInternal(groupCode, 2); + let RetInternal = await getDataInternal(groupCode, 2) if (!RetInternal) { - throw new Error('获取群聊之火失败'); + throw new Error('获取群聊之火失败') } HonorInfo.performer_list = []; for (const performer_ele of RetInternal) { @@ -251,54 +224,86 @@ export class NTQQWebApi extends Service { nickname: performer_ele?.name, avatar: performer_ele?.avatar, description: performer_ele?.desc - }); + }) } } catch (e) { - this.ctx.logger.error(e); + this.ctx.logger.error(e) } } if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) { try { - let RetInternal = await getDataInternal(groupCode, 3); + let RetInternal = await getDataInternal(groupCode, 3) if (!RetInternal) { - throw new Error('获取群聊炽焰失败'); + throw new Error('获取群聊炽焰失败') } - HonorInfo.legend_list = []; + HonorInfo.legend_list = [] for (const legend_ele of RetInternal) { HonorInfo.legend_list.push({ user_id: legend_ele?.uin, nickname: legend_ele?.name, avatar: legend_ele?.avatar, desc: legend_ele?.description - }); + }) } } catch (e) { - this.ctx.logger.error('获取群聊炽焰失败', e); + this.ctx.logger.error('获取群聊炽焰失败', e) } } if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) { try { - let RetInternal = await getDataInternal(groupCode, 6); + let RetInternal = await getDataInternal(groupCode, 6) if (!RetInternal) { - throw new Error('获取快乐源泉失败'); + throw new Error('获取快乐源泉失败') } - HonorInfo.emotion_list = []; + HonorInfo.emotion_list = [] for (const emotion_ele of RetInternal) { HonorInfo.emotion_list.push({ user_id: emotion_ele?.uin, nickname: emotion_ele?.name, avatar: emotion_ele?.avatar, desc: emotion_ele?.description - }); + }) } } catch (e) { - this.ctx.logger.error('获取快乐源泉失败', e); + this.ctx.logger.error('获取快乐源泉失败', e) } } //冒尖小春笋好像已经被tx扬了 if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) { - HonorInfo.strong_newbie_list = []; + HonorInfo.strong_newbie_list = [] } - return HonorInfo; + return HonorInfo + } + + async setGroupNotice(params: SetGroupNoticeParams): Promise { + const cookieObject = await this.ctx.ntUserApi.getCookies('qun.qq.com') + const settings = JSON.stringify({ + is_show_edit_card: params.isShowEditCard, + tip_window_type: params.tipWindowType, + confirm_required: params.confirmRequired + }) + + return await RequestUtil.HttpGetJson( + `https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?${new URLSearchParams({ + bkn: this.genBkn(cookieObject.skey), + qid: params.groupCode, + text: params.content, + pinned: params.pinned.toString(), + type: params.type.toString(), + settings: settings, + ...(params.picId !== '' && { + pic: params.picId, + imgWidth: params.imgWidth?.toString(), + imgHeight: params.imgHeight?.toString(), + }) + })}`, + 'POST', + '', + { 'Cookie': this.cookieToString(cookieObject) } + ) + } + + private cookieToString(cookieObject: Dict) { + return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ') } } diff --git a/src/ntqqapi/core.ts b/src/ntqqapi/core.ts index 883f52a..3ed93ad 100644 --- a/src/ntqqapi/core.ts +++ b/src/ntqqapi/core.ts @@ -37,7 +37,7 @@ declare module 'cordis' { } class Core extends Service { - static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi'] + static inject = ['ntMsgApi', 'ntFriendApi', 'ntGroupApi'] constructor(protected ctx: Context, public config: Core.Config) { super(ctx, 'app', true) diff --git a/src/onebot11/action/go-cqhttp/SendGroupNotice.ts b/src/onebot11/action/go-cqhttp/SendGroupNotice.ts new file mode 100644 index 0000000..1e54a82 --- /dev/null +++ b/src/onebot11/action/go-cqhttp/SendGroupNotice.ts @@ -0,0 +1,37 @@ +import BaseAction from '../BaseAction' +import { ActionName } from '../types' + +interface Payload { + group_id: number | string + content: string + image?: string + pinned?: number | string //扩展 + confirm_required?: number | string //扩展 +} + +export class SendGroupNotice extends BaseAction { + actionName = ActionName.GoCQHTTP_SendGroupNotice + + async _handle(payload: Payload) { + const type = 1 + const isShowEditCard = 0 + const tipWindowType = 0 + const pinned = Number(payload.pinned ?? 0) + const confirmRequired = Number(payload.confirm_required ?? 1) + + const result = await this.ctx.ntWebApi.setGroupNotice({ + groupCode: payload.group_id.toString(), + content: payload.content, + pinned, + type, + isShowEditCard, + tipWindowType, + confirmRequired, + picId: '' + }) + if (result.ec !== 0) { + throw new Error(`设置群公告失败, 错误信息: ${result.em}`) + } + return null + } +} \ No newline at end of file diff --git a/src/onebot11/action/index.ts b/src/onebot11/action/index.ts index 7cebfb4..4845f45 100644 --- a/src/onebot11/action/index.ts +++ b/src/onebot11/action/index.ts @@ -61,6 +61,7 @@ import { DelGroupFolder } from './go-cqhttp/DelGroupFolder' import { GetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain' import { GetGroupRootFiles } from './go-cqhttp/GetGroupRootFiles' import { SetOnlineStatus } from './llonebot/SetOnlineStatus' +import { SendGroupNotice } from './go-cqhttp/SendGroupNotice' export function initActionMap(adapter: Adapter) { const actionHandlers = [ @@ -128,7 +129,8 @@ export function initActionMap(adapter: Adapter) { new CreateGroupFileFolder(adapter), new DelGroupFolder(adapter), new GetGroupAtAllRemain(adapter), - new GetGroupRootFiles(adapter) + new GetGroupRootFiles(adapter), + new SendGroupNotice(adapter) ] const actionMap = new Map>() for (const action of actionHandlers) { diff --git a/src/onebot11/action/types.ts b/src/onebot11/action/types.ts index deab293..704c5b8 100644 --- a/src/onebot11/action/types.ts +++ b/src/onebot11/action/types.ts @@ -79,5 +79,6 @@ export enum ActionName { GoCQHTTP_CreateGroupFileFolder = 'create_group_file_folder', GoCQHTTP_DelGroupFolder = 'delete_group_folder', GoCQHTTP_GetGroupAtAllRemain = 'get_group_at_all_remain', - GoCQHTTP_GetGroupRootFiles = 'get_group_root_files' + GoCQHTTP_GetGroupRootFiles = 'get_group_root_files', + GoCQHTTP_SendGroupNotice = '_send_group_notice', } diff --git a/src/onebot11/adapter.ts b/src/onebot11/adapter.ts index e8e2885..6af8cf3 100644 --- a/src/onebot11/adapter.ts +++ b/src/onebot11/adapter.ts @@ -35,7 +35,7 @@ declare module 'cordis' { } class OneBot11Adapter extends Service { - static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi'] + static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi'] public messages: Map = new Map() public startTime = 0