Feat: 添加语音云转码功能,系统优化和故障修复 (#378)

* 修复后台API反代地址未能正确显示的问题

* 更新渲染页面配置

* 添加个人聊天模式配置

* 将用户数据获取改到common中

* 修复错误的渲染页面参数

* 修复bug

* 添加Live2D

* 修复渲染页面错误

* 修复渲染传入值

* 更新渲染

* 修复图表渲染bug

* 调整live2d模型大小

* 修复live2d无法关闭问题

* 修复错误的传值

* 修复ai命名

* 更新渲染

* 添加用户独立设定

* 更新渲染配置适配个人设置

* 修复合并导致的渲染文件异常删除

* 修复用户数据缺失问题

* 修复旧版本数据缺失问题

* 修复bing参数不存在问题,兼容miao的截图

* 修复受限token重试时不被排除的问题

* 修复个人模式下结束对话的模式错误

* 更新渲染页面,将预览版转为正式版

* 修复传统渲染无法调用截图功能的问题

* 文字模式也进行一次缓存

* 更新README

* Update README.md

* 更新渲染

* 更新渲染页面

* 添加版本信息

* 遗漏参数

* 丢失引用

* 补充路由

* 添加云转码功能

* 判断node-silk是否正常合成

* 云转码提示

* 修复图片渲染出错
This commit is contained in:
HalcyonAlcedo 2023-04-21 23:36:25 +08:00 committed by GitHub
parent 26b4534cfb
commit 55f060dc5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 169 additions and 99 deletions

View file

@ -44,13 +44,6 @@ if (Config.proxy) {
console.warn('未安装https-proxy-agent请在插件目录下执行pnpm add https-proxy-agent')
}
}
let useSilk = false
try {
await import('node-silk')
useSilk = true
} catch (e) {
useSilk = false
}
/**
* 每个对话保留的时长单个对话内ai是保留上下文的超时后销毁对话再次对话创建新的对话
* 单位
@ -73,6 +66,8 @@ const newFetch = (url, options = {}) => {
return fetch(url, mergedOptions)
}
// 后台地址
const viewHost = Config.viewHost ? `${Config.viewHost}/` : `http://127.0.0.1:${Config.serverPort || 3321}/`
export class chatgpt extends plugin {
constructor () {
let toggleMode = Config.toggleMode
@ -893,6 +888,8 @@ export class chatgpt extends plugin {
if (quote.imageLink) imgUrls.push(quote.imageLink)
}
if (useTTS) {
// 缓存数据
this.cacheContent(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls)
// 处理tts输入文本
let ttsResponse, ttsRegex
const regex = /^\/(.*)\/([gimuy]*)$/
@ -921,17 +918,18 @@ export class chatgpt extends plugin {
if (Config.ttsSpace && ttsResponse.length <= Config.ttsAutoFallbackThreshold) {
try {
let wav = await generateAudio(ttsResponse, speaker, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
if (useSilk) {
try {
let sendable = await uploadRecord(wav)
await e.reply(sendable)
if (sendable) {
await e.reply(sendable)
} else {
//如果合成失败尝试使用ffmpeg合成
await e.reply(segment.record(wav))
}
} catch (err) {
logger.error(err)
await e.reply(segment.record(wav))
}
} else {
await e.reply(segment.record(wav))
}
} catch (err) {
await this.reply('合成语音发生错误~')
}
@ -951,6 +949,7 @@ export class chatgpt extends plugin {
this.reply(`建议的回复:\n${chatMessage.suggestedResponses}`)
}
} else {
this.cacheContent(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls)
await this.reply(await convertFaces(response, Config.enableRobotAt, e), e.isGroup)
if (quotemessage.length > 0) {
this.reply(await makeForwardMsg(this.e, quotemessage.map(msg => `${msg.text} - ${msg.url}`)))
@ -1071,43 +1070,50 @@ export class chatgpt extends plugin {
return true
}
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
let cacheData = { file: '', cacheUrl: Config.cacheUrl, status: '' }
cacheData.file = randomString()
const cacheresOption = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: {
content: new Buffer.from(content).toString('base64'),
prompt: new Buffer.from(prompt).toString('base64'),
senderName: e.sender.nickname,
style: Config.toneStyle,
mood,
quote,
group: e.isGroup ? e.group.name : '',
suggest: suggest ? suggest.split('\n').filter(Boolean) : [],
images: imgUrls
},
model: use,
bing: use === 'bing',
entry: cacheData.file,
userImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${e.sender.user_id}`,
botImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${Bot.uin}`,
cacheHost: Config.serverHost,
qq: e.sender.user_id
})
}
const cacheres = await fetch(viewHost + 'cache', cacheresOption)
if (cacheres.ok) {
cacheData = Object.assign({}, cacheData, await cacheres.json())
} else {
cacheData.error = '渲染服务器出错!'
}
cacheData.status = cacheres.status
return cacheData
}
async renderImage (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
let cacheData = { file: '', cacheUrl: Config.cacheUrl }
let cacheData = await this.cacheContent(e, use, content, prompt, quote, mood, suggest, imgUrls)
const template = use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index'
if (!Config.oldview) {
cacheData.file = randomString()
const cacheresOption = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: {
content: new Buffer.from(content).toString('base64'),
prompt: new Buffer.from(prompt).toString('base64'),
senderName: e.sender.nickname,
style: Config.toneStyle,
mood,
quote,
group: e.isGroup ? e.group.name : '',
suggest: suggest ? suggest.split('\n').filter(Boolean) : [],
images: imgUrls
},
model: use,
bing: use === 'bing',
entry: cacheData.file,
userImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${e.sender.user_id}`,
botImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${Bot.uin}`,
cacheHost: Config.serverHost,
qq: e.sender.user_id
})
}
const viewHost = Config.viewHost ? `${Config.viewHost}/` : `http://127.0.0.1:${Config.serverPort || 3321}/`
const cacheres = await fetch(viewHost + 'cache', cacheresOption)
if (cacheres.ok) {
cacheData = Object.assign({}, cacheData, await cacheres.json())
}
if (cacheData.error || cacheres.status != 200) { await this.reply(`出现错误:${cacheData.error || 'server error ' + cacheres.status}`, true) } else { await e.reply(await renderUrl(e, viewHost + `page/${cacheData.file}?qr=${Config.showQRCode ? 'true' : 'false'}`, { retType: Config.quoteReply ? 'base64' : '', Viewport: { width: Config.chatViewWidth, height: parseInt(Config.chatViewWidth * 0.56) } }), e.isGroup && Config.quoteReply) }
if (cacheData.error || cacheData.status != 200) { await this.reply(`出现错误:${cacheData.error || 'server error ' + cacheData.status}`, true) } else { await e.reply(await renderUrl(e, viewHost + `page/${cacheData.file}?qr=${Config.showQRCode ? 'true' : 'false'}`, { retType: Config.quoteReply ? 'base64' : '', Viewport: { width: Config.chatViewWidth, height: parseInt(Config.chatViewWidth * 0.56) } }), e.isGroup && Config.quoteReply) }
} else {
if (Config.cacheEntry) cacheData.file = randomString()
const cacheresOption = {

View file

@ -1,7 +1,7 @@
import plugin from '../../../lib/plugins/plugin.js'
import { Config } from '../utils/config.js'
import { exec } from 'child_process'
import { checkPnpm, formatDuration, parseDuration, getPublicIP } from '../utils/common.js'
import { checkPnpm, formatDuration, parseDuration, getPublicIP, renderUrl } from '../utils/common.js'
import SydneyAIClient from '../utils/SydneyAIClient.js'
import { convertSpeaker, speakers } from '../utils/tts.js'
import md5 from 'md5'
@ -110,6 +110,10 @@ export class ChatgptManagement extends plugin {
reg: '^#chatgpt(强制)?更新$',
fnc: 'updateChatGPTPlugin'
},
{
reg: '^#chatgpt版本(信息)',
fnc: 'versionChatGPTPlugin'
},
{
reg: '^#chatgpt(本群)?(群\\d+)?闭嘴',
fnc: 'shutUp',
@ -828,6 +832,10 @@ export class ChatgptManagement extends plugin {
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 = {