Merge branch 'v2' into v2

This commit is contained in:
ifeif 2023-06-06 14:34:59 +08:00 committed by GitHub
commit be91e25415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1029 additions and 632 deletions

View file

@ -7,11 +7,10 @@ import { ChatGPTAPI } from 'chatgpt'
import { BingAIClient } from '@waylaidwanderer/chatgpt-api'
import SydneyAIClient from '../utils/SydneyAIClient.js'
import { PoeClient } from '../utils/poe/index.js'
import AzureTTS from '../utils/tts/microsoft-azure.js'
import AzureTTS, { supportConfigurations } from '../utils/tts/microsoft-azure.js'
import VoiceVoxTTS from '../utils/tts/voicevox.js'
import { translate } from '../utils/translate.js'
import fs from 'fs'
import { getImg, getImageOcrText } from './entertainment.js'
import {
render, renderUrl,
getMessageById,
@ -21,7 +20,7 @@ import {
completeJSON,
isImage,
getUserData,
getDefaultReplySetting, isCN, getMasterQQ
getDefaultReplySetting, isCN, getMasterQQ, getUserReplySetting, getImageOcrText, getImg, processList
} from '../utils/common.js'
import { ChatGPTPuppeteer } from '../utils/browser.js'
import { KeyvFile } from 'keyv-file'
@ -544,12 +543,7 @@ export class chatgpt extends plugin {
}
async switch2Text (e) {
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userSetting) {
userSetting = getDefaultReplySetting()
} else {
userSetting = JSON.parse(userSetting)
}
let userSetting = await getUserReplySetting(this.e)
userSetting.usePicture = false
userSetting.useTTS = false
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
@ -577,12 +571,7 @@ export class chatgpt extends plugin {
}
break
}
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userSetting) {
userSetting = getDefaultReplySetting()
} else {
userSetting = JSON.parse(userSetting)
}
let userSetting = await getUserReplySetting(this.e)
userSetting.useTTS = true
userSetting.usePicture = false
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
@ -629,37 +618,35 @@ export class chatgpt extends plugin {
let speaker = e.msg.replace(regex, '').trim() || '随机'
switch (Config.ttsMode) {
case 'vits-uma-genshin-honkai': {
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userSetting) {
userSetting = getDefaultReplySetting()
} else {
userSetting = JSON.parse(userSetting)
}
let userSetting = await getUserReplySetting(this.e)
userSetting.ttsRole = convertSpeaker(speaker)
if (speakers.indexOf(userSetting.ttsRole) >= 0) {
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
await this.reply(`您的默认语音角色已被设置为”${userSetting.ttsRole}`)
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "${userSetting.ttsRole}" `)
} else if (speaker === '随机') {
userSetting.ttsRole = '随机'
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `)
} else {
await this.reply(`抱歉,"${userSetting.ttsRole}"我还不认识呢`)
}
break
}
case 'azure': {
let userSetting = await getUserReplySetting(this.e)
let chosen = AzureTTS.supportConfigurations.filter(s => s.name === speaker)
if (chosen.length === 0) {
if (speaker === '随机') {
userSetting.ttsRoleAzure = '随机'
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `)
} else if (chosen.length === 0) {
await this.reply(`抱歉,没有"${speaker}"这个角色目前azure模式下支持的角色有${AzureTTS.supportConfigurations.map(item => item.name).join('、')}`)
} else {
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userSetting) {
userSetting = getDefaultReplySetting()
} else {
userSetting = JSON.parse(userSetting)
}
userSetting.ttsRoleAzure = chosen[0].code
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
// Config.azureTTSSpeaker = chosen[0].code
const supportEmotion = AzureTTS.supportConfigurations.find(config => config.name === speaker)?.emotion
await this.reply(`您的默认语音角色已被设置为 ${speaker}-${chosen[0].gender}-${chosen[0].languageDetail} ${supportEmotion && Config.azureTTSEmotion ? ',此角色支持多情绪配置,建议重新使用设定并结束对话以获得最佳体验!' : ''}`)
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 ${speaker}-${chosen[0].gender}-${chosen[0].languageDetail} ${supportEmotion && Config.azureTTSEmotion ? ',此角色支持多情绪配置,建议重新使用设定并结束对话以获得最佳体验!' : ''}`)
}
break
}
@ -671,6 +658,13 @@ export class chatgpt extends plugin {
speaker = match[1]
style = match[2]
}
let userSetting = await getUserReplySetting(e)
if (speaker === '随机') {
userSetting.ttsRoleVoiceVox = '随机'
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `)
break
}
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('、')}`)
@ -680,15 +674,9 @@ export class chatgpt extends plugin {
await this.reply(`抱歉,"${speaker}"这个角色没有"${style}"这个风格,目前支持的风格有${chosen[0].styles.map(item => item.name).join('、')}`)
break
}
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userSetting) {
userSetting = getDefaultReplySetting()
} else {
userSetting = JSON.parse(userSetting)
}
userSetting.ttsRoleVoiceVox = chosen[0].name + (style ? `-${style}` : '')
await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting))
await this.reply(`您的默认语音角色已被设置为”${userSetting.ttsRoleVoiceVox}`)
await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "${userSetting.ttsRoleVoiceVox}" `)
break
}
}
@ -698,23 +686,6 @@ export class chatgpt extends plugin {
* #chatgpt
*/
async chatgpt (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
// await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (e.isGroup) {
let cm = new ChatgptManagement()
let [groupWhitelist, groupBlacklist] = await cm.processList(Config.groupWhitelist, Config.groupBlacklist)
// logger.info('groupWhitelist:', Config.groupWhitelist, 'groupBlacklist', Config.groupBlacklist)
const whitelist = groupWhitelist.filter(group => group.trim())
if (whitelist.length > 0 && !whitelist.includes(e.group_id.toString())) {
return false
}
const blacklist = groupBlacklist.filter(group => group.trim())
if (blacklist.length > 0 && blacklist.includes(e.group_id.toString())) {
return false
}
}
let prompt
if (this.toggleMode === 'at') {
if (!e.raw_message || e.msg?.startsWith('#')) {
@ -781,15 +752,24 @@ export class chatgpt extends plugin {
}
async abstractChat (e, prompt, use) {
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (userSetting) {
userSetting = JSON.parse(userSetting)
if (Object.keys(userSetting).indexOf('useTTS') < 0) {
userSetting.useTTS = Config.defaultUseTTS
}
} else {
userSetting = getDefaultReplySetting()
// 关闭私聊通道后不回复
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
return false
}
// 黑白名单过滤对话
let [whitelist, blacklist] = processList(Config.whitelist, Config.blacklist)
if (whitelist.length > 0) {
if (e.isGroup && !whitelist.includes(e.group_id.toString())) return false
const list = whitelist.filter(elem => elem.startsWith('^')).map(elem => elem.slice(1))
if (!list.includes(e.sender.user_id.toString())) return false
}
if (blacklist.length > 0) {
if (e.isGroup && blacklist.includes(e.group_id.toString())) return false
const list = blacklist.filter(elem => elem.startsWith('^')).map(elem => elem.slice(1))
if (list.includes(e.sender.user_id.toString())) return false
}
let userSetting = await getUserReplySetting(this.e)
let useTTS = !!userSetting.useTTS
let speaker
if (Config.ttsMode === 'vits-uma-genshin-honkai') {
@ -869,10 +849,7 @@ export class chatgpt extends plugin {
}
}
const emotionFlag = await redis.get(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`)
let userReplySetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
userReplySetting = !userReplySetting
? getDefaultReplySetting()
: JSON.parse(userReplySetting)
let userReplySetting = await getUserReplySetting(this.e)
// 图片模式就不管了,降低抱歉概率
if (Config.ttsMode === 'azure' && Config.enhanceAzureTTSEmotion && userReplySetting.useTTS === true && await AzureTTS.getEmotionPrompt(e)) {
switch (emotionFlag) {
@ -1163,10 +1140,23 @@ export class chatgpt extends plugin {
await this.reply('合成语音发生错误~')
}
} else if (Config.ttsMode === 'azure' && Config.azureTTSKey) {
const ttsRoleAzure = userReplySetting.ttsRoleAzure
const isEn = AzureTTS.supportConfigurations.find(config => config.code === ttsRoleAzure)?.language.includes('en')
if (isEn) {
ttsResponse = (await translate(ttsResponse, '英')).replace('\n', '')
if (speaker !== '随机') {
let languagePrefix = AzureTTS.supportConfigurations.find(config => config.code === speaker).languageDetail.charAt(0)
languagePrefix = languagePrefix.startsWith('E') ? '英' : languagePrefix
ttsResponse = (await translate(ttsResponse, languagePrefix)).replace('\n', '')
} else {
let role, languagePrefix
role = AzureTTS.supportConfigurations[Math.floor(Math.random() * supportConfigurations.length)]
speaker = role.code
languagePrefix = role.languageDetail.charAt(0).startsWith('E') ? '英' : role.languageDetail.charAt(0)
ttsResponse = (await translate(ttsResponse, languagePrefix)).replace('\n', '')
if (role?.emotion) {
const keys = Object.keys(role.emotion)
emotion = keys[Math.floor(Math.random() * keys.length)]
}
logger.info('using speaker: ' + speaker)
logger.info('using language: ' + languagePrefix)
logger.info('using emotion: ' + emotion)
}
let ssml = AzureTTS.generateSsml(ttsResponse, {
speaker,
@ -1177,6 +1167,7 @@ export class chatgpt extends plugin {
speaker
}, await ssml)
} else if (Config.ttsMode === 'voicevox' && Config.voicevoxSpace) {
ttsResponse = (await translate(ttsResponse, '日')).replace('\n', '')
wav = await VoiceVoxTTS.generateAudio(ttsResponse, {
speaker
})
@ -1256,10 +1247,6 @@ export class chatgpt extends plugin {
}
async chatgpt1 (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (!Config.allowOtherMode) {
return false
}
@ -1279,10 +1266,6 @@ export class chatgpt extends plugin {
}
async chatgpt3 (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (!Config.allowOtherMode) {
return false
}
@ -1321,10 +1304,6 @@ export class chatgpt extends plugin {
}
async bing (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (!Config.allowOtherMode) {
return false
}
@ -1344,10 +1323,6 @@ export class chatgpt extends plugin {
}
async claude (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
// await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (!Config.allowOtherMode) {
return false
}
@ -1365,11 +1340,8 @@ export class chatgpt extends plugin {
await this.abstractChat(e, prompt, 'claude')
return true
}
async xh (e) {
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
// await this.reply('ChatGpt私聊通道已关闭。')
return false
}
if (!Config.allowOtherMode) {
return false
}
@ -1650,7 +1622,7 @@ export class chatgpt extends plugin {
// 如果token曾经有异常则清除异常
let Tokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const TokenIndex = Tokens.findIndex(element => element.Token === abtrs.bingToken)
if (Tokens[TokenIndex].exception) {
if (TokenIndex > 0 && Tokens[TokenIndex].exception) {
delete Tokens[TokenIndex].exception
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(Tokens))
}
@ -1672,21 +1644,22 @@ export class chatgpt extends plugin {
// 不减次数
} else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) {
// token过期了
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
// 可能是微软抽风,给三次机会
if (bingTokens[badBingToken].exception) {
if (bingTokens[badBingToken].exception <= 3) {
bingTokens[badBingToken].exception += 1
} else {
bingTokens[badBingToken].exception = 0
bingTokens[badBingToken].State = '过期'
}
} else {
bingTokens[badBingToken].exception = 1
}
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
logger.warn(`token${bingToken}已过期`)
// let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
// const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
// // 可能是微软抽风,给三次机会
// if (bingTokens[badBingToken].exception) {
// if (bingTokens[badBingToken].exception <= 3) {
// bingTokens[badBingToken].exception += 1
// } else {
// bingTokens[badBingToken].exception = 0
// bingTokens[badBingToken].State = '过期'
// }
// } else {
// bingTokens[badBingToken].exception = 1
// }
// await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
logger.warn(`token${bingToken}疑似不存在或已过期,再试试`)
retry = retry - 0.1
} else {
retry--
errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message
@ -1778,9 +1751,11 @@ export class chatgpt extends plugin {
if (Config.slackClaudeEnableGlobalPreset && (useCast?.slack || Config.slackClaudeGlobalPreset)) {
// 先发送设定
let prompt = (useCast?.slack || Config.slackClaudeGlobalPreset)
let emotion = await AzureTTS.getEmotionPrompt(e)
if (emotion) {
prompt = prompt + '\n' + emotion
}
await client.sendMessage(prompt, e)
// 处理可能由情绪参数导致的设定超限问题
await client.sendMessage(await AzureTTS.getEmotionPrompt(e), e)
logger.info('claudeFirst:', prompt)
}
}
@ -1829,10 +1804,10 @@ export class chatgpt extends plugin {
timeoutMs: 120000
// systemMessage: promptPrefix
}
if (Math.floor(Math.random() * 100) < 5) {
// 小概率再次发送系统消息
option.systemMessage = promptPrefix
}
// if (Math.floor(Math.random() * 100) < 5) {
// // 小概率再次发送系统消息
// option.systemMessage = promptPrefix
// }
if (conversation) {
option = Object.assign(option, conversation)
}
@ -2098,7 +2073,11 @@ export class chatgpt extends plugin {
async function getAvailableBingToken (conversation, throttled = []) {
let allThrottled = false
if (!await redis.get('CHATGPT:BING_TOKENS')) {
throw new Error('未绑定Bing Cookie请使用#chatgpt设置必应token命令绑定Bing Cookie')
return {
bingToken: null,
allThrottled
}
// throw new Error('未绑定Bing Cookie请使用#chatgpt设置必应token命令绑定Bing Cookie')
}
let bingToken = ''
@ -2128,7 +2107,11 @@ async function getAvailableBingToken (conversation, throttled = []) {
})
bingToken = minElement.Token
} else {
throw new Error('全部Token均已失效暂时无法使用')
// throw new Error('全部Token均已失效暂时无法使用')
return {
bingToken: null,
allThrottled
}
}
if (Config.toneStyle != 'Sydney' && Config.toneStyle != 'Custom') {
// bing 下需要保证同一对话使用同一账号的token