chatgpt-plugin/utils/tools/ImageTool.js

273 lines
No EOL
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AbstractTool } from './AbstractTool.js'
import { getToimg, downImg, fileImgList } from '../../utils/ToDoimg.js'
export class ImageProcessTool extends AbstractTool {
name = 'imageProcess'
#availableImages = []
#initialized = false
#currentImageIndex = 0
#totalImages = 0
#needReprocess = false
parameters = {
properties: {
action: {
type: 'string',
enum: ['get', 'download', 'notImage', 'listImages'],
description: 'Action to perform: get (local image), download (save image), notImage (switch mode), or listImages'
},
imageName: {
type: 'string',
description: 'Name or tag of the image'
},
imageData: {
type: 'string',
description: 'Base64 image data for download action'
}
},
required: ['action']
}
constructor(e, previousRole, bymGo) {
super()
this.e = e
this.previousRole = previousRole
this.bymGo = bymGo
this.initializeImageList()
}
async func(opts) {
const { action, imageName, imageData } = opts
try {
switch (action) {
case 'get':
const getResult = await this.handleGetImage(imageName)
return getResult
case 'download':
const downloadResult = await this.handleDownloadImage(imageName, imageData)
return downloadResult
case 'notImage':
const notImageResult = await this.handleNotImage()
return notImageResult
case 'listImages':
return this.getImagesPrompt() // 直接返回列表文本,让模型知道有哪些表情包
default:
throw new Error(`未知操作: ${action}`)
}
} catch (error) {
throw error
}
}
async initializeImageList() {
try {
this.#availableImages = await fileImgList()
this.#initialized = true
} catch (error) {
this.#availableImages = []
}
}
async getAvailableImages() {
if (!this.#initialized) {
await this.initializeImageList()
}
return this.#availableImages
}
async getImagesPrompt() {
const images = await this.getAvailableImages()
if (images.length === 0) {
return '当前没有可用的表情包'
}
return `可用的表情包列表(共 ${images.length} 个):\n${images.join('\n')}\n\n使用方法:发送 GETIMG: <表情包名称> 来使用表情包`
}
async processText(text, options = {}) {
// 初始化或重置图片处理信息
if (options.images?.length > 0) {
this.#totalImages = options.images.length
// 只在新的处理周期时重置索引
if (!this.#needReprocess && this.#currentImageIndex >= this.#totalImages) {
this.#currentImageIndex = 0
}
} else if (options.image) {
this.#totalImages = 1
if (!this.#needReprocess) {
this.#currentImageIndex = 0
}
}
const commands = {
get: {
regex: /GETIMG:\s*([\s\S]+?)\s*$/i,
handler: async (match) => {
const imageName = match[1].trim()
await this.handleGetImage(imageName)
return true
}
},
notImage: {
regex: /NOTIMG(.*)/i,
handler: async (match) => {
const result = await this.handleNotImage()
if (result.success) {
// 标记需要重新处理当前图片
this.#needReprocess = true
return {
success: true,
continueProcess: true,
currentIndex: this.#currentImageIndex, // 保持当前索引不变
switchRole: result.switchRole,
needResponse: result.needResponse,
reprocess: true // 添加重新处理标记
}
}
return true
}
},
download: {
regex: /DOWNIMG:\s*(.+)/i,
handler: async (match) => {
const baseName = match[1].trim()
if (options.images) {
const imageName = this.#totalImages === 1
? baseName
: `${baseName}_${this.#currentImageIndex + 1}`
await this.handleDownloadImage(imageName, options.images[this.#currentImageIndex])
// 处理完当前图片后,重置重新处理标记
this.#needReprocess = false
// 只有在不是重新处理时才增加索引
if (this.#currentImageIndex < this.#totalImages - 1) {
this.#currentImageIndex++
return {
success: true,
continueProcess: true,
currentIndex: this.#currentImageIndex
}
}
} else if (options.image) {
await this.handleDownloadImage(baseName, options.image)
}
return true
}
},
list: {
regex: /^(?:表情包列表|查看表情包|列出表情包)$/i,
handler: async () => {
await this.e.reply(await this.getImagesPrompt())
return true
}
}
}
for (const [type, { regex, handler }] of Object.entries(commands)) {
const match = text.match(regex)
if (match) {
try {
const result = await handler(match)
return result
} catch (error) {
await this.e.reply(`处理失败: ${error.message}`)
return true
}
}
}
return null
}
async handleGetImage(imageName) {
if (!imageName) {
throw new Error('需要指定表情包名称')
}
try {
const result = await getToimg(this.e, imageName)
if (result === undefined) {
await this.e.reply(`未找到匹配的表情包: ${imageName}`)
}
return true // 表示已处理
} catch (error) {
throw error
}
}
async handleDownloadImage(imageName, imageData) {
if (!imageName || !imageData) {
throw new Error('需要提供图片名称和数据')
}
try {
const text = `DOWNIMG: ${imageName}`
await downImg(this.e, imageData, text)
await this.initializeImageList()
return true
} catch (error) {
throw error
}
}
async handleNotImage() {
try {
return {
success: true,
switchRole: this.previousRole,
needResponse: true
}
} catch (error) {
throw error
}
}
getCurrentProgress() {
return {
currentIndex: this.#currentImageIndex,
totalImages: this.#totalImages,
isProcessing: this.#currentImageIndex < this.#totalImages
}
}
description = `图片处理工具:支持发送本地表情包、保存新表情包、切换处理模式。
可用命令:
- GETIMG: <表情包名称> - 发送指定表情包
- DOWNIMG: <名称> - 保存当前图片为表情包
- NOTIMG - 切换到文本模式
- 表情包列表 - 查看所有可用表情包
\n\n多图片处理说明:
- 当处理多张图片时,会自动为每张图片添加序号后缀
- 例如DOWNIMG: happy 命令处理多张图片时会保存为 happy_1, happy_2 等\n`
// 添加 getSystemPrompt 方法
async getSystemPrompt() {
const images = await this.getAvailableImages()
const progress = this.getCurrentProgress()
let prompt = `${this.description}\n`
if (progress.isProcessing) {
prompt += `\n当前正在处理第 ${progress.currentIndex + 1}/${progress.totalImages} 张图片\n`
}
if (images.length > 0) {
prompt += `\n当前可用的表情包:\n${images.join('\n')}`
prompt += `\n使用 GETIMG: <表情包名称> 来发送表情包`
} else {
prompt += '\n当前没有可用的表情包'
}
return prompt
}
}
export async function initializeImageTool(e, previousRole, bymGo) {
const tool = new ImageProcessTool(e, previousRole, bymGo)
await tool.initializeImageList() // 确保初始化完成
return tool
}