diff --git a/README.md b/README.md index c9bfe2d4..9f32b81d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ +> 由于相关领域发展快速,迭代较多,本文档有部分过时内容,不确定的问题可以开discussion或加群问群里的大佬们哦 ### 推荐的相关文档和参考资料 本README diff --git a/apps/button.js b/apps/button.js index 7bff6ff6..d443ba4b 100644 --- a/apps/button.js +++ b/apps/button.js @@ -53,6 +53,14 @@ export class ChatGPTButtonHandler extends plugin { }) } + /** + * 按钮处理器 + * @param e + * @param options + * @param reject + * @deprecated + * @return {Promise<{appid: number, rows: [{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]}]}|{appid: number, rows: [{buttons: [{render_data: {style: number, label: *, visited_label: *}, action: {data: *, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string},{render_data: {style: number, label: *, visited_label: *}, action: {data: *, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string},{render_data: {style: number, label: *, visited_label: *}, action: {data: *, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}]}]}|{appid: number, rows: [{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]},{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]},{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]},{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]}]}|{appid: number, rows: [{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]},{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]},{buttons: {render_data: {style: number, label, visited_label}, action: {data, permission: {type: number}, unsupport_tips: string, enter: boolean, type: number}, id: string}[]}]}|null>} + */ async btnHandler (e, options, reject) { // logger.mark('[chatgpt按钮处理器]') if (!Config.enableMd) { diff --git a/apps/chat.js b/apps/chat.js index ee40ad08..a7fa756b 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -74,43 +74,43 @@ export class chatgpt extends plugin { rule: [ { /** 命令正则匹配 */ - reg: '^#chat3[sS]*', + reg: '^#(图片)?chat3[sS]*', /** 执行方法 */ fnc: 'chatgpt3' }, { /** 命令正则匹配 */ - reg: '^#chat1[sS]*', + reg: '^#(图片)?chat1[sS]*', /** 执行方法 */ fnc: 'chatgpt1' }, { /** 命令正则匹配 */ - reg: '^#chatglm[sS]*', + reg: '^#(图片)?chatglm[sS]*', /** 执行方法 */ fnc: 'chatglm' }, { /** 命令正则匹配 */ - reg: '^#bing[sS]*', + reg: '^#(图片)?bing[sS]*', /** 执行方法 */ fnc: 'bing' }, { /** 命令正则匹配 */ - reg: '^#claude(2|3|.ai)[sS]*', + reg: '^#(图片)?claude(2|3|.ai)[sS]*', /** 执行方法 */ fnc: 'claude2' }, { /** 命令正则匹配 */ - reg: '^#claude[sS]*', + reg: '^#(图片)?claude[sS]*', /** 执行方法 */ fnc: 'claude' }, { /** 命令正则匹配 */ - reg: '^#xh[sS]*', + reg: '^#(图片)?xh[sS]*', /** 执行方法 */ fnc: 'xh' }, @@ -124,25 +124,25 @@ export class chatgpt extends plugin { }, { /** 命令正则匹配 */ - reg: '^#glm4[sS]*', + reg: '^#(图片)?glm4[sS]*', /** 执行方法 */ fnc: 'glm4' }, { /** 命令正则匹配 */ - reg: '^#qwen[sS]*', + reg: '^#(图片)?qwen[sS]*', /** 执行方法 */ fnc: 'qwen' }, { /** 命令正则匹配 */ - reg: '^#gemini[sS]*', + reg: '^#(图片)?gemini[sS]*', /** 执行方法 */ fnc: 'gemini' }, { /** 命令正则匹配 */ - reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*', + reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#(图片)?chat[^gpt][sS]*', /** 执行方法 */ fnc: 'chatgpt', log: false @@ -483,6 +483,7 @@ export class chatgpt extends plugin { async chatgpt (e) { let msg = e.msg let prompt + let forcePictureMode = false if (this.toggleMode === 'at') { if (!msg || e.msg?.startsWith('#')) { return false @@ -533,7 +534,10 @@ export class chatgpt extends plugin { } return false } - prompt = _.replace(e.msg.trimStart(), '#chat', '').trim() + if (e.msg.trimStart().startsWith('#图片chat')) { + forcePictureMode = true + } + prompt = _.replace(e.msg.trimStart(), /#(图片)?chat/, '').trim() if (prompt.length === 0) { return false } @@ -548,10 +552,10 @@ export class chatgpt extends plugin { const use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE') || 'api' // 自动化插件本月已发送xx条消息更新太快,由于延迟和缓存问题导致不同客户端不一样,at文本和获取的card不一致。因此单独处理一下 prompt = prompt.replace(/^|本月已发送\d+条消息/, '') - await this.abstractChat(e, prompt, use) + await this.abstractChat(e, prompt, use, forcePictureMode) } - async abstractChat (e, prompt, use) { + async abstractChat (e, prompt, use, forcePictureMode = false) { // 关闭私聊通道后不回复 if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) { return false @@ -819,6 +823,17 @@ export class chatgpt extends plugin { } } let response = chatMessage?.text?.replace('\n\n\n', '\n') + + if (handler.has('chatgpt.response.post')) { + logger.debug('调用后处理器: chatgpt.response.post') + handler.call('chatgpt.response.post', this.e, { + content: response, + use, + prompt + }, true).catch(err => { + logger.error('后处理器出错', err) + }) + } let mood = 'blandness' if (!response) { await this.reply('没有任何回复', true) @@ -981,7 +996,7 @@ export class chatgpt extends plugin { } else { await this.reply('合成语音发生错误~') } - } else if (userSetting.usePicture || (!Config.enableMd && Config.autoUsePicture && response.length > Config.autoUsePictureThreshold)) { + } else if (forcePictureMode || userSetting.usePicture || (Config.autoUsePicture && response.length > Config.autoUsePictureThreshold)) { try { await this.renderImage(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls) } catch (err) { @@ -1052,11 +1067,11 @@ export class chatgpt extends plugin { } async chatgpt1 (e) { - return await this.otherMode(e, 'api', '#chat1') + return await this.otherMode(e, 'api', /#(图片)?chat1/) } async chatgpt3 (e) { - return await this.otherMode(e, 'api3', '#chat3') + return await this.otherMode(e, 'api3', /#(图片)?chat3/) } async chatglm (e) { @@ -1064,31 +1079,31 @@ export class chatgpt extends plugin { } async bing (e) { - return await this.otherMode(e, 'bing') + return await this.otherMode(e, 'bing', /#(图片)?bing/) } async claude2 (e) { - return await this.otherMode(e, 'claude2', /^#claude(2|3|.ai)/) + return await this.otherMode(e, 'claude2', /^#(图片)?claude(2|3|.ai)/) } async claude (e) { - return await this.otherMode(e, 'claude') + return await this.otherMode(e, 'claude', /#(图片)?claude/) } async qwen (e) { - return await this.otherMode(e, 'qwen') + return await this.otherMode(e, 'qwen', /#(图片)?qwen/) } async glm4 (e) { - return await this.otherMode(e, 'chatglm4', '#glm4') + return await this.otherMode(e, 'chatglm4', /#(图片)?glm4/) } async gemini (e) { - return await this.otherMode(e, 'gemini') + return await this.otherMode(e, 'gemini', /#(图片)?gemini/) } async xh (e) { - return await this.otherMode(e, 'xh') + return await this.otherMode(e, 'xh', /#(图片)?xh/) } async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) { @@ -1396,7 +1411,8 @@ export class chatgpt extends plugin { if (prompt.length === 0) { return false } - await this.abstractChat(e, prompt, mode) + let forcePictureMode = e.msg.trimStart().startsWith('#图片') + await this.abstractChat(e, prompt, mode, forcePictureMode) return true } } diff --git a/apps/example_handler.js b/apps/example_handler.js new file mode 100644 index 00000000..27f60c56 --- /dev/null +++ b/apps/example_handler.js @@ -0,0 +1,46 @@ +/** + * 示例后处理器。你可以在example下面写一个新的。默认会调用所有此key的处理器 + */ +export class ChatGPTResponsePostHandler extends plugin { + constructor () { + super({ + name: 'chatgpt文本回复后处理器', + priority: -100, + namespace: 'chatgpt-plugin', + handler: [{ + key: 'chatgpt.response.post', // key必须是chatgpt.response.post + fn: 'postHandler' + }] + }) + } + + async postHandler (e, options, reject) { + const { content, use, prompt } = options + // 你可以在这里处理返回的文本,比如使用自定义的语音api来合成语音 + + // 返回值会被忽略 + // 以下是一个简单的例子 + // const response = await fetch('https://api.fish.audio/v1/tts', { + // method: 'POST', + // headers: { + // Authorization: 'Bearer + key', + // 'Content-Type': 'application/json' + // }, + // body: JSON.stringify({ + // text: content, + // reference_id: '1aacaeb1b840436391b835fd5513f4c4', + // format: 'mp3', + // latency: 'normal' + // }) + // }) + // + // if (!response.ok) { + // throw new Error(`无法从服务器获取音频数据:${response.statusText}`) + // } + // + // const audio = await response.blob() + // // to Buffer + // const buffer = await audio.arrayBuffer() + // e.reply(segment.record(Buffer.from(buffer))) + } +} diff --git a/apps/update.js b/apps/update.js index f22c8a6d..2effe56b 100644 --- a/apps/update.js +++ b/apps/update.js @@ -186,13 +186,14 @@ export class Update extends plugin { * @returns */ async makeForwardMsg (title, msg, end) { - let nickname = (this.e.bot ?? Bot).nickname + const _bot = this.e.bot ?? Bot + let nickname = _bot.nickname if (this.e.isGroup) { - let info = await (this.e.bot ?? Bot).getGroupMemberInfo(this.e.group_id, (this.e.bot ?? Bot).uin) + let info = await _bot?.pickMember?.(this.e.group_id, _bot.uin) || await _bot?.getGroupMemberInfo?.(this.e.group_id, _bot.uin) nickname = info.card || info.nickname } let userInfo = { - user_id: (this.e.bot ?? Bot).uin, + user_id: _bot.uin, nickname } diff --git a/client/CustomGoogleGeminiClient.js b/client/CustomGoogleGeminiClient.js index 7dd9fdbe..b15b0522 100644 --- a/client/CustomGoogleGeminiClient.js +++ b/client/CustomGoogleGeminiClient.js @@ -239,9 +239,11 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { } else { // execute function try { + let isAdmin = ['admin', 'owner'].includes(this.e.sender.role) || (this.e.group?.is_admin && this.e.isMaster) + let isOwner = ['owner'].includes(this.e.sender.role) || (this.e.group?.is_owner && this.e.isMaster) let args = Object.assign(functionCall.args, { - isAdmin: this.e.group?.is_admin, - isOwner: this.e.group?.is_owner, + isAdmin, + isOwner, sender: this.e.sender, mode: 'gemini' }) diff --git a/client/OpenAILikeClient.js b/client/OpenAILikeClient.js index f9279e5b..ddb79560 100644 --- a/client/OpenAILikeClient.js +++ b/client/OpenAILikeClient.js @@ -1,7 +1,6 @@ -import OpenAI from 'openai'; +import OpenAI from 'openai' import { BaseClient } from './BaseClient.js' export default class OpenAILikeClient extends BaseClient { - } diff --git a/guoba.support.js b/guoba.support.js index 03403152..8dec6fb7 100644 --- a/guoba.support.js +++ b/guoba.support.js @@ -63,12 +63,12 @@ export function supportGuoba () { bottomHelpMessage: '独立的后台管理面板(默认3321端口),与锅巴类似。工具箱会有额外占用,启动速度稍慢,酌情开启。修改后需重启生效!!!', component: 'Switch' }, - { - field: 'enableMd', - label: 'QQ开启markdown', - bottomHelpMessage: 'qq的第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误。默认关闭', - component: 'Switch' - }, + // { + // field: 'enableMd', + // label: 'QQ开启markdown', + // bottomHelpMessage: 'qq的第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误。默认关闭', + // component: 'Switch' + // }, { field: 'translateSource', label: '翻译来源', diff --git a/model/core.js b/model/core.js index a4588f12..9a2547f8 100644 --- a/model/core.js +++ b/model/core.js @@ -873,13 +873,13 @@ class Core { // 如果配了proxy(或者不在国内),而且有反代,但是没开启强制反代,将baseurl删掉 delete opts.apiBaseUrl } - const client = new OpenAI({ - apiKey: Config.apiKey, - baseURL: opts.apiBaseUrl, - fetch: newFetch - }) + // const client = new OpenAI({ + // apiKey: Config.apiKey, + // baseURL: opts.apiBaseUrl, + // fetch: newFetch + // }) - // this.chatGPTApi = new ChatGPTAPI(opts) + this.chatGPTApi = new ChatGPTAPI(opts) let option = { timeoutMs: 600000, completionParams, @@ -1065,7 +1065,7 @@ async function collectTools (e) { } let systemAddition = '' if (e.isGroup) { - let botInfo = await e.bot.getGroupMemberInfo(e.group_id, getUin(e), true) + let botInfo = await e.bot?.pickMember?.(e.group_id, getUin(e), true) || await e.bot?.getGroupMemberInfo?.(e.group_id, getUin(e), true) if (botInfo.role !== 'member') { // 管理员才给这些工具 tools.push(...[new EditCardTool(), new JinyanTool(), new KickOutTool(), new HandleMessageMsgTool(), new SetTitleTool()]) diff --git a/utils/tools/QueryUserinfoTool.js b/utils/tools/QueryUserinfoTool.js index 974c08b0..2a0d422f 100644 --- a/utils/tools/QueryUserinfoTool.js +++ b/utils/tools/QueryUserinfoTool.js @@ -18,8 +18,8 @@ export class QueryUserinfoTool extends AbstractTool { try { let { qq } = opts qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim()) - if (e.isGroup && typeof e.bot.getGroupMemberInfo === 'function') { - let user = await e.bot.getGroupMemberInfo(e.group_id, qq || e.sender.user_id, true) + if (e.isGroup) { + let user = await e.bot?.pickMember?.(e.group_id, qq || e.sender.user_id, true) || await e.bot?.getGroupMemberInfo?.(e.group_id, qq || e.sender.user_id, true) // let mm = await e.group.getMemberMap() // let user = mm.get(qq) || e.sender.user_id let master = (await getMasterQQ())[0]