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 --------- Co-authored-by: Sean <1519059137@qq.com> Co-authored-by: ikechan8370 <geyinchibuaa@gmail.com>
1337 lines
47 KiB
JavaScript
1337 lines
47 KiB
JavaScript
import plugin from '../../../lib/plugins/plugin.js'
|
||
import { Config } from '../utils/config.js'
|
||
import { exec } from 'child_process'
|
||
import {
|
||
checkPnpm,
|
||
formatDuration,
|
||
parseDuration,
|
||
getPublicIP,
|
||
renderUrl,
|
||
makeForwardMsg,
|
||
getDefaultReplySetting
|
||
} 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 { supportConfigurations as voxRoleList } from '../utils/tts/voicevox.js'
|
||
import { supportConfigurations as azureRoleList } from '../utils/tts/microsoft-azure.js'
|
||
let isWhiteList = true
|
||
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: 'updateChatGPTPlugin'
|
||
},
|
||
{
|
||
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(打开|关闭|设置)?全局((图片模式|语音模式|(语音角色|角色语音|角色).*)|回复帮助)$',
|
||
fnc: 'setDefaultReplySetting',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
/** 命令正则匹配 */
|
||
reg: '^#(关闭|打开)群聊上下文$',
|
||
/** 执行方法 */
|
||
fnc: 'enableGroupContext',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(允许|禁止|打开|关闭|同意)私聊$',
|
||
fnc: 'enablePrivateChat',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(设置|添加)群聊[白黑]名单$',
|
||
fnc: 'setList',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt查看群聊[白黑]名单$',
|
||
fnc: 'checkGroupList',
|
||
permission: 'master'
|
||
},
|
||
{
|
||
reg: '^#chatgpt(删除|移除)群聊[白黑]名单$',
|
||
fnc: 'delGroupList',
|
||
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角色列表$',
|
||
fnc: 'getTTSRoleList'
|
||
},
|
||
{
|
||
reg: '^#chatgpt设置后台(刷新|refresh)(t|T)oken$',
|
||
fnc: 'setOpenAIPlatformToken'
|
||
}
|
||
]
|
||
})
|
||
}
|
||
|
||
async getTTSRoleList (e) {
|
||
let userReplySetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
|
||
userReplySetting = !userReplySetting
|
||
? getDefaultReplySetting()
|
||
: JSON.parse(userReplySetting)
|
||
if (!userReplySetting.useTTS) return
|
||
let ttsMode = Config.ttsMode
|
||
let roleList = []
|
||
if (ttsMode === 'vits-uma-genshin-honkai') {
|
||
const [firstHalf, secondHalf] = [vitsRoleList.slice(0, Math.floor(vitsRoleList.length / 2)).join('、'), vitsRoleList.slice(Math.floor(vitsRoleList.length / 2)).join('、')]
|
||
const [chunk1, chunk2] = [firstHalf.match(/[^、]+(?:、[^、]+){0,30}/g), secondHalf.match(/[^、]+(?:、[^、]+){0,30}/g)]
|
||
const list = [await makeForwardMsg(e, chunk1, `${Config.ttsMode}角色列表1`), await makeForwardMsg(e, chunk2, `${Config.ttsMode}角色列表2`)]
|
||
roleList = await makeForwardMsg(e, list, `${Config.ttsMode}角色列表`)
|
||
await this.reply(roleList)
|
||
return
|
||
} else if (ttsMode === 'voicevox') {
|
||
roleList = voxRoleList.map(item => item.name).join('、')
|
||
} else if (ttsMode === 'azure') {
|
||
roleList = azureRoleList.map(item => item.name).join('、')
|
||
}
|
||
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 redis.get(`CHATGPT:USER:${e.sender.user_id}`)
|
||
userReplySetting = !userReplySetting
|
||
? getDefaultReplySetting()
|
||
: JSON.parse(userReplySetting)
|
||
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 (!this.e.isMaster) { return this.reply('你没有权限') }
|
||
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 (e.msg.includes('搜索')) {
|
||
let cmd = e.msg.trim().match(/^#(chatgpt)?(对话|管理|娱乐|绘图|人物设定|聊天记录)?指令表(帮助|搜索(.+))?/)[4]
|
||
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
|
||
}
|
||
|
||
/**
|
||
* 对原始黑白名单进行去重和去除无效群号处理
|
||
* @param whitelist
|
||
* @param blacklist
|
||
* @returns {Promise<any[][]>}
|
||
*/
|
||
async processList (whitelist, blacklist) {
|
||
let groupWhitelist = Array.isArray(whitelist)
|
||
? whitelist
|
||
: String(whitelist).split(/[,,]/)
|
||
let groupBlacklist = !Array.isArray(blacklist)
|
||
? blacklist
|
||
: String(blacklist).split(/[,,]/)
|
||
groupWhitelist = Array.from(new Set(groupWhitelist)).filter(value => /^[1-9]\d{8,9}$/.test(value))
|
||
groupBlacklist = Array.from(new Set(groupBlacklist)).filter(value => /^[1-9]\d{8,9}$/.test(value))
|
||
return [groupWhitelist, groupBlacklist]
|
||
}
|
||
|
||
async setList (e) {
|
||
this.setContext('saveList')
|
||
isWhiteList = e.msg.includes('白')
|
||
const listType = isWhiteList ? '白名单' : '黑名单'
|
||
await this.reply(`请发送需要设置的群聊${listType},群号间使用,隔开`, e.isGroup)
|
||
return false
|
||
}
|
||
|
||
async saveList (e) {
|
||
if (!this.e.msg) return
|
||
const listType = isWhiteList ? '白名单' : '黑名单'
|
||
const inputMatch = this.e.msg.match(/\d+/g)
|
||
let [groupWhitelist, groupBlacklist] = await this.processList(Config.groupWhitelist, Config.groupBlacklist)
|
||
let inputList = Array.isArray(inputMatch) ? this.e.msg.match(/\d+/g).filter(value => /^[1-9]\d{8,9}$/.test(value)) : []
|
||
if (!inputList.length) {
|
||
await this.reply('无效输入,请在检查群号是否正确后重新输入', e.isGroup)
|
||
return false
|
||
}
|
||
inputList = Array.from(new Set(inputList))
|
||
let whitelist = []
|
||
let blacklist = []
|
||
for (const element of inputList) {
|
||
if (listType === '白名单') {
|
||
groupWhitelist = groupWhitelist.filter(item => item !== element)
|
||
whitelist.push(element)
|
||
} else {
|
||
groupBlacklist = groupBlacklist.filter(item => item !== element)
|
||
blacklist.push(element)
|
||
}
|
||
}
|
||
if (!(whitelist.length || blacklist.length)) {
|
||
await this.reply('无效输入,请在检查群号是否正确或重复添加后重新输入', e.isGroup)
|
||
return false
|
||
} else {
|
||
if (listType === '白名单') {
|
||
Config.groupWhitelist = groupWhitelist
|
||
.filter(group => group !== '')
|
||
.concat(whitelist)
|
||
} else {
|
||
Config.groupBlacklist = groupBlacklist
|
||
.filter(group => group !== '')
|
||
.concat(blacklist)
|
||
}
|
||
}
|
||
let replyMsg = `群聊${listType}已更新,可通过\n'#chatgpt查看群聊${listType}'查看最新名单\n'#chatgpt移除群聊${listType}'管理名单`
|
||
if (e.isPrivate) {
|
||
replyMsg += `\n当前群聊${listType}为:${listType === '白名单' ? Config.groupWhitelist : Config.groupBlacklist}`
|
||
}
|
||
await this.reply(replyMsg, e.isGroup)
|
||
this.finish('saveList')
|
||
}
|
||
|
||
async checkGroupList (e) {
|
||
isWhiteList = e.msg.includes('白')
|
||
const list = isWhiteList ? Config.groupWhitelist : Config.groupBlacklist
|
||
const listType = isWhiteList ? '白名单' : '黑名单'
|
||
const replyMsg = list.length ? `当前群聊${listType}为:${list}` : `当前没有设置任何群聊${listType}`
|
||
await this.reply(replyMsg, e.isGroup)
|
||
return false
|
||
}
|
||
|
||
async delGroupList (e) {
|
||
isWhiteList = e.msg.includes('白')
|
||
const listType = isWhiteList ? '白名单' : '黑名单'
|
||
let replyMsg = ''
|
||
if (Config.groupWhitelist.length === 0 && Config.groupBlacklist.length === 0) {
|
||
replyMsg = `当前群聊(白|黑)名单为空,请先添加${listType}吧~`
|
||
} else if ((listType === '白名单' && !Config.groupWhitelist.length) || (listType === '黑名单' && !Config.groupBlacklist.length)) {
|
||
replyMsg = `当前群聊${listType}为空,请先添加吧~`
|
||
}
|
||
if (replyMsg) {
|
||
await this.reply(replyMsg, e.isGroup)
|
||
return false
|
||
}
|
||
this.setContext('confirmDelGroup')
|
||
await this.reply(`请发送需要删除的群聊${listType},群号间使用,隔开。输入‘全部删除’清空${listType}。`, e.isGroup)
|
||
return false
|
||
}
|
||
|
||
async confirmDelGroup (e) {
|
||
if (!this.e.msg) return
|
||
const isAllDeleted = this.e.msg.trim() === '全部删除'
|
||
const groupNumRegex = /^[1-9]\d{8,9}$/
|
||
const inputMatch = this.e.msg.match(/\d+/g)
|
||
const validGroups = Array.isArray(inputMatch) ? inputMatch.filter(groupNum => groupNumRegex.test(groupNum)) : []
|
||
let [groupWhitelist, groupBlacklist] = await this.processList(Config.groupWhitelist, Config.groupBlacklist)
|
||
if (isAllDeleted) {
|
||
Config.groupWhitelist = isWhiteList ? [] : groupWhitelist
|
||
Config.groupBlacklist = !isWhiteList ? [] : groupBlacklist
|
||
} else {
|
||
if (!validGroups.length) {
|
||
await this.reply('无效输入,请在检查群号是否正确后重新输入', e.isGroup)
|
||
return false
|
||
} else {
|
||
for (const element of validGroups) {
|
||
if (isWhiteList) {
|
||
Config.groupWhitelist = groupWhitelist.filter(item => item !== element)
|
||
} else {
|
||
Config.groupBlacklist = groupBlacklist.filter(item => item !== element)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const listType = isWhiteList ? '白名单' : '黑名单'
|
||
let replyMsg = `群聊${listType}已更新,可通过'#chatgpt查看群聊${listType}'命令查看最新名单`
|
||
if (e.isPrivate) {
|
||
replyMsg += `\n当前群聊${listType}为:${listType === '白名单' ? Config.groupWhitelist : Config.groupBlacklist}`
|
||
}
|
||
await this.reply(replyMsg, e.isGroup)
|
||
this.finish('confirmDelGroup')
|
||
}
|
||
|
||
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(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|(语音角色|角色语音|角色).*)|回复帮助)/
|
||
const matchCommand = e.msg.match(reg)
|
||
const settingType = matchCommand[2]
|
||
let replyMsg = ''
|
||
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 (!Config.ttsSpace) {
|
||
replyMsg = '您没有配置VITS API,请前往锅巴面板进行配置'
|
||
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设置全局(语音角色|角色语音|角色)+角色名称(留空则为随机)'
|
||
break
|
||
default:
|
||
if (!Config.ttsSpace) {
|
||
replyMsg = '您没有配置VITS API,请前往锅巴面板进行配置'
|
||
break
|
||
}
|
||
if (settingType.match(/(语音角色|角色语音|角色)/)) {
|
||
const speaker = matchCommand[2].replace(/(语音角色|角色语音|角色)/, '').trim() || ''
|
||
if (!speaker.length) {
|
||
replyMsg = 'ChatGpt将随机挑选角色回复'
|
||
Config.defaultTTSRole = ''
|
||
} else {
|
||
const ttsRole = convertSpeaker(speaker)
|
||
if (vitsRoleList.includes(ttsRole)) {
|
||
Config.defaultTTSRole = ttsRole
|
||
replyMsg = `ChatGPT默认语音角色已被设置为“${ttsRole}”`
|
||
} else {
|
||
replyMsg = `抱歉,我还不认识“${ttsRole}”这个语音角色`
|
||
}
|
||
}
|
||
} 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
|
||
}
|
||
|
||
// modified from miao-plugin
|
||
async updateChatGPTPlugin (e) {
|
||
let timer
|
||
if (!await this.checkAuth(e)) {
|
||
return true
|
||
}
|
||
let isForce = e.msg.includes('强制')
|
||
let command = 'git pull'
|
||
if (isForce) {
|
||
command = 'git checkout . && git pull'
|
||
e.reply('正在执行强制更新操作,请稍等')
|
||
} else {
|
||
e.reply('正在执行更新操作,请稍等')
|
||
}
|
||
const _path = process.cwd()
|
||
exec(command, { cwd: `${_path}/plugins/chatgpt-plugin/` }, async function (error, stdout, stderr) {
|
||
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(stdout)) {
|
||
e.reply('目前已经是最新版ChatGPT了~')
|
||
return true
|
||
}
|
||
if (error) {
|
||
e.reply('ChatGPT更新失败!\nError code: ' + error.code + '\n' + error.stack + '\n 请稍后重试。')
|
||
return true
|
||
}
|
||
e.reply('ChatGPT更新成功,正在尝试重新启动Yunzai以应用更新...')
|
||
e.reply('更新日志:\n' + stdout)
|
||
timer && clearTimeout(timer)
|
||
|
||
let data = JSON.stringify({
|
||
isGroup: !!e.isGroup,
|
||
id: e.isGroup ? e.group_id : e.user_id,
|
||
time: new Date().getTime()
|
||
})
|
||
await redis.set('Yz:restart', data, { EX: 120 })
|
||
let npm = await checkPnpm()
|
||
timer = setTimeout(function () {
|
||
let command = `${npm} start`
|
||
if (process.argv[1].includes('pm2')) {
|
||
command = `${npm} run restart`
|
||
}
|
||
exec(command, function (error, stdout, stderr) {
|
||
if (error) {
|
||
e.reply('自动重启失败,请手动重启以应用新版ChatGPT。\nError code: ' + error.code + '\n' + error.stack + '\n')
|
||
Bot.logger.error(`重启失败\n${error.stack}`)
|
||
return true
|
||
} else if (stdout) {
|
||
Bot.logger.mark('重启成功,运行已转为后台,查看日志请用命令:npm run log')
|
||
Bot.logger.mark('停止后台运行命令:npm stop')
|
||
process.exit()
|
||
}
|
||
})
|
||
}, 1000)
|
||
})
|
||
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模式和浏览器模式如何选择?
|
||
|
||
// eslint-disable-next-line no-irregular-whitespace
|
||
API模式会调用OpenAI官方提供的gpt-3.5-turbo API,只需要提供API Key。一般情况下,该种方式响应速度更快,不会像chatGPT官网一样总出现不可用的现象,但注意gpt-3.5-turbo的API调用是收费的,新用户有18美元试用金可用于支付,价格为$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设置PoeToken
|
||
|
||
您可以使用‘#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')
|
||
}
|
||
}
|