import os import ssl from nonebot import on_command, require, logger from nonebot.adapters.onebot.v11 import MessageEvent from nonebot.plugin import PluginMetadata from hexi.plugins.nonebot_plugin_hexi_core.message_utils import get_reply_message require("nonebot_plugin_alconna") from nonebot_plugin_alconna import UniMessage from nonebot.adapters.onebot.v11.bot import Bot from nonebot.plugin.on import on_keyword import httpx from io import BytesIO from PIL import Image, ImageSequence re_gif = on_keyword({"倒放"}) __plugin_meta__ = PluginMetadata( name="GIF倒放", description="GIF倒放?", usage="倒放+图片", type="application", ) @re_gif.handle() async def rev_gif(event: MessageEvent, bot: Bot): # 情况1,用户对需要倒放的gif进行回复 messages = get_reply_message(event) await match_revgif(messages) async def match_revgif(messages): img_urls = [] for message in messages: logger.info(f"遍历消息: {message}") # 只处理图片消息 if message.type != "image": continue url = message.data.get("url") if url: img_urls.append(url) # 没有图片直接返回 if not img_urls: logger.info("消息中没有图片") return for url in img_urls: logger.info(f"获取到的图片链接:{url}") await do_revgif(img_urls) ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS) fd = os.path.dirname(__file__) async def do_revgif(image_urls): gif_img = [] async with httpx.AsyncClient(timeout=30) as client: for image_url in image_urls: try: resp = await client.get(image_url) resp.raise_for_status() image = Image.open(BytesIO(resp.content)) # 不是gif或只有1帧 if getattr(image, "n_frames", 1) == 1: continue gif_img.append(image) except Exception as e: logger.warning(f"下载或解析图片失败: {e}") if not gif_img: await UniMessage.text("没有发现GIF图片").send() return suit_gifs = [] for gif in gif_img: if gif.n_frames <= 400: suit_gifs.append(gif) if not suit_gifs: await UniMessage.text("GIF帧数太多了,懒得倒放😅").send() return suit_image = suit_gifs[0] sequence = [f.copy() for f in ImageSequence.Iterator(suit_image)] logger.info(f"GIF帧数: {len(sequence)}") if len(sequence) <= 2: await UniMessage.text("写入文件时发生未知错误").send() return await UniMessage.text("ℹ正在翻转图片序列,请稍候").send() sequence.reverse() bio = BytesIO() sequence[0].save( bio, format="GIF", save_all=True, append_images=sequence[1:], disposal=1, loop=0 ) bio.seek(0) await UniMessage.image(raw=bio.getvalue()).send() def image_to_bytes(image: Image.Image, input_format: str) -> bytes: with BytesIO() as bio: image.save(bio, format=input_format) return bio.getvalue()