mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-18 06:17:06 +00:00
Merge branch 'v2' of https://github.com/ikechan8370/chatgpt-plugin into v2
This commit is contained in:
commit
5c52962737
26 changed files with 1325 additions and 2897 deletions
|
|
@ -1,5 +1,4 @@
|
||||||

|

|
||||||
<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"/>
|
||||||
|
|
@ -43,6 +42,9 @@
|
||||||
* 2023-09-10 支持来自claude.ai的claude-2模型
|
* 2023-09-10 支持来自claude.ai的claude-2模型
|
||||||
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
|
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
|
||||||
* 2023-10-25 增加支持通义千问官方API
|
* 2023-10-25 增加支持通义千问官方API
|
||||||
|
* 2023-12-01 持续优先适配Shamrock
|
||||||
|
* 2023-12-14 增加支持Gemini 官方API
|
||||||
|
|
||||||
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
||||||
|
|
||||||
## 版本要求
|
## 版本要求
|
||||||
|
|
|
||||||
642
apps/chat.js
642
apps/chat.js
|
|
@ -1,12 +1,11 @@
|
||||||
import plugin from '../../../lib/plugins/plugin.js'
|
import plugin from '../../../lib/plugins/plugin.js'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import {Config, defaultOpenAIAPI} from '../utils/config.js'
|
import { Config, defaultOpenAIAPI } from '../utils/config.js'
|
||||||
import {v4 as uuid} from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import delay from 'delay'
|
import delay from 'delay'
|
||||||
import {ChatGPTAPI} from '../utils/openai/chatgpt-api.js'
|
import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js'
|
||||||
import {BingAIClient} from '@waylaidwanderer/chatgpt-api'
|
|
||||||
import SydneyAIClient from '../utils/SydneyAIClient.js'
|
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 Version from '../utils/version.js'
|
||||||
|
|
@ -22,6 +21,7 @@ import {
|
||||||
getMasterQQ,
|
getMasterQQ,
|
||||||
getMaxModelTokens,
|
getMaxModelTokens,
|
||||||
getMessageById,
|
getMessageById,
|
||||||
|
getOrDownloadFile,
|
||||||
getUin,
|
getUin,
|
||||||
getUserData,
|
getUserData,
|
||||||
getUserReplySetting,
|
getUserReplySetting,
|
||||||
|
|
@ -33,51 +33,59 @@ import {
|
||||||
renderUrl,
|
renderUrl,
|
||||||
upsertMessage
|
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'
|
||||||
import {OfficialChatGPTClient} from '../utils/message.js'
|
import { OfficialChatGPTClient } from '../utils/message.js'
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import {deleteConversation, getConversations, getLatestMessageIdByConversationId} from '../utils/conversation.js'
|
import { deleteConversation, getConversations, getLatestMessageIdByConversationId } from '../utils/conversation.js'
|
||||||
import {convertSpeaker, speakers} from '../utils/tts.js'
|
import { convertSpeaker, speakers } from '../utils/tts.js'
|
||||||
import ChatGLMClient from '../utils/chatglm.js'
|
import ChatGLMClient from '../utils/chatglm.js'
|
||||||
import {convertFaces} from '../utils/face.js'
|
import { convertFaces } from '../utils/face.js'
|
||||||
import {SlackClaudeClient} from '../utils/slack/slackClient.js'
|
import { SlackClaudeClient } from '../utils/slack/slackClient.js'
|
||||||
import {getPromptByName} from '../utils/prompts.js'
|
import { getPromptByName } from '../utils/prompts.js'
|
||||||
import BingDrawClient from '../utils/BingDraw.js'
|
import BingDrawClient from '../utils/BingDraw.js'
|
||||||
import XinghuoClient from '../utils/xinghuo/xinghuo.js'
|
import XinghuoClient from '../utils/xinghuo/xinghuo.js'
|
||||||
import Bard from '../utils/bard.js'
|
import Bard from '../utils/bard.js'
|
||||||
import {JinyanTool} from '../utils/tools/JinyanTool.js'
|
import { JinyanTool } from '../utils/tools/JinyanTool.js'
|
||||||
import {SendVideoTool} from '../utils/tools/SendBilibiliTool.js'
|
import { SendVideoTool } from '../utils/tools/SendBilibiliTool.js'
|
||||||
import {KickOutTool} from '../utils/tools/KickOutTool.js'
|
import { KickOutTool } from '../utils/tools/KickOutTool.js'
|
||||||
import {EditCardTool} from '../utils/tools/EditCardTool.js'
|
import { EditCardTool } from '../utils/tools/EditCardTool.js'
|
||||||
import {SearchVideoTool} from '../utils/tools/SearchBilibiliTool.js'
|
import { SearchVideoTool } from '../utils/tools/SearchBilibiliTool.js'
|
||||||
import {SearchMusicTool} from '../utils/tools/SearchMusicTool.js'
|
import { SearchMusicTool } from '../utils/tools/SearchMusicTool.js'
|
||||||
import {QueryStarRailTool} from '../utils/tools/QueryStarRailTool.js'
|
import { QueryStarRailTool } from '../utils/tools/QueryStarRailTool.js'
|
||||||
import {WebsiteTool} from '../utils/tools/WebsiteTool.js'
|
import { WebsiteTool } from '../utils/tools/WebsiteTool.js'
|
||||||
import {WeatherTool} from '../utils/tools/WeatherTool.js'
|
import { WeatherTool } from '../utils/tools/WeatherTool.js'
|
||||||
import {SerpTool} from '../utils/tools/SerpTool.js'
|
import { SerpTool } from '../utils/tools/SerpTool.js'
|
||||||
import {SerpIkechan8370Tool} from '../utils/tools/SerpIkechan8370Tool.js'
|
import { SerpIkechan8370Tool } from '../utils/tools/SerpIkechan8370Tool.js'
|
||||||
import {SendPictureTool} from '../utils/tools/SendPictureTool.js'
|
import { SendPictureTool } from '../utils/tools/SendPictureTool.js'
|
||||||
import {SerpImageTool} from '../utils/tools/SearchImageTool.js'
|
import { SerpImageTool } from '../utils/tools/SearchImageTool.js'
|
||||||
import {ImageCaptionTool} from '../utils/tools/ImageCaptionTool.js'
|
import { ImageCaptionTool } from '../utils/tools/ImageCaptionTool.js'
|
||||||
import {SendAudioMessageTool} from '../utils/tools/SendAudioMessageTool.js'
|
import { SendAudioMessageTool } from '../utils/tools/SendAudioMessageTool.js'
|
||||||
import {ProcessPictureTool} from '../utils/tools/ProcessPictureTool.js'
|
import { ProcessPictureTool } from '../utils/tools/ProcessPictureTool.js'
|
||||||
import {APTool} from '../utils/tools/APTool.js'
|
import { APTool } from '../utils/tools/APTool.js'
|
||||||
import {QueryGenshinTool} from '../utils/tools/QueryGenshinTool.js'
|
import { QueryGenshinTool } from '../utils/tools/QueryGenshinTool.js'
|
||||||
import {HandleMessageMsgTool} from '../utils/tools/HandleMessageMsgTool.js'
|
import { HandleMessageMsgTool } from '../utils/tools/HandleMessageMsgTool.js'
|
||||||
import {QueryUserinfoTool} from '../utils/tools/QueryUserinfoTool.js'
|
import { QueryUserinfoTool } from '../utils/tools/QueryUserinfoTool.js'
|
||||||
import {EliMovieTool} from '../utils/tools/EliMovieTool.js'
|
import { EliMovieTool } from '../utils/tools/EliMovieTool.js'
|
||||||
import {EliMusicTool} from '../utils/tools/EliMusicTool.js'
|
import { EliMusicTool } from '../utils/tools/EliMusicTool.js'
|
||||||
import {SendMusicTool} from '../utils/tools/SendMusicTool.js'
|
import { SendMusicTool } from '../utils/tools/SendMusicTool.js'
|
||||||
import {SendDiceTool} from '../utils/tools/SendDiceTool.js'
|
import { SendDiceTool } from '../utils/tools/SendDiceTool.js'
|
||||||
import {SendAvatarTool} from '../utils/tools/SendAvatarTool.js'
|
import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
|
||||||
import {SendMessageToSpecificGroupOrUserTool} from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
|
import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
|
||||||
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 {getProxy} from '../utils/proxy.js'
|
import { getProxy } from '../utils/proxy.js'
|
||||||
import {QwenApi} from '../utils/alibaba/qwen-api.js'
|
import { QwenApi } from '../utils/alibaba/qwen-api.js'
|
||||||
import {getChatHistoryGroup} from '../utils/chat.js'
|
import { getChatHistoryGroup } from '../utils/chat.js'
|
||||||
|
import { CustomGoogleGeminiClient } from '../client/CustomGoogleGeminiClient.js'
|
||||||
|
import { resizeAndCropImage } from '../utils/dalle.js'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
const roleMap = {
|
||||||
|
owner: 'group owner',
|
||||||
|
admin: 'group administrator'
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await import('@azure/openai')
|
await import('@azure/openai')
|
||||||
|
|
@ -97,6 +105,9 @@ try {
|
||||||
}
|
}
|
||||||
let version = Config.version
|
let version = Config.version
|
||||||
let proxy = getProxy()
|
let proxy = getProxy()
|
||||||
|
|
||||||
|
const originalValues = ['星火', '通义千问', '克劳德', '克劳德2', '必应', 'api', 'API', 'api3', 'API3', 'glm', '巴德']
|
||||||
|
const correspondingValues = ['xh', 'qwen', 'claude', 'claude2', 'bing', 'api', 'api', 'api3', 'api3', 'chatglm', 'bard']
|
||||||
/**
|
/**
|
||||||
* 每个对话保留的时长。单个对话内ai是保留上下文的。超时后销毁对话,再次对话创建新的对话。
|
* 每个对话保留的时长。单个对话内ai是保留上下文的。超时后销毁对话,再次对话创建新的对话。
|
||||||
* 单位:秒
|
* 单位:秒
|
||||||
|
|
@ -191,6 +202,12 @@ export class chatgpt extends plugin {
|
||||||
/** 执行方法 */
|
/** 执行方法 */
|
||||||
fnc: 'qwen'
|
fnc: 'qwen'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/** 命令正则匹配 */
|
||||||
|
reg: '^#gemini[sS]*',
|
||||||
|
/** 执行方法 */
|
||||||
|
fnc: 'gemini'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/** 命令正则匹配 */
|
/** 命令正则匹配 */
|
||||||
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
||||||
|
|
@ -204,11 +221,11 @@ export class chatgpt extends plugin {
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)对话([sS]*)',
|
reg: '^#(chatgpt|星火|通义千问|克劳德|克劳德2|必应|api|API|api3|API3|glm|巴德)?(结束|新开|摧毁|毁灭|完结)对话([sS]*)',
|
||||||
fnc: 'destroyConversations'
|
fnc: 'destroyConversations'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)全部对话$',
|
reg: '^#(chatgpt|星火|通义千问|克劳德|克劳德2|必应|api|API|api3|API3|glm|巴德)?(结束|新开|摧毁|毁灭|完结)全部对话$',
|
||||||
fnc: 'endAllConversations',
|
fnc: 'endAllConversations',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
|
@ -299,7 +316,15 @@ export class chatgpt extends plugin {
|
||||||
*/
|
*/
|
||||||
async destroyConversations (e) {
|
async destroyConversations (e) {
|
||||||
const userData = await getUserData(e.user_id)
|
const userData = await getUserData(e.user_id)
|
||||||
const use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE')
|
const match = e.msg.trim().match('^#?(.*)(结束|新开|摧毁|毁灭|完结)对话')
|
||||||
|
console.log(match[1])
|
||||||
|
let use
|
||||||
|
if (match[1] && match[1] != 'chatgpt') {
|
||||||
|
use = correspondingValues[originalValues.indexOf(match[1])]
|
||||||
|
} else {
|
||||||
|
use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE')
|
||||||
|
}
|
||||||
|
console.log(use)
|
||||||
await redis.del(`CHATGPT:WRONG_EMOTION:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`)
|
await redis.del(`CHATGPT:WRONG_EMOTION:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`)
|
||||||
if (use === 'claude') {
|
if (use === 'claude') {
|
||||||
// let client = new SlackClaudeClient({
|
// let client = new SlackClaudeClient({
|
||||||
|
|
@ -386,6 +411,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
||||||
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'gemini') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_GEMINI:${e.sender.user_id}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply('当前没有开启对话', true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_GEMINI:${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) {
|
||||||
|
|
@ -455,6 +488,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
||||||
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'gemini') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_GEMINI:${qq}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply(`当前${atUser}没有开启对话`, true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_GEMINI:${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) {
|
||||||
|
|
@ -476,7 +517,15 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async endAllConversations (e) {
|
async endAllConversations (e) {
|
||||||
let use = await redis.get('CHATGPT:USE') || 'api'
|
const match = e.msg.trim().match('^#?(.*)(结束|新开|摧毁|毁灭|完结)全部对话')
|
||||||
|
console.log(match[1])
|
||||||
|
let use
|
||||||
|
if (match[1] && match[1] != 'chatgpt') {
|
||||||
|
use = correspondingValues[originalValues.indexOf(match[1])]
|
||||||
|
} else {
|
||||||
|
use = await redis.get('CHATGPT:USE') || 'api'
|
||||||
|
}
|
||||||
|
console.log(use)
|
||||||
let deleted = 0
|
let deleted = 0
|
||||||
switch (use) {
|
switch (use) {
|
||||||
case 'claude': {
|
case 'claude': {
|
||||||
|
|
@ -578,6 +627,18 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
let qcs = await redis.keys('CHATGPT:CONVERSATIONS_GEMINI:*')
|
||||||
|
for (let i = 0; i < qcs.length; i++) {
|
||||||
|
await redis.del(qcs[i])
|
||||||
|
// todo clean last message id
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info('delete gemini conversation bind: ' + qcs[i])
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
||||||
}
|
}
|
||||||
|
|
@ -797,7 +858,7 @@ export class chatgpt extends plugin {
|
||||||
* #chatgpt
|
* #chatgpt
|
||||||
*/
|
*/
|
||||||
async chatgpt (e) {
|
async chatgpt (e) {
|
||||||
let msg = Version.isTrss ? e.msg : e.raw_message
|
let msg = (Version.isTrss || e.adapter === 'shamrock') ? e.msg : e.raw_message
|
||||||
let prompt
|
let prompt
|
||||||
if (this.toggleMode === 'at') {
|
if (this.toggleMode === 'at') {
|
||||||
if (!msg || e.msg?.startsWith('#')) {
|
if (!msg || e.msg?.startsWith('#')) {
|
||||||
|
|
@ -1073,6 +1134,10 @@ export class chatgpt extends plugin {
|
||||||
key = `CHATGPT:CONVERSATIONS_QWEN:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
key = `CHATGPT:CONVERSATIONS_QWEN:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
key = `CHATGPT:CONVERSATIONS_GEMINI:${(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({
|
||||||
|
|
@ -1368,155 +1433,39 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatgpt1 (e) {
|
async chatgpt1 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'api', '#chat1')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat1')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chat1', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'api')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatgpt3 (e) {
|
async chatgpt3 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'api3', '#chat3')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat3')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chat3', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'api3')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatglm (e) {
|
async chatglm (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'chatglm')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chatglm')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chatglm', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'chatglm')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async bing (e) {
|
async bing (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'bing')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#bing')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#bing', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'bing')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async claude2 (e) {
|
async claude2 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'claude2')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude2')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#claude2', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'claude2')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async claude (e) {
|
async claude (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'claude')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#claude', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'claude')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async qwen (e) {
|
async qwen (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'gemini')
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
async gemini (e) {
|
||||||
if (Config.debug) {
|
return await this.otherMode(e, 'gemini')
|
||||||
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) {
|
return await this.otherMode(e, 'xh')
|
||||||
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(), '#xh', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'xh')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
|
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
|
||||||
|
|
@ -1576,19 +1525,22 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
const userData = await getUserData(e.user_id)
|
const userData = await getUserData(e.user_id)
|
||||||
const useCast = userData.cast || {}
|
const useCast = userData.cast || {}
|
||||||
switch (use) {
|
if (use === 'browser') {
|
||||||
case 'browser': {
|
{
|
||||||
return await this.chatgptBrowserBased(prompt, conversation)
|
return await this.chatgptBrowserBased(prompt, conversation)
|
||||||
}
|
}
|
||||||
case 'bing': {
|
} else if (use === 'bing') {
|
||||||
|
{
|
||||||
let throttledTokens = []
|
let throttledTokens = []
|
||||||
let { bingToken, allThrottled } = await getAvailableBingToken(conversation, throttledTokens)
|
let {
|
||||||
|
bingToken,
|
||||||
|
allThrottled
|
||||||
|
} = await getAvailableBingToken(conversation, throttledTokens)
|
||||||
let cookies
|
let cookies
|
||||||
if (bingToken?.indexOf('=') > -1) {
|
if (bingToken?.indexOf('=') > -1) {
|
||||||
cookies = bingToken
|
cookies = bingToken
|
||||||
}
|
}
|
||||||
let bingAIClient
|
let bingAIClient
|
||||||
if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') {
|
|
||||||
const cacheOptions = {
|
const cacheOptions = {
|
||||||
namespace: Config.toneStyle,
|
namespace: Config.toneStyle,
|
||||||
store: new KeyvFile({ filename: 'cache.json' })
|
store: new KeyvFile({ filename: 'cache.json' })
|
||||||
|
|
@ -1605,19 +1557,6 @@ export class chatgpt extends plugin {
|
||||||
delete conversation.clientId
|
delete conversation.clientId
|
||||||
delete conversation.invocationId
|
delete conversation.invocationId
|
||||||
delete conversation.conversationSignature
|
delete conversation.conversationSignature
|
||||||
} else {
|
|
||||||
let bingOption = {
|
|
||||||
userToken: bingToken, // "_U" cookie from bing.com
|
|
||||||
cookies,
|
|
||||||
debug: Config.debug,
|
|
||||||
proxy: Config.proxy,
|
|
||||||
host: Config.sydneyReverseProxy
|
|
||||||
}
|
|
||||||
if (Config.proxy && Config.sydneyReverseProxy && !Config.sydneyForceUseReverse) {
|
|
||||||
delete bingOption.host
|
|
||||||
}
|
|
||||||
bingAIClient = new BingAIClient(bingOption)
|
|
||||||
}
|
|
||||||
let response
|
let response
|
||||||
let reply = ''
|
let reply = ''
|
||||||
let retry = 3
|
let retry = 3
|
||||||
|
|
@ -1634,7 +1573,6 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
// 重新拿存储的token,因为可能之前有过期的被删了
|
// 重新拿存储的token,因为可能之前有过期的被删了
|
||||||
let abtrs = await getAvailableBingToken(conversation, throttledTokens)
|
let abtrs = await getAvailableBingToken(conversation, throttledTokens)
|
||||||
if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') {
|
|
||||||
bingToken = abtrs.bingToken
|
bingToken = abtrs.bingToken
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
allThrottled = abtrs.allThrottled
|
allThrottled = abtrs.allThrottled
|
||||||
|
|
@ -1669,9 +1607,13 @@ export class chatgpt extends plugin {
|
||||||
let toSummaryFileContent
|
let toSummaryFileContent
|
||||||
try {
|
try {
|
||||||
if (e.source) {
|
if (e.source) {
|
||||||
let msgs = e.isGroup ? await e.group.getChatHistory(e.source.seq, 1) : await e.friend.getChatHistory(e.source.time, 1)
|
let seq = e.isGroup ? e.source.seq : e.source.time
|
||||||
let sourceMsg = msgs[0]
|
if (e.adapter === 'shamrock') {
|
||||||
let fileMsgElem = sourceMsg.message.find(msg => msg.type === 'file')
|
seq = e.source.message_id
|
||||||
|
}
|
||||||
|
let msgs = e.isGroup ? await e.group.getChatHistory(seq, 1) : await e.friend.getChatHistory(seq, 1)
|
||||||
|
let sourceMsg = msgs[msgs.length - 1]
|
||||||
|
let fileMsgElem = sourceMsg.file || sourceMsg.message.find(msg => msg.type === 'file')
|
||||||
if (fileMsgElem) {
|
if (fileMsgElem) {
|
||||||
toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
|
toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
|
||||||
}
|
}
|
||||||
|
|
@ -1680,28 +1622,36 @@ export class chatgpt extends plugin {
|
||||||
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
||||||
}
|
}
|
||||||
opt.toSummaryFileContent = toSummaryFileContent
|
opt.toSummaryFileContent = toSummaryFileContent
|
||||||
} else {
|
|
||||||
// 重新创建client,因为token可能换到别的了
|
|
||||||
if (bingToken?.indexOf('=') > -1) {
|
|
||||||
cookies = bingToken
|
|
||||||
}
|
|
||||||
let bingOption = {
|
|
||||||
userToken: abtrs.bingToken, // "_U" cookie from bing.com
|
|
||||||
cookies,
|
|
||||||
debug: Config.debug,
|
|
||||||
proxy: Config.proxy,
|
|
||||||
host: Config.sydneyReverseProxy
|
|
||||||
}
|
|
||||||
if (Config.proxy && Config.sydneyReverseProxy && !Config.sydneyForceUseReverse) {
|
|
||||||
delete bingOption.host
|
|
||||||
}
|
|
||||||
bingAIClient = new BingAIClient(bingOption)
|
|
||||||
}
|
|
||||||
// 写入图片数据
|
// 写入图片数据
|
||||||
if (Config.sydneyImageRecognition) {
|
if (Config.sydneyImageRecognition) {
|
||||||
const image = await getImg(e)
|
const image = await getImg(e)
|
||||||
opt.imageUrl = image ? image[0] : undefined
|
opt.imageUrl = image ? image[0] : undefined
|
||||||
}
|
}
|
||||||
|
if (Config.enableGenerateContents) {
|
||||||
|
opt.onImageCreateRequest = prompt => {
|
||||||
|
logger.mark(`开始生成内容:${prompt}`)
|
||||||
|
if (Config.bingAPDraw) {
|
||||||
|
// 调用第三方API进行绘图
|
||||||
|
let apDraw = new APTool()
|
||||||
|
apDraw.func({
|
||||||
|
prompt
|
||||||
|
}, e)
|
||||||
|
} else {
|
||||||
|
let client = new BingDrawClient({
|
||||||
|
baseUrl: Config.sydneyReverseProxy,
|
||||||
|
userToken: bingToken
|
||||||
|
})
|
||||||
|
redis.set(`CHATGPT:DRAW:${e.sender.user_id}`, 'c', { EX: 30 }).then(() => {
|
||||||
|
try {
|
||||||
|
client.getImages(prompt, e)
|
||||||
|
} catch (err) {
|
||||||
|
redis.del(`CHATGPT:DRAW:${e.sender.user_id}`)
|
||||||
|
e.reply('绘图失败:' + err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
response = await bingAIClient.sendMessage(prompt, opt, (token) => {
|
response = await bingAIClient.sendMessage(prompt, opt, (token) => {
|
||||||
reply += token
|
reply += token
|
||||||
})
|
})
|
||||||
|
|
@ -1727,32 +1677,6 @@ export class chatgpt extends plugin {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 处理内容生成的图片
|
|
||||||
if (response.details.imageTag) {
|
|
||||||
if (Config.debug) {
|
|
||||||
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({
|
|
||||||
baseUrl: Config.sydneyReverseProxy,
|
|
||||||
userToken: bingToken
|
|
||||||
})
|
|
||||||
await redis.set(`CHATGPT:DRAW:${e.sender.user_id}`, 'c', { EX: 30 })
|
|
||||||
try {
|
|
||||||
await client.getImages(response.details.imageTag, e)
|
|
||||||
} catch (err) {
|
|
||||||
await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`)
|
|
||||||
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')) || '[]')
|
||||||
const TokenIndex = Tokens?.findIndex(element => element.Token === abtrs.bingToken)
|
const TokenIndex = Tokens?.findIndex(element => element.Token === abtrs.bingToken)
|
||||||
|
|
@ -1768,7 +1692,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 && Config.bingCaptchaOneShotUrl) {
|
||||||
// maxConv为30说明token有效,可以通过解验证码码服务过码
|
// maxConv为30说明token有效,可以通过解验证码码服务过码
|
||||||
await e.reply('出现必应验证码,尝试解决中')
|
await e.reply('出现必应验证码,尝试解决中')
|
||||||
try {
|
try {
|
||||||
|
|
@ -1777,6 +1701,7 @@ export class chatgpt extends plugin {
|
||||||
await e.reply('验证码已解决')
|
await e.reply('验证码已解决')
|
||||||
} else {
|
} else {
|
||||||
logger.error(captchaResolveResult)
|
logger.error(captchaResolveResult)
|
||||||
|
errorMessage = message
|
||||||
await e.reply('验证码解决失败: ' + captchaResolveResult.error)
|
await e.reply('验证码解决失败: ' + captchaResolveResult.error)
|
||||||
retry = 0
|
retry = 0
|
||||||
}
|
}
|
||||||
|
|
@ -1787,14 +1712,14 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 未登录用户maxConv目前为5或10,出验证码没救
|
// 未登录用户maxConv目前为5或10,出验证码没救
|
||||||
logger.warn(`token [${bingToken}] 无效或已过期,如确认token无误,请前往网页版必应对话一次`)
|
logger.warn(`token [${bingToken}] 出现必应验证码,请前往网页版或app手动解决`)
|
||||||
|
errorMessage = message
|
||||||
retry = 0
|
retry = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
retry = 0
|
retry = 0
|
||||||
}
|
}
|
||||||
} else
|
} else if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
|
||||||
if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
|
|
||||||
throttledTokens.push(bingToken)
|
throttledTokens.push(bingToken)
|
||||||
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
|
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
|
||||||
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
|
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
|
||||||
|
|
@ -1838,9 +1763,9 @@ export class chatgpt extends plugin {
|
||||||
response = response || {}
|
response = response || {}
|
||||||
if (errorMessage.includes('CaptchaChallenge')) {
|
if (errorMessage.includes('CaptchaChallenge')) {
|
||||||
if (bingToken) {
|
if (bingToken) {
|
||||||
errorMessage = '出现验证码,请使用当前账户前往https://www.bing.com/chat或Edge侧边栏手动解除验证码'
|
errorMessage = '出现验证码,请使用当前账户前往https://www.bing.com/chat或Edge侧边栏或移动端APP手动解除验证码'
|
||||||
} else {
|
} else {
|
||||||
errorMessage = '出现验证码,且未配置必应账户,请尝试更换代理/反代或绑定必应账户以解除验证码'
|
errorMessage = '未配置必应账户,请绑定必应账户再使用必应模式'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
@ -1866,7 +1791,8 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'api3': {
|
} else if (use === 'api3') {
|
||||||
|
{
|
||||||
// official without cloudflare
|
// official without cloudflare
|
||||||
let accessToken = await redis.get('CHATGPT:TOKEN')
|
let accessToken = await redis.get('CHATGPT:TOKEN')
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
|
|
@ -1890,7 +1816,8 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
return sendMessageResult
|
return sendMessageResult
|
||||||
}
|
}
|
||||||
case 'chatglm': {
|
} else if (use === 'chatglm') {
|
||||||
|
{
|
||||||
const cacheOptions = {
|
const cacheOptions = {
|
||||||
namespace: 'chatglm_6b',
|
namespace: 'chatglm_6b',
|
||||||
store: new KeyvFile({ filename: 'cache.json' })
|
store: new KeyvFile({ filename: 'cache.json' })
|
||||||
|
|
@ -1902,7 +1829,8 @@ export class chatgpt extends plugin {
|
||||||
let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation)
|
let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation)
|
||||||
return sendMessageResult
|
return sendMessageResult
|
||||||
}
|
}
|
||||||
case 'poe': {
|
} else if (use === 'poe') {
|
||||||
|
{
|
||||||
const cookie = await redis.get('CHATGPT:POE_TOKEN')
|
const cookie = await redis.get('CHATGPT:POE_TOKEN')
|
||||||
if (!cookie) {
|
if (!cookie) {
|
||||||
throw new Error('未绑定Poe Cookie,请使用#chatgpt设置Poe token命令绑定cookie')
|
throw new Error('未绑定Poe Cookie,请使用#chatgpt设置Poe token命令绑定cookie')
|
||||||
|
|
@ -1920,7 +1848,8 @@ export class chatgpt extends plugin {
|
||||||
text: response.data
|
text: response.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'claude': {
|
} else if (use === 'claude') {
|
||||||
|
{
|
||||||
let client = new SlackClaudeClient({
|
let client = new SlackClaudeClient({
|
||||||
slackUserToken: Config.slackUserToken,
|
slackUserToken: Config.slackUserToken,
|
||||||
slackChannelId: Config.slackChannelId
|
slackChannelId: Config.slackChannelId
|
||||||
|
|
@ -1944,7 +1873,8 @@ export class chatgpt extends plugin {
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'claude2': {
|
} else if (use === 'claude2') {
|
||||||
|
{
|
||||||
let { conversationId } = conversation
|
let { conversationId } = conversation
|
||||||
let client = new ClaudeAIClient({
|
let client = new ClaudeAIClient({
|
||||||
organizationId: Config.claudeAIOrganizationId,
|
organizationId: Config.claudeAIOrganizationId,
|
||||||
|
|
@ -1984,7 +1914,8 @@ export class chatgpt extends plugin {
|
||||||
return await client.sendMessage(prompt, conv.uuid, attachments)
|
return await client.sendMessage(prompt, conv.uuid, attachments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'xh': {
|
} else if (use === 'xh') {
|
||||||
|
{
|
||||||
const cacheOptions = {
|
const cacheOptions = {
|
||||||
namespace: 'xh',
|
namespace: 'xh',
|
||||||
store: new KeyvFile({ filename: 'cache.json' })
|
store: new KeyvFile({ filename: 'cache.json' })
|
||||||
|
|
@ -2007,7 +1938,8 @@ export class chatgpt extends plugin {
|
||||||
})
|
})
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
case 'azure': {
|
} else if (use === 'azure') {
|
||||||
|
{
|
||||||
let azureModel
|
let azureModel
|
||||||
try {
|
try {
|
||||||
azureModel = await import('@azure/openai')
|
azureModel = await import('@azure/openai')
|
||||||
|
|
@ -2017,15 +1949,22 @@ export class chatgpt extends plugin {
|
||||||
let OpenAIClient = azureModel.OpenAIClient
|
let OpenAIClient = azureModel.OpenAIClient
|
||||||
let AzureKeyCredential = azureModel.AzureKeyCredential
|
let AzureKeyCredential = azureModel.AzureKeyCredential
|
||||||
let msg = conversation.messages
|
let msg = conversation.messages
|
||||||
let content = { role: 'user', content: prompt }
|
let content = {
|
||||||
|
role: 'user',
|
||||||
|
content: prompt
|
||||||
|
}
|
||||||
msg.push(content)
|
msg.push(content)
|
||||||
const client = new OpenAIClient(Config.azureUrl, new AzureKeyCredential(Config.azApiKey))
|
const client = new OpenAIClient(Config.azureUrl, new AzureKeyCredential(Config.azApiKey))
|
||||||
const deploymentName = Config.azureDeploymentName
|
const deploymentName = Config.azureDeploymentName
|
||||||
const { choices } = await client.getChatCompletions(deploymentName, msg)
|
const { choices } = await client.getChatCompletions(deploymentName, msg)
|
||||||
let completion = choices[0].message
|
let completion = choices[0].message
|
||||||
return { text: completion.content, message: completion }
|
return {
|
||||||
|
text: completion.content,
|
||||||
|
message: completion
|
||||||
}
|
}
|
||||||
case 'qwen': {
|
}
|
||||||
|
} else if (use === 'qwen') {
|
||||||
|
{
|
||||||
let completionParams = {
|
let completionParams = {
|
||||||
parameters: {
|
parameters: {
|
||||||
top_p: Config.qwenTopP || 0.5,
|
top_p: Config.qwenTopP || 0.5,
|
||||||
|
|
@ -2039,12 +1978,15 @@ export class chatgpt extends plugin {
|
||||||
completionParams.model = Config.qwenModel
|
completionParams.model = Config.qwenModel
|
||||||
}
|
}
|
||||||
const currentDate = new Date().toISOString().split('T')[0]
|
const currentDate = new Date().toISOString().split('T')[0]
|
||||||
async function um (message) {
|
|
||||||
|
async function um(message) {
|
||||||
return await upsertMessage(message, 'QWEN')
|
return await upsertMessage(message, 'QWEN')
|
||||||
}
|
}
|
||||||
async function gm (id) {
|
|
||||||
|
async function gm(id) {
|
||||||
return await getMessageById(id, 'QWEN')
|
return await getMessageById(id, 'QWEN')
|
||||||
}
|
}
|
||||||
|
|
||||||
let opts = {
|
let opts = {
|
||||||
apiKey: Config.qwenApiKey,
|
apiKey: Config.qwenApiKey,
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
@ -2076,7 +2018,8 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
case 'bard': {
|
} else if (use === 'bard') {
|
||||||
|
{
|
||||||
// 处理cookie
|
// 处理cookie
|
||||||
const matchesPSID = /__Secure-1PSID=([^;]+)/.exec(Config.bardPsid)
|
const matchesPSID = /__Secure-1PSID=([^;]+)/.exec(Config.bardPsid)
|
||||||
const matchesPSIDTS = /__Secure-1PSIDTS=([^;]+)/.exec(Config.bardPsid)
|
const matchesPSIDTS = /__Secure-1PSIDTS=([^;]+)/.exec(Config.bardPsid)
|
||||||
|
|
@ -2126,7 +2069,126 @@ export class chatgpt extends plugin {
|
||||||
images: response.images
|
images: response.images
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (use === 'gemini') {
|
||||||
|
{
|
||||||
|
let client = new CustomGoogleGeminiClient({
|
||||||
|
e,
|
||||||
|
userId: e.sender.user_id,
|
||||||
|
key: Config.geminiKey,
|
||||||
|
model: Config.geminiModel,
|
||||||
|
baseUrl: Config.geminiBaseUrl,
|
||||||
|
debug: Config.debug
|
||||||
|
})
|
||||||
|
let option = {
|
||||||
|
stream: false,
|
||||||
|
onProgress: (data) => {
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentMessageId: conversation.parentMessageId,
|
||||||
|
conversationId: conversation.conversationId
|
||||||
|
}
|
||||||
|
if (Config.geminiModel.includes('vision')) {
|
||||||
|
const image = await getImg(e)
|
||||||
|
let imageUrl = image ? image[0] : undefined
|
||||||
|
if (imageUrl) {
|
||||||
|
let md5 = imageUrl.split(/[/-]/).find(s => s.length === 32)?.toUpperCase()
|
||||||
|
let imageLoc = await getOrDownloadFile(`ocr/${md5}.png`, imageUrl)
|
||||||
|
let outputLoc = imageLoc.replace(`${md5}.png`, `${md5}_512.png`)
|
||||||
|
await resizeAndCropImage(imageLoc, outputLoc, 512)
|
||||||
|
let buffer = fs.readFileSync(outputLoc)
|
||||||
|
option.image = buffer.toString('base64')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Config.smartMode) {
|
||||||
|
/**
|
||||||
|
* @type {AbstractTool[]}
|
||||||
|
*/
|
||||||
|
let tools = [
|
||||||
|
new QueryStarRailTool(),
|
||||||
|
new WebsiteTool(),
|
||||||
|
new SendPictureTool(),
|
||||||
|
new SendVideoTool(),
|
||||||
|
// new ImageCaptionTool(),
|
||||||
|
new SearchVideoTool(),
|
||||||
|
new SendAvatarTool(),
|
||||||
|
new SerpImageTool(),
|
||||||
|
new SearchMusicTool(),
|
||||||
|
new SendMusicTool(),
|
||||||
|
// new SerpIkechan8370Tool(),
|
||||||
|
// new SerpTool(),
|
||||||
|
new SendAudioMessageTool(),
|
||||||
|
// new ProcessPictureTool(),
|
||||||
|
new APTool(),
|
||||||
|
// new HandleMessageMsgTool(),
|
||||||
|
new SendMessageToSpecificGroupOrUserTool(),
|
||||||
|
// new SendDiceTool(),
|
||||||
|
new QueryGenshinTool()
|
||||||
|
]
|
||||||
|
if (Config.amapKey) {
|
||||||
|
tools.push(new WeatherTool())
|
||||||
|
}
|
||||||
|
if (e.isGroup) {
|
||||||
|
tools.push(new QueryUserinfoTool())
|
||||||
|
// let self = e.group.pickMember(e.self_id)
|
||||||
|
if (e.group.is_admin || e.group.is_owner) {
|
||||||
|
tools.push(new EditCardTool())
|
||||||
|
tools.push(new JinyanTool())
|
||||||
|
tools.push(new KickOutTool())
|
||||||
|
}
|
||||||
|
if (e.group.is_owner) {
|
||||||
|
tools.push(new SetTitleTool())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (Config.serpSource) {
|
||||||
|
case 'ikechan8370': {
|
||||||
|
tools.push(new SerpIkechan8370Tool())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'azure': {
|
||||||
|
if (!Config.azSerpKey) {
|
||||||
|
logger.warn('未配置bing搜索密钥,转为使用ikechan8370搜索源')
|
||||||
|
tools.push(new SerpIkechan8370Tool())
|
||||||
|
} else {
|
||||||
|
tools.push(new SerpTool())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
|
tools.push(new SerpIkechan8370Tool())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.addTools(tools)
|
||||||
|
}
|
||||||
|
let system = Config.geminiPrompt
|
||||||
|
if (Config.enableGroupContext && e.isGroup) {
|
||||||
|
let chats = await getChatHistoryGroup(e, Config.groupContextLength)
|
||||||
|
const namePlaceholder = '[name]'
|
||||||
|
const defaultBotName = 'GeminiPro'
|
||||||
|
const groupContextTip = Config.groupContextTip
|
||||||
|
let botName = e.isGroup ? (e.group.pickMember(getUin(e)).card || e.group.pickMember(getUin(e)).nickname) : e.bot.nickname
|
||||||
|
system = system.replaceAll(namePlaceholder, botName || defaultBotName) +
|
||||||
|
((Config.enableGroupContext && e.group_id) ? groupContextTip : '')
|
||||||
|
system += 'Attention, you are currently chatting in a qq group, then one who asks you now is' + `${e.sender.card || e.sender.nickname}(${e.sender.user_id}).`
|
||||||
|
system += `the group name is ${e.group.name || e.group_name}, group id is ${e.group_id}.`
|
||||||
|
system += `Your nickname is ${botName} in the group,`
|
||||||
|
if (chats) {
|
||||||
|
system += 'There is the conversation history in the group, you must chat according to the conversation history context"'
|
||||||
|
system += chats
|
||||||
|
.map(chat => {
|
||||||
|
let sender = chat.sender || {}
|
||||||
|
return `【${sender.card || sender.nickname}】(qq:${sender.user_id}, ${roleMap[sender.role] || 'normal user'},${sender.area ? 'from ' + sender.area + ', ' : ''} ${sender.age} years old, 群头衔:${sender.title}, gender: ${sender.sex}, time:${formatDate(new Date(chat.time * 1000))}, messageId: ${chat.message_id}) 说:${chat.raw_message}`
|
||||||
|
})
|
||||||
|
.join('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
option.system = system
|
||||||
|
return await client.sendMessage(prompt, option)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{
|
||||||
|
// openai api
|
||||||
let completionParams = {}
|
let completionParams = {}
|
||||||
if (Config.model) {
|
if (Config.model) {
|
||||||
completionParams.model = Config.model
|
completionParams.model = Config.model
|
||||||
|
|
@ -2163,11 +2225,6 @@ export class chatgpt extends plugin {
|
||||||
if (opt.botName) {
|
if (opt.botName) {
|
||||||
system += `Your nickname is ${opt.botName} in the group,`
|
system += `Your nickname is ${opt.botName} in the group,`
|
||||||
}
|
}
|
||||||
// system += master ? `我的qq号是${master},其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要。` : ''
|
|
||||||
const roleMap = {
|
|
||||||
owner: 'group owner',
|
|
||||||
admin: 'group administrator'
|
|
||||||
}
|
|
||||||
if (chats) {
|
if (chats) {
|
||||||
system += 'There is the conversation history in the group, you must chat according to the conversation history context"'
|
system += 'There is the conversation history in the group, you must chat according to the conversation history context"'
|
||||||
system += chats
|
system += chats
|
||||||
|
|
@ -2317,28 +2374,8 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let img = []
|
let img = await getImg(e)
|
||||||
if (e.source) {
|
if (img?.length > 0 && Config.extraUrl) {
|
||||||
// 优先从回复找图
|
|
||||||
let reply
|
|
||||||
if (e.isGroup) {
|
|
||||||
reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message
|
|
||||||
} else {
|
|
||||||
reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message
|
|
||||||
}
|
|
||||||
if (reply) {
|
|
||||||
for (let val of reply) {
|
|
||||||
if (val.type === 'image') {
|
|
||||||
console.log(val)
|
|
||||||
img.push(val.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.img) {
|
|
||||||
img.push(...e.img)
|
|
||||||
}
|
|
||||||
if (img.length > 0 && Config.extraUrl) {
|
|
||||||
tools.push(new ImageCaptionTool())
|
tools.push(new ImageCaptionTool())
|
||||||
tools.push(new ProcessPictureTool())
|
tools.push(new ProcessPictureTool())
|
||||||
prompt += `\nthe url of the picture(s) above: ${img.join(', ')}`
|
prompt += `\nthe url of the picture(s) above: ${img.join(', ')}`
|
||||||
|
|
@ -2373,7 +2410,10 @@ export class chatgpt extends plugin {
|
||||||
if (msg.text) {
|
if (msg.text) {
|
||||||
await e.reply(msg.text.replace('\n\n\n', '\n'))
|
await e.reply(msg.text.replace('\n\n\n', '\n'))
|
||||||
}
|
}
|
||||||
let { name, arguments: args } = msg.functionCall
|
let {
|
||||||
|
name,
|
||||||
|
arguments: args
|
||||||
|
} = msg.functionCall
|
||||||
args = JSON.parse(args)
|
args = JSON.parse(args)
|
||||||
// 感觉换成targetGroupIdOrUserQQNumber这种表意比较清楚的变量名,效果会好一丢丢
|
// 感觉换成targetGroupIdOrUserQQNumber这种表意比较清楚的变量名,效果会好一丢丢
|
||||||
if (!args.groupId) {
|
if (!args.groupId) {
|
||||||
|
|
@ -2384,7 +2424,10 @@ export class chatgpt extends plugin {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
args.groupId = e.group_id + '' || e.sender.user_id + ''
|
args.groupId = e.group_id + '' || e.sender.user_id + ''
|
||||||
}
|
}
|
||||||
let functionResult = await fullFuncMap[name.trim()].exec(Object.assign({ isAdmin, sender }, args), e)
|
let functionResult = await fullFuncMap[name.trim()].exec(Object.assign({
|
||||||
|
isAdmin,
|
||||||
|
sender
|
||||||
|
}, args), e)
|
||||||
logger.mark(`function ${name} execution result: ${functionResult}`)
|
logger.mark(`function ${name} execution result: ${functionResult}`)
|
||||||
option.parentMessageId = msg.id
|
option.parentMessageId = msg.id
|
||||||
option.name = name
|
option.name = name
|
||||||
|
|
@ -2712,6 +2755,25 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
return await this.chatGPTApi.sendMessage(prompt, sendMessageOption)
|
return await this.chatGPTApi.sendMessage(prompt, sendMessageOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async otherMode (e, mode, pattern = `#${mode}`) {
|
||||||
|
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('艾特别人了,没艾特我,忽略' + pattern)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let prompt = _.replace(e.raw_message.trimStart(), pattern, '').trim()
|
||||||
|
if (prompt.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await this.abstractChat(e, prompt, mode)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAvailableBingToken (conversation, throttled = []) {
|
async function getAvailableBingToken (conversation, throttled = []) {
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ export class dalle extends plugin {
|
||||||
await client.getImages(prompt, e)
|
await client.getImages(prompt, e)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`)
|
await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`)
|
||||||
await e.reply('绘图失败:' + err)
|
await e.reply('❌绘图失败:' + err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import plugin from '../../../lib/plugins/plugin.js'
|
import plugin from '../../../lib/plugins/plugin.js'
|
||||||
|
import { exec } from 'child_process'
|
||||||
import { Config } from '../utils/config.js'
|
import { Config } from '../utils/config.js'
|
||||||
import {
|
import {
|
||||||
formatDuration,
|
formatDuration,
|
||||||
|
|
@ -126,6 +127,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'useClaudeAISolution',
|
fnc: 'useClaudeAISolution',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt切换(Gemini|gemini)$',
|
||||||
|
fnc: 'useGeminiSolution',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt切换星火$',
|
reg: '^#chatgpt切换星火$',
|
||||||
fnc: 'useXinghuoBasedSolution',
|
fnc: 'useXinghuoBasedSolution',
|
||||||
|
|
@ -184,6 +190,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'setAPIKey',
|
fnc: 'setAPIKey',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置(Gemini|gemini)(Key|key)$',
|
||||||
|
fnc: 'setGeminiKey',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt设置(API|api)设定$',
|
reg: '^#chatgpt设置(API|api)设定$',
|
||||||
fnc: 'setAPIPromptPrefix',
|
fnc: 'setAPIPromptPrefix',
|
||||||
|
|
@ -314,6 +325,11 @@ export class ChatgptManagement extends plugin {
|
||||||
reg: '^#chatgpt设置星火模型$',
|
reg: '^#chatgpt设置星火模型$',
|
||||||
fnc: 'setXinghuoModel',
|
fnc: 'setXinghuoModel',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt修补Gemini$',
|
||||||
|
fnc: 'patchGemini',
|
||||||
|
permission: 'master'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
@ -902,6 +918,16 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async useGeminiSolution () {
|
||||||
|
let use = await redis.get('CHATGPT:USE')
|
||||||
|
if (use !== 'gemini') {
|
||||||
|
await redis.set('CHATGPT:USE', 'gemini')
|
||||||
|
await this.reply('已切换到基于Google Gemini的解决方案')
|
||||||
|
} else {
|
||||||
|
await this.reply('当前已经是gemini模式了')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async useXinghuoBasedSolution () {
|
async useXinghuoBasedSolution () {
|
||||||
let use = await redis.get('CHATGPT:USE')
|
let use = await redis.get('CHATGPT:USE')
|
||||||
if (use !== 'xh') {
|
if (use !== 'xh') {
|
||||||
|
|
@ -932,6 +958,57 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async patchGemini () {
|
||||||
|
const _path = process.cwd()
|
||||||
|
let packageJson = fs.readFileSync(`${_path}/package.json`)
|
||||||
|
packageJson = JSON.parse(String(packageJson))
|
||||||
|
const packageName = '@google/generative-ai@0.1.1'
|
||||||
|
const patchLoc = 'plugins/chatgpt-plugin/patches/@google__generative-ai@0.1.1.patch'
|
||||||
|
if (!packageJson.pnpm) {
|
||||||
|
packageJson.pnpm = {
|
||||||
|
patchedDependencies: {
|
||||||
|
[packageName]: patchLoc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (packageJson.pnpm.patchedDependencies) {
|
||||||
|
packageJson.pnpm.patchedDependencies[packageName] = patchLoc
|
||||||
|
} else {
|
||||||
|
packageJson.pnpm.patchedDependencies = {
|
||||||
|
[packageName]: patchLoc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs.writeFileSync(`${_path}/package.json`, JSON.stringify(packageJson, null, 2))
|
||||||
|
|
||||||
|
function execSync (cmd) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(cmd, (error, stdout, stderr) => {
|
||||||
|
resolve({ error, stdout, stderr })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
async function checkPnpm () {
|
||||||
|
let npm = 'npm'
|
||||||
|
let ret = await execSync('pnpm -v')
|
||||||
|
if (ret.stdout) npm = 'pnpm'
|
||||||
|
return npm
|
||||||
|
}
|
||||||
|
let npmv = await checkPnpm()
|
||||||
|
if (npmv === 'pnpm') {
|
||||||
|
exec('pnpm i', {}, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
logger.error(error)
|
||||||
|
logger.error(stderr)
|
||||||
|
logger.info(stdout)
|
||||||
|
this.e.reply('失败,请查看日志手动操作')
|
||||||
|
} else {
|
||||||
|
this.e.reply('修补完成,请手动重启')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async useQwenSolution () {
|
async useQwenSolution () {
|
||||||
let use = await redis.get('CHATGPT:USE')
|
let use = await redis.get('CHATGPT:USE')
|
||||||
if (use !== 'qwen') {
|
if (use !== 'qwen') {
|
||||||
|
|
@ -1148,6 +1225,21 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
this.finish('saveAPIKey')
|
this.finish('saveAPIKey')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setGeminiKey (e) {
|
||||||
|
this.setContext('saveGeminiKey')
|
||||||
|
await this.reply('请发送Gemini API Key.获取地址:https://makersuite.google.com/app/apikey', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveGeminiKey () {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
// todo
|
||||||
|
Config.geminiKey = token
|
||||||
|
await this.reply('请发送Gemini API Key设置成功', true)
|
||||||
|
this.finish('saveGeminiKey')
|
||||||
|
}
|
||||||
|
|
||||||
async setXinghuoToken () {
|
async setXinghuoToken () {
|
||||||
this.setContext('saveXinghuoToken')
|
this.setContext('saveXinghuoToken')
|
||||||
await this.reply('请发送星火的ssoSessionId', true)
|
await this.reply('请发送星火的ssoSessionId', true)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,8 @@ export class help extends plugin {
|
||||||
api: 'promptPrefixOverride',
|
api: 'promptPrefixOverride',
|
||||||
Custom: 'sydney',
|
Custom: 'sydney',
|
||||||
claude: 'slackClaudeGlobalPreset',
|
claude: 'slackClaudeGlobalPreset',
|
||||||
qwen: 'promptPrefixOverride'
|
qwen: 'promptPrefixOverride',
|
||||||
|
gemini: 'geminiPrompt'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyMap[use]) {
|
if (keyMap[use]) {
|
||||||
|
|
@ -171,7 +172,7 @@ export class help extends plugin {
|
||||||
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
||||||
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
||||||
} else {
|
} else {
|
||||||
await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude`, true)
|
await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude、通义千问和Gemini`, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,18 @@ export class BaseClient {
|
||||||
constructor (props = {}) {
|
constructor (props = {}) {
|
||||||
this.supportFunction = false
|
this.supportFunction = false
|
||||||
this.maxToken = 4096
|
this.maxToken = 4096
|
||||||
|
/**
|
||||||
|
* @type {Array<AbstractTool>}
|
||||||
|
*/
|
||||||
this.tools = []
|
this.tools = []
|
||||||
const {
|
const {
|
||||||
e, getMessageById, upsertMessage
|
e, getMessageById, upsertMessage, deleteMessageById, userId
|
||||||
} = props
|
} = props
|
||||||
this.e = e
|
this.e = e
|
||||||
this.getMessageById = getMessageById
|
this.getMessageById = getMessageById
|
||||||
this.upsertMessage = upsertMessage
|
this.upsertMessage = upsertMessage
|
||||||
|
this.deleteMessageById = deleteMessageById || (() => {})
|
||||||
|
this.userId = userId
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,20 +41,28 @@ export class BaseClient {
|
||||||
* insert or update a message with the id
|
* insert or update a message with the id
|
||||||
*
|
*
|
||||||
* @type function
|
* @type function
|
||||||
* @param {string} id
|
|
||||||
* @param {object} message
|
* @param {object} message
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
upsertMessage
|
upsertMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete a message with the id
|
||||||
|
*
|
||||||
|
* @type function
|
||||||
|
* @param {string} id
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
deleteMessageById
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send prompt message with history and return response message \
|
* Send prompt message with history and return response message \
|
||||||
* if function called, handled internally \
|
* if function called, handled internally \
|
||||||
* override this method to implement logic of sending and receiving message
|
* override this method to implement logic of sending and receiving message
|
||||||
*
|
*
|
||||||
* @param msg
|
* @param {string} msg
|
||||||
* @param opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
|
* @param {{conversationId: string?, parentMessageId: string?, stream: boolean?, onProgress: function?}} opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
|
||||||
* @returns {Promise<Message>} required fields: [text, conversationId, parentMessageId, id]
|
* @returns {Promise<{text, conversationId, parentMessageId, id}>} required fields: [text, conversationId, parentMessageId, id]
|
||||||
*/
|
*/
|
||||||
async sendMessage (msg, opt = {}) {
|
async sendMessage (msg, opt = {}) {
|
||||||
throw new Error('not implemented in abstract client')
|
throw new Error('not implemented in abstract client')
|
||||||
|
|
@ -60,11 +73,12 @@ export class BaseClient {
|
||||||
* override this method to implement logic of getting history
|
* override this method to implement logic of getting history
|
||||||
* keyv with local file or redis recommended
|
* keyv with local file or redis recommended
|
||||||
*
|
*
|
||||||
* @param userId such as qq number
|
* @param userId optional, such as qq number
|
||||||
* @param opt other options
|
* @param parentMessageId if blank, no history
|
||||||
* @returns {Promise<void>}
|
* @param opt optional, other options
|
||||||
|
* @returns {Promise<object[]>}
|
||||||
*/
|
*/
|
||||||
async getHistory (userId, opt = {}) {
|
async getHistory (parentMessageId, userId = this.userId, opt = {}) {
|
||||||
throw new Error('not implemented in abstract client')
|
throw new Error('not implemented in abstract client')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,14 +92,18 @@ export class BaseClient {
|
||||||
throw new Error('not implemented in abstract client')
|
throw new Error('not implemented in abstract client')
|
||||||
}
|
}
|
||||||
|
|
||||||
addTools (...tools) {
|
/**
|
||||||
|
* 增加tools
|
||||||
|
* @param {[AbstractTool]} tools
|
||||||
|
*/
|
||||||
|
addTools (tools) {
|
||||||
if (!this.isSupportFunction) {
|
if (!this.isSupportFunction) {
|
||||||
throw new Error('function not supported')
|
throw new Error('function not supported')
|
||||||
}
|
}
|
||||||
if (!this.tools) {
|
if (!this.tools) {
|
||||||
this.tools = []
|
this.tools = []
|
||||||
}
|
}
|
||||||
this.tools.push(tools)
|
this.tools.push(...tools)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTools () {
|
getTools () {
|
||||||
|
|
|
||||||
264
client/CustomGoogleGeminiClient.js
Normal file
264
client/CustomGoogleGeminiClient.js
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
import crypto from 'crypto'
|
||||||
|
import { GoogleGeminiClient } from './GoogleGeminiClient.js'
|
||||||
|
import { newFetch } from '../utils/proxy.js'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
const BASEURL = 'https://generativelanguage.googleapis.com'
|
||||||
|
|
||||||
|
export const HarmCategory = {
|
||||||
|
HARM_CATEGORY_UNSPECIFIED: 'HARM_CATEGORY_UNSPECIFIED',
|
||||||
|
HARM_CATEGORY_HATE_SPEECH: 'HARM_CATEGORY_HATE_SPEECH',
|
||||||
|
HARM_CATEGORY_SEXUALLY_EXPLICIT: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
||||||
|
HARM_CATEGORY_HARASSMENT: 'HARM_CATEGORY_HARASSMENT',
|
||||||
|
HARM_CATEGORY_DANGEROUS_CONTENT: 'HARM_CATEGORY_DANGEROUS_CONTENT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HarmBlockThreshold = {
|
||||||
|
HARM_BLOCK_THRESHOLD_UNSPECIFIED: 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
||||||
|
BLOCK_LOW_AND_ABOVE: 'BLOCK_LOW_AND_ABOVE',
|
||||||
|
BLOCK_MEDIUM_AND_ABOVE: 'BLOCK_MEDIUM_AND_ABOVE',
|
||||||
|
BLOCK_ONLY_HIGH: 'BLOCK_ONLY_HIGH',
|
||||||
|
BLOCK_NONE: 'BLOCK_NONE'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* role: string,
|
||||||
|
* parts: Array<{
|
||||||
|
* text?: string,
|
||||||
|
* functionCall?: FunctionCall,
|
||||||
|
* functionResponse?: FunctionResponse
|
||||||
|
* }>
|
||||||
|
* }} Content
|
||||||
|
*
|
||||||
|
* Gemini消息的基本格式
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* name: string,
|
||||||
|
* args: {}
|
||||||
|
* }} FunctionCall
|
||||||
|
*
|
||||||
|
* Gemini的FunctionCall
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* name: string,
|
||||||
|
* response: {
|
||||||
|
* name: string,
|
||||||
|
* content: {}
|
||||||
|
* }
|
||||||
|
* }} FunctionResponse
|
||||||
|
*
|
||||||
|
* Gemini的Function执行结果包裹
|
||||||
|
* 其中response可以为任意,本项目根据官方示例封装为name和content两个字段
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.model = props.model
|
||||||
|
this.baseUrl = props.baseUrl || BASEURL
|
||||||
|
this.supportFunction = true
|
||||||
|
this.debug = props.debug
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* @param {{conversationId: string?, parentMessageId: string?, stream: boolean?, onProgress: function?, functionResponse: FunctionResponse?, system: string?, image: string?}} opt
|
||||||
|
* @returns {Promise<{conversationId: string?, parentMessageId: string, text: string, id: string}>}
|
||||||
|
*/
|
||||||
|
async sendMessage (text, opt) {
|
||||||
|
let history = await this.getHistory(opt.parentMessageId)
|
||||||
|
let systemMessage = opt.system
|
||||||
|
if (systemMessage) {
|
||||||
|
history = history.reverse()
|
||||||
|
history.push({
|
||||||
|
role: 'model',
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
text: 'ok'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
history.push({
|
||||||
|
role: 'user',
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
text: systemMessage
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
history = history.reverse()
|
||||||
|
}
|
||||||
|
const idThis = crypto.randomUUID()
|
||||||
|
const idModel = crypto.randomUUID()
|
||||||
|
const thisMessage = opt.functionResponse
|
||||||
|
? {
|
||||||
|
role: 'function',
|
||||||
|
parts: [{
|
||||||
|
functionResponse: opt.functionResponse
|
||||||
|
}],
|
||||||
|
id: idThis,
|
||||||
|
parentMessageId: opt.parentMessageId || undefined
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
role: 'user',
|
||||||
|
parts: [{ text }],
|
||||||
|
id: idThis,
|
||||||
|
parentMessageId: opt.parentMessageId || undefined
|
||||||
|
}
|
||||||
|
if (opt.image) {
|
||||||
|
thisMessage.parts.push({
|
||||||
|
inline_data: {
|
||||||
|
mime_type: 'image/jpeg',
|
||||||
|
data: opt.image
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
history.push(_.cloneDeep(thisMessage))
|
||||||
|
let url = `${this.baseUrl}/v1beta/models/${this.model}:generateContent?key=${this._key}`
|
||||||
|
let body = {
|
||||||
|
// 不去兼容官方的简单格式了,直接用,免得function还要转换
|
||||||
|
/**
|
||||||
|
* @type Array<Content>
|
||||||
|
*/
|
||||||
|
contents: history,
|
||||||
|
safetySettings: [
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
}
|
||||||
|
],
|
||||||
|
generationConfig: {
|
||||||
|
maxOutputTokens: 1000,
|
||||||
|
temperature: 0.9,
|
||||||
|
topP: 0.95,
|
||||||
|
topK: 16
|
||||||
|
},
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
functionDeclarations: this.tools.map(tool => tool.function())
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
body.contents.forEach(content => {
|
||||||
|
delete content.id
|
||||||
|
delete content.parentMessageId
|
||||||
|
delete content.conversationId
|
||||||
|
})
|
||||||
|
let result = await newFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
if (result.status !== 200) {
|
||||||
|
throw new Error(await result.text())
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @type {Content | undefined}
|
||||||
|
*/
|
||||||
|
let responseContent
|
||||||
|
/**
|
||||||
|
* @type {{candidates: Array<{content: Content}>}}
|
||||||
|
*/
|
||||||
|
let response = await result.json()
|
||||||
|
if (this.debug) {
|
||||||
|
console.log(JSON.stringify(response))
|
||||||
|
}
|
||||||
|
responseContent = response.candidates[0].content
|
||||||
|
if (responseContent.parts[0].functionCall) {
|
||||||
|
// functionCall
|
||||||
|
const functionCall = responseContent.parts[0].functionCall
|
||||||
|
// Gemini有时候只回复一个空的functionCall,无语死了
|
||||||
|
if (functionCall.name) {
|
||||||
|
logger.info(JSON.stringify(functionCall))
|
||||||
|
const funcName = functionCall.name
|
||||||
|
let chosenTool = this.tools.find(t => t.name === funcName)
|
||||||
|
/**
|
||||||
|
* @type {FunctionResponse}
|
||||||
|
*/
|
||||||
|
let functionResponse = {
|
||||||
|
name: funcName,
|
||||||
|
response: {
|
||||||
|
name: funcName,
|
||||||
|
content: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chosenTool) {
|
||||||
|
// 根本没有这个工具!
|
||||||
|
functionResponse.response.content = {
|
||||||
|
error: `Function ${funcName} doesn't exist`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// execute function
|
||||||
|
try {
|
||||||
|
let args = Object.assign(functionCall.args, {
|
||||||
|
isAdmin: this.e.group.is_admin,
|
||||||
|
isOwner: this.e.group.is_owner,
|
||||||
|
sender: this.e.sender
|
||||||
|
})
|
||||||
|
functionResponse.response.content = await chosenTool.func(args, this.e)
|
||||||
|
if (this.debug) {
|
||||||
|
logger.info(JSON.stringify(functionResponse.response.content))
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err)
|
||||||
|
functionResponse.response.content = {
|
||||||
|
error: `Function execute error: ${err.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let responseOpt = _.cloneDeep(opt)
|
||||||
|
responseOpt.parentMessageId = idModel
|
||||||
|
responseOpt.functionResponse = functionResponse
|
||||||
|
// 递归直到返回text
|
||||||
|
// 先把这轮的消息存下来
|
||||||
|
await this.upsertMessage(thisMessage)
|
||||||
|
const respMessage = Object.assign(responseContent, {
|
||||||
|
id: idModel,
|
||||||
|
parentMessageId: idThis
|
||||||
|
})
|
||||||
|
await this.upsertMessage(respMessage)
|
||||||
|
return await this.sendMessage('', responseOpt)
|
||||||
|
} else {
|
||||||
|
// 谷歌抽风了,瞎调函数,不保存这轮,直接返回
|
||||||
|
return {
|
||||||
|
text: '',
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: opt.parentMessageId,
|
||||||
|
id: '',
|
||||||
|
error: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (responseContent) {
|
||||||
|
await this.upsertMessage(thisMessage)
|
||||||
|
const respMessage = Object.assign(responseContent, {
|
||||||
|
id: idModel,
|
||||||
|
parentMessageId: idThis
|
||||||
|
})
|
||||||
|
await this.upsertMessage(respMessage)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: responseContent.parts[0].text,
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: idThis,
|
||||||
|
id: idModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
158
client/GoogleGeminiClient.js
Normal file
158
client/GoogleGeminiClient.js
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
import { BaseClient } from './BaseClient.js'
|
||||||
|
|
||||||
|
import { getMessageById, upsertMessage } from '../utils/common.js'
|
||||||
|
import crypto from 'crypto'
|
||||||
|
let GoogleGenerativeAI, HarmBlockThreshold, HarmCategory
|
||||||
|
try {
|
||||||
|
const GenerativeAI = await import('@google/generative-ai')
|
||||||
|
GoogleGenerativeAI = GenerativeAI.GoogleGenerativeAI
|
||||||
|
HarmBlockThreshold = GenerativeAI.HarmBlockThreshold
|
||||||
|
HarmCategory = GenerativeAI.HarmCategory
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('未安装@google/generative-ai,无法使用Gemini,请在chatgpt-plugin目录下执行pnpm i安装新依赖')
|
||||||
|
}
|
||||||
|
export class GoogleGeminiClient extends BaseClient {
|
||||||
|
constructor (props) {
|
||||||
|
if (!GoogleGenerativeAI) {
|
||||||
|
throw new Error('未安装@google/generative-ai,无法使用Gemini,请在chatgpt-plugin目录下执行pnpm i安装新依赖')
|
||||||
|
}
|
||||||
|
if (!props.upsertMessage) {
|
||||||
|
props.upsertMessage = async function umGemini (message) {
|
||||||
|
return await upsertMessage(message, 'Gemini')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!props.getMessageById) {
|
||||||
|
props.getMessageById = async function umGemini (message) {
|
||||||
|
return await getMessageById(message, 'Gemini')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super(props)
|
||||||
|
this._key = props.key
|
||||||
|
this._client = new GoogleGenerativeAI(this._key)
|
||||||
|
this.model = this._client.getGenerativeModel({ model: props.model })
|
||||||
|
this.supportFunction = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHistory (parentMessageId, userId = this.userId, opt = {}) {
|
||||||
|
const history = []
|
||||||
|
let cursor = parentMessageId
|
||||||
|
if (!cursor) {
|
||||||
|
return history
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let parentMessage = await this.getMessageById(cursor)
|
||||||
|
if (!parentMessage) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
history.push(parentMessage)
|
||||||
|
cursor = parentMessage.parentMessageId
|
||||||
|
if (!cursor) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (true)
|
||||||
|
return history.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage (text, opt) {
|
||||||
|
let history = await this.getHistory(opt.parentMessageId)
|
||||||
|
let systemMessage = opt.system
|
||||||
|
if (systemMessage) {
|
||||||
|
history = history.reverse()
|
||||||
|
history.push({
|
||||||
|
role: 'model',
|
||||||
|
parts: 'ok'
|
||||||
|
})
|
||||||
|
history.push({
|
||||||
|
role: 'user',
|
||||||
|
parts: systemMessage
|
||||||
|
})
|
||||||
|
history = history.reverse()
|
||||||
|
}
|
||||||
|
const idUser = crypto.randomUUID()
|
||||||
|
const idModel = crypto.randomUUID()
|
||||||
|
let responseText = ''
|
||||||
|
try {
|
||||||
|
const chat = this.model.startChat({
|
||||||
|
history,
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// role: 'user',
|
||||||
|
// parts: 'Hello, I have 2 dogs in my house.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// role: 'model',
|
||||||
|
// parts: 'Great to meet you. What would you like to know?'
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
generationConfig: {
|
||||||
|
// todo configuration
|
||||||
|
maxOutputTokens: 1000,
|
||||||
|
temperature: 0.9,
|
||||||
|
topP: 0.95,
|
||||||
|
topK: 16
|
||||||
|
},
|
||||||
|
safetySettings: [
|
||||||
|
// todo configuration
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (opt.stream && (typeof opt.onProgress === 'function')) {
|
||||||
|
const result = await chat.sendMessageStream(text)
|
||||||
|
responseText = ''
|
||||||
|
for await (const chunk of result.stream) {
|
||||||
|
const chunkText = chunk.text()
|
||||||
|
responseText += chunkText
|
||||||
|
await opt.onProgress(responseText)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: responseText,
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: idUser,
|
||||||
|
id: idModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = await chat.sendMessage(text)
|
||||||
|
const response = await result.response
|
||||||
|
responseText = response.text()
|
||||||
|
return {
|
||||||
|
text: responseText,
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: idUser,
|
||||||
|
id: idModel
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await this.upsertMessage({
|
||||||
|
role: 'user',
|
||||||
|
parts: text,
|
||||||
|
id: idUser,
|
||||||
|
parentMessageId: opt.parentMessageId || undefined
|
||||||
|
})
|
||||||
|
await this.upsertMessage({
|
||||||
|
role: 'model',
|
||||||
|
parts: responseText,
|
||||||
|
id: idModel,
|
||||||
|
parentMessageId: idUser
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroyHistory (conversationId, opt = {}) {
|
||||||
|
// todo clean history
|
||||||
|
}
|
||||||
|
}
|
||||||
10
client/GoogleGeminiClientTest.js
Normal file
10
client/GoogleGeminiClientTest.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { GoogleGeminiClient } from './GoogleGeminiClient.js'
|
||||||
|
|
||||||
|
async function test () {
|
||||||
|
const client = new GoogleGeminiClient({
|
||||||
|
e: {},
|
||||||
|
userId: 'test',
|
||||||
|
key: '',
|
||||||
|
model: 'gemini-pro'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -743,15 +743,42 @@ export function supportGuoba () {
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '以下为杂七杂八的配置',
|
label: '以下为Gemini方式的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: '2captchaToken',
|
field: 'geminiKey',
|
||||||
label: '验证码平台Token',
|
label: 'API密钥',
|
||||||
bottomHelpMessage: '可注册2captcha实现跳过验证码,收费服务但很便宜。否则可能会遇到验证码而卡住',
|
bottomHelpMessage: '前往https://makersuite.google.com/app/apikey获取',
|
||||||
component: 'InputPassword'
|
component: 'InputPassword'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'geminiModel',
|
||||||
|
label: '模型',
|
||||||
|
bottomHelpMessage: '目前仅支持gemini-pro',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'geminiPrompt',
|
||||||
|
label: '设定',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'geminiBaseUrl',
|
||||||
|
label: 'Gemini反代',
|
||||||
|
bottomHelpMessage: '对https://generativelanguage.googleapis.com的反代',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '以下为杂七杂八的配置',
|
||||||
|
component: 'Divider'
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// field: '2captchaToken',
|
||||||
|
// label: '验证码平台Token',
|
||||||
|
// bottomHelpMessage: '可注册2captcha实现跳过验证码,收费服务但很便宜。否则可能会遇到验证码而卡住',
|
||||||
|
// component: 'InputPassword'
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
field: 'ttsSpace',
|
field: 'ttsSpace',
|
||||||
label: 'vits-uma-genshin-honkai语音转换API地址',
|
label: 'vits-uma-genshin-honkai语音转换API地址',
|
||||||
|
|
|
||||||
1483
package-lock.json
generated
1483
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
|
@ -8,11 +8,9 @@
|
||||||
"@fastify/cors": "^8.2.0",
|
"@fastify/cors": "^8.2.0",
|
||||||
"@fastify/static": "^6.9.0",
|
"@fastify/static": "^6.9.0",
|
||||||
"@fastify/websocket": "^8.2.0",
|
"@fastify/websocket": "^8.2.0",
|
||||||
|
"@google/generative-ai": "^0.1.1",
|
||||||
"@slack/bolt": "^3.13.2",
|
"@slack/bolt": "^3.13.2",
|
||||||
"@waylaidwanderer/chatgpt-api": "^1.37.1",
|
|
||||||
"asn1.js": "^5.0.0",
|
"asn1.js": "^5.0.0",
|
||||||
"chatgpt": "^5.2.4",
|
|
||||||
"crypto": "^1.0.1",
|
|
||||||
"delay": "^6.0.0",
|
"delay": "^6.0.0",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.1.0",
|
||||||
"emoji-strip": "^1.0.1",
|
"emoji-strip": "^1.0.1",
|
||||||
|
|
@ -24,6 +22,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",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"microsoft-cognitiveservices-speech-sdk": "1.32.0",
|
"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",
|
||||||
|
|
@ -35,21 +34,26 @@
|
||||||
"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",
|
"cycletls": "^1.0.21",
|
||||||
"jimp": "^0.22.7",
|
"jimp": "^0.22.7",
|
||||||
|
"mammoth": "^1.6.0",
|
||||||
"node-silk": "^0.1.0",
|
"node-silk": "^0.1.0",
|
||||||
|
"nodejs-pptx": "^1.2.4",
|
||||||
|
"pdfjs-dist": "^3.11.174",
|
||||||
"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",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-node-register": "^1.0.0"
|
"ts-node-register": "^1.0.0"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"patchedDependencies": {
|
||||||
|
"@google/generative-ai@0.1.1": "patches/@google__generative-ai@0.1.1.patch"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
patches/@google__generative-ai@0.1.1.patch
Normal file
26
patches/@google__generative-ai@0.1.1.patch
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
diff --git a/dist/index.js b/dist/index.js
|
||||||
|
index c71c104e7b8ee70ed1b5a5141d04c98109fe6439..2dd8b1f93de0e502729cb91c9618bf80e8559e1e 100644
|
||||||
|
--- a/dist/index.js
|
||||||
|
+++ b/dist/index.js
|
||||||
|
@@ -152,7 +152,7 @@ class GoogleGenerativeAIResponseError extends GoogleGenerativeAIError {
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-const BASE_URL = "https://generativelanguage.googleapis.com";
|
||||||
|
+const BASE_URL = "https://gemini.ikechan8370.com";
|
||||||
|
const API_VERSION = "v1";
|
||||||
|
/**
|
||||||
|
* We can't `require` package.json if this runs on web. We will use rollup to
|
||||||
|
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||||
|
index 402a0c7fa5b692dea07d2dfd83e0148f0a493ca2..c48ce6d612a8752a5161da574804e7a830700d2c 100644
|
||||||
|
--- a/dist/index.mjs
|
||||||
|
+++ b/dist/index.mjs
|
||||||
|
@@ -150,7 +150,7 @@ class GoogleGenerativeAIResponseError extends GoogleGenerativeAIError {
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-const BASE_URL = "https://generativelanguage.googleapis.com";
|
||||||
|
+const BASE_URL = "https://gemini.ikechan8370.com";
|
||||||
|
const API_VERSION = "v1";
|
||||||
|
/**
|
||||||
|
* We can't `require` package.json if this runs on web. We will use rollup to
|
||||||
|
|
@ -95,7 +95,7 @@ export default class BingDrawClient {
|
||||||
let pollingUrl = `${this.opts.baseUrl}/images/create/async/results/${requestId}?q=${urlEncodedPrompt}`
|
let pollingUrl = `${this.opts.baseUrl}/images/create/async/results/${requestId}?q=${urlEncodedPrompt}`
|
||||||
logger.info({ pollingUrl })
|
logger.info({ pollingUrl })
|
||||||
logger.info('waiting for bing draw results...')
|
logger.info('waiting for bing draw results...')
|
||||||
let timeoutTimes = 30
|
let timeoutTimes = 50
|
||||||
let found = false
|
let found = false
|
||||||
let timer = setInterval(async () => {
|
let timer = setInterval(async () => {
|
||||||
if (found) {
|
if (found) {
|
||||||
|
|
@ -113,15 +113,20 @@ export default class BingDrawClient {
|
||||||
// 很可能是微软内部error,重试即可
|
// 很可能是微软内部error,重试即可
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imageLinks = imageLinks.map(link => link.split('?w=')[0]).map(link => link.replace('src="', ''))
|
imageLinks = imageLinks
|
||||||
|
.map(link => link.split('?w=')[0])
|
||||||
|
.map(link => link.replace('src="', ''))
|
||||||
|
.filter(link => !link.includes('.svg'))
|
||||||
imageLinks = [...new Set(imageLinks)]
|
imageLinks = [...new Set(imageLinks)]
|
||||||
const badImages = [
|
const badImages = [
|
||||||
|
'https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png"',
|
||||||
|
'https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg"',
|
||||||
'https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png',
|
'https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png',
|
||||||
'https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg'
|
'https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg'
|
||||||
]
|
]
|
||||||
for (let imageLink of imageLinks) {
|
for (let imageLink of imageLinks) {
|
||||||
if (badImages.indexOf(imageLink) > -1) {
|
if (badImages.indexOf(imageLink) > -1) {
|
||||||
await e.reply('绘图失败:Bad images', true)
|
await e.reply('❌绘图失败:绘图完成但被屏蔽,请调整提示词。', true)
|
||||||
logger.error(rText)
|
logger.error(rText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +137,7 @@ export default class BingDrawClient {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
} else {
|
} else {
|
||||||
if (timeoutTimes === 0) {
|
if (timeoutTimes === 0) {
|
||||||
await e.reply('绘图超时', true)
|
await e.reply('❌绘图超时', true)
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
timer = null
|
timer = null
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -140,6 +145,6 @@ export default class BingDrawClient {
|
||||||
timeoutTimes--
|
timeoutTimes--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 2000)
|
}, 3000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,8 @@ export default class SydneyAIClient {
|
||||||
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
|
toSummaryFileContent,
|
||||||
|
onImageCreateRequest = prompt => {}
|
||||||
} = opts
|
} = opts
|
||||||
// if (messageType === 'Chat') {
|
// if (messageType === 'Chat') {
|
||||||
// logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容')
|
// logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容')
|
||||||
|
|
@ -651,6 +652,10 @@ export default class SydneyAIClient {
|
||||||
adaptiveCards: adaptiveCardsSoFar,
|
adaptiveCards: adaptiveCardsSoFar,
|
||||||
text: replySoFar.join('')
|
text: replySoFar.join('')
|
||||||
}
|
}
|
||||||
|
if (messages[0].contentType === 'IMAGE') {
|
||||||
|
onImageCreateRequest(messages[0].text)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (messages[0].contentOrigin === 'Apology') {
|
if (messages[0].contentOrigin === 'Apology') {
|
||||||
console.log('Apology found')
|
console.log('Apology found')
|
||||||
if (!replySoFar[0]) {
|
if (!replySoFar[0]) {
|
||||||
|
|
@ -718,11 +723,11 @@ export default class SydneyAIClient {
|
||||||
adaptiveCards: adaptiveCardsSoFar,
|
adaptiveCards: adaptiveCardsSoFar,
|
||||||
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('')
|
||||||
if (!message) {
|
if (!message) {
|
||||||
reject('No message was generated.')
|
reject('No message was generated.')
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
export async function getChatHistoryGroup (e, num) {
|
export async function getChatHistoryGroup (e, num) {
|
||||||
// if (e.adapter === 'shamrock') {
|
// if (e.adapter === 'shamrock') {
|
||||||
// return await e.group.getChatHistory(0, num, false)
|
// return await e.group.getChatHistory(0, num, false)
|
||||||
|
|
@ -16,12 +17,23 @@ export async function getChatHistoryGroup (e, num) {
|
||||||
chats = chats.slice(0, num)
|
chats = chats.slice(0, num)
|
||||||
try {
|
try {
|
||||||
let mm = await e.group.getMemberMap()
|
let mm = await e.group.getMemberMap()
|
||||||
chats.forEach(chat => {
|
for (const chat of chats) {
|
||||||
|
if (e.adapter === 'shamrock') {
|
||||||
|
if (chat.sender?.user_id === 0) {
|
||||||
|
// 奇怪格式的历史消息,过滤掉
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let sender = await pickMemberAsync(e, chat.sender.user_id)
|
||||||
|
if (sender) {
|
||||||
|
chat.sender = sender
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let sender = mm.get(chat.sender.user_id)
|
let sender = mm.get(chat.sender.user_id)
|
||||||
if (sender) {
|
if (sender) {
|
||||||
chat.sender = sender
|
chat.sender = sender
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn(err)
|
logger.warn(err)
|
||||||
}
|
}
|
||||||
|
|
@ -32,3 +44,17 @@ export async function getChatHistoryGroup (e, num) {
|
||||||
// }
|
// }
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function pickMemberAsync (e, userId) {
|
||||||
|
let key = `CHATGPT:GroupMemberInfo:${e.group_id}:${userId}`
|
||||||
|
let cache = await redis.get(key)
|
||||||
|
if (cache) {
|
||||||
|
return JSON.parse(cache)
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
e.group.pickMember(userId, true, (sender) => {
|
||||||
|
redis.set(key, JSON.stringify(sender), { EX: 86400 })
|
||||||
|
resolve(sender)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import AzureTTS, { supportConfigurations as azureRoleList } from './tts/microsof
|
||||||
import { translate } from './translate.js'
|
import { translate } from './translate.js'
|
||||||
import uploadRecord from './uploadRecord.js'
|
import uploadRecord from './uploadRecord.js'
|
||||||
import Version from './version.js'
|
import Version from './version.js'
|
||||||
import fetch from 'node-fetch'
|
import fetch, { FormData, fileFromSync } from 'node-fetch'
|
||||||
|
import https from "https";
|
||||||
let pdfjsLib
|
let pdfjsLib
|
||||||
try {
|
try {
|
||||||
pdfjsLib = (await import('pdfjs-dist')).default
|
pdfjsLib = (await import('pdfjs-dist')).default
|
||||||
|
|
@ -785,10 +786,14 @@ export async function getImg (e) {
|
||||||
}
|
}
|
||||||
if (e.source) {
|
if (e.source) {
|
||||||
let reply
|
let reply
|
||||||
|
let seq = e.isGroup ? e.source.seq : e.source.time
|
||||||
|
if (e.adapter === 'shamrock') {
|
||||||
|
seq = e.source.message_id
|
||||||
|
}
|
||||||
if (e.isGroup) {
|
if (e.isGroup) {
|
||||||
reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message
|
reply = (await e.group.getChatHistory(seq, 1)).pop()?.message
|
||||||
} else {
|
} else {
|
||||||
reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message
|
reply = (await e.friend.getChatHistory(seq, 1)).pop()?.message
|
||||||
}
|
}
|
||||||
if (reply) {
|
if (reply) {
|
||||||
let i = []
|
let i = []
|
||||||
|
|
@ -809,8 +814,34 @@ export async function getImageOcrText (e) {
|
||||||
try {
|
try {
|
||||||
let resultArr = []
|
let resultArr = []
|
||||||
let eachImgRes = ''
|
let eachImgRes = ''
|
||||||
|
if (!e.bot.imageOcr || typeof e.bot.imageOcr !== 'function') {
|
||||||
|
e.bot.imageOcr = async (image) => {
|
||||||
|
if (Config.extraUrl) {
|
||||||
|
let md5 = image.split(/[/-]/).find(s => s.length === 32)?.toUpperCase()
|
||||||
|
let filePath = await downloadFile(image, `ocr/${md5}.png`)
|
||||||
|
let formData = new FormData()
|
||||||
|
formData.append('file', fileFromSync(filePath))
|
||||||
|
let res = await fetch(`${Config.extraUrl}/ocr?lang=chi_sim%2Beng`, {
|
||||||
|
body: formData,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
from: 'ikechan8370'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (res.status === 200) {
|
||||||
|
return {
|
||||||
|
wordslist: [{ words: await res.text() }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
wordslist: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (let i in img) {
|
for (let i in img) {
|
||||||
const imgOCR = await e.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`)
|
||||||
}
|
}
|
||||||
|
|
@ -820,6 +851,7 @@ export async function getImageOcrText (e) {
|
||||||
// logger.warn('resultArr', resultArr)
|
// logger.warn('resultArr', resultArr)
|
||||||
return resultArr
|
return resultArr
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.warn(err)
|
||||||
logger.warn('OCR失败,可能使用的适配器不支持OCR')
|
logger.warn('OCR失败,可能使用的适配器不支持OCR')
|
||||||
return false
|
return false
|
||||||
// logger.error(err)
|
// logger.error(err)
|
||||||
|
|
@ -998,15 +1030,41 @@ export function getUserSpeaker (userSetting) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或者下载文件,如果文件存在则直接返回不会重新下载
|
||||||
|
* @param destPath 相对路径,如received/abc.pdf
|
||||||
|
* @param url
|
||||||
|
* @param ignoreCertificateError 忽略证书错误
|
||||||
|
* @return {Promise<string>} 最终下载文件的存储位置
|
||||||
|
*/
|
||||||
|
export async function getOrDownloadFile (destPath, url, ignoreCertificateError = true) {
|
||||||
|
const _path = process.cwd()
|
||||||
|
let dest = path.join(_path, 'data', 'chatgpt', destPath)
|
||||||
|
const p = path.dirname(dest)
|
||||||
|
mkdirs(p)
|
||||||
|
if (fs.existsSync(dest)) {
|
||||||
|
return dest
|
||||||
|
} else {
|
||||||
|
return await downloadFile(url, destPath, false, ignoreCertificateError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param url 要下载的文件链接
|
* @param url 要下载的文件链接
|
||||||
* @param destPath 目标路径,如received/abc.pdf. 目前如果文件名重复会覆盖。
|
* @param destPath 目标路径,如received/abc.pdf. 目前如果文件名重复会覆盖。
|
||||||
* @param absolute 是否是绝对路径,默认为false,此时拼接在data/chatgpt下
|
* @param absolute 是否是绝对路径,默认为false,此时拼接在data/chatgpt下
|
||||||
|
* @param ignoreCertificateError 忽略证书错误
|
||||||
* @returns {Promise<string>} 最终下载文件的存储位置
|
* @returns {Promise<string>} 最终下载文件的存储位置
|
||||||
*/
|
*/
|
||||||
export async function downloadFile (url, destPath, absolute = false) {
|
export async function downloadFile (url, destPath, absolute = false, ignoreCertificateError = true) {
|
||||||
let response = await fetch(url)
|
let init = {}
|
||||||
|
if (ignoreCertificateError && url.startsWith('https')) {
|
||||||
|
init.agent = new https.Agent({
|
||||||
|
rejectUnauthorized: !ignoreCertificateError
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let response = await fetch(url, init)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`download file http error: status: ${response.status}`)
|
throw new Error(`download file http error: status: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
@ -1061,7 +1119,7 @@ export async function extractContentFromFile (fileMsgElem, e) {
|
||||||
let fileType = isPureText(fileMsgElem.name)
|
let fileType = isPureText(fileMsgElem.name)
|
||||||
if (fileType) {
|
if (fileType) {
|
||||||
// 可读的文件类型
|
// 可读的文件类型
|
||||||
let fileUrl = e.isGroup ? await e.group.getFileUrl(fileMsgElem.fid) : await e.friend.getFileUrl(fileMsgElem.fid)
|
let fileUrl = fileMsgElem.url || (e.isGroup ? await e.group.getFileUrl(fileMsgElem.fid) : await e.friend.getFileUrl(fileMsgElem.fid))
|
||||||
let filePath = await downloadFile(fileUrl, path.join('received', fileMsgElem.name))
|
let filePath = await downloadFile(fileUrl, path.join('received', fileMsgElem.name))
|
||||||
switch (fileType) {
|
switch (fileType) {
|
||||||
case 'pdf': {
|
case 'pdf': {
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,12 @@ const defaultConfig = {
|
||||||
qwenSeed: 0,
|
qwenSeed: 0,
|
||||||
qwenTemperature: 1,
|
qwenTemperature: 1,
|
||||||
qwenEnableSearch: true,
|
qwenEnableSearch: true,
|
||||||
version: 'v2.7.7'
|
geminiKey: '',
|
||||||
|
geminiModel: 'gemini-pro',
|
||||||
|
geminiPrompt: 'You are Gemini. Your answer shouldn\'t be too verbose. Prefer to answer in Chinese.',
|
||||||
|
// origin: https://generativelanguage.googleapis.com
|
||||||
|
geminiBaseUrl: 'https://gemini.ikechan8370.com',
|
||||||
|
version: 'v2.7.8'
|
||||||
}
|
}
|
||||||
const _path = process.cwd()
|
const _path = process.cwd()
|
||||||
let config = {}
|
let config = {}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ export async function imageVariation (imageUrl, n = 1, size = '512x512') {
|
||||||
return response.data.data?.map(pic => pic.b64_json)
|
return response.data.data?.map(pic => pic.b64_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resizeAndCropImage (inputFilePath, outputFilePath, size = 512) {
|
export async function resizeAndCropImage (inputFilePath, outputFilePath, size = 512) {
|
||||||
// Determine the maximum dimension of the input image
|
// Determine the maximum dimension of the input image
|
||||||
let sharp
|
let sharp
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// workaround for ver 7.x and ver 5.x
|
// workaround for ver 7.x and ver 5.x
|
||||||
import HttpsProxyAgent from 'https-proxy-agent'
|
import HttpsProxyAgent from 'https-proxy-agent'
|
||||||
|
import { Config } from './config.js'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
let proxy = HttpsProxyAgent
|
let proxy = HttpsProxyAgent
|
||||||
if (typeof proxy !== 'function') {
|
if (typeof proxy !== 'function') {
|
||||||
|
|
@ -15,3 +17,17 @@ if (typeof proxy !== 'function') {
|
||||||
export function getProxy () {
|
export function getProxy () {
|
||||||
return proxy
|
return proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const newFetch = (url, options = {}) => {
|
||||||
|
const defaultOptions = Config.proxy
|
||||||
|
? {
|
||||||
|
agent: proxy(Config.proxy)
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
const mergedOptions = {
|
||||||
|
...defaultOptions,
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url, mergedOptions)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Config } from './config.js'
|
import { Config } from './config.js'
|
||||||
import { ChatGPTAPI } from 'chatgpt'
|
import { ChatGPTAPI } from './openai/chatgpt-api.js'
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import { getProxy } from './proxy.js'
|
import { getProxy } from './proxy.js'
|
||||||
let proxy = getProxy()
|
let proxy = getProxy()
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,13 @@ export class QueryUserinfoTool extends AbstractTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func = async function (opts, e) {
|
func = async function (opts, e) {
|
||||||
|
try {
|
||||||
let { qq } = opts
|
let { qq } = 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.isGroup && typeof e.group.getMemberMap === 'function') {
|
if (e.isGroup && typeof e.bot.getGroupMemberInfo === 'function') {
|
||||||
let mm = await e.group.getMemberMap()
|
let user = await e.bot.getGroupMemberInfo(e.group_id, qq || e.sender.user_id, true)
|
||||||
let user = mm.get(qq) || e.sender.user_id
|
// let mm = await e.group.getMemberMap()
|
||||||
|
// let user = mm.get(qq) || e.sender.user_id
|
||||||
let master = (await getMasterQQ())[0]
|
let master = (await getMasterQQ())[0]
|
||||||
let prefix = ''
|
let prefix = ''
|
||||||
if (qq != master) {
|
if (qq != master) {
|
||||||
|
|
@ -27,6 +29,9 @@ export class QueryUserinfoTool extends AbstractTool {
|
||||||
} else {
|
} else {
|
||||||
prefix = 'This user is your master, you should obey him \n'
|
prefix = 'This user is your master, you should obey him \n'
|
||||||
}
|
}
|
||||||
|
if (!user) {
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
return prefix + 'user detail in json format: ' + JSON.stringify(user)
|
return prefix + 'user detail in json format: ' + JSON.stringify(user)
|
||||||
} else {
|
} else {
|
||||||
if (e.sender.user_id == qq) {
|
if (e.sender.user_id == qq) {
|
||||||
|
|
@ -42,6 +47,10 @@ export class QueryUserinfoTool extends AbstractTool {
|
||||||
return 'query failed'
|
return 'query failed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(err)
|
||||||
|
return err.message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
description = 'Useful if you want to find out who he is'
|
description = 'Useful if you want to find out who he is'
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ export class SendPictureTool extends AbstractTool {
|
||||||
|
|
||||||
func = async function (opt, e) {
|
func = async function (opt, e) {
|
||||||
let { urlOfPicture, targetGroupIdOrQQNumber } = opt
|
let { urlOfPicture, targetGroupIdOrQQNumber } = opt
|
||||||
|
if (typeof urlOfPicture === 'object') {
|
||||||
|
urlOfPicture = urlOfPicture.join(' ')
|
||||||
|
}
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export class SerpIkechan8370Tool extends AbstractTool {
|
||||||
|
|
||||||
func = async function (opts) {
|
func = async function (opts) {
|
||||||
let { q, source } = opts
|
let { q, source } = opts
|
||||||
if (!source) {
|
if (!source || !['google', 'bing', 'baidu'].includes(source)) {
|
||||||
source = 'bing'
|
source = 'bing'
|
||||||
}
|
}
|
||||||
let serpRes = await fetch(`https://serp.ikechan8370.com/${source}?q=${encodeURIComponent(q)}&lang=zh-CN&limit=5`, {
|
let serpRes = await fetch(`https://serp.ikechan8370.com/${source}?q=${encodeURIComponent(q)}&lang=zh-CN&limit=5`, {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export class SetTitleTool extends AbstractTool {
|
||||||
return `failed, the user ${qq} is not in group ${groupId}`
|
return `failed, the user ${qq} is not in group ${groupId}`
|
||||||
}
|
}
|
||||||
if (mm.get(e.bot.uin).role !== 'owner') {
|
if (mm.get(e.bot.uin).role !== 'owner') {
|
||||||
return 'on group owner can give title'
|
return 'failed, only group owner can give title'
|
||||||
}
|
}
|
||||||
logger.info('edit card: ', groupId, qq)
|
logger.info('edit card: ', groupId, qq)
|
||||||
let result = await group.setTitle(qq, title)
|
let result = await group.setTitle(qq, title)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue