mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 13:57:10 +00:00
Merge branch 'ikechan8370:v2' into v2
This commit is contained in:
commit
9723e6db17
92 changed files with 4289 additions and 1073 deletions
24
README.md
24
README.md
|
|
@ -1,5 +1,5 @@
|
||||||

|

|
||||||
<div align=center> <h1>云崽QQ机器人的ChatGPT插件</h1> </div>
|
<div align=center> <h1>云崽系机器人的智能聊天插件</h1> </div>
|
||||||
<div align=center>
|
<div align=center>
|
||||||
|
|
||||||
<img src ="https://img.shields.io/github/issues/ikechan8370/chatgpt-plugin?logo=github"/>
|
<img src ="https://img.shields.io/github/issues/ikechan8370/chatgpt-plugin?logo=github"/>
|
||||||
|
|
@ -18,9 +18,9 @@
|
||||||
|
|
||||||
### 推荐的相关文档和参考资料
|
### 推荐的相关文档和参考资料
|
||||||
本README
|
本README
|
||||||
[手册](https://chatgptplugin.ikechan8370.com/)
|
[手册](https://yunzai.chat)
|
||||||
[文档1(建设中)](https://chatgpt-docs.err0r.top/)
|
[文档1(建设中)](https://chatgpt-docs.err0r.top/)
|
||||||
[插件常见问题(鹤望兰版)](https://www.wolai.com/4FCxxWAdjbrHF29MCJmAQK)
|
[插件常见问题(鹤望兰版)](https://chatgptplugin.ikechan8370.com/guide/)
|
||||||
[Yunzai常见问题(LUCK小运版)](https://www.wolai.com/oA43vuW71aBnv7UsEysn4T)
|
[Yunzai常见问题(LUCK小运版)](https://www.wolai.com/oA43vuW71aBnv7UsEysn4T)
|
||||||
[憨憨博客](https://blog.hanhanz.top/)
|
[憨憨博客](https://blog.hanhanz.top/)
|
||||||
|
|
||||||
|
|
@ -41,6 +41,8 @@
|
||||||
* 2023-05-29 支持gpt-4 API.必应无需cookie即可对话(Sydney和自定义模式)
|
* 2023-05-29 支持gpt-4 API.必应无需cookie即可对话(Sydney和自定义模式)
|
||||||
* 2023-07 支持智能模式,机器人可以实现禁言、群名片/头衔(需给机器人管理员/群主)、分享音乐视频、主动发音频、对接ap,sr和喵喵等插件、联网搜索等,需api模式0613系列模型。智能模式所需的额外api和搜索api分别可以参考[chatgpt-plugin-extras](https://github.com/ikechan8370/chatgpt-plugin-extras) 和 [search-api](https://github.com/ikechan8370/search-api) 自行搭建,其中后者提供了一个公益版本,前者可使用[huggingface](https://huggingface.co/spaces/ikechan8370/cp-extra)部署
|
* 2023-07 支持智能模式,机器人可以实现禁言、群名片/头衔(需给机器人管理员/群主)、分享音乐视频、主动发音频、对接ap,sr和喵喵等插件、联网搜索等,需api模式0613系列模型。智能模式所需的额外api和搜索api分别可以参考[chatgpt-plugin-extras](https://github.com/ikechan8370/chatgpt-plugin-extras) 和 [search-api](https://github.com/ikechan8370/search-api) 自行搭建,其中后者提供了一个公益版本,前者可使用[huggingface](https://huggingface.co/spaces/ikechan8370/cp-extra)部署
|
||||||
* 2023-09-10 支持来自claude.ai的claude-2模型
|
* 2023-09-10 支持来自claude.ai的claude-2模型
|
||||||
|
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
|
||||||
|
* 2023-10-25 增加支持通义千问官方API
|
||||||
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
||||||
|
|
||||||
## 版本要求
|
## 版本要求
|
||||||
|
|
@ -50,16 +52,6 @@ Node.js >= 18 / Node.js >= 14(with node-fetch)
|
||||||
## 安装与使用方法
|
## 安装与使用方法
|
||||||
|
|
||||||
### 安装
|
### 安装
|
||||||
|
|
||||||
在安装之前,请先判断自己需要使用哪种模式,本插件支持官方API/第三方API/~~浏览器~~/必应四种模式。也可以选择**我全都要**(通过qq发送命令`#chatgpt切换浏览器/API/API3/Bing`实时切换)
|
|
||||||
|
|
||||||
> #### API模式和浏览器模式如何选择?
|
|
||||||
>
|
|
||||||
> * API模式会调用OpenAI官方提供的gpt-3.5-turbo API,ChatGPT官网同款模型,只需要提供API Key。一般情况下,该种方式响应速度更快,可配置项多,且不会像chatGPT官网一样总出现不可用的现象,但注意API调用是收费的,新用户有18美元试用金可用于支付,价格为`$0.0020/ 1K tokens`。(问题和回答**加起来**算token)
|
|
||||||
> * API3模式会调用第三方提供的官网反代API,他会帮你绕过CF防护,需要提供ChatGPT的Token。效果与官网和浏览器一致,但稳定性不一定。发送#chatgpt设置token来设置token。
|
|
||||||
> * (Deprecated)浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站,使得获得和官方以及API2模式一模一样的回复质量,同时保证安全性。缺点是本方法对环境要求较高,需要提供桌面环境和一个可用的代理(能够访问ChatGPT的IP地址),且响应速度不如API,而且高峰期容易无法使用。一般作为API3的下位替代。
|
|
||||||
> * 必应(Bing)将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的Bing登录Cookie方可使用。强烈推荐
|
|
||||||
|
|
||||||
1. 进入 Yunzai根目录
|
1. 进入 Yunzai根目录
|
||||||
|
|
||||||
2. 请将 chatgpt-plugin 放置在 Yunzai-Bot 的 plugins 目录下
|
2. 请将 chatgpt-plugin 放置在 Yunzai-Bot 的 plugins 目录下
|
||||||
|
|
@ -81,12 +73,8 @@ pnpm i
|
||||||
|
|
||||||
如果是手工下载的 zip 压缩包,请将解压后的 chatgpt-plugin 文件夹(请删除压缩自带的-master或版本号后缀)放置在 Yunzai-Bot 目录下的 plugins 文件夹内
|
如果是手工下载的 zip 压缩包,请将解压后的 chatgpt-plugin 文件夹(请删除压缩自带的-master或版本号后缀)放置在 Yunzai-Bot 目录下的 plugins 文件夹内
|
||||||
|
|
||||||
> ~~浏览器模式仅为备选,如您需要使用浏览器模式,您还需要有**桌面环境**,优先级建议:API≈必应>API3>浏览器~~\
|
|
||||||
> ~~2.20更新:必应被大削,变得蠢了,建议还是API/API3优先~~\
|
|
||||||
> 4.2更新:必应站起来了,必应天下第一。建议都用必应,别用API/API3了。浏览器模式除非极其特殊的需求否则强烈建议不使用,已经不维护了。
|
|
||||||
|
|
||||||
3. 修改配置
|
3. 修改配置
|
||||||
**本插件配置项比较多,强烈建议使用后台面板或[锅巴面板](https://github.com/guoba-yunzai/Guoba-Plugin)修改**
|
**本插件配置项比较多,强烈建议使用后台工具箱或[锅巴面板](https://github.com/guoba-yunzai/Guoba-Plugin)修改**
|
||||||
|
|
||||||
或者创建和编辑config/config.json文件。
|
或者创建和编辑config/config.json文件。
|
||||||
|
|
||||||
|
|
|
||||||
321
apps/chat.js
321
apps/chat.js
|
|
@ -9,23 +9,29 @@ import SydneyAIClient from '../utils/SydneyAIClient.js'
|
||||||
import {PoeClient} from '../utils/poe/index.js'
|
import {PoeClient} from '../utils/poe/index.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 Version from '../utils/version.js'
|
||||||
import {
|
import {
|
||||||
render,
|
|
||||||
renderUrl,
|
|
||||||
getMessageById,
|
|
||||||
makeForwardMsg,
|
|
||||||
upsertMessage,
|
|
||||||
randomString,
|
|
||||||
completeJSON,
|
completeJSON,
|
||||||
isImage,
|
extractContentFromFile,
|
||||||
getUserData,
|
formatDate,
|
||||||
|
formatDate2,
|
||||||
|
generateAudio,
|
||||||
getDefaultReplySetting,
|
getDefaultReplySetting,
|
||||||
isCN,
|
|
||||||
getMasterQQ,
|
|
||||||
getUserReplySetting,
|
|
||||||
getImageOcrText,
|
getImageOcrText,
|
||||||
getImg,
|
getImg,
|
||||||
getMaxModelTokens, formatDate, generateAudio, formatDate2, mkdirs
|
getMasterQQ,
|
||||||
|
getMaxModelTokens,
|
||||||
|
getMessageById,
|
||||||
|
getUin,
|
||||||
|
getUserData,
|
||||||
|
getUserReplySetting,
|
||||||
|
isCN,
|
||||||
|
isImage,
|
||||||
|
makeForwardMsg,
|
||||||
|
randomString,
|
||||||
|
render,
|
||||||
|
renderUrl,
|
||||||
|
upsertMessage
|
||||||
} from '../utils/common.js'
|
} from '../utils/common.js'
|
||||||
import {ChatGPTPuppeteer} from '../utils/browser.js'
|
import {ChatGPTPuppeteer} from '../utils/browser.js'
|
||||||
import {KeyvFile} from 'keyv-file'
|
import {KeyvFile} from 'keyv-file'
|
||||||
|
|
@ -69,8 +75,9 @@ import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessage
|
||||||
import {SetTitleTool} from '../utils/tools/SetTitleTool.js'
|
import {SetTitleTool} from '../utils/tools/SetTitleTool.js'
|
||||||
import {solveCaptchaOneShot} from '../utils/bingCaptcha.js'
|
import {solveCaptchaOneShot} from '../utils/bingCaptcha.js'
|
||||||
import {ClaudeAIClient} from '../utils/claude.ai/index.js'
|
import {ClaudeAIClient} from '../utils/claude.ai/index.js'
|
||||||
import fs from 'fs'
|
|
||||||
import {getProxy} from '../utils/proxy.js'
|
import {getProxy} from '../utils/proxy.js'
|
||||||
|
import {QwenApi} from '../utils/alibaba/qwen-api.js'
|
||||||
|
import {getChatHistoryGroup} from '../utils/chat.js'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await import('@azure/openai')
|
await import('@azure/openai')
|
||||||
|
|
@ -178,6 +185,12 @@ export class chatgpt extends plugin {
|
||||||
reg: '^#星火(搜索|查找)助手',
|
reg: '^#星火(搜索|查找)助手',
|
||||||
fnc: 'searchxhBot'
|
fnc: 'searchxhBot'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/** 命令正则匹配 */
|
||||||
|
reg: '^#qwen[sS]*',
|
||||||
|
/** 执行方法 */
|
||||||
|
fnc: 'qwen'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/** 命令正则匹配 */
|
/** 命令正则匹配 */
|
||||||
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
||||||
|
|
@ -315,7 +328,7 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
const isAtMode = Config.toggleMode === 'at'
|
const isAtMode = Config.toggleMode === 'at'
|
||||||
if (isAtMode) ats = ats.filter(item => item.qq !== Bot.uin)
|
if (isAtMode) ats = ats.filter(item => item.qq !== getUin(e))
|
||||||
if (ats.length === 0) {
|
if (ats.length === 0) {
|
||||||
if (use === 'api3') {
|
if (use === 'api3') {
|
||||||
await redis.del(`CHATGPT:QQ_CONVERSATION:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`)
|
await redis.del(`CHATGPT:QQ_CONVERSATION:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`)
|
||||||
|
|
@ -365,6 +378,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`)
|
await redis.del(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`)
|
||||||
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'qwen') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply('当前没有开启对话', true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
||||||
|
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
||||||
|
}
|
||||||
} else if (use === 'bing') {
|
} else if (use === 'bing') {
|
||||||
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`)
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`)
|
||||||
if (!c) {
|
if (!c) {
|
||||||
|
|
@ -426,6 +447,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS:${qq}`)
|
await redis.del(`CHATGPT:CONVERSATIONS:${qq}`)
|
||||||
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'qwen') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply(`当前${atUser}没有开启对话`, true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
||||||
|
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
||||||
|
}
|
||||||
} else if (use === 'bing') {
|
} else if (use === 'bing') {
|
||||||
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`)
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`)
|
||||||
if (!c) {
|
if (!c) {
|
||||||
|
|
@ -537,6 +566,18 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'qwen': {
|
||||||
|
let qcs = await redis.keys('CHATGPT:CONVERSATIONS_QWEN:*')
|
||||||
|
for (let i = 0; i < qcs.length; i++) {
|
||||||
|
await redis.del(qcs[i])
|
||||||
|
// todo clean last message id
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info('delete qwen conversation bind: ' + qcs[i])
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +589,7 @@ export class chatgpt extends plugin {
|
||||||
await this.reply('本功能当前仅支持API3模式', true)
|
await this.reply('本功能当前仅支持API3模式', true)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (ats.length === 0 || (ats.length === 1 && e.atme)) {
|
if (ats.length === 0 || (ats.length === 1 && (e.atme || e.atBot))) {
|
||||||
let conversationId = _.trimStart(e.msg, '#chatgpt删除对话').trim()
|
let conversationId = _.trimStart(e.msg, '#chatgpt删除对话').trim()
|
||||||
if (!conversationId) {
|
if (!conversationId) {
|
||||||
await this.reply('指令格式错误,请同时加上对话id或@某人以删除他当前进行的对话', true)
|
await this.reply('指令格式错误,请同时加上对话id或@某人以删除他当前进行的对话', true)
|
||||||
|
|
@ -756,19 +797,21 @@ export class chatgpt extends plugin {
|
||||||
* #chatgpt
|
* #chatgpt
|
||||||
*/
|
*/
|
||||||
async chatgpt (e) {
|
async chatgpt (e) {
|
||||||
|
let msg = Version.isTrss ? e.msg : e.raw_message
|
||||||
let prompt
|
let prompt
|
||||||
if (this.toggleMode === 'at') {
|
if (this.toggleMode === 'at') {
|
||||||
if (!e.raw_message || e.msg?.startsWith('#')) {
|
if (!msg || e.msg?.startsWith('#')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (e.isGroup && !e.atme) {
|
if ((e.isGroup || e.group_id) && !(e.atme || e.atBot)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (e.user_id == Bot.uin) return false
|
if (e.user_id == getUin(e)) return false
|
||||||
prompt = e.raw_message.trim()
|
prompt = msg.trim()
|
||||||
|
try {
|
||||||
if (e.isGroup && typeof this.e.group.getMemberMap === 'function') {
|
if (e.isGroup && typeof this.e.group.getMemberMap === 'function') {
|
||||||
let mm = await this.e.group.getMemberMap()
|
let mm = await this.e.group.getMemberMap()
|
||||||
let me = mm.get(Bot.uin)
|
let me = mm.get(getUin(e)) || {}
|
||||||
let card = me.card
|
let card = me.card
|
||||||
let nickname = me.nickname
|
let nickname = me.nickname
|
||||||
if (nickname && card) {
|
if (nickname && card) {
|
||||||
|
|
@ -795,9 +838,12 @@ export class chatgpt extends plugin {
|
||||||
prompt = prompt.replace(`@${card}`, '').trim()
|
prompt = prompt.replace(`@${card}`, '').trim()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat')
|
logger.mark('艾特别人了,没艾特我,忽略#chat')
|
||||||
}
|
}
|
||||||
|
|
@ -1023,6 +1069,10 @@ export class chatgpt extends plugin {
|
||||||
key = `CHATGPT:CONVERSATIONS_AZURE:${e.sender.user_id}`
|
key = `CHATGPT:CONVERSATIONS_AZURE:${e.sender.user_id}`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'qwen': {
|
||||||
|
key = `CHATGPT:CONVERSATIONS_QWEN:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let ctime = new Date()
|
let ctime = new Date()
|
||||||
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
||||||
|
|
@ -1053,9 +1103,7 @@ export class chatgpt extends plugin {
|
||||||
logger.mark({ conversation })
|
logger.mark({ conversation })
|
||||||
}
|
}
|
||||||
let chatMessage = await this.sendMessage(prompt, conversation, use, e)
|
let chatMessage = await this.sendMessage(prompt, conversation, use, e)
|
||||||
if (chatMessage.image) {
|
if (chatMessage?.noMsg) {
|
||||||
this.setContext('solveBingCaptcha', false, 60)
|
|
||||||
await e.reply([chatMessage.text, segment.image(`base64://${chatMessage.image}`)])
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// 处理星火和bard图片
|
// 处理星火和bard图片
|
||||||
|
|
@ -1311,7 +1359,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat1')
|
logger.mark('艾特别人了,没艾特我,忽略#chat1')
|
||||||
}
|
}
|
||||||
|
|
@ -1330,7 +1378,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat3')
|
logger.mark('艾特别人了,没艾特我,忽略#chat3')
|
||||||
}
|
}
|
||||||
|
|
@ -1349,7 +1397,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chatglm')
|
logger.mark('艾特别人了,没艾特我,忽略#chatglm')
|
||||||
}
|
}
|
||||||
|
|
@ -1368,7 +1416,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#bing')
|
logger.mark('艾特别人了,没艾特我,忽略#bing')
|
||||||
}
|
}
|
||||||
|
|
@ -1387,7 +1435,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude2')
|
logger.mark('艾特别人了,没艾特我,忽略#claude2')
|
||||||
}
|
}
|
||||||
|
|
@ -1406,7 +1454,7 @@ export class chatgpt extends plugin {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude')
|
logger.mark('艾特别人了,没艾特我,忽略#claude')
|
||||||
}
|
}
|
||||||
|
|
@ -1420,12 +1468,31 @@ export class chatgpt extends plugin {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async qwen (e) {
|
||||||
|
if (!Config.allowOtherMode) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let prompt = _.replace(e.raw_message.trimStart(), '#qwen', '').trim()
|
||||||
|
if (prompt.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await this.abstractChat(e, prompt, 'qwen')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
async xh (e) {
|
async xh (e) {
|
||||||
if (!Config.allowOtherMode) {
|
if (!Config.allowOtherMode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
||||||
}
|
}
|
||||||
|
|
@ -1464,7 +1531,7 @@ export class chatgpt extends plugin {
|
||||||
chatViewBotName: Config.chatViewBotName || '',
|
chatViewBotName: Config.chatViewBotName || '',
|
||||||
entry: cacheData.file,
|
entry: cacheData.file,
|
||||||
userImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${e.sender.user_id}`,
|
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}`,
|
botImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${getUin(e)}`,
|
||||||
cacheHost: Config.serverHost,
|
cacheHost: Config.serverHost,
|
||||||
qq: e.sender.user_id
|
qq: e.sender.user_id
|
||||||
})
|
})
|
||||||
|
|
@ -1573,33 +1640,33 @@ export class chatgpt extends plugin {
|
||||||
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
|
||||||
opt.botName = e.isGroup ? (e.group.pickMember(Bot.uin).card || e.group.pickMember(Bot.uin).nickname) : 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) {
|
||||||
opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
|
opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
|
||||||
}
|
}
|
||||||
if (master && !e.group) {
|
if (master && !e.group) {
|
||||||
opt.masterName = Bot.getFriendList().get(parseInt(master))?.nickname
|
opt.masterName = e.bot.getFriendList().get(parseInt(master))?.nickname
|
||||||
}
|
}
|
||||||
let latestChat = await e.group.getChatHistory(0, 1)
|
opt.chats = await getChatHistoryGroup(e, Config.groupContextLength)
|
||||||
let seq = latestChat[0].seq
|
|
||||||
let chats = []
|
|
||||||
while (chats.length < Config.groupContextLength) {
|
|
||||||
let chatHistory = await e.group.getChatHistory(seq, 20)
|
|
||||||
chats.push(...chatHistory)
|
|
||||||
}
|
|
||||||
chats = chats.slice(0, Config.groupContextLength)
|
|
||||||
let mm = await e.group.getMemberMap()
|
|
||||||
chats.forEach(chat => {
|
|
||||||
let sender = mm.get(chat.sender.user_id)
|
|
||||||
chat.sender = sender
|
|
||||||
})
|
|
||||||
// console.log(chats)
|
|
||||||
opt.chats = chats
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
|
logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let toSummaryFileContent
|
||||||
|
try {
|
||||||
|
if (e.source) {
|
||||||
|
let msgs = e.isGroup ? await e.group.getChatHistory(e.source.seq, 1) : await e.friend.getChatHistory(e.source.time, 1)
|
||||||
|
let sourceMsg = msgs[0]
|
||||||
|
let fileMsgElem = sourceMsg.message.find(msg => msg.type === 'file')
|
||||||
|
if (fileMsgElem) {
|
||||||
|
toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
||||||
|
}
|
||||||
|
opt.toSummaryFileContent = toSummaryFileContent
|
||||||
} else {
|
} else {
|
||||||
// 重新创建client,因为token可能换到别的了
|
// 重新创建client,因为token可能换到别的了
|
||||||
if (bingToken?.indexOf('=') > -1) {
|
if (bingToken?.indexOf('=') > -1) {
|
||||||
|
|
@ -1652,6 +1719,13 @@ export class chatgpt extends plugin {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark(`开始生成内容:${response.details.imageTag}`)
|
logger.mark(`开始生成内容:${response.details.imageTag}`)
|
||||||
}
|
}
|
||||||
|
if (Config.bingAPDraw) {
|
||||||
|
// 调用第三方API进行绘图
|
||||||
|
let apDraw = new APTool()
|
||||||
|
apDraw.func({
|
||||||
|
prompt: response.details.imageTag
|
||||||
|
}, e)
|
||||||
|
} else {
|
||||||
let client = new BingDrawClient({
|
let client = new BingDrawClient({
|
||||||
baseUrl: Config.sydneyReverseProxy,
|
baseUrl: Config.sydneyReverseProxy,
|
||||||
userToken: bingToken
|
userToken: bingToken
|
||||||
|
|
@ -1664,6 +1738,7 @@ export class chatgpt extends plugin {
|
||||||
await e.reply('绘图失败:' + err)
|
await e.reply('绘图失败:' + err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果token曾经有异常,则清除异常
|
// 如果token曾经有异常,则清除异常
|
||||||
let Tokens = JSON.parse((await redis.get('CHATGPT:BING_TOKENS')) || '[]')
|
let Tokens = JSON.parse((await redis.get('CHATGPT:BING_TOKENS')) || '[]')
|
||||||
|
|
@ -1680,7 +1755,7 @@ export class chatgpt extends plugin {
|
||||||
const { maxConv } = error
|
const { maxConv } = error
|
||||||
if (message && typeof message === 'string' && message.indexOf('CaptchaChallenge') > -1) {
|
if (message && typeof message === 'string' && message.indexOf('CaptchaChallenge') > -1) {
|
||||||
if (bingToken) {
|
if (bingToken) {
|
||||||
if (maxConv > 20) {
|
if (maxConv >= 20) {
|
||||||
// maxConv为30说明token有效,可以通过解验证码码服务过码
|
// maxConv为30说明token有效,可以通过解验证码码服务过码
|
||||||
await e.reply('出现必应验证码,尝试解决中')
|
await e.reply('出现必应验证码,尝试解决中')
|
||||||
try {
|
try {
|
||||||
|
|
@ -1699,8 +1774,11 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 未登录用户maxConv目前为5或10,出验证码没救
|
// 未登录用户maxConv目前为5或10,出验证码没救
|
||||||
logger.warn(`token [${bingToken}] 无效或已过期`)
|
logger.warn(`token [${bingToken}] 无效或已过期,如确认token无误,请前往网页版必应对话一次`)
|
||||||
|
retry = 0
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
retry = 0
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
|
if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
|
||||||
|
|
@ -1756,10 +1834,10 @@ export class chatgpt extends plugin {
|
||||||
text: errorMessage,
|
text: errorMessage,
|
||||||
error: true
|
error: true
|
||||||
}
|
}
|
||||||
} else {
|
} else if (response?.response) {
|
||||||
return {
|
return {
|
||||||
text: response?.response,
|
text: response?.response,
|
||||||
quote: response.quote,
|
quote: response?.quote,
|
||||||
suggestedResponses: response.suggestedResponses,
|
suggestedResponses: response.suggestedResponses,
|
||||||
conversationId: response.conversationId,
|
conversationId: response.conversationId,
|
||||||
clientId: response.clientId,
|
clientId: response.clientId,
|
||||||
|
|
@ -1768,6 +1846,11 @@ export class chatgpt extends plugin {
|
||||||
parentMessageId: response.apology ? conversation.parentMessageId : response.messageId,
|
parentMessageId: response.apology ? conversation.parentMessageId : response.messageId,
|
||||||
bingToken
|
bingToken
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug('no message')
|
||||||
|
return {
|
||||||
|
noMsg: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'api3': {
|
case 'api3': {
|
||||||
|
|
@ -1856,40 +1939,30 @@ export class chatgpt extends plugin {
|
||||||
debug: Config.debug,
|
debug: Config.debug,
|
||||||
proxy: Config.proxy
|
proxy: Config.proxy
|
||||||
})
|
})
|
||||||
let fileUrl, filename, attachments
|
let toSummaryFileContent
|
||||||
if (e.source && e.source.message === '[文件]') {
|
try {
|
||||||
if (e.isGroup) {
|
if (e.source) {
|
||||||
let source = (await e.group.getChatHistory(e.source.seq, 1))[0]
|
let msgs = e.isGroup ? await e.group.getChatHistory(e.source.seq, 1) : await e.friend.getChatHistory(e.source.time, 1)
|
||||||
let file = source.message.find(m => m.type === 'file')
|
let sourceMsg = msgs[0]
|
||||||
if (file) {
|
let fileMsgElem = sourceMsg.message.find(msg => msg.type === 'file')
|
||||||
filename = file.name
|
if (fileMsgElem) {
|
||||||
fileUrl = await e.group.getFileUrl(file.fid)
|
toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let source = (await e.friend.getChatHistory(e.source.time, 1))[0]
|
|
||||||
let file = source.message.find(m => m.type === 'file')
|
|
||||||
if (file) {
|
|
||||||
filename = file.name
|
|
||||||
fileUrl = await e.group.getFileUrl(file.fid)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
||||||
}
|
}
|
||||||
if (fileUrl) {
|
|
||||||
logger.info('文件地址:' + fileUrl)
|
let attachments = []
|
||||||
mkdirs('data/chatgpt/files')
|
if (toSummaryFileContent?.content) {
|
||||||
let destinationPath = 'data/chatgpt/files/' + filename
|
attachments.push({
|
||||||
const response = await fetch(fileUrl)
|
extracted_content: toSummaryFileContent.content,
|
||||||
const fileStream = fs.createWriteStream(destinationPath)
|
file_name: toSummaryFileContent.name,
|
||||||
await new Promise((resolve, reject) => {
|
file_type: 'pdf',
|
||||||
response.body.pipe(fileStream)
|
file_size: 200312,
|
||||||
response.body.on('error', (err) => {
|
totalPages: 20
|
||||||
reject(err)
|
|
||||||
})
|
})
|
||||||
fileStream.on('finish', () => {
|
logger.info(toSummaryFileContent.content)
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
attachments = [await client.convertDocument(destinationPath, filename)]
|
|
||||||
}
|
}
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
return await client.sendMessage(prompt, conversationId, attachments)
|
return await client.sendMessage(prompt, conversationId, attachments)
|
||||||
|
|
@ -1939,6 +2012,57 @@ export class chatgpt extends plugin {
|
||||||
let completion = choices[0].message
|
let completion = choices[0].message
|
||||||
return { text: completion.content, message: completion }
|
return { text: completion.content, message: completion }
|
||||||
}
|
}
|
||||||
|
case 'qwen': {
|
||||||
|
let completionParams = {
|
||||||
|
parameters: {
|
||||||
|
top_p: Config.qwenTopP || 0.5,
|
||||||
|
top_k: Config.qwenTopK || 50,
|
||||||
|
seed: Config.qwenSeed > 0 ? Config.qwenSeed : Math.floor(Math.random() * 114514),
|
||||||
|
temperature: Config.qwenTemperature || 1,
|
||||||
|
enable_search: !!Config.qwenEnableSearch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Config.qwenModel) {
|
||||||
|
completionParams.model = Config.qwenModel
|
||||||
|
}
|
||||||
|
const currentDate = new Date().toISOString().split('T')[0]
|
||||||
|
async function um (message) {
|
||||||
|
return await upsertMessage(message, 'QWEN')
|
||||||
|
}
|
||||||
|
async function gm (id) {
|
||||||
|
return await getMessageById(id, 'QWEN')
|
||||||
|
}
|
||||||
|
let opts = {
|
||||||
|
apiKey: Config.qwenApiKey,
|
||||||
|
debug: false,
|
||||||
|
upsertMessage: um,
|
||||||
|
getMessageById: gm,
|
||||||
|
systemMessage: `You are ${Config.assistantLabel} ${useCast?.api || Config.promptPrefixOverride || defaultPropmtPrefix}
|
||||||
|
Current date: ${currentDate}`,
|
||||||
|
completionParams,
|
||||||
|
assistantLabel: Config.assistantLabel,
|
||||||
|
fetch: newFetch
|
||||||
|
}
|
||||||
|
this.qwenApi = new QwenApi(opts)
|
||||||
|
let option = {
|
||||||
|
timeoutMs: 600000,
|
||||||
|
completionParams
|
||||||
|
}
|
||||||
|
if (conversation) {
|
||||||
|
if (!conversation.conversationId) {
|
||||||
|
conversation.conversationId = uuid()
|
||||||
|
}
|
||||||
|
option = Object.assign(option, conversation)
|
||||||
|
}
|
||||||
|
let msg
|
||||||
|
try {
|
||||||
|
msg = await this.qwenApi.sendMessage(prompt, option)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
throw new Error(err)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
case 'bard': {
|
case 'bard': {
|
||||||
// 处理cookie
|
// 处理cookie
|
||||||
const matchesPSID = /__Secure-1PSID=([^;]+)/.exec(Config.bardPsid)
|
const matchesPSID = /__Secure-1PSID=([^;]+)/.exec(Config.bardPsid)
|
||||||
|
|
@ -2005,30 +2129,16 @@ 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(Bot.uin).card || e.group.pickMember(Bot.uin).nickname) : 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) {
|
||||||
opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
|
opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
|
||||||
}
|
}
|
||||||
if (master && !e.group) {
|
if (master && !e.group) {
|
||||||
opt.masterName = Bot.getFriendList().get(parseInt(master))?.nickname
|
opt.masterName = e.bot.getFriendList().get(parseInt(master))?.nickname
|
||||||
}
|
}
|
||||||
let latestChat = await e.group.getChatHistory(0, 1)
|
let chats = await getChatHistoryGroup(e, Config.groupContextLength)
|
||||||
let seq = latestChat[0].seq
|
|
||||||
let chats = []
|
|
||||||
while (chats.length < Config.groupContextLength) {
|
|
||||||
let chatHistory = await e.group.getChatHistory(seq, 20)
|
|
||||||
chats.push(...chatHistory.reverse())
|
|
||||||
}
|
|
||||||
chats = chats.slice(0, Config.groupContextLength)
|
|
||||||
// 太多可能会干扰AI对自身qq号和用户qq的判断,感觉gpt3.5也处理不了那么多信息
|
|
||||||
chats = chats > 50 ? 50 : chats
|
|
||||||
let mm = await e.group.getMemberMap()
|
|
||||||
chats.forEach(chat => {
|
|
||||||
let sender = mm.get(chat.sender.user_id)
|
|
||||||
chat.sender = sender
|
|
||||||
})
|
|
||||||
opt.chats = chats
|
opt.chats = chats
|
||||||
const namePlaceholder = '[name]'
|
const namePlaceholder = '[name]'
|
||||||
const defaultBotName = 'ChatGPT'
|
const defaultBotName = 'ChatGPT'
|
||||||
|
|
@ -2050,7 +2160,7 @@ export class chatgpt extends plugin {
|
||||||
system += chats
|
system += chats
|
||||||
.map(chat => {
|
.map(chat => {
|
||||||
let sender = chat.sender || {}
|
let sender = chat.sender || {}
|
||||||
// if (sender.user_id === Bot.uin && chat.raw_message.startsWith('建议的回复')) {
|
// if (sender.user_id === e.bot.uin && chat.raw_message.startsWith('建议的回复')) {
|
||||||
if (chat.raw_message.startsWith('建议的回复')) {
|
if (chat.raw_message.startsWith('建议的回复')) {
|
||||||
// 建议的回复太容易污染设定导致对话太固定跑偏了
|
// 建议的回复太容易污染设定导致对话太固定跑偏了
|
||||||
return ''
|
return ''
|
||||||
|
|
@ -2097,6 +2207,9 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
option.systemMessage = system
|
option.systemMessage = system
|
||||||
if (conversation) {
|
if (conversation) {
|
||||||
|
if (!conversation.conversationId) {
|
||||||
|
conversation.conversationId = uuid()
|
||||||
|
}
|
||||||
option = Object.assign(option, conversation)
|
option = Object.assign(option, conversation)
|
||||||
}
|
}
|
||||||
if (Config.smartMode) {
|
if (Config.smartMode) {
|
||||||
|
|
@ -2178,7 +2291,7 @@ export class chatgpt extends plugin {
|
||||||
logger.mark(logger.green('【ChatGPT-Plugin】插件avocado-plugin未安装') + ',安装后可查看最近热映电影与体验可玩性更高的点歌工具。\n可前往 https://github.com/Qz-Sean/avocado-plugin 获取')
|
logger.mark(logger.green('【ChatGPT-Plugin】插件avocado-plugin未安装') + ',安装后可查看最近热映电影与体验可玩性更高的点歌工具。\n可前往 https://github.com/Qz-Sean/avocado-plugin 获取')
|
||||||
}
|
}
|
||||||
if (e.isGroup) {
|
if (e.isGroup) {
|
||||||
let botInfo = await Bot.getGroupMemberInfo(e.group_id, Bot.uin, true)
|
let botInfo = await e.bot.getGroupMemberInfo(e.group_id, getUin(e), true)
|
||||||
if (botInfo.role !== 'member') {
|
if (botInfo.role !== 'member') {
|
||||||
// 管理员才给这些工具
|
// 管理员才给这些工具
|
||||||
tools.push(...[new EditCardTool(), new JinyanTool(), new KickOutTool(), new HandleMessageMsgTool(), new SetTitleTool()])
|
tools.push(...[new EditCardTool(), new JinyanTool(), new KickOutTool(), new HandleMessageMsgTool(), new SetTitleTool()])
|
||||||
|
|
@ -2430,7 +2543,7 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
if (bots.code === 0) {
|
if (bots.code === 0) {
|
||||||
if (bots.data.pageList.length > 0) {
|
if (bots.data.pageList.length > 0) {
|
||||||
this.reply(await makeForwardMsg(this.e, bots.data.pageList.map(msg => `${msg.bot.botId} - ${msg.bot.botName}`)))
|
this.reply(await makeForwardMsg(this.e, bots.data.pageList.map(msg => `${msg.e.bot.botId} - ${msg.e.bot.botName}`)))
|
||||||
} else {
|
} else {
|
||||||
await e.reply('未查到相关助手', true)
|
await e.reply('未查到相关助手', true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ export class Entertainment extends plugin {
|
||||||
reg: '^#(|最新)词云(\\d{1,2}h{0,1}|)$',
|
reg: '^#(|最新)词云(\\d{1,2}h{0,1}|)$',
|
||||||
fnc: 'wordcloud_latest'
|
fnc: 'wordcloud_latest'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#(我的)?(本月|本周|今日)?词云$',
|
||||||
|
fnc: 'wordcloud_new'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#((寄批踢|gpt|GPT)?翻.*|chatgpt翻译帮助)',
|
reg: '^#((寄批踢|gpt|GPT)?翻.*|chatgpt翻译帮助)',
|
||||||
fnc: 'translate'
|
fnc: 'translate'
|
||||||
|
|
@ -218,7 +222,7 @@ ${translateLangLabels}
|
||||||
const duration = !match[1] ? 12 : parseInt(match[1]) // default 12h
|
const duration = !match[1] ? 12 : parseInt(match[1]) // default 12h
|
||||||
|
|
||||||
if (duration > 24) {
|
if (duration > 24) {
|
||||||
await e.reply('最多只能统计24小时内的记录哦')
|
await e.reply('最多只能统计24小时内的记录哦,你可以使用#本周词云和#本月词云获取更长时间的统计~')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
await e.reply('在统计啦,请稍等...')
|
await e.reply('在统计啦,请稍等...')
|
||||||
|
|
@ -236,6 +240,56 @@ ${translateLangLabels}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async wordcloud_new (e) {
|
||||||
|
if (e.isGroup) {
|
||||||
|
let groupId = e.group_id
|
||||||
|
let userId
|
||||||
|
if (e.msg.includes('我的')) {
|
||||||
|
userId = e.sender.user_id
|
||||||
|
}
|
||||||
|
let at = e.message.find(m => m.type === 'at')
|
||||||
|
if (at) {
|
||||||
|
userId = at.qq
|
||||||
|
}
|
||||||
|
let lock = await redis.get(`CHATGPT:WORDCLOUD_NEW:${groupId}_${userId}`)
|
||||||
|
if (lock) {
|
||||||
|
await e.reply('别着急,上次统计还没完呢')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
await e.reply('在统计啦,请稍等...')
|
||||||
|
let duration = 24
|
||||||
|
if (e.msg.includes('本周')) {
|
||||||
|
const now = new Date() // Get the current date and time
|
||||||
|
let day = now.getDay()
|
||||||
|
let diff = now.getDate() - day + (day === 0 ? -6 : 1)
|
||||||
|
const startOfWeek = new Date(new Date().setDate(diff))
|
||||||
|
startOfWeek.setHours(0, 0, 0, 0) // Set the time to midnight (start of the day)
|
||||||
|
duration = (now - startOfWeek) / 1000 / 60 / 60
|
||||||
|
} else if (e.msg.includes('本月')) {
|
||||||
|
const now = new Date() // Get the current date and time
|
||||||
|
const startOfMonth = new Date(new Date().setDate(0))
|
||||||
|
startOfMonth.setHours(0, 0, 0, 0) // Set the time to midnight (start of the day)
|
||||||
|
duration = (now - startOfMonth) / 1000 / 60 / 60
|
||||||
|
} else {
|
||||||
|
// 默认今天
|
||||||
|
const now = new Date()
|
||||||
|
const startOfToday = new Date() // Get the current date and time
|
||||||
|
startOfToday.setHours(0, 0, 0, 0) // Set the time to midnight (start of the day)
|
||||||
|
duration = (now - startOfToday) / 1000 / 60 / 60
|
||||||
|
}
|
||||||
|
await redis.set(`CHATGPT:WORDCLOUD_NEW:${groupId}_${userId}`, '1', { EX: 600 })
|
||||||
|
try {
|
||||||
|
await makeWordcloud(e, e.group_id, duration, userId)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
await e.reply(err)
|
||||||
|
}
|
||||||
|
await redis.del(`CHATGPT:WORDCLOUD_NEW:${groupId}_${userId}`)
|
||||||
|
} else {
|
||||||
|
await e.reply('请在群里发送此命令')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async combineEmoj (e) {
|
async combineEmoj (e) {
|
||||||
let left = e.msg.codePointAt(0).toString(16).toLowerCase()
|
let left = e.msg.codePointAt(0).toString(16).toLowerCase()
|
||||||
let right = e.msg.codePointAt(2).toString(16).toLowerCase()
|
let right = e.msg.codePointAt(2).toString(16).toLowerCase()
|
||||||
|
|
@ -296,7 +350,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 && !Bot.getGroupList().get(groupId)) {
|
if (groupId && !e.bot.getGroupList().get(groupId)) {
|
||||||
await e.reply('机器人不在这个群里!')
|
await e.reply('机器人不在这个群里!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -310,7 +364,7 @@ ${translateLangLabels}
|
||||||
if (!groupId) {
|
if (!groupId) {
|
||||||
await e.reply(sendable)
|
await e.reply(sendable)
|
||||||
} else {
|
} else {
|
||||||
await Bot.sendGroupMsg(groupId, sendable)
|
await e.bot.sendGroupMsg(groupId, sendable)
|
||||||
await e.reply('发送成功!')
|
await e.reply('发送成功!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +379,7 @@ ${translateLangLabels}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let groupId = parseInt(element)
|
let groupId = parseInt(element)
|
||||||
if (Bot.getGroupList().get(groupId)) {
|
if (this.e.bot.getGroupList().get(groupId)) {
|
||||||
// 打招呼概率
|
// 打招呼概率
|
||||||
if (Math.floor(Math.random() * 100) < Config.helloProbability) {
|
if (Math.floor(Math.random() * 100) < Config.helloProbability) {
|
||||||
let message = await generateHello()
|
let message = await generateHello()
|
||||||
|
|
@ -381,12 +435,12 @@ ${translateLangLabels}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (useSilk) {
|
if (useSilk) {
|
||||||
await Bot.sendGroupMsg(groupId, await uploadRecord(audio))
|
await this.e.bot.sendGroupMsg(groupId, await uploadRecord(audio))
|
||||||
} else {
|
} else {
|
||||||
await Bot.sendGroupMsg(groupId, segment.record(audio))
|
await this.e.bot.sendGroupMsg(groupId, segment.record(audio))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await Bot.sendGroupMsg(groupId, message)
|
await this.e.bot.sendGroupMsg(groupId, message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info(`时机未到,这次就不打招呼给群聊${groupId}了`)
|
logger.info(`时机未到,这次就不打招呼给群聊${groupId}了`)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import plugin from '../../../lib/plugins/plugin.js'
|
import plugin from '../../../lib/plugins/plugin.js'
|
||||||
import { render } from '../utils/common.js'
|
import { render, getUin } from '../utils/common.js'
|
||||||
import { Config } from '../utils/config.js'
|
import { Config } from '../utils/config.js'
|
||||||
import { KeyvFile } from 'keyv-file'
|
import { KeyvFile } from 'keyv-file'
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ export class history extends plugin {
|
||||||
async history (e) {
|
async history (e) {
|
||||||
let use = await redis.get('CHATGPT:USE') || 'api'
|
let use = await redis.get('CHATGPT:USE') || 'api'
|
||||||
let chat = []
|
let chat = []
|
||||||
let filtered = e.message.filter(m => m.type === 'at').filter(m => m.qq !== Bot.uin)
|
let filtered = e.message.filter(m => m.type === 'at').filter(m => m.qq !== getUin(e))
|
||||||
let queryUser = e.sender.user_id
|
let queryUser = e.sender.user_id
|
||||||
let user = e.sender
|
let user = e.sender
|
||||||
if (filtered.length > 0) {
|
if (filtered.length > 0) {
|
||||||
|
|
@ -99,8 +99,8 @@ export class history extends plugin {
|
||||||
name: user.card || user.nickname || user.user_id
|
name: user.card || user.nickname || user.user_id
|
||||||
},
|
},
|
||||||
bot: {
|
bot: {
|
||||||
qq: Bot.uin,
|
qq: getUin(e),
|
||||||
name: Bot.nickname
|
name: e.bot.nickname
|
||||||
},
|
},
|
||||||
chat
|
chat
|
||||||
}, {})
|
}, {})
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'useBardBasedSolution',
|
fnc: 'useBardBasedSolution',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt切换(通义千问|qwen|千问)$',
|
||||||
|
fnc: 'useQwenSolution',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt(必应|Bing)切换',
|
reg: '^#chatgpt(必应|Bing)切换',
|
||||||
fnc: 'changeBingTone',
|
fnc: 'changeBingTone',
|
||||||
|
|
@ -876,6 +881,7 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
await this.reply('当前已经是星火模式了')
|
await this.reply('当前已经是星火模式了')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async useAzureBasedSolution () {
|
async useAzureBasedSolution () {
|
||||||
let use = await redis.get('CHATGPT:USE')
|
let use = await redis.get('CHATGPT:USE')
|
||||||
if (use !== 'azure') {
|
if (use !== 'azure') {
|
||||||
|
|
@ -896,6 +902,16 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async useQwenSolution () {
|
||||||
|
let use = await redis.get('CHATGPT:USE')
|
||||||
|
if (use !== 'qwen') {
|
||||||
|
await redis.set('CHATGPT:USE', 'qwen')
|
||||||
|
await this.reply('已切换到基于通义千问的解决方案')
|
||||||
|
} else {
|
||||||
|
await this.reply('当前已经是通义千问模式了')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async changeBingTone (e) {
|
async changeBingTone (e) {
|
||||||
let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '')
|
let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '')
|
||||||
if (!tongStyle) {
|
if (!tongStyle) {
|
||||||
|
|
@ -1002,7 +1018,7 @@ Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie
|
||||||
}
|
}
|
||||||
} else if (match) {
|
} else if (match) {
|
||||||
const groupId = parseInt(match[1], 10)
|
const groupId = parseInt(match[1], 10)
|
||||||
if (Bot.getGroupList().get(groupId)) {
|
if (e.bot.getGroupList().get(groupId)) {
|
||||||
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
||||||
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
||||||
await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time })
|
await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time })
|
||||||
|
|
@ -1052,7 +1068,7 @@ Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const groupId = parseInt(match[1], 10)
|
const groupId = parseInt(match[1], 10)
|
||||||
if (Bot.getGroupList().get(groupId)) {
|
if (e.bot.getGroupList().get(groupId)) {
|
||||||
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) {
|
||||||
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
await redis.del(`CHATGPT:SHUT_UP:${groupId}`)
|
||||||
await e.reply(`好的主人,我终于又可以在群${groupId}和大家聊天了`)
|
await e.reply(`好的主人,我终于又可以在群${groupId}和大家聊天了`)
|
||||||
|
|
@ -1287,7 +1303,7 @@ Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie
|
||||||
const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/`
|
const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/`
|
||||||
const otp = randomString(6)
|
const otp = randomString(6)
|
||||||
await redis.set(
|
await redis.set(
|
||||||
`CHATGPT:SERVER_QUICK`,
|
'CHATGPT:SERVER_QUICK',
|
||||||
otp,
|
otp,
|
||||||
{ EX: 60000 }
|
{ EX: 60000 }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import plugin from '../../../lib/plugins/plugin.js'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { Config } from '../utils/config.js'
|
import { Config } from '../utils/config.js'
|
||||||
import { getMasterQQ, limitString, makeForwardMsg, maskQQ } from '../utils/common.js'
|
import { getMasterQQ, limitString, makeForwardMsg, maskQQ, getUin } from '../utils/common.js'
|
||||||
import { deleteOnePrompt, getPromptByName, readPrompts, saveOnePrompt } from '../utils/prompts.js'
|
import { deleteOnePrompt, getPromptByName, readPrompts, saveOnePrompt } from '../utils/prompts.js'
|
||||||
import AzureTTS from "../utils/tts/microsoft-azure.js";
|
import AzureTTS from "../utils/tts/microsoft-azure.js";
|
||||||
export class help extends plugin {
|
export class help extends plugin {
|
||||||
|
|
@ -157,7 +157,8 @@ export class help extends plugin {
|
||||||
const keyMap = {
|
const keyMap = {
|
||||||
api: 'promptPrefixOverride',
|
api: 'promptPrefixOverride',
|
||||||
Custom: 'sydney',
|
Custom: 'sydney',
|
||||||
claude: 'slackClaudeGlobalPreset'
|
claude: 'slackClaudeGlobalPreset',
|
||||||
|
qwen: 'promptPrefixOverride'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyMap[use]) {
|
if (keyMap[use]) {
|
||||||
|
|
@ -246,7 +247,7 @@ export class help extends plugin {
|
||||||
async removeSharePrompt (e) {
|
async removeSharePrompt (e) {
|
||||||
let master = (await getMasterQQ())[0]
|
let master = (await getMasterQQ())[0]
|
||||||
let name = e.msg.replace(/^#(chatgpt|ChatGPT)(删除|取消|撤销)共享设定/, '')
|
let name = e.msg.replace(/^#(chatgpt|ChatGPT)(删除|取消|撤销)共享设定/, '')
|
||||||
let response = await fetch(`https://chatgpt.roki.best/prompt?name=${name}&qq=${master || (Bot.uin + '')}`, {
|
let response = await fetch(`https://chatgpt.roki.best/prompt?name=${name}&qq=${master || (getUin(e) + '')}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'FROM-CHATGPT': 'ikechan8370'
|
'FROM-CHATGPT': 'ikechan8370'
|
||||||
|
|
@ -354,7 +355,7 @@ export class help extends plugin {
|
||||||
let toUploadBody = {
|
let toUploadBody = {
|
||||||
title: currentUse,
|
title: currentUse,
|
||||||
prompt: content,
|
prompt: content,
|
||||||
qq: master || (Bot.uin + ''), // 上传者设定为主人qq或机器人qq
|
qq: master || (getUin(this.e) + ''), // 上传者设定为主人qq或机器人qq
|
||||||
use: extraData.use === 'Custom' ? 'Sydney' : 'ChatGPT',
|
use: extraData.use === 'Custom' ? 'Sydney' : 'ChatGPT',
|
||||||
r18,
|
r18,
|
||||||
description
|
description
|
||||||
|
|
|
||||||
101
client/BaseClient.js
Normal file
101
client/BaseClient.js
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* Base LLM Chat Client \
|
||||||
|
* All the Chat Models should extend this class
|
||||||
|
*
|
||||||
|
* @since 2023-10-26
|
||||||
|
* @author ikechan8370
|
||||||
|
*/
|
||||||
|
export class BaseClient {
|
||||||
|
/**
|
||||||
|
* create a new client
|
||||||
|
*
|
||||||
|
* @param props required fields: e, getMessageById, upsertMessage
|
||||||
|
*/
|
||||||
|
constructor (props = {}) {
|
||||||
|
this.supportFunction = false
|
||||||
|
this.maxToken = 4096
|
||||||
|
this.tools = []
|
||||||
|
const {
|
||||||
|
e, getMessageById, upsertMessage
|
||||||
|
} = props
|
||||||
|
this.e = e
|
||||||
|
this.getMessageById = getMessageById
|
||||||
|
this.upsertMessage = upsertMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a message according to the id. note that conversationId is not needed
|
||||||
|
*
|
||||||
|
* @type function
|
||||||
|
* @param {string} id
|
||||||
|
* @return {Promise<object>} message
|
||||||
|
*/
|
||||||
|
getMessageById
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert or update a message with the id
|
||||||
|
*
|
||||||
|
* @type function
|
||||||
|
* @param {string} id
|
||||||
|
* @param {object} message
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
upsertMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send prompt message with history and return response message \
|
||||||
|
* if function called, handled internally \
|
||||||
|
* override this method to implement logic of sending and receiving message
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* @param opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
|
||||||
|
* @returns {Promise<Message>} required fields: [text, conversationId, parentMessageId, id]
|
||||||
|
*/
|
||||||
|
async sendMessage (msg, opt = {}) {
|
||||||
|
throw new Error('not implemented in abstract client')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get chat history between user and assistant
|
||||||
|
* override this method to implement logic of getting history
|
||||||
|
* keyv with local file or redis recommended
|
||||||
|
*
|
||||||
|
* @param userId such as qq number
|
||||||
|
* @param opt other options
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async getHistory (userId, opt = {}) {
|
||||||
|
throw new Error('not implemented in abstract client')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a chat history
|
||||||
|
* @param conversationId conversationId of the chat history
|
||||||
|
* @param opt other options
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async destroyHistory (conversationId, opt = {}) {
|
||||||
|
throw new Error('not implemented in abstract client')
|
||||||
|
}
|
||||||
|
|
||||||
|
addTools (...tools) {
|
||||||
|
if (!this.isSupportFunction) {
|
||||||
|
throw new Error('function not supported')
|
||||||
|
}
|
||||||
|
if (!this.tools) {
|
||||||
|
this.tools = []
|
||||||
|
}
|
||||||
|
this.tools.push(tools)
|
||||||
|
}
|
||||||
|
|
||||||
|
getTools () {
|
||||||
|
if (!this.isSupportFunction) {
|
||||||
|
throw new Error('function not supported')
|
||||||
|
}
|
||||||
|
return this.tools || []
|
||||||
|
}
|
||||||
|
|
||||||
|
get isSupportFunction () {
|
||||||
|
return this.supportFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -321,7 +321,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'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -462,7 +462,7 @@ export function supportGuoba () {
|
||||||
{
|
{
|
||||||
field: 'sydneyWebsocketUseProxy',
|
field: 'sydneyWebsocketUseProxy',
|
||||||
label: '对话使用sydney反代',
|
label: '对话使用sydney反代',
|
||||||
bottomHelpMessage: '【一般情况无需也不建议开启】默认情况下仅创建对话走反代,对话时仍然直连微软。开启本选项将使对话过程也走反,需反代支持',
|
bottomHelpMessage: '默认情况下仅创建对话走反代,对话时仍然直连微软。开启本选项将使对话过程也走反代,需反代支持。默认开启',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -511,40 +511,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'
|
||||||
|
|
@ -631,16 +597,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'
|
||||||
|
|
@ -655,6 +611,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' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -747,6 +704,53 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '开启后将通过反代访问bard',
|
bottomHelpMessage: '开启后将通过反代访问bard',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '以下为通义千问API方式的配置',
|
||||||
|
component: 'Divider'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenApiKey',
|
||||||
|
label: '通义千问API Key',
|
||||||
|
component: 'InputPassword'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenModel',
|
||||||
|
label: '通义千问模型',
|
||||||
|
bottomHelpMessage: '指明需要调用的模型,目前可选 qwen-turbo 和 qwen-plus',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenTopP',
|
||||||
|
label: '通义千问topP',
|
||||||
|
bottomHelpMessage: '生成时,核采样方法的概率阈值。例如,取值为0.8时,仅保留累计概率之和大于等于0.8的概率分布中的token,作为随机采样的候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值 0.5。注意,取值不要大于等于1',
|
||||||
|
component: 'InputNumber'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenTopK',
|
||||||
|
label: '通义千问topK',
|
||||||
|
bottomHelpMessage: '生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。注意:如果top_k的值大于100,top_k将采用默认值0,表示不启用top_k策略,此时仅有top_p策略生效。',
|
||||||
|
component: 'InputNumber'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenSeed',
|
||||||
|
label: '通义千问Seed',
|
||||||
|
bottomHelpMessage: '生成时,随机数的种子,用于控制模型生成的随机性。如果使用相同的种子,每次运行生成的结果都将相同;当需要复现模型的生成结果时,可以使用相同的种子。seed参数支持无符号64位整数类型。默认值 0, 表示每次随机生成',
|
||||||
|
component: 'InputNumber'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenTemperature',
|
||||||
|
label: '通义千问温度',
|
||||||
|
bottomHelpMessage: '用于控制随机性和多样性的程度。具体来说,temperature值控制了生成文本时对每个候选词的概率分布进行平滑的程度。较高的temperature值会降低概率分布的峰值,使得更多的低概率词被选择,生成结果更加多样化;而较低的temperature值则会增强概率分布的峰值,使得高概率词更容易被选择,生成结果更加确定。\n' +
|
||||||
|
'\n' +
|
||||||
|
'取值范围: (0, 2),系统默认值1.0',
|
||||||
|
component: 'InputNumber'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'qwenEnableSearch',
|
||||||
|
label: '通义千问允许搜索',
|
||||||
|
bottomHelpMessage: '生成时,是否参考夸克搜索的结果。注意:打开搜索并不意味着一定会使用搜索结果;如果打开搜索,模型会将搜索结果作为prompt,进而“自行判断”是否生成结合搜索结果的文本,默认为false',
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '以下为杂七杂八的配置',
|
label: '以下为杂七杂八的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
|
||||||
868
package-lock.json
generated
868
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
|
@ -24,7 +24,7 @@
|
||||||
"js-tiktoken": "^1.0.5",
|
"js-tiktoken": "^1.0.5",
|
||||||
"keyv": "^4.5.3",
|
"keyv": "^4.5.3",
|
||||||
"keyv-file": "^0.2.0",
|
"keyv-file": "^0.2.0",
|
||||||
"microsoft-cognitiveservices-speech-sdk": "^1.30.1",
|
"microsoft-cognitiveservices-speech-sdk": "1.32.0",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.1",
|
||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
"p-timeout": "^6.1.2",
|
"p-timeout": "^6.1.2",
|
||||||
|
|
@ -35,14 +35,18 @@
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"mammoth": "^1.6.0",
|
||||||
|
"pdfjs-dist": "^3.11.174",
|
||||||
|
"nodejs-pptx": "^1.2.4",
|
||||||
"@node-rs/jieba": "^1.6.2",
|
"@node-rs/jieba": "^1.6.2",
|
||||||
|
"cycletls": "^1.0.21",
|
||||||
"jimp": "^0.22.7",
|
"jimp": "^0.22.7",
|
||||||
"node-silk": "^0.1.0",
|
"node-silk": "^0.1.0",
|
||||||
"puppeteer-extra": "^3.3.6",
|
"puppeteer-extra": "^3.3.6",
|
||||||
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
"sharp": "^0.32.3",
|
"sharp": "^0.32.3"
|
||||||
"cycletls": "^1.0.21"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,12 @@
|
||||||
"label": "额外工具url",
|
"label": "额外工具url",
|
||||||
"placeholder": "测试期间提供一个公益接口,一段时间后撤掉",
|
"placeholder": "测试期间提供一个公益接口,一段时间后撤掉",
|
||||||
"data": "extraUrl"
|
"data": "extraUrl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Trss主账号",
|
||||||
|
"placeholder": "用于Trss配置脚本时使用的主账号",
|
||||||
|
"data": "trssBotUin"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -630,6 +636,12 @@
|
||||||
"label": "必应验证码pass服务",
|
"label": "必应验证码pass服务",
|
||||||
"placeholder": "必应出验证码会自动用该服务绕过",
|
"placeholder": "必应出验证码会自动用该服务绕过",
|
||||||
"data": "bingCaptchaOneShotUrl"
|
"data": "bingCaptchaOneShotUrl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "check",
|
||||||
|
"label": "第三方绘图",
|
||||||
|
"placeholder": "使用AP插件代替Bing进行绘图",
|
||||||
|
"data": "bingAPDraw"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -706,6 +718,10 @@
|
||||||
"label": "讯飞星火认知大模型V2.0",
|
"label": "讯飞星火认知大模型V2.0",
|
||||||
"value": "apiv2"
|
"value": "apiv2"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "讯飞星火认知大模型V3.0",
|
||||||
|
"value": "apiv3"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "讯飞星火助手",
|
"label": "讯飞星火助手",
|
||||||
"value": "assistants"
|
"value": "assistants"
|
||||||
|
|
|
||||||
115
server/index.js
115
server/index.js
|
|
@ -7,16 +7,16 @@ import websocket from '@fastify/websocket'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import os from 'os'
|
import os from 'os'
|
||||||
import schedule from 'node-schedule'
|
|
||||||
import websocketclient from 'ws'
|
import websocketclient from 'ws'
|
||||||
|
|
||||||
import { Config } from '../utils/config.js'
|
import { Config } from '../utils/config.js'
|
||||||
import { UserInfo, GetUser, AddUser } from './modules/user_data.js'
|
import { UserInfo, GetUser, AddUser } from './modules/user_data.js'
|
||||||
import { getPublicIP, getUserData, getMasterQQ, randomString } from '../utils/common.js'
|
import { getPublicIP, getUserData, getMasterQQ, randomString, getUin } from '../utils/common.js'
|
||||||
|
|
||||||
import webRoute from './modules/web_route.js'
|
import webRoute from './modules/web_route.js'
|
||||||
import webUser from './modules/user.js'
|
import webUser from './modules/user.js'
|
||||||
import webPrompt from './modules/prompts.js'
|
import webPrompt from './modules/prompts.js'
|
||||||
|
import Guoba from './modules/guoba.js'
|
||||||
import SettingView from './modules/setting_view.js'
|
import SettingView from './modules/setting_view.js'
|
||||||
|
|
||||||
const __dirname = path.resolve()
|
const __dirname = path.resolve()
|
||||||
|
|
@ -24,41 +24,6 @@ const server = fastify({
|
||||||
logger: Config.debug
|
logger: Config.debug
|
||||||
})
|
})
|
||||||
|
|
||||||
let Statistics = {
|
|
||||||
SystemAccess: {
|
|
||||||
count: 0,
|
|
||||||
oldCount: 0
|
|
||||||
},
|
|
||||||
CacheFile: {
|
|
||||||
count: 0,
|
|
||||||
oldCount: 0
|
|
||||||
},
|
|
||||||
WebAccess: {
|
|
||||||
count: 0,
|
|
||||||
oldCount: 0
|
|
||||||
},
|
|
||||||
SystemLoad: {
|
|
||||||
count: 0,
|
|
||||||
oldCount: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getLoad() {
|
|
||||||
// 获取当前操作系统平台
|
|
||||||
const platform = os.platform()
|
|
||||||
// 判断平台是Linux还是Windows
|
|
||||||
if (platform === 'linux') {
|
|
||||||
// 如果是Linux,使用os.loadavg()方法获取负载平均值
|
|
||||||
const loadAvg = os.loadavg()
|
|
||||||
return loadAvg[0] * 100
|
|
||||||
} else if (platform === 'win32') {
|
|
||||||
// 如果是Windows不获取性能
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setUserData(qq, data) {
|
async function setUserData(qq, data) {
|
||||||
const dir = 'resources/ChatGPTCache/user'
|
const dir = 'resources/ChatGPTCache/user'
|
||||||
const filename = `${qq}.json`
|
const filename = `${qq}.json`
|
||||||
|
|
@ -84,6 +49,7 @@ await server.register(webRoute)
|
||||||
await server.register(webUser)
|
await server.register(webUser)
|
||||||
await server.register(SettingView)
|
await server.register(SettingView)
|
||||||
await server.register(webPrompt)
|
await server.register(webPrompt)
|
||||||
|
await server.register(Guoba)
|
||||||
|
|
||||||
// 无法访问端口的情况下创建与media的通讯
|
// 无法访问端口的情况下创建与media的通讯
|
||||||
async function mediaLink() {
|
async function mediaLink() {
|
||||||
|
|
@ -107,7 +73,7 @@ async function mediaLink() {
|
||||||
ws.on('open', () => {
|
ws.on('open', () => {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
command: 'register',
|
command: 'register',
|
||||||
region: Bot.uin,
|
region: getUin(),
|
||||||
type: 'server',
|
type: 'server',
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
@ -118,7 +84,11 @@ async function mediaLink() {
|
||||||
case 'register':
|
case 'register':
|
||||||
if (data.state) {
|
if (data.state) {
|
||||||
let master = (await getMasterQQ())[0]
|
let master = (await getMasterQQ())[0]
|
||||||
|
if (Array.isArray(Bot.uin)) {
|
||||||
|
Bot.pickFriend(master).sendMsg(`当前chatgpt插件服务无法被外网访问,已启用代理链接,访问代码:${data.token}`)
|
||||||
|
} else {
|
||||||
Bot.sendPrivateMsg(master, `当前chatgpt插件服务无法被外网访问,已启用代理链接,访问代码:${data.token}`, false)
|
Bot.sendPrivateMsg(master, `当前chatgpt插件服务无法被外网访问,已启用代理链接,访问代码:${data.token}`, false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('注册区域失败')
|
console.log('注册区域失败')
|
||||||
}
|
}
|
||||||
|
|
@ -128,30 +98,30 @@ async function mediaLink() {
|
||||||
const user = UserInfo(data.token)
|
const user = UserInfo(data.token)
|
||||||
if (user) {
|
if (user) {
|
||||||
ws.login = true
|
ws.login = true
|
||||||
ws.send(JSON.stringify({ command: data.command, state: true, region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: true, region: getUin(), type: 'server' }))
|
||||||
} else {
|
} else {
|
||||||
ws.send(JSON.stringify({ command: data.command, state: false, error: '权限验证失败', region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: false, error: '权限验证失败', region: getUin(), type: 'server' }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'post_login':
|
case 'post_login':
|
||||||
if (data.qq && data.passwd) {
|
if (data.qq && data.passwd) {
|
||||||
const token = randomString(32)
|
const token = randomString(32)
|
||||||
if (data.qq == Bot.uin && await redis.get('CHATGPT:ADMIN_PASSWD') == data.passwd) {
|
if (data.qq == getUin() && await redis.get('CHATGPT:ADMIN_PASSWD') == data.passwd) {
|
||||||
AddUser({ user: data.qq, token: token, autho: 'admin' })
|
AddUser({ user: data.qq, token: token, autho: 'admin' })
|
||||||
ws.send(JSON.stringify({ command: data.command, state: true, autho: 'admin', token: token, region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: true, autho: 'admin', token: token, region: getUin(), type: 'server' }))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const user = await getUserData(data.qq)
|
const user = await getUserData(data.qq)
|
||||||
if (user.passwd != '' && user.passwd === data.passwd) {
|
if (user.passwd != '' && user.passwd === data.passwd) {
|
||||||
AddUser({ user: data.qq, token: token, autho: 'user' })
|
AddUser({ user: data.qq, token: token, autho: 'user' })
|
||||||
ws.send(JSON.stringify({ command: data.command, state: true, autho: 'user', token: token, region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: true, autho: 'user', token: token, region: getUin(), type: 'server' }))
|
||||||
} else {
|
} else {
|
||||||
ws.send(JSON.stringify({ command: data.command, state: false, error: `用户名密码错误,如果忘记密码请私聊机器人输入 ${data.qq == Bot.uin ? '#修改管理密码' : '#修改用户密码'} 进行修改`, region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: false, error: `用户名密码错误,如果忘记密码请私聊机器人输入 ${data.qq == getUin() ? '#修改管理密码' : '#修改用户密码'} 进行修改`, region: getUin(), type: 'server' }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ws.send(JSON.stringify({ command: data.command, state: false, error: '未输入用户名或密码', region: Bot.uin, type: 'server' }))
|
ws.send(JSON.stringify({ command: data.command, state: false, error: '未输入用户名或密码', region: getUin(), type: 'server' }))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'post_command':
|
case 'post_command':
|
||||||
|
|
@ -163,7 +133,7 @@ async function mediaLink() {
|
||||||
const response = await fetch(`http://localhost:${Config.serverPort || 3321}${data.postPath}`, fetchOptions)
|
const response = await fetch(`http://localhost:${Config.serverPort || 3321}${data.postPath}`, fetchOptions)
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
ws.send(JSON.stringify({ command: data.command, state: true, region: Bot.uin, type: 'server', path: data.postPath, data: json }))
|
ws.send(JSON.stringify({ command: data.command, state: true, region: getUin(), type: 'server', path: data.postPath, data: json }))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +256,6 @@ export async function createServer() {
|
||||||
time: data.time
|
time: data.time
|
||||||
})
|
})
|
||||||
await setUserData(body.qq, user)
|
await setUserData(body.qq, user)
|
||||||
Statistics.CacheFile.count += 1
|
|
||||||
reply.send({ file: body.entry, cacheUrl: `http://${ip}:${Config.serverPort || 3321}/page/${body.entry}` })
|
reply.send({ file: body.entry, cacheUrl: `http://${ip}:${Config.serverPort || 3321}/page/${body.entry}` })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
server.log.error(`用户生成缓存${body.entry}时发生错误: ${err}`)
|
server.log.error(`用户生成缓存${body.entry}时发生错误: ${err}`)
|
||||||
|
|
@ -295,12 +264,6 @@ export async function createServer() {
|
||||||
}
|
}
|
||||||
return reply
|
return reply
|
||||||
})
|
})
|
||||||
// 获取系统状态
|
|
||||||
server.post('/system-statistics', async (request, reply) => {
|
|
||||||
Statistics.SystemLoad.count = await getLoad()
|
|
||||||
reply.send(Statistics)
|
|
||||||
return reply
|
|
||||||
})
|
|
||||||
|
|
||||||
// 清除缓存数据
|
// 清除缓存数据
|
||||||
server.post('/cleanCache', async (request, reply) => {
|
server.post('/cleanCache', async (request, reply) => {
|
||||||
|
|
@ -329,6 +292,7 @@ export async function createServer() {
|
||||||
connection.socket.send(JSON.stringify(response))
|
connection.socket.send(JSON.stringify(response))
|
||||||
})
|
})
|
||||||
connection.socket.on('message', async (message) => {
|
connection.socket.on('message', async (message) => {
|
||||||
|
const isTrss = Array.isArray(Bot.uin)
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(message)
|
const data = JSON.parse(message)
|
||||||
const user = UserInfo(data.token)
|
const user = UserInfo(data.token)
|
||||||
|
|
@ -340,10 +304,18 @@ export async function createServer() {
|
||||||
}
|
}
|
||||||
if (data.id && data.message) {
|
if (data.id && data.message) {
|
||||||
if (data.group) {
|
if (data.group) {
|
||||||
|
if (isTrss) {
|
||||||
|
Bot[user.user].pickGroup(parseInt(data.id)).sendMsg(data.message)
|
||||||
|
} else {
|
||||||
Bot.sendGroupMsg(parseInt(data.id), data.message, data.quotable)
|
Bot.sendGroupMsg(parseInt(data.id), data.message, data.quotable)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isTrss) {
|
||||||
|
Bot[user.user].pickFriend(parseInt(data.id)).sendMsg(data.message)
|
||||||
} else {
|
} else {
|
||||||
Bot.sendPrivateMsg(parseInt(data.id), data.message, data.quotable)
|
Bot.sendPrivateMsg(parseInt(data.id), data.message, data.quotable)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
await connection.socket.send(JSON.stringify({ command: data.command, state: true, }))
|
await connection.socket.send(JSON.stringify({ command: data.command, state: true, }))
|
||||||
} else {
|
} else {
|
||||||
await connection.socket.send(JSON.stringify({ command: data.command, state: false, error: '参数不足' }))
|
await connection.socket.send(JSON.stringify({ command: data.command, state: false, error: '参数不足' }))
|
||||||
|
|
@ -357,7 +329,6 @@ export async function createServer() {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'login': // 登录
|
case 'login': // 登录
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
clients[user.user] = connection.socket
|
clients[user.user] = connection.socket
|
||||||
connection.login = true
|
connection.login = true
|
||||||
|
|
@ -371,13 +342,17 @@ export async function createServer() {
|
||||||
await connection.socket.send(JSON.stringify({ command: data.command, state: false, error: '请先登录账号' }))
|
await connection.socket.send(JSON.stringify({ command: data.command, state: false, error: '请先登录账号' }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (user.autho != 'admin') {
|
if (user?.autho != 'admin') {
|
||||||
await connection.socket.send(JSON.stringify({ command: data.command, state: true, error: '普通用户无需进行初始化' }))
|
await connection.socket.send(JSON.stringify({ command: data.command, state: true, error: '普通用户无需进行初始化' }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const groupList = Bot.getGroupList()
|
let _Bot = Bot
|
||||||
|
if (isTrss) {
|
||||||
|
_Bot = Bot[user.user]
|
||||||
|
}
|
||||||
|
const groupList = await _Bot.getGroupList()
|
||||||
groupList.forEach(async (item) => {
|
groupList.forEach(async (item) => {
|
||||||
const group = Bot.pickGroup(item.group_id)
|
const group = _Bot.pickGroup(item.group_id)
|
||||||
const groupMessages = await group.getChatHistory()
|
const groupMessages = await group.getChatHistory()
|
||||||
groupMessages.forEach(async (e) => {
|
groupMessages.forEach(async (e) => {
|
||||||
const messageData = {
|
const messageData = {
|
||||||
|
|
@ -402,12 +377,14 @@ export async function createServer() {
|
||||||
await connection.socket.send(JSON.stringify(messageData))
|
await connection.socket.send(JSON.stringify(messageData))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
await connection.socket.send(JSON.stringify({ "data": data }))
|
await connection.socket.send(JSON.stringify({ "data": data }))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
await connection.socket.send(JSON.stringify({ "error": error.message }))
|
await connection.socket.send(JSON.stringify({ "error": error.message }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -424,7 +401,7 @@ export async function createServer() {
|
||||||
message: e.message,
|
message: e.message,
|
||||||
sender: e.sender,
|
sender: e.sender,
|
||||||
group: {
|
group: {
|
||||||
isGroup: e.isGroup,
|
isGroup: e.isGroup || e.group_id != undefined,
|
||||||
group_id: e.group_id,
|
group_id: e.group_id,
|
||||||
group_name: e.group_name
|
group_name: e.group_name
|
||||||
},
|
},
|
||||||
|
|
@ -598,28 +575,6 @@ export async function createServer() {
|
||||||
return reply
|
return reply
|
||||||
})
|
})
|
||||||
|
|
||||||
server.addHook('onRequest', (request, reply, done) => {
|
|
||||||
if (request.method == 'POST') { Statistics.SystemAccess.count += 1 }
|
|
||||||
if (request.method == 'GET') { Statistics.WebAccess.count += 1 }
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
// 定时任务
|
|
||||||
let rule = new schedule.RecurrenceRule()
|
|
||||||
rule.hour = 0
|
|
||||||
rule.minute = 0
|
|
||||||
let job_Statistics = schedule.scheduleJob(rule, function () {
|
|
||||||
Statistics.SystemAccess.oldCount = Statistics.SystemAccess.count
|
|
||||||
Statistics.CacheFile.oldCount = Statistics.CacheFile.count
|
|
||||||
Statistics.WebAccess.oldCount = Statistics.WebAccess.count
|
|
||||||
Statistics.SystemAccess.count = 0
|
|
||||||
Statistics.CacheFile.count = 0
|
|
||||||
Statistics.WebAccess.count = 0
|
|
||||||
})
|
|
||||||
let job_Statistics_SystemLoad = schedule.scheduleJob('0 * * * *', async function () {
|
|
||||||
Statistics.SystemLoad.count = await getLoad()
|
|
||||||
Statistics.SystemLoad.oldCount = Statistics.SystemLoad.count
|
|
||||||
})
|
|
||||||
|
|
||||||
server.listen({
|
server.listen({
|
||||||
port: Config.serverPort || 3321,
|
port: Config.serverPort || 3321,
|
||||||
host: '::'
|
host: '::'
|
||||||
|
|
|
||||||
72
server/modules/guoba.js
Normal file
72
server/modules/guoba.js
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { UserInfo } from './user_data.js'
|
||||||
|
|
||||||
|
async function Guoba(fastify, options) {
|
||||||
|
// 获取锅巴登陆链接
|
||||||
|
fastify.post('/guobaLogin', async (request, reply) => {
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
let user = UserInfo(token)
|
||||||
|
if (user && user.autho == 'admin') {
|
||||||
|
try {
|
||||||
|
let { LoginService } = await import('../../../Guoba-Plugin/server/service/both/LoginService.js')
|
||||||
|
const guobaLoginService = new LoginService()
|
||||||
|
const guobaAPI = await guobaLoginService.setQuickLogin(user.user)
|
||||||
|
reply.send({ guoba: guobaAPI })
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
reply.send({ state: false, error: err })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.send({ state: false, error: '用户权限不足' })
|
||||||
|
}
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
// 代理锅巴接口
|
||||||
|
fastify.post('/guobaApi', async (request, reply) => {
|
||||||
|
const body = request.body || {}
|
||||||
|
const token = request.cookies.token || request.body?.token || 'unknown'
|
||||||
|
let user = UserInfo(token)
|
||||||
|
if (user && user.autho == 'admin' && body.guobaToken) {
|
||||||
|
try {
|
||||||
|
let { getAllWebAddress } = await import('../../../Guoba-Plugin/utils/common.js')
|
||||||
|
const { custom, local, remote } = await getAllWebAddress()
|
||||||
|
if (local.length > 0) {
|
||||||
|
const guobaOptions = {
|
||||||
|
method: body.post ? 'POST' : 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Guoba-Access-Token': body.guobaToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (body.data) {
|
||||||
|
if (body.post) {
|
||||||
|
guobaOptions.body = JSON.stringify(body.data)
|
||||||
|
} else {
|
||||||
|
let paramsArray = []
|
||||||
|
Object.keys(body.data).forEach(key => paramsArray.push(key + '=' + body.data[key]))
|
||||||
|
if (paramsArray.length > 0) {
|
||||||
|
body.path += '?' + paramsArray.join('&')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const response = await fetch(`${local[0]}/${body.path}`, guobaOptions)
|
||||||
|
if (response.ok) {
|
||||||
|
const json = await response.json()
|
||||||
|
reply.send(json)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.send({ state: false, error: '锅巴接口异常' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
reply.send({ state: false, error: err })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.send({ state: false, error: '用户权限不足' })
|
||||||
|
}
|
||||||
|
return reply
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Guoba
|
||||||
|
|
@ -1,36 +1,65 @@
|
||||||
import { UserInfo, AddUser } from './user_data.js'
|
import { UserInfo, AddUser } from './user_data.js'
|
||||||
import { randomString, getUserData, getMasterQQ } from '../../utils/common.js'
|
import { randomString, getUserData, getMasterQQ, getUin } from '../../utils/common.js'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 guobaAPI = ''
|
||||||
|
try {
|
||||||
|
let { LoginService } = await import('../../../Guoba-Plugin/server/service/both/LoginService.js')
|
||||||
|
let { getAllWebAddress } = await import('../../../Guoba-Plugin/utils/common.js')
|
||||||
|
guobaLoginService = new LoginService()
|
||||||
|
guobaAPI = await getAllWebAddress()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
guobaLoginService = {
|
||||||
|
signToken: () => { return null }
|
||||||
|
}
|
||||||
|
}
|
||||||
if (body.qq && body.passwd) {
|
if (body.qq && body.passwd) {
|
||||||
const token = randomString(32)
|
const token = randomString(32)
|
||||||
if (body.qq == Bot.uin && await redis.get('CHATGPT:ADMIN_PASSWD') == body.passwd) {
|
if (body.qq == getUin() && await redis.get('CHATGPT:ADMIN_PASSWD') == body.passwd) {
|
||||||
AddUser({ user: body.qq, token: token, autho: 'admin' })
|
const guobaToken = await guobaLoginService.signToken(body.qq)
|
||||||
|
AddUser({ user: body.qq, token, autho: 'admin' })
|
||||||
reply.setCookie('token', token, { path: '/' })
|
reply.setCookie('token', token, { path: '/' })
|
||||||
reply.send({ login: true, autho: 'admin', token: token })
|
reply.send({ login: true, autho: 'admin', token, guobaToken, guoba: guobaAPI })
|
||||||
} else {
|
} else {
|
||||||
const user = await getUserData(body.qq)
|
const user = await getUserData(body.qq)
|
||||||
if (user.passwd != '' && user.passwd === body.passwd) {
|
if (user.passwd != '' && user.passwd === body.passwd) {
|
||||||
AddUser({ user: body.qq, token: token, autho: 'user' })
|
AddUser({ user: body.qq, token, autho: 'user' })
|
||||||
reply.setCookie('token', token, { path: '/' })
|
reply.setCookie('token', token, { path: '/' })
|
||||||
reply.send({ login: true, autho: 'user', token: token })
|
reply.send({ login: true, autho: 'user', token })
|
||||||
} else {
|
} else {
|
||||||
reply.send({ login: false, err: `用户名密码错误,如果忘记密码请私聊机器人输入 ${body.qq == Bot.uin ? '#修改管理密码' : '#修改用户密码'} 进行修改` })
|
reply.send({ login: false, err: `用户名密码错误,如果忘记密码请私聊机器人输入 ${body.qq == getUin() ? '#修改管理密码' : '#修改用户密码'} 进行修改` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (body.otp) {
|
} else if (body.otp) {
|
||||||
const token = randomString(32)
|
const token = randomString(32)
|
||||||
const opt = await redis.get(`CHATGPT:SERVER_QUICK`)
|
const opt = await redis.get('CHATGPT:SERVER_QUICK')
|
||||||
if (opt && body.otp == opt) {
|
if (opt && body.otp == opt) {
|
||||||
AddUser({ user: Bot.uin, token: token, autho: 'admin' })
|
const guobaToken = await guobaLoginService.signToken(getUin())
|
||||||
|
AddUser({ user: getUin(), token, autho: 'admin' })
|
||||||
reply.setCookie('token', token, { path: '/' })
|
reply.setCookie('token', token, { path: '/' })
|
||||||
reply.send({ login: true, autho: 'admin', token: token, user: Bot.uin })
|
reply.send({ login: true, autho: 'admin', token, user: getUin(), guobaToken, guoba: guobaAPI })
|
||||||
} else {
|
} else {
|
||||||
reply.send({ login: false, err: `快捷登录代码错误,请检查后重试` })
|
reply.send({ login: false, err: '快捷登录代码错误,请检查后重试' })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reply.send({ login: false, err: '未输入用户名或密码' })
|
reply.send({ login: false, err: '未输入用户名或密码' })
|
||||||
|
|
@ -41,12 +70,15 @@ async function User(fastify, options) {
|
||||||
fastify.post('/quick', async (request, reply) => {
|
fastify.post('/quick', async (request, reply) => {
|
||||||
const otp = randomString(6)
|
const otp = randomString(6)
|
||||||
await redis.set(
|
await redis.set(
|
||||||
`CHATGPT:SERVER_QUICK`,
|
'CHATGPT:SERVER_QUICK',
|
||||||
otp,
|
otp,
|
||||||
{ EX: 60000 }
|
{ EX: 60000 }
|
||||||
)
|
)
|
||||||
const master = (await getMasterQQ())[0]
|
const master = (await getMasterQQ())[0]
|
||||||
Bot.sendPrivateMsg(master, `收到工具箱快捷登录请求,1分钟内有效:${otp}`, false)
|
let bots = getBots()
|
||||||
|
for (let bot of bots) {
|
||||||
|
bot.pickUser(master).sendMsg(`收到工具箱快捷登录请求,1分钟内有效:${otp}`)
|
||||||
|
}
|
||||||
reply.send({ state: true })
|
reply.send({ state: true })
|
||||||
return reply
|
return reply
|
||||||
})
|
})
|
||||||
|
|
@ -56,7 +88,7 @@ async function User(fastify, options) {
|
||||||
const user = UserInfo(token)
|
const user = UserInfo(token)
|
||||||
if (!user || token === 'unknown') {
|
if (!user || token === 'unknown') {
|
||||||
reply.send({
|
reply.send({
|
||||||
verify: false,
|
verify: false
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +96,7 @@ async function User(fastify, options) {
|
||||||
verify: true,
|
verify: true,
|
||||||
user: user.user,
|
user: user.user,
|
||||||
autho: user.autho,
|
autho: user.autho,
|
||||||
version: 10016,
|
version: 10016
|
||||||
})
|
})
|
||||||
return reply
|
return reply
|
||||||
})
|
})
|
||||||
|
|
@ -81,7 +113,7 @@ async function User(fastify, options) {
|
||||||
api: '', // API设定
|
api: '', // API设定
|
||||||
bing: '', // 必应设定
|
bing: '', // 必应设定
|
||||||
bing_resource: '', // 必应扩展资料
|
bing_resource: '', // 必应扩展资料
|
||||||
slack: '', //Slack设定
|
slack: '' // Slack设定
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return reply
|
return reply
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,14 @@ async function routes(fastify, options) {
|
||||||
reply.type('text/html').send(stream)
|
reply.type('text/html').send(stream)
|
||||||
return reply
|
return reply
|
||||||
})
|
})
|
||||||
|
fastify.setNotFoundHandler((request, reply) => {
|
||||||
|
if (request.method == 'GET') {
|
||||||
|
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
|
||||||
|
reply.type('text/html').send(stream)
|
||||||
|
} else {
|
||||||
|
reply.code(404).send(new Error('Not Found'))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
27
server/static/assets/AutoSettings.a106e074.js
Normal file
27
server/static/assets/AutoSettings.a106e074.js
Normal file
File diff suppressed because one or more lines are too long
1
server/static/assets/VAutocomplete.52d2da64.js
Normal file
1
server/static/assets/VAutocomplete.52d2da64.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
import{m as g,V as t}from"./VCheckboxBtn.b7a3a8e0.js";import{p as A,ae as F,af as I,n as B,u as U,ag as D,ah as R,o as _,ac as $,ai as j,aj as l,D as u,J as c}from"./index.f985de17.js";const J=A({...F(),...I(g(),["inline"])},"VCheckbox"),z=B()({name:"VCheckbox",inheritAttrs:!1,props:J(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,r){let{attrs:d,slots:a}=r;const s=U(e,"modelValue"),{isFocused:n,focus:i,blur:m}=D(e),V=R(),b=_(()=>e.id||`checkbox-${V}`);return $(()=>{const[p,k]=j(d),[f,M]=l.filterProps(e),[h,N]=t.filterProps(e);return u(l,c({class:["v-checkbox",e.class]},p,f,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:b.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:v,messagesId:x,isDisabled:P,isReadonly:C}=o;return u(t,c(h,{id:v.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},k,{modelValue:s.value,"onUpdate:modelValue":y=>s.value=y,onFocus:i,onBlur:m}),a)}})}),{}}});export{z as V};
|
import{m as g,V as t}from"./VCheckboxBtn.ae1aff4b.js";import{p as A,ae as F,af as I,n as B,u as U,ag as D,ah as R,o as _,ac as $,ai as j,aj as l,D as u,J as c}from"./index.a457a291.js";const J=A({...F(),...I(g(),["inline"])},"VCheckbox"),z=B()({name:"VCheckbox",inheritAttrs:!1,props:J(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,r){let{attrs:d,slots:a}=r;const s=U(e,"modelValue"),{isFocused:n,focus:i,blur:m}=D(e),V=R(),b=_(()=>e.id||`checkbox-${V}`);return $(()=>{const[p,k]=j(d),[f,M]=l.filterProps(e),[h,N]=t.filterProps(e);return u(l,c({class:["v-checkbox",e.class]},p,f,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:b.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:v,messagesId:x,isDisabled:P,isReadonly:C}=o;return u(t,c(h,{id:v.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},k,{modelValue:s.value,"onUpdate:modelValue":y=>s.value=y,onFocus:i,onBlur:m}),a)}})}),{}}});export{z as V};
|
||||||
File diff suppressed because one or more lines are too long
1
server/static/assets/VDataTable.e3c98ce6.js
Normal file
1
server/static/assets/VDataTable.e3c98ce6.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
server/static/assets/VDialog.368f468c.js
Normal file
1
server/static/assets/VDialog.368f468c.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
import{p as w,O as V,o as D,aD as b,b4 as O,av as k,a_ as x,b5 as L,b6 as j,n as B,u as R,b7 as T,a$ as A,aR as P,ax as C,J as I,ac as N,aT as K,D as S,G as $,b3 as z,b8 as G}from"./index.a457a291.js";const J=(t,f,e)=>t==null||f==null?-1:t.toString().toLocaleLowerCase().indexOf(f.toString().toLocaleLowerCase()),_=w({customFilter:Function,customKeyFilter:Object,filterKeys:[Array,String],filterMode:{type:String,default:"intersection"},noFilter:Boolean},"filter");function Q(t,f,e){var v,l,s;const r=[],d=(v=e==null?void 0:e.default)!=null?v:J,n=e!=null&&e.filterKeys?k(e.filterKeys):!1,m=Object.keys((l=e==null?void 0:e.customKeyFilter)!=null?l:{}).length;if(!(t!=null&&t.length))return r;e:for(let c=0;c<t.length;c++){const a=t[c],o={},i={};let u=-1;if(f&&!(e!=null&&e.noFilter)){if(typeof a=="object"){const h=n||Object.keys(a);for(const F of h){const E=x(a,F,a),M=(s=e==null?void 0:e.customKeyFilter)==null?void 0:s[F];if(u=M?M(E,f,a):d(E,f,a),u!==-1&&u!==!1)M?o[F]=u:i[F]=u;else if((e==null?void 0:e.filterMode)==="every")continue e}}else u=d(a,f,a),u!==-1&&u!==!1&&(i.title=u);const g=Object.keys(i).length,y=Object.keys(o).length;if(!g&&!y||(e==null?void 0:e.filterMode)==="union"&&y!==m&&!g||(e==null?void 0:e.filterMode)==="intersection"&&(y!==m||!g))continue}r.push({index:c,matches:{...i,...o}})}return r}function H(t,f,e,r){const d=V([]),n=V(new Map),m=D(()=>r!=null&&r.transform?b(f).map(r==null?void 0:r.transform):b(f));O(()=>{const l=typeof e=="function"?e():b(e),s=typeof l!="string"&&typeof l!="number"?"":String(l),c=Q(m.value,s,{customKeyFilter:t.customKeyFilter,default:t.customFilter,filterKeys:t.filterKeys,filterMode:t.filterMode,noFilter:t.noFilter}),a=b(f),o=[],i=new Map;c.forEach(u=>{let{index:g,matches:y}=u;const h=a[g];o.push(h),i.set(h.value,y)}),d.value=o,n.value=i});function v(l){return n.value.get(l.value)}return{filteredItems:d,filteredMatches:n,getMatches:v}}const U=w({fullscreen:Boolean,retainFocus:{type:Boolean,default:!0},scrollable:Boolean,...L({origin:"center center",scrollStrategy:"block",transition:{component:j},zIndex:2400})},"VDialog"),X=B()({name:"VDialog",props:U(),emits:{"update:modelValue":t=>!0},setup(t,f){let{slots:e}=f;const r=R(t,"modelValue"),{scopeId:d}=T(),n=V();function m(l){var a,o;const s=l.relatedTarget,c=l.target;if(s!==c&&((a=n.value)==null?void 0:a.contentEl)&&((o=n.value)==null?void 0:o.globalTop)&&![document,n.value.contentEl].includes(c)&&!n.value.contentEl.contains(c)){const i=G(n.value.contentEl);if(!i.length)return;const u=i[0],g=i[i.length-1];s===u?g.focus():u.focus()}}A&&P(()=>r.value&&t.retainFocus,l=>{l?document.addEventListener("focusin",m):document.removeEventListener("focusin",m)},{immediate:!0}),P(r,async l=>{var s,c;await C(),l?(s=n.value.contentEl)==null||s.focus({preventScroll:!0}):(c=n.value.activatorEl)==null||c.focus({preventScroll:!0})});const v=D(()=>I({"aria-haspopup":"dialog","aria-expanded":String(r.value)},t.activatorProps));return N(()=>{const[l]=K.filterProps(t);return S(K,I({ref:n,class:["v-dialog",{"v-dialog--fullscreen":t.fullscreen,"v-dialog--scrollable":t.scrollable},t.class],style:t.style},l,{modelValue:r.value,"onUpdate:modelValue":s=>r.value=s,"aria-modal":"true",activatorProps:v.value,role:"dialog"},d),{activator:e.activator,default:function(){for(var s=arguments.length,c=new Array(s),a=0;a<s;a++)c[a]=arguments[a];return S($,{root:"VDialog"},{default:()=>{var o;return[(o=e.default)==null?void 0:o.call(e,...c)]}})}})}),z({},n)}});export{X as V,_ as m,H as u};
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.v-select .v-field .v-text-field__prefix,.v-select .v-field .v-text-field__suffix,.v-select .v-field .v-field__input,.v-select .v-field.v-field{cursor:pointer}.v-select .v-field .v-field__input>input{align-self:flex-start;opacity:1;flex:0 0;position:absolute;width:100%;transition:none;pointer-events:none;caret-color:transparent}.v-select .v-field--dirty .v-select__selection{margin-inline-end:2px}.v-select .v-select__selection-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.v-select__content{overflow:hidden;box-shadow:0 2px 4px -1px var(--v-shadow-key-umbra-opacity),0 4px 5px 0 var(--v-shadow-key-penumbra-opacity),0 1px 10px 0 var(--v-shadow-key-ambient-opacity);border-radius:4px}.v-select__selection{display:inline-flex;align-items:center;letter-spacing:inherit;line-height:inherit;max-width:100%}.v-select .v-select__selection{margin-top:var(--v-input-chips-margin-top);margin-bottom:var(--v-input-chips-margin-bottom)}.v-select .v-select__selection:first-child{margin-inline-start:0}.v-select--selected .v-field .v-field__input>input{opacity:0}.v-select__menu-icon{margin-inline-start:4px;transition:.2s cubic-bezier(.4,0,.2,1)}.v-select--active-menu .v-select__menu-icon{opacity:var(--v-high-emphasis-opacity);transform:rotate(180deg)}.v-virtual-scroll{display:block;flex:1 1 auto;max-width:100%;overflow:auto;position:relative}.v-virtual-scroll__container{display:block}.v-dialog{align-items:center;justify-content:center;margin:auto}.v-dialog>.v-overlay__content{max-height:calc(100% - 48px);width:calc(100% - 48px);max-width:calc(100% - 48px);margin:24px;display:flex;flex-direction:column}.v-dialog>.v-overlay__content>.v-card,.v-dialog>.v-overlay__content>.v-sheet,.v-dialog>.v-overlay__content>form>.v-card,.v-dialog>.v-overlay__content>form>.v-sheet{--v-scrollbar-offset: 0px;border-radius:6px;overflow-y:auto;box-shadow:0 11px 15px -7px var(--v-shadow-key-umbra-opacity),0 24px 38px 3px var(--v-shadow-key-penumbra-opacity),0 9px 46px 8px var(--v-shadow-key-ambient-opacity)}.v-dialog>.v-overlay__content>.v-card,.v-dialog>.v-overlay__content>form>.v-card{display:flex;flex-direction:column}.v-dialog>.v-overlay__content>.v-card>.v-card-item,.v-dialog>.v-overlay__content>form>.v-card>.v-card-item{padding:20px}.v-dialog>.v-overlay__content>.v-card>.v-card-item+.v-card-text,.v-dialog>.v-overlay__content>form>.v-card>.v-card-item+.v-card-text{padding-top:0}.v-dialog>.v-overlay__content>.v-card>.v-card-text,.v-dialog>.v-overlay__content>form>.v-card>.v-card-text{font-size:inherit;letter-spacing:.0094rem;line-height:inherit;padding:20px}.v-dialog--fullscreen{--v-scrollbar-offset: 0px}.v-dialog--fullscreen>.v-overlay__content{border-radius:0;margin:0;padding:0;width:100%;height:100%;max-width:100%;max-height:100%;overflow-y:auto;top:0;left:0}.v-dialog--fullscreen>.v-overlay__content>.v-card,.v-dialog--fullscreen>.v-overlay__content>.v-sheet,.v-dialog--fullscreen>.v-overlay__content>form>.v-card,.v-dialog--fullscreen>.v-overlay__content>form>.v-sheet{min-height:100%;min-width:100%;border-radius:0}.v-dialog--scrollable>.v-overlay__content,.v-dialog--scrollable>.v-overlay__content>form{display:flex;overflow:hidden}.v-dialog--scrollable>.v-overlay__content>.v-card,.v-dialog--scrollable>.v-overlay__content>form>.v-card{display:flex;flex:1 1 100%;flex-direction:column;max-height:100%;max-width:100%}.v-dialog--scrollable>.v-overlay__content>.v-card>.v-card-text,.v-dialog--scrollable>.v-overlay__content>form>.v-card>.v-card-text{backface-visibility:hidden;overflow-y:auto}
|
|
||||||
1
server/static/assets/VDialog.779e5ae2.css
Normal file
1
server/static/assets/VDialog.779e5ae2.css
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.v-dialog{align-items:center;justify-content:center;margin:auto}.v-dialog>.v-overlay__content{max-height:calc(100% - 48px);width:calc(100% - 48px);max-width:calc(100% - 48px);margin:24px;display:flex;flex-direction:column}.v-dialog>.v-overlay__content>.v-card,.v-dialog>.v-overlay__content>.v-sheet,.v-dialog>.v-overlay__content>form>.v-card,.v-dialog>.v-overlay__content>form>.v-sheet{--v-scrollbar-offset: 0px;border-radius:6px;overflow-y:auto;box-shadow:0 11px 15px -7px var(--v-shadow-key-umbra-opacity),0 24px 38px 3px var(--v-shadow-key-penumbra-opacity),0 9px 46px 8px var(--v-shadow-key-ambient-opacity)}.v-dialog>.v-overlay__content>.v-card,.v-dialog>.v-overlay__content>form>.v-card{display:flex;flex-direction:column}.v-dialog>.v-overlay__content>.v-card>.v-card-item,.v-dialog>.v-overlay__content>form>.v-card>.v-card-item{padding:20px}.v-dialog>.v-overlay__content>.v-card>.v-card-item+.v-card-text,.v-dialog>.v-overlay__content>form>.v-card>.v-card-item+.v-card-text{padding-top:0}.v-dialog>.v-overlay__content>.v-card>.v-card-text,.v-dialog>.v-overlay__content>form>.v-card>.v-card-text{font-size:inherit;letter-spacing:.0094rem;line-height:inherit;padding:20px}.v-dialog--fullscreen{--v-scrollbar-offset: 0px}.v-dialog--fullscreen>.v-overlay__content{border-radius:0;margin:0;padding:0;width:100%;height:100%;max-width:100%;max-height:100%;overflow-y:auto;top:0;left:0}.v-dialog--fullscreen>.v-overlay__content>.v-card,.v-dialog--fullscreen>.v-overlay__content>.v-sheet,.v-dialog--fullscreen>.v-overlay__content>form>.v-card,.v-dialog--fullscreen>.v-overlay__content>form>.v-sheet{min-height:100%;min-width:100%;border-radius:0}.v-dialog--scrollable>.v-overlay__content,.v-dialog--scrollable>.v-overlay__content>form{display:flex;overflow:hidden}.v-dialog--scrollable>.v-overlay__content>.v-card,.v-dialog--scrollable>.v-overlay__content>form>.v-card{display:flex;flex:1 1 100%;flex-direction:column;max-height:100%;max-width:100%}.v-dialog--scrollable>.v-overlay__content>.v-card>.v-card-text,.v-dialog--scrollable>.v-overlay__content>form>.v-card>.v-card-text{backface-visibility:hidden;overflow-y:auto}
|
||||||
1
server/static/assets/VSelect.5c080fb6.js
Normal file
1
server/static/assets/VSelect.5c080fb6.js
Normal file
File diff suppressed because one or more lines are too long
1
server/static/assets/VSelect.7e043724.css
Normal file
1
server/static/assets/VSelect.7e043724.css
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.v-select .v-field .v-text-field__prefix,.v-select .v-field .v-text-field__suffix,.v-select .v-field .v-field__input,.v-select .v-field.v-field{cursor:pointer}.v-select .v-field .v-field__input>input{align-self:flex-start;opacity:1;flex:0 0;position:absolute;width:100%;transition:none;pointer-events:none;caret-color:transparent}.v-select .v-field--dirty .v-select__selection{margin-inline-end:2px}.v-select .v-select__selection-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.v-select__content{overflow:hidden;box-shadow:0 2px 4px -1px var(--v-shadow-key-umbra-opacity),0 4px 5px 0 var(--v-shadow-key-penumbra-opacity),0 1px 10px 0 var(--v-shadow-key-ambient-opacity);border-radius:4px}.v-select__selection{display:inline-flex;align-items:center;letter-spacing:inherit;line-height:inherit;max-width:100%}.v-select .v-select__selection{margin-top:var(--v-input-chips-margin-top);margin-bottom:var(--v-input-chips-margin-bottom)}.v-select .v-select__selection:first-child{margin-inline-start:0}.v-select--selected .v-field .v-field__input>input{opacity:0}.v-select__menu-icon{margin-inline-start:4px;transition:.2s cubic-bezier(.4,0,.2,1)}.v-select--active-menu .v-select__menu-icon{opacity:var(--v-high-emphasis-opacity);transform:rotate(180deg)}.v-virtual-scroll{display:block;flex:1 1 auto;max-width:100%;overflow:auto;position:relative}.v-virtual-scroll__container{display:block}
|
||||||
|
|
@ -1 +1 @@
|
||||||
import{p as d,m as h,a as m,j as b,k as v,n as c,q as f,t as u,ac as g,D as t,ad as T}from"./index.f985de17.js";const x=d({fixedHeader:Boolean,fixedFooter:Boolean,height:[Number,String],hover:Boolean,...h(),...m(),...b(),...v()},"VTable"),V=c()({name:"VTable",props:x(),setup(a,n){let{slots:e}=n;const{themeClasses:r}=f(a),{densityClasses:i}=u(a);return g(()=>t(a.tag,{class:["v-table",{"v-table--fixed-height":!!a.height,"v-table--fixed-header":a.fixedHeader,"v-table--fixed-footer":a.fixedFooter,"v-table--has-top":!!e.top,"v-table--has-bottom":!!e.bottom,"v-table--hover":a.hover},r.value,i.value,a.class],style:a.style},{default:()=>{var o,s,l;return[(o=e.top)==null?void 0:o.call(e),e.default?t("div",{class:"v-table__wrapper",style:{height:T(a.height)}},[t("table",null,[e.default()])]):(s=e.wrapper)==null?void 0:s.call(e),(l=e.bottom)==null?void 0:l.call(e)]}})),{}}});export{V,x as m};
|
import{p as d,m as h,a as m,j as b,k as v,n as c,q as f,t as u,ac as g,D as t,ad as T}from"./index.a457a291.js";const x=d({fixedHeader:Boolean,fixedFooter:Boolean,height:[Number,String],hover:Boolean,...h(),...m(),...b(),...v()},"VTable"),V=c()({name:"VTable",props:x(),setup(a,n){let{slots:e}=n;const{themeClasses:r}=f(a),{densityClasses:i}=u(a);return g(()=>t(a.tag,{class:["v-table",{"v-table--fixed-height":!!a.height,"v-table--fixed-header":a.fixedHeader,"v-table--fixed-footer":a.fixedFooter,"v-table--has-top":!!e.top,"v-table--has-bottom":!!e.bottom,"v-table--hover":a.hover},r.value,i.value,a.class],style:a.style},{default:()=>{var o,s,l;return[(o=e.top)==null?void 0:o.call(e),e.default?t("div",{class:"v-table__wrapper",style:{height:T(a.height)}},[t("table",null,[e.default()])]):(s=e.wrapper)==null?void 0:s.call(e),(l=e.bottom)==null?void 0:l.call(e)]}})),{}}});export{V,x as m};
|
||||||
|
|
@ -1 +1 @@
|
||||||
import{p as Z,ae as p,bf as ee,n as te,bg as ae,u as ne,ag as le,o as y,O as x,aq as oe,b4 as ue,b8 as ie,aT as m,bh as re,ac as se,ai as ce,aj as _,bi as de,D as o,bj as fe,J as I,a2 as R,ar as z,as as ve,bk as xe,bl as me,a_ as ge,ax as S,bm as he,ad as we,az as Ve}from"./index.f985de17.js";const ye=Z({autoGrow:Boolean,autofocus:Boolean,counter:[Boolean,Number,String],counterValue:Function,prefix:String,placeholder:String,persistentPlaceholder:Boolean,persistentCounter:Boolean,noResize:Boolean,rows:{type:[Number,String],default:5,validator:e=>!isNaN(parseFloat(e))},maxRows:{type:[Number,String],validator:e=>!isNaN(parseFloat(e))},suffix:String,modelModifiers:Object,...p(),...ee()},"VTextarea"),be=te()({name:"VTextarea",directives:{Intersect:ae},inheritAttrs:!1,props:ye(),emits:{"click:control":e=>!0,"mousedown:control":e=>!0,"update:focused":e=>!0,"update:modelValue":e=>!0},setup(e,A){let{attrs:F,emit:M,slots:i}=A;const u=ne(e,"modelValue"),{isFocused:f,focus:D,blur:G}=le(e),E=y(()=>typeof e.counterValue=="function"?e.counterValue(u.value):(u.value||"").toString().length),U=y(()=>{if(F.maxlength)return F.maxlength;if(!(!e.counter||typeof e.counter!="number"&&typeof e.counter!="string"))return e.counter});function O(t,n){var a,l;!e.autofocus||!t||(l=(a=n[0].target)==null?void 0:a.focus)==null||l.call(a)}const T=x(),g=x(),B=oe(""),h=x(),j=y(()=>e.persistentPlaceholder||f.value||e.active);function b(){var t;h.value!==document.activeElement&&((t=h.value)==null||t.focus()),f.value||D()}function $(t){b(),M("click:control",t)}function q(t){M("mousedown:control",t)}function J(t){t.stopPropagation(),b(),S(()=>{u.value="",he(e["onClick:clear"],t)})}function K(t){var a;const n=t.target;if(u.value=n.value,(a=e.modelModifiers)!=null&&a.trim){const l=[n.selectionStart,n.selectionEnd];S(()=>{n.selectionStart=l[0],n.selectionEnd=l[1]})}}const c=x(),w=x(+e.rows),C=y(()=>["plain","underlined"].includes(e.variant));ue(()=>{e.autoGrow||(w.value=+e.rows)});function d(){!e.autoGrow||S(()=>{if(!c.value||!g.value)return;const t=getComputedStyle(c.value),n=getComputedStyle(g.value.$el),a=parseFloat(t.getPropertyValue("--v-field-padding-top"))+parseFloat(t.getPropertyValue("--v-input-padding-top"))+parseFloat(t.getPropertyValue("--v-field-padding-bottom")),l=c.value.scrollHeight,V=parseFloat(t.lineHeight),P=Math.max(parseFloat(e.rows)*V+a,parseFloat(n.getPropertyValue("--v-input-control-height"))),k=parseFloat(e.maxRows)*V+a||1/0,s=Ve(l!=null?l:0,P,k);w.value=Math.floor((s-a)/V),B.value=we(s)})}ie(d),m(u,d),m(()=>e.rows,d),m(()=>e.maxRows,d),m(()=>e.density,d);let r;return m(c,t=>{t?(r=new ResizeObserver(d),r.observe(c.value)):r==null||r.disconnect()}),re(()=>{r==null||r.disconnect()}),se(()=>{const t=!!(i.counter||e.counter||e.counterValue),n=!!(t||i.details),[a,l]=ce(F),[{modelValue:V,...P}]=_.filterProps(e),[k]=de(e);return o(_,I({ref:T,modelValue:u.value,"onUpdate:modelValue":s=>u.value=s,class:["v-textarea v-text-field",{"v-textarea--prefixed":e.prefix,"v-textarea--suffixed":e.suffix,"v-text-field--prefixed":e.prefix,"v-text-field--suffixed":e.suffix,"v-textarea--auto-grow":e.autoGrow,"v-textarea--no-resize":e.noResize||e.autoGrow,"v-text-field--plain-underlined":C.value},e.class],style:e.style},a,P,{centerAffix:w.value===1&&!C.value,focused:f.value}),{...i,default:s=>{let{isDisabled:v,isDirty:H,isReadonly:L,isValid:Q}=s;return o(fe,I({ref:g,style:{"--v-textarea-control-height":B.value},onClick:$,onMousedown:q,"onClick:clear":J,"onClick:prependInner":e["onClick:prependInner"],"onClick:appendInner":e["onClick:appendInner"]},k,{active:j.value||H.value,centerAffix:w.value===1&&!C.value,dirty:H.value||e.dirty,disabled:v.value,focused:f.value,error:Q.value===!1}),{...i,default:W=>{let{props:{class:N,...X}}=W;return o(R,null,[e.prefix&&o("span",{class:"v-text-field__prefix"},[e.prefix]),z(o("textarea",I({ref:h,class:N,value:u.value,onInput:K,autofocus:e.autofocus,readonly:L.value,disabled:v.value,placeholder:e.placeholder,rows:e.rows,name:e.name,onFocus:b,onBlur:G},X,l),null),[[ve("intersect"),{handler:O},null,{once:!0}]]),e.autoGrow&&z(o("textarea",{class:[N,"v-textarea__sizer"],"onUpdate:modelValue":Y=>u.value=Y,ref:c,readonly:!0,"aria-hidden":"true"},null),[[xe,u.value]]),e.suffix&&o("span",{class:"v-text-field__suffix"},[e.suffix])])}})},details:n?s=>{var v;return o(R,null,[(v=i.details)==null?void 0:v.call(i,s),t&&o(R,null,[o("span",null,null),o(me,{active:e.persistentCounter||f.value,value:E.value,max:U.value},i.counter)])])}:void 0})}),ge({},T,g,h)}});export{be as V};
|
import{p as Z,ae as p,bg as ee,n as te,bh as ae,u as ne,ag as le,o as y,O as x,aq as oe,b4 as ue,bd as ie,aR as m,bi as re,ac as se,ai as ce,aj as z,bj as de,D as o,bk as fe,J as R,a2 as I,ar as A,as as ve,bl as xe,bm as me,b3 as ge,ax as S,bn as he,ad as we,az as Ve}from"./index.a457a291.js";const ye=Z({autoGrow:Boolean,autofocus:Boolean,counter:[Boolean,Number,String],counterValue:Function,prefix:String,placeholder:String,persistentPlaceholder:Boolean,persistentCounter:Boolean,noResize:Boolean,rows:{type:[Number,String],default:5,validator:e=>!isNaN(parseFloat(e))},maxRows:{type:[Number,String],validator:e=>!isNaN(parseFloat(e))},suffix:String,modelModifiers:Object,...p(),...ee()},"VTextarea"),Fe=te()({name:"VTextarea",directives:{Intersect:ae},inheritAttrs:!1,props:ye(),emits:{"click:control":e=>!0,"mousedown:control":e=>!0,"update:focused":e=>!0,"update:modelValue":e=>!0},setup(e,_){let{attrs:b,emit:M,slots:i}=_;const u=ne(e,"modelValue"),{isFocused:f,focus:D,blur:G}=le(e),E=y(()=>typeof e.counterValue=="function"?e.counterValue(u.value):(u.value||"").toString().length),U=y(()=>{if(b.maxlength)return b.maxlength;if(!(!e.counter||typeof e.counter!="number"&&typeof e.counter!="string"))return e.counter});function O(t,n){var a,l;!e.autofocus||!t||(l=(a=n[0].target)==null?void 0:a.focus)==null||l.call(a)}const B=x(),g=x(),H=oe(""),h=x(),j=y(()=>e.persistentPlaceholder||f.value||e.active);function F(){var t;h.value!==document.activeElement&&((t=h.value)==null||t.focus()),f.value||D()}function $(t){F(),M("click:control",t)}function q(t){M("mousedown:control",t)}function J(t){t.stopPropagation(),F(),S(()=>{u.value="",he(e["onClick:clear"],t)})}function K(t){var a;const n=t.target;if(u.value=n.value,(a=e.modelModifiers)!=null&&a.trim){const l=[n.selectionStart,n.selectionEnd];S(()=>{n.selectionStart=l[0],n.selectionEnd=l[1]})}}const c=x(),w=x(+e.rows),C=y(()=>["plain","underlined"].includes(e.variant));ue(()=>{e.autoGrow||(w.value=+e.rows)});function d(){!e.autoGrow||S(()=>{if(!c.value||!g.value)return;const t=getComputedStyle(c.value),n=getComputedStyle(g.value.$el),a=parseFloat(t.getPropertyValue("--v-field-padding-top"))+parseFloat(t.getPropertyValue("--v-input-padding-top"))+parseFloat(t.getPropertyValue("--v-field-padding-bottom")),l=c.value.scrollHeight,V=parseFloat(t.lineHeight),P=Math.max(parseFloat(e.rows)*V+a,parseFloat(n.getPropertyValue("--v-input-control-height"))),k=parseFloat(e.maxRows)*V+a||1/0,s=Ve(l!=null?l:0,P,k);w.value=Math.floor((s-a)/V),H.value=we(s)})}ie(d),m(u,d),m(()=>e.rows,d),m(()=>e.maxRows,d),m(()=>e.density,d);let r;return m(c,t=>{t?(r=new ResizeObserver(d),r.observe(c.value)):r==null||r.disconnect()}),re(()=>{r==null||r.disconnect()}),se(()=>{const t=!!(i.counter||e.counter||e.counterValue),n=!!(t||i.details),[a,l]=ce(b),[{modelValue:V,...P}]=z.filterProps(e),[k]=de(e);return o(z,R({ref:B,modelValue:u.value,"onUpdate:modelValue":s=>u.value=s,class:["v-textarea v-text-field",{"v-textarea--prefixed":e.prefix,"v-textarea--suffixed":e.suffix,"v-text-field--prefixed":e.prefix,"v-text-field--suffixed":e.suffix,"v-textarea--auto-grow":e.autoGrow,"v-textarea--no-resize":e.noResize||e.autoGrow,"v-text-field--plain-underlined":C.value},e.class],style:e.style},a,P,{centerAffix:w.value===1&&!C.value,focused:f.value}),{...i,default:s=>{let{isDisabled:v,isDirty:N,isReadonly:L,isValid:Q}=s;return o(fe,R({ref:g,style:{"--v-textarea-control-height":H.value},onClick:$,onMousedown:q,"onClick:clear":J,"onClick:prependInner":e["onClick:prependInner"],"onClick:appendInner":e["onClick:appendInner"]},k,{active:j.value||N.value,centerAffix:w.value===1&&!C.value,dirty:N.value||e.dirty,disabled:v.value,focused:f.value,error:Q.value===!1}),{...i,default:W=>{let{props:{class:T,...X}}=W;return o(I,null,[e.prefix&&o("span",{class:"v-text-field__prefix"},[e.prefix]),A(o("textarea",R({ref:h,class:T,value:u.value,onInput:K,autofocus:e.autofocus,readonly:L.value,disabled:v.value,placeholder:e.placeholder,rows:e.rows,name:e.name,onFocus:F,onBlur:G},X,l),null),[[ve("intersect"),{handler:O},null,{once:!0}]]),e.autoGrow&&A(o("textarea",{class:[T,"v-textarea__sizer"],"onUpdate:modelValue":Y=>u.value=Y,ref:c,readonly:!0,"aria-hidden":"true"},null),[[xe,u.value]]),e.suffix&&o("span",{class:"v-text-field__suffix"},[e.suffix])])}})},details:n?s=>{var v;return o(I,null,[(v=i.details)==null?void 0:v.call(i,s),t&&o(I,null,[o("span",null,null),o(me,{active:e.persistentCounter||f.value,value:E.value,max:U.value},i.counter)])])}:void 0})}),ge({},B,g,h)}});export{Fe as V};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
import{d as a,h as e,V as n,r as o}from"./index.f985de17.js";const s=a({setup(){const t=o("router-view");return()=>e(n,{class:"layout-wrapper layout-blank"},{default:()=>e(t)})}});export{s as default};
|
import{d as a,h as e,V as n,r as o}from"./index.a457a291.js";const s=a({setup(){const t=o("router-view");return()=>e(n,{class:"layout-wrapper layout-blank"},{default:()=>e(t)})}});export{s as default};
|
||||||
|
|
@ -1 +1 @@
|
||||||
.avatar-center[data-v-57768842]{position:absolute;border:3px solid rgb(var(--v-theme-surface));inset-block-start:-2rem;inset-inline-start:1rem}.member-pricing-bg[data-v-57768842]{position:relative;background-color:rgba(var(--v-theme-on-surface),var(--v-hover-opacity))}.membership-pricing sup[data-v-57768842]{inset-block-start:9px}.v-rating{max-width:100%;display:inline-flex;white-space:nowrap}.v-rating--readonly{pointer-events:none}.v-rating__wrapper{align-items:center;display:inline-flex;flex-direction:column}.v-rating__wrapper--bottom{flex-direction:column-reverse}.v-rating__item{display:inline-flex;position:relative}.v-rating__item label{cursor:pointer}.v-rating__item .v-btn--variant-plain{opacity:1}.v-rating__item .v-btn{transition-property:transform}.v-rating__item .v-btn .v-icon{transition:inherit;transition-timing-function:cubic-bezier(0,0,.2,1)}.v-rating--hover .v-rating__item:hover:not(.v-rating__item--focused) .v-btn{transform:scale(1.25)}.v-rating__item--half{overflow:hidden;position:absolute;clip-path:polygon(0 0,50% 0,50% 100%,0 100%);z-index:1}.v-rating__item--half .v-btn__overlay,.v-rating__item--half:hover .v-btn__overlay{opacity:0}.v-rating__hidden{height:0;opacity:0;position:absolute;width:0}
|
.avatar-center[data-v-cd7ddfc4]{position:absolute;border:3px solid rgb(var(--v-theme-surface));inset-block-start:-2rem;inset-inline-start:1rem}.member-pricing-bg[data-v-cd7ddfc4]{position:relative;background-color:rgba(var(--v-theme-on-surface),var(--v-hover-opacity))}.membership-pricing sup[data-v-cd7ddfc4]{inset-block-start:9px}.v-rating{max-width:100%;display:inline-flex;white-space:nowrap}.v-rating--readonly{pointer-events:none}.v-rating__wrapper{align-items:center;display:inline-flex;flex-direction:column}.v-rating__wrapper--bottom{flex-direction:column-reverse}.v-rating__item{display:inline-flex;position:relative}.v-rating__item label{cursor:pointer}.v-rating__item .v-btn--variant-plain{opacity:1}.v-rating__item .v-btn{transition-property:transform}.v-rating__item .v-btn .v-icon{transition:inherit;transition-timing-function:cubic-bezier(0,0,.2,1)}.v-rating--hover .v-rating__item:hover:not(.v-rating__item--focused) .v-btn{transform:scale(1.25)}.v-rating__item--half{overflow:hidden;position:absolute;clip-path:polygon(0 0,50% 0,50% 100%,0 100%);z-index:1}.v-rating__item--half .v-btn__overlay,.v-rating__item--half:hover .v-btn__overlay{opacity:0}.v-rating__hidden{height:0;opacity:0;position:absolute;width:0}
|
||||||
File diff suppressed because one or more lines are too long
1
server/static/assets/config-setting-old.7e3cf0e2.js
Normal file
1
server/static/assets/config-setting-old.7e3cf0e2.js
Normal file
File diff suppressed because one or more lines are too long
1
server/static/assets/config-setting.4deef524.js
Normal file
1
server/static/assets/config-setting.4deef524.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
server/static/assets/guoba.0a045bba.js
Normal file
1
server/static/assets/guoba.0a045bba.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
import{Q as a,a1 as o,a0 as c,a2 as d,a3 as m,D as i,S as e,W as s,F as u,bn as r,a4 as n,a5 as p,U as f,H as h}from"./index.f985de17.js";const _={class:"d-flex align-center flex-wrap"},b={class:"text-center"},V={__name:"icons",setup(g){const l=["mdi-ab-testing","mdi-abacus","mdi-abjad-arabic","mdi-abjad-hebrew","mdi-abugida-devanagari","mdi-abugida-thai","mdi-access-point","mdi-access-point-check","mdi-access-point-minus","mdi-access-point-network","mdi-access-point-network-off","mdi-access-point-off","mdi-access-point-plus","mdi-access-point-remove","mdi-account-alert-outline","mdi-account-arrow-left-outline","mdi-account-arrow-right-outline","mdi-account-box-multiple-outline","mdi-account-box-outline","mdi-account-cancel-outline","mdi-account-cash-outline","mdi-account-check-outline","mdi-account-child-outline","mdi-account-circle-outline","mdi-account-clock-outline","mdi-account-cog-outline","mdi-account-details-outline","mdi-alarm-light-outline","mdi-alert-box-outline","mdi-alert-circle-check-outline","mdi-alert-decagram-outline","mdi-alert-minus-outline","mdi-alert-outline","mdi-alert-plus-outline","mdi-check-outline","mdi-clipboard-outline","mdi-clipboard-play-outline","mdi-close-outline","mdi-cloud-check-outline","mdi-cloud-download-outline","mdi-cog-outline","mdi-compass-off-outline","mdi-contactless-payment-circle-outline","mdi-crown-outline","mdi-delete-outline","mdi-diamond-outline","mdi-email-open-outline","mdi-emoticon-happy-outline","mdi-file-multiple-outline","mdi-flask-empty-outline"];return(k,x)=>(a(),o("div",null,[c("div",_,[(a(),o(d,null,m(l,t=>i(f,{key:t,class:"mb-6 me-6"},{default:e(()=>[i(s,{class:"py-3 px-4"},{default:e(()=>[i(u,{size:"30",icon:t},null,8,["icon"])]),_:2},1024),i(r,{location:"top",activator:"parent"},{default:e(()=>[n(p(t),1)]),_:2},1024)]),_:2},1024)),64))]),c("div",b,[i(h,{href:"https://materialdesignicons.com/",rel:"noopener noreferrer",color:"primary",target:"_blank"},{default:e(()=>[n(" View All Material Design Icons ")]),_:1})])]))}};export{V as default};
|
import{Q as a,a1 as o,a0 as c,a2 as d,a3 as m,D as i,S as e,W as s,F as u,bo as r,a4 as n,a5 as p,U as f,H as h}from"./index.a457a291.js";const _={class:"d-flex align-center flex-wrap"},b={class:"text-center"},V={__name:"icons",setup(g){const l=["mdi-ab-testing","mdi-abacus","mdi-abjad-arabic","mdi-abjad-hebrew","mdi-abugida-devanagari","mdi-abugida-thai","mdi-access-point","mdi-access-point-check","mdi-access-point-minus","mdi-access-point-network","mdi-access-point-network-off","mdi-access-point-off","mdi-access-point-plus","mdi-access-point-remove","mdi-account-alert-outline","mdi-account-arrow-left-outline","mdi-account-arrow-right-outline","mdi-account-box-multiple-outline","mdi-account-box-outline","mdi-account-cancel-outline","mdi-account-cash-outline","mdi-account-check-outline","mdi-account-child-outline","mdi-account-circle-outline","mdi-account-clock-outline","mdi-account-cog-outline","mdi-account-details-outline","mdi-alarm-light-outline","mdi-alert-box-outline","mdi-alert-circle-check-outline","mdi-alert-decagram-outline","mdi-alert-minus-outline","mdi-alert-outline","mdi-alert-plus-outline","mdi-check-outline","mdi-clipboard-outline","mdi-clipboard-play-outline","mdi-close-outline","mdi-cloud-check-outline","mdi-cloud-download-outline","mdi-cog-outline","mdi-compass-off-outline","mdi-contactless-payment-circle-outline","mdi-crown-outline","mdi-delete-outline","mdi-diamond-outline","mdi-email-open-outline","mdi-emoticon-happy-outline","mdi-file-multiple-outline","mdi-flask-empty-outline"];return(k,x)=>(a(),o("div",null,[c("div",_,[(a(),o(d,null,m(l,t=>i(f,{key:t,class:"mb-6 me-6"},{default:e(()=>[i(s,{class:"py-3 px-4"},{default:e(()=>[i(u,{size:"30",icon:t},null,8,["icon"])]),_:2},1024),i(r,{location:"top",activator:"parent"},{default:e(()=>[n(p(t),1)]),_:2},1024)]),_:2},1024)),64))]),c("div",b,[i(h,{href:"https://materialdesignicons.com/",rel:"noopener noreferrer",color:"primary",target:"_blank"},{default:e(()=>[n(" View All Material Design Icons ")]),_:1})])]))}};export{V as default};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
server/static/assets/login.89c8b29d.js
Normal file
1
server/static/assets/login.89c8b29d.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
||||||
import{ak as commonjsGlobal}from"./index.f985de17.js";var md5$1={exports:{}};/**
|
import{ak as commonjsGlobal}from"./index.a457a291.js";var md5$1={exports:{}};/**
|
||||||
* [js-md5]{@link https://github.com/emn178/js-md5}
|
* [js-md5]{@link https://github.com/emn178/js-md5}
|
||||||
*
|
*
|
||||||
* @namespace md5
|
* @namespace md5
|
||||||
1
server/static/assets/prompt.afc0ddd0.js
Normal file
1
server/static/assets/prompt.afc0ddd0.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
import{bs as b,Q as y,a1 as x,a3 as T,D as e,aD as d,H as g,a2 as C,O as _,o as D,r as I,S as a,aE as M,a0 as s,bt as L,aF as P,a4 as c,W as v,Z as U,aN as B,_ as F,T as n,$ as m,at as N,Y as V,U as S,aC as p}from"./index.f985de17.js";import{a as j,b as A,c as E,d as H}from"./auth-v1-tree.f5524167.js";import{b as h}from"./route-block.7577f022.js";import{V as R}from"./VCheckbox.833361ee.js";import"./VCheckboxBtn.b7a3a8e0.js";const $={__name:"AuthProvider",setup(k){const o=b(),u=[{icon:"mdi-facebook",color:"#4267b2",colorInDark:"#4267b2"},{icon:"mdi-twitter",color:"#1da1f2",colorInDark:"#1da1f2"},{icon:"mdi-github",color:"#272727",colorInDark:"#fff"},{icon:"mdi-google",color:"#db4437",colorInDark:"#db4437"}];return(f,r)=>(y(),x(C,null,T(u,i=>e(g,{key:i.icon,icon:i.icon,variant:"text",color:d(o).global.name.value==="dark"?i.colorInDark:i.color},null,8,["icon","color"])),64))}};const O={class:"auth-wrapper d-flex align-center justify-center pa-4"},Q={class:"d-flex"},W=["innerHTML"],Y=s("h5",{class:"text-h5 font-weight-semibold mb-1"}," Adventure starts here \u{1F680} ",-1),Z=s("p",{class:"mb-0"}," Make your app management easy and fun! ",-1),q={class:"d-flex align-center mt-1 mb-4"},z=s("span",{class:"me-1"},"I agree to",-1),G=s("a",{href:"javascript:void(0)",class:"text-primary"},"privacy policy & terms",-1),J=s("span",null,"Already have an account?",-1),K=s("span",{class:"mx-4"},"or",-1),X={__name:"register",setup(k){const o=_({username:"",email:"",password:"",privacyPolicies:!1}),u=b(),f=D(()=>u.global.name.value==="light"?j:A),r=_(!1);return(i,t)=>{const w=I("RouterLink");return y(),x("div",O,[e(S,{class:"auth-card pa-4 pt-7","max-width":"448"},{default:a(()=>[e(M,{class:"justify-center"},{prepend:a(()=>[s("div",Q,[s("div",{innerHTML:d(L)},null,8,W)])]),default:a(()=>[e(P,{class:"font-weight-semibold text-2xl text-uppercase"},{default:a(()=>[c(" Materio ")]),_:1})]),_:1}),e(v,{class:"pt-2"},{default:a(()=>[Y,Z]),_:1}),e(v,null,{default:a(()=>[e(U,{onSubmit:t[5]||(t[5]=B(()=>{},["prevent"]))},{default:a(()=>[e(F,null,{default:a(()=>[e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.username,"onUpdate:modelValue":t[0]||(t[0]=l=>o.value.username=l),label:"Username"},null,8,["modelValue"])]),_:1}),e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.email,"onUpdate:modelValue":t[1]||(t[1]=l=>o.value.email=l),label:"Email",type:"email"},null,8,["modelValue"])]),_:1}),e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.password,"onUpdate:modelValue":t[2]||(t[2]=l=>o.value.password=l),label:"Password",type:r.value?"text":"password","append-inner-icon":r.value?"mdi-eye-off-outline":"mdi-eye-outline","onClick:appendInner":t[3]||(t[3]=l=>r.value=!r.value)},null,8,["modelValue","type","append-inner-icon"]),s("div",q,[e(R,{id:"privacy-policy",modelValue:o.value.privacyPolicies,"onUpdate:modelValue":t[4]||(t[4]=l=>o.value.privacyPolicies=l),inline:""},null,8,["modelValue"]),e(N,{for:"privacy-policy",style:{opacity:"1"}},{default:a(()=>[z,G]),_:1})]),e(g,{block:"",type:"submit"},{default:a(()=>[c(" Sign up ")]),_:1})]),_:1}),e(n,{cols:"12",class:"text-center text-base"},{default:a(()=>[J,e(w,{class:"text-primary ms-2",to:"login"},{default:a(()=>[c(" Sign in instead ")]),_:1})]),_:1}),e(n,{cols:"12",class:"d-flex align-center"},{default:a(()=>[e(V),K,e(V)]),_:1}),e(n,{cols:"12",class:"text-center"},{default:a(()=>[e($)]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),e(p,{class:"auth-footer-start-tree d-none d-md-block",src:d(E),width:250},null,8,["src"]),e(p,{src:d(H),class:"auth-footer-end-tree d-none d-md-block",width:350},null,8,["src"]),e(p,{class:"auth-footer-mask d-none d-md-block",src:d(f)},null,8,["src"])])}}};typeof h=="function"&&h(X);export{X as default};
|
import{bt as b,Q as y,a1 as x,a3 as T,D as e,aD as d,H as g,a2 as C,O as _,o as D,r as I,S as a,aE as M,a0 as s,bu as L,aF as P,a4 as c,W as v,Z as U,aO as B,_ as F,T as n,$ as m,at as S,Y as V,U as j,aC as p}from"./index.a457a291.js";import{a as A,b as E,c as H,d as N}from"./auth-v1-tree.f5524167.js";import{b as h}from"./route-block.7577f022.js";import{V as R}from"./VCheckbox.650ef329.js";import"./VCheckboxBtn.ae1aff4b.js";const $={__name:"AuthProvider",setup(k){const o=b(),u=[{icon:"mdi-facebook",color:"#4267b2",colorInDark:"#4267b2"},{icon:"mdi-twitter",color:"#1da1f2",colorInDark:"#1da1f2"},{icon:"mdi-github",color:"#272727",colorInDark:"#fff"},{icon:"mdi-google",color:"#db4437",colorInDark:"#db4437"}];return(f,r)=>(y(),x(C,null,T(u,i=>e(g,{key:i.icon,icon:i.icon,variant:"text",color:d(o).global.name.value==="dark"?i.colorInDark:i.color},null,8,["icon","color"])),64))}};const O={class:"auth-wrapper d-flex align-center justify-center pa-4"},Q={class:"d-flex"},W=["innerHTML"],Y=s("h5",{class:"text-h5 font-weight-semibold mb-1"}," Adventure starts here \u{1F680} ",-1),Z=s("p",{class:"mb-0"}," Make your app management easy and fun! ",-1),q={class:"d-flex align-center mt-1 mb-4"},z=s("span",{class:"me-1"},"I agree to",-1),G=s("a",{href:"javascript:void(0)",class:"text-primary"},"privacy policy & terms",-1),J=s("span",null,"Already have an account?",-1),K=s("span",{class:"mx-4"},"or",-1),X={__name:"register",setup(k){const o=_({username:"",email:"",password:"",privacyPolicies:!1}),u=b(),f=D(()=>u.global.name.value==="light"?A:E),r=_(!1);return(i,t)=>{const w=I("RouterLink");return y(),x("div",O,[e(j,{class:"auth-card pa-4 pt-7","max-width":"448"},{default:a(()=>[e(M,{class:"justify-center"},{prepend:a(()=>[s("div",Q,[s("div",{innerHTML:d(L)},null,8,W)])]),default:a(()=>[e(P,{class:"font-weight-semibold text-2xl text-uppercase"},{default:a(()=>[c(" Materio ")]),_:1})]),_:1}),e(v,{class:"pt-2"},{default:a(()=>[Y,Z]),_:1}),e(v,null,{default:a(()=>[e(U,{onSubmit:t[5]||(t[5]=B(()=>{},["prevent"]))},{default:a(()=>[e(F,null,{default:a(()=>[e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.username,"onUpdate:modelValue":t[0]||(t[0]=l=>o.value.username=l),label:"Username"},null,8,["modelValue"])]),_:1}),e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.email,"onUpdate:modelValue":t[1]||(t[1]=l=>o.value.email=l),label:"Email",type:"email"},null,8,["modelValue"])]),_:1}),e(n,{cols:"12"},{default:a(()=>[e(m,{modelValue:o.value.password,"onUpdate:modelValue":t[2]||(t[2]=l=>o.value.password=l),label:"Password",type:r.value?"text":"password","append-inner-icon":r.value?"mdi-eye-off-outline":"mdi-eye-outline","onClick:appendInner":t[3]||(t[3]=l=>r.value=!r.value)},null,8,["modelValue","type","append-inner-icon"]),s("div",q,[e(R,{id:"privacy-policy",modelValue:o.value.privacyPolicies,"onUpdate:modelValue":t[4]||(t[4]=l=>o.value.privacyPolicies=l),inline:""},null,8,["modelValue"]),e(S,{for:"privacy-policy",style:{opacity:"1"}},{default:a(()=>[z,G]),_:1})]),e(g,{block:"",type:"submit"},{default:a(()=>[c(" Sign up ")]),_:1})]),_:1}),e(n,{cols:"12",class:"text-center text-base"},{default:a(()=>[J,e(w,{class:"text-primary ms-2",to:"login"},{default:a(()=>[c(" Sign in instead ")]),_:1})]),_:1}),e(n,{cols:"12",class:"d-flex align-center"},{default:a(()=>[e(V),K,e(V)]),_:1}),e(n,{cols:"12",class:"text-center"},{default:a(()=>[e($)]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),e(p,{class:"auth-footer-start-tree d-none d-md-block",src:d(H),width:250},null,8,["src"]),e(p,{src:d(N),class:"auth-footer-end-tree d-none d-md-block",width:350},null,8,["src"]),e(p,{class:"auth-footer-mask d-none d-md-block",src:d(f)},null,8,["src"])])}}};typeof h=="function"&&h(X);export{X as default};
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
||||||
import{K as F,O as i,Q as B,R as S,S as a,D as e,bv as I,bJ as H,a4 as n,Y as J,aI as f,H as c,J as V,U as k,aF as C,W as P,b1 as Y,_ as y,T as N,$ as q,aH as x,aY as z,a5 as A,bn as O,a0 as w,F as Q,aD as W,P as D}from"./index.f985de17.js";import{d as b}from"./VDialog.311575a1.js";import{q as j}from"./VDataTable.fa01315e.js";import"./VCheckboxBtn.b7a3a8e0.js";import"./VTable.e8eece46.js";const E=w("span",{class:"text-h5"},"\u65B0\u589EToken",-1),G=["onClick"],L={__name:"BingTokens",setup(R){const s=F(),_=i([{title:"Token",align:"start",sortable:!1,key:"Token"},{title:"\u7528\u91CF",key:"Usage"},{title:"\u72B6\u6001",key:"State"},{title:"",key:"actions",sortable:!1}]),r=i(),d=i(),u=i(),m=i(),g=i(),U=o=>o>800?"error":o>600?"warning":"success",p=()=>{D.post(`${s.getters.serverApi}sysconfig`,{token:s.getters.userToken}).then(o=>{o.data&&(o.data.error?s.commit("app/ADD_SNACKBAR",{message:o.data.error,color:"error"}):r.value=o.data.redisConfig.bingTokens)}).catch(o=>{s.commit("app/ADD_SNACKBAR",{message:o.message,color:"error"}),console.log(o)})},v=o=>{s.getters.runmode==="online"?D.post(`${s.getters.serverApi}saveconfig`,{token:s.getters.userToken,...o}).then(l=>{var t;(t=l.data)!=null&&t.error?s.commit("app/ADD_SNACKBAR",{message:l.data.error,color:"error"}):s.commit("app/ADD_SNACKBAR",{message:"\u4FDD\u5B58\u6210\u529F",color:"success"}),p()}).catch(l=>{p(),s.commit("app/ADD_SNACKBAR",{message:l.message,color:"error"}),console.log(l)}):s.commit("app/ADD_SNACKBAR",{message:"\u4EC5\u652F\u6301\u5728\u7EBF\u6A21\u5F0F\u64CD\u4F5C",color:"warn"})},h=()=>{const o={redisConfig:{bingTokens:[...r.value,{Token:m.value,State:"\u6B63\u5E38",Usage:0}]}};v(o),d.value=!1},K=()=>{const o=r.value.findIndex(t=>t.Token===g.value);o!==-1&&r.value.splice(o,1);const l={redisConfig:{bingTokens:[...r.value]}};v(l),u.value=!1},$=async o=>{try{await navigator.clipboard.writeText(o),s.commit("app/ADD_SNACKBAR",{message:"\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",color:"info"})}catch(l){console.error("\u526A\u8D34\u677F\u590D\u5236\u5931\u8D25: ",l)}};return p(),(o,l)=>(B(),S(W(j),{headers:_.value,items:r.value,class:"elevation-1"},{top:a(()=>[e(I,{flat:""},{default:a(()=>[e(H,null,{default:a(()=>[n("Bing Token \u7BA1\u7406")]),_:1}),e(J,{class:"mx-4",inset:"",vertical:""}),e(f),e(b,{modelValue:d.value,"onUpdate:modelValue":l[2]||(l[2]=t=>d.value=t),"max-width":"500px"},{activator:a(({props:t})=>[e(c,V({color:"primary",dark:"",class:"mb-2"},t),{default:a(()=>[n(" \u6DFB\u52A0Token ")]),_:2},1040)]),default:a(()=>[e(k,null,{default:a(()=>[e(C,null,{default:a(()=>[E]),_:1}),e(P,null,{default:a(()=>[e(Y,null,{default:a(()=>[e(y,null,{default:a(()=>[e(N,{cols:"12"},{default:a(()=>[e(q,{modelValue:m.value,"onUpdate:modelValue":l[0]||(l[0]=t=>m.value=t),label:"Token"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})]),_:1}),e(x,null,{default:a(()=>[e(f),e(c,{color:"blue-darken-1",variant:"text",onClick:l[1]||(l[1]=t=>{d.value=!1,m.value=""})},{default:a(()=>[n(" \u53D6\u6D88 ")]),_:1}),e(c,{color:"blue-darken-1",variant:"text",onClick:h},{default:a(()=>[n(" \u6DFB\u52A0 ")]),_:1})]),_:1})]),_:1})]),_:1},8,["modelValue"]),e(b,{modelValue:u.value,"onUpdate:modelValue":l[4]||(l[4]=t=>u.value=t),"max-width":"550px"},{default:a(()=>[e(k,null,{default:a(()=>[e(C,{class:"text-h5"},{default:a(()=>[n("\u662F\u5426\u786E\u8BA4\u5220\u9664\u8BE5Token\uFF1F\u5220\u9664\u540E\u5C06\u65E0\u6CD5\u6062\u590D\uFF01")]),_:1}),e(x,null,{default:a(()=>[e(f),e(c,{color:"blue-darken-1",variant:"text",onClick:l[3]||(l[3]=t=>u.value=!1)},{default:a(()=>[n("\u53D6\u6D88")]),_:1}),e(c,{color:"error",variant:"text",onClick:K},{default:a(()=>[n("\u786E\u8BA4\u5220\u9664")]),_:1}),e(f)]),_:1})]),_:1})]),_:1},8,["modelValue"])]),_:1})]),"item.Usage":a(({item:t})=>[e(z,{color:U(t.columns.Usage)},{default:a(()=>[n(A(t.columns.Usage),1)]),_:2},1032,["color"])]),"item.Token":a(({item:t})=>[e(O,{activator:"parent",location:"bottom",text:t.columns.Token},{activator:a(({props:T})=>[w("span",V(T,{onClick:M=>$(t.columns.Token)}),A(t.columns.Token.substring(0,50))+"... ",17,G)]),_:2},1032,["text"])]),"item.actions":a(({item:t})=>[e(Q,{size:"small",onClick:T=>{g.value=t.raw.Token,u.value=!0}},{default:a(()=>[n(" mdi-delete ")]),_:2},1032,["onClick"])]),_:1},8,["headers","items"]))}},oe={__name:"tokens",setup(R){return(s,_)=>(B(),S(y,null,{default:a(()=>[e(N,{cols:"12"},{default:a(()=>[e(k,null,{default:a(()=>[e(L)]),_:1})]),_:1})]),_:1}))}};export{oe as default};
|
|
||||||
1
server/static/assets/tokens.df4f7fb4.js
Normal file
1
server/static/assets/tokens.df4f7fb4.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
import{K as F,O as i,Q as B,R as S,S as a,D as e,bw as I,bK as H,a4 as n,Y as P,aI as f,H as c,J as V,U as k,aF as C,W as q,aV as z,_ as w,T as y,$ as J,aH as x,aU as O,a5 as A,bo as Q,a0 as N,F as W,aD as Y,P as D}from"./index.a457a291.js";import{V as b}from"./VDialog.368f468c.js";import{q as j}from"./VDataTable.e3c98ce6.js";import"./VSelect.5c080fb6.js";import"./VCheckboxBtn.ae1aff4b.js";import"./VTable.4358e1b9.js";const E=N("span",{class:"text-h5"},"\u65B0\u589EToken",-1),G=["onClick"],L={__name:"BingTokens",setup(U){const s=F(),_=i([{title:"Token",align:"start",sortable:!1,key:"Token"},{title:"\u7528\u91CF",key:"Usage"},{title:"\u72B6\u6001",key:"State"},{title:"",key:"actions",sortable:!1}]),r=i(),d=i(),u=i(),m=i(),g=i(),K=o=>o>800?"error":o>600?"warning":"success",p=()=>{D.post(`${s.getters.serverApi}sysconfig`,{token:s.getters.userToken}).then(o=>{o.data&&(o.data.error?s.commit("app/ADD_SNACKBAR",{message:o.data.error,color:"error"}):r.value=o.data.redisConfig.bingTokens)}).catch(o=>{s.commit("app/ADD_SNACKBAR",{message:o.message,color:"error"}),console.log(o)})},v=o=>{s.getters.runmode==="online"?D.post(`${s.getters.serverApi}saveconfig`,{token:s.getters.userToken,...o}).then(l=>{var t;(t=l.data)!=null&&t.error?s.commit("app/ADD_SNACKBAR",{message:l.data.error,color:"error"}):s.commit("app/ADD_SNACKBAR",{message:"\u4FDD\u5B58\u6210\u529F",color:"success"}),p()}).catch(l=>{p(),s.commit("app/ADD_SNACKBAR",{message:l.message,color:"error"}),console.log(l)}):s.commit("app/ADD_SNACKBAR",{message:"\u4EC5\u652F\u6301\u5728\u7EBF\u6A21\u5F0F\u64CD\u4F5C",color:"warn"})},R=()=>{const o={redisConfig:{bingTokens:[...r.value,{Token:m.value,State:"\u6B63\u5E38",Usage:0}]}};v(o),d.value=!1},h=()=>{const o=r.value.findIndex(t=>t.Token===g.value);o!==-1&&r.value.splice(o,1);const l={redisConfig:{bingTokens:[...r.value]}};v(l),u.value=!1},$=async o=>{try{await navigator.clipboard.writeText(o),s.commit("app/ADD_SNACKBAR",{message:"\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",color:"info"})}catch(l){console.error("\u526A\u8D34\u677F\u590D\u5236\u5931\u8D25: ",l)}};return p(),(o,l)=>(B(),S(Y(j),{headers:_.value,items:r.value,class:"elevation-1"},{top:a(()=>[e(I,{flat:""},{default:a(()=>[e(H,null,{default:a(()=>[n("Bing Token \u7BA1\u7406")]),_:1}),e(P,{class:"mx-4",inset:"",vertical:""}),e(f),e(b,{modelValue:d.value,"onUpdate:modelValue":l[2]||(l[2]=t=>d.value=t),"max-width":"500px"},{activator:a(({props:t})=>[e(c,V({color:"primary",dark:"",class:"mb-2"},t),{default:a(()=>[n(" \u6DFB\u52A0Token ")]),_:2},1040)]),default:a(()=>[e(k,null,{default:a(()=>[e(C,null,{default:a(()=>[E]),_:1}),e(q,null,{default:a(()=>[e(z,null,{default:a(()=>[e(w,null,{default:a(()=>[e(y,{cols:"12"},{default:a(()=>[e(J,{modelValue:m.value,"onUpdate:modelValue":l[0]||(l[0]=t=>m.value=t),label:"Token"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1})]),_:1}),e(x,null,{default:a(()=>[e(f),e(c,{color:"blue-darken-1",variant:"text",onClick:l[1]||(l[1]=t=>{d.value=!1,m.value=""})},{default:a(()=>[n(" \u53D6\u6D88 ")]),_:1}),e(c,{color:"blue-darken-1",variant:"text",onClick:R},{default:a(()=>[n(" \u6DFB\u52A0 ")]),_:1})]),_:1})]),_:1})]),_:1},8,["modelValue"]),e(b,{modelValue:u.value,"onUpdate:modelValue":l[4]||(l[4]=t=>u.value=t),"max-width":"550px"},{default:a(()=>[e(k,null,{default:a(()=>[e(C,{class:"text-h5"},{default:a(()=>[n("\u662F\u5426\u786E\u8BA4\u5220\u9664\u8BE5Token\uFF1F\u5220\u9664\u540E\u5C06\u65E0\u6CD5\u6062\u590D\uFF01")]),_:1}),e(x,null,{default:a(()=>[e(f),e(c,{color:"blue-darken-1",variant:"text",onClick:l[3]||(l[3]=t=>u.value=!1)},{default:a(()=>[n("\u53D6\u6D88")]),_:1}),e(c,{color:"error",variant:"text",onClick:h},{default:a(()=>[n("\u786E\u8BA4\u5220\u9664")]),_:1}),e(f)]),_:1})]),_:1})]),_:1},8,["modelValue"])]),_:1})]),"item.Usage":a(({item:t})=>[e(O,{color:K(t.columns.Usage)},{default:a(()=>[n(A(t.columns.Usage),1)]),_:2},1032,["color"])]),"item.Token":a(({item:t})=>[e(Q,{activator:"parent",location:"bottom",text:t.columns.Token},{activator:a(({props:T})=>[N("span",V(T,{onClick:M=>$(t.columns.Token)}),A(t.columns.Token.substring(0,50))+"... ",17,G)]),_:2},1032,["text"])]),"item.actions":a(({item:t})=>[e(W,{size:"small",onClick:T=>{g.value=t.raw.Token,u.value=!0}},{default:a(()=>[n(" mdi-delete ")]),_:2},1032,["onClick"])]),_:1},8,["headers","items"]))}},le={__name:"tokens",setup(U){return(s,_)=>(B(),S(w,null,{default:a(()=>[e(y,{cols:"12"},{default:a(()=>[e(k,null,{default:a(()=>[e(L)]),_:1})]),_:1})]),_:1}))}};export{le as default};
|
||||||
|
|
@ -1 +1 @@
|
||||||
import{aB as c,Q as l,R as n,S as e,D as s,W as d,U as _,a0 as t,_ as h,T as o}from"./index.f985de17.js";const u={},r=t("div",null,[t("h1",{class:"text-h1"}," Heading 1 "),t("span",null,"font-size: 6rem / line-height: 6rem / font-weight: 300")],-1),m=t("div",null,[t("h2",{class:"text-h2"}," Heading 2 "),t("span",null,"font-size: 3.75rem / line-height: 3.75rem / font-weight: 300")],-1),f=t("div",null,[t("h3",{class:"text-h3"}," Heading 3 "),t("span",null,"font-size: 3rem / line-height: 3.125rem / font-weight: 400")],-1),p=t("div",null,[t("h4",{class:"text-h4"}," Heading 4 "),t("span",null,"font-size: 2.125rem / line-height: 2.5rem / font-weight: 400")],-1),x=t("div",null,[t("h5",{class:"text-h5"}," Heading 5 "),t("span",null,"font-size: 1.5rem / line-height: 2rem / font-weight: 400")],-1),g=t("div",null,[t("h6",{class:"text-h6"}," Heading 6 "),t("span",null,"font-size: 1.25rem / line-height: 2rem / font-weight: 500")],-1);function b(a,i){return l(),n(_,{title:"Headlines"},{default:e(()=>[s(d,{class:"d-flex flex-column gap-y-8"},{default:e(()=>[r,m,f,p,x,g]),_:1})]),_:1})}const w=c(u,[["render",b]]),k={},y=t("span",{class:"text-subtitle-1 text-no-wrap"},"text-subtitle-1",-1),z=t("p",{class:"text-subtitle-1 text-truncate mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),C=t("small",null,"font-size: 1rem / line-height: 1.75rem / font-weight: 400",-1),v=t("span",{class:"text-subtitle-2 text-no-wrap"},"text-subtitle-2",-1),$=t("p",{class:"text-subtitle-2 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),H=t("small",null,"font-size: 0.875rem / line-height: 1.375rem / font-weight: 500",-1),T=t("span",{class:"text-body-1 text-no-wrap"},"text-body-1",-1),V=t("p",{class:"text-body-1 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),B=t("small",null,"font-size: 1rem / line-height: 1.5rem / font-weight: 400",-1),N=t("span",{class:"text-body-2 text-no-wrap"},"text-body-2",-1),R=t("p",{class:"text-body-2 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),D=t("small",null,"font-size: 0.875rem / line-height: 1.25rem / font-weight: 400",-1),Q=t("span",{class:"text-caption"},"text-caption",-1),S=t("p",{class:"text-caption mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),U=t("small",null,"font-size: 0.75rem / line-height: 1.25rem / font-weight: 400",-1),W=t("span",{class:"text-overline text-no-wrap"},"text-overline",-1),j=t("p",{class:"text-overline mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),q=t("small",null,"font-size: 0.75rem / line-height: 2rem / font-weight: 500",-1),A=t("span",{class:"text-button"},"text-button",-1),E=t("p",{class:"text-button mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),F=t("small",null,"font-size: 0.875rem / line-height: 2.25rem / font-weight: 500",-1);function G(a,i){return l(),n(_,{title:"Texts"},{default:e(()=>[s(d,null,{default:e(()=>[s(h,null,{default:e(()=>[s(o,{cols:"4",md:"2"},{default:e(()=>[y]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[z,C]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[v]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[$,H]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[T]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[V,B]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[N]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[R,D]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[Q]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[S,U]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[W]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[j,q]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[A]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[E,F]),_:1})]),_:1})]),_:1})]),_:1})}const I=c(k,[["render",G]]),L={__name:"typography",setup(a){return(i,J)=>(l(),n(h,null,{default:e(()=>[s(o,{cols:"12"},{default:e(()=>[s(w)]),_:1}),s(o,{cols:"12"},{default:e(()=>[s(I)]),_:1})]),_:1}))}};export{L as default};
|
import{aB as c,Q as l,R as n,S as e,D as s,W as d,U as _,a0 as t,_ as h,T as o}from"./index.a457a291.js";const u={},r=t("div",null,[t("h1",{class:"text-h1"}," Heading 1 "),t("span",null,"font-size: 6rem / line-height: 6rem / font-weight: 300")],-1),m=t("div",null,[t("h2",{class:"text-h2"}," Heading 2 "),t("span",null,"font-size: 3.75rem / line-height: 3.75rem / font-weight: 300")],-1),f=t("div",null,[t("h3",{class:"text-h3"}," Heading 3 "),t("span",null,"font-size: 3rem / line-height: 3.125rem / font-weight: 400")],-1),p=t("div",null,[t("h4",{class:"text-h4"}," Heading 4 "),t("span",null,"font-size: 2.125rem / line-height: 2.5rem / font-weight: 400")],-1),x=t("div",null,[t("h5",{class:"text-h5"}," Heading 5 "),t("span",null,"font-size: 1.5rem / line-height: 2rem / font-weight: 400")],-1),g=t("div",null,[t("h6",{class:"text-h6"}," Heading 6 "),t("span",null,"font-size: 1.25rem / line-height: 2rem / font-weight: 500")],-1);function b(a,i){return l(),n(_,{title:"Headlines"},{default:e(()=>[s(d,{class:"d-flex flex-column gap-y-8"},{default:e(()=>[r,m,f,p,x,g]),_:1})]),_:1})}const w=c(u,[["render",b]]),k={},y=t("span",{class:"text-subtitle-1 text-no-wrap"},"text-subtitle-1",-1),z=t("p",{class:"text-subtitle-1 text-truncate mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),C=t("small",null,"font-size: 1rem / line-height: 1.75rem / font-weight: 400",-1),v=t("span",{class:"text-subtitle-2 text-no-wrap"},"text-subtitle-2",-1),$=t("p",{class:"text-subtitle-2 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),H=t("small",null,"font-size: 0.875rem / line-height: 1.375rem / font-weight: 500",-1),T=t("span",{class:"text-body-1 text-no-wrap"},"text-body-1",-1),V=t("p",{class:"text-body-1 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),B=t("small",null,"font-size: 1rem / line-height: 1.5rem / font-weight: 400",-1),N=t("span",{class:"text-body-2 text-no-wrap"},"text-body-2",-1),R=t("p",{class:"text-body-2 mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),D=t("small",null,"font-size: 0.875rem / line-height: 1.25rem / font-weight: 400",-1),Q=t("span",{class:"text-caption"},"text-caption",-1),S=t("p",{class:"text-caption mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),U=t("small",null,"font-size: 0.75rem / line-height: 1.25rem / font-weight: 400",-1),W=t("span",{class:"text-overline text-no-wrap"},"text-overline",-1),j=t("p",{class:"text-overline mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),q=t("small",null,"font-size: 0.75rem / line-height: 2rem / font-weight: 500",-1),A=t("span",{class:"text-button"},"text-button",-1),E=t("p",{class:"text-button mb-0"}," Cupcake ipsum dolor sit amet fruitcake donut chocolate. ",-1),F=t("small",null,"font-size: 0.875rem / line-height: 2.25rem / font-weight: 500",-1);function G(a,i){return l(),n(_,{title:"Texts"},{default:e(()=>[s(d,null,{default:e(()=>[s(h,null,{default:e(()=>[s(o,{cols:"4",md:"2"},{default:e(()=>[y]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[z,C]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[v]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[$,H]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[T]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[V,B]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[N]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[R,D]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[Q]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[S,U]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[W]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[j,q]),_:1}),s(o,{cols:"4",md:"2"},{default:e(()=>[A]),_:1}),s(o,{cols:"8",md:"10"},{default:e(()=>[E,F]),_:1})]),_:1})]),_:1})]),_:1})}const I=c(k,[["render",G]]),L={__name:"typography",setup(a){return(i,J)=>(l(),n(h,null,{default:e(()=>[s(o,{cols:"12"},{default:e(()=>[s(w)]),_:1}),s(o,{cols:"12"},{default:e(()=>[s(I)]),_:1})]),_:1}))}};export{L as default};
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>ChatGPT 工具箱</title>
|
<title>ChatGPT 工具箱</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./loader.css" />
|
<link rel="stylesheet" type="text/css" href="./loader.css" />
|
||||||
<script type="module" crossorigin src="./assets/index.f985de17.js"></script>
|
<script type="module" crossorigin src="./assets/index.a457a291.js"></script>
|
||||||
<link rel="stylesheet" href="./assets/index.7236eb96.css">
|
<link rel="stylesheet" href="./assets/index.6bc88549.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ import fetch, {
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import WebSocket from 'ws'
|
import WebSocket from 'ws'
|
||||||
import { Config, pureSydneyInstruction } from './config.js'
|
import { Config, pureSydneyInstruction } from './config.js'
|
||||||
import { formatDate, getMasterQQ, isCN, getUserData } from './common.js'
|
import { formatDate, getMasterQQ, isCN, getUserData, limitString } from './common.js'
|
||||||
import delay from 'delay'
|
import delay from 'delay'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { getProxy } from './proxy.js'
|
import { getProxy } from './proxy.js'
|
||||||
|
import Version from './version.js'
|
||||||
|
|
||||||
if (!globalThis.fetch) {
|
if (!globalThis.fetch) {
|
||||||
globalThis.fetch = fetch
|
globalThis.fetch = fetch
|
||||||
|
|
@ -80,7 +81,7 @@ export default class SydneyAIClient {
|
||||||
// 'x-ms-client-request-id': crypto.randomUUID(),
|
// '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.3 OS/macOS',
|
// '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}`,
|
// cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
|
||||||
Referer: 'https://edgeservices.bing.com/edgesvc/chat?udsframed=1&form=SHORUN&clientscopes=chat,noheader,channelstable,',
|
Referer: 'https://edgeservices.bing.com/edgesvc/chat?udsframed=1&form=SHORUN&clientscopes=chat,noheader,channelstable,'
|
||||||
// 'Referrer-Policy': 'origin-when-cross-origin',
|
// 'Referrer-Policy': 'origin-when-cross-origin',
|
||||||
// Workaround for request being blocked due to geolocation
|
// Workaround for request being blocked due to geolocation
|
||||||
// 'x-forwarded-for': '1.1.1.1'
|
// 'x-forwarded-for': '1.1.1.1'
|
||||||
|
|
@ -100,12 +101,12 @@ export default class SydneyAIClient {
|
||||||
this.opts.host = 'https://edgeservices.bing.com/edgesvc'
|
this.opts.host = 'https://edgeservices.bing.com/edgesvc'
|
||||||
}
|
}
|
||||||
logger.mark('使用host:' + this.opts.host)
|
logger.mark('使用host:' + this.opts.host)
|
||||||
let response = await fetch(`${this.opts.host}/turing/conversation/create?bundleVersion=1.1055.6`, fetchOptions)
|
let response = await fetch(`${this.opts.host}/turing/conversation/create?bundleVersion=1.1055.10`, fetchOptions)
|
||||||
let text = await response.text()
|
let text = await response.text()
|
||||||
let retry = 10
|
let retry = 10
|
||||||
while (retry >= 0 && response.status === 200 && !text) {
|
while (retry >= 0 && response.status === 200 && !text) {
|
||||||
await delay(400)
|
await delay(400)
|
||||||
response = await fetch(`${this.opts.host}/turing/conversation/create`, fetchOptions)
|
response = await fetch(`${this.opts.host}/turing/conversation/create?bundleVersion=1.1055.10`, fetchOptions)
|
||||||
text = await response.text()
|
text = await response.text()
|
||||||
retry--
|
retry--
|
||||||
}
|
}
|
||||||
|
|
@ -137,8 +138,12 @@ export default class SydneyAIClient {
|
||||||
agent = proxy(this.opts.proxy)
|
agent = proxy(this.opts.proxy)
|
||||||
}
|
}
|
||||||
if (Config.sydneyWebsocketUseProxy) {
|
if (Config.sydneyWebsocketUseProxy) {
|
||||||
|
if (!Config.sydneyReverseProxy) {
|
||||||
|
logger.warn('用户开启了对话反代,但是没有配置反代,忽略反代配置')
|
||||||
|
} else {
|
||||||
sydneyHost = Config.sydneyReverseProxy.replace('https://', 'wss://').replace('http://', 'ws://')
|
sydneyHost = Config.sydneyReverseProxy.replace('https://', 'wss://').replace('http://', 'ws://')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
logger.mark(`use sydney websocket host: ${sydneyHost}`)
|
logger.mark(`use sydney websocket host: ${sydneyHost}`)
|
||||||
let host = sydneyHost + '/sydney/ChatHub'
|
let host = sydneyHost + '/sydney/ChatHub'
|
||||||
if (encryptedconversationsignature) {
|
if (encryptedconversationsignature) {
|
||||||
|
|
@ -221,8 +226,8 @@ export default class SydneyAIClient {
|
||||||
timeout = Config.defaultTimeoutMs,
|
timeout = Config.defaultTimeoutMs,
|
||||||
firstMessageTimeout = Config.sydneyFirstMessageTimeout,
|
firstMessageTimeout = Config.sydneyFirstMessageTimeout,
|
||||||
groupId, nickname, qq, groupName, chats, botName, masterName,
|
groupId, nickname, qq, groupName, chats, botName, masterName,
|
||||||
messageType = 'Chat'
|
messageType = 'Chat',
|
||||||
|
toSummaryFileContent
|
||||||
} = opts
|
} = opts
|
||||||
// if (messageType === 'Chat') {
|
// if (messageType === 'Chat') {
|
||||||
// logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容')
|
// logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容')
|
||||||
|
|
@ -371,6 +376,10 @@ export default class SydneyAIClient {
|
||||||
let maxConv = Config.maxNumUserMessagesInConversation
|
let maxConv = Config.maxNumUserMessagesInConversation
|
||||||
const currentDate = moment().format('YYYY-MM-DDTHH:mm:ssZ')
|
const currentDate = moment().format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
const imageDate = await this.kblobImage(opts.imageUrl)
|
const imageDate = await this.kblobImage(opts.imageUrl)
|
||||||
|
if (toSummaryFileContent?.content) {
|
||||||
|
// message = `请不要进行搜索,用户的问题是:"${message}"`
|
||||||
|
messageType = 'Chat'
|
||||||
|
}
|
||||||
let argument0 = {
|
let argument0 = {
|
||||||
source: 'cib',
|
source: 'cib',
|
||||||
optionsSets,
|
optionsSets,
|
||||||
|
|
@ -414,10 +423,12 @@ export default class SydneyAIClient {
|
||||||
text: message,
|
text: message,
|
||||||
messageType,
|
messageType,
|
||||||
userIpAddress: await generateRandomIP(),
|
userIpAddress: await generateRandomIP(),
|
||||||
timestamp: currentDate
|
timestamp: currentDate,
|
||||||
|
privacy: 'Internal'
|
||||||
// messageType: 'SearchQuery'
|
// messageType: 'SearchQuery'
|
||||||
},
|
},
|
||||||
tone: 'Creative',
|
tone: 'Creative',
|
||||||
|
privacy: 'Internal',
|
||||||
conversationSignature,
|
conversationSignature,
|
||||||
participant: {
|
participant: {
|
||||||
id: clientId
|
id: clientId
|
||||||
|
|
@ -439,7 +450,7 @@ export default class SydneyAIClient {
|
||||||
}
|
}
|
||||||
// simulates document summary function on Edge's Bing sidebar
|
// simulates document summary function on Edge's Bing sidebar
|
||||||
// unknown character limit, at least up to 7k
|
// unknown character limit, at least up to 7k
|
||||||
if (groupId) {
|
if (groupId && !toSummaryFileContent?.content) {
|
||||||
context += '注意,你现在正在一个qq群里和人聊天,现在问你问题的人是' + `${nickname}(${qq})。`
|
context += '注意,你现在正在一个qq群里和人聊天,现在问你问题的人是' + `${nickname}(${qq})。`
|
||||||
if (Config.enforceMaster && master) {
|
if (Config.enforceMaster && master) {
|
||||||
if (qq === master) {
|
if (qq === master) {
|
||||||
|
|
@ -461,13 +472,11 @@ export default class SydneyAIClient {
|
||||||
admin: '管理员'
|
admin: '管理员'
|
||||||
}
|
}
|
||||||
if (chats) {
|
if (chats) {
|
||||||
context += `以下是一段qq群内的对话,提供给你作为上下文,你在回答所有问题时必须优先考虑这些信息,结合这些上下文进行回答,这很重要!!!。"
|
context += '以下是一段qq群内的对话,提供给你作为上下文,你在回答所有问题时必须优先考虑这些信息,结合这些上下文进行回答,这很重要!!!。"'
|
||||||
`
|
|
||||||
context += chats
|
context += chats
|
||||||
.map(chat => {
|
.map(chat => {
|
||||||
let sender = chat.sender || {}
|
let sender = chat.sender || chat || {}
|
||||||
// if (sender.user_id === Bot.uin && chat.raw_message.startsWith('建议的回复')) {
|
if (chat.raw_message?.startsWith('建议的回复')) {
|
||||||
if (chat.raw_message.startsWith('建议的回复')) {
|
|
||||||
// 建议的回复太容易污染设定导致对话太固定跑偏了
|
// 建议的回复太容易污染设定导致对话太固定跑偏了
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
@ -486,7 +495,19 @@ export default class SydneyAIClient {
|
||||||
}).join('\n')
|
}).join('\n')
|
||||||
context += '\n'
|
context += '\n'
|
||||||
}
|
}
|
||||||
if (context) {
|
if (toSummaryFileContent?.content) {
|
||||||
|
// 忽略context 不然可能会爆炸
|
||||||
|
obj.arguments[0].previousMessages.push({
|
||||||
|
author: 'user',
|
||||||
|
description: limitString(toSummaryFileContent?.content, 20000, true),
|
||||||
|
contextType: 'WebPage',
|
||||||
|
messageType: 'Context',
|
||||||
|
sourceName: toSummaryFileContent?.name,
|
||||||
|
sourceUrl: 'file:///C:/Users/turing/Downloads/Documents/' + toSummaryFileContent?.name || 'file.pdf',
|
||||||
|
// locale: 'und',
|
||||||
|
// privacy: 'Internal'
|
||||||
|
})
|
||||||
|
} else if (context) {
|
||||||
obj.arguments[0].previousMessages.push({
|
obj.arguments[0].previousMessages.push({
|
||||||
author: 'user',
|
author: 'user',
|
||||||
description: context,
|
description: context,
|
||||||
|
|
@ -655,7 +676,7 @@ export default class SydneyAIClient {
|
||||||
text: replySoFar.join('')
|
text: replySoFar.join('')
|
||||||
}
|
}
|
||||||
// 获取到图片内容
|
// 获取到图片内容
|
||||||
if (messages.some(obj => obj.contentType === "IMAGE")) {
|
if (messages.some(obj => obj.contentType === 'IMAGE')) {
|
||||||
message.imageTag = messages.filter(m => m.contentType === 'IMAGE').map(m => m.text).join('')
|
message.imageTag = messages.filter(m => m.contentType === 'IMAGE').map(m => m.text).join('')
|
||||||
}
|
}
|
||||||
message.text = messages.filter(m => m.author === 'bot' && m.contentType != 'IMAGE').map(m => m.text).join('')
|
message.text = messages.filter(m => m.author === 'bot' && m.contentType != 'IMAGE').map(m => m.text).join('')
|
||||||
|
|
|
||||||
394
utils/alibaba/qwen-api.js
Normal file
394
utils/alibaba/qwen-api.js
Normal file
|
|
@ -0,0 +1,394 @@
|
||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||||
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||||
|
if (ar || !(i in from)) {
|
||||||
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||||
|
ar[i] = from[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to.concat(ar || Array.prototype.slice.call(from));
|
||||||
|
};
|
||||||
|
import Keyv from 'keyv';
|
||||||
|
import pTimeout from 'p-timeout';
|
||||||
|
import QuickLRU from 'quick-lru';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import * as tokenizer from './tokenizer.js';
|
||||||
|
import * as types from './types.js';
|
||||||
|
import globalFetch from 'node-fetch';
|
||||||
|
var CHATGPT_MODEL = 'qwen-turbo'; // qwen-plus
|
||||||
|
var USER_LABEL_DEFAULT = 'User';
|
||||||
|
var ASSISTANT_LABEL_DEFAULT = '同义千问';
|
||||||
|
var QwenApi = /** @class */ (function () {
|
||||||
|
/**
|
||||||
|
* Creates a new client wrapper around Qwen's chat completion API, mimicing the official ChatGPT webapp's functionality as closely as possible.
|
||||||
|
*
|
||||||
|
* @param opts
|
||||||
|
*/
|
||||||
|
function QwenApi(opts) {
|
||||||
|
var apiKey = opts.apiKey, _a = opts.apiBaseUrl, apiBaseUrl = _a === void 0 ? 'https://dashscope.aliyuncs.com/api/v1' : _a, _b = opts.debug, debug = _b === void 0 ? false : _b, messageStore = opts.messageStore, completionParams = opts.completionParams, parameters = opts.parameters, systemMessage = opts.systemMessage, getMessageById = opts.getMessageById, upsertMessage = opts.upsertMessage, _c = opts.fetch, fetch = _c === void 0 ? globalFetch : _c;
|
||||||
|
this._apiKey = apiKey;
|
||||||
|
this._apiBaseUrl = apiBaseUrl;
|
||||||
|
this._debug = !!debug;
|
||||||
|
this._fetch = fetch;
|
||||||
|
this._completionParams = __assign({ model: CHATGPT_MODEL, parameters: __assign({ top_p: 0.5, top_k: 50, temperature: 1.0, seed: 114514, enable_search: true, result_format: "text", incremental_output: false }, parameters) }, completionParams);
|
||||||
|
this._systemMessage = systemMessage;
|
||||||
|
if (this._systemMessage === undefined) {
|
||||||
|
var currentDate = new Date().toISOString().split('T')[0];
|
||||||
|
this._systemMessage = "You are ChatGPT, a large language model trained by Qwen. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: ".concat(currentDate);
|
||||||
|
}
|
||||||
|
this._getMessageById = getMessageById !== null && getMessageById !== void 0 ? getMessageById : this._defaultGetMessageById;
|
||||||
|
this._upsertMessage = upsertMessage !== null && upsertMessage !== void 0 ? upsertMessage : this._defaultUpsertMessage;
|
||||||
|
if (messageStore) {
|
||||||
|
this._messageStore = messageStore;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._messageStore = new Keyv({
|
||||||
|
store: new QuickLRU({ maxSize: 10000 })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!this._apiKey) {
|
||||||
|
throw new Error('Qwen missing required apiKey');
|
||||||
|
}
|
||||||
|
if (!this._fetch) {
|
||||||
|
throw new Error('Invalid environment; fetch is not defined');
|
||||||
|
}
|
||||||
|
if (typeof this._fetch !== 'function') {
|
||||||
|
throw new Error('Invalid "fetch" is not a function');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sends a message to the Qwen chat completions endpoint, waits for the response
|
||||||
|
* to resolve, and returns the response.
|
||||||
|
*
|
||||||
|
* If you want your response to have historical context, you must provide a valid `parentMessageId`.
|
||||||
|
*
|
||||||
|
* If you want to receive a stream of partial responses, use `opts.onProgress`.
|
||||||
|
*
|
||||||
|
* Set `debug: true` in the `ChatGPTAPI` constructor to log more info on the full prompt sent to the Qwen chat completions API. You can override the `systemMessage` in `opts` to customize the assistant's instructions.
|
||||||
|
*
|
||||||
|
* @param message - The prompt message to send
|
||||||
|
* @param opts.parentMessageId - Optional ID of the previous message in the conversation (defaults to `undefined`)
|
||||||
|
* @param opts.conversationId - Optional ID of the conversation (defaults to `undefined`)
|
||||||
|
* @param opts.messageId - Optional ID of the message to send (defaults to a random UUID)
|
||||||
|
* @param opts.systemMessage - Optional override for the chat "system message" which acts as instructions to the model (defaults to the ChatGPT system message)
|
||||||
|
* @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout)
|
||||||
|
* @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
|
||||||
|
* @param opts.abortSignal - Optional callback used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
||||||
|
* @param completionParams - Optional overrides to send to the [Qwen chat completion API](https://platform.openai.com/docs/api-reference/chat/create). Options like `temperature` and `presence_penalty` can be tweaked to change the personality of the assistant.
|
||||||
|
*
|
||||||
|
* @returns The response from ChatGPT
|
||||||
|
*/
|
||||||
|
QwenApi.prototype.sendMessage = function (text, opts, role) {
|
||||||
|
if (opts === void 0) { opts = {}; }
|
||||||
|
if (role === void 0) { role = 'user'; }
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var parentMessageId, _a, messageId, timeoutMs, completionParams, conversationId, abortSignal, abortController, message, latestQuestion, _b, messages, maxTokens, numTokens, result, responseP;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_c) {
|
||||||
|
switch (_c.label) {
|
||||||
|
case 0:
|
||||||
|
parentMessageId = opts.parentMessageId, _a = opts.messageId, messageId = _a === void 0 ? uuidv4() : _a, timeoutMs = opts.timeoutMs, completionParams = opts.completionParams, conversationId = opts.conversationId;
|
||||||
|
abortSignal = opts.abortSignal;
|
||||||
|
abortController = null;
|
||||||
|
if (timeoutMs && !abortSignal) {
|
||||||
|
abortController = new AbortController();
|
||||||
|
abortSignal = abortController.signal;
|
||||||
|
}
|
||||||
|
message = {
|
||||||
|
role: role,
|
||||||
|
id: messageId,
|
||||||
|
conversationId: conversationId,
|
||||||
|
parentMessageId: parentMessageId,
|
||||||
|
text: text,
|
||||||
|
};
|
||||||
|
latestQuestion = message;
|
||||||
|
return [4 /*yield*/, this._buildMessages(text, role, opts, completionParams)];
|
||||||
|
case 1:
|
||||||
|
_b = _c.sent(), messages = _b.messages, maxTokens = _b.maxTokens, numTokens = _b.numTokens;
|
||||||
|
console.log("maxTokens: ".concat(maxTokens, ", numTokens: ").concat(numTokens));
|
||||||
|
result = {
|
||||||
|
role: 'assistant',
|
||||||
|
id: uuidv4(),
|
||||||
|
conversationId: conversationId,
|
||||||
|
parentMessageId: messageId,
|
||||||
|
text: undefined,
|
||||||
|
};
|
||||||
|
this._completionParams.input = { messages: messages };
|
||||||
|
responseP = new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
|
||||||
|
var url, headers, body, res, reason, msg, error, response, err_1;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this._apiBaseUrl, "/services/aigc/text-generation/generation");
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: "Bearer ".concat(this._apiKey)
|
||||||
|
};
|
||||||
|
body = __assign(__assign({}, this._completionParams), completionParams);
|
||||||
|
if (this._debug) {
|
||||||
|
console.log(JSON.stringify(body));
|
||||||
|
}
|
||||||
|
if (this._debug) {
|
||||||
|
console.log("sendMessage (".concat(numTokens, " tokens)"), body);
|
||||||
|
}
|
||||||
|
_a.label = 1;
|
||||||
|
case 1:
|
||||||
|
_a.trys.push([1, 6, , 7]);
|
||||||
|
return [4 /*yield*/, this._fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
signal: abortSignal
|
||||||
|
})];
|
||||||
|
case 2:
|
||||||
|
res = _a.sent();
|
||||||
|
if (!!res.ok) return [3 /*break*/, 4];
|
||||||
|
return [4 /*yield*/, res.text()];
|
||||||
|
case 3:
|
||||||
|
reason = _a.sent();
|
||||||
|
msg = "Qwen error ".concat(res.status || res.statusText, ": ").concat(reason);
|
||||||
|
error = new types.ChatGPTError(msg, { cause: res });
|
||||||
|
error.statusCode = res.status;
|
||||||
|
error.statusText = res.statusText;
|
||||||
|
return [2 /*return*/, reject(error)];
|
||||||
|
case 4: return [4 /*yield*/, res.json()];
|
||||||
|
case 5:
|
||||||
|
response = _a.sent();
|
||||||
|
if (this._debug) {
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
if (response === null || response === void 0 ? void 0 : response.request_id) {
|
||||||
|
result.id = response.request_id;
|
||||||
|
}
|
||||||
|
result.detail = response;
|
||||||
|
result.text = response.output.text;
|
||||||
|
return [2 /*return*/, resolve(result)];
|
||||||
|
case 6:
|
||||||
|
err_1 = _a.sent();
|
||||||
|
return [2 /*return*/, reject(err_1)];
|
||||||
|
case 7: return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); }).then(function (message) { return __awaiter(_this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, Promise.all([
|
||||||
|
this._upsertMessage(latestQuestion),
|
||||||
|
this._upsertMessage(message)
|
||||||
|
]).then(function () { return message; })];
|
||||||
|
});
|
||||||
|
}); });
|
||||||
|
if (timeoutMs) {
|
||||||
|
if (abortController) {
|
||||||
|
// This will be called when a timeout occurs in order for us to forcibly
|
||||||
|
// ensure that the underlying HTTP request is aborted.
|
||||||
|
;
|
||||||
|
responseP.cancel = function () {
|
||||||
|
abortController.abort();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return [2 /*return*/, pTimeout(responseP, {
|
||||||
|
milliseconds: timeoutMs,
|
||||||
|
message: 'Qwen timed out waiting for response'
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [2 /*return*/, responseP];
|
||||||
|
}
|
||||||
|
return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(QwenApi.prototype, "apiKey", {
|
||||||
|
get: function () {
|
||||||
|
return this._apiKey;
|
||||||
|
},
|
||||||
|
set: function (apiKey) {
|
||||||
|
this._apiKey = apiKey;
|
||||||
|
},
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
QwenApi.prototype._buildMessages = function (text, role, opts, completionParams) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var _a, systemMessage, parentMessageId, userLabel, assistantLabel, maxNumTokens, messages, systemMessageOffset, nextMessages, functionToken, numTokens, prompt_1, nextNumTokensEstimate, _i, nextMessages_1, m1, _b, isValidPrompt, parentMessage, parentMessageRole, maxTokens;
|
||||||
|
return __generator(this, function (_c) {
|
||||||
|
switch (_c.label) {
|
||||||
|
case 0:
|
||||||
|
_a = opts.systemMessage, systemMessage = _a === void 0 ? this._systemMessage : _a;
|
||||||
|
parentMessageId = opts.parentMessageId;
|
||||||
|
userLabel = USER_LABEL_DEFAULT;
|
||||||
|
assistantLabel = ASSISTANT_LABEL_DEFAULT;
|
||||||
|
maxNumTokens = 6000;
|
||||||
|
messages = [];
|
||||||
|
if (systemMessage) {
|
||||||
|
messages.push({
|
||||||
|
role: 'system',
|
||||||
|
content: systemMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
systemMessageOffset = messages.length;
|
||||||
|
nextMessages = text
|
||||||
|
? messages.concat([
|
||||||
|
{
|
||||||
|
role: role,
|
||||||
|
content: text
|
||||||
|
}
|
||||||
|
])
|
||||||
|
: messages;
|
||||||
|
functionToken = 0;
|
||||||
|
numTokens = functionToken;
|
||||||
|
_c.label = 1;
|
||||||
|
case 1:
|
||||||
|
prompt_1 = nextMessages
|
||||||
|
.reduce(function (prompt, message) {
|
||||||
|
switch (message.role) {
|
||||||
|
case 'system':
|
||||||
|
return prompt.concat(["Instructions:\n".concat(message.content)]);
|
||||||
|
case 'user':
|
||||||
|
return prompt.concat(["".concat(userLabel, ":\n").concat(message.content)]);
|
||||||
|
default:
|
||||||
|
return message.content ? prompt.concat(["".concat(assistantLabel, ":\n").concat(message.content)]) : prompt;
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
.join('\n\n');
|
||||||
|
return [4 /*yield*/, this._getTokenCount(prompt_1)];
|
||||||
|
case 2:
|
||||||
|
nextNumTokensEstimate = _c.sent();
|
||||||
|
_i = 0, nextMessages_1 = nextMessages;
|
||||||
|
_c.label = 3;
|
||||||
|
case 3:
|
||||||
|
if (!(_i < nextMessages_1.length)) return [3 /*break*/, 6];
|
||||||
|
m1 = nextMessages_1[_i];
|
||||||
|
_b = nextNumTokensEstimate;
|
||||||
|
return [4 /*yield*/, this._getTokenCount('')];
|
||||||
|
case 4:
|
||||||
|
nextNumTokensEstimate = _b + _c.sent();
|
||||||
|
_c.label = 5;
|
||||||
|
case 5:
|
||||||
|
_i++;
|
||||||
|
return [3 /*break*/, 3];
|
||||||
|
case 6:
|
||||||
|
isValidPrompt = nextNumTokensEstimate + functionToken <= maxNumTokens;
|
||||||
|
if (prompt_1 && !isValidPrompt) {
|
||||||
|
return [3 /*break*/, 9];
|
||||||
|
}
|
||||||
|
messages = nextMessages;
|
||||||
|
numTokens = nextNumTokensEstimate + functionToken;
|
||||||
|
if (!isValidPrompt) {
|
||||||
|
return [3 /*break*/, 9];
|
||||||
|
}
|
||||||
|
if (!parentMessageId) {
|
||||||
|
return [3 /*break*/, 9];
|
||||||
|
}
|
||||||
|
return [4 /*yield*/, this._getMessageById(parentMessageId)];
|
||||||
|
case 7:
|
||||||
|
parentMessage = _c.sent();
|
||||||
|
if (!parentMessage) {
|
||||||
|
return [3 /*break*/, 9];
|
||||||
|
}
|
||||||
|
parentMessageRole = parentMessage.role || 'user';
|
||||||
|
nextMessages = nextMessages.slice(0, systemMessageOffset).concat(__spreadArray([
|
||||||
|
{
|
||||||
|
role: parentMessageRole,
|
||||||
|
content: parentMessage.text
|
||||||
|
}
|
||||||
|
], nextMessages.slice(systemMessageOffset), true));
|
||||||
|
parentMessageId = parentMessage.parentMessageId;
|
||||||
|
_c.label = 8;
|
||||||
|
case 8:
|
||||||
|
if (true) return [3 /*break*/, 1];
|
||||||
|
_c.label = 9;
|
||||||
|
case 9:
|
||||||
|
maxTokens = Math.max(1, Math.min(this._maxModelTokens - numTokens, this._maxResponseTokens));
|
||||||
|
return [2 /*return*/, { messages: messages, maxTokens: maxTokens, numTokens: numTokens }];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
QwenApi.prototype._getTokenCount = function (text) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
if (!text) {
|
||||||
|
return [2 /*return*/, 0];
|
||||||
|
}
|
||||||
|
// TODO: use a better fix in the tokenizer
|
||||||
|
text = text.replace(/<\|endoftext\|>/g, '');
|
||||||
|
return [2 /*return*/, tokenizer.encode(text).length];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
QwenApi.prototype._defaultGetMessageById = function (id) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var res;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, this._messageStore.get(id)];
|
||||||
|
case 1:
|
||||||
|
res = _a.sent();
|
||||||
|
return [2 /*return*/, res];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
QwenApi.prototype._defaultUpsertMessage = function (message) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, this._messageStore.set(message.request_id, message)];
|
||||||
|
case 1:
|
||||||
|
_a.sent();
|
||||||
|
return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return QwenApi;
|
||||||
|
}());
|
||||||
|
export { QwenApi };
|
||||||
382
utils/alibaba/qwen-api.ts
Normal file
382
utils/alibaba/qwen-api.ts
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
import Keyv from 'keyv'
|
||||||
|
import pTimeout from 'p-timeout'
|
||||||
|
import QuickLRU from 'quick-lru'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
import * as tokenizer from './tokenizer'
|
||||||
|
import * as types from './types'
|
||||||
|
import globalFetch from 'node-fetch'
|
||||||
|
import {qwen, Role} from "./types";
|
||||||
|
|
||||||
|
const CHATGPT_MODEL = 'qwen-turbo' // qwen-plus
|
||||||
|
|
||||||
|
const USER_LABEL_DEFAULT = 'User'
|
||||||
|
const ASSISTANT_LABEL_DEFAULT = '同义千问'
|
||||||
|
|
||||||
|
export class QwenApi {
|
||||||
|
protected _apiKey: string
|
||||||
|
protected _apiBaseUrl: string
|
||||||
|
protected _debug: boolean
|
||||||
|
|
||||||
|
protected _systemMessage: string
|
||||||
|
protected _completionParams: Omit<
|
||||||
|
types.qwen.CreateChatCompletionRequest,
|
||||||
|
'messages' | 'n'
|
||||||
|
>
|
||||||
|
protected _maxModelTokens: number
|
||||||
|
protected _maxResponseTokens: number
|
||||||
|
protected _fetch: types.FetchFn
|
||||||
|
|
||||||
|
protected _getMessageById: types.GetMessageByIdFunction
|
||||||
|
protected _upsertMessage: types.UpsertMessageFunction
|
||||||
|
|
||||||
|
protected _messageStore: Keyv<types.ChatMessage>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new client wrapper around Qwen's chat completion API, mimicing the official ChatGPT webapp's functionality as closely as possible.
|
||||||
|
*
|
||||||
|
* @param opts
|
||||||
|
*/
|
||||||
|
constructor(opts: types.QWenAPIOptions) {
|
||||||
|
const {
|
||||||
|
apiKey,
|
||||||
|
apiBaseUrl = 'https://dashscope.aliyuncs.com/api/v1',
|
||||||
|
debug = false,
|
||||||
|
messageStore,
|
||||||
|
completionParams,
|
||||||
|
parameters,
|
||||||
|
systemMessage,
|
||||||
|
getMessageById,
|
||||||
|
upsertMessage,
|
||||||
|
fetch = globalFetch
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
this._apiKey = apiKey
|
||||||
|
this._apiBaseUrl = apiBaseUrl
|
||||||
|
this._debug = !!debug
|
||||||
|
this._fetch = fetch
|
||||||
|
|
||||||
|
this._completionParams = {
|
||||||
|
model: CHATGPT_MODEL,
|
||||||
|
parameters: {
|
||||||
|
top_p: 0.5,
|
||||||
|
top_k: 50,
|
||||||
|
temperature: 1.0,
|
||||||
|
seed: 114514,
|
||||||
|
enable_search: true,
|
||||||
|
result_format: "text",
|
||||||
|
incremental_output: false,
|
||||||
|
...parameters
|
||||||
|
},
|
||||||
|
...completionParams
|
||||||
|
}
|
||||||
|
|
||||||
|
this._systemMessage = systemMessage
|
||||||
|
|
||||||
|
if (this._systemMessage === undefined) {
|
||||||
|
const currentDate = new Date().toISOString().split('T')[0]
|
||||||
|
this._systemMessage = `You are ChatGPT, a large language model trained by Qwen. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: ${currentDate}`
|
||||||
|
}
|
||||||
|
|
||||||
|
this._getMessageById = getMessageById ?? this._defaultGetMessageById
|
||||||
|
this._upsertMessage = upsertMessage ?? this._defaultUpsertMessage
|
||||||
|
|
||||||
|
if (messageStore) {
|
||||||
|
this._messageStore = messageStore
|
||||||
|
} else {
|
||||||
|
this._messageStore = new Keyv<types.ChatMessage, any>({
|
||||||
|
store: new QuickLRU<string, types.ChatMessage>({ maxSize: 10000 })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._apiKey) {
|
||||||
|
throw new Error('Qwen missing required apiKey')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._fetch) {
|
||||||
|
throw new Error('Invalid environment; fetch is not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this._fetch !== 'function') {
|
||||||
|
throw new Error('Invalid "fetch" is not a function')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the Qwen chat completions endpoint, waits for the response
|
||||||
|
* to resolve, and returns the response.
|
||||||
|
*
|
||||||
|
* If you want your response to have historical context, you must provide a valid `parentMessageId`.
|
||||||
|
*
|
||||||
|
* If you want to receive a stream of partial responses, use `opts.onProgress`.
|
||||||
|
*
|
||||||
|
* Set `debug: true` in the `ChatGPTAPI` constructor to log more info on the full prompt sent to the Qwen chat completions API. You can override the `systemMessage` in `opts` to customize the assistant's instructions.
|
||||||
|
*
|
||||||
|
* @param message - The prompt message to send
|
||||||
|
* @param opts.parentMessageId - Optional ID of the previous message in the conversation (defaults to `undefined`)
|
||||||
|
* @param opts.conversationId - Optional ID of the conversation (defaults to `undefined`)
|
||||||
|
* @param opts.messageId - Optional ID of the message to send (defaults to a random UUID)
|
||||||
|
* @param opts.systemMessage - Optional override for the chat "system message" which acts as instructions to the model (defaults to the ChatGPT system message)
|
||||||
|
* @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout)
|
||||||
|
* @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
|
||||||
|
* @param opts.abortSignal - Optional callback used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
||||||
|
* @param completionParams - Optional overrides to send to the [Qwen chat completion API](https://platform.openai.com/docs/api-reference/chat/create). Options like `temperature` and `presence_penalty` can be tweaked to change the personality of the assistant.
|
||||||
|
*
|
||||||
|
* @returns The response from ChatGPT
|
||||||
|
*/
|
||||||
|
async sendMessage(
|
||||||
|
text: string,
|
||||||
|
opts: types.SendMessageOptions = {},
|
||||||
|
role: Role = 'user',
|
||||||
|
): Promise<types.ChatMessage> {
|
||||||
|
const {
|
||||||
|
parentMessageId,
|
||||||
|
messageId = uuidv4(),
|
||||||
|
timeoutMs,
|
||||||
|
completionParams,
|
||||||
|
conversationId
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
let { abortSignal } = opts
|
||||||
|
|
||||||
|
let abortController: AbortController = null
|
||||||
|
if (timeoutMs && !abortSignal) {
|
||||||
|
abortController = new AbortController()
|
||||||
|
abortSignal = abortController.signal
|
||||||
|
}
|
||||||
|
|
||||||
|
const message: types.ChatMessage = {
|
||||||
|
role,
|
||||||
|
id: messageId,
|
||||||
|
conversationId,
|
||||||
|
parentMessageId,
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestQuestion = message
|
||||||
|
|
||||||
|
const { messages, maxTokens, numTokens } = await this._buildMessages(
|
||||||
|
text,
|
||||||
|
role,
|
||||||
|
opts,
|
||||||
|
completionParams
|
||||||
|
)
|
||||||
|
console.log(`maxTokens: ${maxTokens}, numTokens: ${numTokens}`)
|
||||||
|
const result: types.ChatMessage = {
|
||||||
|
role: 'assistant',
|
||||||
|
id: uuidv4(),
|
||||||
|
conversationId,
|
||||||
|
parentMessageId: messageId,
|
||||||
|
text: undefined,
|
||||||
|
}
|
||||||
|
this._completionParams.input = { messages }
|
||||||
|
const responseP = new Promise<types.ChatMessage>(
|
||||||
|
async (resolve, reject) => {
|
||||||
|
const url = `${this._apiBaseUrl}/services/aigc/text-generation/generation`
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${this._apiKey}`
|
||||||
|
}
|
||||||
|
const body = {
|
||||||
|
...this._completionParams,
|
||||||
|
...completionParams
|
||||||
|
}
|
||||||
|
if (this._debug) {
|
||||||
|
console.log(JSON.stringify(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._debug) {
|
||||||
|
console.log(`sendMessage (${numTokens} tokens)`, body)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await this._fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
signal: abortSignal
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const reason = await res.text()
|
||||||
|
const msg = `Qwen error ${
|
||||||
|
res.status || res.statusText
|
||||||
|
}: ${reason}`
|
||||||
|
const error = new types.ChatGPTError(msg, { cause: res })
|
||||||
|
error.statusCode = res.status
|
||||||
|
error.statusText = res.statusText
|
||||||
|
return reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: types.qwen.CreateChatCompletionResponse =
|
||||||
|
await res.json()
|
||||||
|
if (this._debug) {
|
||||||
|
console.log(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response?.request_id) {
|
||||||
|
result.id = response.request_id
|
||||||
|
}
|
||||||
|
result.detail = response
|
||||||
|
result.text = response.output.text
|
||||||
|
return resolve(result)
|
||||||
|
} catch (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
).then(async (message) => {
|
||||||
|
return Promise.all([
|
||||||
|
this._upsertMessage(latestQuestion),
|
||||||
|
this._upsertMessage(message)
|
||||||
|
]).then(() => message)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (timeoutMs) {
|
||||||
|
if (abortController) {
|
||||||
|
// This will be called when a timeout occurs in order for us to forcibly
|
||||||
|
// ensure that the underlying HTTP request is aborted.
|
||||||
|
;(responseP as any).cancel = () => {
|
||||||
|
abortController.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pTimeout(responseP, {
|
||||||
|
milliseconds: timeoutMs,
|
||||||
|
message: 'Qwen timed out waiting for response'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return responseP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get apiKey(): string {
|
||||||
|
return this._apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
set apiKey(apiKey: string) {
|
||||||
|
this._apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async _buildMessages(text: string, role: Role, opts: types.SendMessageOptions, completionParams: Partial<
|
||||||
|
Omit<qwen.CreateChatCompletionRequest, 'messages' | 'n' | 'stream'>
|
||||||
|
>) {
|
||||||
|
const { systemMessage = this._systemMessage } = opts
|
||||||
|
let { parentMessageId } = opts
|
||||||
|
|
||||||
|
const userLabel = USER_LABEL_DEFAULT
|
||||||
|
const assistantLabel = ASSISTANT_LABEL_DEFAULT
|
||||||
|
|
||||||
|
// fix number of qwen
|
||||||
|
const maxNumTokens = 6000
|
||||||
|
let messages: types.qwen.ChatCompletionRequestMessage[] = []
|
||||||
|
|
||||||
|
if (systemMessage) {
|
||||||
|
messages.push({
|
||||||
|
role: 'system',
|
||||||
|
content: systemMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemMessageOffset = messages.length
|
||||||
|
let nextMessages = text
|
||||||
|
? messages.concat([
|
||||||
|
{
|
||||||
|
role,
|
||||||
|
content: text
|
||||||
|
}
|
||||||
|
])
|
||||||
|
: messages
|
||||||
|
|
||||||
|
let functionToken = 0
|
||||||
|
|
||||||
|
let numTokens = functionToken
|
||||||
|
|
||||||
|
do {
|
||||||
|
const prompt = nextMessages
|
||||||
|
.reduce((prompt, message) => {
|
||||||
|
switch (message.role) {
|
||||||
|
case 'system':
|
||||||
|
return prompt.concat([`Instructions:\n${message.content}`])
|
||||||
|
case 'user':
|
||||||
|
return prompt.concat([`${userLabel}:\n${message.content}`])
|
||||||
|
default:
|
||||||
|
return message.content ? prompt.concat([`${assistantLabel}:\n${message.content}`]) : prompt
|
||||||
|
}
|
||||||
|
}, [] as string[])
|
||||||
|
.join('\n\n')
|
||||||
|
|
||||||
|
let nextNumTokensEstimate = await this._getTokenCount(prompt)
|
||||||
|
|
||||||
|
for (const m1 of nextMessages) {
|
||||||
|
nextNumTokensEstimate += await this._getTokenCount('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValidPrompt = nextNumTokensEstimate + functionToken <= maxNumTokens
|
||||||
|
|
||||||
|
if (prompt && !isValidPrompt) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
messages = nextMessages
|
||||||
|
numTokens = nextNumTokensEstimate + functionToken
|
||||||
|
|
||||||
|
if (!isValidPrompt) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parentMessageId) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentMessage = await this._getMessageById(parentMessageId)
|
||||||
|
if (!parentMessage) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentMessageRole = parentMessage.role || 'user'
|
||||||
|
|
||||||
|
nextMessages = nextMessages.slice(0, systemMessageOffset).concat([
|
||||||
|
{
|
||||||
|
role: parentMessageRole,
|
||||||
|
content: parentMessage.text
|
||||||
|
},
|
||||||
|
...nextMessages.slice(systemMessageOffset)
|
||||||
|
])
|
||||||
|
|
||||||
|
parentMessageId = parentMessage.parentMessageId
|
||||||
|
|
||||||
|
} while (true)
|
||||||
|
|
||||||
|
// Use up to 4096 tokens (prompt + response), but try to leave 1000 tokens
|
||||||
|
// for the response.
|
||||||
|
const maxTokens = Math.max(
|
||||||
|
1,
|
||||||
|
Math.min(this._maxModelTokens - numTokens, this._maxResponseTokens)
|
||||||
|
)
|
||||||
|
|
||||||
|
return { messages, maxTokens, numTokens }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _getTokenCount(text: string) {
|
||||||
|
if (!text) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// TODO: use a better fix in the tokenizer
|
||||||
|
text = text.replace(/<\|endoftext\|>/g, '')
|
||||||
|
|
||||||
|
return tokenizer.encode(text).length
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _defaultGetMessageById(
|
||||||
|
id: string
|
||||||
|
): Promise<types.ChatMessage> {
|
||||||
|
const res = await this._messageStore.get(id)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _defaultUpsertMessage(
|
||||||
|
message: types.ChatMessage
|
||||||
|
): Promise<void> {
|
||||||
|
await this._messageStore.set(message.request_id, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
6
utils/alibaba/tokenizer.js
Normal file
6
utils/alibaba/tokenizer.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { getEncoding } from 'js-tiktoken';
|
||||||
|
// TODO: make this configurable
|
||||||
|
var tokenizer = getEncoding('cl100k_base');
|
||||||
|
export function encode(input) {
|
||||||
|
return new Uint32Array(tokenizer.encode(input));
|
||||||
|
}
|
||||||
8
utils/alibaba/tokenizer.ts
Normal file
8
utils/alibaba/tokenizer.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { getEncoding } from 'js-tiktoken'
|
||||||
|
|
||||||
|
// TODO: make this configurable
|
||||||
|
const tokenizer = getEncoding('cl100k_base')
|
||||||
|
|
||||||
|
export function encode(input: string): Uint32Array {
|
||||||
|
return new Uint32Array(tokenizer.encode(input))
|
||||||
|
}
|
||||||
5
utils/alibaba/tsconfig.json
Normal file
5
utils/alibaba/tsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "es2020"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
utils/alibaba/types.js
Normal file
26
utils/alibaba/types.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
var __extends = (this && this.__extends) || (function () {
|
||||||
|
var extendStatics = function (d, b) {
|
||||||
|
extendStatics = Object.setPrototypeOf ||
|
||||||
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||||
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||||
|
return extendStatics(d, b);
|
||||||
|
};
|
||||||
|
return function (d, b) {
|
||||||
|
if (typeof b !== "function" && b !== null)
|
||||||
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||||
|
extendStatics(d, b);
|
||||||
|
function __() { this.constructor = d; }
|
||||||
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
var ChatGPTError = /** @class */ (function (_super) {
|
||||||
|
__extends(ChatGPTError, _super);
|
||||||
|
function ChatGPTError() {
|
||||||
|
return _super !== null && _super.apply(this, arguments) || this;
|
||||||
|
}
|
||||||
|
return ChatGPTError;
|
||||||
|
}(Error));
|
||||||
|
export { ChatGPTError };
|
||||||
|
export var qwen;
|
||||||
|
(function (qwen) {
|
||||||
|
})(qwen || (qwen = {}));
|
||||||
313
utils/alibaba/types.ts
Normal file
313
utils/alibaba/types.ts
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
import Keyv from 'keyv'
|
||||||
|
|
||||||
|
export type Role = 'user' | 'assistant' | 'system'
|
||||||
|
|
||||||
|
export type FetchFn = typeof fetch
|
||||||
|
|
||||||
|
export type QWenAPIOptions = {
|
||||||
|
apiKey: string
|
||||||
|
|
||||||
|
/** @defaultValue `'https://dashscope.aliyuncs.com/api/v1'` **/
|
||||||
|
apiBaseUrl?: string
|
||||||
|
|
||||||
|
apiOrg?: string
|
||||||
|
|
||||||
|
/** @defaultValue `false` **/
|
||||||
|
debug?: boolean
|
||||||
|
|
||||||
|
completionParams?: Partial<
|
||||||
|
Omit<qwen.CreateChatCompletionRequest, 'messages' | 'n' | 'stream'>
|
||||||
|
>
|
||||||
|
parameters?: qwen.QWenParameters,
|
||||||
|
|
||||||
|
systemMessage?: string
|
||||||
|
|
||||||
|
messageStore?: Keyv
|
||||||
|
getMessageById?: GetMessageByIdFunction
|
||||||
|
upsertMessage?: UpsertMessageFunction
|
||||||
|
|
||||||
|
fetch?: FetchFn
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SendMessageOptions = {
|
||||||
|
/**
|
||||||
|
* function role name
|
||||||
|
*/
|
||||||
|
name?: string
|
||||||
|
messageId?: string
|
||||||
|
stream?: boolean
|
||||||
|
systemMessage?: string
|
||||||
|
parentMessageId?: string
|
||||||
|
conversationId?: string
|
||||||
|
timeoutMs?: number
|
||||||
|
onProgress?: (partialResponse: ChatMessage) => void
|
||||||
|
abortSignal?: AbortSignal
|
||||||
|
completionParams?: Partial<
|
||||||
|
Omit<qwen.CreateChatCompletionRequest, 'messages' | 'n' | 'stream'>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageActionType = 'next' | 'variant'
|
||||||
|
|
||||||
|
export type SendMessageBrowserOptions = {
|
||||||
|
conversationId?: string
|
||||||
|
parentMessageId?: string
|
||||||
|
messageId?: string
|
||||||
|
action?: MessageActionType
|
||||||
|
timeoutMs?: number
|
||||||
|
onProgress?: (partialResponse: ChatMessage) => void
|
||||||
|
abortSignal?: AbortSignal
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatMessage {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
role: Role
|
||||||
|
parentMessageId?: string
|
||||||
|
conversationId?: string
|
||||||
|
detail?:
|
||||||
|
| qwen.CreateChatCompletionResponse
|
||||||
|
| CreateChatCompletionStreamResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChatGPTError extends Error {
|
||||||
|
statusCode?: number
|
||||||
|
statusText?: string
|
||||||
|
isFinal?: boolean
|
||||||
|
accountId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a chat message from a store by it's ID (or null if not found). */
|
||||||
|
export type GetMessageByIdFunction = (id: string) => Promise<ChatMessage>
|
||||||
|
|
||||||
|
/** Upserts a chat message to a store. */
|
||||||
|
export type UpsertMessageFunction = (message: ChatMessage) => Promise<void>
|
||||||
|
|
||||||
|
export interface CreateChatCompletionStreamResponse
|
||||||
|
extends openai.CreateChatCompletionDeltaResponse {
|
||||||
|
usage: CreateCompletionStreamResponseUsage
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateCompletionStreamResponseUsage
|
||||||
|
extends openai.CreateCompletionResponseUsage {
|
||||||
|
estimated: true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://chat.openapi.com/backend-api/conversation
|
||||||
|
*/
|
||||||
|
export type ConversationJSONBody = {
|
||||||
|
/**
|
||||||
|
* The action to take
|
||||||
|
*/
|
||||||
|
action: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the conversation
|
||||||
|
*/
|
||||||
|
conversation_id?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts to provide
|
||||||
|
*/
|
||||||
|
messages: Prompt[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model to use
|
||||||
|
*/
|
||||||
|
model: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parent message ID
|
||||||
|
*/
|
||||||
|
parent_message_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Prompt = {
|
||||||
|
/**
|
||||||
|
* The content of the prompt
|
||||||
|
*/
|
||||||
|
content: PromptContent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the prompt
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The role played in the prompt
|
||||||
|
*/
|
||||||
|
role: Role
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ContentType = 'text'
|
||||||
|
|
||||||
|
export type PromptContent = {
|
||||||
|
/**
|
||||||
|
* The content type of the prompt
|
||||||
|
*/
|
||||||
|
content_type: ContentType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parts to the prompt
|
||||||
|
*/
|
||||||
|
parts: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConversationResponseEvent = {
|
||||||
|
message?: Message
|
||||||
|
conversation_id?: string
|
||||||
|
error?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Message = {
|
||||||
|
id: string
|
||||||
|
content: MessageContent
|
||||||
|
role: Role
|
||||||
|
user: string | null
|
||||||
|
create_time: string | null
|
||||||
|
update_time: string | null
|
||||||
|
end_turn: null
|
||||||
|
weight: number
|
||||||
|
recipient: string
|
||||||
|
metadata: MessageMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageContent = {
|
||||||
|
content_type: string
|
||||||
|
parts: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageMetadata = any
|
||||||
|
|
||||||
|
export namespace qwen {
|
||||||
|
export interface CreateChatCompletionDeltaResponse {
|
||||||
|
id: string
|
||||||
|
object: 'chat.completion.chunk'
|
||||||
|
created: number
|
||||||
|
model: string
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
delta: {
|
||||||
|
role: Role
|
||||||
|
content?: string,
|
||||||
|
function_call?: {name: string, arguments: string}
|
||||||
|
}
|
||||||
|
index: number
|
||||||
|
finish_reason: string | null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface ChatCompletionRequestMessage
|
||||||
|
*/
|
||||||
|
export interface ChatCompletionRequestMessage {
|
||||||
|
/**
|
||||||
|
* The role of the author of this message.
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ChatCompletionRequestMessage
|
||||||
|
*/
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum
|
||||||
|
/**
|
||||||
|
* The contents of the message
|
||||||
|
* @type {string}
|
||||||
|
* @memberof ChatCompletionRequestMessage
|
||||||
|
*/
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare const ChatCompletionRequestMessageRoleEnum: {
|
||||||
|
readonly System: 'system'
|
||||||
|
readonly User: 'user'
|
||||||
|
readonly Assistant: 'assistant'
|
||||||
|
}
|
||||||
|
export declare type ChatCompletionRequestMessageRoleEnum =
|
||||||
|
(typeof ChatCompletionRequestMessageRoleEnum)[keyof typeof ChatCompletionRequestMessageRoleEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export interface QWenInput {
|
||||||
|
messages: Array<ChatCompletionRequestMessage>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QWenParameters {
|
||||||
|
result_format: string
|
||||||
|
top_p: number
|
||||||
|
top_k: number
|
||||||
|
seed: number
|
||||||
|
temperature: number
|
||||||
|
enable_search: boolean
|
||||||
|
incremental_output: boolean
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CreateChatCompletionRequest
|
||||||
|
*/
|
||||||
|
export interface CreateChatCompletionRequest {
|
||||||
|
/**
|
||||||
|
* ID of the model to use. Currently, only `gpt-3.5-turbo` and `gpt-3.5-turbo-0301` are supported.
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CreateChatCompletionRequest
|
||||||
|
*/
|
||||||
|
model: string
|
||||||
|
/**
|
||||||
|
* The messages to generate chat completions for, in the [chat format](/docs/guides/chat/introduction).
|
||||||
|
* @type {Array<ChatCompletionRequestMessage>}
|
||||||
|
* @memberof CreateChatCompletionRequest
|
||||||
|
*/
|
||||||
|
input?: QWenInput
|
||||||
|
|
||||||
|
parameters: QWenParameters
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CreateChatCompletionResponse
|
||||||
|
*/
|
||||||
|
export interface CreateChatCompletionResponse {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof CreateChatCompletionResponse
|
||||||
|
*/
|
||||||
|
request_id: string
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {QWenOutput}
|
||||||
|
* @memberof CreateChatCompletionResponse
|
||||||
|
*/
|
||||||
|
output: QWenOutput
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {CreateCompletionResponseUsage}
|
||||||
|
* @memberof CreateChatCompletionResponse
|
||||||
|
*/
|
||||||
|
usage?: CreateCompletionResponseUsage
|
||||||
|
}
|
||||||
|
export interface QWenOutput {
|
||||||
|
finish_reason: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface CreateCompletionResponseUsage
|
||||||
|
*/
|
||||||
|
export interface CreateCompletionResponseUsage {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof CreateCompletionResponseUsage
|
||||||
|
*/
|
||||||
|
input_tokens: number
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof CreateCompletionResponseUsage
|
||||||
|
*/
|
||||||
|
output_tokens: number
|
||||||
|
}
|
||||||
|
}
|
||||||
71
utils/bilibili/wbi.js
Normal file
71
utils/bilibili/wbi.js
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import md5 from 'md5'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
|
const mixinKeyEncTab = [
|
||||||
|
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
|
||||||
|
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
|
||||||
|
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
|
||||||
|
36, 20, 34, 44, 52
|
||||||
|
]
|
||||||
|
|
||||||
|
// 对 imgKey 和 subKey 进行字符顺序打乱编码
|
||||||
|
function getMixinKey (orig) {
|
||||||
|
let temp = ''
|
||||||
|
mixinKeyEncTab.forEach((n) => {
|
||||||
|
temp += orig[n]
|
||||||
|
})
|
||||||
|
return temp.slice(0, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为请求参数进行 wbi 签名
|
||||||
|
function encWbi (params, imgKey, subKey) {
|
||||||
|
const mixinKey = getMixinKey(imgKey + subKey)
|
||||||
|
const currTime = Math.round(Date.now() / 1000)
|
||||||
|
const chrFilter = /[!'()*]/g
|
||||||
|
let query = []
|
||||||
|
Object.assign(params, { wts: currTime }) // 添加 wts 字段
|
||||||
|
// 按照 key 重排参数
|
||||||
|
Object.keys(params).sort().forEach((key) => {
|
||||||
|
query.push(
|
||||||
|
`${encodeURIComponent(key)}=${encodeURIComponent(
|
||||||
|
// 过滤 value 中的 "!'()*" 字符
|
||||||
|
params[key].toString().replace(chrFilter, '')
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
query = query.join('&')
|
||||||
|
const wbiSign = md5(query + mixinKey) // 计算 w_rid
|
||||||
|
return query + '&w_rid=' + wbiSign
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新的 img_key 和 sub_key
|
||||||
|
async function getWbiKeys () {
|
||||||
|
const resp = await fetch('https://api.bilibili.com/x/web-interface/nav')
|
||||||
|
const jsonContent = resp.data
|
||||||
|
const imgUrl = jsonContent.data.wbi_img.img_url
|
||||||
|
const subUrl = jsonContent.data.wbi_img.sub_url
|
||||||
|
|
||||||
|
return {
|
||||||
|
img_key: imgUrl.slice(
|
||||||
|
imgUrl.lastIndexOf('/') + 1,
|
||||||
|
imgUrl.lastIndexOf('.')
|
||||||
|
),
|
||||||
|
sub_key: subUrl.slice(
|
||||||
|
subUrl.lastIndexOf('/') + 1,
|
||||||
|
subUrl.lastIndexOf('.')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWbiKeys().then((wbi_keys) => {
|
||||||
|
// const query = encWbi(
|
||||||
|
// {
|
||||||
|
// foo: '114',
|
||||||
|
// bar: '514',
|
||||||
|
// baz: 1919810
|
||||||
|
// },
|
||||||
|
// wbi_keys.img_key,
|
||||||
|
// wbi_keys.sub_key
|
||||||
|
// )
|
||||||
|
// console.log(query)
|
||||||
|
// })
|
||||||
34
utils/chat.js
Normal file
34
utils/chat.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
export async function getChatHistoryGroup (e, num) {
|
||||||
|
// if (e.adapter === 'shamrock') {
|
||||||
|
// return await e.group.getChatHistory(0, num, false)
|
||||||
|
// } else {
|
||||||
|
let latestChats = await e.group.getChatHistory(0, 1)
|
||||||
|
if (latestChats.length > 0) {
|
||||||
|
let latestChat = latestChats[0]
|
||||||
|
if (latestChat) {
|
||||||
|
let seq = latestChat.seq || latestChat.message_id
|
||||||
|
let chats = []
|
||||||
|
while (chats.length < num) {
|
||||||
|
let chatHistory = await e.group.getChatHistory(seq, 20)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
@ -186,6 +186,7 @@ export class ClaudeAIClient {
|
||||||
} else if (streamDataRes.status === 408) {
|
} else if (streamDataRes.status === 408) {
|
||||||
throw new Error('claude.ai响应超时,可能是回复文本太多,请调高超时时间重试')
|
throw new Error('claude.ai响应超时,可能是回复文本太多,请调高超时时间重试')
|
||||||
} else {
|
} else {
|
||||||
|
logger.error(streamDataRes.status, streamDataRes.body)
|
||||||
throw new Error('unknown error')
|
throw new Error('unknown error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
298
utils/common.js
298
utils/common.js
|
|
@ -1,5 +1,3 @@
|
||||||
// import { remark } from 'remark'
|
|
||||||
// import stripMarkdown from 'strip-markdown'
|
|
||||||
import { exec } from 'child_process'
|
import { exec } from 'child_process'
|
||||||
import lodash from 'lodash'
|
import lodash from 'lodash'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
@ -7,18 +5,34 @@ import path from 'node:path'
|
||||||
import buffer from 'buffer'
|
import buffer from 'buffer'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
|
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
|
||||||
|
import common from '../../../lib/common/common.js'
|
||||||
import { Config } from './config.js'
|
import { Config } from './config.js'
|
||||||
import { convertSpeaker, generateVitsAudio, speakers as vitsRoleList } from './tts.js'
|
import { convertSpeaker, generateVitsAudio, speakers as vitsRoleList } from './tts.js'
|
||||||
import VoiceVoxTTS, { supportConfigurations as voxRoleList } from './tts/voicevox.js'
|
import VoiceVoxTTS, { supportConfigurations as voxRoleList } from './tts/voicevox.js'
|
||||||
import AzureTTS, { supportConfigurations as azureRoleList } from './tts/microsoft-azure.js'
|
import AzureTTS, { supportConfigurations as azureRoleList } from './tts/microsoft-azure.js'
|
||||||
import { translate } from './translate.js'
|
import { translate } from './translate.js'
|
||||||
import uploadRecord from './uploadRecord.js'
|
import uploadRecord from './uploadRecord.js'
|
||||||
// export function markdownToText (markdown) {
|
import Version from './version.js'
|
||||||
// return remark()
|
import fetch from 'node-fetch'
|
||||||
// .use(stripMarkdown)
|
let pdfjsLib
|
||||||
// .processSync(markdown ?? '')
|
try {
|
||||||
// .toString()
|
pdfjsLib = (await import('pdfjs-dist')).default
|
||||||
// }
|
} catch (err) {}
|
||||||
|
|
||||||
|
let mammoth
|
||||||
|
try {
|
||||||
|
mammoth = (await import('mammoth')).default
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
let XLSX
|
||||||
|
try {
|
||||||
|
XLSX = (await import('xlsx')).default
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
let PPTX
|
||||||
|
try {
|
||||||
|
PPTX = (await import('nodejs-pptx')).default
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
let _puppeteer
|
let _puppeteer
|
||||||
try {
|
try {
|
||||||
|
|
@ -59,12 +73,18 @@ export function randomString (length = 5) {
|
||||||
return str.substr(0, length)
|
return str.substr(0, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function upsertMessage (message) {
|
export async function upsertMessage (message, suffix = '') {
|
||||||
await redis.set(`CHATGPT:MESSAGE:${message.id}`, JSON.stringify(message))
|
if (suffix) {
|
||||||
|
suffix = '_' + suffix
|
||||||
|
}
|
||||||
|
await redis.set(`CHATGPT:MESSAGE${suffix}:${message.id}`, JSON.stringify(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMessageById (id) {
|
export async function getMessageById (id, suffix = '') {
|
||||||
let messageStr = await redis.get(`CHATGPT:MESSAGE:${id}`)
|
if (suffix) {
|
||||||
|
suffix = '_' + suffix
|
||||||
|
}
|
||||||
|
let messageStr = await redis.get(`CHATGPT:MESSAGE${suffix}:${id}`)
|
||||||
return JSON.parse(messageStr)
|
return JSON.parse(messageStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,17 +101,20 @@ export async function tryTimes (promiseFn, maxTries = 10) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function makeForwardMsg (e, msg = [], dec = '') {
|
export async function makeForwardMsg (e, msg = [], dec = '') {
|
||||||
let nickname = Bot.nickname
|
if (Version.isTrss) {
|
||||||
|
return common.makeForwardMsg(e, msg, dec)
|
||||||
|
}
|
||||||
|
let nickname = e.bot.nickname
|
||||||
if (e.isGroup) {
|
if (e.isGroup) {
|
||||||
try {
|
try {
|
||||||
let info = await Bot.getGroupMemberInfo(e.group_id, Bot.uin)
|
let info = await e.bot.getGroupMemberInfo(e.group_id, getUin(e))
|
||||||
nickname = info.card || info.nickname
|
nickname = info.card || info.nickname
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to get group member info: ${err}`)
|
console.error(`Failed to get group member info: ${err}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let userInfo = {
|
let userInfo = {
|
||||||
user_id: Bot.uin,
|
user_id: getUin(e),
|
||||||
nickname
|
nickname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +132,7 @@ export async function makeForwardMsg (e, msg = [], dec = '') {
|
||||||
} else if (e.friend) {
|
} else if (e.friend) {
|
||||||
forwardMsg = await e.friend.makeForwardMsg(forwardMsg)
|
forwardMsg = await e.friend.makeForwardMsg(forwardMsg)
|
||||||
} else {
|
} else {
|
||||||
return false
|
return msg.join('\n')
|
||||||
}
|
}
|
||||||
let forwardMsg_json = forwardMsg.data
|
let forwardMsg_json = forwardMsg.data
|
||||||
if (typeof (forwardMsg_json) === 'object') {
|
if (typeof (forwardMsg_json) === 'object') {
|
||||||
|
|
@ -787,7 +810,7 @@ export async function getImageOcrText (e) {
|
||||||
let resultArr = []
|
let resultArr = []
|
||||||
let eachImgRes = ''
|
let eachImgRes = ''
|
||||||
for (let i in img) {
|
for (let i in img) {
|
||||||
const imgOCR = await Bot.imageOcr(img[i])
|
const imgOCR = await e.bot.imageOcr(img[i])
|
||||||
for (let text of imgOCR.wordslist) {
|
for (let text of imgOCR.wordslist) {
|
||||||
eachImgRes += (`${text?.words} \n`)
|
eachImgRes += (`${text?.words} \n`)
|
||||||
}
|
}
|
||||||
|
|
@ -797,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)
|
||||||
}
|
}
|
||||||
|
|
@ -809,8 +833,10 @@ export function getMaxModelTokens (model = 'gpt-3.5-turbo') {
|
||||||
if (model.startsWith('gpt-3.5-turbo')) {
|
if (model.startsWith('gpt-3.5-turbo')) {
|
||||||
if (model.includes('16k')) {
|
if (model.includes('16k')) {
|
||||||
return 16000
|
return 16000
|
||||||
} else {
|
} else if (model.includes('0613') || model.includes('0314')) {
|
||||||
return 4000
|
return 4000
|
||||||
|
} else {
|
||||||
|
return 16000
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (model.includes('32k')) {
|
if (model.includes('32k')) {
|
||||||
|
|
@ -821,6 +847,20 @@ export function getMaxModelTokens (model = 'gpt-3.5-turbo') {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUin (e) {
|
||||||
|
if (e?.bot?.uin) return e.bot.uin
|
||||||
|
if (Array.isArray(Bot.uin)) {
|
||||||
|
if (Config.trssBotUin && Bot.uin.indexOf(Config.trssBotUin) > -1) { return Config.trssBotUin } else {
|
||||||
|
Bot.uin.forEach((u) => {
|
||||||
|
if (Bot[u].self_id) {
|
||||||
|
return Bot[u].self_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Bot.uin[Bot.uin.length - 1]
|
||||||
|
}
|
||||||
|
} else return Bot.uin
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成当前语音模式下可发送的音频信息
|
* 生成当前语音模式下可发送的音频信息
|
||||||
* @param e - 上下文对象
|
* @param e - 上下文对象
|
||||||
|
|
@ -833,6 +873,7 @@ export async function generateAudio (e, pendingText, speakingEmotion, emotionDeg
|
||||||
if (!Config.ttsSpace && !Config.azureTTSKey && !Config.voicevoxSpace) return false
|
if (!Config.ttsSpace && !Config.azureTTSKey && !Config.voicevoxSpace) return false
|
||||||
let wav
|
let wav
|
||||||
const speaker = getUserSpeaker(await getUserReplySetting(e))
|
const speaker = getUserSpeaker(await getUserReplySetting(e))
|
||||||
|
let ignoreEncode = e.adapter === 'shamrock'
|
||||||
try {
|
try {
|
||||||
if (Config.ttsMode === 'vits-uma-genshin-honkai' && Config.ttsSpace) {
|
if (Config.ttsMode === 'vits-uma-genshin-honkai' && Config.ttsSpace) {
|
||||||
if (Config.autoJapanese) {
|
if (Config.autoJapanese) {
|
||||||
|
|
@ -845,7 +886,7 @@ export async function generateAudio (e, pendingText, speakingEmotion, emotionDeg
|
||||||
}
|
}
|
||||||
wav = await generateVitsAudio(pendingText, speaker, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
|
wav = await generateVitsAudio(pendingText, speaker, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)')
|
||||||
} else if (Config.ttsMode === 'azure' && Config.azureTTSKey) {
|
} else if (Config.ttsMode === 'azure' && Config.azureTTSKey) {
|
||||||
return await generateAzureAudio(pendingText, speaker, speakingEmotion, emotionDegree)
|
return await generateAzureAudio(pendingText, speaker, speakingEmotion, emotionDegree, ignoreEncode)
|
||||||
} else if (Config.ttsMode === 'voicevox' && Config.voicevoxSpace) {
|
} else if (Config.ttsMode === 'voicevox' && Config.voicevoxSpace) {
|
||||||
pendingText = (await translate(pendingText, '日')).replace('\n', '')
|
pendingText = (await translate(pendingText, '日')).replace('\n', '')
|
||||||
wav = await VoiceVoxTTS.generateAudio(pendingText, {
|
wav = await VoiceVoxTTS.generateAudio(pendingText, {
|
||||||
|
|
@ -859,7 +900,7 @@ export async function generateAudio (e, pendingText, speakingEmotion, emotionDeg
|
||||||
let sendable
|
let sendable
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
sendable = await uploadRecord(wav, Config.ttsMode)
|
sendable = await uploadRecord(wav, Config.ttsMode, ignoreEncode)
|
||||||
if (!sendable) {
|
if (!sendable) {
|
||||||
// 如果合成失败,尝试使用ffmpeg合成
|
// 如果合成失败,尝试使用ffmpeg合成
|
||||||
sendable = segment.record(wav)
|
sendable = segment.record(wav)
|
||||||
|
|
@ -889,9 +930,10 @@ export async function generateAudio (e, pendingText, speakingEmotion, emotionDeg
|
||||||
* @param role - 发言人
|
* @param role - 发言人
|
||||||
* @param speakingEmotion - 发言人情绪
|
* @param speakingEmotion - 发言人情绪
|
||||||
* @param emotionDegree - 发言人情绪强度
|
* @param emotionDegree - 发言人情绪强度
|
||||||
|
* @param ignoreEncode - 不在客户端处理编码
|
||||||
* @returns {Promise<{file: string, type: string}|boolean>}
|
* @returns {Promise<{file: string, type: string}|boolean>}
|
||||||
*/
|
*/
|
||||||
export async function generateAzureAudio (pendingText, role = '随机', speakingEmotion, emotionDegree = 1) {
|
export async function generateAzureAudio (pendingText, role = '随机', speakingEmotion, emotionDegree = 1, ignoreEncode = false) {
|
||||||
if (!Config.azureTTSKey) return false
|
if (!Config.azureTTSKey) return false
|
||||||
let speaker
|
let speaker
|
||||||
try {
|
try {
|
||||||
|
|
@ -911,7 +953,6 @@ export async function generateAzureAudio (pendingText, role = '随机', speaking
|
||||||
let languagePrefix = azureRoleList.find(config => config.code === speaker).languageDetail.charAt(0)
|
let languagePrefix = azureRoleList.find(config => config.code === speaker).languageDetail.charAt(0)
|
||||||
languagePrefix = languagePrefix.startsWith('E') ? '英' : languagePrefix
|
languagePrefix = languagePrefix.startsWith('E') ? '英' : languagePrefix
|
||||||
pendingText = (await translate(pendingText, languagePrefix)).replace('\n', '')
|
pendingText = (await translate(pendingText, languagePrefix)).replace('\n', '')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let role, languagePrefix
|
let role, languagePrefix
|
||||||
role = azureRoleList[Math.floor(Math.random() * azureRoleList.length)]
|
role = azureRoleList[Math.floor(Math.random() * azureRoleList.length)]
|
||||||
|
|
@ -933,11 +974,13 @@ export async function generateAzureAudio (pendingText, role = '随机', speaking
|
||||||
pendingText,
|
pendingText,
|
||||||
emotionDegree
|
emotionDegree
|
||||||
})
|
})
|
||||||
return await uploadRecord(
|
let record = await AzureTTS.generateAudio(pendingText, {
|
||||||
await AzureTTS.generateAudio(pendingText, {
|
|
||||||
speaker
|
speaker
|
||||||
}, await ssml)
|
}, await ssml)
|
||||||
, Config.ttsMode
|
return await uploadRecord(
|
||||||
|
record
|
||||||
|
, Config.ttsMode,
|
||||||
|
ignoreEncode
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
|
|
@ -954,3 +997,208 @@ export function getUserSpeaker (userSetting) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url 要下载的文件链接
|
||||||
|
* @param destPath 目标路径,如received/abc.pdf. 目前如果文件名重复会覆盖。
|
||||||
|
* @param absolute 是否是绝对路径,默认为false,此时拼接在data/chatgpt下
|
||||||
|
* @returns {Promise<string>} 最终下载文件的存储位置
|
||||||
|
*/
|
||||||
|
export async function downloadFile (url, destPath, absolute = false) {
|
||||||
|
let response = await fetch(url)
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`download file http error: status: ${response.status}`)
|
||||||
|
}
|
||||||
|
let dest = destPath
|
||||||
|
if (!absolute) {
|
||||||
|
const _path = process.cwd()
|
||||||
|
dest = path.join(_path, 'data', 'chatgpt', dest)
|
||||||
|
const lastLevelDirPath = path.dirname(dest)
|
||||||
|
mkdirs(lastLevelDirPath)
|
||||||
|
}
|
||||||
|
const fileStream = fs.createWriteStream(dest)
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
response.body.pipe(fileStream)
|
||||||
|
response.body.on('error', err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
fileStream.on('finish', function () {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
logger.info(`File downloaded successfully! URL: ${url}, Destination: ${dest}`)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPureText (filename) {
|
||||||
|
const ext = path.extname(filename).toLowerCase()
|
||||||
|
|
||||||
|
// List of file extensions that can be treated as pure text
|
||||||
|
const textFileExtensions = ['.txt', '.log', '.md', '.csv', '.html', '.css', '.js', '.json', '.xml', '.py', '.java', '.cpp', '.c', '.rb', '.php', '.sql', '.sh', '.pl', '.r', '.swift', '.go', '.ts', '.htm', '.yaml', '.yml', '.ini', '.properties', '.tsv']
|
||||||
|
|
||||||
|
// File types that require additional processing
|
||||||
|
const processingExtensions = ['.docx', '.pptx', '.xlsx', '.pdf', '.epub']
|
||||||
|
|
||||||
|
if (textFileExtensions.includes(ext)) {
|
||||||
|
return 'text'
|
||||||
|
} else if (processingExtensions.includes(ext)) {
|
||||||
|
// Return the file extension if additional processing is needed
|
||||||
|
return ext.replace('.', '')
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件中提取文本内容
|
||||||
|
* @param fileMsgElem MessageElem
|
||||||
|
* @param e
|
||||||
|
* @returns {Promise<{}>} 提取的文本内容和文件名
|
||||||
|
*/
|
||||||
|
export async function extractContentFromFile (fileMsgElem, e) {
|
||||||
|
logger.info('filename: ' + fileMsgElem.name)
|
||||||
|
let fileType = isPureText(fileMsgElem.name)
|
||||||
|
if (fileType) {
|
||||||
|
// 可读的文件类型
|
||||||
|
let fileUrl = e.isGroup ? await e.group.getFileUrl(fileMsgElem.fid) : await e.friend.getFileUrl(fileMsgElem.fid)
|
||||||
|
let filePath = await downloadFile(fileUrl, path.join('received', fileMsgElem.name))
|
||||||
|
switch (fileType) {
|
||||||
|
case 'pdf': {
|
||||||
|
if (!pdfjsLib) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const data = new Uint8Array(fs.readFileSync(filePath))
|
||||||
|
let loadingTask = pdfjsLib.getDocument(data)
|
||||||
|
try {
|
||||||
|
const pdfDocument = await loadingTask.promise
|
||||||
|
const numPages = pdfDocument.numPages
|
||||||
|
let pdfText = ''
|
||||||
|
|
||||||
|
// limit pages to prevent OOM or LLM down
|
||||||
|
let maxPage = 100
|
||||||
|
// Iterate through each page and extract text
|
||||||
|
for (let pageNum = 1; pageNum <= Math.min(numPages, maxPage); ++pageNum) {
|
||||||
|
const page = await pdfDocument.getPage(pageNum)
|
||||||
|
const textContent = await page.getTextContent()
|
||||||
|
const pageText = textContent.items.map(item => item.str).join(' ')
|
||||||
|
pdfText += pageText
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: pdfText,
|
||||||
|
name: fileMsgElem.name
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading PDF file:', error)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'doc': {
|
||||||
|
logger.error('not supported file type now')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
case 'docx': {
|
||||||
|
if (!mammoth) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { value } = await mammoth.extractRawText({ path: filePath })
|
||||||
|
return {
|
||||||
|
content: value,
|
||||||
|
name: fileMsgElem.name
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error reading .docx file:', error)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'xls': {
|
||||||
|
logger.error('not supported file type now')
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
case 'xlsx': {
|
||||||
|
if (!XLSX) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const workbook = XLSX.readFile(filePath)
|
||||||
|
const sheetName = workbook.SheetNames[0] // Assuming the first sheet is the one you want to read
|
||||||
|
const sheet = workbook.Sheets[sheetName]
|
||||||
|
const data = XLSX.utils.sheet_to_json(sheet, { header: 1 })
|
||||||
|
|
||||||
|
// Convert the 2D array to plain text
|
||||||
|
return {
|
||||||
|
content: data.map(row => row.join('\t')).join('\n'),
|
||||||
|
name: fileMsgElem.name
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading .xlsx file:', error)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'ppt': {
|
||||||
|
logger.error('not supported file type now')
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
case 'pptx': {
|
||||||
|
if (!PPTX) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let pptx = new PPTX.Composer()
|
||||||
|
await pptx.load(filePath)
|
||||||
|
let presentationContent = []
|
||||||
|
let slideNumber = 1
|
||||||
|
let maxSlideNumber = 60
|
||||||
|
while (slideNumber <= maxSlideNumber) {
|
||||||
|
let slide
|
||||||
|
try {
|
||||||
|
slide = pptx.getSlide(slideNumber)
|
||||||
|
} catch (error) {
|
||||||
|
// Slide number out of range, break the loop
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let slideContent = []
|
||||||
|
|
||||||
|
// Iterate through slide elements and extract text content
|
||||||
|
slide.elements.forEach(element => {
|
||||||
|
if (element.text) {
|
||||||
|
slideContent.push(element.text)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add slide content to the presentation content array
|
||||||
|
presentationContent.push(slideContent.join('\n'))
|
||||||
|
|
||||||
|
// Move to the next slide
|
||||||
|
slideNumber++
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
content: presentationContent.join('\n'),
|
||||||
|
name: fileMsgElem.name
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading .pptx file:', error)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'epub': {
|
||||||
|
logger.error('not supported file type now')
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// text type
|
||||||
|
const data = fs.readFileSync(filePath)
|
||||||
|
let text = String(data)
|
||||||
|
if (text) {
|
||||||
|
return {
|
||||||
|
content: text,
|
||||||
|
name: fileMsgElem.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ const defaultConfig = {
|
||||||
drawCD: 30,
|
drawCD: 30,
|
||||||
model: '',
|
model: '',
|
||||||
temperature: 0.8,
|
temperature: 0.8,
|
||||||
toneStyle: 'balanced', // or creative, precise
|
toneStyle: 'Sydney', // or creative, precise
|
||||||
sydney: pureSydneyInstruction,
|
sydney: pureSydneyInstruction,
|
||||||
sydneyReverseProxy: 'https://666102.201666.xyz',
|
sydneyReverseProxy: 'https://666102.201666.xyz',
|
||||||
sydneyForceUseReverse: false,
|
sydneyForceUseReverse: false,
|
||||||
sydneyWebsocketUseProxy: false,
|
sydneyWebsocketUseProxy: true,
|
||||||
sydneyBrainWash: true,
|
sydneyBrainWash: true,
|
||||||
sydneyBrainWashStrength: 15,
|
sydneyBrainWashStrength: 15,
|
||||||
sydneyBrainWashName: 'Sydney',
|
sydneyBrainWashName: 'Sydney',
|
||||||
|
|
@ -93,9 +93,10 @@ const defaultConfig = {
|
||||||
groupContextTip: '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。但要看清楚哦,不要把我和其他人弄混啦,也不要把自己看晕啦~~',
|
groupContextTip: '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。但要看清楚哦,不要把我和其他人弄混啦,也不要把自己看晕啦~~',
|
||||||
groupContextLength: 50,
|
groupContextLength: 50,
|
||||||
enableRobotAt: true,
|
enableRobotAt: true,
|
||||||
maxNumUserMessagesInConversation: 20,
|
maxNumUserMessagesInConversation: 30,
|
||||||
sydneyApologyIgnored: true,
|
sydneyApologyIgnored: true,
|
||||||
enforceMaster: false,
|
enforceMaster: false,
|
||||||
|
bingAPDraw: false,
|
||||||
serverPort: 3321,
|
serverPort: 3321,
|
||||||
serverHost: '',
|
serverHost: '',
|
||||||
viewHost: '',
|
viewHost: '',
|
||||||
|
|
@ -144,7 +145,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: '',
|
||||||
|
|
@ -152,7 +153,17 @@ const defaultConfig = {
|
||||||
claudeAITimeout: 120,
|
claudeAITimeout: 120,
|
||||||
claudeAIJA3: '772,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,27-5-65281-13-35-0-51-18-16-43-10-45-11-17513-23,29-23-24,0',
|
claudeAIJA3: '772,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,27-5-65281-13-35-0-51-18-16-43-10-45-11-17513-23,29-23-24,0',
|
||||||
claudeAIUA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
claudeAIUA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
||||||
version: 'v2.7.5'
|
// trss配置
|
||||||
|
trssBotUin: '',
|
||||||
|
// 同义千问
|
||||||
|
qwenApiKey: '',
|
||||||
|
qwenModel: 'qwen-turbo',
|
||||||
|
qwenTopP: 0.5,
|
||||||
|
qwenTopK: 50,
|
||||||
|
qwenSeed: 0,
|
||||||
|
qwenTemperature: 1,
|
||||||
|
qwenEnableSearch: true,
|
||||||
|
version: 'v2.7.7'
|
||||||
}
|
}
|
||||||
const _path = process.cwd()
|
const _path = process.cwd()
|
||||||
let config = {}
|
let config = {}
|
||||||
|
|
|
||||||
|
|
@ -1,278 +0,0 @@
|
||||||
import { readFileSync } from 'fs'
|
|
||||||
import { scrape } from './credential.js'
|
|
||||||
import fetch from 'node-fetch'
|
|
||||||
import crypto from 'crypto'
|
|
||||||
// used when test as a single file
|
|
||||||
// const _path = process.cwd()
|
|
||||||
const _path = process.cwd() + '/plugins/chatgpt-plugin/utils/poe'
|
|
||||||
const gqlDir = `${_path}/graphql`
|
|
||||||
const queries = {
|
|
||||||
// chatViewQuery: readFileSync(gqlDir + '/ChatViewQuery.graphql', 'utf8'),
|
|
||||||
addMessageBreakMutation: readFileSync(gqlDir + '/AddMessageBreakMutation.graphql', 'utf8'),
|
|
||||||
chatPaginationQuery: readFileSync(gqlDir + '/ChatPaginationQuery.graphql', 'utf8'),
|
|
||||||
addHumanMessageMutation: readFileSync(gqlDir + '/AddHumanMessageMutation.graphql', 'utf8'),
|
|
||||||
loginMutation: readFileSync(gqlDir + '/LoginWithVerificationCodeMutation.graphql', 'utf8'),
|
|
||||||
signUpWithVerificationCodeMutation: readFileSync(gqlDir + '/SignupWithVerificationCodeMutation.graphql', 'utf8'),
|
|
||||||
sendVerificationCodeMutation: readFileSync(gqlDir + '/SendVerificationCodeForLoginMutation.graphql', 'utf8')
|
|
||||||
}
|
|
||||||
const optionMap = [
|
|
||||||
{ title: 'Claude (Powered by Anthropic)', value: 'a2' },
|
|
||||||
{ title: 'Sage (Powered by OpenAI - logical)', value: 'capybara' },
|
|
||||||
{ title: 'Dragonfly (Powered by OpenAI - simpler)', value: 'nutria' },
|
|
||||||
{ title: 'ChatGPT (Powered by OpenAI - current)', value: 'chinchilla' },
|
|
||||||
{ title: 'Claude+', value: 'a2_2' },
|
|
||||||
{ title: 'GPT-4', value: 'beaver' }
|
|
||||||
]
|
|
||||||
export class PoeClient {
|
|
||||||
constructor (props) {
|
|
||||||
this.config = props
|
|
||||||
}
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Referrer: 'https://poe.com/',
|
|
||||||
Origin: 'https://poe.com',
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
|
||||||
}
|
|
||||||
|
|
||||||
chatId = 0
|
|
||||||
bot = ''
|
|
||||||
|
|
||||||
reConnectWs = false
|
|
||||||
|
|
||||||
async setCredentials () {
|
|
||||||
let result = await scrape(this.config.quora_cookie)
|
|
||||||
console.log(result)
|
|
||||||
this.config.quora_formkey = result.appSettings.formkey
|
|
||||||
this.config.channel_name = result.channelName
|
|
||||||
this.config.app_settings = result.appSettings
|
|
||||||
|
|
||||||
// set value
|
|
||||||
this.headers['poe-formkey'] = this.config.quora_formkey
|
|
||||||
this.headers['poe-tchannel'] = this.config.channel_name
|
|
||||||
this.headers.Cookie = this.config.quora_cookie
|
|
||||||
console.log(this.headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
async subscribe () {
|
|
||||||
const query = {
|
|
||||||
queryName: 'subscriptionsMutation',
|
|
||||||
variables: {
|
|
||||||
subscriptions: [
|
|
||||||
{
|
|
||||||
subscriptionName: 'messageAdded',
|
|
||||||
query: 'subscription subscriptions_messageAdded_Subscription(\n $chatId: BigInt!\n) {\n messageAdded(chatId: $chatId) {\n id\n messageId\n creationTime\n state\n ...ChatMessage_message\n ...chatHelpers_isBotMessage\n }\n}\n\nfragment ChatMessageDownvotedButton_message on Message {\n ...MessageFeedbackReasonModal_message\n ...MessageFeedbackOtherModal_message\n}\n\nfragment ChatMessageDropdownMenu_message on Message {\n id\n messageId\n vote\n text\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageFeedbackButtons_message on Message {\n id\n messageId\n vote\n voteReason\n ...ChatMessageDownvotedButton_message\n}\n\nfragment ChatMessageOverflowButton_message on Message {\n text\n ...ChatMessageDropdownMenu_message\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {\n messageId\n}\n\nfragment ChatMessageSuggestedReplies_message on Message {\n suggestedReplies\n ...ChatMessageSuggestedReplies_SuggestedReplyButton_message\n}\n\nfragment ChatMessage_message on Message {\n id\n messageId\n text\n author\n linkifiedText\n state\n ...ChatMessageSuggestedReplies_message\n ...ChatMessageFeedbackButtons_message\n ...ChatMessageOverflowButton_message\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isBotMessage\n ...chatHelpers_isChatBreak\n ...chatHelpers_useTimeoutLevel\n ...MarkdownLinkInner_message\n}\n\nfragment MarkdownLinkInner_message on Message {\n messageId\n}\n\nfragment MessageFeedbackOtherModal_message on Message {\n id\n messageId\n}\n\nfragment MessageFeedbackReasonModal_message on Message {\n id\n messageId\n}\n\nfragment chatHelpers_isBotMessage on Message {\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isChatBreak\n}\n\nfragment chatHelpers_isChatBreak on Message {\n author\n}\n\nfragment chatHelpers_isHumanMessage on Message {\n author\n}\n\nfragment chatHelpers_useTimeoutLevel on Message {\n id\n state\n text\n messageId\n}\n'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subscriptionName: 'viewerStateUpdated',
|
|
||||||
query: 'subscription subscriptions_viewerStateUpdated_Subscription {\n viewerStateUpdated {\n id\n ...ChatPageBotSwitcher_viewer\n }\n}\n\nfragment BotHeader_bot on Bot {\n displayName\n ...BotImage_bot\n}\n\nfragment BotImage_bot on Bot {\n profilePicture\n displayName\n}\n\nfragment BotLink_bot on Bot {\n displayName\n}\n\nfragment ChatPageBotSwitcher_viewer on Viewer {\n availableBots {\n id\n ...BotLink_bot\n ...BotHeader_bot\n }\n}\n'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
query: 'mutation subscriptionsMutation(\n $subscriptions: [AutoSubscriptionQuery!]!\n) {\n autoSubscribe(subscriptions: $subscriptions) {\n viewer {\n id\n }\n }\n}\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.makeRequest(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
async makeRequest (request) {
|
|
||||||
let payload = JSON.stringify(request)
|
|
||||||
let baseString = payload + this.headers['poe-formkey'] + 'WpuLMiXEKKE98j56k'
|
|
||||||
const md5 = crypto.createHash('md5').update(baseString).digest('hex')
|
|
||||||
const response = await fetch('https://poe.com/api/gql_POST', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: Object.assign(this.headers, {
|
|
||||||
'poe-tag-id': md5,
|
|
||||||
'content-type': 'application/json'
|
|
||||||
}),
|
|
||||||
body: payload
|
|
||||||
})
|
|
||||||
let text = await response.text()
|
|
||||||
try {
|
|
||||||
let result = JSON.parse(text)
|
|
||||||
console.log({ result })
|
|
||||||
return result
|
|
||||||
} catch (e) {
|
|
||||||
console.error(text)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBot (displayName) {
|
|
||||||
let r
|
|
||||||
let retry = 10
|
|
||||||
while (retry >= 0) {
|
|
||||||
let url = `https://poe.com/_next/data/${this.nextData.buildId}/${displayName}.json`
|
|
||||||
let r = await fetch(url, {
|
|
||||||
headers: this.headers
|
|
||||||
})
|
|
||||||
let res = await r.text()
|
|
||||||
try {
|
|
||||||
let chatData = (JSON.parse(res)).pageProps.payload.chatOfBotDisplayName
|
|
||||||
return chatData
|
|
||||||
} catch (e) {
|
|
||||||
r = res
|
|
||||||
retry--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatId () {
|
|
||||||
let r = await fetch('https://poe.com', {
|
|
||||||
headers: this.headers
|
|
||||||
})
|
|
||||||
let text = await r.text()
|
|
||||||
const jsonRegex = /<script id="__NEXT_DATA__" type="application\/json">(.+?)<\/script>/
|
|
||||||
const jsonText = text.match(jsonRegex)[1]
|
|
||||||
const nextData = JSON.parse(jsonText)
|
|
||||||
this.nextData = nextData
|
|
||||||
this.viewer = nextData.props.pageProps.payload.viewer
|
|
||||||
this.formkey = nextData.props.formkey
|
|
||||||
let bots = this.viewer.availableBots
|
|
||||||
this.bots = {}
|
|
||||||
for (let i = 0; i < bots.length; i++) {
|
|
||||||
let bot = bots[i]
|
|
||||||
let chatData = await this.getBot(bot.displayName)
|
|
||||||
this.bots[chatData.defaultBotObject.nickname] = chatData
|
|
||||||
}
|
|
||||||
console.log(this.bots)
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearContext (bot) {
|
|
||||||
try {
|
|
||||||
const data = await this.makeRequest({
|
|
||||||
query: `${queries.addMessageBreakMutation}`,
|
|
||||||
variables: { chatId: this.config.chat_ids[bot] }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!data.data) {
|
|
||||||
this.reConnectWs = true // for websocket purpose
|
|
||||||
console.log('ON TRY! Could not clear context! Trying to reLogin..')
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
} catch (e) {
|
|
||||||
this.reConnectWs = true // for websocket purpose
|
|
||||||
console.log('ON CATCH! Could not clear context! Trying to reLogin..')
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendMsg (bot, query) {
|
|
||||||
try {
|
|
||||||
const data = await this.makeRequest({
|
|
||||||
query: `${queries.addHumanMessageMutation}`,
|
|
||||||
variables: {
|
|
||||||
bot,
|
|
||||||
chatId: this.bots[bot].chatId,
|
|
||||||
query,
|
|
||||||
source: null,
|
|
||||||
withChatBreak: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log(data)
|
|
||||||
if (!data.data) {
|
|
||||||
this.reConnectWs = true // for cli websocket purpose
|
|
||||||
console.log('Could not send message! Trying to reLogin..')
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
} catch (e) {
|
|
||||||
this.reConnectWs = true // for cli websocket purpose
|
|
||||||
console.error(e)
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getHistory (bot) {
|
|
||||||
try {
|
|
||||||
let response = await this.makeRequest({
|
|
||||||
query: `${queries.chatPaginationQuery}`,
|
|
||||||
variables: {
|
|
||||||
before: null,
|
|
||||||
bot,
|
|
||||||
last: 25
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data.chatOfBot.messagesConnection.edges
|
|
||||||
.map(({ node: { messageId, text, authorNickname } }) => ({
|
|
||||||
messageId,
|
|
||||||
text,
|
|
||||||
authorNickname
|
|
||||||
}))
|
|
||||||
} catch (e) {
|
|
||||||
console.log('There has been an error while fetching your history!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteMessages (msgIds) {
|
|
||||||
await this.makeRequest({
|
|
||||||
queryName: 'MessageDeleteConfirmationModal_deleteMessageMutation_Mutation',
|
|
||||||
variables: {
|
|
||||||
messageIds: msgIds
|
|
||||||
},
|
|
||||||
query: 'mutation MessageDeleteConfirmationModal_deleteMessageMutation_Mutation(\n $messageIds: [BigInt!]!\n){\n messagesDelete(messageIds: $messageIds) {\n edgeIds\n }\n}\n'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getResponse (bot) {
|
|
||||||
let text
|
|
||||||
let state
|
|
||||||
let authorNickname
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
||||||
let response = await this.makeRequest({
|
|
||||||
query: `${queries.chatPaginationQuery}`,
|
|
||||||
variables: {
|
|
||||||
before: null,
|
|
||||||
bot,
|
|
||||||
last: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let base = response.data.chatOfBot.messagesConnection.edges
|
|
||||||
let lastEdgeIndex = base.length - 1
|
|
||||||
text = base[lastEdgeIndex].node.text
|
|
||||||
authorNickname = base[lastEdgeIndex].node.authorNickname
|
|
||||||
state = base[lastEdgeIndex].node.state
|
|
||||||
if (state === 'complete' && authorNickname === bot) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Could not get response!')
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
message: 'failed',
|
|
||||||
data: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: true,
|
|
||||||
message: 'success',
|
|
||||||
data: text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testPoe () {
|
|
||||||
// const key = 'deb04db9f2332a3287b7d2545061af62'
|
|
||||||
// const channel = 'poe-chan55-8888-ujygckefewomybvkqfrp'
|
|
||||||
const cookie = 'p-b=WSvmyvjHVJoMtQVkirtn-A%3D%3D'
|
|
||||||
let client = new PoeClient({
|
|
||||||
// quora_formkey: key,
|
|
||||||
// channel_name: channel,
|
|
||||||
quora_cookie: cookie
|
|
||||||
})
|
|
||||||
await client.setCredentials()
|
|
||||||
await client.getChatId()
|
|
||||||
let ai = 'a2'
|
|
||||||
await client.sendMsg(ai, '你说话不是很通顺啊')
|
|
||||||
const response = await client.getResponse(ai)
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
// testPoe().then(res => {
|
|
||||||
// console.log(res)
|
|
||||||
// })
|
|
||||||
|
|
@ -17,7 +17,7 @@ export class APTool extends AbstractTool {
|
||||||
|
|
||||||
func = async function (opts, e) {
|
func = async function (opts, e) {
|
||||||
let { prompt } = opts
|
let { prompt } = opts
|
||||||
if (e.at === Bot.uin) {
|
if (e.at === e.bot.uin) {
|
||||||
e.at = null
|
e.at = null
|
||||||
}
|
}
|
||||||
e.atBot = false
|
e.atBot = false
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,18 @@ export class EditCardTool extends AbstractTool {
|
||||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||||
groupId = isNaN(groupId) || !groupId ? e.group_id : parseInt(groupId.trim())
|
groupId = isNaN(groupId) || !groupId ? e.group_id : parseInt(groupId.trim())
|
||||||
|
|
||||||
let group = await Bot.pickGroup(groupId)
|
let group = await e.bot.pickGroup(groupId)
|
||||||
|
try {
|
||||||
let mm = await group.getMemberMap()
|
let mm = await group.getMemberMap()
|
||||||
if (!mm.has(qq)) {
|
if (!mm.has(qq)) {
|
||||||
return `failed, the user ${qq} is not in group ${groupId}`
|
return `failed, the user ${qq} is not in group ${groupId}`
|
||||||
}
|
}
|
||||||
if (mm.get(Bot.uin).role === 'member') {
|
if (mm.get(e.bot.uin) && mm.get(e.bot.uin).role === 'member') {
|
||||||
return `failed, you, not user, don't have permission to edit card in group ${groupId}`
|
return `failed, you, not user, don't have permission to edit card in group ${groupId}`
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('获取群信息失败,可能使用的底层协议不完善')
|
||||||
|
}
|
||||||
logger.info('edit card: ', groupId, qq)
|
logger.info('edit card: ', groupId, qq)
|
||||||
await group.setCard(qq, card)
|
await group.setCard(qq, card)
|
||||||
return `the user ${qq}'s card has been changed into ${card}`
|
return `the user ${qq}'s card has been changed into ${card}`
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export class EliMovieTool extends AbstractTool {
|
||||||
if (yesOrNo === 'no') {
|
if (yesOrNo === 'no') {
|
||||||
return 'tell user why you don\'t want to check'
|
return 'tell user why you don\'t want to check'
|
||||||
}
|
}
|
||||||
if (e.at === Bot.uin) {
|
if (e.at === e.bot.uin) {
|
||||||
e.at = null
|
e.at = null
|
||||||
}
|
}
|
||||||
e.atBot = false
|
e.atBot = false
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ export class HandleMessageMsgTool extends AbstractTool {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'essence': {
|
case 'essence': {
|
||||||
await Bot.setEssenceMessage(messageId)
|
await e.bot.setEssenceMessage(messageId)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'un-essence': {
|
case 'un-essence': {
|
||||||
await Bot.removeEssenceMessage(messageId)
|
await e.bot.removeEssenceMessage(messageId)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,13 @@ export class JinyanTool extends AbstractTool {
|
||||||
qq = qq !== 'all'
|
qq = qq !== 'all'
|
||||||
? isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
? isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||||
: 'all'
|
: 'all'
|
||||||
let group = await Bot.pickGroup(groupId)
|
let group = await e.bot.pickGroup(groupId)
|
||||||
if (qq !== 'all') {
|
if (qq !== 'all') {
|
||||||
let m = await group.getMemberMap()
|
let m = await group.getMemberMap()
|
||||||
if (!m.has(qq)) {
|
if (!m.has(qq)) {
|
||||||
return `failed, the user ${qq} is not in group ${groupId}`
|
return `failed, the user ${qq} is not in group ${groupId}`
|
||||||
}
|
}
|
||||||
if (m.get(Bot.uin).role === 'member') {
|
if (m.get(e.bot.uin).role === 'member') {
|
||||||
return `failed, you, not user, don't have permission to mute other in group ${groupId}`
|
return `failed, you, not user, don't have permission to mute other in group ${groupId}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export class KickOutTool extends AbstractTool {
|
||||||
return 'the user is not admin, he cannot kickout other people. he should be punished'
|
return 'the user is not admin, he cannot kickout other people. he should be punished'
|
||||||
}
|
}
|
||||||
console.log('kickout', groupId, qq)
|
console.log('kickout', groupId, qq)
|
||||||
let group = await Bot.pickGroup(groupId)
|
let group = await e.bot.pickGroup(groupId)
|
||||||
await group.kickMember(qq)
|
await group.kickMember(qq)
|
||||||
if (isPunish === 'true') {
|
if (isPunish === 'true') {
|
||||||
return `the user ${qq} has been kicked out from group ${groupId} as punishment because of his 不正当行为`
|
return `the user ${qq} has been kicked out from group ${groupId} as punishment because of his 不正当行为`
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export class QueryGenshinTool extends AbstractTool {
|
||||||
func = async function (opts, e) {
|
func = async function (opts, e) {
|
||||||
let { qq, uid = '', character = '' } = opts
|
let { qq, uid = '', character = '' } = opts
|
||||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||||
if (e.at === Bot.uin) {
|
if (e.at === e.bot.uin) {
|
||||||
e.at = null
|
e.at = null
|
||||||
}
|
}
|
||||||
e.atBot = false
|
e.atBot = false
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export class QueryStarRailTool extends AbstractTool {
|
||||||
func = async function (opts, e) {
|
func = async function (opts, e) {
|
||||||
let { qq, uid, character } = opts
|
let { qq, uid, character } = opts
|
||||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||||
if (e.at === Bot.uin) {
|
if (e.at === e.bot.uin) {
|
||||||
e.at = null
|
e.at = null
|
||||||
}
|
}
|
||||||
e.atBot = false
|
e.atBot = false
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ export class SendAudioMessageTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
try {
|
try {
|
||||||
switch (ttsMode) {
|
switch (ttsMode) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
@ -102,14 +102,19 @@ export class SendAudioMessageTool extends AbstractTool {
|
||||||
return `audio generation failed, error: ${JSON.stringify(err)}`
|
return `audio generation failed, error: ${JSON.stringify(err)}`
|
||||||
}
|
}
|
||||||
if (sendable) {
|
if (sendable) {
|
||||||
let groupList = await Bot.getGroupList()
|
let groupList
|
||||||
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
await group.sendMsg(sendable)
|
await group.sendMsg(sendable)
|
||||||
return 'audio has been sent to group' + target
|
return 'audio has been sent to group' + target
|
||||||
} else {
|
} else {
|
||||||
let user = await Bot.pickFriend(target)
|
let user = await e.bot.pickFriend(target)
|
||||||
await user.sendMsg(sendable)
|
await user.sendMsg(sendable)
|
||||||
return 'audio has been sent to user' + target
|
return 'audio has been sent to user' + target
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,16 @@ export class SendAvatarTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
|
let groupList
|
||||||
let groupList = await Bot.getGroupList()
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
console.log('sendAvatar', target, pictures)
|
console.log('sendAvatar', target, pictures)
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
await group.sendMsg(pictures)
|
await group.sendMsg(pictures)
|
||||||
}
|
}
|
||||||
return `the ${pictures.length > 1 ? 'users: ' + qq + '\'s avatar' : 'avatar'} has been sent to group ${target}`
|
return `the ${pictures.length > 1 ? 'users: ' + qq + '\'s avatar' : 'avatar'} has been sent to group ${target}`
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ export class SendVideoTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
|
|
||||||
let msg = []
|
let msg = []
|
||||||
try {
|
try {
|
||||||
let { arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like, honor } = await getBilibili(id)
|
let { arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like, honor } = await getBilibili(id)
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
msg.push(title.replace(/(<([^>]+)>)/ig, '') + '\n')
|
msg.push(title.replace(/(<([^>]+)>)/ig, '') + '\n')
|
||||||
msg.push(`UP主:${author} 发布日期:${formatDate(new Date(pubdate * 1000))} 播放量:${play} 点赞:${like}\n`)
|
msg.push(`UP主:${author} 发布日期:${formatDate(new Date(pubdate * 1000))} 播放量:${play} 点赞:${like}\n`)
|
||||||
msg.push(arcurl + '\n')
|
msg.push(arcurl + '\n')
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,21 @@ export class SendDiceTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
let groupList = await Bot.getGroupList()
|
let groupList
|
||||||
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
num = isNaN(num) || !num ? 1 : num > 5 ? 5 : num
|
num = isNaN(num) || !num ? 1 : num > 5 ? 5 : num
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target, true)
|
let group = await e.bot.pickGroup(target, true)
|
||||||
for (let i = 0; i < num; i++) {
|
for (let i = 0; i < num; i++) {
|
||||||
await group.sendMsg(segment.dice())
|
await group.sendMsg(segment.dice())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let friend = await Bot.pickFriend(target)
|
let friend = await e.bot.pickFriend(target)
|
||||||
await friend.sendMsg(segment.dice())
|
await friend.sendMsg(segment.dice())
|
||||||
}
|
}
|
||||||
if (num === 5) {
|
if (num === 5) {
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,21 @@ export class SendMessageToSpecificGroupOrUserTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
|
|
||||||
let groupList = await Bot.getGroupList()
|
let groupList
|
||||||
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
await group.sendMsg(await convertFaces(msg, true, e))
|
await group.sendMsg(await convertFaces(msg, true, e))
|
||||||
return 'msg has been sent to group' + target
|
return 'msg has been sent to group' + target
|
||||||
} else {
|
} else {
|
||||||
let user = await Bot.pickFriend(target)
|
let user = await e.bot.pickFriend(target)
|
||||||
await user.sendMsg(msg)
|
await user.sendMsg(msg)
|
||||||
return 'msg has been sent to user' + target
|
return 'msg has been sent to user' + target
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ export class SendMusicTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
await group.shareMusic('163', id)
|
await group.shareMusic('163', id)
|
||||||
return `the music has been shared to ${target}`
|
return `the music has been shared to ${target}`
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export class SendPictureTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
// 处理错误url和picture留空的情况
|
// 处理错误url和picture留空的情况
|
||||||
const urlRegex = /(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((?:(?:[a-z0-9\u00a1-\u4dff\u9fd0-\uffff][a-z0-9\u00a1-\u4dff\u9fd0-\uffff_-]{0,62})?[a-z0-9\u00a1-\u4dff\u9fd0-\uffff]\.)+(?:[a-z\u00a1-\u4dff\u9fd0-\uffff]{2,}\.?))(?::\d{2,5})?)(?:\/[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),%]+)*(?:\?(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*(),%:@&=]|(?:[\[\]])|(?:[\u00a1-\u4dff\u9fd0-\uffff]))*)?(?:#(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),;:@&=]|(?:[\[\]]))*)?\/?/i
|
const urlRegex = /(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((?:(?:[a-z0-9\u00a1-\u4dff\u9fd0-\uffff][a-z0-9\u00a1-\u4dff\u9fd0-\uffff_-]{0,62})?[a-z0-9\u00a1-\u4dff\u9fd0-\uffff]\.)+(?:[a-z\u00a1-\u4dff\u9fd0-\uffff]{2,}\.?))(?::\d{2,5})?)(?:\/[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),%]+)*(?:\?(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*(),%:@&=]|(?:[\[\]])|(?:[\u00a1-\u4dff\u9fd0-\uffff]))*)?(?:#(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),;:@&=]|(?:[\[\]]))*)?\/?/i
|
||||||
if (/https:\/\/example.com/.test(urlOfPicture) || !urlOfPicture || !urlRegex.test(urlOfPicture)) urlOfPicture = ''
|
if (/https:\/\/example.com/.test(urlOfPicture) || !urlOfPicture || !urlRegex.test(urlOfPicture)) urlOfPicture = ''
|
||||||
|
|
@ -32,14 +32,19 @@ export class SendPictureTool extends AbstractTool {
|
||||||
let pictures = urlOfPicture.trim().split(' ')
|
let pictures = urlOfPicture.trim().split(' ')
|
||||||
logger.mark('pictures to send: ', pictures)
|
logger.mark('pictures to send: ', pictures)
|
||||||
pictures = pictures.map(img => segment.image(img))
|
pictures = pictures.map(img => segment.image(img))
|
||||||
let groupList = await Bot.getGroupList()
|
let groupList
|
||||||
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
await group.sendMsg(pictures)
|
await group.sendMsg(pictures)
|
||||||
return 'picture has been sent to group' + target
|
return 'picture has been sent to group' + target
|
||||||
} else {
|
} else {
|
||||||
let user = await Bot.pickFriend(target)
|
let user = await e.bot.pickFriend(target)
|
||||||
await user.sendMsg(pictures)
|
await user.sendMsg(pictures)
|
||||||
return 'picture has been sent to user' + target
|
return 'picture has been sent to user' + target
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,18 @@ export class SendRPSTool extends AbstractTool {
|
||||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||||
? defaultTarget
|
? defaultTarget
|
||||||
: parseInt(targetGroupIdOrQQNumber) === Bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
let groupList = await Bot.getGroupList()
|
let groupList
|
||||||
|
try {
|
||||||
|
groupList = await e.bot.getGroupList()
|
||||||
|
} catch (err) {
|
||||||
|
groupList = e.bot.gl
|
||||||
|
}
|
||||||
if (groupList.get(target)) {
|
if (groupList.get(target)) {
|
||||||
let group = await Bot.pickGroup(target, true)
|
let group = await e.bot.pickGroup(target, true)
|
||||||
await group.sendMsg(segment.rps(num))
|
await group.sendMsg(segment.rps(num))
|
||||||
} else {
|
} else {
|
||||||
let friend = await Bot.pickFriend(target)
|
let friend = await e.bot.pickFriend(target)
|
||||||
await friend.sendMsg(segment.rps(num))
|
await friend.sendMsg(segment.rps(num))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,12 @@ export class SetTitleTool extends AbstractTool {
|
||||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||||
groupId = isNaN(groupId) || !groupId ? e.group_id : parseInt(groupId.trim())
|
groupId = isNaN(groupId) || !groupId ? e.group_id : parseInt(groupId.trim())
|
||||||
|
|
||||||
let group = await Bot.pickGroup(groupId)
|
let group = await e.bot.pickGroup(groupId)
|
||||||
let mm = await group.getMemberMap()
|
let mm = await group.getMemberMap()
|
||||||
if (!mm.has(qq)) {
|
if (!mm.has(qq)) {
|
||||||
return `failed, the user ${qq} is not in group ${groupId}`
|
return `failed, the user ${qq} is not in group ${groupId}`
|
||||||
}
|
}
|
||||||
if (mm.get(Bot.uin).role !== 'owner') {
|
if (mm.get(e.bot.uin).role !== 'owner') {
|
||||||
return 'on group owner can give title'
|
return 'on group owner can give title'
|
||||||
}
|
}
|
||||||
logger.info('edit card: ', groupId, qq)
|
logger.info('edit card: ', groupId, qq)
|
||||||
|
|
|
||||||
10
utils/tts.js
10
utils/tts.js
|
|
@ -2,14 +2,8 @@ import { Config } from './config.js'
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { wrapTextByLanguage } from './common.js'
|
import { wrapTextByLanguage } from './common.js'
|
||||||
let proxy
|
import { getProxy } from './proxy.js'
|
||||||
if (Config.proxy) {
|
let proxy = getProxy()
|
||||||
try {
|
|
||||||
proxy = (await import('https-proxy-agent')).default
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('未安装https-proxy-agent,请在插件目录下执行pnpm add https-proxy-agent')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const newFetch = (url, options = {}) => {
|
const newFetch = (url, options = {}) => {
|
||||||
const defaultOptions = Config.proxy
|
const defaultOptions = Config.proxy
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import crypto from 'crypto'
|
||||||
import child_process from 'child_process'
|
import child_process from 'child_process'
|
||||||
import { Config } from './config.js'
|
import { Config } from './config.js'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { mkdirs } from './common.js'
|
import { mkdirs, getUin } from './common.js'
|
||||||
let module
|
let module
|
||||||
try {
|
try {
|
||||||
module = await import('oicq')
|
module = await import('oicq')
|
||||||
|
|
@ -41,7 +41,7 @@ if (module) {
|
||||||
// import { pcm2slk } from 'node-silk'
|
// import { pcm2slk } from 'node-silk'
|
||||||
let errors = {}
|
let errors = {}
|
||||||
|
|
||||||
async function uploadRecord (recordUrl, ttsMode = 'vits-uma-genshin-honkai') {
|
async function uploadRecord (recordUrl, ttsMode = 'vits-uma-genshin-honkai', ignoreEncode = false) {
|
||||||
let recordType = 'url'
|
let recordType = 'url'
|
||||||
let tmpFile = ''
|
let tmpFile = ''
|
||||||
if (ttsMode === 'azure') {
|
if (ttsMode === 'azure') {
|
||||||
|
|
@ -50,6 +50,9 @@ async function uploadRecord (recordUrl, ttsMode = 'vits-uma-genshin-honkai') {
|
||||||
recordType = 'buffer'
|
recordType = 'buffer'
|
||||||
tmpFile = `data/chatgpt/tts/tmp/${crypto.randomUUID()}.wav`
|
tmpFile = `data/chatgpt/tts/tmp/${crypto.randomUUID()}.wav`
|
||||||
}
|
}
|
||||||
|
if (ignoreEncode) {
|
||||||
|
return segment.record(recordUrl)
|
||||||
|
}
|
||||||
let result
|
let result
|
||||||
if (Config.ttsHD) {
|
if (Config.ttsHD) {
|
||||||
result = await getPttBuffer(recordUrl, Bot.config.ffmpeg_path, false)
|
result = await getPttBuffer(recordUrl, Bot.config.ffmpeg_path, false)
|
||||||
|
|
@ -144,7 +147,7 @@ async function uploadRecord (recordUrl, ttsMode = 'vits-uma-genshin-honkai') {
|
||||||
2: 3,
|
2: 3,
|
||||||
5: {
|
5: {
|
||||||
1: Contactable.target,
|
1: Contactable.target,
|
||||||
2: Bot.uin,
|
2: getUin(),
|
||||||
3: 0,
|
3: 0,
|
||||||
4: hash,
|
4: hash,
|
||||||
5: buf.length,
|
5: buf.length,
|
||||||
|
|
@ -188,7 +191,7 @@ async function uploadRecord (recordUrl, ttsMode = 'vits-uma-genshin-honkai') {
|
||||||
const fid = rsp[11].toBuffer()
|
const fid = rsp[11].toBuffer()
|
||||||
const b = core.pb.encode({
|
const b = core.pb.encode({
|
||||||
1: 4,
|
1: 4,
|
||||||
2: Bot.uin,
|
2: getUin(),
|
||||||
3: fid,
|
3: fid,
|
||||||
4: hash,
|
4: hash,
|
||||||
5: hash.toString('hex') + '.amr',
|
5: hash.toString('hex') + '.amr',
|
||||||
|
|
|
||||||
33
utils/version.js
Normal file
33
utils/version.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* from miao-plugin
|
||||||
|
*
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
let packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
||||||
|
|
||||||
|
const yunzaiVersion = packageJson.version
|
||||||
|
const isV3 = yunzaiVersion[0] === '3'
|
||||||
|
let isMiao = false; let isTrss = false
|
||||||
|
let name = 'Yunzai-Bot'
|
||||||
|
if (packageJson.name === 'miao-yunzai') {
|
||||||
|
isMiao = true
|
||||||
|
name = 'Miao-Yunzai'
|
||||||
|
} else if (packageJson.name === 'trss-yunzai') {
|
||||||
|
isMiao = true
|
||||||
|
isTrss = true
|
||||||
|
name = 'TRSS-Yunzai'
|
||||||
|
}
|
||||||
|
|
||||||
|
let Version = {
|
||||||
|
isV3,
|
||||||
|
isMiao,
|
||||||
|
isTrss,
|
||||||
|
name,
|
||||||
|
get yunzai () {
|
||||||
|
return yunzaiVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Version
|
||||||
|
|
@ -10,11 +10,11 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tokenizer {
|
export class Tokenizer {
|
||||||
async getHistory (groupId, date = new Date(), duration = 0) {
|
async getHistory (e, groupId, date = new Date(), duration = 0, userId) {
|
||||||
if (!groupId) {
|
if (!groupId) {
|
||||||
throw new Error('no valid group id')
|
throw new Error('no valid group id')
|
||||||
}
|
}
|
||||||
let group = Bot.pickGroup(groupId, true)
|
let group = e.bot.pickGroup(groupId, true)
|
||||||
let latestChat = await group.getChatHistory(0, 1)
|
let latestChat = await group.getChatHistory(0, 1)
|
||||||
let seq = latestChat[0].seq
|
let seq = latestChat[0].seq
|
||||||
let chats = latestChat
|
let chats = latestChat
|
||||||
|
|
@ -42,13 +42,14 @@ export class Tokenizer {
|
||||||
// if duration > 0, go back to the specified number of hours
|
// if duration > 0, go back to the specified number of hours
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
// duration should be in range [0, 24]
|
// duration should be in range [0, 24]
|
||||||
duration = Math.min(duration, 24)
|
// duration = Math.min(duration, 24)
|
||||||
startOfSpecifiedDate = currentTime - (duration * 60 * 60 * 1000)
|
startOfSpecifiedDate = currentTime - (duration * 60 * 60 * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Get the end of the specified date by adding 24 hours (in milliseconds)
|
// Step 4: Get the end of the specified date by current time
|
||||||
const endOfSpecifiedDate = startOfSpecifiedDate + (24 * 60 * 60 * 1000)
|
const endOfSpecifiedDate = currentTime
|
||||||
while (isTimestampInDateRange(chats[0]?.time, startOfSpecifiedDate, endOfSpecifiedDate) && isTimestampInDateRange(chats[chats.length - 1]?.time, startOfSpecifiedDate, endOfSpecifiedDate)) {
|
while (isTimestampInDateRange(chats[0]?.time, startOfSpecifiedDate, endOfSpecifiedDate) &&
|
||||||
|
isTimestampInDateRange(chats[chats.length - 1]?.time, startOfSpecifiedDate, endOfSpecifiedDate)) {
|
||||||
let chatHistory = await group.getChatHistory(seq, 20)
|
let chatHistory = await group.getChatHistory(seq, 20)
|
||||||
if (chatHistory.length === 1) {
|
if (chatHistory.length === 1) {
|
||||||
if (chats[0].seq === chatHistory[0].seq) {
|
if (chats[0].seq === chatHistory[0].seq) {
|
||||||
|
|
@ -58,23 +59,29 @@ export class Tokenizer {
|
||||||
}
|
}
|
||||||
chats.push(...chatHistory)
|
chats.push(...chatHistory)
|
||||||
chats.sort(compareByTime)
|
chats.sort(compareByTime)
|
||||||
seq = chatHistory[0].seq
|
seq = chatHistory?.[0]?.seq
|
||||||
|
if (!seq) {
|
||||||
|
break
|
||||||
|
}
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.info(`拉取到${chatHistory.length}条聊天记录,当前已累计获取${chats.length}条聊天记录,继续拉...`)
|
logger.info(`拉取到${chatHistory.length}条聊天记录,当前已累计获取${chats.length}条聊天记录,继续拉...`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chats = chats.filter(chat => isTimestampInDateRange(chat.time, startOfSpecifiedDate, endOfSpecifiedDate))
|
chats = chats.filter(chat => isTimestampInDateRange(chat.time, startOfSpecifiedDate, endOfSpecifiedDate))
|
||||||
|
if (userId) {
|
||||||
|
chats = chats.filter(chat => chat.sender.user_id === userId)
|
||||||
|
}
|
||||||
return chats
|
return chats
|
||||||
}
|
}
|
||||||
|
|
||||||
async getKeywordTopK (groupId, topK = 100, duration = 0) {
|
async getKeywordTopK (e, groupId, topK = 100, duration = 0, userId) {
|
||||||
if (!nodejieba) {
|
if (!nodejieba) {
|
||||||
throw new Error('未安装node-rs/jieba,娱乐功能-词云统计不可用')
|
throw new Error('未安装node-rs/jieba,娱乐功能-词云统计不可用')
|
||||||
}
|
}
|
||||||
// duration represents the number of hours to go back, should in range [0, 24]
|
// duration represents the number of hours to go back, should in range [0, 24]
|
||||||
let chats = await this.getHistory(groupId, new Date(), duration)
|
let chats = await this.getHistory(e, groupId, new Date(), duration, userId)
|
||||||
let duration_str = duration > 0 ? `${duration}小时` : '今日'
|
let durationStr = duration > 0 ? `${duration}小时` : '今日'
|
||||||
logger.mark(`聊天记录拉取完成,获取到${duration_str}内${chats.length}条聊天记录,准备分词中`)
|
logger.mark(`聊天记录拉取完成,获取到${durationStr}内${chats.length}条聊天记录,准备分词中`)
|
||||||
|
|
||||||
const _path = process.cwd()
|
const _path = process.cwd()
|
||||||
let stopWordsPath = `${_path}/plugins/chatgpt-plugin/utils/wordcloud/cn_stopwords.txt`
|
let stopWordsPath = `${_path}/plugins/chatgpt-plugin/utils/wordcloud/cn_stopwords.txt`
|
||||||
|
|
@ -85,18 +92,18 @@ export class Tokenizer {
|
||||||
// 只统计文本内容
|
// 只统计文本内容
|
||||||
.filter(item => item.type == 'text')
|
.filter(item => item.type == 'text')
|
||||||
.map(textItem => `${textItem.text}`)
|
.map(textItem => `${textItem.text}`)
|
||||||
.join("").trim()
|
.join('').trim()
|
||||||
)
|
)
|
||||||
.map(c => {
|
.map(c => {
|
||||||
let length = c.length
|
// let length = c.length
|
||||||
let threshold = 10
|
let threshold = 2
|
||||||
if (length < 100 && length > 50) {
|
// if (length < 100 && length > 50) {
|
||||||
threshold = 6
|
// threshold = 6
|
||||||
} else if (length <= 50 && length > 25) {
|
// } else if (length <= 50 && length > 25) {
|
||||||
threshold = 3
|
// threshold = 3
|
||||||
} else if (length <= 25) {
|
// } else if (length <= 25) {
|
||||||
threshold = 2
|
// threshold = 2
|
||||||
}
|
// }
|
||||||
return nodejieba.extract(c, threshold)
|
return nodejieba.extract(c, threshold)
|
||||||
})
|
})
|
||||||
.reduce((acc, curr) => acc.concat(curr), [])
|
.reduce((acc, curr) => acc.concat(curr), [])
|
||||||
|
|
@ -132,6 +139,85 @@ export class Tokenizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ShamrockTokenizer extends Tokenizer {
|
||||||
|
async getHistory (e, groupId, date = new Date(), duration = 0, userId) {
|
||||||
|
logger.mark('当前使用Shamrock适配器')
|
||||||
|
if (!groupId) {
|
||||||
|
throw new Error('no valid group id')
|
||||||
|
}
|
||||||
|
let group = e.bot.pickGroup(groupId, true)
|
||||||
|
// 直接加大力度
|
||||||
|
let pageSize = 500
|
||||||
|
let chats = (await group.getChatHistory(0, pageSize, false)) || []
|
||||||
|
// 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
|
||||||
|
// 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 currentTime
|
||||||
|
const endOfSpecifiedDate = currentTime
|
||||||
|
let cursor = chats.length
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// | | |
|
||||||
|
// -------------------------------------------------------
|
||||||
|
// ^ ^
|
||||||
|
// long ago cursor+pageSize cursor current
|
||||||
|
while (isTimestampInDateRange(chats[0]?.time, startOfSpecifiedDate, endOfSpecifiedDate)) {
|
||||||
|
// 由于Shamrock消息是从最新的开始拉,结束时由于动态更新,一旦有人发送消息就会立刻停止,所以不判断结束时间
|
||||||
|
// 拉到后面会巨卡,所以增大page减少次数
|
||||||
|
pageSize = Math.floor(Math.max(cursor / 2, pageSize))
|
||||||
|
cursor = cursor + pageSize
|
||||||
|
let retries = 3
|
||||||
|
let chatHistory
|
||||||
|
while (retries >= 0) {
|
||||||
|
try {
|
||||||
|
chatHistory = await group.getChatHistory(0, cursor, false)
|
||||||
|
break
|
||||||
|
} catch (err) {
|
||||||
|
if (retries === 0) {
|
||||||
|
logger.error(err)
|
||||||
|
}
|
||||||
|
retries--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (retries < 0) {
|
||||||
|
logger.warn('拉不动了,就这样吧')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (chatHistory.length === 1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (chatHistory.length === chats.length) {
|
||||||
|
// 没有了!再拉也没有了
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let oldLength = chats.length
|
||||||
|
chats = chatHistory
|
||||||
|
// chats.sort(compareByTime)
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info(`拉取到${chats.length - oldLength}条聊天记录,当前已累计获取${chats.length}条聊天记录,继续拉...`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chats = chats.filter(chat => isTimestampInDateRange(chat.time, startOfSpecifiedDate, endOfSpecifiedDate))
|
||||||
|
if (userId) {
|
||||||
|
chats = chats.filter(chat => chat.sender.user_id === userId)
|
||||||
|
}
|
||||||
|
return chats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isTimestampInDateRange (timestamp, startOfSpecifiedDate, endOfSpecifiedDate) {
|
function isTimestampInDateRange (timestamp, startOfSpecifiedDate, endOfSpecifiedDate) {
|
||||||
if (!timestamp) {
|
if (!timestamp) {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
import { Tokenizer } from './tokenizer.js'
|
import { ShamrockTokenizer, Tokenizer } from './tokenizer.js'
|
||||||
import { render } from '../common.js'
|
import { render } from '../common.js'
|
||||||
|
|
||||||
export async function makeWordcloud (e, groupId, duration = 0) {
|
export async function makeWordcloud (e, groupId, duration = 0, userId) {
|
||||||
let tokenizer = new Tokenizer()
|
let tokenizer = getTokenizer(e)
|
||||||
let topK = await tokenizer.getKeywordTopK(groupId, 100, duration)
|
let topK = await tokenizer.getKeywordTopK(e, groupId, 100, duration, userId)
|
||||||
let list = JSON.stringify(topK)
|
let list = JSON.stringify(topK)
|
||||||
// let list = topK
|
logger.info(list)
|
||||||
console.log(list)
|
let img = await render(e, 'chatgpt-plugin', 'wordcloud/index', { list }, { retType: 'base64' })
|
||||||
await render(e, 'chatgpt-plugin', 'wordcloud/index', { list })
|
await e.reply(img, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTokenizer (e) {
|
||||||
|
if (e.adapter === 'shamrock') {
|
||||||
|
return new ShamrockTokenizer()
|
||||||
|
} else {
|
||||||
|
return new Tokenizer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,12 +15,6 @@ try {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn('未安装form-data,无法使用星火模式')
|
logger.warn('未安装form-data,无法使用星火模式')
|
||||||
}
|
}
|
||||||
let crypto
|
|
||||||
try {
|
|
||||||
crypto = (await import('crypto')).default
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('未安装crypto,无法使用星火api模式')
|
|
||||||
}
|
|
||||||
async function getKeyv () {
|
async function getKeyv () {
|
||||||
let Keyv
|
let Keyv
|
||||||
try {
|
try {
|
||||||
|
|
@ -84,26 +79,27 @@ 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') {
|
||||||
|
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
|
||||||
|
|
@ -112,7 +108,7 @@ export default class XinghuoClient {
|
||||||
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()
|
||||||
|
|
@ -123,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
|
||||||
})
|
})
|
||||||
|
|
@ -178,8 +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'
|
||||||
|
if (Config.xhmode == 'apiv2') { domain = 'generalv2' } else if (Config.xhmode == 'apiv3') { domain = 'generalv3' }
|
||||||
// 编写消息内容
|
// 编写消息内容
|
||||||
const wsSendData = {
|
const wsSendData = {
|
||||||
header: {
|
header: {
|
||||||
|
|
@ -188,7 +185,7 @@ export default class XinghuoClient {
|
||||||
},
|
},
|
||||||
parameter: {
|
parameter: {
|
||||||
chat: {
|
chat: {
|
||||||
domain: Config.xhmode == 'api' ? "general" : "generalv2",
|
domain,
|
||||||
temperature: Config.xhTemperature, // 核采样阈值
|
temperature: Config.xhTemperature, // 核采样阈值
|
||||||
max_tokens: Config.xhMaxTokens, // tokens最大长度
|
max_tokens: Config.xhMaxTokens, // tokens最大长度
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
|
|
@ -197,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 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -275,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)
|
||||||
|
|
@ -378,7 +375,7 @@ export default class XinghuoClient {
|
||||||
let chatId = option?.chatId
|
let chatId = option?.chatId
|
||||||
let image = option?.image
|
let image = option?.image
|
||||||
|
|
||||||
if (Config.xhmode == 'api' || Config.xhmode == 'apiv2' || Config.xhmode == 'assistants') {
|
if (Config.xhmode == 'api' || Config.xhmode == 'apiv2' || Config.xhmode == 'apiv3' || Config.xhmode == 'assistants') {
|
||||||
if (!Config.xhAppId || !Config.xhAPISecret || !Config.xhAPIKey) throw new Error('未配置api')
|
if (!Config.xhAppId || !Config.xhAPISecret || !Config.xhAPIKey) throw new Error('未配置api')
|
||||||
let Prompt = []
|
let Prompt = []
|
||||||
// 设定
|
// 设定
|
||||||
|
|
@ -390,7 +387,7 @@ 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 => {
|
||||||
|
|
@ -421,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)
|
||||||
// 处理图片
|
// 处理图片
|
||||||
|
|
@ -445,7 +442,7 @@ 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('星火模式错误')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue