UNPKG

koishi-plugin-emojihub-bili

Version:

[<ruby>Emojihub-bili<rp>(</rp><rt>点我查看食用方法</rt><rp>)</rp></ruby>](https://www.npmjs.com/package/koishi-plugin-emojihub-bili)🐱支持QQ官方json按钮!20群即可发按钮!😍可自定义添加或删除任意表情包!基本每个指令都有上千张!支持定时/定量消息触发![点我查看食用方法](https://www.npmjs.com/package/koishi-plugin-emojihub-bi

275 lines (248 loc) 9.5 kB
import fs from "node:fs"; import path from "node:path"; import { type Context } from "koishi"; import { Config } from "./config"; import { logError, logInfoformat, getAllFiles, getVirtualFilename, logger } from "./utils"; import { isHttpUrl, resolveLocalPath } from "./path"; import { getLocalImageProxyUrl } from "./media-proxy"; export interface ImageResult { imageUrl: string | null; isLocal: boolean; imageName?: string; imageTime?: Date; imageSize?: number; imagePath?: string; } function isImageFile(filePath: string) { const lower = filePath.toLowerCase(); return lower.endsWith(".jpg") || lower.endsWith(".png") || lower.endsWith(".gif") || lower.endsWith(".bmp") || lower.endsWith(".webp"); } function isAbsoluteLikePath(input: string) { return path.isAbsolute(input) || path.win32.isAbsolute(input) || path.posix.isAbsolute(input); } function toLocalImageUrl(ctx: Context, localPath: string): string | null { return getLocalImageProxyUrl(ctx, localPath); } export async function getImageAsBase64(imagePath: string) { try { const filePath = resolveLocalPath(imagePath) ?? imagePath; const imageBuffer = fs.readFileSync(filePath); return imageBuffer.toString("base64"); } catch (error) { logger.error("Error converting image to base64:", error); return null; } } export async function determineImagePath( txtPath: string, config: Config, channelId: string | null, command: string, ctx: Context, local_picture_name: string[] | null = null, ): Promise<ImageResult> { const localPath = resolveLocalPath(txtPath); if (localPath) { try { const stats = fs.lstatSync(localPath); if (stats.isDirectory()) { return await getRandomImageFromFolder(localPath, config, channelId, command, ctx, local_picture_name); } if (stats.isFile()) { if (isImageFile(localPath)) { logInfoformat(config, channelId, command, `本地图片的绝对路径: ${txtPath}`); const imageUrl = toLocalImageUrl(ctx, localPath); if (!imageUrl) return { imageUrl: null, isLocal: false }; return { imageUrl, isLocal: true, imageName: path.basename(localPath), imageTime: stats.mtime, imageSize: stats.size, imagePath: localPath, }; } if (localPath.toLowerCase().endsWith(".txt")) { return await getRandomImageUrlFromFile(localPath, config, channelId, command, ctx); } } } catch { return { imageUrl: null, isLocal: false }; } } if (isHttpUrl(txtPath)) { logInfoformat(config, channelId, command, `直接的图片链接 ${txtPath}`); return { imageUrl: txtPath, isLocal: false }; } const allValidPaths = getAllValidPaths(config); if (allValidPaths.length === 0) { return { imageUrl: null, isLocal: false }; } const pickedPath = allValidPaths[Math.floor(Math.random() * allValidPaths.length)]; const pickedLocalPath = resolveLocalPath(pickedPath); if (!pickedLocalPath) return { imageUrl: null, isLocal: false }; try { const stats = fs.lstatSync(pickedLocalPath); if (stats.isDirectory()) { return await getRandomImageFromFolder(pickedLocalPath, config, channelId, command, ctx, local_picture_name); } if (stats.isFile()) { if (isImageFile(pickedLocalPath)) { logInfoformat(config, channelId, command, `随机选择的本地图片路径: ${pickedPath}`); const imageUrl = toLocalImageUrl(ctx, pickedLocalPath); if (!imageUrl) return { imageUrl: null, isLocal: false }; return { imageUrl, isLocal: true, imageName: path.basename(pickedLocalPath), imageTime: stats.mtime, imageSize: stats.size, imagePath: pickedLocalPath, }; } if (pickedLocalPath.toLowerCase().endsWith(".txt")) { return await getRandomImageUrlFromFile(pickedLocalPath, config, channelId, command, ctx); } } } catch { return { imageUrl: null, isLocal: false }; } return { imageUrl: null, isLocal: false }; } export function getRandomEmojiHubCommand(config: Config) { const commands = config.MoreEmojiHubList.map((emoji) => emoji.command); if (commands.length > 0) { return commands[Math.floor(Math.random() * commands.length)]; } return null; } function getAllValidPaths(config: Config): string[] { return config.MoreEmojiHubList.filter((emoji) => { const sourceUrl = emoji.source_url; return isAbsoluteLikePath(sourceUrl) || isHttpUrl(sourceUrl) || sourceUrl.startsWith("file:"); }).map((emoji) => emoji.source_url); } async function getRandomImageFromFolder( folderPath: string, config: Config, channelId: string | null, command: string, ctx: Context, local_picture_name: string[] | null, ) { if (!fs.existsSync(folderPath)) { logError(`错误:路径不存在: ${folderPath}`); return { imageUrl: null, isLocal: false }; } let files = config.searchSubfolders ? getAllFiles(folderPath) : fs.readdirSync(folderPath).map((file) => path.join(folderPath, file)); files = files.filter((file) => isImageFile(file)); if (files.length === 0) { logError("文件夹中未找到有效图片文件(jpg,png,gif,webp,bmp)"); return { imageUrl: null, isLocal: false }; } if (local_picture_name?.length > 0) { files = files.filter((file) => { let filenameToMatch; if (config.searchSubfoldersWithfilename && config.searchSubfolders) { filenameToMatch = getVirtualFilename(file, folderPath); } else { filenameToMatch = path.basename(file); } const filenameLower = filenameToMatch.toLowerCase(); return local_picture_name.every((keyword) => filenameLower.includes(keyword.toLowerCase())); }); if (files.length === 0) { logError(`未找到匹配关键词 "${local_picture_name.join(" ")}" 的图片文件`); return { imageUrl: null, isLocal: false }; } } const imagePath = files[Math.floor(Math.random() * files.length)]; logInfoformat(config, channelId, command, `使用文件夹 ${folderPath} \n发送本地图片为 ${imagePath}`); const stats = fs.statSync(imagePath); const imageUrl = toLocalImageUrl(ctx, imagePath); if (!imageUrl) return { imageUrl: null, isLocal: false }; return { imageUrl, isLocal: true, imageName: path.basename(imagePath), imageTime: stats.mtime, imageSize: stats.size, imagePath, }; } export async function getRandomImageUrlFromFile( txtPath: string, config: Config, channelId: string | null, command: string, ctx: Context, ) { let urls: string[]; let imageUrl: string; try { urls = fs.readFileSync(txtPath, "utf8").split("\n").filter((url) => url.trim() !== ""); } catch (error) { if (error instanceof Error && error.message === "ENOENT") { return { imageUrl: null, isLocal: false }; } logError(error); return { imageUrl: null, isLocal: false }; } if (urls.length === 0) { logError(`错误!无有效URL可用:${txtPath}`); return { imageUrl: null, isLocal: false }; } const index = Math.floor(Math.random() * urls.length); let txtUrl = urls[index].trim(); const extraPrefix = "https:"; const bilibiliPrefix = "https://i0.hdslb.com/bfs/"; if (txtUrl.startsWith(extraPrefix + bilibiliPrefix)) { txtUrl = txtUrl.replace(extraPrefix, ""); } if (!txtUrl.startsWith("https://") && !txtUrl.startsWith("http://")) { txtUrl = bilibiliPrefix + txtUrl; } imageUrl = txtUrl.trim(); if (config.LocalSendNetworkPicturesList && config.LocalSendNetworkPicturesList.length > 0) { const normalizedList = config.LocalSendNetworkPicturesList.split(/\n|,|,/).map((item) => item.trim().toLowerCase()); const lowerCaseCommand = command.toLowerCase(); if (normalizedList.includes(lowerCaseCommand)) { const outputPath = path.join(__dirname, `${Date.now()}.png`); try { const localOutputPath = await downloadImage(txtUrl, outputPath, ctx); ctx.setTimeout(() => { fs.unlinkSync(localOutputPath); logInfoformat(config, null, null, `临时文件已删除:${localOutputPath}`); }, config.deletePictime * 1000); logInfoformat(config, channelId, command, `下载并发送本地图片: ${localOutputPath}`); const localImageUrl = toLocalImageUrl(ctx, localOutputPath); return { imageUrl: localImageUrl, isLocal: true, imagePath: localOutputPath }; } catch (downloadError) { logError(`图片下载失败:${downloadError.message}`); return { imageUrl: null, isLocal: false }; } } } logInfoformat(config, channelId, command, `使用文件 ${txtPath} \n发送URL为:${imageUrl}`); return { imageUrl, isLocal: false }; } export async function downloadImage(url: string, outputPath: string, ctx: Context) { try { const response = await ctx.http.get(url, { responseType: "arraybuffer" }); const buffer = Buffer.from(response); await fs.promises.writeFile(outputPath, buffer); return outputPath; } catch (error) { logError(`下载图片失败: ${error.message}`); throw error; } } export function listAllCommands(config: Config): string[] { const allCommands = config.MoreEmojiHubList.map((emoji) => emoji.command); if (allCommands.length === 0) { logError("未找到任何表情包指令。"); } return allCommands; }