mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 22:07:10 +00:00
Merge branch 'v2' of https://github.com/HalcyonAlcedo/chatgpt-plugin; branch 'v2' of https://github.com/ikechan8370/chatgpt-plugin into v2
This commit is contained in:
commit
d795e8ea7d
13 changed files with 1881 additions and 351 deletions
|
|
@ -803,7 +803,7 @@ export class chatgpt extends plugin {
|
||||||
if (!msg || e.msg?.startsWith('#')) {
|
if (!msg || e.msg?.startsWith('#')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if ((e.isGroup || e.group_id) && !(e.atme || e.atBot)) {
|
if ((e.isGroup || e.group_id) && !(e.atme || e.atBot || (e.at === e.self_id))) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (e.user_id == getUin(e)) return false
|
if (e.user_id == getUin(e)) return false
|
||||||
|
|
@ -1652,7 +1652,7 @@ export class chatgpt extends plugin {
|
||||||
opt.groupId = e.group_id
|
opt.groupId = e.group_id
|
||||||
opt.qq = e.sender.user_id
|
opt.qq = e.sender.user_id
|
||||||
opt.nickname = e.sender.card
|
opt.nickname = e.sender.card
|
||||||
opt.groupName = e.group.name
|
opt.groupName = e.group.name || e.group_name
|
||||||
opt.botName = e.isGroup ? (e.group.pickMember(getUin(e)).card || e.group.pickMember(getUin(e)).nickname) : e.bot.nickname
|
opt.botName = e.isGroup ? (e.group.pickMember(getUin(e)).card || e.group.pickMember(getUin(e)).nickname) : e.bot.nickname
|
||||||
let master = (await getMasterQQ())[0]
|
let master = (await getMasterQQ())[0]
|
||||||
if (master && e.group) {
|
if (master && e.group) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { translate, translateLangSupports } from '../utils/translate.js'
|
||||||
import AzureTTS from '../utils/tts/microsoft-azure.js'
|
import AzureTTS from '../utils/tts/microsoft-azure.js'
|
||||||
import VoiceVoxTTS from '../utils/tts/voicevox.js'
|
import VoiceVoxTTS from '../utils/tts/voicevox.js'
|
||||||
import { URL } from 'node:url'
|
import { URL } from 'node:url'
|
||||||
|
import { getBots } from '../utils/bot.js'
|
||||||
|
|
||||||
let useSilk = false
|
let useSilk = false
|
||||||
try {
|
try {
|
||||||
|
|
@ -350,7 +351,7 @@ ${translateLangLabels}
|
||||||
let groupId = e.msg.replace(/^#chatgpt打招呼/, '')
|
let groupId = e.msg.replace(/^#chatgpt打招呼/, '')
|
||||||
logger.info(groupId)
|
logger.info(groupId)
|
||||||
groupId = parseInt(groupId)
|
groupId = parseInt(groupId)
|
||||||
if (groupId && !e.bot.getGroupList().get(groupId)) {
|
if (groupId && !e.bot.gl.get(groupId)) {
|
||||||
await e.reply('机器人不在这个群里!')
|
await e.reply('机器人不在这个群里!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -379,74 +380,77 @@ ${translateLangLabels}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let groupId = parseInt(element)
|
let groupId = parseInt(element)
|
||||||
if (this.e.bot.getGroupList().get(groupId)) {
|
let bots = this.e ? [this.e.bot] : getBots()
|
||||||
// 打招呼概率
|
for (let bot of bots) {
|
||||||
if (Math.floor(Math.random() * 100) < Config.helloProbability) {
|
if (bot.gl?.get(groupId)) {
|
||||||
let message = await generateHello()
|
// 打招呼概率
|
||||||
logger.info(`打招呼给群聊${groupId}:` + message)
|
if (Math.floor(Math.random() * 100) < Config.helloProbability) {
|
||||||
if (Config.defaultUseTTS) {
|
let message = await generateHello()
|
||||||
let audio
|
logger.info(`打招呼给群聊${groupId}:` + message)
|
||||||
const [defaultVitsTTSRole, defaultAzureTTSRole, defaultVoxTTSRole] = [Config.defaultTTSRole, Config.azureTTSSpeaker, Config.voicevoxTTSSpeaker]
|
if (Config.defaultUseTTS) {
|
||||||
let ttsSupportKinds = []
|
let audio
|
||||||
if (Config.azureTTSKey) ttsSupportKinds.push(1)
|
const [defaultVitsTTSRole, defaultAzureTTSRole, defaultVoxTTSRole] = [Config.defaultTTSRole, Config.azureTTSSpeaker, Config.voicevoxTTSSpeaker]
|
||||||
if (Config.ttsSpace) ttsSupportKinds.push(2)
|
let ttsSupportKinds = []
|
||||||
if (Config.voicevoxSpace) ttsSupportKinds.push(3)
|
if (Config.azureTTSKey) ttsSupportKinds.push(1)
|
||||||
if (!ttsSupportKinds.length) {
|
if (Config.ttsSpace) ttsSupportKinds.push(2)
|
||||||
logger.warn('没有配置任何语音服务!')
|
if (Config.voicevoxSpace) ttsSupportKinds.push(3)
|
||||||
return false
|
if (!ttsSupportKinds.length) {
|
||||||
}
|
logger.warn('没有配置任何语音服务!')
|
||||||
const randomIndex = Math.floor(Math.random() * ttsSupportKinds.length)
|
return false
|
||||||
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 : {
|
const randomIndex = Math.floor(Math.random() * ttsSupportKinds.length)
|
||||||
if (Config.autoJapanese) {
|
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 {
|
try {
|
||||||
message = await translate(message, '日')
|
audio = await generateVitsAudio(message, defaultVitsTTSRole, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
try {
|
case 3 : {
|
||||||
audio = await generateVitsAudio(message, defaultVitsTTSRole, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
|
message = (await translate(message, '日')).replace('\n', '')
|
||||||
} catch (err) {
|
try {
|
||||||
logger.error(err)
|
audio = await VoiceVoxTTS.generateAudio(message, {
|
||||||
|
speaker: defaultVoxTTSRole
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
case 3 : {
|
if (useSilk) {
|
||||||
message = (await translate(message, '日')).replace('\n', '')
|
await this.e.bot.sendGroupMsg(groupId, await uploadRecord(audio))
|
||||||
try {
|
} else {
|
||||||
audio = await VoiceVoxTTS.generateAudio(message, {
|
await this.e.bot.sendGroupMsg(groupId, segment.record(audio))
|
||||||
speaker: defaultVoxTTSRole
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (useSilk) {
|
|
||||||
await this.e.bot.sendGroupMsg(groupId, await uploadRecord(audio))
|
|
||||||
} else {
|
} else {
|
||||||
await this.e.bot.sendGroupMsg(groupId, segment.record(audio))
|
await this.e.bot.sendGroupMsg(groupId, message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.e.bot.sendGroupMsg(groupId, message)
|
logger.info(`时机未到,这次就不打招呼给群聊${groupId}了`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info(`时机未到,这次就不打招呼给群聊${groupId}了`)
|
logger.warn('机器人不在要发送的群组里,忽略群。同时建议检查配置文件修改要打招呼的群号。' + groupId)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.warn('机器人不在要发送的群组里,忽略群。同时建议检查配置文件修改要打招呼的群号。' + groupId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,25 @@ export class ChatgptManagement extends plugin {
|
||||||
reg: '^#chatgpt(开启|关闭)智能模式$',
|
reg: '^#chatgpt(开启|关闭)智能模式$',
|
||||||
fnc: 'switchSmartMode',
|
fnc: 'switchSmartMode',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt模型列表$',
|
||||||
|
fnc: 'viewAPIModel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置(API|api)模型$',
|
||||||
|
fnc: 'setAPIModel',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置(API|api)反代$',
|
||||||
|
fnc: 'setOpenAiBaseUrl',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置星火模型$',
|
||||||
|
fnc: 'setXinghuoModel',
|
||||||
|
permission: 'master'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
@ -1435,4 +1454,94 @@ Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie
|
||||||
await e.reply('好的,已经关闭智能模式')
|
await e.reply('好的,已经关闭智能模式')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async viewAPIModel (e) {
|
||||||
|
const contents = [
|
||||||
|
'仅列出部分模型以供参考',
|
||||||
|
'gpt-3.5-turbo',
|
||||||
|
'gpt-3.5-turbo-0301',
|
||||||
|
'gpt-3.5-turbo-0613',
|
||||||
|
'gpt-3.5-turbo-1106',
|
||||||
|
'gpt-3.5-turbo-16k',
|
||||||
|
'gpt-3.5-turbo-16k-0613',
|
||||||
|
'gpt-4',
|
||||||
|
'gpt-4-32k',
|
||||||
|
'gpt-4-1106-preview'
|
||||||
|
]
|
||||||
|
let modelList = []
|
||||||
|
contents.forEach(value => {
|
||||||
|
// console.log(value)
|
||||||
|
modelList.push(value)
|
||||||
|
})
|
||||||
|
await this.e.reply(makeForwardMsg(e, modelList, '模型列表'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAPIModel (e) {
|
||||||
|
this.setContext('saveAPIModel')
|
||||||
|
await this.reply('请发送API模型', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveAPIModel () {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
Config.model = token
|
||||||
|
await this.reply('API模型设置成功', true)
|
||||||
|
this.finish('saveAPIModel')
|
||||||
|
}
|
||||||
|
|
||||||
|
async setOpenAiBaseUrl (e) {
|
||||||
|
this.setContext('saveOpenAiBaseUrl')
|
||||||
|
await this.reply('请发送API反代', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveOpenAiBaseUrl () {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
// console.log(token.startsWith('http://') || token.startsWith('https://'))
|
||||||
|
if (token.startsWith('http://') || token.startsWith('https://')) {
|
||||||
|
Config.openAiBaseUrl = token
|
||||||
|
await this.reply('API反代设置成功', true)
|
||||||
|
this.finish('saveOpenAiBaseUrl')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await this.reply('你的输入不是一个有效的URL,请检查是否含有http://或https://', true)
|
||||||
|
this.finish('saveOpenAiBaseUrl')
|
||||||
|
}
|
||||||
|
|
||||||
|
async setXinghuoModel (e) {
|
||||||
|
this.setContext('saveXinghuoModel')
|
||||||
|
await this.reply('1:星火V1.5\n2:星火V2\n3:星火V3\n4:星火助手')
|
||||||
|
await this.reply('请发送序号', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveXinghuoModel (e) {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
let ver
|
||||||
|
switch (token) {
|
||||||
|
case '3':
|
||||||
|
ver = 'V3'
|
||||||
|
Config.xhmode = 'apiv3'
|
||||||
|
break
|
||||||
|
case '2':
|
||||||
|
ver = 'V2'
|
||||||
|
Config.xhmode = 'apiv2'
|
||||||
|
break
|
||||||
|
case '1':
|
||||||
|
ver = 'V1.5'
|
||||||
|
Config.xhmode = 'api'
|
||||||
|
break
|
||||||
|
case '4':
|
||||||
|
ver = '助手'
|
||||||
|
Config.xhmode = 'assistants'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await this.reply(`已成功切换到星火${ver}`, true)
|
||||||
|
this.finish('saveXinghuoModel')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ export function supportGuoba () {
|
||||||
{
|
{
|
||||||
field: 'model',
|
field: 'model',
|
||||||
label: 'OpenAI 模型',
|
label: 'OpenAI 模型',
|
||||||
bottomHelpMessage: 'gpt-4, gpt-4-0613, gpt-4-32k, gpt-4-32k-0613, gpt-3.5-turbo, gpt-3.5-turbo-0613, gpt-3.5-turbo-16k-0613。默认为gpt-3.5-turbo,gpt-4需账户支持',
|
bottomHelpMessage: 'gpt-4, gpt-4-0613, gpt-4-1106, gpt-4-32k, gpt-4-32k-0613, gpt-3.5-turbo, gpt-3.5-turbo-0613, gpt-3.5-turbo-1106, gpt-3.5-turbo-16k-0613。默认为gpt-3.5-turbo,gpt-4需账户支持',
|
||||||
component: 'Input'
|
component: 'Input'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -456,7 +456,7 @@ export function supportGuoba () {
|
||||||
{
|
{
|
||||||
field: 'sydneyWebsocketUseProxy',
|
field: 'sydneyWebsocketUseProxy',
|
||||||
label: '对话使用sydney反代',
|
label: '对话使用sydney反代',
|
||||||
bottomHelpMessage: '【一般情况无需也不建议开启】默认情况下仅创建对话走反代,对话时仍然直连微软。开启本选项将使对话过程也走反,需反代支持',
|
bottomHelpMessage: '默认情况下仅创建对话走反代,对话时仍然直连微软。开启本选项将使对话过程也走反代,需反代支持。默认开启',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -505,40 +505,6 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '使用GPT-4,注意试用配额较低,如果用不了就关掉',
|
bottomHelpMessage: '使用GPT-4,注意试用配额较低,如果用不了就关掉',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '以下为浏览器方式的配置.(Deprecated)',
|
|
||||||
component: 'Divider'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'username',
|
|
||||||
label: '用户名',
|
|
||||||
bottomHelpMessage: 'OpenAI用户名。',
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'password',
|
|
||||||
label: '密码',
|
|
||||||
bottomHelpMessage: 'OpenAI密码。',
|
|
||||||
component: 'InputPassword'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'UA',
|
|
||||||
label: '浏览器UA',
|
|
||||||
bottomHelpMessage: '模拟浏览器UA,无特殊需求保持默认即可',
|
|
||||||
component: 'InputTextArea'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'headless',
|
|
||||||
label: '无头模式',
|
|
||||||
bottomHelpMessage: '无界面的服务器可以开启,但遇到验证码时可能无法使用。(实测很容易卡住,几乎不可用)',
|
|
||||||
component: 'Switch'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'chromePath',
|
|
||||||
label: 'Chrome路径',
|
|
||||||
bottomHelpMessage: '为空使用默认puppeteer的chromium,也可以传递自己本机安装的Chrome可执行文件地址,提高通过率。windows可以是‘C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe’,linux通过which查找路径',
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '以下为Slack Claude方式的配置',
|
label: '以下为Slack Claude方式的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
@ -625,16 +591,6 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '等待响应的超时时间,单位为秒,默认为120。如果不使用反代而是使用代理可以适当调低。',
|
bottomHelpMessage: '等待响应的超时时间,单位为秒,默认为120。如果不使用反代而是使用代理可以适当调低。',
|
||||||
component: 'InputNumber'
|
component: 'InputNumber'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '以下为ChatGLM方式的配置',
|
|
||||||
component: 'Divider'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'chatglmBaseUrl',
|
|
||||||
label: 'ChatGLM API地址',
|
|
||||||
bottomHelpMessage: '如 http://localhost:8080',
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '以下为星火方式的配置',
|
label: '以下为星火方式的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
@ -649,6 +605,7 @@ export function supportGuoba () {
|
||||||
{ label: '体验版', value: 'web' },
|
{ label: '体验版', value: 'web' },
|
||||||
{ label: '讯飞星火认知大模型V1.5', value: 'api' },
|
{ label: '讯飞星火认知大模型V1.5', value: 'api' },
|
||||||
{ label: '讯飞星火认知大模型V2.0', value: 'apiv2' },
|
{ label: '讯飞星火认知大模型V2.0', value: 'apiv2' },
|
||||||
|
{ label: '讯飞星火认知大模型V3.0', value: 'apiv3' },
|
||||||
{ label: '讯飞星火助手', value: 'assistants' }
|
{ label: '讯飞星火助手', value: 'assistants' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
868
package-lock.json
generated
868
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,171 +1,171 @@
|
||||||
import { UserInfo, AddUser } from './user_data.js'
|
import { UserInfo, AddUser } from './user_data.js'
|
||||||
import { randomString, getUserData, getMasterQQ, getUin } from '../../utils/common.js'
|
import { randomString, getUserData, getMasterQQ, getUin } from '../../utils/common.js'
|
||||||
|
import { getBots } from '../../utils/bot.js';
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
async function User(fastify, options) {
|
async function User (fastify, options) {
|
||||||
// 登录
|
// 登录
|
||||||
fastify.post('/login', async (request, reply) => {
|
fastify.post('/login', async (request, reply) => {
|
||||||
const body = request.body || {}
|
const body = request.body || {}
|
||||||
let guobaLoginService
|
let guobaLoginService
|
||||||
let guobaAPI = ''
|
let guobaAPI = ''
|
||||||
try {
|
try {
|
||||||
let { LoginService } = await import('../../../Guoba-Plugin/server/service/both/LoginService.js')
|
let { LoginService } = await import('../../../Guoba-Plugin/server/service/both/LoginService.js')
|
||||||
let { getAllWebAddress } = await import('../../../Guoba-Plugin/utils/common.js')
|
let { getAllWebAddress } = await import('../../../Guoba-Plugin/utils/common.js')
|
||||||
guobaLoginService = new LoginService()
|
guobaLoginService = new LoginService()
|
||||||
guobaAPI = await getAllWebAddress()
|
guobaAPI = await getAllWebAddress()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
guobaLoginService = {
|
||||||
|
signToken: () => { return null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (body.qq && body.passwd) {
|
||||||
|
const token = randomString(32)
|
||||||
|
if (body.qq == getUin() && await redis.get('CHATGPT:ADMIN_PASSWD') == body.passwd) {
|
||||||
|
const guobaToken = await guobaLoginService.signToken(body.qq)
|
||||||
|
AddUser({ user: body.qq, token, autho: 'admin' })
|
||||||
|
reply.setCookie('token', token, { path: '/' })
|
||||||
|
reply.send({ login: true, autho: 'admin', token, guobaToken, guoba: guobaAPI })
|
||||||
|
} else {
|
||||||
|
const user = await getUserData(body.qq)
|
||||||
|
if (user.passwd != '' && user.passwd === body.passwd) {
|
||||||
|
AddUser({ user: body.qq, token, autho: 'user' })
|
||||||
|
reply.setCookie('token', token, { path: '/' })
|
||||||
|
reply.send({ login: true, autho: 'user', token })
|
||||||
|
} else {
|
||||||
|
reply.send({ login: false, err: `用户名密码错误,如果忘记密码请私聊机器人输入 ${body.qq == getUin() ? '#修改管理密码' : '#修改用户密码'} 进行修改` })
|
||||||
}
|
}
|
||||||
catch (err) {
|
}
|
||||||
|
} else if (body.otp) {
|
||||||
|
const token = randomString(32)
|
||||||
|
const opt = await redis.get('CHATGPT:SERVER_QUICK')
|
||||||
|
if (opt && body.otp == opt) {
|
||||||
|
const guobaToken = await guobaLoginService.signToken(getUin())
|
||||||
|
AddUser({ user: getUin(), token, autho: 'admin' })
|
||||||
|
reply.setCookie('token', token, { path: '/' })
|
||||||
|
reply.send({ login: true, autho: 'admin', token, user: getUin(), guobaToken, guoba: guobaAPI })
|
||||||
|
} else {
|
||||||
|
reply.send({ login: false, err: '快捷登录代码错误,请检查后重试' })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.send({ login: false, err: '未输入用户名或密码' })
|
||||||
|
}
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 快速登录
|
||||||
|
fastify.post('/quick', async (request, reply) => {
|
||||||
|
const otp = randomString(6)
|
||||||
|
await redis.set(
|
||||||
|
'CHATGPT:SERVER_QUICK',
|
||||||
|
otp,
|
||||||
|
{ EX: 60000 }
|
||||||
|
)
|
||||||
|
const master = (await getMasterQQ())[0]
|
||||||
|
let bots = getBots()
|
||||||
|
for (let bot of bots) {
|
||||||
|
bot.pickUser(master).sendMsg(`收到工具箱快捷登录请求,1分钟内有效:${otp}`)
|
||||||
|
}
|
||||||
|
reply.send({ state: true })
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 检查用户是否存在
|
||||||
|
fastify.post('/verify', async (request, reply) => {
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
const user = UserInfo(token)
|
||||||
|
if (!user || token === 'unknown') {
|
||||||
|
reply.send({
|
||||||
|
verify: false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reply.send({
|
||||||
|
verify: true,
|
||||||
|
user: user.user,
|
||||||
|
autho: user.autho,
|
||||||
|
version: 10016
|
||||||
|
})
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 获取用户数据
|
||||||
|
fastify.post('/userData', async (request, reply) => {
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
let user = UserInfo(token)
|
||||||
|
if (!user) user = { user: '' }
|
||||||
|
const userData = await getUserData(user.user)
|
||||||
|
reply.send({
|
||||||
|
chat: userData.chat || [],
|
||||||
|
mode: userData.mode || '',
|
||||||
|
cast: userData.cast || {
|
||||||
|
api: '', // API设定
|
||||||
|
bing: '', // 必应设定
|
||||||
|
bing_resource: '', // 必应扩展资料
|
||||||
|
slack: '' // Slack设定
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 删除用户
|
||||||
|
fastify.post('/deleteUser', async (request, reply) => {
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
const user = UserInfo(token)
|
||||||
|
if (!user || user === 'unknown') {
|
||||||
|
reply.send({ state: false, error: '无效token' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const filepath = `resources/ChatGPTCache/user/${user.user}.json`
|
||||||
|
fs.unlinkSync(filepath)
|
||||||
|
reply.send({ state: true })
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 修改密码
|
||||||
|
fastify.post('/changePassword', async (request, reply) => {
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
const user = UserInfo(token)
|
||||||
|
if (!user || user === 'unknown') {
|
||||||
|
reply.send({ state: false, error: '无效的用户信息' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const userData = await getUserData(user.user)
|
||||||
|
const body = request.body || {}
|
||||||
|
if (!body.newPasswd) {
|
||||||
|
reply.send({ state: false, error: '无效参数' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (body.passwd && body.passwd != userData.passwd) {
|
||||||
|
reply.send({ state: false, error: '原始密码错误' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (user.autho === 'admin') {
|
||||||
|
await redis.set('CHATGPT:ADMIN_PASSWD', body.newPasswd)
|
||||||
|
} else if (user.autho === 'user') {
|
||||||
|
const dir = 'resources/ChatGPTCache/user'
|
||||||
|
const filename = `${user.user}.json`
|
||||||
|
const filepath = path.join(dir, filename)
|
||||||
|
fs.mkdirSync(dir, { recursive: true })
|
||||||
|
if (fs.existsSync(filepath)) {
|
||||||
|
fs.readFile(filepath, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
guobaLoginService = {
|
|
||||||
signToken: () => {return null}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (body.qq && body.passwd) {
|
|
||||||
const token = randomString(32)
|
|
||||||
if (body.qq == getUin() && await redis.get('CHATGPT:ADMIN_PASSWD') == body.passwd) {
|
|
||||||
const guobaToken = await guobaLoginService.signToken(body.qq)
|
|
||||||
AddUser({ user: body.qq, token: token, autho: 'admin' })
|
|
||||||
reply.setCookie('token', token, { path: '/' })
|
|
||||||
reply.send({ login: true, autho: 'admin', token: token, guobaToken: guobaToken, guoba: guobaAPI })
|
|
||||||
} else {
|
|
||||||
const user = await getUserData(body.qq)
|
|
||||||
if (user.passwd != '' && user.passwd === body.passwd) {
|
|
||||||
AddUser({ user: body.qq, token: token, autho: 'user' })
|
|
||||||
reply.setCookie('token', token, { path: '/' })
|
|
||||||
reply.send({ login: true, autho: 'user', token: token })
|
|
||||||
} else {
|
|
||||||
reply.send({ login: false, err: `用户名密码错误,如果忘记密码请私聊机器人输入 ${body.qq == getUin() ? '#修改管理密码' : '#修改用户密码'} 进行修改` })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (body.otp) {
|
|
||||||
const token = randomString(32)
|
|
||||||
const opt = await redis.get(`CHATGPT:SERVER_QUICK`)
|
|
||||||
if (opt && body.otp == opt) {
|
|
||||||
const guobaToken = await guobaLoginService.signToken(getUin())
|
|
||||||
AddUser({ user: getUin(), token: token, autho: 'admin' })
|
|
||||||
reply.setCookie('token', token, { path: '/' })
|
|
||||||
reply.send({ login: true, autho: 'admin', token: token, user: getUin(), guobaToken: guobaToken, guoba: guobaAPI })
|
|
||||||
} else {
|
|
||||||
reply.send({ login: false, err: `快捷登录代码错误,请检查后重试` })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reply.send({ login: false, err: '未输入用户名或密码' })
|
|
||||||
}
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
// 快速登录
|
|
||||||
fastify.post('/quick', async (request, reply) => {
|
|
||||||
const otp = randomString(6)
|
|
||||||
await redis.set(
|
|
||||||
`CHATGPT:SERVER_QUICK`,
|
|
||||||
otp,
|
|
||||||
{ EX: 60000 }
|
|
||||||
)
|
|
||||||
const master = (await getMasterQQ())[0]
|
|
||||||
if (Array.isArray(Bot.uin)) {
|
|
||||||
Bot.pickFriend(master).sendMsg(`收到工具箱快捷登录请求,1分钟内有效:${otp}`)
|
|
||||||
} else {
|
|
||||||
Bot.sendPrivateMsg(master, `收到工具箱快捷登录请求,1分钟内有效:${otp}`, false)
|
|
||||||
}
|
|
||||||
reply.send({ state: true })
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
// 检查用户是否存在
|
|
||||||
fastify.post('/verify', async (request, reply) => {
|
|
||||||
const token = request.cookies.token || request.body?.token || 'unknown'
|
|
||||||
const user = UserInfo(token)
|
|
||||||
if (!user || token === 'unknown') {
|
|
||||||
reply.send({
|
|
||||||
verify: false,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reply.send({
|
const config = JSON.parse(data)
|
||||||
verify: true,
|
config.passwd = body.newPasswd
|
||||||
user: user.user,
|
fs.writeFile(filepath, JSON.stringify(config), 'utf8', (err) => {
|
||||||
autho: user.autho,
|
if (err) {
|
||||||
version: 10016,
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
return reply
|
} else {
|
||||||
})
|
reply.send({ state: false, error: '错误的用户数据' })
|
||||||
// 获取用户数据
|
return
|
||||||
fastify.post('/userData', async (request, reply) => {
|
}
|
||||||
const token = request.cookies.token || request.body?.token || 'unknown'
|
}
|
||||||
let user = UserInfo(token)
|
reply.send({ state: true })
|
||||||
if (!user) user = { user: '' }
|
return reply
|
||||||
const userData = await getUserData(user.user)
|
})
|
||||||
reply.send({
|
|
||||||
chat: userData.chat || [],
|
|
||||||
mode: userData.mode || '',
|
|
||||||
cast: userData.cast || {
|
|
||||||
api: '', //API设定
|
|
||||||
bing: '', //必应设定
|
|
||||||
bing_resource: '', //必应扩展资料
|
|
||||||
slack: '', //Slack设定
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
// 删除用户
|
|
||||||
fastify.post('/deleteUser', async (request, reply) => {
|
|
||||||
const token = request.cookies.token || request.body?.token || 'unknown'
|
|
||||||
const user = UserInfo(token)
|
|
||||||
if (!user || user === 'unknown') {
|
|
||||||
reply.send({ state: false, error: '无效token' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const filepath = `resources/ChatGPTCache/user/${user.user}.json`
|
|
||||||
fs.unlinkSync(filepath)
|
|
||||||
reply.send({ state: true })
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
// 修改密码
|
|
||||||
fastify.post('/changePassword', async (request, reply) => {
|
|
||||||
const token = request.cookies.token || request.body?.token || 'unknown'
|
|
||||||
const user = UserInfo(token)
|
|
||||||
if (!user || user === 'unknown') {
|
|
||||||
reply.send({ state: false, error: '无效的用户信息' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const userData = await getUserData(user.user)
|
|
||||||
const body = request.body || {}
|
|
||||||
if (!body.newPasswd) {
|
|
||||||
reply.send({ state: false, error: '无效参数' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (body.passwd && body.passwd != userData.passwd) {
|
|
||||||
reply.send({ state: false, error: '原始密码错误' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (user.autho === 'admin') {
|
|
||||||
await redis.set('CHATGPT:ADMIN_PASSWD', body.newPasswd)
|
|
||||||
} else if (user.autho === 'user') {
|
|
||||||
const dir = 'resources/ChatGPTCache/user'
|
|
||||||
const filename = `${user.user}.json`
|
|
||||||
const filepath = path.join(dir, filename)
|
|
||||||
fs.mkdirSync(dir, { recursive: true })
|
|
||||||
if (fs.existsSync(filepath)) {
|
|
||||||
fs.readFile(filepath, 'utf8', (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const config = JSON.parse(data)
|
|
||||||
config.passwd = body.newPasswd
|
|
||||||
fs.writeFile(filepath, JSON.stringify(config), 'utf8', (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
reply.send({ state: false, error: '错误的用户数据' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reply.send({ state: true })
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default User
|
export default User
|
||||||
|
|
|
||||||
15
utils/bot.js
Normal file
15
utils/bot.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
export function getBots () {
|
||||||
|
if (Bot.uin === 88888) {
|
||||||
|
// 找适配器
|
||||||
|
let adapters = Bot.adapter
|
||||||
|
return adapters?.map(uin => Bot[uin])
|
||||||
|
} else if (Bot.adapter && Bot.adapter.length > 0) {
|
||||||
|
let bots = [Bot]
|
||||||
|
Bot.adapter.forEach(uin => {
|
||||||
|
bots.push(Bot[uin])
|
||||||
|
})
|
||||||
|
return bots
|
||||||
|
} else {
|
||||||
|
return [Bot]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,33 +1,34 @@
|
||||||
export async function getChatHistoryGroup (e, num) {
|
export async function getChatHistoryGroup (e, num) {
|
||||||
if (e.adapter === 'shamrock') {
|
// if (e.adapter === 'shamrock') {
|
||||||
return await e.group.getChatHistory(0, num, false)
|
// return await e.group.getChatHistory(0, num, false)
|
||||||
} else {
|
// } else {
|
||||||
let latestChats = await e.group.getChatHistory(0, 1)
|
let latestChats = await e.group.getChatHistory(0, 1)
|
||||||
if (latestChats.length > 0) {
|
if (latestChats.length > 0) {
|
||||||
let latestChat = latestChats[0]
|
let latestChat = latestChats[0]
|
||||||
if (latestChat) {
|
if (latestChat) {
|
||||||
let seq = latestChat.seq
|
let seq = latestChat.seq || latestChat.message_id
|
||||||
let chats = []
|
let chats = []
|
||||||
while (chats.length < num) {
|
while (chats.length < num) {
|
||||||
let chatHistory = await e.group.getChatHistory(seq, 20)
|
let chatHistory = await e.group.getChatHistory(seq, 20)
|
||||||
chats.push(...chatHistory)
|
chats.push(...chatHistory)
|
||||||
}
|
seq = chatHistory[0].seq || chatHistory[0].message_id
|
||||||
chats = chats.slice(0, num)
|
|
||||||
try {
|
|
||||||
let mm = await e.group.getMemberMap()
|
|
||||||
chats.forEach(chat => {
|
|
||||||
let sender = mm.get(chat.sender.user_id)
|
|
||||||
if (sender) {
|
|
||||||
chat.sender = sender
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn(err)
|
|
||||||
}
|
|
||||||
// console.log(chats)
|
|
||||||
return chats
|
|
||||||
}
|
}
|
||||||
|
chats = chats.slice(0, num)
|
||||||
|
try {
|
||||||
|
let mm = await e.group.getMemberMap()
|
||||||
|
chats.forEach(chat => {
|
||||||
|
let sender = mm.get(chat.sender.user_id)
|
||||||
|
if (sender) {
|
||||||
|
chat.sender = sender
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(err)
|
||||||
|
}
|
||||||
|
// console.log(chats)
|
||||||
|
return chats
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ export class ClaudeAIClient {
|
||||||
attachments,
|
attachments,
|
||||||
completion: {
|
completion: {
|
||||||
incremental: true,
|
incremental: true,
|
||||||
model: 'claude-2',
|
model: 'claude-2.1',
|
||||||
prompt: text,
|
prompt: text,
|
||||||
timezone: 'Asia/Hong_Kong'
|
timezone: 'Asia/Hong_Kong'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -820,6 +820,7 @@ export async function getImageOcrText (e) {
|
||||||
// logger.warn('resultArr', resultArr)
|
// logger.warn('resultArr', resultArr)
|
||||||
return resultArr
|
return resultArr
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.warn('OCR失败,可能使用的适配器不支持OCR')
|
||||||
return false
|
return false
|
||||||
// logger.error(err)
|
// logger.error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -847,6 +848,7 @@ export function getMaxModelTokens (model = 'gpt-3.5-turbo') {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUin (e) {
|
export function getUin (e) {
|
||||||
|
if (e?.self_id) return e.self_id
|
||||||
if (e?.bot?.uin) return e.bot.uin
|
if (e?.bot?.uin) return e.bot.uin
|
||||||
if (Array.isArray(Bot.uin)) {
|
if (Array.isArray(Bot.uin)) {
|
||||||
if (Config.trssBotUin && Bot.uin.indexOf(Config.trssBotUin) > -1) { return Config.trssBotUin } else {
|
if (Config.trssBotUin && Bot.uin.indexOf(Config.trssBotUin) > -1) { return Config.trssBotUin } else {
|
||||||
|
|
@ -1051,6 +1053,7 @@ export function isPureText (filename) {
|
||||||
/**
|
/**
|
||||||
* 从文件中提取文本内容
|
* 从文件中提取文本内容
|
||||||
* @param fileMsgElem MessageElem
|
* @param fileMsgElem MessageElem
|
||||||
|
* @param e
|
||||||
* @returns {Promise<{}>} 提取的文本内容和文件名
|
* @returns {Promise<{}>} 提取的文本内容和文件名
|
||||||
*/
|
*/
|
||||||
export async function extractContentFromFile (fileMsgElem, e) {
|
export async function extractContentFromFile (fileMsgElem, e) {
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ const defaultConfig = {
|
||||||
serpSource: 'ikechan8370',
|
serpSource: 'ikechan8370',
|
||||||
extraUrl: 'https://cpe.ikechan8370.com',
|
extraUrl: 'https://cpe.ikechan8370.com',
|
||||||
smartMode: false,
|
smartMode: false,
|
||||||
bingCaptchaOneShotUrl: 'http://bingcaptcha.ikechan8370.com/bing',
|
bingCaptchaOneShotUrl: '',
|
||||||
// claude2
|
// claude2
|
||||||
claudeAIOrganizationId: '',
|
claudeAIOrganizationId: '',
|
||||||
claudeAISessionKey: '',
|
claudeAISessionKey: '',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { Config } from '../config.js'
|
||||||
import { createParser } from 'eventsource-parser'
|
import { createParser } from 'eventsource-parser'
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
import WebSocket from 'ws'
|
import WebSocket from 'ws'
|
||||||
|
import { createHmac } from 'crypto'
|
||||||
|
|
||||||
const referer = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNuL2NoYXQ/aWQ9')
|
const referer = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNuL2NoYXQ/aWQ9')
|
||||||
const origin = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNu')
|
const origin = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNu')
|
||||||
|
|
@ -14,13 +15,7 @@ try {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn('未安装form-data,无法使用星火模式')
|
logger.warn('未安装form-data,无法使用星火模式')
|
||||||
}
|
}
|
||||||
let crypto
|
async function getKeyv () {
|
||||||
try {
|
|
||||||
crypto = (await import('crypto')).default
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('未安装crypto,无法使用星火api模式')
|
|
||||||
}
|
|
||||||
async function getKeyv() {
|
|
||||||
let Keyv
|
let Keyv
|
||||||
try {
|
try {
|
||||||
Keyv = (await import('keyv')).default
|
Keyv = (await import('keyv')).default
|
||||||
|
|
@ -30,7 +25,7 @@ async function getKeyv() {
|
||||||
return Keyv
|
return Keyv
|
||||||
}
|
}
|
||||||
export default class XinghuoClient {
|
export default class XinghuoClient {
|
||||||
constructor(opts) {
|
constructor (opts) {
|
||||||
this.cache = opts.cache
|
this.cache = opts.cache
|
||||||
this.ssoSessionId = opts.ssoSessionId
|
this.ssoSessionId = opts.ssoSessionId
|
||||||
this.headers = {
|
this.headers = {
|
||||||
|
|
@ -41,7 +36,7 @@ export default class XinghuoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apiErrorInfo(code) {
|
apiErrorInfo (code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 10000: return '升级为ws出现错误'
|
case 10000: return '升级为ws出现错误'
|
||||||
case 10001: return '通过ws读取用户的消息出错'
|
case 10001: return '通过ws读取用户的消息出错'
|
||||||
|
|
@ -74,7 +69,7 @@ export default class XinghuoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async initCache() {
|
async initCache () {
|
||||||
if (!this.conversationsCache) {
|
if (!this.conversationsCache) {
|
||||||
const cacheOptions = this.cache || {}
|
const cacheOptions = this.cache || {}
|
||||||
cacheOptions.namespace = cacheOptions.namespace || 'xh'
|
cacheOptions.namespace = cacheOptions.namespace || 'xh'
|
||||||
|
|
@ -83,38 +78,37 @@ export default class XinghuoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getWsUrl() {
|
async getWsUrl () {
|
||||||
if (!crypto) return false
|
|
||||||
const APISecret = Config.xhAPISecret
|
const APISecret = Config.xhAPISecret
|
||||||
const APIKey = Config.xhAPIKey
|
const APIKey = Config.xhAPIKey
|
||||||
let APILink = '/v1.1/chat'
|
let APILink = '/v1.1/chat'
|
||||||
if (Config.xhmode == 'apiv2') {
|
if (Config.xhmode === 'apiv2') {
|
||||||
APILink = '/v2.1/chat'
|
APILink = '/v2.1/chat'
|
||||||
} else if (Config.xhmode == 'apiv3') {
|
} else if (Config.xhmode === 'apiv3') {
|
||||||
APILink = '/v3.1/chat'
|
APILink = '/v3.1/chat'
|
||||||
}
|
}
|
||||||
const date = new Date().toGMTString()
|
const date = new Date().toGMTString()
|
||||||
const algorithm = 'hmac-sha256'
|
const algorithm = 'hmac-sha256'
|
||||||
const headers = 'host date request-line'
|
const headers = 'host date request-line'
|
||||||
const signatureOrigin = `host: spark-api.xf-yun.com\ndate: ${date}\nGET ${APILink} HTTP/1.1`
|
const signatureOrigin = `host: spark-api.xf-yun.com\ndate: ${date}\nGET ${APILink} HTTP/1.1`
|
||||||
const hmac = crypto.createHmac('sha256', APISecret)
|
const hmac = createHmac('sha256', APISecret)
|
||||||
hmac.update(signatureOrigin)
|
hmac.update(signatureOrigin)
|
||||||
const signature = hmac.digest('base64')
|
const signature = hmac.digest('base64')
|
||||||
const authorizationOrigin = `api_key="${APIKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
|
const authorizationOrigin = `api_key="${APIKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
|
||||||
const authorization = Buffer.from(authorizationOrigin).toString('base64')
|
const authorization = Buffer.from(authorizationOrigin).toString('base64')
|
||||||
const v = {
|
const v = {
|
||||||
authorization: authorization,
|
authorization,
|
||||||
date: date,
|
date,
|
||||||
host: "spark-api.xf-yun.com"
|
host: 'spark-api.xf-yun.com'
|
||||||
}
|
}
|
||||||
const url = `wss://spark-api.xf-yun.com${APILink}?${Object.keys(v).map(key => `${key}=${v[key]}`).join('&')}`
|
const url = `wss://spark-api.xf-yun.com${APILink}?${Object.keys(v).map(key => `${key}=${v[key]}`).join('&')}`
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadImage(url) {
|
async uploadImage (url) {
|
||||||
// 获取图片
|
// 获取图片
|
||||||
let response = await fetch(url, {
|
let response = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET'
|
||||||
})
|
})
|
||||||
const blob = await response.blob()
|
const blob = await response.blob()
|
||||||
const arrayBuffer = await blob.arrayBuffer()
|
const arrayBuffer = await blob.arrayBuffer()
|
||||||
|
|
@ -125,7 +119,7 @@ export default class XinghuoClient {
|
||||||
const respOss = await fetch('https://xinghuo.xfyun.cn/iflygpt/oss/sign', {
|
const respOss = await fetch('https://xinghuo.xfyun.cn/iflygpt/oss/sign', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: 'ssoSessionId=' + this.ssoSessionId + ';',
|
Cookie: 'ssoSessionId=' + this.ssoSessionId + ';'
|
||||||
},
|
},
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
|
|
@ -167,7 +161,7 @@ export default class XinghuoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async apiMessage(prompt, chatId, ePrompt = []) {
|
async apiMessage (prompt, chatId, ePrompt = []) {
|
||||||
if (!chatId) chatId = (Math.floor(Math.random() * 1000000) + 100000).toString()
|
if (!chatId) chatId = (Math.floor(Math.random() * 1000000) + 100000).toString()
|
||||||
|
|
||||||
// 初始化缓存
|
// 初始化缓存
|
||||||
|
|
@ -180,12 +174,9 @@ export default class XinghuoClient {
|
||||||
|
|
||||||
// 获取ws链接
|
// 获取ws链接
|
||||||
const wsUrl = Config.xhmode == 'assistants' ? Config.xhAssistants : await this.getWsUrl()
|
const wsUrl = Config.xhmode == 'assistants' ? Config.xhAssistants : await this.getWsUrl()
|
||||||
if (!wsUrl) throw new Error('缺少依赖:crypto。请安装依赖后重试')
|
if (!wsUrl) throw new Error('获取ws链接失败')
|
||||||
let domain = 'general'
|
let domain = 'general'
|
||||||
if (Config.xhmode == 'apiv2')
|
if (Config.xhmode == 'apiv2') { domain = 'generalv2' } else if (Config.xhmode == 'apiv3') { domain = 'generalv3' }
|
||||||
domain = "generalv2"
|
|
||||||
else if (Config.xhmode == 'apiv3')
|
|
||||||
domain = "generalv3"
|
|
||||||
// 编写消息内容
|
// 编写消息内容
|
||||||
const wsSendData = {
|
const wsSendData = {
|
||||||
header: {
|
header: {
|
||||||
|
|
@ -194,7 +185,7 @@ export default class XinghuoClient {
|
||||||
},
|
},
|
||||||
parameter: {
|
parameter: {
|
||||||
chat: {
|
chat: {
|
||||||
domain: domain,
|
domain,
|
||||||
temperature: Config.xhTemperature, // 核采样阈值
|
temperature: Config.xhTemperature, // 核采样阈值
|
||||||
max_tokens: Config.xhMaxTokens, // tokens最大长度
|
max_tokens: Config.xhMaxTokens, // tokens最大长度
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
|
|
@ -203,10 +194,10 @@ export default class XinghuoClient {
|
||||||
},
|
},
|
||||||
payload: {
|
payload: {
|
||||||
message: {
|
message: {
|
||||||
"text": [
|
text: [
|
||||||
...ePrompt,
|
...ePrompt,
|
||||||
...conversation.messages,
|
...conversation.messages,
|
||||||
{ "role": "user", "content": prompt }
|
{ role: 'user', content: prompt }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,8 +220,8 @@ export default class XinghuoClient {
|
||||||
const half = Math.floor(conversation.messages.length / 2)
|
const half = Math.floor(conversation.messages.length / 2)
|
||||||
conversation.messages.splice(0, half)
|
conversation.messages.splice(0, half)
|
||||||
await this.conversationsCache.set(conversationKey, conversation)
|
await this.conversationsCache.set(conversationKey, conversation)
|
||||||
resolve({
|
resolve({
|
||||||
id: (Math.floor(Math.random() * 1000000) + 100000).toString() ,
|
id: (Math.floor(Math.random() * 1000000) + 100000).toString(),
|
||||||
response: '对话以达到上限,已自动清理对话,请重试'
|
response: '对话以达到上限,已自动清理对话,请重试'
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -256,8 +247,8 @@ export default class XinghuoClient {
|
||||||
conversation.messages.splice(0, half)
|
conversation.messages.splice(0, half)
|
||||||
}
|
}
|
||||||
await this.conversationsCache.set(conversationKey, conversation)
|
await this.conversationsCache.set(conversationKey, conversation)
|
||||||
resolve({
|
resolve({
|
||||||
id: chatId ,
|
id: chatId,
|
||||||
response: resMessage
|
response: resMessage
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +262,7 @@ export default class XinghuoClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async webMessage(prompt, chatId, botId) {
|
async webMessage (prompt, chatId, botId) {
|
||||||
if (!FormData) {
|
if (!FormData) {
|
||||||
throw new Error('缺少依赖:form-data。请安装依赖后重试')
|
throw new Error('缺少依赖:form-data。请安装依赖后重试')
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +272,7 @@ export default class XinghuoClient {
|
||||||
formData.append('clientType', '2')
|
formData.append('clientType', '2')
|
||||||
formData.append('chatId', chatId)
|
formData.append('chatId', chatId)
|
||||||
if (prompt.image) {
|
if (prompt.image) {
|
||||||
prompt.text = prompt.text.replace("[图片]", "") // 清理消息中中首个被使用的图片
|
prompt.text = prompt.text.replace('[图片]', '') // 清理消息中中首个被使用的图片
|
||||||
const imgdata = await this.uploadImage(prompt.image)
|
const imgdata = await this.uploadImage(prompt.image)
|
||||||
if (imgdata) {
|
if (imgdata) {
|
||||||
formData.append('fileUrl', imgdata.url)
|
formData.append('fileUrl', imgdata.url)
|
||||||
|
|
@ -313,7 +304,7 @@ export default class XinghuoClient {
|
||||||
logger.error('星火statusCode:' + statusCode)
|
logger.error('星火statusCode:' + statusCode)
|
||||||
}
|
}
|
||||||
let response = ''
|
let response = ''
|
||||||
function onMessage(data) {
|
function onMessage (data) {
|
||||||
// console.log(data)
|
// console.log(data)
|
||||||
if (data === '<end>') {
|
if (data === '<end>') {
|
||||||
return resolve({
|
return resolve({
|
||||||
|
|
@ -380,7 +371,7 @@ export default class XinghuoClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMessage(prompt, option) {
|
async sendMessage (prompt, option) {
|
||||||
let chatId = option?.chatId
|
let chatId = option?.chatId
|
||||||
let image = option?.image
|
let image = option?.image
|
||||||
|
|
||||||
|
|
@ -396,9 +387,9 @@ export default class XinghuoClient {
|
||||||
logger.warn('星火设定序列化失败,本次对话不附带设定')
|
logger.warn('星火设定序列化失败,本次对话不附带设定')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Prompt = Config.xhPrompt ? [{ "role": "user", "content": Config.xhPrompt }] : []
|
Prompt = Config.xhPrompt ? [{ role: 'user', content: Config.xhPrompt }] : []
|
||||||
}
|
}
|
||||||
if(Config.xhPromptEval) {
|
if (Config.xhPromptEval) {
|
||||||
Prompt.forEach(obj => {
|
Prompt.forEach(obj => {
|
||||||
try {
|
try {
|
||||||
obj.content = obj.content.replace(/{{(.*?)}}/g, (match, variable) => {
|
obj.content = obj.content.replace(/{{(.*?)}}/g, (match, variable) => {
|
||||||
|
|
@ -427,7 +418,7 @@ export default class XinghuoClient {
|
||||||
if (!chatId) {
|
if (!chatId) {
|
||||||
chatId = (await this.createChatList()).chatListId
|
chatId = (await this.createChatList()).chatListId
|
||||||
}
|
}
|
||||||
let { response } = await this.webMessage({ text: prompt, image: image }, chatId, botId)
|
let { response } = await this.webMessage({ text: prompt, image }, chatId, botId)
|
||||||
// logger.info(response)
|
// logger.info(response)
|
||||||
// let responseText = atob(response)
|
// let responseText = atob(response)
|
||||||
// 处理图片
|
// 处理图片
|
||||||
|
|
@ -451,14 +442,14 @@ export default class XinghuoClient {
|
||||||
return {
|
return {
|
||||||
conversationId: chatId,
|
conversationId: chatId,
|
||||||
text: response,
|
text: response,
|
||||||
images: images
|
images
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('星火模式错误')
|
throw new Error('星火模式错误')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createChatList(bot = false) {
|
async createChatList (bot = false) {
|
||||||
let createChatListRes = await fetch(createChatUrl, {
|
let createChatListRes = await fetch(createChatUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: Object.assign(this.headers, {
|
headers: Object.assign(this.headers, {
|
||||||
|
|
@ -487,6 +478,6 @@ export default class XinghuoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function atob(s) {
|
function atob (s) {
|
||||||
return Buffer.from(s, 'base64').toString()
|
return Buffer.from(s, 'base64').toString()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue