+
+
+JetBrains for Open Source development license
+
+
+
diff --git a/apps/chat.js b/apps/chat.js
index 5105098..a7ee408 100644
--- a/apps/chat.js
+++ b/apps/chat.js
@@ -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
diff --git a/apps/draw.js b/apps/draw.js
index c4e00f6..10eed61 100644
--- a/apps/draw.js
+++ b/apps/draw.js
@@ -239,7 +239,7 @@ export class dalle extends plugin {
this.reply('请提供绘图prompt')
return false
}
-
+ this.reply('在画了,请稍等……')
let bingToken = ''
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
diff --git a/apps/entertainment.js b/apps/entertainment.js
index 3dec498..a9c51be 100644
--- a/apps/entertainment.js
+++ b/apps/entertainment.js
@@ -5,10 +5,13 @@ import { generateAudio } from '../utils/tts.js'
import fs from 'fs'
import { emojiRegex, googleRequestUrl } from '../utils/emoj/index.js'
import fetch from 'node-fetch'
-import { makeForwardMsg, mkdirs } from '../utils/common.js'
+import { getImageOcrText, getImg, makeForwardMsg, mkdirs } from '../utils/common.js'
import uploadRecord from '../utils/uploadRecord.js'
import { makeWordcloud } from '../utils/wordcloud/wordcloud.js'
import { translate, translateLangSupports } from '../utils/translate.js'
+import AzureTTS from '../utils/tts/microsoft-azure.js'
+import VoiceVoxTTS from '../utils/tts/voicevox.js'
+
let useSilk = false
try {
await import('node-silk')
@@ -17,7 +20,7 @@ try {
useSilk = false
}
export class Entertainment extends plugin {
- constructor(e) {
+ constructor (e) {
super({
name: 'ChatGPT-Plugin 娱乐小功能',
dsc: '让你的聊天更有趣!现已支持主动打招呼、表情合成、群聊词云统计、文本翻译与图片ocr小功能!',
@@ -42,6 +45,10 @@ export class Entertainment extends plugin {
reg: '^#?(今日词云|群友在聊什么)$',
fnc: 'wordcloud'
},
+ {
+ reg: '^#(|最新)词云(\\d{1,2}h{0,1}|)$',
+ fnc: 'wordcloud_latest'
+ },
{
reg: '^#((寄批踢|gpt|GPT)?翻.*|chatgpt翻译帮助)',
fnc: 'translate'
@@ -56,12 +63,13 @@ export class Entertainment extends plugin {
{
// 设置十分钟左右的浮动
cron: '0 ' + Math.ceil(Math.random() * 10) + ' 7-23/' + Config.helloInterval + ' * * ?',
- // cron: '0 ' + '*/' + Config.helloInterval + ' * * * ?',
+ // cron: '*/2 * * * *',
name: 'ChatGPT主动随机说话',
fnc: this.sendRandomMessage.bind(this)
}
]
}
+
async ocr (e) {
let replyMsg
let imgOcrText = await getImageOcrText(e)
@@ -72,7 +80,8 @@ export class Entertainment extends plugin {
replyMsg = await makeForwardMsg(e, imgOcrText, 'OCR结果')
await this.reply(replyMsg, e.isGroup)
}
- async translate(e) {
+
+ async translate (e) {
const translateLangLabels = translateLangSupports.map(item => item.label).join(',')
const translateLangLabelAbbrS = translateLangSupports.map(item => item.abbr).join(',')
if (e.msg.trim() === '#chatgpt翻译帮助') {
@@ -167,7 +176,8 @@ ${translateLangLabels}
await this.reply(result, e.isGroup)
return true
}
- async wordcloud(e) {
+
+ async wordcloud (e) {
if (e.isGroup) {
let groupId = e.group_id
let lock = await redis.get(`CHATGPT:WORDCLOUD:${groupId}`)
@@ -176,7 +186,7 @@ ${translateLangLabels}
return true
}
await e.reply('在统计啦,请稍等...')
- await redis.set(`CHATGPT:WORDCLOUD:${groupId}`, '1', {EX: 600})
+ await redis.set(`CHATGPT:WORDCLOUD:${groupId}`, '1', { EX: 600 })
try {
await makeWordcloud(e, e.group_id)
} catch (err) {
@@ -188,8 +198,39 @@ ${translateLangLabels}
await e.reply('请在群里发送此命令')
}
}
+ async wordcloud_latest(e) {
+ if (e.isGroup) {
+ let groupId = e.group_id
+ let lock = await redis.get(`CHATGPT:WORDCLOUD:${groupId}`)
+ if (lock) {
+ await e.reply('别着急,上次统计还没完呢')
+ return true
+ }
- async combineEmoj(e) {
+ const regExp = /词云(\d{0,2})(|h)/
+ const match = e.msg.trim().match(regExp)
+ const duration = !match[1] ? 12 : parseInt(match[1]) // default 12h
+
+ if(duration > 24) {
+ await e.reply('最多只能统计24小时内的记录哦')
+ return false
+ }
+ await e.reply('在统计啦,请稍等...')
+
+ await redis.set(`CHATGPT:WORDCLOUD:${groupId}`, '1', {EX: 600})
+ try {
+ await makeWordcloud(e, e.group_id, duration)
+ } catch (err) {
+ logger.error(err)
+ await e.reply(err)
+ }
+ await redis.del(`CHATGPT:WORDCLOUD:${groupId}`)
+ } else {
+ await e.reply('请在群里发送此命令')
+ }
+ }
+
+ async combineEmoj (e) {
let left = e.msg.codePointAt(0).toString(16).toLowerCase()
let right = e.msg.codePointAt(2).toString(16).toLowerCase()
if (left === right) {
@@ -237,7 +278,7 @@ ${translateLangLabels}
return true
}
- async sendMessage(e) {
+ async sendMessage (e) {
if (e.msg.match(/^#chatgpt打招呼帮助/) !== null) {
await this.reply('设置主动打招呼的群聊名单,群号之间以,隔开,参数之间空格隔开\n' +
'#chatgpt打招呼+群号:立即在指定群聊发起打招呼' +
@@ -268,23 +309,71 @@ ${translateLangLabels}
}
}
- async sendRandomMessage() {
+ async sendRandomMessage () {
if (Config.debug) {
logger.info('开始处理:ChatGPT随机打招呼。')
}
let toSend = Config.initiativeChatGroups || []
- for (let i = 0; i < toSend.length; i++) {
- if (!toSend[i]) {
+ for (const element of toSend) {
+ if (!element) {
continue
}
- let groupId = parseInt(toSend[i])
+ let groupId = parseInt(element)
if (Bot.getGroupList().get(groupId)) {
// 打招呼概率
if (Math.floor(Math.random() * 100) < Config.helloProbability) {
let message = await generateHello()
logger.info(`打招呼给群聊${groupId}:` + message)
if (Config.defaultUseTTS) {
- let audio = await generateAudio(message, Config.defaultTTSRole)
+ let audio
+ const [defaultVitsTTSRole, defaultAzureTTSRole, defaultVoxTTSRole] = [Config.defaultTTSRole, Config.azureTTSSpeaker, Config.voicevoxTTSSpeaker]
+ let ttsSupportKinds = []
+ if (Config.azureTTSKey) ttsSupportKinds.push(1)
+ if (Config.ttsSpace) ttsSupportKinds.push(2)
+ if (Config.voicevoxSpace) ttsSupportKinds.push(3)
+ if (!ttsSupportKinds.length) {
+ logger.warn('没有配置任何语音服务!')
+ return false
+ }
+ const randomIndex = Math.floor(Math.random() * ttsSupportKinds.length)
+ switch (ttsSupportKinds[randomIndex]) {
+ case 1 : {
+ const isEn = AzureTTS.supportConfigurations.find(config => config.code === defaultAzureTTSRole)?.language.includes('en')
+ if (isEn) {
+ message = (await translate(message, '英')).replace('\n', '')
+ }
+ audio = await AzureTTS.generateAudio(message, {
+ defaultAzureTTSRole
+ })
+ break
+ }
+ case 2 : {
+ if (Config.autoJapanese) {
+ try {
+ message = await translate(message, '日')
+ } catch (err) {
+ logger.error(err)
+ }
+ }
+ try {
+ audio = await generateAudio(message, defaultVitsTTSRole, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
+ } catch (err) {
+ logger.error(err)
+ }
+ break
+ }
+ case 3 : {
+ message = (await translate(message, '日')).replace('\n', '')
+ try {
+ audio = await VoiceVoxTTS.generateAudio(message, {
+ speaker: defaultVoxTTSRole
+ })
+ } catch (err) {
+ logger.error(err)
+ }
+ break
+ }
+ }
if (useSilk) {
await Bot.sendGroupMsg(groupId, await uploadRecord(audio))
} else {
@@ -302,7 +391,7 @@ ${translateLangLabels}
}
}
- async handleSentMessage(e) {
+ async handleSentMessage (e) {
const addReg = /^#chatgpt设置打招呼[::]?\s?(\S+)(?:\s+(\d+))?(?:\s+(\d+))?$/
const delReg = /^#chatgpt删除打招呼[::\s]?(\S+)/
const checkReg = /^#chatgpt查看打招呼$/
@@ -360,8 +449,8 @@ ${translateLangLabels}
return false
} else {
Config.initiativeChatGroups = Config.initiativeChatGroups
- .filter(group => group.trim() !== '')
- .concat(validGroups)
+ .filter(group => group.trim() !== '')
+ .concat(validGroups)
}
if (typeof paramArray[2] === 'undefined' && typeof paramArray[3] === 'undefined') {
replyMsg = `已更新打招呼设置:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%`
@@ -378,51 +467,3 @@ ${translateLangLabels}
return false
}
}
-export async function getImg (e) {
- // 取消息中的图片、at的头像、回复的图片,放入e.img
- if (e.at && !e.source) {
- e.img = [`https://q1.qlogo.cn/g?b=qq&s=0&nk=${e.at}`]
- }
- if (e.source) {
- let reply
- if (e.isGroup) {
- reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message
- } else {
- reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message
- }
- if (reply) {
- let i = []
- for (let val of reply) {
- if (val.type === 'image') {
- i.push(val.url)
- }
- }
- e.img = i
- }
- }
- return e.img
-}
-export async function getImageOcrText (e) {
- const img = await getImg(e)
- if (img) {
- try {
- let resultArr = []
- let eachImgRes = ''
- for (let i in img) {
- const imgOCR = await Bot.imageOcr(img[i])
- for (let text of imgOCR.wordslist) {
- eachImgRes += (`${text?.words} \n`)
- }
- if (eachImgRes) resultArr.push(eachImgRes)
- eachImgRes = ''
- }
- // logger.warn('resultArr', resultArr)
- return resultArr
- } catch (err) {
- return false
- // logger.error(err)
- }
- } else {
- return false
- }
-}
diff --git a/apps/help.js b/apps/help.js
index e45f50e..547e252 100644
--- a/apps/help.js
+++ b/apps/help.js
@@ -331,7 +331,11 @@ export class help extends plugin {
}
async help (e) {
- if (!Config.oldview) { await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/help/`, { Viewport: { width: 800, height: 600 } }) } else { await render(e, 'chatgpt-plugin', 'help/index', { helpData, version }) }
+ if (Config.newhelp && !Config.oldview) {
+ await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/help/`, { Viewport: { width: 800, height: 600 } })
+ } else {
+ await render(e, 'chatgpt-plugin', 'help/index', { helpData, version })
+ }
}
async newHelp (e) {
diff --git a/apps/management.js b/apps/management.js
index 3ed7145..a877976 100644
--- a/apps/management.js
+++ b/apps/management.js
@@ -4,11 +4,14 @@ import { exec } from 'child_process'
import {
checkPnpm,
formatDuration,
- parseDuration,
+ getAzureRoleList,
getPublicIP,
- renderUrl,
+ getUserReplySetting,
+ getVitsRoleList,
+ getVoicevoxRoleList,
makeForwardMsg,
- getDefaultReplySetting
+ parseDuration, processList,
+ renderUrl
} from '../utils/common.js'
import SydneyAIClient from '../utils/SydneyAIClient.js'
import { convertSpeaker, speakers as vitsRoleList } from '../utils/tts.js'
@@ -16,9 +19,11 @@ 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 VoiceVoxTTS, { supportConfigurations as voxRoleList } from '../utils/tts/voicevox.js'
import { supportConfigurations as azureRoleList } from '../utils/tts/microsoft-azure.js'
+
let isWhiteList = true
+let isSetGroup = true
export class ChatgptManagement extends plugin {
constructor (e) {
super({
@@ -130,17 +135,17 @@ export class ChatgptManagement extends plugin {
fnc: 'versionChatGPTPlugin'
},
{
- reg: '^#chatgpt(本群)?(群\\d+)?闭嘴',
+ reg: '^#chatgpt(本群)?(群\\d+)?(关闭|闭嘴|关机|休眠|下班)',
fnc: 'shutUp',
permission: 'master'
},
{
- reg: '^#chatgpt(本群)?(群\\d+)?(张嘴|开口|说话|上班)',
+ reg: '^#chatgpt(本群)?(群\\d+)?(开启|启动|激活|张嘴|开口|说话|上班)',
fnc: 'openMouth',
permission: 'master'
},
{
- reg: '^#chatgpt查看闭嘴',
+ reg: '^#chatgpt查看?(关闭|闭嘴|关机|休眠|下班|休眠)列表',
fnc: 'listShutUp',
permission: 'master'
},
@@ -180,7 +185,7 @@ export class ChatgptManagement extends plugin {
permission: 'master'
},
{
- reg: '^#chatgpt(打开|关闭|设置)?全局((图片模式|语音模式|(语音角色|角色语音|角色).*)|回复帮助)$',
+ reg: '^#chatgpt(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|((azure|vits|vox)?语音角色|角色语音|角色).*)|回复帮助)$',
fnc: 'setDefaultReplySetting',
permission: 'master'
},
@@ -202,18 +207,18 @@ export class ChatgptManagement extends plugin {
permission: 'master'
},
{
- reg: '^#chatgpt(设置|添加)群聊[白黑]名单$',
+ reg: '^#chatgpt(设置|添加)对话[白黑]名单$',
fnc: 'setList',
permission: 'master'
},
{
- reg: '^#chatgpt查看群聊[白黑]名单$',
- fnc: 'checkGroupList',
+ reg: '^#chatgpt(查看)?对话[白黑]名单(帮助)?$',
+ fnc: 'checkList',
permission: 'master'
},
{
- reg: '^#chatgpt(删除|移除)群聊[白黑]名单$',
- fnc: 'delGroupList',
+ reg: '^#chatgpt(删除|移除)对话[白黑]名单$',
+ fnc: 'delList',
permission: 'master'
},
{
@@ -244,48 +249,89 @@ export class ChatgptManagement extends plugin {
permission: 'master'
},
{
- reg: '^#chatgpt角色列表$',
+ reg: '^#(chatgpt)?(vits|azure|vox)?语音(角色列表|服务)$',
fnc: 'getTTSRoleList'
},
{
reg: '^#chatgpt设置后台(刷新|refresh)(t|T)oken$',
fnc: 'setOpenAIPlatformToken'
+ },
+ {
+ reg: '^#(chatgpt)?查看回复设置$',
+ fnc: 'viewUserSetting'
}
]
})
}
+ 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) {
- let userReplySetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
- userReplySetting = !userReplySetting
- ? getDefaultReplySetting()
- : JSON.parse(userReplySetting)
- if (!userReplySetting.useTTS) return
+ 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 (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 (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':
+ if (matchCommand[2] === '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}角色列表`)
+ 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)
+ let userReplySetting = await getUserReplySetting(this.e)
if (!userReplySetting.useTTS) {
let replyMsg
if (userReplySetting.usePicture) {
@@ -312,7 +358,6 @@ export class ChatgptManagement extends plugin {
}
async commandHelp (e) {
- if (!this.e.isMaster) { return this.reply('你没有权限') }
if (/^#(chatgpt)?指令表帮助$/.exec(e.msg.trim())) {
await this.reply('#chatgpt指令表: 查看本插件的所有指令\n' +
'#chatgpt(对话|管理|娱乐|绘图|人物设定|聊天记录)指令表: 查看对应功能分类的指令表\n' +
@@ -346,8 +391,8 @@ export class ChatgptManagement extends plugin {
commandSet.push({ name, dsc: plugin.dsc, rule })
}
}
- if (e.msg.includes('搜索')) {
- let cmd = e.msg.trim().match(/^#(chatgpt)?(对话|管理|娱乐|绘图|人物设定|聊天记录)?指令表(帮助|搜索(.+))?/)[4]
+ if (/^#(chatgpt)?指令表搜索(.+)/.test(e.msg.trim())) {
+ let cmd = e.msg.trim().match(/#(chatgpt)?指令表搜索(.+)/)[2]
if (!cmd) {
await this.reply('(⊙ˍ⊙)')
return 0
@@ -393,134 +438,124 @@ export class ChatgptManagement extends plugin {
return true
}
- /**
- * 对原始黑白名单进行去重和去除无效群号处理
- * @param whitelist
- * @param blacklist
- * @returns {Promise欢迎使用chatgpt-plugin插件
| Referral | Visitors | |
|---|---|---|
| 1,480 | 60% | |
| 5,480 | 70% | |
| 4,807 | 80% | |
| 3,678 | 75% | |
| 2,645 | 30% |
\n \n \n {{ statPercent }}%\n \n {{ statDescripiron }}\n
\n| \r\n 缓存地址\r\n | \r\n\r\n 用户\r\n | \r\n\r\n 群\r\n | \r\n\r\n 时间\r\n | \r\n
|---|---|---|---|
| \r\n {{ item.herf }} \r\n | \r\n\r\n {{ item.user }}\r\n | \r\n\r\n {{ item.group || '-' }}\r\n | \r\n\r\n {{ new Date(item.time).toLocaleString('zh', { hour12: false }).replaceAll('/', '-') }}\r\n | \r\n
\r\n\r\n\r\n 访问代码\r\n
\r\n\r\n {{ this.$route.params.code }}\r\n
\r\n
\r\n {{ item.text }}\r\n
\r\n\r\n 当前页面发生错误,请联系服务管理人员检查后台错误信息!\r\n
\r\n\r\n 页面代码:{{this.$route.query.code}}\r\n
\r\n\r\n {{this.$route.query.error}}\r\n
\r\n\r\n 欢迎使用chatgpt-plugin插件\r\n
\r\n| \n Referral\n | \n\n Visitors\n | \n\n |
|---|---|---|
| \n Facebook\n | \n\n 1,480\n | \n\n \n 60%\n \n \n \n \n \n \n | \n
| \n Facebook\n | \n\n 5,480\n | \n\n \n 70%\n \n \n \n \n \n \n | \n
| \n Google\n | \n\n 4,807\n | \n\n \n 80%\n \n \n \n \n \n \n | \n
| \n Instagram\n | \n\n 3,678\n | \n\n \n 75%\n \n \n \n \n \n \n | \n
| \n twitter\n | \n\n 2,645\n | \n\n \n 30%\n \n \n \n \n \n \n | \n
欢迎使用chatgpt-plugin插件
| Referral | Visitors | |
|---|---|---|
| 1,480 | 60% | |
| 5,480 | 70% | |
| 4,807 | 80% | |
| 3,678 | 75% | |
| 2,645 | 30% |
\n \n \n {{ statPercent }}%\n \n {{ statDescripiron }}\n
\n| \r\n 缓存地址\r\n | \r\n\r\n 用户\r\n | \r\n\r\n 群\r\n | \r\n\r\n 时间\r\n | \r\n
|---|---|---|---|
| \r\n {{ item.herf }} \r\n | \r\n\r\n {{ item.user }}\r\n | \r\n\r\n {{ item.group || '-' }}\r\n | \r\n\r\n {{ new Date(item.time).toLocaleString('zh', { hour12: false }).replaceAll('/', '-') }}\r\n | \r\n
\r\n\r\n\r\n 访问代码\r\n
\r\n\r\n {{ this.$route.params.code }}\r\n
\r\n
\r\n {{ item.text }}\r\n
\r\n\r\n 当前页面发生错误,请联系服务管理人员检查后台错误信息!\r\n
\r\n\r\n 页面代码:{{this.$route.query.code}}\r\n
\r\n\r\n {{this.$route.query.error}}\r\n
\r\n\r\n 欢迎使用chatgpt-plugin插件\r\n
\r\n| \n Referral\n | \n\n Visitors\n | \n\n |
|---|---|---|
| \n Facebook\n | \n\n 1,480\n | \n\n \n 60%\n \n \n \n \n \n \n | \n
| \n Facebook\n | \n\n 5,480\n | \n\n \n 70%\n \n \n \n \n \n \n | \n
| \n Google\n | \n\n 4,807\n | \n\n \n 80%\n \n \n \n \n \n \n | \n
| \n Instagram\n | \n\n 3,678\n | \n\n \n 75%\n \n \n \n \n \n \n | \n
| \n twitter\n | \n\n 2,645\n | \n\n \n 30%\n \n \n \n \n \n \n | \n
欢迎使用chatgpt-plugin插件
| Referral | Visitors | |
|---|---|---|
| 1,480 | 60% | |
| 5,480 | 70% | |
| 4,807 | 80% | |
| 3,678 | 75% | |
| 2,645 | 30% |
\n \n \n {{ statPercent }}%\n \n {{ statDescripiron }}\n
\n| \r\n 缓存地址\r\n | \r\n\r\n 用户\r\n | \r\n\r\n 群\r\n | \r\n\r\n 时间\r\n | \r\n
|---|---|---|---|
| \r\n {{ item.herf }} \r\n | \r\n\r\n {{ item.user }}\r\n | \r\n\r\n {{ item.group || '-' }}\r\n | \r\n\r\n {{ new Date(item.time).toLocaleString('zh', { hour12: false }).replaceAll('/', '-') }}\r\n | \r\n
\r\n\r\n\r\n 访问代码\r\n
\r\n\r\n {{ this.$route.params.code }}\r\n
\r\n
\r\n {{ item.text }}\r\n
\r\n\r\n 当前页面发生错误,请联系服务管理人员检查后台错误信息!\r\n
\r\n\r\n 页面代码:{{this.$route.query.code}}\r\n
\r\n\r\n {{this.$route.query.error}}\r\n
\r\n\r\n 欢迎使用chatgpt-plugin插件\r\n
\r\n| \n Referral\n | \n\n Visitors\n | \n\n |
|---|---|---|
| \n Facebook\n | \n\n 1,480\n | \n\n \n 60%\n \n \n \n \n \n \n | \n
| \n Facebook\n | \n\n 5,480\n | \n\n \n 70%\n \n \n \n \n \n \n | \n
| \n Google\n | \n\n 4,807\n | \n\n \n 80%\n \n \n \n \n \n \n | \n
| \n Instagram\n | \n\n 3,678\n | \n\n \n 75%\n \n \n \n \n \n \n | \n
| \n twitter\n | \n\n 2,645\n | \n\n \n 30%\n \n \n \n \n \n \n | \n
欢迎使用chatgpt-plugin插件
| Referral | Visitors | |
|---|---|---|
| 1,480 | 60% | |
| 5,480 | 70% | |
| 4,807 | 80% | |
| 3,678 | 75% | |
| 2,645 | 30% |
\n \n \n {{ statPercent }}%\n \n {{ statDescripiron }}\n
\n| \r\n 缓存地址\r\n | \r\n\r\n 用户\r\n | \r\n\r\n 群\r\n | \r\n\r\n 时间\r\n | \r\n
|---|---|---|---|
| \r\n {{ item.herf }} \r\n | \r\n\r\n {{ item.user }}\r\n | \r\n\r\n {{ item.group || '-' }}\r\n | \r\n\r\n {{ new Date(item.time).toLocaleString('zh', { hour12: false }).replaceAll('/', '-') }}\r\n | \r\n
\r\n\r\n\r\n 访问代码\r\n
\r\n\r\n {{ this.$route.params.code }}\r\n
\r\n
\r\n {{ item.text }}\r\n
\r\n\r\n 当前页面发生错误,请联系服务管理人员检查后台错误信息!\r\n
\r\n\r\n 页面代码:{{this.$route.query.code}}\r\n
\r\n\r\n {{this.$route.query.error}}\r\n
\r\n\r\n 欢迎使用chatgpt-plugin插件\r\n
\r\n| \n Referral\n | \n\n Visitors\n | \n\n |
|---|---|---|
| \n Facebook\n | \n\n 1,480\n | \n\n \n 60%\n \n \n \n \n \n \n | \n
| \n Facebook\n | \n\n 5,480\n | \n\n \n 70%\n \n \n \n \n \n \n | \n
| \n Google\n | \n\n 4,807\n | \n\n \n 80%\n \n \n \n \n \n \n | \n
| \n Instagram\n | \n\n 3,678\n | \n\n \n 75%\n \n \n \n \n \n \n | \n
| \n twitter\n | \n\n 2,645\n | \n\n \n 30%\n \n \n \n \n \n \n | \n