mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 05:47:11 +00:00
添加伪人图片重发机制,尝试适配伪人在onebot协议发送音乐卡片
This commit is contained in:
parent
b4032881da
commit
b5e82ff9d5
2 changed files with 344 additions and 40 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import { AbstractTool } from './AbstractTool.js'
|
import { AbstractTool } from './AbstractTool.js'
|
||||||
|
import https from 'https'
|
||||||
|
|
||||||
export class SendMusicTool extends AbstractTool {
|
export class SendMusicTool extends AbstractTool {
|
||||||
name = 'sendMusic'
|
name = 'sendMusic'
|
||||||
|
|
@ -17,6 +18,61 @@ export class SendMusicTool extends AbstractTool {
|
||||||
required: ['id']
|
required: ['id']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取歌曲详情
|
||||||
|
getSongDetail(id) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const options = {
|
||||||
|
hostname: 'music.163.com',
|
||||||
|
path: `/api/song/detail/?id=${id}&ids=[${id}]`,
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||||
|
'Referer': 'https://music.163.com/',
|
||||||
|
'Origin': 'https://music.163.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = ''
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk
|
||||||
|
})
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(data)
|
||||||
|
if (result.code === 200 && result.songs && result.songs[0]) {
|
||||||
|
const song = result.songs[0]
|
||||||
|
resolve({
|
||||||
|
name: song.name,
|
||||||
|
artist: song.artists[0].name,
|
||||||
|
album: song.album.name,
|
||||||
|
picUrl: song.album.picUrl,
|
||||||
|
duration: Math.floor(song.duration / 1000), // 转换为秒
|
||||||
|
quality: {
|
||||||
|
sq: song.sqMusic ? '无损' : null,
|
||||||
|
hq: song.hMusic ? '高品质' : null,
|
||||||
|
lq: song.lMusic ? '标准' : null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析歌曲详情失败:', e)
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
req.on('error', (e) => {
|
||||||
|
console.error('获取歌曲详情失败:', e)
|
||||||
|
resolve(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
req.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func = async function (opts, e) {
|
func = async function (opts, e) {
|
||||||
let { id, targetGroupIdOrQQNumber } = opts
|
let { id, targetGroupIdOrQQNumber } = opts
|
||||||
// 非法值则发送到当前群聊
|
// 非法值则发送到当前群聊
|
||||||
|
|
@ -29,16 +85,35 @@ export class SendMusicTool extends AbstractTool {
|
||||||
let group = await e.bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
|
|
||||||
// 检查是否支持 shareMusic 方法
|
// 检查是否支持 shareMusic 方法
|
||||||
if (typeof group.shareMusic === 'function') {
|
if (typeof group.shareMusic === 'function' && e.adapter_name === 'icqq') {
|
||||||
await group.shareMusic('163', id)
|
await group.shareMusic('163', id)
|
||||||
} else {
|
} else {
|
||||||
|
// 获取歌曲详情
|
||||||
|
const songDetail = await this.getSongDetail(id)
|
||||||
|
|
||||||
// 构建音乐分享消息
|
// 构建音乐分享消息
|
||||||
const musicMsg = {
|
let musicMsg
|
||||||
type: 'music',
|
if (e.adapter_name === 'OneBotv11') {
|
||||||
data: {
|
// 适配onebotv11协议
|
||||||
type: '163',
|
musicMsg = [{
|
||||||
id: id,
|
type: 'music',
|
||||||
jumpUrl: `https://music.163.com/#/song?id=${id}`
|
data: {
|
||||||
|
type: 'custom',
|
||||||
|
url: `https://music.163.com/#/song?id=${id}`,
|
||||||
|
audio: `http://music.163.com/song/media/outer/url?id=${id}.mp3`,
|
||||||
|
title: songDetail ? `${songDetail.name} - ${songDetail.artist}` : '网易云音乐',
|
||||||
|
image: songDetail?.picUrl || 'https://p1.music.126.net/tBTNafgjNnTL1KlZMt7lVA==/18885211718935735.jpg'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
// 原有格式
|
||||||
|
musicMsg = {
|
||||||
|
type: 'music',
|
||||||
|
data: {
|
||||||
|
type: '163',
|
||||||
|
id: id,
|
||||||
|
jumpUrl: `https://music.163.com/#/song?id=${id}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await e.reply(musicMsg)
|
await e.reply(musicMsg)
|
||||||
|
|
@ -50,4 +125,4 @@ export class SendMusicTool extends AbstractTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
description = 'Useful when you want to share music. You must use searchMusic first to get the music id. If no extra description needed, just reply <EMPTY> at the next turn'
|
description = 'Useful when you want to share music. You must use searchMusic first to get the music id. If no extra description needed, just reply <EMPTY> at the next turn'
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,37 @@ import {Config} from '../config.js'
|
||||||
export class SendPictureTool extends AbstractTool {
|
export class SendPictureTool extends AbstractTool {
|
||||||
name = 'sendPicture'
|
name = 'sendPicture'
|
||||||
|
|
||||||
|
// 解析 CQ 码的方法
|
||||||
|
parseCQCode(message) {
|
||||||
|
const cqRegex = /\[CQ:([^,]+)(?:,([^\]]+))?\]/g
|
||||||
|
const result = []
|
||||||
|
let match
|
||||||
|
|
||||||
|
while ((match = cqRegex.exec(message)) !== null) {
|
||||||
|
const type = match[1]
|
||||||
|
const params = {}
|
||||||
|
|
||||||
|
if (match[2]) {
|
||||||
|
match[2].split(',').forEach(param => {
|
||||||
|
const [key, ...values] = param.split('=')
|
||||||
|
if (key && values.length) {
|
||||||
|
params[key] = values.join('=')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'image') {
|
||||||
|
result.push(params.url || params.file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
parameters = {
|
parameters = {
|
||||||
properties: {
|
properties: {
|
||||||
urlOfPicture: {
|
urlOfPicture: {
|
||||||
|
|
@ -21,64 +52,262 @@ export class SendPictureTool extends AbstractTool {
|
||||||
|
|
||||||
func = async function (opt, e) {
|
func = async function (opt, e) {
|
||||||
let { urlOfPicture, targetGroupIdOrQQNumber, sender } = opt
|
let { urlOfPicture, targetGroupIdOrQQNumber, sender } = opt
|
||||||
if (typeof urlOfPicture === 'object') {
|
|
||||||
urlOfPicture = urlOfPicture.join(' ')
|
// 处理数组格式的消息体
|
||||||
|
if (typeof urlOfPicture === 'string') {
|
||||||
|
try {
|
||||||
|
// 尝试解析为 JSON
|
||||||
|
const msgArray = JSON.parse(urlOfPicture)
|
||||||
|
if (Array.isArray(msgArray)) {
|
||||||
|
// 处理消息数组
|
||||||
|
const messages = []
|
||||||
|
for (const msg of msgArray) {
|
||||||
|
if (typeof msg === 'object' && msg.type === 'text' && msg.data?.text) {
|
||||||
|
if (msg.data.text.includes('[CQ:image')) {
|
||||||
|
// 提取 CQ 码中的参数
|
||||||
|
const cqMatch = msg.data.text.match(/\[CQ:image,([^\]]+)\]/)
|
||||||
|
if (cqMatch) {
|
||||||
|
const params = {}
|
||||||
|
cqMatch[1].split(',').forEach(param => {
|
||||||
|
const [key, ...values] = param.split('=')
|
||||||
|
if (key && values.length) {
|
||||||
|
params[key] = values.join('=')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 创建正确的图片消息格式
|
||||||
|
messages.push({
|
||||||
|
type: 'image',
|
||||||
|
data: {
|
||||||
|
file: params.url || params.file,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 保留普通文本消息
|
||||||
|
messages.push(msg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messages.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 只保留图片消息
|
||||||
|
const imageMsg = messages.find(msg => msg.type === 'image')
|
||||||
|
if (imageMsg) {
|
||||||
|
urlOfPicture = imageMsg.data.file
|
||||||
|
} else {
|
||||||
|
return 'No valid image found in the message'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 如果不是JSON格式,尝试解析 CQ 码
|
||||||
|
if (urlOfPicture.includes('[CQ:image')) {
|
||||||
|
const cqMatch = urlOfPicture.match(/\[CQ:image,([^\]]+)\]/)
|
||||||
|
if (cqMatch) {
|
||||||
|
const params = {}
|
||||||
|
cqMatch[1].split(',').forEach(param => {
|
||||||
|
const [key, ...values] = param.split('=')
|
||||||
|
if (key && values.length) {
|
||||||
|
params[key] = values.join('=')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
urlOfPicture = params.url || params.file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof urlOfPicture === 'object') {
|
||||||
|
if (Array.isArray(urlOfPicture)) {
|
||||||
|
// 处理消息数组
|
||||||
|
const messages = []
|
||||||
|
for (const msg of urlOfPicture) {
|
||||||
|
if (typeof msg === 'object' && msg.type === 'text' && msg.data?.text) {
|
||||||
|
if (msg.data.text.includes('[CQ:image')) {
|
||||||
|
// 提取 CQ 码中的参数
|
||||||
|
const cqMatch = msg.data.text.match(/\[CQ:image,([^\]]+)\]/)
|
||||||
|
if (cqMatch) {
|
||||||
|
const params = {}
|
||||||
|
cqMatch[1].split(',').forEach(param => {
|
||||||
|
const [key, ...values] = param.split('=')
|
||||||
|
if (key && values.length) {
|
||||||
|
params[key] = values.join('=')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 创建正确的图片消息格式
|
||||||
|
messages.push({
|
||||||
|
type: 'image',
|
||||||
|
data: {
|
||||||
|
file: params.url || params.file,
|
||||||
|
...params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 保留普通文本消息
|
||||||
|
messages.push(msg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messages.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 只保留图片消息
|
||||||
|
const imageMsg = messages.find(msg => msg.type === 'image')
|
||||||
|
if (imageMsg) {
|
||||||
|
urlOfPicture = imageMsg.data.file
|
||||||
|
} else {
|
||||||
|
return 'No valid image found in the message'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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
|
||||||
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
: parseInt(targetGroupIdOrQQNumber) === e.bot.uin ? defaultTarget : parseInt(targetGroupIdOrQQNumber)
|
||||||
|
|
||||||
// 处理错误url和picture留空的情况
|
// 处理错误url和picture留空的情况
|
||||||
const urlRegex = /(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((?:(?:[a-z0-9\u00a1-\u4dff\u9fd0-\uffff][a-z0-9\u00a1-\u4dff\u9fd0-\uffff_-]{0,62})?[a-z0-9\u00a1-\u4dff\u9fd0-\uffff]\.)+(?:[a-z\u00a1-\u4dff\u9fd0-\uffff]{2,}\.?))(?::\d{2,5})?)(?:\/[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),%]+)*(?:\?(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*(),%:@&=]|(?:[\[\]])|(?:[\u00a1-\u4dff\u9fd0-\uffff]))*)?(?:#(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),;:@&=]|(?:[\[\]]))*)?\/?/i
|
const urlRegex = /(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((?:(?:[a-z0-9\u00a1-\u4dff\u9fd0-\uffff][a-z0-9\u00a1-\u4dff\u9fd0-\uffff_-]{0,62})?[a-z0-9\u00a1-\u4dff\u9fd0-\uffff]\.)+(?:[a-z\u00a1-\u4dff\u9fd0-\uffff]{2,}\.?))(?::\d{2,5})?)(?:\/[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),%]+)*(?:\?(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*(),%:@&=]|(?:[\[\]])|(?:[\u00a1-\u4dff\u9fd0-\uffff]))*)?(?:#(?:[\w\u00a1-\u4dff\u9fd0-\uffff$-_.+!*'(),;:@&=]|(?:[\[\]]))*)?\/?/i
|
||||||
if (/https:\/\/example.com/.test(urlOfPicture) || !urlOfPicture || !urlRegex.test(urlOfPicture)) urlOfPicture = ''
|
|
||||||
if (!urlOfPicture) {
|
if (!urlOfPicture) {
|
||||||
return 'Because there is no correct URL for the picture ,tell user the reason and ask user if he want to use SearchImageTool'
|
return 'No picture URL provided'
|
||||||
}
|
}
|
||||||
let pictures = urlOfPicture.trim().split(' ')
|
|
||||||
logger.mark('pictures to send: ', pictures)
|
// 处理多个URL的情况
|
||||||
pictures = pictures.map(img => segment.image(img))
|
let urls = urlOfPicture.split(/\s+/).filter(url => {
|
||||||
let groupList
|
return urlRegex.test(url)
|
||||||
try {
|
})
|
||||||
groupList = await e.bot.getGroupList()
|
|
||||||
} catch (err) {
|
if (urls.length === 0) {
|
||||||
groupList = e.bot.gl
|
return 'No valid picture URLs found'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.mark('pictures to send: ', urls)
|
||||||
|
let pictures = urls.map(img => {
|
||||||
|
// 检测适配器类型
|
||||||
|
const isICQQ = !!(e.bot?.adapter?.name === 'icqq')
|
||||||
|
|
||||||
|
if (img.startsWith('http')) {
|
||||||
|
if (isICQQ) {
|
||||||
|
// ICQQ适配器使用segment
|
||||||
|
return segment.image(img)
|
||||||
|
} else {
|
||||||
|
// OneBotv11适配器使用对象格式
|
||||||
|
return {
|
||||||
|
type: 'image',
|
||||||
|
data: {
|
||||||
|
file: img,
|
||||||
|
cache: false, // 禁用缓存
|
||||||
|
proxy: false, // 禁用代理
|
||||||
|
timeout: 60000, // 60秒超时
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
|
'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return segment.image(img)
|
||||||
|
})
|
||||||
|
|
||||||
let errs = []
|
let errs = []
|
||||||
try {
|
try {
|
||||||
if ((typeof groupList.get === 'function' && groupList.get(target)) ||
|
// 获取群列表
|
||||||
(Array.isArray(groupList) && groupList.includes(target))) {
|
let groupList = await e.bot?.getGroupList?.() || e.bot.gl || []
|
||||||
|
|
||||||
|
// 发送到群
|
||||||
|
if (target.toString().length >= 6) { // 群号一般大于6位
|
||||||
let group = await e.bot.pickGroup(target)
|
let group = await e.bot.pickGroup(target)
|
||||||
|
if (!group) {
|
||||||
|
return `Failed to find group: ${target}`
|
||||||
|
}
|
||||||
|
|
||||||
for (let pic of pictures) {
|
for (let pic of pictures) {
|
||||||
try {
|
let retryCount = 0
|
||||||
await group.sendMsg(pic)
|
const maxRetries = 3
|
||||||
} catch (err) {
|
let lastError = null
|
||||||
errs.push(pic)
|
|
||||||
|
while (retryCount < maxRetries) {
|
||||||
|
try {
|
||||||
|
// 根据适配器类型选择发送方式
|
||||||
|
const isICQQ = !!(e.bot?.adapter?.name === 'icqq')
|
||||||
|
const msgToSend = isICQQ ? pic : [pic]
|
||||||
|
|
||||||
|
// 设置超时Promise
|
||||||
|
const sendPromise = group.sendMsg(msgToSend)
|
||||||
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
|
setTimeout(() => reject(new Error('Send timeout')), 30000)
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.race([sendPromise, timeoutPromise])
|
||||||
|
logger.mark(`图片发送成功: ${typeof pic === 'string' ? pic : pic.data?.file}`)
|
||||||
|
break
|
||||||
|
} catch (err) {
|
||||||
|
lastError = err
|
||||||
|
retryCount++
|
||||||
|
const isTimeout = err.message?.includes('ETIMEDOUT') || err.message?.includes('timeout')
|
||||||
|
logger.error(`发送图片失败 (尝试 ${retryCount}/${maxRetries}): ${isTimeout ? '连接超时' : err.message}`)
|
||||||
|
|
||||||
|
if (retryCount === maxRetries) {
|
||||||
|
errs.push(typeof pic === 'string' ? pic : (pic.data?.file || pic.url || '未知图片'))
|
||||||
|
logger.error('图片发送最终失败:', lastError.message || lastError)
|
||||||
|
} else {
|
||||||
|
// 根据错误类型调整等待时间
|
||||||
|
const waitTime = isTimeout ? 5000 * retryCount : 3000 * retryCount
|
||||||
|
await new Promise(resolve => setTimeout(resolve, waitTime))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// await group.sendMsg(pictures)
|
|
||||||
return 'picture has been sent to group' + target + (errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '')
|
if (errs.length > 0) {
|
||||||
} else {
|
return `部分图片发送失败 (${errs.length}/${pictures.length}): ${errs.join(', ')}`
|
||||||
let masters = (await getMasterQQ())
|
|
||||||
if (!Config.enableToolPrivateSend && !masters.includes(sender + '')) {
|
|
||||||
return 'you are not allowed to pm other group members'
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 发送到私聊
|
||||||
|
else {
|
||||||
|
let masters = await getMasterQQ()
|
||||||
|
if (!Config.enableToolPrivateSend && !masters.includes(sender.toString())) {
|
||||||
|
return 'You are not allowed to send private messages'
|
||||||
|
}
|
||||||
|
|
||||||
let user = e.bot.pickUser(target)
|
let user = e.bot.pickUser(target)
|
||||||
if (e.group_id) {
|
|
||||||
user = user.asMember(e.group_id)
|
|
||||||
}
|
|
||||||
for (let pic of pictures) {
|
for (let pic of pictures) {
|
||||||
try {
|
try {
|
||||||
await user.sendMsg(pic)
|
// 根据适配器类型选择发送方式
|
||||||
|
const isICQQ = !!(e.bot?.adapter?.name === 'icqq')
|
||||||
|
const msgToSend = isICQQ ? pic : [pic]
|
||||||
|
|
||||||
|
await user.sendMsg(msgToSend)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errs.push(pic.url)
|
logger.error('发送图片失败:', err)
|
||||||
|
errs.push(typeof pic === 'string' ? pic : (pic.data?.file || pic.url || '未知图片'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'picture has been sent to user' + target + (errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '')
|
return `Pictures have been sent to user ${target}${errs.length > 0 ? `, but ${errs.length} pictures failed to send` : ''}`
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return `failed to send pictures, error: ${JSON.stringify(err)}`
|
logger.error('发送图片过程出错:', err)
|
||||||
|
return `Failed to send pictures: ${err.message || JSON.stringify(err)}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
description = 'Useful when you want to send one or more pictures. If no extra description needed, just reply <EMPTY> at the next turn'
|
description = 'Useful when you want to send one or more pictures. If no extra description needed, just reply <EMPTY> at the next turn'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue