# Hmeme-img **Repository Path**: fox-glaze/hmeme-img ## Basic Information - **Project Name**: Hmeme-img - **Description**: Hmeme 的表情合成资源库 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2025-12-27 - **Last Updated**: 2026-01-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # H-Meme 表情开发文档 ## ⚠️ 前置要求 本插件依赖 **FFmpeg**。 请确保服务器/电脑已安装 FFmpeg 并配置了环境变量。 - **Windows**: 下载 `ffmpeg.exe` 并将其所在目录添加到系统 Path 变量中。 - **Linux**: 运行 `sudo apt install ffmpeg` 或 `yum install ffmpeg`。 --- ## 📂 推荐目录结构 建议保持以下目录结构,以确保 `Utils.js` 能正确加载音频资源: ```text /plugins/HMeme ├── index.js <-- 插件主入口 (工具箱 handler) ├── lib/ │ └── Utils.js <-- 核心工具类 ├── resources/ │ └── audio/ <-- 音频素材 (所有 .mp3 必须放这里) ├── memes/ │ ├── [表情ID]/ <-- 英文或拼音,唯一标识 │ │ ├── index.js <-- 具体的表情逻辑代码 │ │ └── resources/ <-- 该表情专用的图片素材 │ └── ... ``` --- ## 🛠️ 快速开发模版 在 `memes` 目录下新建一个文件夹(例如 `demo`),并在其中创建 `index.js`。 ```javascript import path from 'path' import sharp from 'sharp' // 1. 注册指令配置 export const config = { name: '测试表情', // 插件显示的名称 reg: '^#?(测试|test)$', // 触发正则 priority: 50 // 优先级 } /** * 2. 处理函数 * @param {Object} e - Yunzai 的消息事件对象 * @param {Object} utils - Utils 工具类实例 (已注入) * @param {String} resourceDir - 当前表情的资源目录绝对路径 */ export async function handler(e, utils, resourceDir) { // [步骤1] 获取目标信息 // 自动识别优先级: 引用回复图片 > 消息内带图 > @用户 > 自己 const { targetId, isUrl } = await utils.getMentionedInfo(e) try { // [步骤2] 下载目标头像或图片 // 返回的是 Buffer const imgBuffer = await utils.downloadAvatar(targetId) // 示例 A: 生成静态图或 GIF // 使用 sharp 处理图片 const processed = await sharp(imgBuffer) .resize(200, 200) .grayscale() // 变灰 .toBuffer() // 发送图片 (utils.saveAndSend 会自动处理 Buffer) // 第三个参数是临时文件前缀,用于调试 await utils.saveAndSend(e, processed, 'demo_img') // 示例 B: 生成带音频的视频 // 假设你有一个 GIF URL,想把它变成视频并配上 resources/audio/ji.mp3 // 1. 先下载 GIF 到临时文件 // const gifPath = await utils.downloadToTemp(targetId, '.gif') // 2. 转换视频 (自动循环画面以匹配音频长度) // const videoPath = await utils.convertGifToMp4(gifPath, 'ji.mp3') // 3. 发送视频 (传入路径,utils 会自动识别 mp4 后缀发送视频消息) // await utils.saveAndSend(e, videoPath, 'demo_video') return true } catch (err) { logger.error(`[H-Meme] 合成失败:`, err) e.reply('生成失败,请查看后台日志') return false } } ``` --- ## Utils 工具类 API 参考 `handler` 函数的第二个参数 `utils` 提供了所有核心能力 ### 1. 信息与下载 #### `async utils.getMentionedInfo(e)` 获取目标信息。 * **返回**: `{ selfId, targetId, isSelf, isUrl }` #### `async utils.downloadAvatar(target)` 下载图片或头像。 * **target**: QQ号 (Number/String) 或 图片URL (String)。 * **返回**: `Promise` (下载失败会返回一张灰色占位图) #### `async utils.downloadToTemp(url, ext = '.gif')` 下载文件并保存到临时目录。 * **ext**: 文件后缀。 * **返回**: `Promise` (临时文件的绝对路径) --- ### 2. 图片处理 #### `async utils.processAvatar(buffer, width, height, rotation)` 头像标准化处理:切圆、缩放、旋转。 * **返回**: `Promise` #### `async utils.getGifFrames(buffer)` 将图片拆解为帧数组。 * **功能**: 如果是 GIF,返回所有帧;如果是静态图,返回单帧数组。 * **返回**: `Promise` #### `async utils.generateGif(frames, delay, repeat)` 将多帧图片合成为 GIF。 * **frames**: `Buffer[]` * **delay**: 帧间隔 (毫秒)。 * **repeat**: `0` (无限循环), `-1` (不循环)。 * **返回**: `Promise` (已修复类型问题,返回 Node Buffer) #### `async utils.textToImage(text, width, options)` 文字转图片,支持自动换行 * **options**: `{ fontSize: 30 }` * **返回**: `Promise<{ buffer: Buffer, height: Number }>` --- ### 3. 视频与音频 #### `async utils.convertGifToMp4(gifPath, audioName)` 将 GIF 转为 MP4,支持音频合成与画面循环。 * **gifPath**: GIF 文件的本地路径。 * **audioName**: `resources/audio` 目录下的文件名 (如 `小萝莉.mp3`)。 * 若为 `null`,生成无声视频。 * 若有值,会自动计算音频时长,将视频画面循环播放以对齐音频,并使用 `libx264` 重新编码以确保兼容性。 * **返回**: `Promise` (MP4 文件路径) #### `async utils.convertGifToLongImage(gifPath)` 将 GIF 拆解并纵向拼成一张长图。 * **返回**: `Promise<{ longImgPath, frameCount }>` #### `utils.getAudioList()` 获取 `resources/audio` 目录下的所有 `.mp3` 文件名 * **返回**: `String[]` --- ### 4. 发送与清理 #### `async utils.saveAndSend(e, data, prefix)` 统一的发送接口,自动处理保存和类型识别。 * **参数**: * `e`: 消息事件对象 * `data`: * 如果是 `Buffer` 或 `Uint8Array` -> 保存为图片/GIF 发送。 * 如果是 `String` (路径) 且以 `.mp4` 结尾 -> 发送**视频**。 * 如果是 `String` (路径) -> 发送**图片**。 * `prefix`: 临时文件名前缀,避免冲突。 * **清理**: 发送完成后,文件会保留 60秒,之后自动删除。 * **返回**: `Promise` (最终的文件路径)