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

@ -1,4 +1,4 @@
import fetch from 'node-fetch'
import fetch, { FormData } from 'node-fetch'
import { makeForwardMsg } from './common.js'
import { Config } from './config.js'
@ -22,8 +22,8 @@ export default class BingDrawClient {
async getImages (prompt, e) {
let urlEncodedPrompt = encodeURIComponent(prompt)
let url = `${this.opts.baseUrl}/images/create?q=${urlEncodedPrompt}&rt=4&FORM=GENCRE`
let d = Math.ceil(Math.random() * 255)
let randomIp = '141.11.138.' + d
// let d = Math.ceil(Math.random() * 255)
// let randomIp = '141.11.138.' + d
let headers = {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'en-US,en;q=0.9',
@ -31,31 +31,58 @@ export default class BingDrawClient {
'content-type': 'application/x-www-form-urlencoded',
referrer: 'https://www.bing.com/images/create/',
origin: 'https://www.bing.com',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50',
cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
'x-forwarded-for': randomIp
// 'x-forwarded-for': randomIp,
Dnt: '1',
'sec-ch-ua': '"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
'sec-ch-ua-arch': '"x86"',
'sec-ch-ua-bitness': '"64"',
'sec-ch-ua-full-version': '"113.0.5672.126"',
'sec-ch-ua-full-version-list': '"Google Chrome";v="113.0.5672.126", "Chromium";v="113.0.5672.126", "Not-A.Brand";v="24.0.0.0"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-model': '',
'sec-ch-ua-platform': '"macOS"',
'sec-ch-ua-platform-version': '"13.1.0"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'Referrer-Policy': 'origin-when-cross-origin',
'x-edge-shopping-flag': '1'
}
// headers['x-forwarded-for'] = '141.11.138.30'
let body = new FormData()
body.append('q', prompt)
body.append('qs', 'ds')
let fetchOptions = {
method: 'POST',
headers,
redirect: 'manual'
headers
}
if (Config.proxy) {
fetchOptions.agent = proxy(Config.proxy)
}
let response = await fetch(url, fetchOptions)
let res = await response.text()
if (res.toLowerCase().indexOf('this prompt has been blocked') > -1) {
throw new Error('Your prompt has been blocked by Bing. Try to change any bad words and try again.')
}
if (response.status !== 302) {
url = `${this.opts.baseUrl}/images/create?q=${urlEncodedPrompt}&rt=3&FORM=GENCRE`
let response3 = await fetch(url, fetchOptions)
if (response3.status !== 302) {
throw new Error('绘图失败请检查Bing token和代理/反代配置')
let success = false
let retry = 5
let response
while (!success && retry >= 0) {
response = await fetch(url, Object.assign(fetchOptions, { body, redirect: 'manual', method: 'POST' }))
let res = await response.text()
if (res.toLowerCase().indexOf('this prompt has been blocked') > -1) {
throw new Error('Your prompt has been blocked by Bing. Try to change any bad words and try again.')
}
response = response3
if (response.status !== 302) {
url = `${this.opts.baseUrl}/images/create?q=${urlEncodedPrompt}&rt=3&FORM=GENCRE`
response = await fetch(url, Object.assign(fetchOptions, { body, redirect: 'manual', method: 'POST' }))
}
if (response.status === 302) {
success = true
break
} else {
retry--
}
}
if (!success) {
throw new Error('绘图失败请检查Bing token和代理/反代配置')
}
let redirectUrl = response.headers.get('Location').replace('&nfy=1', '')
let requestId = redirectUrl.split('id=')[1]
@ -66,26 +93,23 @@ export default class BingDrawClient {
let pollingUrl = `${this.opts.baseUrl}/images/create/async/results/${requestId}?q=${urlEncodedPrompt}`
logger.info({ pollingUrl })
logger.info('waiting for bing draw results...')
let timeoutTimes = 50
let timeoutTimes = 30
let found = false
let timer = setInterval(async () => {
if (found) {
return
}
let r = await fetch(pollingUrl, {
headers
})
let r = await fetch(pollingUrl, fetchOptions)
let rText = await r.text()
if (rText) {
if (r.status === 200 && rText) {
// logger.info(rText)
logger.info('got bing draw results!')
found = true
let regex = /src="([^"]+)"/g
let imageLinks = rText.match(regex)
if (!imageLinks || imageLinks.length === 0) {
await e.reply('绘图失败no images', true)
logger.error(rText)
throw new Error('no images')
// 很可能是微软内部error重试即可
return
}
imageLinks = imageLinks.map(link => link.split('?w=')[0]).map(link => link.replace('src="', ''))
imageLinks = [...new Set(imageLinks)]
@ -108,11 +132,12 @@ export default class BingDrawClient {
if (timeoutTimes === 0) {
await e.reply('绘图超时', true)
clearInterval(timer)
timer = null
} else {
logger.info('still waiting for bing draw results... times left: ' + timeoutTimes)
timeoutTimes--
}
}
}, 1500)
}, 2000)
}
}

View file

@ -4,7 +4,7 @@ import fetch, {
Response
} from 'node-fetch'
import crypto from 'crypto'
import WebSocket from 'ws'
import HttpsProxyAgent from 'https-proxy-agent'
import { Config, pureSydneyInstruction } from './config.js'
import { formatDate, getMasterQQ, isCN, getUserData } from './common.js'
@ -29,15 +29,16 @@ if (Config.proxy) {
console.warn('未安装https-proxy-agent请在插件目录下执行pnpm add https-proxy-agent')
}
}
async function getWebSocket () {
let WebSocket
try {
WebSocket = (await import('ws')).default
} catch (error) {
throw new Error('ws依赖未安装请使用pnpm install ws安装')
}
return WebSocket
}
// async function getWebSocket () {
// let WebSocket
// try {
// WebSocket = (await import('ws')).default
// } catch (error) {
// throw new Error('ws依赖未安装请使用pnpm install ws安装')
// }
// return WebSocket
// }
async function getKeyv () {
let Keyv
try {
@ -58,7 +59,7 @@ export default class SydneyAIClient {
constructor (opts) {
this.opts = {
...opts,
host: opts.host || Config.sydneyReverseProxy || 'https://www.bing.com'
host: opts.host || Config.sydneyReverseProxy || 'https://edgeservices.bing.com/edgesvc'
}
// if (opts.proxy && !Config.sydneyForceUseReverse) {
// this.opts.host = 'https://www.bing.com'
@ -80,42 +81,46 @@ export default class SydneyAIClient {
const fetchOptions = {
headers: {
accept: 'application/json',
'accept-language': 'en-US,en;q=0.9',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'content-type': 'application/json',
'sec-ch-ua': '"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"',
'sec-ch-ua-arch': '"x86"',
'sec-ch-ua-bitness': '"64"',
'sec-ch-ua-full-version': '"112.0.1722.7"',
'sec-ch-ua-full-version-list': '"Chromium";v="112.0.5615.20", "Microsoft Edge";v="112.0.1722.7", "Not:A-Brand";v="99.0.0.0"',
'sec-ch-ua': '"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
// 'sec-ch-ua-arch': '"x86"',
// 'sec-ch-ua-bitness': '"64"',
// 'sec-ch-ua-full-version': '"112.0.1722.7"',
// 'sec-ch-ua-full-version-list': '"Chromium";v="112.0.5615.20", "Microsoft Edge";v="112.0.1722.7", "Not:A-Brand";v="99.0.0.0"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-model': '',
'sec-ch-ua-platform': '"Windows"',
'sec-ch-ua-platform-version': '"15.0.0"',
// 'sec-ch-ua-model': '',
'sec-ch-ua-platform': '"macOS"',
// 'sec-ch-ua-platform-version': '"15.0.0"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'x-ms-client-request-id': crypto.randomUUID(),
'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32',
cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
Referer: 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx',
'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.3 OS/macOS',
// cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
Referer: 'https://edgeservices.bing.com/edgesvc/chat?udsframed=1&form=SHORUN&clientscopes=chat,noheader,channelstable,',
'Referrer-Policy': 'origin-when-cross-origin',
// Workaround for request being blocked due to geolocation
'x-forwarded-for': '1.1.1.1'
}
}
if (this.opts.cookies || this.opts.userToken) {
// 疑似无需token了
fetchOptions.headers.cookie = this.opts.cookies || `_U=${this.opts.userToken}`
}
if (this.opts.proxy) {
fetchOptions.agent = proxy(Config.proxy)
}
let accessible = !(await isCN()) || this.opts.proxy
if (accessible && !Config.sydneyForceUseReverse) {
// 本身能访问bing.com那就不用反代啦重置host
logger.info('change hosts to https://www.bing.com')
this.opts.host = 'https://www.bing.com'
logger.info('change hosts to https://edgeservices.bing.com')
this.opts.host = 'https://edgeservices.bing.com/edgesvc'
}
logger.mark('使用host' + this.opts.host)
let response = await fetch(`${this.opts.host}/turing/conversation/create`, fetchOptions)
let text = await response.text()
let retry = 30
let retry = 10
while (retry >= 0 && response.status === 200 && !text) {
await delay(400)
response = await fetch(`${this.opts.host}/turing/conversation/create`, fetchOptions)
@ -138,7 +143,7 @@ export default class SydneyAIClient {
async createWebSocketConnection () {
await this.initCache()
let WebSocket = await getWebSocket()
// let WebSocket = await getWebSocket()
return new Promise((resolve, reject) => {
let agent
let sydneyHost = 'wss://sydney.bing.com'
@ -149,7 +154,7 @@ export default class SydneyAIClient {
sydneyHost = Config.sydneyReverseProxy.replace('https://', 'wss://').replace('http://', 'ws://')
}
logger.mark(`use sydney websocket host: ${sydneyHost}`)
let ws = new WebSocket(sydneyHost + '/sydney/ChatHub', { agent })
let ws = new WebSocket(sydneyHost + '/sydney/ChatHub', undefined, { agent, origin: 'https://edgeservices.bing.com' })
ws.on('error', (err) => {
console.error(err)
reject(err)
@ -304,7 +309,8 @@ export default class SydneyAIClient {
const text = (pureSydney ? pureSydneyInstruction : (useCast?.bing || Config.sydney)).replaceAll(namePlaceholder, botName || defaultBotName) +
((Config.enableGroupContext && groupId) ? groupContextTip : '') +
((Config.enforceMaster && master) ? masterTip : '') +
(Config.sydneyMood ? moodTip : '')
(Config.sydneyMood ? moodTip : '') +
(Config.sydneySystemCode ? '' : '')
// logger.info(text)
if (pureSydney) {
previousMessages = invocationId === 0
@ -347,25 +353,31 @@ export default class SydneyAIClient {
logger.mark('sydney websocket constructed successful')
}
const toneOption = 'h3imaginative'
let optionsSets = [
'nlu_direct_response_filter',
'deepleo',
'disable_emoji_spoken_text',
'responsible_ai_policy_235',
'enablemm',
toneOption,
'dagslnv1',
'sportsansgnd',
'dl_edge_desc',
'noknowimg',
// 'dtappid',
// 'cricinfo',
// 'cricinfov2',
'dv3sugg',
'gencontentv3'
]
if (Config.enableGenerateContents) {
optionsSets.push(...['gencontentv3'])
}
const obj = {
arguments: [
{
source: 'cib',
optionsSets: [
'nlu_direct_response_filter',
'deepleo',
'disable_emoji_spoken_text',
'responsible_ai_policy_235',
'enablemm',
toneOption,
'clgalileo',
'gencontentv3',
'rai267',
'dtappid',
'cricinfo',
'cricinfov2',
'dv3sugg'
],
optionsSets,
sliceIds: [
'222dtappid',
'225cricinfo',

View file

@ -8,6 +8,9 @@ import buffer from 'buffer'
import yaml from 'yaml'
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
import { Config } from './config.js'
import { speakers as vitsRoleList } from './tts.js'
import { supportConfigurations as voxRoleList } from './tts/voicevox.js'
import { supportConfigurations as azureRoleList } from './tts/microsoft-azure.js'
// export function markdownToText (markdown) {
// return remark()
// .use(stripMarkdown)
@ -19,7 +22,7 @@ let _puppeteer
try {
const Puppeteer = (await import('../../../renderers/puppeteer/lib/puppeteer.js')).default
let puppeteerCfg = {}
let configFile = `./renderers/puppeteer/config.yaml`
let configFile = './renderers/puppeteer/config.yaml'
if (fs.existsSync(configFile)) {
try {
puppeteerCfg = yaml.parse(fs.readFileSync(configFile, 'utf8'))
@ -335,7 +338,7 @@ export async function renderUrl (e, url, renderCfg = {}) {
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: url,
url,
option: {
width: renderCfg.Viewport.width || 1280,
height: renderCfg.Viewport.height || 720,
@ -350,7 +353,7 @@ export async function renderUrl (e, url, renderCfg = {}) {
})
if (resultres.ok) {
const buff = Buffer.from(await resultres.arrayBuffer())
if(buff) {
if (buff) {
const base64 = segment.image(buff)
if (renderCfg.retType === 'base64') {
return base64
@ -363,7 +366,7 @@ export async function renderUrl (e, url, renderCfg = {}) {
}
}
}
await _puppeteer.browserInit()
const page = await _puppeteer.browser.newPage()
let base64
@ -401,7 +404,8 @@ export function getDefaultReplySetting () {
usePicture: Config.defaultUsePicture,
useTTS: Config.defaultUseTTS,
ttsRole: Config.defaultTTSRole,
ttsRoleAzure: Config.azureTTSSpeaker
ttsRoleAzure: Config.azureTTSSpeaker,
ttsRoleVoiceVox: Config.voicevoxTTSSpeaker
}
}
@ -679,16 +683,106 @@ export async function getUserData (user) {
return JSON.parse(data)
} catch (error) {
return {
user: user,
user,
passwd: '',
chat: [],
mode: '',
cast: {
api: '', //API设定
bing: '', //必应设定
bing_resource: '', //必应扩展资料
slack: '', //Slack设定
api: '', // API设定
bing: '', // 必应设定
bing_resource: '', // 必应扩展资料
slack: '' // Slack设定
}
}
}
}
}
export function getVoicevoxRoleList () {
return voxRoleList.map(item => item.name).join('、')
}
export function getAzureRoleList () {
return azureRoleList.map(item => item.name).join('、')
}
export async function getVitsRoleList (e) {
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, 'vits角色列表1'), await makeForwardMsg(e, chunk2, 'vits角色列表2')]
return await makeForwardMsg(e, list, 'vits角色列表')
}
export async function getUserReplySetting (e) {
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()
}
return userSetting
}
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
}
}
// 对原始黑白名单进行去重和去除无效群号处理,并处理通过锅巴面板添加错误配置时可能导致的问题
export function processList (whitelist, blacklist) {
whitelist = Array.isArray(whitelist)
? whitelist
: String(whitelist).split(/[,]/)
blacklist = !Array.isArray(blacklist)
? blacklist
: String(blacklist).split(/[,]/)
whitelist = Array.from(new Set(whitelist)).filter(value => /^\^?[1-9]\d{5,9}$/.test(value))
blacklist = Array.from(new Set(blacklist)).filter(value => /^\^?[1-9]\d{5,9}$/.test(value))
return [whitelist, blacklist]
}

View file

@ -85,6 +85,7 @@ const defaultConfig = {
sydneyApologyIgnored: true,
enforceMaster: false,
oldview: false,
newhelp: false,
serverPort: 3321,
serverHost: '',
viewHost: '',
@ -98,8 +99,8 @@ const defaultConfig = {
live2dOption_rotation: 0,
groupAdminPage: false,
enablePrivateChat: false,
groupWhitelist: [],
groupBlacklist: [],
whitelist: [],
blacklist: [],
ttsRegex: '/匹配规则/匹配模式',
slackUserToken: '',
slackBotUserToken: '',
@ -123,7 +124,8 @@ const defaultConfig = {
azureTTSEmotion: false,
enhanceAzureTTSEmotion: false,
autoJapanese: false,
version: 'v2.6.0'
enableGenerateContents: false,
version: 'v2.6.2'
}
const _path = process.cwd()
let config = {}

View file

@ -468,12 +468,18 @@ export async function convertFaces (msg, handleAt = false, e) {
handleAt = e?.isGroup && handleAt
let groupMembers
let groupCardQQMap = {}
if (handleAt && typeof e.group.getMemberMap === 'function') {
groupMembers = await e.group.getMemberMap()
for (let key of groupMembers.keys()) {
groupCardQQMap[groupMembers.get(key).card || groupMembers.get(key).nickname] = groupMembers.get(key).user_id
if (handleAt) {
try {
groupMembers = await e.group.getMemberMap()
} catch (err) {
console.error(`Failed to get group members: ${err}`)
}
}
if (groupMembers) {
for (let key of groupMembers.keys()) {
groupCardQQMap[groupMembers.get(key).card || groupMembers.get(key).nickname] = groupMembers.get(key).user_id
}
}
}
let tmpMsg = ''
let tmpFace = ''
let tmpAt = ''

View file

@ -1,6 +1,7 @@
import crypto from 'crypto'
import { getDefaultReplySetting, mkdirs } from '../common.js'
import { Config } from '../config.js'
import { translate } from '../translate.js'
let sdk
try {
@ -20,20 +21,29 @@ async function generateAudio (text, option = {}, ssml = '') {
let filename = `${_path}/data/chatgpt/tts/azure/${crypto.randomUUID()}.wav`
let audioConfig = sdk.AudioConfig.fromAudioFileOutput(filename)
let synthesizer
let speaker = option?.speaker || '随机'
let context = text
// 打招呼用
if (speaker === '随机') {
speaker = supportConfigurations[Math.floor(Math.random() * supportConfigurations.length)].code
let languagePrefix = supportConfigurations.find(config => config.code === speaker).languageDetail.charAt(0)
languagePrefix = languagePrefix.startsWith('E') ? '英' : languagePrefix
context = (await translate(context, languagePrefix)).replace('\n', '')
}
if (ssml) {
synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig)
await speakSsmlAsync(synthesizer, ssml)
} else {
speechConfig.speechSynthesisLanguage = option?.language || 'zh-CN'
logger.info('using speaker: ' + option?.speaker || 'zh-CN-YunyeNeural')
speechConfig.speechSynthesisVoiceName = option?.speaker || 'zh-CN-YunyeNeural'
} else { // 打招呼用
speechConfig.speechSynthesisLanguage = option?.language || supportConfigurations.find(config => config.code === speaker).language
speechConfig.speechSynthesisVoiceName = speaker
logger.info('using speaker: ' + speaker)
logger.info('using language: ' + speechConfig.speechSynthesisLanguage)
synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig)
await speakTextAsync(synthesizer, text)
await speakTextAsync(synthesizer, context)
}
console.log('synthesis finished.')
synthesizer.close()
synthesizer = undefined
return filename
}
@ -73,11 +83,27 @@ async function speakSsmlAsync (synthesizer, ssml) {
})
}
async function generateSsml (text, option = {}) {
const voiceName = option.speaker || 'zh-CN-YunyeNeural'
const expressAs = option.emotion ? `<mstts:express-as style="${option.emotion}" styledegree="${option.emotionDegree || 1}">` : ''
let speaker = option?.speaker || '随机'
let emotionDegree, role, emotion
// 打招呼用
if (speaker === '随机') {
role = supportConfigurations[Math.floor(Math.random() * supportConfigurations.length)]
speaker = role.code
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 emotion: ' + emotion)
emotionDegree = 2
} else {
emotion = option.emotion
emotionDegree = option.emotionDegree
}
const expressAs = emotion !== undefined ? `<mstts:express-as style="${emotion}" styledegree="${emotionDegree || 1}">` : ''
return `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="zh-CN">
<voice name="${voiceName}">
<voice name="${speaker}">
${expressAs}${text}${expressAs ? '</mstts:express-as>' : ''}
</voice>
</speak>`
@ -91,7 +117,7 @@ async function getEmotionPrompt (e) {
let emotionPrompt = ''
let ttsRoleAzure = userReplySetting.ttsRoleAzure
const configuration = Config.ttsMode === 'azure' ? supportConfigurations.find(config => config.code === ttsRoleAzure) : ''
if (configuration !== '' && configuration.emotion) {
if (configuration !== '' && configuration?.emotion) {
// 0-1 感觉没啥区别说实话只有1和2听得出差别。。
emotionPrompt = `\n在回复的最开始使用[]在其中表示你这次回复的情绪风格和程度(1-2)最小单位0.1
\n例如['angry',2]表示你极度愤怒
@ -110,28 +136,32 @@ export const supportConfigurations = [
name: '晓北',
language: 'zh-CN',
languageDetail: '中文(东北官话,简体)',
gender: '女'
gender: '女',
roleInfo: '晓北-女-中文(东北官话,简体)'
},
{
code: 'zh-CN-henan-YundengNeural',
name: '云登',
language: 'zh-CN',
languageDetail: '中文(中原官话河南,简体)',
gender: '男'
gender: '男',
roleInfo: '云登-男-中文(中原官话河南,简体)'
},
{
code: 'zh-CN-shaanxi-XiaoniNeural',
name: '晓妮',
language: 'zh-CN',
languageDetail: '中文(中原官话陕西,简体)',
gender: '女'
gender: '女',
roleInfo: '晓妮-女-中文(中原官话陕西,简体)'
},
{
code: 'zh-CN-henan-YundengNeural',
name: '云翔',
language: 'zh-CN',
languageDetail: '中文(冀鲁官话,简体)',
gender: '男'
gender: '男',
roleInfo: '云翔-男-中文(冀鲁官话,简体)'
},
{
code: 'zh-CN-XiaoxiaoNeural',
@ -157,7 +187,8 @@ export const supportConfigurations = [
'poetry-reading': '读诗时带情感和节奏的语气',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓晓-女-中文(普通话,简体)'
},
{
code: 'zh-CN-YunxiNeural',
@ -178,7 +209,8 @@ export const supportConfigurations = [
newscast: '用于新闻播报,表现出庄重、严谨的语气',
sad: '表达悲伤、失落的语气',
serious: '表现出认真、严肃的语气'
}
},
roleInfo: '云希-男-中文 (普通话,简体)'
},
{
code: 'zh-CN-YunyangNeural',
@ -190,7 +222,8 @@ export const supportConfigurations = [
customerservice: '以亲切友好的语气为客户提供支持',
'narration-professional': '以专业、稳重的语气讲述',
'newscast-casual': '以轻松自然的语气播报新闻'
}
},
roleInfo: '云扬-男-中文 (普通话,简体)'
},
{
code: 'zh-CN-YunyeNeural',
@ -207,7 +240,8 @@ export const supportConfigurations = [
fearful: '表达害怕和不安的语气',
sad: '表达悲伤和失落的语气',
serious: '以认真和严肃的态度说话'
}
},
roleInfo: '云野-男-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoshuangNeural',
@ -215,37 +249,40 @@ export const supportConfigurations = [
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女',
emotion: {
chat: '表达轻松随意的语气'
}
emotion: { chat: '表达轻松随意的语气' },
roleInfo: '晓双-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoyouNeural',
name: '晓悠',
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女'
gender: '女',
roleInfo: '晓悠-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoqiuNeural',
name: '晓秋',
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女'
gender: '女',
roleInfo: '晓秋-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaochenNeural',
name: '晓辰',
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女'
gender: '女',
roleInfo: '晓辰-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoyanNeural',
name: '晓颜',
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女'
gender: '女',
roleInfo: '晓颜-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaomoNeural',
@ -266,7 +303,8 @@ export const supportConfigurations = [
gentle: '温和、礼貌、愉快的语气,音调和音量较低',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓墨-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoxuanNeural',
@ -283,7 +321,8 @@ export const supportConfigurations = [
fearful: '恐惧、紧张的语气,说话人处于紧张和不安的状态',
gentle: '温和、礼貌、愉快的语气,音调和音量较低',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓萱-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaohanNeural',
@ -302,7 +341,8 @@ export const supportConfigurations = [
gentle: '温和、礼貌、愉快的语气,音调和音量较低',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓涵-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoruiNeural',
@ -315,7 +355,8 @@ export const supportConfigurations = [
calm: '沉着冷静的态度说话。语气、音调和韵律统一',
fearful: '恐惧、紧张的语气,说话人处于紧张和不安的状态',
sad: '表达悲伤语气'
}
},
roleInfo: '晓睿-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaomengNeural',
@ -323,9 +364,8 @@ export const supportConfigurations = [
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '女',
emotion: {
chat: '表达轻松随意的语气'
}
emotion: { chat: '表达轻松随意的语气' },
roleInfo: '晓梦-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaoyiNeural',
@ -340,7 +380,8 @@ export const supportConfigurations = [
gentle: '温和、礼貌、愉快的语气,音调和音量较低',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓伊-女-中文(普通话,简体)'
},
{
code: 'zh-CN-XiaozhenNeural',
@ -355,7 +396,8 @@ export const supportConfigurations = [
fearful: '恐惧、紧张的语气,说话人处于紧张和不安的状态',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '晓甄-女-中文(普通话,简体)'
},
{
code: 'zh-CN-YunfengNeural',
@ -371,14 +413,16 @@ export const supportConfigurations = [
fearful: '恐惧、紧张的语气,说话人处于紧张和不安的状态',
sad: '表达悲伤语气',
serious: '严肃、命令的语气'
}
},
roleInfo: '云枫-男-中文(普通话,简体)'
},
{
code: 'zh-CN-YunhaoNeural',
name: '云皓',
language: 'zh-CN',
languageDetail: '中文(普通话,简体)',
gender: '男'
gender: '男',
roleInfo: '云皓-男-中文(普通话,简体)'
},
{
code: 'zh-CN-YunjianNeural',
@ -390,7 +434,8 @@ export const supportConfigurations = [
'narration-relaxed': '以轻松、自然的语气进行叙述',
'sports-commentary': '在解说体育比赛时,使用专业而自信的语气',
'sports-commentary-excited': '在解说激动人心的体育比赛时,使用兴奋和激动的语气'
}
},
roleInfo: '云健-男-中文(普通话,简体)'
},
{
code: 'zh-CN-YunxiaNeural',
@ -404,7 +449,8 @@ export const supportConfigurations = [
cheerful: '表达积极愉快的语气',
fearful: '表达害怕、紧张的语气',
sad: '表达悲伤和失落的语气'
}
},
roleInfo: '云夏-男-中文 (普通话,简体)'
},
{
code: 'zh-CN-YunzeNeural',
@ -422,105 +468,120 @@ export const supportConfigurations = [
fearful: '表达害怕、不安的情绪',
sad: '用悲伤的语气表达悲伤和失落',
serious: '以严肃的语气和态度表现出对事情的重视和认真对待'
}
},
roleInfo: '云泽-男-中文 (普通话,简体)'
},
{
code: 'zh-HK-HiuGaaiNeural',
name: '曉佳',
language: 'zh-CN',
languageDetail: '中文(粤语,繁体)',
gender: '女'
gender: '女',
roleInfo: '曉佳-女-中文(粤语,繁体)'
},
{
code: 'zh-HK-HiuMaanNeural',
name: '曉曼',
language: 'zh-CN',
languageDetail: '中文(粤语,繁体)',
gender: '女'
gender: '女',
roleInfo: '曉曼-女-中文(粤语,繁体)'
},
{
code: 'zh-HK-WanLungNeural',
name: '雲龍',
language: 'zh-CN',
languageDetail: '中文(粤语,繁体)',
gender: '男'
gender: '男',
roleInfo: '雲龍-男-中文(粤语,繁体)'
},
{
code: 'en-GB-AbbiNeural',
name: 'Abbi',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Abbi-女-英语(英国)'
},
{
code: 'en-GB-AlfieNeural',
name: 'Alfie',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Alfie-男-英语(英国)'
},
{
code: 'en-GB-BellaNeural',
name: 'Bella',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Bella-女-英语(英国)'
},
{
code: 'en-GB-ElliotNeural',
name: 'Elliot',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Elliot-男-英语(英国)'
},
{
code: 'en-GB-EthanNeural',
name: 'Ethan',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Ethan-男-英语(英国)'
},
{
code: 'en-GB-HollieNeural',
name: 'Hollie',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Hollie-女-英语(英国)'
},
{
code: 'en-GB-LibbyNeural',
name: 'Libby',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Libby-女-英语(英国)'
},
{
code: 'en-GB-MaisieNeural',
name: 'Maisie',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Maisie-女-英语(英国)'
},
{
code: 'en-GB-NoahNeural',
name: 'Noah',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Noah-男-英语(英国)'
},
{
code: 'en-GB-OliverNeural',
name: 'Oliver',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Oliver-男-英语(英国)'
},
{
code: 'en-GB-OliviaNeural',
name: 'Olivia',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female'
gender: 'female',
roleInfo: 'Olivia-女-英语(英国)'
},
{
code: 'en-GB-RyanNeural',
@ -528,11 +589,8 @@ export const supportConfigurations = [
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male',
emotion: {
chat: '表达轻松随意的语气',
cheerful: '表达积极愉快的语气'
}
emotion: { chat: '表达轻松随意的语气', cheerful: '表达积极愉快的语气' },
roleInfo: 'Ryan-男-英语(英国)'
},
{
code: 'en-GB-SoniaNeural',
@ -540,46 +598,48 @@ export const supportConfigurations = [
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'female',
emotion: {
cheerful: '表达积极愉快的语气',
sad: '表达悲伤语气'
}
emotion: { cheerful: '表达积极愉快的语气', sad: '表达悲伤语气' },
roleInfo: 'Sonia-女-英语(英国)'
},
{
code: 'en-GB-ThomasNeural',
name: 'Thomas',
language: 'en-GB',
languageDetail: '英语(英国)',
gender: 'male'
gender: 'male',
roleInfo: 'Thomas-男-英语(英国)'
},
{
code: 'ja-JP-AoiNeural',
name: '葵',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '女'
gender: '女',
roleInfo: '葵-女-日语(日本)'
},
{
code: 'ja-JP-DaichiNeural',
name: '大地',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '男'
gender: '男',
roleInfo: '大地-男-日语(日本)'
},
{
code: 'ja-JP-KeitaNeural',
name: '慶太',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '男'
gender: '男',
roleInfo: '慶太-男-日语(日本)'
},
{
code: 'ja-JP-MayuNeural',
name: '真由',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '女'
gender: '女',
roleInfo: '真由-女-日语(日本)'
},
{
code: 'ja-JP-NanamiNeural',
@ -591,49 +651,56 @@ export const supportConfigurations = [
chat: '表达轻松随意的语气',
cheerful: '表达积极愉快的语气',
customerservice: '以友好热情的语气为客户提供支持'
}
},
roleInfo: '七海-女-日语(日本)'
},
{
code: 'ja-JP-NaokiNeural',
name: '直樹',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '男'
gender: '男',
roleInfo: '直樹-男-日语(日本)'
},
{
code: 'ja-JP-ShioriNeural',
name: '栞',
language: 'ja-JP',
languageDetail: '日语(日本)',
gender: '女'
gender: '女',
roleInfo: '栞-女-日语(日本)'
},
{
code: 'en-US-AIGenerate1Neural1',
name: 'AI Generate 1',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '男'
gender: '男',
roleInfo: 'AI Generate 1-男-英语(美国)'
},
{
code: 'en-US-AIGenerate2Neural1',
name: 'AI Generate 2',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女'
gender: '女',
roleInfo: 'AI Generate 2-女-英语(美国)'
},
{
code: 'en-US-AmberNeural',
name: 'Amber',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女'
gender: '女',
roleInfo: 'Amber-女-英语(美国)'
},
{
code: 'en-US-AnaNeural',
name: 'Ana',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女性、儿童'
gender: '女性、儿童',
roleInfo: 'Ana-女性、儿童-英语(美国)'
},
{
code: 'en-US-AriaNeural',
@ -658,35 +725,40 @@ export const supportConfigurations = [
'narration-professional': '以专业、客观的语气朗读内容',
'newscast-casual': '以通用、随意的语气发布一般新闻',
'newscast-formal': '以正式、自信和权威的语气发布新闻'
}
},
roleInfo: 'Aria-女-英语(美国)'
},
{
code: 'en-US-AshleyNeural',
name: 'Ashley',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女'
gender: '女',
roleInfo: 'Ashley-女-英语(美国)'
},
{
code: 'en-US-BrandonNeural',
name: 'Brandon',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '男'
gender: '男',
roleInfo: 'Brandon-男-英语(美国)'
},
{
code: 'en-US-ChristopherNeural',
name: 'Christopher',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '男'
gender: '男',
roleInfo: 'Christopher-男-英语(美国)'
},
{
code: 'en-US-CoraNeural',
name: 'Cora',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女'
gender: '女',
roleInfo: 'Cora-女-英语(美国)'
},
{
code: 'en-US-DavisNeural',
@ -705,21 +777,24 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
roleInfo: 'Davis-男-英语(美国)'
},
{
code: 'en-US-ElizabethNeural',
name: 'Elizabeth',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '女'
gender: '女',
roleInfo: 'Elizabeth-女-英语(美国)'
},
{
code: 'en-US-EricNeural',
name: 'Eric',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '男'
gender: '男',
roleInfo: 'Eric-男-英语(美国)'
},
{
code: 'en-US-GuyNeural',
@ -739,15 +814,16 @@ export const supportConfigurations = [
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔',
newscast: '以正式专业的语气叙述新闻'
}
},
roleInfo: 'Guy-男-英语(美国)'
},
{
code: 'en-US-JacobNeural',
name: 'Jacob',
language: 'en-US',
languageDetail: 'English (United States)',
gender: '男'
gender: '男',
roleInfo: 'Jacob-男-英语(美国)'
},
{
code: 'en-US-JaneNeural',
@ -766,7 +842,8 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
roleInfo: 'Jane-女-英语(美国)'
},
{
code: 'en-US-JasonNeural',
@ -785,14 +862,8 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
{
code: 'en-US-JennyMultilingualNeural3',
name: 'Jenny',
language: 'en-US',
languageDetail: '英语(美国)',
gender: 'female'
},
roleInfo: 'Jason-男-英语(美国)'
},
{
code: 'en-US-JennyNeural',
@ -815,22 +886,24 @@ export const supportConfigurations = [
chat: '表达轻松随意的语气',
customerservice: '以友好热情的语气为客户提供支持',
newscast: '以正式专业的语气叙述新闻'
}
},
roleInfo: 'Jenny-女-英语(美国)'
},
{
code: 'en-US-MichelleNeural',
name: 'Michelle',
language: 'en-US',
languageDetail: '英语(美国)',
gender: 'female'
gender: 'female',
roleInfo: 'Michelle-女-英语(美国)'
},
{
code: 'en-US-MonicaNeural',
name: 'Monica',
language: 'en-US',
languageDetail: '英语(美国)',
gender: 'female'
gender: 'female',
roleInfo: 'Monica-女-英语(美国)'
},
{
code: 'en-US-NancyNeural',
@ -849,14 +922,16 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
roleInfo: 'Nancy-女-英语(美国)'
},
{
code: 'en-US-RogerNeural',
name: 'Roger',
language: 'en-US',
languageDetail: '英语(美国)',
gender: 'male'
gender: 'male',
roleInfo: 'Roger-男-英语(美国)'
},
{
code: 'en-US-SaraNeural',
@ -875,15 +950,16 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
roleInfo: 'Sara-女-英语(美国)'
},
{
code: 'en-US-SteffanNeural',
name: 'Steffan',
language: 'en-US',
languageDetail: '英语(美国)',
gender: 'male'
gender: 'male',
roleInfo: 'Steffan-男-英语(美国)'
},
{
code: 'en-US-TonyNeural',
@ -902,21 +978,24 @@ export const supportConfigurations = [
terrified: '非常害怕的语气,语速快且声音颤抖。不稳定的疯狂状态',
unfriendly: '表达一种冷淡无情的语气',
whispering: '说话非常柔和,发出的声音小且温柔'
}
},
roleInfo: 'Tony-男-英语(美国)'
},
{
code: 'en-IN-NeerjaNeural',
name: 'Neerja',
language: 'en',
languageDetail: '英语(印度)',
gender: 'female'
gender: 'female',
roleInfo: 'Neerja-女-英语(印度)'
},
{
code: 'en-IN-PrabhatNeural',
name: 'Prabhat',
language: 'en',
languageDetail: '英语(印度)',
gender: 'male'
gender: 'male',
roleInfo: 'Prabhat-男-英语(印度)'
}
]

View file

@ -10,7 +10,7 @@ try {
}
export class Tokenizer {
async getTodayHistory (groupId, date = new Date()) {
async getHistory (groupId, date = new Date(), duration = 0) {
if (!groupId) {
throw new Error('no valid group id')
}
@ -29,11 +29,22 @@ export class Tokenizer {
}
return 0
}
// Get the current timestamp
let currentTime = date.getTime()
// Step 2: Set the hours, minutes, seconds, and milliseconds to 0
date.setHours(0, 0, 0, 0)
// Step 3: Calculate the timestamp representing the start of the specified date
const startOfSpecifiedDate = date.getTime()
// duration represents the number of hours to go back
// if duration is 0, keeping the original date (start of today)
let startOfSpecifiedDate = date.getTime()
// if duration > 0, go back to the specified number of hours
if (duration > 0) {
// duration should be in range [0, 24]
duration = Math.min(duration, 24)
startOfSpecifiedDate = currentTime - (duration * 60 * 60 * 1000)
}
// Step 4: Get the end of the specified date by adding 24 hours (in milliseconds)
const endOfSpecifiedDate = startOfSpecifiedDate + (24 * 60 * 60 * 1000)
@ -56,12 +67,14 @@ export class Tokenizer {
return chats
}
async getTodayKeywordTopK (groupId, topK = 100) {
async getKeywordTopK (groupId, topK = 100, duration = 0) {
if (!nodejieba) {
throw new Error('未安装node-rs/jieba娱乐功能-词云统计不可用')
}
let chats = await this.getTodayHistory(groupId)
logger.mark(`聊天记录拉去完成,获取到今日内${chats.length}条聊天记录,准备分词中`)
// duration represents the number of hours to go back, should in range [0, 24]
let chats = await this.getHistory(groupId, new Date(), duration)
let duration_str = duration > 0 ? `${duration}小时` : '今日'
logger.mark(`聊天记录拉取完成,获取到${duration_str}${chats.length}条聊天记录,准备分词中`)
const _path = process.cwd()
let stopWordsPath = `${_path}/plugins/chatgpt-plugin/utils/wordcloud/cn_stopwords.txt`

View file

@ -1,9 +1,9 @@
import { Tokenizer } from './tokenizer.js'
import { render } from '../common.js'
export async function makeWordcloud (e, groupId) {
export async function makeWordcloud (e, groupId, duration = 0) {
let tokenizer = new Tokenizer()
let topK = await tokenizer.getTodayKeywordTopK(groupId, 100)
let topK = await tokenizer.getKeywordTopK(groupId, 100, duration)
let list = JSON.stringify(topK)
// let list = topK
console.log(list)