mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 21:37:11 +00:00
commit
eddb7c7685
11 changed files with 106 additions and 171 deletions
|
|
@ -238,11 +238,11 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
|||
|
||||
// ANY要笑死人的效果
|
||||
let mode = opt.toolMode || 'AUTO'
|
||||
let lastFuncName = opt.functionResponse?.name
|
||||
let lastFuncName = (/** @type {FunctionResponse[] | undefined}**/ opt.functionResponse)?.map(rsp => rsp.name)
|
||||
const mustSendNextTurn = [
|
||||
'searchImage', 'searchMusic', 'searchVideo'
|
||||
]
|
||||
if (lastFuncName && mustSendNextTurn.includes(lastFuncName)) {
|
||||
if (lastFuncName && lastFuncName?.find(name => mustSendNextTurn.includes(name))) {
|
||||
mode = 'ANY'
|
||||
}
|
||||
body.tool_config = {
|
||||
|
|
@ -328,7 +328,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
|||
let args = Object.assign(fc.args, {
|
||||
isAdmin,
|
||||
isOwner,
|
||||
sender: this.e.sender,
|
||||
sender: this.e.sender.user_id,
|
||||
mode: 'gemini'
|
||||
})
|
||||
functionResponse.response.content = await chosenTool.func(args, this.e)
|
||||
|
|
|
|||
|
|
@ -75,12 +75,12 @@ export function supportGuoba () {
|
|||
bottomHelpMessage: '独立的后台管理面板(默认3321端口),与锅巴类似。工具箱会有额外占用,启动速度稍慢,酌情开启。修改后需重启生效!!!',
|
||||
component: 'Switch'
|
||||
},
|
||||
// {
|
||||
// field: 'enableMd',
|
||||
// label: 'QQ开启markdown',
|
||||
// bottomHelpMessage: 'qq的第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误。默认关闭',
|
||||
// component: 'Switch'
|
||||
// },
|
||||
{
|
||||
field: 'enableToolPrivateSend',
|
||||
label: '允许智能模式私聊',
|
||||
bottomHelpMessage: '是否允许智能模式下发起临时对话骚扰其他群友。默认开启,如果怕Bot乱骚扰其他人可以关闭。主人不受影响。',
|
||||
component: 'Switch'
|
||||
},
|
||||
{
|
||||
field: 'translateSource',
|
||||
label: '翻译来源',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
|
|||
import { SerpImageTool } from '../utils/tools/SearchImageTool.js'
|
||||
import { SearchMusicTool } from '../utils/tools/SearchMusicTool.js'
|
||||
import { SendMusicTool } from '../utils/tools/SendMusicTool.js'
|
||||
import { SendAudioMessageTool } from '../utils/tools/SendAudioMessageTool.js'
|
||||
// import { SendAudioMessageTool } from '../utils/tools/SendAudioMessageTool.js'
|
||||
import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
|
||||
import { QueryGenshinTool } from '../utils/tools/QueryGenshinTool.js'
|
||||
import { WeatherTool } from '../utils/tools/WeatherTool.js'
|
||||
|
|
@ -42,11 +42,11 @@ import { SerpIkechan8370Tool } from '../utils/tools/SerpIkechan8370Tool.js'
|
|||
import { SerpTool } from '../utils/tools/SerpTool.js'
|
||||
import common from '../../../lib/common/common.js'
|
||||
import { SendDiceTool } from '../utils/tools/SendDiceTool.js'
|
||||
import { EliMovieTool } from '../utils/tools/EliMovieTool.js'
|
||||
import { EliMusicTool } from '../utils/tools/EliMusicTool.js'
|
||||
// import { EliMovieTool } from '../utils/tools/EliMovieTool.js'
|
||||
// import { EliMusicTool } from '../utils/tools/EliMusicTool.js'
|
||||
import { HandleMessageMsgTool } from '../utils/tools/HandleMessageMsgTool.js'
|
||||
import { ProcessPictureTool } from '../utils/tools/ProcessPictureTool.js'
|
||||
import { ImageCaptionTool } from '../utils/tools/ImageCaptionTool.js'
|
||||
// import { ImageCaptionTool } from '../utils/tools/ImageCaptionTool.js'
|
||||
import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js'
|
||||
import { newFetch } from '../utils/proxy.js'
|
||||
import { ChatGLM4Client } from '../client/ChatGLM4Client.js'
|
||||
|
|
@ -530,56 +530,10 @@ class Core {
|
|||
option.image = base64Image.toString('base64')
|
||||
}
|
||||
if (opt.enableSmart) {
|
||||
/**
|
||||
* @type {AbstractTool[]}
|
||||
*/
|
||||
let tools = [
|
||||
new QueryStarRailTool(),
|
||||
new WebsiteTool(),
|
||||
new SendPictureTool(),
|
||||
new SendVideoTool(),
|
||||
new SearchVideoTool(),
|
||||
new SendAvatarTool(),
|
||||
new SerpImageTool(),
|
||||
new SearchMusicTool(),
|
||||
new SendMusicTool(),
|
||||
new SendAudioMessageTool(),
|
||||
new APTool(),
|
||||
new SendMessageToSpecificGroupOrUserTool(),
|
||||
new QueryGenshinTool()
|
||||
]
|
||||
if (Config.amapKey) {
|
||||
tools.push(new WeatherTool())
|
||||
}
|
||||
if (e.isGroup) {
|
||||
tools.push(new QueryUserinfoTool())
|
||||
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())
|
||||
}
|
||||
}
|
||||
const {
|
||||
funcMap
|
||||
} = await collectTools(e)
|
||||
let tools = Object.keys(funcMap).map(k => funcMap[k].tool)
|
||||
client.addTools(tools)
|
||||
}
|
||||
let system = opt.system.gemini
|
||||
|
|
@ -821,12 +775,12 @@ async function collectTools (e) {
|
|||
new SetTitleTool()
|
||||
]
|
||||
// todo 3.0再重构tool的插拔和管理
|
||||
let /** @type{AbstractTool} **/ tools = [
|
||||
let /** @type{AbstractTool[]} **/ tools = [
|
||||
new SendAvatarTool(),
|
||||
new SendDiceTool(),
|
||||
new SendMessageToSpecificGroupOrUserTool(),
|
||||
// new EditCardTool(),
|
||||
// new QueryStarRailTool(),
|
||||
new QueryStarRailTool(),
|
||||
new QueryGenshinTool(),
|
||||
new SendMusicTool(),
|
||||
new SearchMusicTool(),
|
||||
|
|
@ -873,13 +827,15 @@ async function collectTools (e) {
|
|||
tools.forEach(tool => {
|
||||
funcMap[tool.name] = {
|
||||
exec: tool.func,
|
||||
function: tool.function()
|
||||
function: tool.function(),
|
||||
tool
|
||||
}
|
||||
})
|
||||
fullTools.forEach(tool => {
|
||||
fullFuncMap[tool.name] = {
|
||||
exec: tool.func,
|
||||
function: tool.function()
|
||||
function: tool.function(),
|
||||
tool
|
||||
}
|
||||
})
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -227,7 +227,8 @@ const defaultConfig = {
|
|||
_2captchaKey: '',
|
||||
bingReasoning: false, // 是否深度思考
|
||||
apiMaxToken: 4096,
|
||||
version: 'v2.8.3'
|
||||
enableToolPrivateSend: true, // 是否允许智能模式下私聊骚扰其他群友。主人不受影响。
|
||||
version: 'v2.8.4'
|
||||
}
|
||||
const _path = process.cwd()
|
||||
let config = {}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class EditCardTool extends AbstractTool {
|
|||
description = 'Useful when you want to edit someone\'s card in the group(群名片)'
|
||||
|
||||
func = async function (opts, e) {
|
||||
let { qq, card, groupId, isAdmin } = opts
|
||||
let { qq, card, groupId, sender, isAdmin } = opts
|
||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||
groupId = isNaN(groupId) || !groupId ? e.group_id : parseInt(groupId.trim())
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ export class EditCardTool extends AbstractTool {
|
|||
logger.error('获取群信息失败,可能使用的底层协议不完善')
|
||||
}
|
||||
logger.info('edit card: ', groupId, qq)
|
||||
if (isAdmin) {
|
||||
if (isAdmin || sender == qq) {
|
||||
await group.setCard(qq, card)
|
||||
} else {
|
||||
return 'the user is not admin, he can\'t edit card of other people.'
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class JinyanTool extends AbstractTool {
|
|||
},
|
||||
time: {
|
||||
type: 'string',
|
||||
description: '禁言时长,单位为秒,默认为600'
|
||||
description: '禁言时长,单位为秒,默认为600。如果需要解除禁言则填0.'
|
||||
},
|
||||
isPunish: {
|
||||
type: 'string',
|
||||
|
|
|
|||
|
|
@ -22,65 +22,29 @@ export class QueryStarRailTool extends AbstractTool {
|
|||
}
|
||||
|
||||
func = async function (opts, e) {
|
||||
let { qq, uid, character } = opts
|
||||
let { qq, uid = '', character = '' } = opts
|
||||
qq = isNaN(qq) || !qq ? e.sender.user_id : parseInt(qq.trim())
|
||||
if (e.at === e.bot.uin) {
|
||||
e.at = null
|
||||
}
|
||||
e.atBot = false
|
||||
|
||||
if (!uid) {
|
||||
try {
|
||||
let { Panel } = await import('../../../StarRail-plugin/apps/panel.js')
|
||||
uid = await redis.get(`STAR_RAILWAY:UID:${qq}`)
|
||||
if (!uid) {
|
||||
return '用户没有绑定uid,无法查询。可以让用户主动提供uid进行查询'
|
||||
}
|
||||
} catch (e) {
|
||||
// todo support miao-plugin and sruid
|
||||
return '未安装StarRail-Plugin,无法查询'
|
||||
}
|
||||
}
|
||||
try {
|
||||
let { Panel } = await import('../../../StarRail-plugin/apps/panel.js')
|
||||
e.msg = character ? `*${character}面板${uid}` : '*更新面板' + uid
|
||||
e.user_id = qq
|
||||
e.isSr = true
|
||||
let panel = new Panel(e)
|
||||
panel.e = e
|
||||
panel.panel(e).catch(e => logger.warn(e))
|
||||
let uidRes = await fetch('https://avocado.wiki/v1/info/' + uid)
|
||||
uidRes = await uidRes.json()
|
||||
let { assistAvatar, displayAvatars } = uidRes.playerDetailInfo
|
||||
function dealAvatar (avatar) {
|
||||
delete avatar.position
|
||||
delete avatar.vo_tag
|
||||
delete avatar.desc
|
||||
delete avatar.promption
|
||||
delete avatar.relics
|
||||
delete avatar.behaviorList
|
||||
delete avatar.images
|
||||
delete avatar.ranks
|
||||
if (avatar.equipment) {
|
||||
avatar.equipment = {
|
||||
level: avatar.equipment.level,
|
||||
rank: avatar.equipment.rank,
|
||||
name: avatar.equipment.name,
|
||||
skill_desc: avatar.equipment.skill_desc
|
||||
}
|
||||
}
|
||||
if (character) {
|
||||
let ProfileDetail = (await import('../../../miao-plugin/apps/profile/ProfileDetail.js')).default
|
||||
// e.msg = `#${character}面板${uid}`
|
||||
e.original_msg = `*${character}面板${uid}`
|
||||
e.user_id = parseInt(qq)
|
||||
e.isSr = true
|
||||
await ProfileDetail.detail(e)
|
||||
return 'the character panel of star rail has been sent to group. you don\'t need text version'
|
||||
} else {
|
||||
let ProfileList = (await import('../../../miao-plugin/apps/profile/ProfileList.js')).default
|
||||
e.msg = `*面板${uid}`
|
||||
e.user_id = qq
|
||||
e.isSr = true
|
||||
await ProfileList.render(e)
|
||||
return 'the player panel of genshin impact has been sent to group. you don\'t need text version'
|
||||
}
|
||||
dealAvatar(assistAvatar)
|
||||
if (displayAvatars) {
|
||||
displayAvatars.forEach(avatar => {
|
||||
dealAvatar(avatar)
|
||||
})
|
||||
}
|
||||
uidRes.playerDetailInfo.assistAvatar = assistAvatar
|
||||
uidRes.playerDetailInfo.displayAvatars = displayAvatars
|
||||
delete uidRes.repository
|
||||
delete uidRes.version
|
||||
return `the player info in json format is: \n${JSON.stringify(uidRes)}`
|
||||
} catch (err) {
|
||||
return `failed to query, error: ${err.toString()}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { AbstractTool } from './AbstractTool.js'
|
||||
import { convertFaces } from '../face.js'
|
||||
import {getMasterQQ} from '../common.js'
|
||||
import {Config} from '../config.js'
|
||||
|
||||
export class SendMessageToSpecificGroupOrUserTool extends AbstractTool {
|
||||
name = 'sendMessage'
|
||||
|
|
@ -19,7 +21,7 @@ export class SendMessageToSpecificGroupOrUserTool extends AbstractTool {
|
|||
}
|
||||
|
||||
func = async function (opt, e) {
|
||||
let { msg, targetGroupIdOrQQNumber } = opt
|
||||
let { msg, sender, targetGroupIdOrQQNumber } = opt
|
||||
const defaultTarget = e.isGroup ? e.group_id : e.sender.user_id
|
||||
const target = isNaN(targetGroupIdOrQQNumber) || !targetGroupIdOrQQNumber
|
||||
? defaultTarget
|
||||
|
|
@ -37,6 +39,10 @@ export class SendMessageToSpecificGroupOrUserTool extends AbstractTool {
|
|||
await group.sendMsg(await convertFaces(msg, true, e))
|
||||
return 'msg has been sent to group' + target
|
||||
} else {
|
||||
let masters = (await getMasterQQ())
|
||||
if (!Config.enableToolPrivateSend && !masters.includes(sender + '')) {
|
||||
return 'you are not allowed to pm other group members'
|
||||
}
|
||||
let user = e.bot.pickUser(target)
|
||||
if (e.group_id) {
|
||||
user = user.asMember(e.group_id)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { AbstractTool } from './AbstractTool.js'
|
||||
import {getMasterQQ} from '../common.js'
|
||||
import {Config} from '../config.js'
|
||||
|
||||
export class SendPictureTool extends AbstractTool {
|
||||
name = 'sendPicture'
|
||||
|
|
@ -18,7 +20,7 @@ export class SendPictureTool extends AbstractTool {
|
|||
}
|
||||
|
||||
func = async function (opt, e) {
|
||||
let { urlOfPicture, targetGroupIdOrQQNumber } = opt
|
||||
let { urlOfPicture, targetGroupIdOrQQNumber, sender } = opt
|
||||
if (typeof urlOfPicture === 'object') {
|
||||
urlOfPicture = urlOfPicture.join(' ')
|
||||
}
|
||||
|
|
@ -55,6 +57,10 @@ export class SendPictureTool extends AbstractTool {
|
|||
// await group.sendMsg(pictures)
|
||||
return 'picture has been sent to group' + target + (errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '')
|
||||
} else {
|
||||
let masters = (await getMasterQQ())
|
||||
if (!Config.enableToolPrivateSend && !masters.includes(sender + '')) {
|
||||
return 'you are not allowed to pm other group members'
|
||||
}
|
||||
let user = e.bot.pickUser(target)
|
||||
if (e.group_id) {
|
||||
user = user.asMember(e.group_id)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ export class SerpIkechan8370Tool extends AbstractTool {
|
|||
},
|
||||
source: {
|
||||
type: 'string',
|
||||
enum: ['google', 'bing', 'baidu']
|
||||
enum: ['google', 'bing', 'baidu', 'duckduckgo'],
|
||||
description: 'search source, default value is bing'
|
||||
}
|
||||
},
|
||||
required: ['q']
|
||||
|
|
@ -19,7 +20,7 @@ export class SerpIkechan8370Tool extends AbstractTool {
|
|||
|
||||
func = async function (opts) {
|
||||
let { q, source } = opts
|
||||
if (!source || !['google', 'bing', 'baidu'].includes(source)) {
|
||||
if (!source || !['google', 'bing', 'baidu', 'duckduckgo'].includes(source)) {
|
||||
source = 'bing'
|
||||
}
|
||||
let serpRes = await fetch(`https://serp.ikechan8370.com/${source}?q=${encodeURIComponent(q)}&lang=zh-CN&limit=5`, {
|
||||
|
|
@ -29,11 +30,11 @@ export class SerpIkechan8370Tool extends AbstractTool {
|
|||
})
|
||||
serpRes = await serpRes.json()
|
||||
|
||||
let res = serpRes.data
|
||||
let res = serpRes.data || serpRes.results
|
||||
res?.forEach(r => {
|
||||
delete r?.rank
|
||||
})
|
||||
return `the search results are here in json format:\n${JSON.stringify(res)}`
|
||||
return `the search results are here in json format:\n${JSON.stringify(res)} \n(Notice that these information are only available for you, the user cannot see them, you next answer should consider about the information)`
|
||||
}
|
||||
|
||||
description = 'Useful when you want to search something from the Internet. If you don\'t know much about the user\'s question, prefer to search about it! If you want to know further details of a result, you can use website tool'
|
||||
|
|
|
|||
|
|
@ -61,48 +61,49 @@ export class WebsiteTool extends AbstractTool {
|
|||
.replace(/\s{2}/g, '') // 多个空格只保留一个空格
|
||||
.replace('<!DOCTYPE html>', '') // 去除<!DOCTYPE>声明
|
||||
|
||||
if (mode === 'gemini') {
|
||||
let client = new CustomGoogleGeminiClient({
|
||||
e,
|
||||
userId: e?.sender?.user_id,
|
||||
key: Config.getGeminiKey(),
|
||||
model: Config.geminiModel,
|
||||
baseUrl: Config.geminiBaseUrl,
|
||||
debug: Config.debug
|
||||
})
|
||||
const htmlContentSummaryRes = await client.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`)
|
||||
let htmlContentSummary = htmlContentSummaryRes.text
|
||||
return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
} else {
|
||||
let maxModelTokens = getMaxModelTokens(Config.model)
|
||||
text = text.slice(0, Math.min(text.length, maxModelTokens - 1600))
|
||||
let completionParams = {
|
||||
// model: Config.model
|
||||
model: 'gpt-3.5-turbo-16k'
|
||||
}
|
||||
let api = new ChatGPTAPI({
|
||||
apiBaseUrl: Config.openAiBaseUrl,
|
||||
apiKey: Config.apiKey,
|
||||
debug: false,
|
||||
completionParams,
|
||||
fetch: (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
return fetch(url, mergedOptions)
|
||||
},
|
||||
maxModelTokens
|
||||
})
|
||||
const htmlContentSummaryRes = await api.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`, { completionParams })
|
||||
let htmlContentSummary = htmlContentSummaryRes.text
|
||||
return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
}
|
||||
// if (mode === 'gemini') {
|
||||
// let client = new CustomGoogleGeminiClient({
|
||||
// e,
|
||||
// userId: e?.sender?.user_id,
|
||||
// key: Config.getGeminiKey(),
|
||||
// model: Config.geminiModel,
|
||||
// baseUrl: Config.geminiBaseUrl,
|
||||
// debug: Config.debug
|
||||
// })
|
||||
// const htmlContentSummaryRes = await client.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`)
|
||||
// let htmlContentSummary = htmlContentSummaryRes.text
|
||||
// return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
// } else {
|
||||
// let maxModelTokens = getMaxModelTokens(Config.model)
|
||||
// text = text.slice(0, Math.min(text.length, maxModelTokens - 1600))
|
||||
// let completionParams = {
|
||||
// // model: Config.model
|
||||
// model: 'gpt-3.5-turbo-16k'
|
||||
// }
|
||||
// let api = new ChatGPTAPI({
|
||||
// apiBaseUrl: Config.openAiBaseUrl,
|
||||
// apiKey: Config.apiKey,
|
||||
// debug: false,
|
||||
// completionParams,
|
||||
// fetch: (url, options = {}) => {
|
||||
// const defaultOptions = Config.proxy
|
||||
// ? {
|
||||
// agent: proxy(Config.proxy)
|
||||
// }
|
||||
// : {}
|
||||
// const mergedOptions = {
|
||||
// ...defaultOptions,
|
||||
// ...options
|
||||
// }
|
||||
// return fetch(url, mergedOptions)
|
||||
// },
|
||||
// maxModelTokens
|
||||
// })
|
||||
// const htmlContentSummaryRes = await api.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`, { completionParams })
|
||||
// let htmlContentSummary = htmlContentSummaryRes.text
|
||||
// return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
// }
|
||||
return `the content of the website is:\n${text}`
|
||||
} catch (err) {
|
||||
return `failed to visit the website, error: ${err.toString()}`
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue