diff --git a/README.md b/README.md
index 6d0e9710..c9bfe2d4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-![chatgpt-plugin](https://user-images.githubusercontent.com/21212372/232115814-de9a0633-371f-4733-8da0-dd6e912c8a1e.png)
-
@@ -43,6 +42,9 @@
* 2023-09-10 支持来自claude.ai的claude-2模型
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
* 2023-10-25 增加支持通义千问官方API
+* 2023-12-01 持续优先适配Shamrock
+* 2023-12-14 增加支持Gemini 官方API
+
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
## 版本要求
diff --git a/apps/chat.js b/apps/chat.js
index 631d2f24..87a0dd0f 100644
--- a/apps/chat.js
+++ b/apps/chat.js
@@ -1,83 +1,91 @@
import plugin from '../../../lib/plugins/plugin.js'
import _ from 'lodash'
-import {Config, defaultOpenAIAPI} from '../utils/config.js'
-import {v4 as uuid} from 'uuid'
+import { Config, defaultOpenAIAPI } from '../utils/config.js'
+import { v4 as uuid } from 'uuid'
import delay from 'delay'
-import {ChatGPTAPI} from '../utils/openai/chatgpt-api.js'
-import {BingAIClient} from '@waylaidwanderer/chatgpt-api'
+import { ChatGPTAPI } from '../utils/openai/chatgpt-api.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 VoiceVoxTTS from '../utils/tts/voicevox.js'
import Version from '../utils/version.js'
import {
- completeJSON,
- extractContentFromFile,
- formatDate,
- formatDate2,
- generateAudio,
- getDefaultReplySetting,
- getImageOcrText,
- getImg,
- getMasterQQ,
- getMaxModelTokens,
- getMessageById,
- getUin,
- getUserData,
- getUserReplySetting,
- isCN,
- isImage,
- makeForwardMsg,
- randomString,
- render,
- renderUrl,
- upsertMessage
+ completeJSON,
+ extractContentFromFile,
+ formatDate,
+ formatDate2,
+ generateAudio,
+ getDefaultReplySetting,
+ getImageOcrText,
+ getImg,
+ getMasterQQ,
+ getMaxModelTokens,
+ getMessageById,
+ getOrDownloadFile,
+ getUin,
+ getUserData,
+ getUserReplySetting,
+ isCN,
+ isImage,
+ makeForwardMsg,
+ randomString,
+ render,
+ renderUrl,
+ upsertMessage
} from '../utils/common.js'
-import {ChatGPTPuppeteer} from '../utils/browser.js'
-import {KeyvFile} from 'keyv-file'
-import {OfficialChatGPTClient} from '../utils/message.js'
+import { ChatGPTPuppeteer } from '../utils/browser.js'
+import { KeyvFile } from 'keyv-file'
+import { OfficialChatGPTClient } from '../utils/message.js'
import fetch from 'node-fetch'
-import {deleteConversation, getConversations, getLatestMessageIdByConversationId} from '../utils/conversation.js'
-import {convertSpeaker, speakers} from '../utils/tts.js'
+import { deleteConversation, getConversations, getLatestMessageIdByConversationId } from '../utils/conversation.js'
+import { convertSpeaker, speakers } from '../utils/tts.js'
import ChatGLMClient from '../utils/chatglm.js'
-import {convertFaces} from '../utils/face.js'
-import {SlackClaudeClient} from '../utils/slack/slackClient.js'
-import {getPromptByName} from '../utils/prompts.js'
+import { convertFaces } from '../utils/face.js'
+import { SlackClaudeClient } from '../utils/slack/slackClient.js'
+import { getPromptByName } from '../utils/prompts.js'
import BingDrawClient from '../utils/BingDraw.js'
import XinghuoClient from '../utils/xinghuo/xinghuo.js'
import Bard from '../utils/bard.js'
-import {JinyanTool} from '../utils/tools/JinyanTool.js'
-import {SendVideoTool} from '../utils/tools/SendBilibiliTool.js'
-import {KickOutTool} from '../utils/tools/KickOutTool.js'
-import {EditCardTool} from '../utils/tools/EditCardTool.js'
-import {SearchVideoTool} from '../utils/tools/SearchBilibiliTool.js'
-import {SearchMusicTool} from '../utils/tools/SearchMusicTool.js'
-import {QueryStarRailTool} from '../utils/tools/QueryStarRailTool.js'
-import {WebsiteTool} from '../utils/tools/WebsiteTool.js'
-import {WeatherTool} from '../utils/tools/WeatherTool.js'
-import {SerpTool} from '../utils/tools/SerpTool.js'
-import {SerpIkechan8370Tool} from '../utils/tools/SerpIkechan8370Tool.js'
-import {SendPictureTool} from '../utils/tools/SendPictureTool.js'
-import {SerpImageTool} from '../utils/tools/SearchImageTool.js'
-import {ImageCaptionTool} from '../utils/tools/ImageCaptionTool.js'
-import {SendAudioMessageTool} from '../utils/tools/SendAudioMessageTool.js'
-import {ProcessPictureTool} from '../utils/tools/ProcessPictureTool.js'
-import {APTool} from '../utils/tools/APTool.js'
-import {QueryGenshinTool} from '../utils/tools/QueryGenshinTool.js'
-import {HandleMessageMsgTool} from '../utils/tools/HandleMessageMsgTool.js'
-import {QueryUserinfoTool} from '../utils/tools/QueryUserinfoTool.js'
-import {EliMovieTool} from '../utils/tools/EliMovieTool.js'
-import {EliMusicTool} from '../utils/tools/EliMusicTool.js'
-import {SendMusicTool} from '../utils/tools/SendMusicTool.js'
-import {SendDiceTool} from '../utils/tools/SendDiceTool.js'
-import {SendAvatarTool} from '../utils/tools/SendAvatarTool.js'
-import {SendMessageToSpecificGroupOrUserTool} from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
-import {SetTitleTool} from '../utils/tools/SetTitleTool.js'
-import {solveCaptchaOneShot} from '../utils/bingCaptcha.js'
-import {ClaudeAIClient} from '../utils/claude.ai/index.js'
-import {getProxy} from '../utils/proxy.js'
-import {QwenApi} from '../utils/alibaba/qwen-api.js'
-import {getChatHistoryGroup} from '../utils/chat.js'
+import { JinyanTool } from '../utils/tools/JinyanTool.js'
+import { SendVideoTool } from '../utils/tools/SendBilibiliTool.js'
+import { KickOutTool } from '../utils/tools/KickOutTool.js'
+import { EditCardTool } from '../utils/tools/EditCardTool.js'
+import { SearchVideoTool } from '../utils/tools/SearchBilibiliTool.js'
+import { SearchMusicTool } from '../utils/tools/SearchMusicTool.js'
+import { QueryStarRailTool } from '../utils/tools/QueryStarRailTool.js'
+import { WebsiteTool } from '../utils/tools/WebsiteTool.js'
+import { WeatherTool } from '../utils/tools/WeatherTool.js'
+import { SerpTool } from '../utils/tools/SerpTool.js'
+import { SerpIkechan8370Tool } from '../utils/tools/SerpIkechan8370Tool.js'
+import { SendPictureTool } from '../utils/tools/SendPictureTool.js'
+import { SerpImageTool } from '../utils/tools/SearchImageTool.js'
+import { ImageCaptionTool } from '../utils/tools/ImageCaptionTool.js'
+import { SendAudioMessageTool } from '../utils/tools/SendAudioMessageTool.js'
+import { ProcessPictureTool } from '../utils/tools/ProcessPictureTool.js'
+import { APTool } from '../utils/tools/APTool.js'
+import { QueryGenshinTool } from '../utils/tools/QueryGenshinTool.js'
+import { HandleMessageMsgTool } from '../utils/tools/HandleMessageMsgTool.js'
+import { QueryUserinfoTool } from '../utils/tools/QueryUserinfoTool.js'
+import { EliMovieTool } from '../utils/tools/EliMovieTool.js'
+import { EliMusicTool } from '../utils/tools/EliMusicTool.js'
+import { SendMusicTool } from '../utils/tools/SendMusicTool.js'
+import { SendDiceTool } from '../utils/tools/SendDiceTool.js'
+import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
+import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
+import { SetTitleTool } from '../utils/tools/SetTitleTool.js'
+import { solveCaptchaOneShot } from '../utils/bingCaptcha.js'
+import { ClaudeAIClient } from '../utils/claude.ai/index.js'
+import { getProxy } from '../utils/proxy.js'
+import { QwenApi } from '../utils/alibaba/qwen-api.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 {
await import('@azure/openai')
@@ -97,6 +105,9 @@ try {
}
let version = Config.version
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是保留上下文的。超时后销毁对话,再次对话创建新的对话。
* 单位:秒
@@ -191,6 +202,12 @@ export class chatgpt extends plugin {
/** 执行方法 */
fnc: 'qwen'
},
+ {
+ /** 命令正则匹配 */
+ reg: '^#gemini[sS]*',
+ /** 执行方法 */
+ fnc: 'gemini'
+ },
{
/** 命令正则匹配 */
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
@@ -204,11 +221,11 @@ export class chatgpt extends plugin {
permission: 'master'
},
{
- reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)对话([sS]*)',
+ reg: '^#(chatgpt|星火|通义千问|克劳德|克劳德2|必应|api|API|api3|API3|glm|巴德)?(结束|新开|摧毁|毁灭|完结)对话([sS]*)',
fnc: 'destroyConversations'
},
{
- reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)全部对话$',
+ reg: '^#(chatgpt|星火|通义千问|克劳德|克劳德2|必应|api|API|api3|API3|glm|巴德)?(结束|新开|摧毁|毁灭|完结)全部对话$',
fnc: 'endAllConversations',
permission: 'master'
},
@@ -299,7 +316,15 @@ export class chatgpt extends plugin {
*/
async destroyConversations (e) {
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}`)
if (use === 'claude') {
// let client = new SlackClaudeClient({
@@ -386,6 +411,14 @@ export class chatgpt extends plugin {
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
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') {
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`)
if (!c) {
@@ -455,6 +488,14 @@ export class chatgpt extends plugin {
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
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') {
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`)
if (!c) {
@@ -476,7 +517,15 @@ export class chatgpt extends plugin {
}
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
switch (use) {
case 'claude': {
@@ -578,6 +627,18 @@ export class chatgpt extends plugin {
}
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)
}
@@ -797,7 +858,7 @@ export class chatgpt extends plugin {
* #chatgpt
*/
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
if (this.toggleMode === 'at') {
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}`
break
}
+ case 'gemini': {
+ key = `CHATGPT:CONVERSATIONS_GEMINI:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
+ break
+ }
}
let ctime = new Date()
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
@@ -1368,155 +1433,39 @@ export class chatgpt extends plugin {
}
async chatgpt1 (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'api', '#chat1')
}
async chatgpt3 (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'api3', '#chat3')
}
async chatglm (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'chatglm')
}
async bing (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'bing')
}
async claude2 (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'claude2')
}
async claude (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#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
+ return await this.otherMode(e, 'claude')
}
async qwen (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#xh')
- }
- return false
- }
- let prompt = _.replace(e.raw_message.trimStart(), '#qwen', '').trim()
- if (prompt.length === 0) {
- return false
- }
- await this.abstractChat(e, prompt, 'qwen')
- return true
+ return await this.otherMode(e, 'gemini')
+ }
+
+ async gemini (e) {
+ return await this.otherMode(e, 'gemini')
}
async xh (e) {
- if (!Config.allowOtherMode) {
- return false
- }
- let ats = e.message.filter(m => m.type === 'at')
- if (!(e.atme || e.atBot) && ats.length > 0) {
- if (Config.debug) {
- logger.mark('艾特别人了,没艾特我,忽略#xh')
- }
- return false
- }
- let prompt = _.replace(e.raw_message.trimStart(), '#xh', '').trim()
- if (prompt.length === 0) {
- return false
- }
- await this.abstractChat(e, prompt, 'xh')
- return true
+ return await this.otherMode(e, 'xh')
}
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
@@ -1576,48 +1525,38 @@ export class chatgpt extends plugin {
}
const userData = await getUserData(e.user_id)
const useCast = userData.cast || {}
- switch (use) {
- case 'browser': {
+ if (use === 'browser') {
+ {
return await this.chatgptBrowserBased(prompt, conversation)
}
- case 'bing': {
+ } else if (use === 'bing') {
+ {
let throttledTokens = []
- let { bingToken, allThrottled } = await getAvailableBingToken(conversation, throttledTokens)
+ let {
+ bingToken,
+ allThrottled
+ } = await getAvailableBingToken(conversation, throttledTokens)
let cookies
if (bingToken?.indexOf('=') > -1) {
cookies = bingToken
}
let bingAIClient
- if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') {
- const cacheOptions = {
- namespace: Config.toneStyle,
- store: new KeyvFile({ filename: 'cache.json' })
- }
- bingAIClient = new SydneyAIClient({
- userToken: bingToken, // "_U" cookie from bing.com
- cookies,
- debug: Config.debug,
- cache: cacheOptions,
- user: e.sender.user_id,
- proxy: Config.proxy
- })
- // Sydney不实现上下文传递,删除上下文索引
- delete conversation.clientId
- delete conversation.invocationId
- 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)
+ const cacheOptions = {
+ namespace: Config.toneStyle,
+ store: new KeyvFile({ filename: 'cache.json' })
}
+ bingAIClient = new SydneyAIClient({
+ userToken: bingToken, // "_U" cookie from bing.com
+ cookies,
+ debug: Config.debug,
+ cache: cacheOptions,
+ user: e.sender.user_id,
+ proxy: Config.proxy
+ })
+ // Sydney不实现上下文传递,删除上下文索引
+ delete conversation.clientId
+ delete conversation.invocationId
+ delete conversation.conversationSignature
let response
let reply = ''
let retry = 3
@@ -1634,74 +1573,85 @@ export class chatgpt extends plugin {
}
// 重新拿存储的token,因为可能之前有过期的被删了
let abtrs = await getAvailableBingToken(conversation, throttledTokens)
- if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') {
- bingToken = abtrs.bingToken
- // eslint-disable-next-line no-unused-vars
- allThrottled = abtrs.allThrottled
- if (bingToken?.indexOf('=') > -1) {
- cookies = bingToken
- }
- if (!bingAIClient.opts) {
- bingAIClient.opts = {}
- }
- bingAIClient.opts.userToken = bingToken
- bingAIClient.opts.cookies = cookies
- // opt.messageType = allThrottled ? 'Chat' : 'SearchQuery'
- if (Config.enableGroupContext && e.isGroup && typeof e.group.getMemberMap === 'function') {
- try {
- opt.groupId = e.group_id
- opt.qq = e.sender.user_id
- opt.nickname = e.sender.card
- opt.groupName = e.group.name || e.group_name
- opt.botName = e.isGroup ? (e.group.pickMember(getUin(e)).card || e.group.pickMember(getUin(e)).nickname) : e.bot.nickname
- let master = (await getMasterQQ())[0]
- if (master && e.group) {
- opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
- }
- if (master && !e.group) {
- opt.masterName = e.bot.getFriendList().get(parseInt(master))?.nickname
- }
- opt.chats = await getChatHistoryGroup(e, Config.groupContextLength)
- } catch (err) {
- logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
- }
- }
- let toSummaryFileContent
+ bingToken = abtrs.bingToken
+ // eslint-disable-next-line no-unused-vars
+ allThrottled = abtrs.allThrottled
+ if (bingToken?.indexOf('=') > -1) {
+ cookies = bingToken
+ }
+ if (!bingAIClient.opts) {
+ bingAIClient.opts = {}
+ }
+ bingAIClient.opts.userToken = bingToken
+ bingAIClient.opts.cookies = cookies
+ // opt.messageType = allThrottled ? 'Chat' : 'SearchQuery'
+ if (Config.enableGroupContext && e.isGroup && typeof e.group.getMemberMap === 'function') {
try {
- if (e.source) {
- let msgs = e.isGroup ? await e.group.getChatHistory(e.source.seq, 1) : await e.friend.getChatHistory(e.source.time, 1)
- let sourceMsg = msgs[0]
- let fileMsgElem = sourceMsg.message.find(msg => msg.type === 'file')
- if (fileMsgElem) {
- toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
- }
+ opt.groupId = e.group_id
+ opt.qq = e.sender.user_id
+ opt.nickname = e.sender.card
+ opt.groupName = e.group.name || e.group_name
+ opt.botName = e.isGroup ? (e.group.pickMember(getUin(e)).card || e.group.pickMember(getUin(e)).nickname) : e.bot.nickname
+ let master = (await getMasterQQ())[0]
+ if (master && e.group) {
+ opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname
+ }
+ if (master && !e.group) {
+ opt.masterName = e.bot.getFriendList().get(parseInt(master))?.nickname
}
+ opt.chats = await getChatHistoryGroup(e, Config.groupContextLength)
} catch (err) {
- logger.warn('读取文件内容出错, 忽略文件内容', err)
- }
- opt.toSummaryFileContent = toSummaryFileContent
- } else {
- // 重新创建client,因为token可能换到别的了
- if (bingToken?.indexOf('=') > -1) {
- cookies = bingToken
+ logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
}
- 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
+ }
+ let toSummaryFileContent
+ try {
+ if (e.source) {
+ let seq = e.isGroup ? e.source.seq : e.source.time
+ if (e.adapter === 'shamrock') {
+ 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) {
+ toSummaryFileContent = await extractContentFromFile(fileMsgElem, e)
+ }
}
- bingAIClient = new BingAIClient(bingOption)
+ } catch (err) {
+ logger.warn('读取文件内容出错, 忽略文件内容', err)
}
+ opt.toSummaryFileContent = toSummaryFileContent
// 写入图片数据
if (Config.sydneyImageRecognition) {
const image = await getImg(e)
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) => {
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曾经有异常,则清除异常
let Tokens = JSON.parse((await redis.get('CHATGPT:BING_TOKENS')) || '[]')
const TokenIndex = Tokens?.findIndex(element => element.Token === abtrs.bingToken)
@@ -1768,7 +1692,7 @@ export class chatgpt extends plugin {
const { maxConv } = error
if (message && typeof message === 'string' && message.indexOf('CaptchaChallenge') > -1) {
if (bingToken) {
- if (maxConv >= 20) {
+ if (maxConv >= 20 && Config.bingCaptchaOneShotUrl) {
// maxConv为30说明token有效,可以通过解验证码码服务过码
await e.reply('出现必应验证码,尝试解决中')
try {
@@ -1777,6 +1701,7 @@ export class chatgpt extends plugin {
await e.reply('验证码已解决')
} else {
logger.error(captchaResolveResult)
+ errorMessage = message
await e.reply('验证码解决失败: ' + captchaResolveResult.error)
retry = 0
}
@@ -1787,60 +1712,60 @@ export class chatgpt extends plugin {
}
} else {
// 未登录用户maxConv目前为5或10,出验证码没救
- logger.warn(`token [${bingToken}] 无效或已过期,如确认token无误,请前往网页版必应对话一次`)
+ logger.warn(`token [${bingToken}] 出现必应验证码,请前往网页版或app手动解决`)
+ errorMessage = message
retry = 0
}
} else {
retry = 0
}
- } else
- if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
- throttledTokens.push(bingToken)
- let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
- const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
- const now = new Date()
- const hours = now.getHours()
- now.setHours(hours + 6)
- bingTokens[badBingToken].State = '受限'
- bingTokens[badBingToken].DisactivationTime = now
- await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
+ } else if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
+ throttledTokens.push(bingToken)
+ let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
+ const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
+ const now = new Date()
+ const hours = now.getHours()
+ now.setHours(hours + 6)
+ bingTokens[badBingToken].State = '受限'
+ bingTokens[badBingToken].DisactivationTime = now
+ await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
// 不减次数
- } else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) {
+ } else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) {
// token过期了
- let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
- const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
- if (badBingToken > 0) {
- // 可能是微软抽风,给三次机会
- if (bingTokens[badBingToken]?.exception) {
- if (bingTokens[badBingToken].exception <= 3) {
- bingTokens[badBingToken].exception += 1
- } else {
- bingTokens[badBingToken].exception = 0
- bingTokens[badBingToken].State = '过期'
- }
+ let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
+ const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
+ if (badBingToken > 0) {
+ // 可能是微软抽风,给三次机会
+ if (bingTokens[badBingToken]?.exception) {
+ if (bingTokens[badBingToken].exception <= 3) {
+ bingTokens[badBingToken].exception += 1
} else {
- bingTokens[badBingToken].exception = 1
+ bingTokens[badBingToken].exception = 0
+ bingTokens[badBingToken].State = '过期'
}
- await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
} else {
- retry = retry - 1
+ bingTokens[badBingToken].exception = 1
}
- errorMessage = 'UnauthorizedRequest:必应token不正确或已过期'
- // logger.warn(`token${bingToken}疑似不存在或已过期,再试试`)
- // retry = retry - 1
+ await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
} else {
- retry--
- errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message
+ retry = retry - 1
}
+ errorMessage = 'UnauthorizedRequest:必应token不正确或已过期'
+ // logger.warn(`token${bingToken}疑似不存在或已过期,再试试`)
+ // retry = retry - 1
+ } else {
+ retry--
+ errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message
+ }
}
} while (retry > 0)
if (errorMessage) {
response = response || {}
if (errorMessage.includes('CaptchaChallenge')) {
if (bingToken) {
- errorMessage = '出现验证码,请使用当前账户前往https://www.bing.com/chat或Edge侧边栏手动解除验证码'
+ errorMessage = '出现验证码,请使用当前账户前往https://www.bing.com/chat或Edge侧边栏或移动端APP手动解除验证码'
} else {
- errorMessage = '出现验证码,且未配置必应账户,请尝试更换代理/反代或绑定必应账户以解除验证码'
+ errorMessage = '未配置必应账户,请绑定必应账户再使用必应模式'
}
}
return {
@@ -1866,7 +1791,8 @@ export class chatgpt extends plugin {
}
}
}
- case 'api3': {
+ } else if (use === 'api3') {
+ {
// official without cloudflare
let accessToken = await redis.get('CHATGPT:TOKEN')
if (!accessToken) {
@@ -1890,7 +1816,8 @@ export class chatgpt extends plugin {
}
return sendMessageResult
}
- case 'chatglm': {
+ } else if (use === 'chatglm') {
+ {
const cacheOptions = {
namespace: 'chatglm_6b',
store: new KeyvFile({ filename: 'cache.json' })
@@ -1902,7 +1829,8 @@ export class chatgpt extends plugin {
let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation)
return sendMessageResult
}
- case 'poe': {
+ } else if (use === 'poe') {
+ {
const cookie = await redis.get('CHATGPT:POE_TOKEN')
if (!cookie) {
throw new Error('未绑定Poe Cookie,请使用#chatgpt设置Poe token命令绑定cookie')
@@ -1920,7 +1848,8 @@ export class chatgpt extends plugin {
text: response.data
}
}
- case 'claude': {
+ } else if (use === 'claude') {
+ {
let client = new SlackClaudeClient({
slackUserToken: Config.slackUserToken,
slackChannelId: Config.slackChannelId
@@ -1944,7 +1873,8 @@ export class chatgpt extends plugin {
text
}
}
- case 'claude2': {
+ } else if (use === 'claude2') {
+ {
let { conversationId } = conversation
let client = new ClaudeAIClient({
organizationId: Config.claudeAIOrganizationId,
@@ -1984,7 +1914,8 @@ export class chatgpt extends plugin {
return await client.sendMessage(prompt, conv.uuid, attachments)
}
}
- case 'xh': {
+ } else if (use === 'xh') {
+ {
const cacheOptions = {
namespace: 'xh',
store: new KeyvFile({ filename: 'cache.json' })
@@ -2007,7 +1938,8 @@ export class chatgpt extends plugin {
})
return response
}
- case 'azure': {
+ } else if (use === 'azure') {
+ {
let azureModel
try {
azureModel = await import('@azure/openai')
@@ -2017,15 +1949,22 @@ export class chatgpt extends plugin {
let OpenAIClient = azureModel.OpenAIClient
let AzureKeyCredential = azureModel.AzureKeyCredential
let msg = conversation.messages
- let content = { role: 'user', content: prompt }
+ let content = {
+ role: 'user',
+ content: prompt
+ }
msg.push(content)
const client = new OpenAIClient(Config.azureUrl, new AzureKeyCredential(Config.azApiKey))
const deploymentName = Config.azureDeploymentName
const { choices } = await client.getChatCompletions(deploymentName, msg)
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 = {
parameters: {
top_p: Config.qwenTopP || 0.5,
@@ -2039,12 +1978,15 @@ export class chatgpt extends plugin {
completionParams.model = Config.qwenModel
}
const currentDate = new Date().toISOString().split('T')[0]
- async function um (message) {
+
+ async function um(message) {
return await upsertMessage(message, 'QWEN')
}
- async function gm (id) {
+
+ async function gm(id) {
return await getMessageById(id, 'QWEN')
}
+
let opts = {
apiKey: Config.qwenApiKey,
debug: false,
@@ -2076,7 +2018,8 @@ export class chatgpt extends plugin {
}
return msg
}
- case 'bard': {
+ } else if (use === 'bard') {
+ {
// 处理cookie
const matchesPSID = /__Secure-1PSID=([^;]+)/.exec(Config.bardPsid)
const matchesPSIDTS = /__Secure-1PSIDTS=([^;]+)/.exec(Config.bardPsid)
@@ -2106,13 +2049,13 @@ export class chatgpt extends plugin {
bardURL: Config.bardForceUseReverse ? Config.bardReverseProxy : 'https://bard.google.com'
})
let chat = await bot.createChat(conversation?.conversationId
- ? {
+ ? {
conversationID: conversation.conversationId,
responseID: conversation.parentMessageId,
choiceID: conversation.clientId,
_reqID: conversation.invocationId
}
- : {})
+ : {})
let response = await chat.ask(prompt, {
image: imageBuff,
format: Bard.JSON
@@ -2126,7 +2069,126 @@ export class chatgpt extends plugin {
images: response.images
}
}
- default: {
+ } 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: {
+ 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 = {}
if (Config.model) {
completionParams.model = Config.model
@@ -2157,30 +2219,25 @@ export class chatgpt extends plugin {
const defaultBotName = 'ChatGPT'
const groupContextTip = Config.groupContextTip
system = system.replaceAll(namePlaceholder, opt.botName || defaultBotName) +
- ((Config.enableGroupContext && opt.groupId) ? groupContextTip : '')
+ ((Config.enableGroupContext && opt.groupId) ? groupContextTip : '')
system += 'Attention, you are currently chatting in a qq group, then one who asks you now is' + `${opt.nickname}(${opt.qq})。`
system += `the group name is ${opt.groupName}, group id is ${opt.groupId}。`
if (opt.botName) {
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) {
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 || {}
- // if (sender.user_id === e.bot.uin && chat.raw_message.startsWith('建议的回复')) {
- if (chat.raw_message.startsWith('建议的回复')) {
- // 建议的回复太容易污染设定导致对话太固定跑偏了
- return ''
- }
- 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')
+ .map(chat => {
+ let sender = chat.sender || {}
+ // if (sender.user_id === e.bot.uin && chat.raw_message.startsWith('建议的回复')) {
+ if (chat.raw_message.startsWith('建议的回复')) {
+ // 建议的回复太容易污染设定导致对话太固定跑偏了
+ return ''
+ }
+ 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')
}
} catch (err) {
if (e.isGroup) {
@@ -2317,28 +2374,8 @@ export class chatgpt extends plugin {
}
}
}
- let img = []
- if (e.source) {
- // 优先从回复找图
- let reply
- if (e.isGroup) {
- reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message
- } else {
- reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message
- }
- if (reply) {
- 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) {
+ let img = await getImg(e)
+ if (img?.length > 0 && Config.extraUrl) {
tools.push(new ImageCaptionTool())
tools.push(new ProcessPictureTool())
prompt += `\nthe url of the picture(s) above: ${img.join(', ')}`
@@ -2373,7 +2410,10 @@ export class chatgpt extends plugin {
if (msg.text) {
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)
// 感觉换成targetGroupIdOrUserQQNumber这种表意比较清楚的变量名,效果会好一丢丢
if (!args.groupId) {
@@ -2384,7 +2424,10 @@ export class chatgpt extends plugin {
} catch (err) {
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}`)
option.parentMessageId = msg.id
option.name = name
@@ -2712,6 +2755,25 @@ export class chatgpt extends plugin {
}
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 = []) {
diff --git a/apps/draw.js b/apps/draw.js
index 10eed610..f45722b5 100644
--- a/apps/draw.js
+++ b/apps/draw.js
@@ -277,7 +277,7 @@ export class dalle extends plugin {
await client.getImages(prompt, e)
} catch (err) {
await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`)
- await e.reply('绘图失败:' + err)
+ await e.reply('❌绘图失败:' + err)
}
}
}
diff --git a/apps/management.js b/apps/management.js
index 689dd285..ff74a30f 100644
--- a/apps/management.js
+++ b/apps/management.js
@@ -1,4 +1,5 @@
import plugin from '../../../lib/plugins/plugin.js'
+import { exec } from 'child_process'
import { Config } from '../utils/config.js'
import {
formatDuration,
@@ -126,6 +127,11 @@ export class ChatgptManagement extends plugin {
fnc: 'useClaudeAISolution',
permission: 'master'
},
+ {
+ reg: '^#chatgpt切换(Gemini|gemini)$',
+ fnc: 'useGeminiSolution',
+ permission: 'master'
+ },
{
reg: '^#chatgpt切换星火$',
fnc: 'useXinghuoBasedSolution',
@@ -184,6 +190,11 @@ export class ChatgptManagement extends plugin {
fnc: 'setAPIKey',
permission: 'master'
},
+ {
+ reg: '^#chatgpt设置(Gemini|gemini)(Key|key)$',
+ fnc: 'setGeminiKey',
+ permission: 'master'
+ },
{
reg: '^#chatgpt设置(API|api)设定$',
fnc: 'setAPIPromptPrefix',
@@ -314,6 +325,11 @@ export class ChatgptManagement extends plugin {
reg: '^#chatgpt设置星火模型$',
fnc: 'setXinghuoModel',
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 () {
let use = await redis.get('CHATGPT:USE')
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 () {
let use = await redis.get('CHATGPT:USE')
if (use !== 'qwen') {
@@ -1148,6 +1225,21 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
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 () {
this.setContext('saveXinghuoToken')
await this.reply('请发送星火的ssoSessionId', true)
diff --git a/apps/prompts.js b/apps/prompts.js
index a2b2809e..a78e4d33 100644
--- a/apps/prompts.js
+++ b/apps/prompts.js
@@ -158,7 +158,8 @@ export class help extends plugin {
api: 'promptPrefixOverride',
Custom: 'sydney',
claude: 'slackClaudeGlobalPreset',
- qwen: 'promptPrefixOverride'
+ qwen: 'promptPrefixOverride',
+ gemini: 'geminiPrompt'
}
if (keyMap[use]) {
@@ -171,7 +172,7 @@ export class help extends plugin {
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
} else {
- await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude`, true)
+ await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude、通义千问和Gemini`, true)
}
}
diff --git a/client/BaseClient.js b/client/BaseClient.js
index 46232df0..1b8eebb9 100644
--- a/client/BaseClient.js
+++ b/client/BaseClient.js
@@ -14,13 +14,18 @@ export class BaseClient {
constructor (props = {}) {
this.supportFunction = false
this.maxToken = 4096
+ /**
+ * @type {Array
}
+ */
this.tools = []
const {
- e, getMessageById, upsertMessage
+ e, getMessageById, upsertMessage, deleteMessageById, userId
} = props
this.e = e
this.getMessageById = getMessageById
this.upsertMessage = upsertMessage
+ this.deleteMessageById = deleteMessageById || (() => {})
+ this.userId = userId
}
/**
@@ -36,20 +41,28 @@ export class BaseClient {
* insert or update a message with the id
*
* @type function
- * @param {string} id
* @param {object} message
* @return {Promise}
*/
upsertMessage
+ /**
+ * delete a message with the id
+ *
+ * @type function
+ * @param {string} id
+ * @return {Promise}
+ */
+ deleteMessageById
+
/**
* Send prompt message with history and return response message \
* if function called, handled internally \
* override this method to implement logic of sending and receiving message
*
- * @param msg
- * @param opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
- * @returns {Promise} required fields: [text, conversationId, parentMessageId, id]
+ * @param {string} msg
+ * @param {{conversationId: string?, parentMessageId: string?, stream: boolean?, onProgress: function?}} opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
+ * @returns {Promise<{text, conversationId, parentMessageId, id}>} required fields: [text, conversationId, parentMessageId, id]
*/
async sendMessage (msg, opt = {}) {
throw new Error('not implemented in abstract client')
@@ -60,11 +73,12 @@ export class BaseClient {
* override this method to implement logic of getting history
* keyv with local file or redis recommended
*
- * @param userId such as qq number
- * @param opt other options
- * @returns {Promise}
+ * @param userId optional, such as qq number
+ * @param parentMessageId if blank, no history
+ * @param opt optional, other options
+ * @returns {Promise