//PLUGIN wiki.ts
import { param, plugins, runcod } from '../lib/decorators.js';
import 'reflect-metadata';
import { GroupMessage, PrivateFriendMessage, PrivateGroupMessage, Receive } from 'node-napcat-ts';
import { execSync } from 'child_process'
import fs from 'fs'
import path from 'path';
import { createWorker } from 'tesseract.js';
import { fileURLToPath } from 'url';
import { PSM } from 'tesseract.js';
import { Jimp, loadFont } from 'jimp'; // 假设未引入，添加引入语句
import sharp from 'sharp';

@plugins({
    easycmd: true,
    name: "枫叶秋林的明日方舟工具箱",
    version: "0.0.1",
    describe: "日常制作不出许多有趣好玩的工具箱",
    author: "枫叶秋林",
    help: {
        enabled: true,
        description: "查看帮助信息"
    }
})
export class wiki {

    @runcod(["干员", "查询干员"], `干员bilibiliWiki截图`)
    async browser(
        @param("干员名称", 'text') name: Receive["text"],
        @param("内容信息", 'text', { type: 'text', data: { text: '' } }, true) element: Receive["text"]
    ) {
        let sandbox = ''
        switch (element?.data?.text) {
            case '评论':
                sandbox = '#flowthread'
                break;
            case '语音':
                sandbox = '::-p-xpath(/html/body/div[2]/div[2]/div[4]/div[5]/div/div[27])'
                break;
            case '档案':
                sandbox = '::-p-xpath(/html/body/div[2]/div[2]/div[4]/div[5]/div/div[24])'
                break;
            case '模组':
                sandbox = '::-p-xpath(/html/body/div[2]/div[2]/div[4]/div[5]/div/div[21])'
                break;
            case '基建':
                sandbox = '::-p-xpath(/html/body/div[2]/div[2]/div[4]/div[5]/div/div[19])'
                break;
            case '技能材料':
                sandbox = '::-p-xpath(/html/body/div[2]/div[2]/div[4]/div[5]/div/div[17])'
            default:
                sandbox = ''
        }
        return {
            selector: sandbox,
            template: { // 模板配置，用于发送图片内容
                enabled: true,//是否启用模板，启用将发送图片内容
                sendText: false,//是否发送文本，启用将发送文本内容，如果都启用则发送两条消息
                render: {//浏览器默认参数设置，用于打开浏览器的设置
                    width: 600, // 模板宽度
                    height: 1, // 模板高度
                    type: 'png',// 模板类型
                    quality: 100,// 模板质量
                    fullPage: false,// 是否全屏
                    background: true,
                    url: `https://wiki.biligame.com/arknights/${name?.data?.text}`// 模板路径，推荐按规范放置在resources目录下
                }
            },
            toString() { //重写toString方法，用于返回文本内容，启用sendText时将发送文本内容，不启用时将发送图片内容，图片发送失败时发送文字内容
                return `访问${name?.data?.text}`;
            }
        }
    }

    @runcod(['方舟智能点击', '智能点击'], `会自动识别文字，安卓模拟器的操作方舟点击操作，优先使用创建的点击步骤`)
    async rig(
        @param("点击文本", 'text') targetText: Receive["text"],
        @param("x偏移", 'text', { type: 'text', data: { text: '' } }, true) x1: Receive["text"],
        @param("y偏移", 'text', { type: 'text', data: { text: '' } }, true) y1: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        if (!targetText?.data?.text) {
            return '请输入文本'
        }
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        const tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const newtempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        try {
            const { adbPath, deviceList } = this.getadb();
            const buffer = await this.getimg(adbPath, deviceList, tempFile);
            if (!buffer) {
                return;
            }

            const readxy = await this.readxy(targetText?.data?.text)
            let x = 0
            let y = 0
            let img = ''
            let txst = ''
            if (readxy) {
                x = readxy.x1;
                y = readxy.y1;
                txst = '记录结果'
                img = buffer.toString('base64');
            } else {
                const data = await this.recognizeTextPosition(tempFile, targetText.data.text);
                x = data.x
                y = data.y
                txst = data.txst
                img = data.img
            }
            if (targetText?.data?.text) {
                if (targetText?.data?.text == '任意') {
                    const x = Math.floor(Math.random() * 1000) + 1;
                    const y = Math.floor(Math.random() * 1000) + 1;
                    execSync(`${adbPath} -s ${deviceList[0]} shell input tap ${x} ${y}`);
                } else {
                    if (x1?.data?.text || y1?.data?.text) {
                        execSync(`${adbPath} -s ${deviceList[0]} shell input tap ${x + Number(x1?.data?.text ?? 0)} ${y + Number(y1?.data?.text ?? 0)}`);
                    } else {
                        execSync(`${adbPath} -s ${deviceList[0]} shell input tap ${x} ${y}`);
                    }
                }
                await new Promise((resolve) => setTimeout(resolve, 4000));
                const buffer = await this.getimg(adbPath, deviceList, newtempFile);
                if (!buffer) {
                    return;
                }
                const base64Data = buffer.toString('base64');
                context.quick_action([{
                    type: 'text',
                    data: {
                        text: `识别结果：x:${x},y:${y}(${txst})`
                    },
                }, {
                    type: 'image',
                    data: {
                        file: `base64://${img}`
                    }
                }, {
                    type: 'image',
                    data: {
                        file: `base64://${base64Data}`
                    }
                }]);
            }
            return '截图成功';
        } catch (error) {
            console.error('[ADB错误]', error);
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
            const errorMsg = error instanceof Error && error.message
            return errorMsg;
        } finally {
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
        }
    }
    @runcod(['方舟滑动', '滑动'], `滑动屏幕`)
    async huadong(
        @param("name", 'text') name: Receive["text"],
        @param("type", 'text', { type: 'text', data: { text: '' } }, true) type: Receive["text"],
        @param("距离", 'text', { type: 'text', data: { text: '' } }, true) distance: Receive["text"],
        @param("X", 'text', { type: 'text', data: { text: '' } }, true) x1: Receive["text"],
        @param("y", 'text', { type: 'text', data: { text: '' } }, true) y1: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        if (!name?.data?.text) { return; }
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        const tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const newtempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const { adbPath, deviceList } = this.getadb();
        const xy = await this.readxy(name?.data?.text)
        if (xy) {
            if (!x1?.data?.text || !y1?.data?.text) {
                execSync(`${adbPath} -s ${deviceList[0]} shell input swipe ${xy.x1} ${xy.y1} ${xy.x2} ${xy.y2}`);
            } else {
                if (xy.x1 != Number(x1?.data?.text) || xy.y1 != Number(y1?.data?.text) || xy.type != type?.data?.text) {
                    try {
                        let x2 = 0
                        let y2 = 0
                        switch (type?.data?.text) {
                            case '上':
                                x2 = Number(x1.data.text)
                                y2 = Number(y1.data.text) + Number(distance?.data?.text ?? 100)
                                break;
                            case '下':
                                x2 = Number(x1.data.text)
                                y2 = Number(y1.data.text) - Number(distance?.data?.text ?? 100)
                                break;
                            case '左':
                                x2 = Number(x1.data.text) + Number(distance?.data?.text ?? 100)
                                y2 = Number(y1.data.text)
                                break;
                            case '右':
                                x2 = Number(x1.data.text) - Number(distance?.data?.text ?? 100)
                                y2 = Number(y1.data.text)
                                break;
                            default:
                                return '请输入滑动方向'
                        }
                        execSync(`${adbPath} -s ${deviceList[0]} shell input swipe ${x1.data.text} ${y1.data.text} ${x2} ${y2}`);
                        this.savetest(name?.data?.text, 'slide', Number(x1?.data?.text), Number(y1?.data?.text), x2, y2)
                        await new Promise((resolve) => setTimeout(resolve, 4000));
                    }
                    catch (error) {
                        console.error('[ADB错误]', error);
                        if (fs.existsSync(tempFile)) {
                            fs.unlinkSync(tempFile);
                        }
                        if (fs.existsSync(newtempFile)) {
                            fs.unlinkSync(newtempFile);
                        }
                        const errorMsg = error instanceof Error && error.message
                        return errorMsg;
                    }
                }
            }
        } else {
            if (!x1?.data?.text || !y1?.data?.text) {
                return '请输入坐标';
            }
            try {
                let x2 = 0
                let y2 = 0
                switch (type?.data?.text) {
                    case '上':
                        x2 = Number(x1.data.text)
                        y2 = Number(y1.data.text) - Number(distance?.data?.text ?? 100)
                        break;
                    case '下':
                        x2 = Number(x1.data.text)
                        y2 = Number(y1.data.text) + Number(distance?.data?.text ?? 100)
                        break;
                    case '左':
                        x2 = Number(x1.data.text) - Number(distance?.data?.text ?? 100)
                        y2 = Number(y1.data.text)
                        break;
                    case '右':
                        x2 = Number(x1.data.text) + Number(distance?.data?.text ?? 100)
                        y2 = Number(y1.data.text)
                        break;
                    default:
                        return '请输入滑动方向'
                }
                execSync(`${adbPath} -s ${deviceList[0]} shell input swipe ${x1.data.text} ${y1.data.text} ${x2} ${y2}`);
                this.savetest(name?.data?.text, 'slide', Number(x1?.data?.text), Number(y1?.data?.text), x2, y2)
                await new Promise((resolve) => setTimeout(resolve, 4000));
            }
            catch (error) {
                console.error('[ADB错误]', error);
                if (fs.existsSync(tempFile)) {
                    fs.unlinkSync(tempFile);
                }
                if (fs.existsSync(newtempFile)) {
                    fs.unlinkSync(newtempFile);
                }
                const errorMsg = error instanceof Error && error.message
                return errorMsg;
            }
        }
        const JGbuffer = await this.getimg(adbPath, deviceList, newtempFile);
        if (!JGbuffer) { return; }
        const base64Data = JGbuffer.toString('base64');
        context.quick_action([
            {
                type: 'image',
                data: {
                    file: `base64://${base64Data}`
                }
            }])
        if (fs.existsSync(tempFile)) {
            fs.unlinkSync(tempFile);
        }
        if (fs.existsSync(newtempFile)) {
            fs.unlinkSync(newtempFile);
        }
        return '操作成功';
    }
    @runcod(['方舟输入', '输入'], `输入文本`)
    async input(
        @param("输入文本", 'text') text: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        const tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const newtempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        try {
            const { adbPath, deviceList } = this.getadb();
            const buffer = await this.getimg(adbPath, deviceList, tempFile);
            if (!buffer) {
                return;
            }
            fs.writeFileSync(tempFile, buffer);
            if (text?.data?.text) {
                // 使用Base64编码处理中文
                const encodedText = text.data.text || "";
                const CMD = `${adbPath} -s ${deviceList[0]} shell am broadcast -a ADB_INPUT_TEXT --es msg "${encodedText}"`
                execSync(CMD);
                const buffer = await this.getimg(adbPath, deviceList, newtempFile);
                if (!buffer) {
                    return;
                }
                const base64Data = buffer.toString('base64');
                context.quick_action([{
                    type: 'text',
                    data: {
                        text: `记录结果：${text.data.text}`
                    },
                }, {
                    type: 'image',
                    data: {
                        file: `base64://${base64Data}`
                    }
                }
                ])
            }
        }
        catch (error) {
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
            console.error('[ADB错误]', error);
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
        } finally {
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
        }
    }

    @runcod(['方舟点击', '点击'], `创建一个点击步骤`)
    async rig2(
        @param("点击文本", 'text') targetText: Receive["text"],
        @param("x偏移", 'text', { type: 'text', data: { text: '' } }, true) x1: Receive["text"],
        @param("y偏移", 'text', { type: 'text', data: { text: '' } }, true) y1: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        const tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const newtempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        try {
            const { adbPath, deviceList } = this.getadb();
            const buffer = await this.getimg(adbPath, deviceList, tempFile);
            if (!buffer) {
                return;
            }
            fs.writeFileSync(tempFile, buffer);
            if (targetText?.data?.text) {
                execSync(`${adbPath} -s ${deviceList[0]} shell input tap ${x1.data.text} ${y1.data.text}`);
                const buffer = await this.getimg(adbPath, deviceList, newtempFile);
                if (!buffer) {
                    return;
                }
                const base64Data = buffer.toString('base64');
                await new Promise((resolve) => setTimeout(resolve, 4000));
                context.quick_action([{
                    type: 'text',
                    data: {
                        text: `记录结果：x:${x1.data.text},y:${y1.data.text}(${targetText.data.text})\n 图片记录`
                    },
                }, {
                    type: 'image',
                    data: {
                        file: `base64://${base64Data}`
                    }
                }]);
                this.savetest(targetText.data.text, 'cilik', Number(x1.data.text), Number(y1.data.text));
            }
            return '操作成功';
        } catch (error) {
            console.error('[ADB错误]', error);
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
            const errorMsg = error instanceof Error && error.message
            return errorMsg;
        } finally {
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
        }
    }

    @runcod(['步骤', '查看步骤'], `查看创建的步骤`)
    async list(
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'test.json');
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
        }
        let str = ''
        for (const key in data) {
            if (data.hasOwnProperty(key)) {
                const element = data[key];
                str += `${key}(${element.type ?? 'click'}):x:${element.x1},y:${element.y1}\n`;
            }
        }
        return str;
    }
    @runcod(['步骤集', '查看步骤集'], `查看创建的步骤集`)
    async lists() {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'tests.json');
        let str = ''
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
        }
        let i = 1;
        for (const key in data) {
            const v = await this.readtests(key)
            if (v) {
                str += `${i++}.${key}:`
                for (const i in v) {
                    str += `${v[i].name}(${v[i].type}),`
                }
            }
        }
        return str;
    }
    @runcod(['创建步骤集'], `创建步骤集`)
    async createlist(
        @param("步骤集名称", 'text') testname: Receive["text"],
        @param("步骤,例子：步骤名称1:类别,步骤名称2:类别", 'text') texts: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        let str = ''
        const tests = texts?.data?.text?.split(',')
        const datatest: {
            name: string;
            type: 'input' | 'click' | 'slide';
        }[] = [];
        if (tests) {
            for (const test of tests) {
                const [name, type] = test.split(':');
                if (type == 'input') {
                    datatest.push({ type, name });
                } else if (type == 'click') {
                    const xy = await this.readxy(name)
                    if (xy) {
                        datatest.push({ type, name });
                    } else {
                        str += `步骤${name}未找到,请创建\n`
                    }
                } else {
                    str += `步骤${name}未找到,请创建\n`
                }

            }
            this.createtests(testname.data.text, datatest);
            str += `步骤集${testname.data.text}创建成功\n`
        }
        return str;
    }
    @runcod(['执行步骤集', 'runtests'], `执行步骤集`)
    async runtest(
        @param("步骤集名称", 'text') name: Receive["text"],
        @param("步骤,例子：输入值1:输入值2:输入值3:", 'text', { type: 'text', data: { text: '' } }, true) input: Receive["text"],
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        if (!name?.data?.text) {
            return '步骤集名称不能为空';
        }
        if (input?.data?.text == '') {
            return '输入值不能为空';
        }
        let str = ''
        const inputs = input?.data?.text?.split(':')
        const tests = await this.readtests(name.data.text)
        const { adbPath, deviceList } = this.getadb();
        if (tests) {
            for (const test of tests) {
                if (test.type == 'input') {
                    if (inputs) {
                        const input = inputs.shift()
                        if (input) {
                            const CMD = `${adbPath} -s ${deviceList[0]} shell am broadcast -a ADB_INPUT_TEXT --es msg "${input}"`
                            execSync(CMD);
                            await new Promise((resolve) => setTimeout(resolve, 3000));
                            str += `步骤${test.name}执行成功 类别:${test.type}输入值:${input}\n`
                        }
                    }
                } else if (test.type == 'click') {
                    const readxy = await this.readxy(test.name)
                    let cmd = `${adbPath} -s ${deviceList[0]} shell input tap ${readxy?.x1 ?? 0} ${readxy?.y1 ?? 0}`
                    execSync(cmd);
                    str += `步骤${test.name}执行成功 类别:${test.type}(x:${readxy?.x1 ?? 0},y:${readxy?.y1 ?? 0})\n`
                    await new Promise((resolve) => setTimeout(resolve, 3000));
                } else if (test.type == 'slide') {
                    const readxy = await this.readxy(test.name)
                    let cmd = `${adbPath} -s ${deviceList[0]} shell input swipe ${readxy?.x1 ?? 0} ${readxy?.y1 ?? 0} ${readxy?.x2 ?? 0} ${readxy?.y2 ?? 0}`
                    execSync(cmd);
                    str += `步骤${test.name}执行成功 类别:${test.type}(x:${readxy?.x1 ?? 0},y:${readxy?.y1 ?? 0},x2:${readxy?.x2 ?? 0},y2:${readxy?.y2 ?? 0})\n`
                    await new Promise((resolve) => setTimeout(resolve, 3000));
                }
            }
            await this.tu(context);
            return str;
        }
        return str ?? '步骤集不存在';
    }
    @runcod(['方舟状态', '截图', '状态'], `当前方舟运行状态`)
    async tu(
        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) {
        if (!context) {
            return;
        }
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        const tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        const newtempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        try {
            const { adbPath, deviceList } = this.getadb();
            const buffer = await this.getimg(adbPath, deviceList, tempFile);
            if (!buffer) {
                return;
            }
            fs.writeFileSync(tempFile, buffer);
            const GRID = (await this.drawGrid(tempFile))
            const base64Data = buffer.toString('base64');
            context.quick_action([{
                type: 'image',
                data: {
                    file: `base64://${base64Data}`
                }
            },
            {
                type: 'image',
                data: {
                    file: `base64://${GRID}`
                }
            }]);
            return '截图成功';
        } catch (error) {
            console.error('[ADB错误]', error);
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
            const errorMsg = error instanceof Error && error.message
            return errorMsg;
        } finally {
            if (fs.existsSync(tempFile)) {
                fs.unlinkSync(tempFile);
            }
            if (fs.existsSync(newtempFile)) {
                fs.unlinkSync(newtempFile);
            }
        }
    }
    @runcod(['红点识别', '点击'], `根据点击的红点识别参数`)
    async recognizeText(@param("识别图片", 'image') image: Receive["image"],
                        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage
    ) { 
        if (!image?.data?.url) {
            return '图片不能为空';
        }
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        let tempDir = path.resolve(__dirname, '..', '..', 'botQQ_screenshots');
        let tempFile = path.join(tempDir, `screenshot_${Date.now()}.png`);
        await this.downloadFile(image.data.url, tempFile);
        const redDot = await this.findRedDot(tempFile);
        if (redDot) {
            fs.unlinkSync(tempFile);
            const { adbPath, deviceList } = this.getadb();
            execSync(`${adbPath} -s ${deviceList[0]} shell input tap ${redDot[0].x} ${redDot[0].y}`);
            await new Promise((resolve) => setTimeout(resolve, 2000));
            await this.tu(context);
            return `识别成功`;
        }
        return '识别失败';
    }
    //获取安卓模拟器设备
    getadb() {
        const adbPath = '/opt/homebrew/bin/adb';
        // 获取有效设备列表
        const devicesOutput = execSync(`${adbPath} devices`).toString();
        const deviceList = devicesOutput.split('\n')
            .slice(1)
            .filter(line => line.trim().endsWith('device'))
            .map(line => line.split('\t')[0]);

        if (deviceList.length === 0) {
            throw new Error('未检测到已连接的安卓设备');
        }
        return {
            adbPath,
            deviceList
        };
    }
    async getimg(adbPath: string, deviceList: string[], tempFile: string) {
        if (deviceList.length === 0) {
            return
        }
        if (!adbPath) {
            return
        }
        execSync(`${adbPath} -s ${deviceList[0]} exec-out screencap -p > ${tempFile}`);
        const readStream = fs.createReadStream(tempFile);
        const chunks = [];
        for await (const chunk of readStream) {
            chunks.push(chunk);
        }
        return Buffer.concat(chunks);
    }
    private async recognizeTextPosition(imagePath: string, text: string): Promise<{ x: number; y: number, img: string, txst: string }> {
        if (!imagePath || !text) {
            return { x: 0, y: 0, img: '', txst: '' };
        }
        try {
            const worker = await createWorker();
            await worker.load('chi_sim');
            await worker.reinitialize('chi_sim');

            await worker.setParameters({
                user_defined_dpi: '320',
                tessedit_char_whitelist: text,
                tessedit_pageseg_mode: PSM.SINGLE_BLOCK

            });
            //识别文本未知
            const { data } = await worker.recognize(imagePath, {

            }, { blocks: true });

            if (!data || data.blocks?.length !== 1) {
                throw new Error('未找到文本块');
            }
            const target = data.blocks[0];


            await worker.terminate();

            if (!target) {
                throw new Error('未找到目标文本');
            }
            let words: any = []
            let txst = '';
            target.paragraphs.forEach((paragraph) => {
                paragraph.lines.forEach((line) => {
                    line.words.forEach((word) => {
                        words.push({
                            x0: word.bbox.x0,
                            x1: word.bbox.x1,
                            y0: word.bbox.y0,
                            y1: word.bbox.y1,
                            text: txst += `文本：${line.text}（${Math.floor(word.bbox.x0 + (word.bbox.x1 - word.bbox.x0) / 2)},${Math.floor(word.bbox.y0 + (word.bbox.y1 - word.bbox.y0) / 2)}）\n`
                        })
                    });
                });
            })
            const x = Math.floor(words[0].x0 + (words[0].x1 - words[0].x0) / 2)
            const y = Math.floor(words[0].y0 + (words[0].y1 - words[0].y0) / 2)
            return { x, y, img: await this.drawCircle(imagePath, x, y), txst };
        } catch (error) {
            throw new Error(`识别失败：${error instanceof Error ? error.message : '请检查OCR依赖安装'}`);
        }
    }
    //绘制圆形标记
    private async drawCircle(imagePath: string, x: number, y: number): Promise<string> {
        if (!imagePath) {
            return '';
        }
        const image = await Jimp.read(imagePath);
        const marker = new Jimp({
            width: 50,
            height: 50,
            color: 0x00000000
        });
        marker.scan(0, 0, marker.bitmap.width, marker.bitmap.height, (x, y, idx) => {
            const dx = x - 25;
            const dy = y - 25;
            const distance = Math.sqrt(dx * dx + dy * dy);
            if (distance <= 25) {
                marker.bitmap.data[idx + 3] = 255;
            }
        });
        marker.circle({ radius: 5, x: 25, y: 25 });
        image.composite(marker, x - 10, y - 10);
        const img = await image.getBase64('image/png');
        return img.split(',')[1];
    }
    // 判断圆形标记位置
    async findRedDot(imagePath: string): Promise<{ x: number, y: number }[]> {
        // 读取图像数据
        if (!imagePath) {
            return [];
        }
        const { data, info } = await sharp(imagePath)
            .raw()
            .toBuffer({ resolveWithObject: true });

        const width = info.width;
        const height = info.height;
        const channels = info.channels;
        const pixels = new Uint8Array(data.buffer);

        const redDots: { x: number, y: number }[] = [];

        // 定义红色的阈值（可以根据需要调整）
        const isRed = (r: number, g: number, b: number) => {
            return r > 200 && g < 50 && b < 50;
        };

        // 检查5x5区域是否是圆形红点
        const isRedDot = (x: number, y: number) => {
            // 检查中心点必须是红色
            if (!isPixelRed(x, y)) return false;

            // 检查5x5区域
            let redCount = 0;
            const radius = 2; // 5x5区域的半径

            for (let dy = -radius; dy <= radius; dy++) {
                for (let dx = -radius; dx <= radius; dx++) {
                    // 圆形检查：只考虑在半径内的点
                    if (dx * dx + dy * dy <= radius * radius) {
                        if (isPixelRed(x + dx, y + dy)) {
                            redCount++;
                        }
                    }
                }
            }

            // 如果红色像素足够多，则认为是红点
            return redCount >= 10; // 5x5圆形区域大约有13个像素
        };

        // 检查单个像素是否是红色
        const isPixelRed = (x: number, y: number) => {
            if (x < 0 || x >= width || y < 0 || y >= height) return false;
            const idx = (y * width + x) * channels;
            const r = pixels[idx];
            const g = pixels[idx + 1];
            const b = pixels[idx + 2];
            return isRed(r, g, b);
        };

        // 扫描图像寻找红点
        for (let y = 2; y < height - 2; y++) {
            for (let x = 2; x < width - 2; x++) {
                if (isRedDot(x, y)) {
                    // 避免重复检测邻近的点
                    if (!redDots.some(dot => Math.abs(dot.x - x) < 5 && Math.abs(dot.y - y) < 5)) {
                        redDots.push({ x, y });
                    }
                }
            }
        }

        return redDots;
    }
    //下载文件
    private async downloadFile(url: string, path: string) {
        if (!url) {
            return;
        }
        if (!path) {
            return;
        }
        const response = await fetch(url);
        const arrayBuffer = await response.arrayBuffer();
        const buffer = Buffer.from(arrayBuffer);
        fs.writeFileSync(path, buffer);
    }
    //绘制网格
    private async drawGrid(imagePath: string): Promise<string> {
        if (!imagePath) {
            return '';
        }
        const image = await Jimp.read(imagePath);
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const tempDir = path.resolve(__dirname, '..', 'font', 'open-sans-8-white.fnt');
        const font = await loadFont(tempDir);
        const width = image.bitmap.width;
        const height = image.bitmap.height;
        const gridSize = 50;
        for (let x = 0; x < width; x += gridSize) {
            if (typeof Jimp !== 'undefined' && image instanceof Jimp) {
                image.scan(x, 0, 1, height, (xScan, yScan, idx) => {
                    image.bitmap.data[idx + 0] = 0;   // R
                    image.bitmap.data[idx + 1] = 0;   // G
                    image.bitmap.data[idx + 2] = 0;   // B
                    image.bitmap.data[idx + 3] = 255; // A
                });
            }
        }
        for (let y = 0; y < height; y += gridSize) {
            // 绘制水平线
            if (typeof Jimp !== 'undefined' && image instanceof Jimp) {
                image.scan(0, y, width, 1, (xScan, yScan, idx) => {
                    image.bitmap.data[idx + 0] = 0;   // R
                    image.bitmap.data[idx + 1] = 0;   // G
                    image.bitmap.data[idx + 2] = 0;   // B
                    image.bitmap.data[idx + 3] = 255; // A  
                })
            }
        }
        // 线两段标注数字
        for (let x = 0; x < width; x += gridSize) {
            for (let y = 0; y < height; y += gridSize) {
                if (typeof Jimp !== 'undefined' && image instanceof Jimp) {
                    if (font) {
                        image.print({ font, x: x - 10, y: y - 10, text: `${x},${y}` });
                    }
                }
            }
        }

        const img = await image.getBase64('image/png');
        return img.split(',')[1];
    }
    //保存步骤记录
    private async savetest(test: string, type: 'cilik' | 'slide', x1: number, y1: number, x2?: number, y2?: number): Promise<void> {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        //json
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'test.json');
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
        } else {
            fs.writeFileSync(filePath, JSON.stringify(data));
        }
        if (data[test]) {
            switch (type) {
                case 'cilik':
                    data[test].x1 = x1
                    data[test].y1 = y1
                    break;
                case 'slide':
                    data[test].x1 = x1
                    data[test].y1 = y1
                    data[test].x2 = x2
                    data[test].y2 = y2
            }
        } else {
            switch (type) {
                case 'cilik':
                    data[test] = { type, x1, y1 }
                    break;
                case 'slide':
                    data[test] = { type, x1, y1, x2, y2 }
            }

        }
        fs.writeFileSync(filePath, JSON.stringify(data));
        return;
    }
    //读取步骤记录
    private async readxy(test: string): Promise<{ type: 'cilik' | 'slide', x1: number, y1: number, x2?: number, y2?: number } | undefined> {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'test.json');
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
        }
        if (data[test]) {
            return data[test] as { type: 'cilik' | 'slide', x1: number, y1: number, x2?: number, y2?: number };
        }
    }
    //创建步骤记录
    private async createtests(name: string, test: {
        name: string;
        type: 'input' | 'click' | 'slide';

    }[]): Promise<void> {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'tests.json');
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
        }
        data[name] = test
        fs.writeFileSync(filePath, JSON.stringify(data));
        return;
    }
    //读取步骤记录
    private async readtests(name: string): Promise<{
        name: string;
        type: 'input' | 'click' | 'slide';
    }[] | undefined> {
        const __dirname = path.dirname(fileURLToPath(import.meta.url));
        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'tests.json');
        let data: any = {};
        if (fs.existsSync(filePath)) {
            const fileContent = fs.readFileSync(filePath, 'utf-8');
            data = JSON.parse(fileContent);
            if (data[name]) {
                return data[name];
            }
        }
        if (data[name]) {
            return data[name];
        }
    }
}

