UNPKG

kivibot-plugin-chatgpt

Version:

QQ 机器人框架 KiviBot 的 OpenAI / ChatGPT 插件

172 lines (130 loc) 4.64 kB
const { KiviPlugin, getTargetId } = require('@kivibot/core') const { Configuration, OpenAIApi } = require('openai') const { version } = require('./package.json') const plugin = new KiviPlugin('ChatGPT', version) const config = { // OpenAI apiKey apiKey: '', // 是否开启 at 触发 enableAt: true, // 是否在群聊中开启(发言不可控,为了账号安全可以关闭群聊功能,仅保留私聊) enableGroup: true, // 触发命令前缀 cmdPrefix: '%' } // session map const sessionMap = new Map() const msgs = { needConfig: `ChatGPT: 请先配置 apiKey,格式:/chatgpt setkey <apikey>`, apiError: 'ChatGPT: API 请求异常,可能是 apiKey 错误或请求太频繁,请查看日志' } const cmds = [ '/chatgpt setkey <apiKey>', '/chatgpt at on/off', '/chatgpt group on/off', '/chatgpt prefix <触发前缀>' ] plugin.onMounted(async bot => { plugin.saveConfig(Object.assign(config, plugin.loadConfig())) plugin.onAdminCmd('/chatgpt', (e, params) => { const [cmd, value] = params if (cmd === 'setkey' && value) { config.apiKey = value plugin.saveConfig(config) return e.reply('apiKey 配置完成,重载插件生效', true) } if (cmd === 'at' && ['on', 'off'].includes(value)) { config.enableAt = value === 'on' plugin.saveConfig(config) return e.reply(`已${config.enableAt ? '开启' : '关闭'} at 触发`, true) } if (cmd === 'group' && ['on', 'off'].includes(value)) { config.enableGroup = value === 'on' plugin.saveConfig(config) return e.reply(`已${config.enableGroup ? '开启' : '关闭'}群聊功能`, true) } if (cmd === 'prefix' && value) { config.cmdPrefix = value plugin.saveConfig(config) return e.reply('已修改命令触发前缀', true) } return e.reply(cmds.join('\n'), true) }) if (!config.apiKey) { bot.sendPrivateMsg(plugin.mainAdmin, msgs.needConfig) return plugin.log(msgs.needConfig) } const configuration = new Configuration({ apiKey: config.apiKey }) const openai = new OpenAIApi(configuration) plugin.onMessage(async event => { const { message, message_type } = event const text = message .filter(e => e.type === 'text') .map(e => e.text) .join('') // 配置关闭群聊时,过滤群聊信息 if (!config.enableGroup && message_type !== 'private') { return } // 消息符合命令前缀 const isCmd = text.startsWith(config.cmdPrefix) // Bot 被艾特 const isAt = message.some(e => e.type === 'at' && e.qq === bot.uin) // 触发条件(符合命令前缀 或者 在启用艾特触发时,Bot 被艾特) const isHit = isCmd || (config.enableAt && isAt) // 过滤不触发的消息 if (!isHit) { return } const reg = /((换个?话题)|([说讲聊]点?(别|(其他))的))/ const shouldChangeContext = text.match(reg) // 过滤更新会话消息 if (shouldChangeContext) { sessionMap.delete(getTargetId(event)) return event.reply('好的,让我们重新开始。', true) } try { const session = getSession(event) const question = text.replace(config.cmdPrefix, '').trim() plugin.debug(question) const prompt = session.context ? `${session.context}\nHuman:${question}\nAI:` : question const { data, status } = await openai.createCompletion({ prompt, model: 'text-davinci-003', temperature: 0.5, max_tokens: 2048 }) if (status !== 200) { return e.reply(msgs.apiError, true) } const res = data.choices[0]?.text.replace(/^\s*[??]?\n*/, '').trim() ?? '' session.context = prompt + res event.reply(res ?? '这个 OpenAI 不知道哦', true) } catch (err) { plugin.throwPluginError(err?.message ?? err) return e.reply(msgs.apiError, true) } }) }) function getSession(event) { const id = getTargetId(event) const now = Date.now() let session = sessionMap.get(id) if (session) { const tenMinutesMs = 1000 * 60 * 10 const isTimeout = now - session.time >= tenMinutesMs const isTextTooLong = session.context.length >= 1600 if (isTimeout || isTextTooLong) { // 超过十分钟没有回复,或者聊天记录大于 1600 字,清空会话记录 session.context = '' } else { // 没超过十分钟,刷新当前会话时间 session.time = now } } else { session = { context: '', time: now } sessionMap.set(id, session) } return session } module.exports = { plugin }