mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 13:27:08 +00:00
* feat: add support for ‘greeting’ and ‘global reply mode’ commands, improve variable naming and remove unnecessary backend output. * feat: Add support for black and white lists, global reply mode and voice role settings, private chat switch, and active greeting configuration. Refactor some variable names and comment out redundant code for better readability and reduced backend output. * feat: 为新功能完善了帮助面板 * docs: 完善了‘打招呼’的帮助说明 * Commit Type: feat, bugfix Add functionality to view plugin command table, fix bug in blacklist/whitelist, and fix bug where chat mode can still be used in private messaging when disabled. * Commit Type: feat, bugfix Add functionality to view plugin command table, fix bug in blacklist/whitelist, and fix bug where chat mode can still be used in private messaging when disabled. * refactor: Remove redundant log output. * Refactor: optimize code logic * Fix: 修复绘图指令表被抢指令的bug。 * Refactor:1. Add support for automatically translating replies to Japanese and generating voice messages in VITS voice mode (please monitor remaining quota after enabling). 2. Add translation function. 3. Add emotion configuration for Azure voice mode, allowing the robot to select appropriate emotional styles for replies. * Refactor:Handle the issue of exceeding character setting limit caused by adding emotion configuration. * Fix: fix bugs * Refactor: Added error feedback to translation service * Refactor: Added support for viewing the list of supported roles for each language mode, and fixed some bugs in the emotion switching feature of the auzre mode. * Refactor: Optimized some command feedback and added owner restriction to chat record export function. * Refactor: Optimized feedback when viewing role list to avoid excessive messages. * Refactor: Optimized feedback when configuring multi-emotion mode. * Feature: Added help instructions for translation feature. * chore: Adjust help instructions for mood settings * Fix: Fixed issue where only first line of multi-line replies were being read and Azure voice was pronouncing punctuation marks. * Fix: Fixed bug where switching to Azure voice mode prompted for missing key and restricted ability to view voice role list to only when in voice mode. * Refactor: Add image OCR function and support translation for both quoted text and image. * fix: Fix issue with error caused by non-image input. * Refactor: Optimize code to filter emojis that cannot be displayed properly in claude mode. * Refactor: Optimize some code structures. * fix: Fix the bug of returning only one result when entering multiple lines of text on Windows system. * Refactor: Optimize code logic for better user experience * Refactor: Fix the conflict issue with other plugin translation commands * Refactor: Replace Baidu Translation with Youdao Translation to eliminate configuration steps; optimize translation experience; add missing dependency prompts instead of causing program errors.Optimize the experience of switching between voice mode and setting global reply mode. * Refactor: Remove unused files and dependencies in the project. * Feature: Add Youdao translation service to provide more comprehensive translation support. * Refactor: Optimize translation experience * Refactor: Optimize translation experience * Feature: Add functionality of keyword search command * Feature: Add functionality of keyword search command. * Refactor: Remove redundant code * Add: Add feature to support randomly selecting roles for Azure voice. Refactor the code to support existing voice services for the ‘greeting’ feature. Fix the display issue of Azure voice role selection on the Guoba panel. * Refactor: Remove redundant code * Refactor: Improve the function of setting global voice roles and viewing role lists. Now you can set default roles for each voice service separately or view the supported role list. * Refactor: Remove redundant code * Feature: Add new function to support random character dialogues in all voice modes, add the ability to view the current user’s reply settings, and improve related functions in the global settings. * Refactor: Add compatibility directive for viewing reply settings feature * Feature: support adding QQ number to blacklist/whitelist * fix: 处理全局设置指令被上下班指令占用的问题 * fix: 处理全局设置指令被上下班指令占用的问题 * Refactor: Preprocess dialogue blacklist/whitelist when filling in the form in Guoba panel * Fix: Fixed the issue where black and white lists were not effective when filled in the Guoba panel, and the issue where no results were returned when viewing the voice role list without parameters in azure tts mode. * fix: 2.7 dev start * feat: 初步支持function call(WIP) * fix: syntax error * fix: syntax error * feat: 群聊上下文 * fix: 暂时阉割掉全员禁言功能 * fix: 修改禁言时间范围 * fix: 修复一些功能易用性 * fix: 只有管理员和群主才能用jinyan和kickout * fix: 加回来禁言和踢出 * fix: 修复管理员权限判断问题(可能吧) * fix: 试图优化逻辑 * fix: fuck openai documents * fix: 删掉认主不然一直禁言我烦死了 * fix: 哔哩哔哩封面损坏问题 * fix: 加个天气小工具 * fix: 天气不存在城市 * fix: website工具用浏览器 * feat: serp tool * feat: 增加一个google搜索源 * fix: 加一句描述 * feat: 增加搜索来源选项 * feat: 搜图和发图 * fix: groupId format error * Refactor: Optimized the HTML parsing rules * fix: Fixed the bug where conversations could not be properly ended in at mode, now it works normally * refactor: Added EliMovieTool and EliMusicTool. Modified some tool’s prompt to make AI make better choices. Optimized the display of chat history.Remove SendMusicTool and replace it with EliMusicTool. * chore: trivial changes. * refactor:Keep SearchMusicTool and SendMusicTool when avocado plugin is not installed, waiting for more testing~~~ * Refactor: Move the parameter processing logic of some tools into each tool internally.🥑Rename ttstool to SendAudioMessageTool, defaulting to the voice role configured by the current user, and support personalized configuration of the existing tts mode in the plugin.🥑Add SendMessageToSpecificGroupOrUserTool, which allows the robot to send messages to specific groups or friends.🥑Encapsulate the function of generating audio messages into an independent function for easy tool invocation.🥑Separate SendPictureTool and SendAvatarTool to avoid unnecessary bugs.🥑 * chore: Remove unnecessary log file🥑 * refactor: Trivial changes and fixed a bug in QueryUserinfoTool.🥑 * Refactor: Rewrite blacklist and whitelist functionality, optimize code calls Description: Rewrote the blacklist and whitelist functionality to support group, QQ number, and a combination of group and QQ number configurations. Removed the command to set the blacklist and whitelist functionality and unified it through the Guoba panel. Also optimized some code calls to EliMusicTool and EliMovieTool.🥑 --------- Co-authored-by: Sean <1519059137@qq.com> Co-authored-by: ikechan8370 <geyinchibuaa@gmail.com> Co-authored-by: ikechan8370 <geyinchi@buaa.edu.cn>
1353 lines
48 KiB
JavaScript
1353 lines
48 KiB
JavaScript
import plugin from '../../../lib/plugins/plugin.js'
|
||
import { Config } from '../utils/config.js'
|
||
import {
|
||
formatDuration,
|
||
getAzureRoleList,
|
||
getPublicIP,
|
||
getUserReplySetting,
|
||
getVitsRoleList,
|
||
getVoicevoxRoleList,
|
||
makeForwardMsg,
|
||
parseDuration,
|
||
renderUrl
|
||
} from '../utils/common.js'
|
||
import SydneyAIClient from '../utils/SydneyAIClient.js'
|
||
import { convertSpeaker, speakers as vitsRoleList } from '../utils/tts.js'
|
||
import md5 from 'md5'
|
||
import path from 'path'
|
||
import fs from 'fs'
|
||
import loader from '../../../lib/plugins/loader.js'
|
||
import VoiceVoxTTS, { supportConfigurations as voxRoleList } from '../utils/tts/voicevox.js'
|
||
import { supportConfigurations as azureRoleList } from '../utils/tts/microsoft-azure.js'
|
||
|
||
export class ChatgptManagement extends plugin {
|
||
constructor (e) {
|
||
super({
|
||
name: 'ChatGPT-Plugin 管理',
|
||
dsc: '插件的管理项配置,让你轻松掌控各个功能的开闭和管理。包含各种实用的配置选项,让你的聊天更加便捷和高效!',
|
||
event: 'message',
|
||
priority: 500,
|
||
rule: [
|
||
{
|
||
reg: '#chatgpt开启(问题)?(回复)?确认',
|
||
fnc: 'turnOnConfirm',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt关闭(问题)?(回复)?确认',
|
||
fnc: 'turnOffConfirm',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(设置|绑定)(token|Token)',
|
||
fnc: 'setAccessToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(设置|绑定)(Poe|POE)(token|Token)',
|
||
fnc: 'setPoeCookie',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(设置|绑定|添加)(必应|Bing |bing )(token|Token)',
|
||
fnc: 'setBingAccessToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(删除|移除)(必应|Bing |bing )(token|Token)',
|
||
fnc: 'delBingAccessToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(查看|浏览)(必应|Bing |bing )(token|Token)',
|
||
fnc: 'getBingAccessToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '#chatgpt(迁移|恢复)(必应|Bing |bing )(token|Token)',
|
||
fnc: 'migrateBingAccessToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换浏览器$',
|
||
fnc: 'useBrowserBasedSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换API$',
|
||
fnc: 'useOpenAIAPIBasedSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换(ChatGLM|chatglm)$',
|
||
fnc: 'useChatGLMSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换API3$',
|
||
fnc: 'useReversedAPIBasedSolution2',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换(必应|Bing)$',
|
||
fnc: 'useBingSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换(Poe|poe)$',
|
||
fnc: 'useClaudeBasedSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换(Claude|claude|slack)$',
|
||
fnc: 'useSlackClaudeBasedSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt切换星火$',
|
||
fnc: 'useXinghuoBasedSolution',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(必应|Bing)切换',
|
||
fnc: 'changeBingTone',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(必应|Bing)(开启|关闭)建议(回复)?',
|
||
fnc: 'bingOpenSuggestedResponses',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt模式(帮助)?$',
|
||
fnc: 'modeHelp'
|
||
},
|
||
{
|
||
reg: '^#chatgpt版本(信息)',
|
||
fnc: 'versionChatGPTPlugin'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(本群)?(群\\d+)?(关闭|闭嘴|关机|休眠|下班)',
|
||
fnc: 'shutUp',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(本群)?(群\\d+)?(开启|启动|激活|张嘴|开口|说话|上班)$',
|
||
fnc: 'openMouth',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt查看?(关闭|闭嘴|关机|休眠|下班|休眠)列表$',
|
||
fnc: 'listShutUp',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置(API|key)(Key|key)$',
|
||
fnc: 'setAPIKey',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置(API|api)设定$',
|
||
fnc: 'setAPIPromptPrefix',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置星火token$',
|
||
fnc: 'setXinghuoToken',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置(Bing|必应|Sydney|悉尼|sydney|bing)设定$',
|
||
fnc: 'setBingPromptPrefix',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(开启|关闭)画图$',
|
||
fnc: 'switchDraw',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt查看(API|api)设定$',
|
||
fnc: 'queryAPIPromptPrefix',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt查看(Bing|必应|Sydney|悉尼|sydney|bing)设定$',
|
||
fnc: 'queryBingPromptPrefix',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|((azure|vits|vox)?语音角色|角色语音|角色).*)|回复帮助)$',
|
||
fnc: 'setDefaultReplySetting',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
/** 命令正则匹配 */
|
||
reg: '^#(关闭|打开)群聊上下文$',
|
||
/** 执行方法 */
|
||
fnc: 'enableGroupContext',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(允许|禁止|打开|关闭|同意)私聊$',
|
||
fnc: 'enablePrivateChat',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#(设置|修改)管理密码',
|
||
fnc: 'setAdminPassword',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#(设置|修改)用户密码',
|
||
fnc: 'setUserPassword'
|
||
},
|
||
{
|
||
reg: '^#chatgpt系统(设置|配置|管理)',
|
||
fnc: 'adminPage',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt用户(设置|配置|管理)',
|
||
fnc: 'userPage'
|
||
},
|
||
{
|
||
reg: '^#?(chatgpt)(对话|管理|娱乐|绘图|人物设定|聊天记录)?指令表(帮助|搜索(.+))?',
|
||
fnc: 'commandHelp'
|
||
},
|
||
{
|
||
reg: '^#语音切换.*',
|
||
fnc: 'ttsSwitch',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#(chatgpt)?(vits|azure|vox)?语音(角色列表|服务)$',
|
||
fnc: 'getTTSRoleList'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置后台(刷新|refresh)(t|T)oken$',
|
||
fnc: 'setOpenAIPlatformToken'
|
||
},
|
||
{
|
||
reg: '^#(chatgpt)?查看回复设置$',
|
||
fnc: 'viewUserSetting'
|
||
},
|
||
{
|
||
reg: '^#chatgpt导出配置',
|
||
fnc: 'exportConfig',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt导入配置',
|
||
fnc: 'importConfig',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(开启|关闭)智能模式$',
|
||
fnc: 'switchSmartMode',
|
||
permission: 'master'
|
||
}
|
||
]
|
||
})
|
||
}
|
||
|
||
async viewUserSetting (e) {
|
||
const userSetting = await getUserReplySetting(this.e)
|
||
const replyMsg = `${this.e.sender.user_id}的回复设置:
|
||
图片模式: ${userSetting.usePicture === true ? '开启' : '关闭'}
|
||
语音模式: ${userSetting.useTTS === true ? '开启' : '关闭'}
|
||
Vits语音角色: ${userSetting.ttsRole}
|
||
Azure语音角色: ${userSetting.ttsRoleAzure}
|
||
VoiceVox语音角色: ${userSetting.ttsRoleVoiceVox}
|
||
${userSetting.useTTS === true ? '当前语音模式为' + Config.ttsMode : ''}`
|
||
await this.reply(replyMsg.replace(/\n\s*$/, ''), e.isGroup)
|
||
return true
|
||
}
|
||
|
||
async getTTSRoleList (e) {
|
||
const matchCommand = e.msg.match(/^#(chatgpt)?(vits|azure|vox)?语音(服务|角色列表)/)
|
||
if (matchCommand[3] === '服务') {
|
||
await this.reply(`当前支持vox、vits、azure语音服务,可使用'#(vox|azure|vits)语音角色列表'查看支持的语音角色。
|
||
|
||
vits语音:主要有赛马娘,原神中文,原神日语,崩坏 3 的音色、结果有随机性,语调可能很奇怪。
|
||
|
||
vox语音:Voicevox 是一款由日本 DeNA 开发的语音合成软件,它可以将文本转换为自然流畅的语音。Voicevox 支持多种语言和声音,可以用于制作各种语音内容,如动画、游戏、广告等。Voicevox 还提供了丰富的调整选项,可以调整声音的音调、速度、音量等参数,以满足不同需求。除了桌面版软件外,Voicevox 还提供了 Web 版本和 API 接口,方便开发者在各种平台上使用。
|
||
|
||
azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,它可以帮助开发者将语音转换为文本、将文本转换为语音、实现自然语言理解和对话等功能。Azure 语音支持多种语言和声音,可以用于构建各种语音应用程序,如智能客服、语音助手、自动化电话系统等。Azure 语音还提供了丰富的 API 和 SDK,方便开发者在各种平台上集成使用。
|
||
`)
|
||
return true
|
||
}
|
||
let userReplySetting = await getUserReplySetting(this.e)
|
||
if (!userReplySetting.useTTS && matchCommand[2] === undefined) {
|
||
await this.reply('当前不是语音模式,如果想查看不同语音模式下支持的角色列表,可使用"#(vox|azure|vits)语音角色列表"查看')
|
||
return false
|
||
}
|
||
let ttsMode = Config.ttsMode
|
||
let roleList = []
|
||
if (matchCommand[2] === 'vits') {
|
||
roleList = getVitsRoleList(this.e)
|
||
} else if (matchCommand[2] === 'vox') {
|
||
roleList = getVoicevoxRoleList()
|
||
} else if (matchCommand[2] === 'azure') {
|
||
roleList = getAzureRoleList()
|
||
} else if (matchCommand[2] === undefined) {
|
||
switch (ttsMode) {
|
||
case 'vits-uma-genshin-honkai':
|
||
roleList = getVitsRoleList(this.e)
|
||
break
|
||
case 'voicevox':
|
||
roleList = getVoicevoxRoleList()
|
||
break
|
||
case 'azure':
|
||
roleList = getAzureRoleList()
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
} else {
|
||
await this.reply('设置错误,请使用"#chatgpt语音服务"查看支持的语音配置')
|
||
return false
|
||
}
|
||
if (roleList.length > 300) {
|
||
let chunks = roleList.match(/[^、]+(?:、[^、]+){0,30}/g)
|
||
roleList = await makeForwardMsg(e, chunks, `${Config.ttsMode}语音角色列表`)
|
||
}
|
||
await this.reply(roleList)
|
||
}
|
||
|
||
async ttsSwitch (e) {
|
||
let userReplySetting = await getUserReplySetting(this.e)
|
||
if (!userReplySetting.useTTS) {
|
||
let replyMsg
|
||
if (userReplySetting.usePicture) {
|
||
replyMsg = `当前为${!userReplySetting.useTTS ? '图片模式' : ''},请先切换到语音模式吧~`
|
||
} else {
|
||
replyMsg = `当前为${!userReplySetting.useTTS ? '文本模式' : ''},请先切换到语音模式吧~`
|
||
}
|
||
await this.reply(replyMsg, e.isGroup)
|
||
return false
|
||
}
|
||
let regExp = /#语音切换(.*)/
|
||
let ttsMode = e.msg.match(regExp)[1]
|
||
if (['vits', 'azure', 'voicevox'].includes(ttsMode)) {
|
||
if (ttsMode === 'vits') {
|
||
Config.ttsMode = 'vits-uma-genshin-honkai'
|
||
} else {
|
||
Config.ttsMode = ttsMode
|
||
}
|
||
await this.reply(`语音回复已切换至${Config.ttsMode}模式${Config.ttsMode === 'azure' ? ',建议重新开始对话以获得更好的对话效果!' : ''}`)
|
||
} else {
|
||
await this.reply('暂不支持此模式,当前支持vits,azure,voicevox。')
|
||
}
|
||
return false
|
||
}
|
||
|
||
async commandHelp (e) {
|
||
if (/^#(chatgpt)?指令表帮助$/.exec(e.msg.trim())) {
|
||
await this.reply('#chatgpt指令表: 查看本插件的所有指令\n' +
|
||
'#chatgpt(对话|管理|娱乐|绘图|人物设定|聊天记录)指令表: 查看对应功能分类的指令表\n' +
|
||
'#chatgpt指令表搜索xxx: 查看包含对应关键词的指令')
|
||
return false
|
||
}
|
||
const categories = {
|
||
对话: '对话',
|
||
管理: '管理',
|
||
娱乐: '娱乐',
|
||
绘图: '绘图',
|
||
人物设定: '人物设定',
|
||
聊天记录: '聊天记录'
|
||
}
|
||
|
||
function getCategory (e, plugin) {
|
||
for (const key in categories) {
|
||
if (e.msg.includes(key) && plugin.name.includes(categories[key])) {
|
||
return '功能名称: '
|
||
}
|
||
}
|
||
return ''
|
||
}
|
||
const commandSet = []
|
||
const plugins = await Promise.all(loader.priority.map(p => new p.class()))
|
||
|
||
for (const plugin of plugins) {
|
||
const name = plugin.name
|
||
const rule = plugin.rule
|
||
if (/^chatgpt/i.test(name) && rule) {
|
||
commandSet.push({ name, dsc: plugin.dsc, rule })
|
||
}
|
||
}
|
||
if (/^#(chatgpt)?指令表搜索(.+)/.test(e.msg.trim())) {
|
||
let cmd = e.msg.trim().match(/#(chatgpt)?指令表搜索(.+)/)[2]
|
||
if (!cmd) {
|
||
await this.reply('(⊙ˍ⊙)')
|
||
return 0
|
||
} else {
|
||
let searchResults = []
|
||
commandSet.forEach(plugin => {
|
||
plugin.rule.forEach(item => {
|
||
if (item.reg.toLowerCase().includes(cmd.toLowerCase())) {
|
||
searchResults.push(item.reg)
|
||
}
|
||
})
|
||
})
|
||
if (!searchResults.length) {
|
||
await this.reply('没有找到符合的结果,换个关键词吧!', e.isGroup)
|
||
return 0
|
||
} else if (searchResults.length <= 5) {
|
||
await this.reply(searchResults.join('\n'), e.isGroup)
|
||
return 1
|
||
} else {
|
||
let msg = await makeForwardMsg(e, searchResults, e.msg.slice(1).startsWith('chatgpt') ? e.msg.slice(8) : 'chatgpt' + e.msg.slice(1))
|
||
await this.reply(msg)
|
||
return 1
|
||
}
|
||
}
|
||
}
|
||
const generatePrompt = (plugin, command) => {
|
||
const category = getCategory(e, plugin)
|
||
const commandsStr = command.length ? `正则指令:\n${command.join('\n')}\n` : '正则指令: 无\n'
|
||
const description = `功能介绍:${plugin.dsc}\n`
|
||
return `${category}${plugin.name}\n${description}${commandsStr}`
|
||
}
|
||
|
||
const prompts = []
|
||
for (const plugin of commandSet) {
|
||
const commands = plugin.rule.map(v => v.reg.includes('[#*0-9]') ? '表情合成功能只需要发送两个emoji表情即可' : v.reg)
|
||
const category = getCategory(e, plugin)
|
||
if (category || (!e.msg.includes('对话') && !e.msg.includes('管理') && !e.msg.includes('娱乐') && !e.msg.includes('绘图') && !e.msg.includes('人物设定') && !e.msg.includes('聊天记录'))) {
|
||
prompts.push(generatePrompt(plugin, commands))
|
||
}
|
||
}
|
||
let msg = await makeForwardMsg(e, prompts, e.msg.slice(1).startsWith('chatgpt') ? e.msg.slice(1) : ('chatgpt' + e.msg.slice(1)))
|
||
await this.reply(msg)
|
||
return true
|
||
}
|
||
|
||
async enablePrivateChat (e) {
|
||
Config.enablePrivateChat = !!e.msg.match(/(允许|打开|同意)/)
|
||
await this.reply('设置成功', e.isGroup)
|
||
return false
|
||
}
|
||
|
||
async enableGroupContext (e) {
|
||
const reg = /(关闭|打开)/
|
||
const match = e.msg.match(reg)
|
||
if (match) {
|
||
const action = match[1]
|
||
if (action === '关闭') {
|
||
Config.enableGroupContext = false // 关闭
|
||
await this.reply('已关闭群聊上下文功能', true)
|
||
} else {
|
||
Config.enableGroupContext = true // 打开
|
||
await this.reply('已打开群聊上下文功能', true)
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
async setDefaultReplySetting (e) {
|
||
const reg = /^#chatgpt(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|((azure|vits|vox)?语音角色|角色语音|角色)(.*))|回复帮助)/
|
||
const matchCommand = e.msg.match(reg)
|
||
const settingType = matchCommand[2]
|
||
let replyMsg = ''
|
||
let ttsSupportKinds = []
|
||
if (Config.azureTTSKey) ttsSupportKinds.push(1)
|
||
if (Config.ttsSpace) ttsSupportKinds.push(2)
|
||
if (Config.voicevoxSpace) ttsSupportKinds.push(3)
|
||
switch (settingType) {
|
||
case '图片模式':
|
||
if (matchCommand[1] === '打开') {
|
||
Config.defaultUsePicture = true
|
||
Config.defaultUseTTS = false
|
||
replyMsg = 'ChatGPT将默认以图片回复'
|
||
} else if (matchCommand[1] === '关闭') {
|
||
Config.defaultUsePicture = false
|
||
if (Config.defaultUseTTS) {
|
||
replyMsg = 'ChatGPT将默认以语音回复'
|
||
} else {
|
||
replyMsg = 'ChatGPT将默认以文本回复'
|
||
}
|
||
} else if (matchCommand[1] === '设置') {
|
||
replyMsg = '请使用“#chatgpt打开全局图片模式”或“#chatgpt关闭全局图片模式”命令来设置回复模式'
|
||
} break
|
||
case '文本模式':
|
||
if (matchCommand[1] === '打开') {
|
||
Config.defaultUsePicture = false
|
||
Config.defaultUseTTS = false
|
||
replyMsg = 'ChatGPT将默认以文本回复'
|
||
} else if (matchCommand[1] === '关闭') {
|
||
if (Config.defaultUseTTS) {
|
||
replyMsg = 'ChatGPT将默认以语音回复'
|
||
} else if (Config.defaultUsePicture) {
|
||
replyMsg = 'ChatGPT将默认以图片回复'
|
||
} else {
|
||
Config.defaultUseTTS = true
|
||
replyMsg = 'ChatGPT将默认以语音回复'
|
||
}
|
||
} else if (matchCommand[1] === '设置') {
|
||
replyMsg = '请使用“#chatgpt打开全局文本模式”或“#chatgpt关闭全局文本模式”命令来设置回复模式'
|
||
} break
|
||
case '语音模式':
|
||
if (!ttsSupportKinds.length) {
|
||
replyMsg = '您没有配置任何语音服务,请前往锅巴面板进行配置'
|
||
break
|
||
}
|
||
if (matchCommand[1] === '打开') {
|
||
Config.defaultUseTTS = true
|
||
Config.defaultUsePicture = false
|
||
replyMsg = 'ChatGPT将默认以语音回复'
|
||
} else if (matchCommand[1] === '关闭') {
|
||
Config.defaultUseTTS = false
|
||
if (Config.defaultUsePicture) {
|
||
replyMsg = 'ChatGPT将默认以图片回复'
|
||
} else {
|
||
replyMsg = 'ChatGPT将默认以文本回复'
|
||
}
|
||
} else if (matchCommand[1] === '设置') {
|
||
replyMsg = '请使用“#chatgpt打开全局语音模式”或“#chatgpt关闭全局语音模式”命令来设置回复模式'
|
||
} break
|
||
case '回复帮助':
|
||
replyMsg = '可使用以下命令配置全局回复:\n#chatgpt(打开/关闭)全局(语音/图片/文本)模式\n#chatgpt设置全局(vox|azure|vits)语音角色+角色名称(留空则为随机)\n'
|
||
break
|
||
default:
|
||
if (!ttsSupportKinds) {
|
||
replyMsg = '您没有配置任何语音服务,请前往锅巴面板进行配置'
|
||
break
|
||
}
|
||
if (settingType.match(/(语音角色|角色语音|角色)/)) {
|
||
const voiceKind = matchCommand[5]
|
||
let speaker = matchCommand[6] || ''
|
||
if (voiceKind === undefined) {
|
||
await this.reply('请选择需要设置的语音类型。使用"#chatgpt语音服务"查看支持的语音类型')
|
||
return false
|
||
}
|
||
if (!speaker.length || speaker === '随机') {
|
||
replyMsg = `设置成功,ChatGpt将在${voiceKind}语音模式下随机挑选角色进行回复`
|
||
if (voiceKind === 'vits') Config.defaultTTSRole = '随机'
|
||
if (voiceKind === 'azure') Config.azureTTSSpeaker = '随机'
|
||
if (voiceKind === 'vox') Config.voicevoxTTSSpeaker = '随机'
|
||
} else {
|
||
if (ttsSupportKinds.includes(1) && voiceKind === 'azure') {
|
||
if (getAzureRoleList().includes(speaker)) {
|
||
Config.defaultUseTTS = azureRoleList.filter(s => s.name === speaker)[0].code
|
||
replyMsg = `ChatGPT默认语音角色已被设置为“${speaker}”`
|
||
} else {
|
||
await this.reply(`抱歉,没有"${speaker}"这个角色,目前azure模式下支持的角色有${azureRoleList.map(item => item.name).join('、')}`)
|
||
return false
|
||
}
|
||
} else if (ttsSupportKinds.includes(2) && voiceKind === 'vits') {
|
||
const ttsRole = convertSpeaker(speaker)
|
||
if (vitsRoleList.includes(ttsRole)) {
|
||
Config.defaultTTSRole = ttsRole
|
||
replyMsg = `ChatGPT默认语音角色已被设置为“${ttsRole}”`
|
||
} else {
|
||
replyMsg = `抱歉,我还不认识“${ttsRole}”这个语音角色,可使用'#vits角色列表'查看可配置的角色`
|
||
}
|
||
} else if (ttsSupportKinds.includes(3) && voiceKind === 'vox') {
|
||
if (getVoicevoxRoleList().includes(speaker)) {
|
||
let regex = /^(.*?)-(.*)$/
|
||
let match = regex.exec(speaker)
|
||
let style = null
|
||
if (match) {
|
||
speaker = match[1]
|
||
style = match[2]
|
||
}
|
||
let chosen = VoiceVoxTTS.supportConfigurations.filter(s => s.name === speaker)
|
||
if (chosen.length === 0) {
|
||
await this.reply(`抱歉,没有"${speaker}"这个角色,目前voicevox模式下支持的角色有${VoiceVoxTTS.supportConfigurations.map(item => item.name).join('、')}`)
|
||
break
|
||
}
|
||
if (style && !chosen[0].styles.find(item => item.name === style)) {
|
||
await this.reply(`抱歉,"${speaker}"这个角色没有"${style}"这个风格,目前支持的风格有${chosen[0].styles.map(item => item.name).join('、')}`)
|
||
break
|
||
}
|
||
Config.ttsRoleVoiceVox = chosen[0].name + (style ? `-${style}` : '')
|
||
replyMsg = `ChatGPT默认语音角色已被设置为“${speaker}”`
|
||
} else {
|
||
await this.reply(`抱歉,没有"${speaker}"这个角色,目前voicevox模式下支持的角色有${voxRoleList.map(item => item.name).join('、')}`)
|
||
return false
|
||
}
|
||
} else {
|
||
replyMsg = `${voiceKind}语音角色设置错误,请检查语音配置~`
|
||
}
|
||
}
|
||
} else {
|
||
replyMsg = "无法识别的设置类型\n请使用'#chatgpt全局回复帮助'查看正确命令"
|
||
}
|
||
}
|
||
await this.reply(replyMsg, true)
|
||
}
|
||
|
||
async turnOnConfirm (e) {
|
||
await redis.set('CHATGPT:CONFIRM', 'on')
|
||
await this.reply('已开启消息确认', true)
|
||
return false
|
||
}
|
||
|
||
async turnOffConfirm (e) {
|
||
await redis.set('CHATGPT:CONFIRM', 'off')
|
||
await this.reply('已关闭消息确认', true)
|
||
return false
|
||
}
|
||
|
||
async setAccessToken (e) {
|
||
this.setContext('saveToken')
|
||
await this.reply('请发送ChatGPT AccessToken', true)
|
||
return false
|
||
}
|
||
|
||
async setPoeCookie () {
|
||
this.setContext('savePoeToken')
|
||
await this.reply('请发送Poe Cookie', true)
|
||
return false
|
||
}
|
||
|
||
async savePoeToken (e) {
|
||
if (!this.e.msg) return
|
||
let token = this.e.msg
|
||
if (!token.startsWith('p-b=')) {
|
||
await this.reply('Poe cookie格式错误', true)
|
||
this.finish('savePoeToken')
|
||
return
|
||
}
|
||
await redis.set('CHATGPT:POE_TOKEN', token)
|
||
await this.reply('Poe cookie设置成功', true)
|
||
this.finish('savePoeToken')
|
||
}
|
||
|
||
async setBingAccessToken (e) {
|
||
this.setContext('saveBingToken')
|
||
await this.reply('请发送Bing Cookie Token.("_U" cookie from bing.com)', true)
|
||
return false
|
||
}
|
||
|
||
async migrateBingAccessToken () {
|
||
let token = await redis.get('CHATGPT:BING_TOKEN')
|
||
if (token) {
|
||
token = token.split('|')
|
||
token = token.map((item, index) => (
|
||
{
|
||
Token: item,
|
||
State: '正常',
|
||
Usage: 0
|
||
}
|
||
))
|
||
} else {
|
||
token = []
|
||
}
|
||
let tokens = await redis.get('CHATGPT:BING_TOKENS')
|
||
if (tokens) {
|
||
tokens = JSON.parse(tokens)
|
||
} else {
|
||
tokens = []
|
||
}
|
||
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify([...token, ...tokens]))
|
||
await this.reply('迁移完成', true)
|
||
}
|
||
|
||
async getBingAccessToken (e) {
|
||
let tokens = await redis.get('CHATGPT:BING_TOKENS')
|
||
if (tokens) tokens = JSON.parse(tokens)
|
||
else tokens = []
|
||
tokens = tokens.length > 0
|
||
? tokens.map((item, index) => (
|
||
`【${index}】 Token:${item.Token.substring(0, 5 / 2) + '...' + item.Token.substring(item.Token.length - 5 / 2, item.Token.length)}`
|
||
)).join('\n')
|
||
: '无必应Token记录'
|
||
await this.reply(`${tokens}`, true)
|
||
return false
|
||
}
|
||
|
||
async delBingAccessToken (e) {
|
||
this.setContext('deleteBingToken')
|
||
let tokens = await redis.get('CHATGPT:BING_TOKENS')
|
||
if (tokens) tokens = JSON.parse(tokens)
|
||
else tokens = []
|
||
tokens = tokens.length > 0
|
||
? tokens.map((item, index) => (
|
||
`【${index}】 Token:${item.Token.substring(0, 5 / 2) + '...' + item.Token.substring(item.Token.length - 5 / 2, item.Token.length)}`
|
||
)).join('\n')
|
||
: '无必应Token记录'
|
||
await this.reply(`请发送要删除的token编号\n${tokens}`, true)
|
||
if (tokens.length == 0) this.finish('saveBingToken')
|
||
return false
|
||
}
|
||
|
||
async saveBingToken () {
|
||
if (!this.e.msg) return
|
||
let token = this.e.msg
|
||
if (token.length < 100) {
|
||
await this.reply('Bing Token格式错误,请确定获取了有效的_U Cookie或完整的Cookie', true)
|
||
this.finish('saveBingToken')
|
||
return
|
||
}
|
||
let cookie
|
||
if (token?.indexOf('=') > -1) {
|
||
cookie = token
|
||
}
|
||
const bingAIClient = new SydneyAIClient({
|
||
userToken: token, // "_U" cookie from bing.com
|
||
cookie,
|
||
debug: Config.debug
|
||
})
|
||
// 异步就好了,不卡着这个context了
|
||
bingAIClient.createNewConversation().then(async res => {
|
||
if (res.clientId) {
|
||
logger.info('bing token 有效')
|
||
} else {
|
||
logger.error('bing token 无效', res)
|
||
// 移除无效token
|
||
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
|
||
let bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
|
||
const element = bingToken.findIndex(element => element.token === token)
|
||
if (element >= 0) {
|
||
bingToken[element].State = '异常'
|
||
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken))
|
||
}
|
||
}
|
||
await this.reply(`经检测,Bing Token无效。来自Bing的错误提示:${res.result?.message}`)
|
||
}
|
||
})
|
||
let bingToken = []
|
||
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
|
||
bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
|
||
if (!bingToken.some(element => element.token === token)) {
|
||
bingToken.push({
|
||
Token: token,
|
||
State: '正常',
|
||
Usage: 0
|
||
})
|
||
}
|
||
} else {
|
||
bingToken = [{
|
||
Token: token,
|
||
State: '正常',
|
||
Usage: 0
|
||
}]
|
||
}
|
||
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken))
|
||
await this.reply('Bing Token设置成功', true)
|
||
this.finish('saveBingToken')
|
||
}
|
||
|
||
async deleteBingToken () {
|
||
if (!this.e.msg) return
|
||
let tokenId = this.e.msg
|
||
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
|
||
let bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
|
||
if (tokenId >= 0 && tokenId < bingToken.length) {
|
||
const removeToken = bingToken[tokenId].Token
|
||
bingToken.splice(tokenId, 1)
|
||
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken))
|
||
await this.reply(`Token ${removeToken.substring(0, 5 / 2) + '...' + removeToken.substring(removeToken.length - 5 / 2, removeToken.length)} 移除成功`, true)
|
||
this.finish('deleteBingToken')
|
||
} else {
|
||
await this.reply('Token编号错误!', true)
|
||
this.finish('deleteBingToken')
|
||
}
|
||
} else {
|
||
await this.reply('Token记录异常', true)
|
||
this.finish('deleteBingToken')
|
||
}
|
||
}
|
||
|
||
async saveToken () {
|
||
if (!this.e.msg) return
|
||
let token = this.e.msg
|
||
if (!token.startsWith('ey') || token.length < 20) {
|
||
await this.reply('ChatGPT AccessToken格式错误', true)
|
||
this.finish('saveToken')
|
||
return
|
||
}
|
||
await redis.set('CHATGPT:TOKEN', token)
|
||
await this.reply('ChatGPT AccessToken设置成功', true)
|
||
this.finish('saveToken')
|
||
}
|
||
|
||
async useBrowserBasedSolution (e) {
|
||
await redis.set('CHATGPT:USE', 'browser')
|
||
await this.reply('已切换到基于浏览器的解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误')
|
||
}
|
||
|
||
async useOpenAIAPIBasedSolution (e) {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'api') {
|
||
await redis.set('CHATGPT:USE', 'api')
|
||
await this.reply('已切换到基于OpenAI API的解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误')
|
||
} else {
|
||
await this.reply('当前已经是API模式了')
|
||
}
|
||
}
|
||
|
||
async useChatGLMSolution (e) {
|
||
await redis.set('CHATGPT:USE', 'chatglm')
|
||
await this.reply('已切换到ChatGLM-6B解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误')
|
||
}
|
||
|
||
async useReversedAPIBasedSolution2 (e) {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'api3') {
|
||
await redis.set('CHATGPT:USE', 'api3')
|
||
await this.reply('已切换到基于第三方Reversed Conversastion API(API3)的解决方案')
|
||
} else {
|
||
await this.reply('当前已经是API3模式了')
|
||
}
|
||
}
|
||
|
||
async useBingSolution (e) {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'bing') {
|
||
await redis.set('CHATGPT:USE', 'bing')
|
||
await this.reply('已切换到基于微软新必应的解决方案,如果已经对话过务必执行`#结束对话`避免引起404错误')
|
||
} else {
|
||
await this.reply('当前已经是必应Bing模式了')
|
||
}
|
||
}
|
||
|
||
async useClaudeBasedSolution (e) {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'poe') {
|
||
await redis.set('CHATGPT:USE', 'poe')
|
||
await this.reply('已切换到基于Quora\'s POE的解决方案')
|
||
} else {
|
||
await this.reply('当前已经是POE模式了')
|
||
}
|
||
}
|
||
|
||
async useSlackClaudeBasedSolution () {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'claude') {
|
||
await redis.set('CHATGPT:USE', 'claude')
|
||
await this.reply('已切换到基于slack claude机器人的解决方案')
|
||
} else {
|
||
await this.reply('当前已经是claude模式了')
|
||
}
|
||
}
|
||
|
||
async useXinghuoBasedSolution () {
|
||
let use = await redis.get('CHATGPT:USE')
|
||
if (use !== 'xh') {
|
||
await redis.set('CHATGPT:USE', 'xh')
|
||
await this.reply('已切换到基于星火的解决方案')
|
||
} else {
|
||
await this.reply('当前已经是星火模式了')
|
||
}
|
||
}
|
||
|
||
async changeBingTone (e) {
|
||
let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '')
|
||
if (!tongStyle) {
|
||
return
|
||
}
|
||
let map = {
|
||
精准: 'precise',
|
||
创意: 'creative',
|
||
均衡: 'balanced',
|
||
Sydney: 'Sydney',
|
||
sydney: 'Sydney',
|
||
悉尼: 'Sydney',
|
||
自设定: 'Custom',
|
||
自定义: 'Custom'
|
||
}
|
||
if (map[tongStyle]) {
|
||
Config.toneStyle = map[tongStyle]
|
||
await e.reply('切换成功')
|
||
} else {
|
||
await e.reply('没有这种风格。支持的风格:精准、创意、均衡、悉尼、自设定')
|
||
}
|
||
}
|
||
|
||
async bingOpenSuggestedResponses (e) {
|
||
Config.enableSuggestedResponses = e.msg.indexOf('开启') > -1
|
||
await e.reply('操作成功')
|
||
}
|
||
|
||
async checkAuth (e) {
|
||
if (!e.isMaster) {
|
||
e.reply(`只有主人才能命令ChatGPT哦~
|
||
(*/ω\*)`)
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
async versionChatGPTPlugin (e) {
|
||
await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/version`, { Viewport: { width: 800, height: 600 } })
|
||
}
|
||
|
||
async modeHelp () {
|
||
let mode = await redis.get('CHATGPT:USE')
|
||
const modeMap = {
|
||
browser: '浏览器',
|
||
// apiReverse: 'API2',
|
||
api: 'API',
|
||
bing: '必应',
|
||
api3: 'API3',
|
||
chatglm: 'ChatGLM-6B',
|
||
claude: 'Claude',
|
||
poe: 'Poe'
|
||
}
|
||
let modeText = modeMap[mode || 'api']
|
||
let message = `API模式和浏览器模式如何选择?
|
||
|
||
API模式会调用 OpenAI 官方提供的 gpt-3.5-turbo API,只需要提供 API Key。一般情况下,该种方式响应速度更快,不会像 chatGPT 官网一样总出现不可用的现象,但要注意 gpt-3.5-turbo 的 API 调用是收费的,新用户有 $5 的试用金可用于支付,价格为 $0.0020/1K tokens。(问题和回答加起来算 token)
|
||
|
||
API3 模式会调用官网反代 API,它会帮你绕过 CF 防护,需要提供 ChatGPT 的 Token。效果与官网和浏览器一致。设置 Token 指令:#chatgpt设置token。
|
||
|
||
浏览器模式通过在本地启动 Chrome 等浏览器模拟用户访问 ChatGPT 网站,使得获得和官方以及 API2 模式一模一样的回复质量,同时保证安全性。缺点是本方法对环境要求较高,需要提供桌面环境和一个可用的代理(能够访问 ChatGPT 的 IP 地址),且响应速度不如 API,而且高峰期容易无法使用。
|
||
|
||
必应(Bing)将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的 Bing 登录 Cookie 方可使用。#chatgpt设置必应 Token。
|
||
|
||
自建 ChatGLM 模式会调用自建的 ChatGLM-6B 服务器 API 进行对话,需要自建。参考 https://github.com/ikechan8370/SimpleChatGLM6BAPI。
|
||
|
||
Claude 模式会调用 Slack 中的 Claude 机器人进行对话,与其他模式不同的是全局共享一个对话。配置参考 https://ikechan8370.com/archives/chatgpt-plugin-for-yunzaipei-zhi-slack-claude。
|
||
|
||
Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie:#chatgpt设置 Poe Token。
|
||
|
||
星火 模式会调用科大讯飞推出的新一代认知智能大模型 '星火认知大模型' 进行对话。需要提供Cookie:#chatgpt设置星火token。
|
||
|
||
您可以使用 "#chatgpt切换浏览器/API/API3/Bing/ChatGLM/Claude/Poe/星火" 来切换到指定模式。
|
||
|
||
当前为 ${modeText} 模式。`
|
||
await this.reply(message)
|
||
}
|
||
|
||
async shutUp (e) {
|
||
let duration = e.msg.replace(/^#chatgpt(本群)?(群\d+)?(关闭|闭嘴|关机|休眠|下班)/, '')
|
||
let scope
|
||
let time = 3600000
|
||
if (duration === '永久') {
|
||
time = 0
|
||
} else if (duration) {
|
||
time = parseDuration(duration)
|
||
}
|
||
const match = e.msg.match(/#chatgpt群(\d+)?(关闭|闭嘴|关机|休眠|下班)(.*)/)
|
||
if (e.msg.indexOf('本群') > -1) {
|
||
if (e.isGroup) {
|
||
scope = e.group.group_id
|
||
if (await redis.get(`CHATGPT:SHUT_UP:${scope}`)) {
|
||
await redis.del(`CHATGPT:SHUT_UP:${scope}`)
|
||
await redis.set(`CHATGPT:SHUT_UP:${scope}`, '1', { EX: time })
|
||
await e.reply(`好的,已切换休眠状态:倒计时${formatDuration(time)}`)
|
||
} else {
|
||
await redis.set(`CHATGPT:SHUT_UP:${scope}`, '1', { EX: time })
|
||
await e.reply(`好的,已切换休眠状态:倒计时${formatDuration(time)}`)
|
||
}
|
||
} else {
|
||
await e.reply('主人,这里好像不是群哦')
|
||
return false
|
||
}
|
||
} else if (match) {
|
||
const groupId = parseInt(match[1], 10)
|
||
if (Bot.getGroupList().get(groupId)) {
|
||
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
||
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
||
await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time })
|
||
await e.reply(`好的,即将在群${groupId}中休眠${formatDuration(time)}`)
|
||
} else {
|
||
await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time })
|
||
await e.reply(`好的,即将在群${groupId}中休眠${formatDuration(time)}`)
|
||
}
|
||
} else {
|
||
await e.reply('主人还没告诉我群号呢')
|
||
return false
|
||
}
|
||
} else {
|
||
if (await redis.get('CHATGPT:SHUT_UP:ALL')) {
|
||
await redis.del('CHATGPT:SHUT_UP:ALL')
|
||
await redis.set('CHATGPT:SHUT_UP:ALL', '1', { EX: time })
|
||
await e.reply(`好的,我会延长休眠时间${formatDuration(time)}`)
|
||
} else {
|
||
await redis.set('CHATGPT:SHUT_UP:ALL', '1', { EX: time })
|
||
await e.reply(`好的,我会延长休眠时间${formatDuration(time)}`)
|
||
}
|
||
}
|
||
}
|
||
|
||
async openMouth (e) {
|
||
const match = e.msg.match(/^#chatgpt群(\d+)/)
|
||
if (e.msg.indexOf('本群') > -1) {
|
||
if (await redis.get('CHATGPT:SHUT_UP:ALL')) {
|
||
await e.reply('当前为休眠模式,没办法做出回应呢')
|
||
return false
|
||
}
|
||
if (e.isGroup) {
|
||
let scope = e.group.group_id
|
||
if (await redis.get(`CHATGPT:SHUT_UP:${scope}`)) {
|
||
await redis.del(`CHATGPT:SHUT_UP:${scope}`)
|
||
await e.reply('好的主人,我又可以和大家聊天啦')
|
||
} else {
|
||
await e.reply('主人,我已经启动过了哦')
|
||
}
|
||
} else {
|
||
await e.reply('主人,这里好像不是群哦')
|
||
return false
|
||
}
|
||
} else if (match) {
|
||
if (await redis.get('CHATGPT:SHUT_UP:ALL')) {
|
||
await e.reply('当前为休眠模式,没办法做出回应呢')
|
||
return false
|
||
}
|
||
const groupId = parseInt(match[1], 10)
|
||
if (Bot.getGroupList().get(groupId)) {
|
||
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
||
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
||
await e.reply(`好的主人,我终于又可以在群${groupId}和大家聊天了`)
|
||
} else {
|
||
await e.reply(`主人,我在群${groupId}中已经是启动状态了哦`)
|
||
}
|
||
} else {
|
||
await e.reply('主人还没告诉我群号呢')
|
||
return false
|
||
}
|
||
} else {
|
||
let keys = await redis.keys('CHATGPT:SHUT_UP:*')
|
||
if (await redis.get('CHATGPT:SHUT_UP:ALL')) {
|
||
await redis.del('CHATGPT:SHUT_UP:ALL')
|
||
for (let i = 0; i < keys.length; i++) {
|
||
await redis.del(keys[i])
|
||
}
|
||
await e.reply('好的,我会开启所有群聊响应')
|
||
} else if (keys || keys.length > 0) {
|
||
for (let i = 0; i < keys.length; i++) {
|
||
await redis.del(keys[i])
|
||
}
|
||
await e.reply('已经开启过全群响应啦')
|
||
} else {
|
||
await e.reply('我没有在任何群休眠哦')
|
||
}
|
||
}
|
||
}
|
||
|
||
async listShutUp () {
|
||
let keys = await redis.keys('CHATGPT:SHUT_UP:*')
|
||
if (!keys || keys.length === 0) {
|
||
await this.reply('已经开启过全群响应啦', true)
|
||
} else {
|
||
let list = []
|
||
for (let i = 0; i < keys.length; i++) {
|
||
let key = keys[i]
|
||
let groupId = key.replace('CHATGPT:SHUT_UP:', '')
|
||
let ttl = await redis.ttl(key)
|
||
let ttlFormat = formatDuration(ttl)
|
||
list.push({ groupId, ttlFormat })
|
||
}
|
||
await this.reply(list.map(item => item.groupId !== 'ALL' ? `群聊${item.groupId}: ${item.ttlFormat}` : `全局: ${item.ttlFormat}`).join('\n'))
|
||
}
|
||
}
|
||
|
||
async setAPIKey (e) {
|
||
this.setContext('saveAPIKey')
|
||
await this.reply('请发送OpenAI API Key.', true)
|
||
return false
|
||
}
|
||
|
||
async saveAPIKey () {
|
||
if (!this.e.msg) return
|
||
let token = this.e.msg
|
||
if (!token.startsWith('sk-')) {
|
||
await this.reply('OpenAI API Key格式错误', true)
|
||
this.finish('saveAPIKey')
|
||
return
|
||
}
|
||
// todo
|
||
Config.apiKey = token
|
||
await this.reply('OpenAI API Key设置成功', true)
|
||
this.finish('saveAPIKey')
|
||
}
|
||
|
||
async setXinghuoToken () {
|
||
this.setContext('saveXinghuoToken')
|
||
await this.reply('请发送星火的ssoSessionId', true)
|
||
return false
|
||
}
|
||
|
||
async saveXinghuoToken () {
|
||
if (!this.e.msg) return
|
||
let token = this.e.msg
|
||
// todo
|
||
Config.xinghuoToken = token
|
||
await this.reply('星火ssoSessionId设置成功', true)
|
||
this.finish('saveXinghuoToken')
|
||
}
|
||
|
||
async setAPIPromptPrefix (e) {
|
||
this.setContext('saveAPIPromptPrefix')
|
||
await this.reply('请发送用于API模式的设定', true)
|
||
return false
|
||
}
|
||
|
||
async saveAPIPromptPrefix (e) {
|
||
if (!this.e.msg) return
|
||
if (this.e.msg === '取消') {
|
||
await this.reply('已取消设置API设定', true)
|
||
this.finish('saveAPIPromptPrefix')
|
||
return
|
||
}
|
||
// todo
|
||
Config.promptPrefixOverride = this.e.msg
|
||
await this.reply('API模式的设定设置成功', true)
|
||
this.finish('saveAPIPromptPrefix')
|
||
}
|
||
|
||
async setBingPromptPrefix (e) {
|
||
this.setContext('saveBingPromptPrefix')
|
||
await this.reply('请发送用于Bing Sydney模式的设定', true)
|
||
return false
|
||
}
|
||
|
||
async saveBingPromptPrefix (e) {
|
||
if (!this.e.msg) return
|
||
if (this.e.msg === '取消') {
|
||
await this.reply('已取消设置Sydney设定', true)
|
||
this.finish('saveBingPromptPrefix')
|
||
return
|
||
}
|
||
Config.sydney = this.e.msg
|
||
await this.reply('Bing Sydney模式的设定设置成功', true)
|
||
this.finish('saveBingPromptPrefix')
|
||
}
|
||
|
||
async switchDraw (e) {
|
||
if (e.msg.indexOf('开启') > -1) {
|
||
if (Config.enableDraw) {
|
||
await this.reply('当前已经开启chatgpt画图功能', true)
|
||
} else {
|
||
Config.enableDraw = true
|
||
await this.reply('chatgpt画图功能开启成功', true)
|
||
}
|
||
} else {
|
||
if (!Config.enableDraw) {
|
||
await this.reply('当前未开启chatgpt画图功能', true)
|
||
} else {
|
||
Config.enableDraw = false
|
||
await this.reply('chatgpt画图功能关闭成功', true)
|
||
}
|
||
}
|
||
}
|
||
|
||
async queryAPIPromptPrefix (e) {
|
||
await this.reply(Config.promptPrefixOverride, true)
|
||
}
|
||
|
||
async queryBingPromptPrefix (e) {
|
||
await this.reply(Config.sydney, true)
|
||
}
|
||
|
||
async setAdminPassword (e) {
|
||
if (e.isGroup || !e.isPrivate) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
this.setContext('saveAdminPassword')
|
||
await this.reply('请发送系统管理密码', true)
|
||
return false
|
||
}
|
||
|
||
async setUserPassword (e) {
|
||
if (e.isGroup || !e.isPrivate) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
this.setContext('saveUserPassword')
|
||
await this.reply('请发送系统用户密码', true)
|
||
return false
|
||
}
|
||
|
||
async saveAdminPassword (e) {
|
||
if (!this.e.msg) return
|
||
const passwd = this.e.msg
|
||
await redis.set('CHATGPT:ADMIN_PASSWD', md5(passwd))
|
||
await this.reply('设置成功', true)
|
||
this.finish('saveAdminPassword')
|
||
}
|
||
|
||
async saveUserPassword (e) {
|
||
if (!this.e.msg) return
|
||
const passwd = this.e.msg
|
||
const dir = 'resources/ChatGPTCache/user'
|
||
const filename = `${this.e.user_id}.json`
|
||
const filepath = path.join(dir, filename)
|
||
fs.mkdirSync(dir, { recursive: true })
|
||
if (fs.existsSync(filepath)) {
|
||
fs.readFile(filepath, 'utf8', (err, data) => {
|
||
if (err) {
|
||
console.error(err)
|
||
return
|
||
}
|
||
const config = JSON.parse(data)
|
||
config.passwd = md5(passwd)
|
||
fs.writeFile(filepath, JSON.stringify(config), 'utf8', (err) => {
|
||
if (err) {
|
||
console.error(err)
|
||
}
|
||
})
|
||
})
|
||
} else {
|
||
fs.writeFile(filepath, JSON.stringify({
|
||
user: this.e.user_id,
|
||
passwd: md5(passwd),
|
||
chat: []
|
||
}), 'utf8', (err) => {
|
||
if (err) {
|
||
console.error(err)
|
||
}
|
||
})
|
||
}
|
||
await this.reply('设置完成', true)
|
||
this.finish('saveUserPassword')
|
||
}
|
||
|
||
async adminPage (e) {
|
||
if (!Config.groupAdminPage && (e.isGroup || !e.isPrivate)) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/`
|
||
await this.reply(`请登录${viewHost + 'admin/settings'}进行系统配置`, true)
|
||
}
|
||
|
||
async userPage (e) {
|
||
if (!Config.groupAdminPage && (e.isGroup || !e.isPrivate)) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/`
|
||
await this.reply(`请登录${viewHost + 'admin/dashboard'}进行系统配置`, true)
|
||
}
|
||
|
||
async setOpenAIPlatformToken (e) {
|
||
this.setContext('doSetOpenAIPlatformToken')
|
||
await e.reply('请发送refreshToken\n你可以在已登录的platform.openai.com后台界面打开调试窗口,在终端中执行\nJSON.parse(localStorage.getItem(Object.keys(localStorage).filter(k => k.includes(\'auth0\'))[0])).body.refresh_token\n如果仍不能查看余额,请退出登录重新获取刷新令牌')
|
||
}
|
||
|
||
async doSetOpenAIPlatformToken () {
|
||
let token = this.e.msg
|
||
if (!token) {
|
||
return false
|
||
}
|
||
Config.OpenAiPlatformRefreshToken = token.replaceAll('\'', '')
|
||
await this.e.reply('设置成功')
|
||
this.finish('doSetOpenAIPlatformToken')
|
||
}
|
||
|
||
async exportConfig (e) {
|
||
if (e.isGroup || !e.isPrivate) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
let redisConfig = {}
|
||
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
|
||
let bingTokens = await redis.get('CHATGPT:BING_TOKENS')
|
||
if (bingTokens) { bingTokens = JSON.parse(bingTokens) } else bingTokens = []
|
||
redisConfig.bingTokens = bingTokens
|
||
} else {
|
||
redisConfig.bingTokens = []
|
||
}
|
||
if (await redis.exists('CHATGPT:CONFIRM') != 0) {
|
||
redisConfig.turnConfirm = await redis.get('CHATGPT:CONFIRM') === 'on'
|
||
}
|
||
if (await redis.exists('CHATGPT:USE') != 0) {
|
||
redisConfig.useMode = await redis.get('CHATGPT:USE')
|
||
}
|
||
const configJson = JSON.stringify({
|
||
chatConfig: Config,
|
||
redisConfig
|
||
})
|
||
console.log(configJson)
|
||
const buf = Buffer.from(configJson)
|
||
e.friend.sendFile(buf, `ChatGPT-Plugin Config ${new Date()}.json`)
|
||
return true
|
||
}
|
||
|
||
async importConfig (e) {
|
||
if (e.isGroup || !e.isPrivate) {
|
||
await this.reply('请私聊发送命令', true)
|
||
return true
|
||
}
|
||
this.setContext('doImportConfig')
|
||
await e.reply('请发送配置文件')
|
||
}
|
||
|
||
async doImportConfig (e) {
|
||
const file = this.e.message.find(item => item.type === 'file')
|
||
if (file) {
|
||
const fileUrl = await this.e.friend.getFileUrl(file.fid)
|
||
if (fileUrl) {
|
||
try {
|
||
let changeConfig = []
|
||
const response = await fetch(fileUrl)
|
||
const data = await response.json()
|
||
const chatdata = data.chatConfig || {}
|
||
for (let [keyPath, value] of Object.entries(chatdata)) {
|
||
if (keyPath === 'blockWords' || keyPath === 'promptBlockWords' || keyPath === 'initiativeChatGroups') { value = value.toString().split(/[,,;;\|]/) }
|
||
if (Config[keyPath] != value) {
|
||
changeConfig.push({
|
||
item: keyPath,
|
||
value: typeof (value) === 'object' ? JSON.stringify(value) : value,
|
||
old: typeof (Config[keyPath]) === 'object' ? JSON.stringify(Config[keyPath]) : Config[keyPath],
|
||
type: 'config'
|
||
})
|
||
Config[keyPath] = value
|
||
}
|
||
}
|
||
const redisConfig = data.redisConfig || {}
|
||
if (redisConfig.bingTokens != null) {
|
||
changeConfig.push({
|
||
item: 'bingTokens',
|
||
value: JSON.stringify(redisConfig.bingTokens),
|
||
old: await redis.get('CHATGPT:BING_TOKENS'),
|
||
type: 'redis'
|
||
})
|
||
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(redisConfig.bingTokens))
|
||
}
|
||
if (redisConfig.turnConfirm != null) {
|
||
changeConfig.push({
|
||
item: 'turnConfirm',
|
||
value: redisConfig.turnConfirm ? 'on' : 'off',
|
||
old: await redis.get('CHATGPT:CONFIRM'),
|
||
type: 'redis'
|
||
})
|
||
await redis.set('CHATGPT:CONFIRM', redisConfig.turnConfirm ? 'on' : 'off')
|
||
}
|
||
if (redisConfig.useMode != null) {
|
||
changeConfig.push({
|
||
item: 'useMode',
|
||
value: redisConfig.useMode,
|
||
old: await redis.get('CHATGPT:USE'),
|
||
type: 'redis'
|
||
})
|
||
await redis.set('CHATGPT:USE', redisConfig.useMode)
|
||
}
|
||
await this.reply(await makeForwardMsg(this.e, changeConfig.map(msg => `修改项:${msg.item}\n旧数据\n\n${msg.old}\n\n新数据\n ${msg.value}`)))
|
||
} catch (error) {
|
||
console.error(error)
|
||
await e.reply('配置文件错误')
|
||
}
|
||
}
|
||
} else {
|
||
await this.reply('未找到配置文件', false)
|
||
return false
|
||
}
|
||
|
||
this.finish('doImportConfig')
|
||
}
|
||
|
||
async switchSmartMode (e) {
|
||
if (e.msg.includes('开启')) {
|
||
if (Config.smartMode) {
|
||
await e.reply('已经开启了')
|
||
return
|
||
}
|
||
Config.smartMode = true
|
||
await e.reply('好的,已经打开智能模式,注意API额度哦。配合开启读取群聊上下文效果更佳!')
|
||
} else {
|
||
if (!Config.smartMode) {
|
||
await e.reply('已经是关闭得了')
|
||
return
|
||
}
|
||
Config.smartMode = false
|
||
await e.reply('好的,已经关闭智能模式')
|
||
}
|
||
}
|
||
}
|