feat: 添加多线程架构支持并优化性能

实现线程管理器以支持高并发场景,添加GIL-free模式提升Python 3.14下的多线程性能
新增B站API集成和本地文件服务器功能,改进镜像插件支持GIF处理
更新文档说明多线程架构和GIL-free模式的使用方法
This commit is contained in:
2026-03-01 16:01:51 +08:00
parent 734c112ee4
commit ff4a4d92a5
20 changed files with 2071 additions and 317 deletions

View File

@@ -2,12 +2,12 @@
镜像头像插件
提供 /镜像 指令,将@的用户头像或用户发送的图片处理成轴对称图形。
支持普通图片和 GIF 动画。
"""
from core.managers.command_manager import matcher
from core.bot import Bot
from models.events.message import MessageEvent
from core.permission import Permission
from PIL import Image
from PIL import Image, ImageSequence
import io
import aiohttp
import base64
@@ -16,7 +16,7 @@ import asyncio
__plugin_meta__ = {
"name": "mirror_avatar",
"description": "将用户头像或图片处理成轴对称图形",
"usage": "/镜像 @人 - 将@的用户头像处理成轴对称图形\n/镜像 - 等待用户发送图片进行镜像处理",
"usage": "/镜像 @人 - 将@的用户头像处理成轴对称图形\n/镜像 gif - 将@的用户头像处理成轴对称GIF动画\n/镜像 - 等待用户发送图片进行镜像处理",
}
# 存储等待图片的用户信息
@@ -71,7 +71,6 @@ def process_avatar(image_bytes: bytes) -> bytes:
# 分割图片为左右两部分
left_half = img.crop((0, 0, mid_x, height))
right_half = img.crop((mid_x, 0, width, height))
# 翻转左侧部分到右侧
left_half_flipped = left_half.transpose(Image.FLIP_LEFT_RIGHT)
@@ -90,6 +89,75 @@ def process_avatar(image_bytes: bytes) -> bytes:
return output.read()
def process_gif_avatar(gif_bytes: bytes) -> bytes:
"""
处理GIF动画为轴对称图形
:param gif_bytes: 原始GIF字节
:return: 处理后的GIF字节
"""
# 打开GIF
gif = Image.open(io.BytesIO(gif_bytes))
# 检查是否为动画GIF
if not getattr(gif, "is_animated", False):
# 如果不是动画,当作普通图片处理
return process_avatar(gif_bytes)
# 获取GIF的所有帧
frames = []
durations = []
disposal_methods = []
for frame in ImageSequence.Iterator(gif):
# 如果是P模式调色板模式需要特殊处理
if frame.mode == 'P':
# 转换为RGB进行处理
frame_rgb = frame.convert('RGB')
else:
frame_rgb = frame.convert('RGB')
# 获取图片尺寸
width, height = frame_rgb.size
# 计算对称轴位置(中间)
mid_x = width // 2
# 分割图片为左右两部分
left_half = frame_rgb.crop((0, 0, mid_x, height))
# 翻转左侧部分到右侧
left_half_flipped = left_half.transpose(Image.FLIP_LEFT_RIGHT)
# 创建新图片
new_frame = Image.new('RGB', (width, height))
# 粘贴左侧原始部分和右侧翻转部分
new_frame.paste(left_half, (0, 0))
new_frame.paste(left_half_flipped, (mid_x, 0))
frames.append(new_frame)
durations.append(frame.info.get('duration', 100))
disposal_methods.append(frame.info.get('disposal', 0))
# 保存处理后的GIF
output = io.BytesIO()
if frames:
# 使用save_all保存多帧GIF
frames[0].save(
output,
format='GIF',
save_all=True,
append_images=frames[1:],
duration=durations,
loop=0,
optimize=False,
disposal=disposal_methods
)
output.seek(0)
return output.read()
async def wait_for_image(bot: Bot, event: MessageEvent):
"""
等待用户发送图片
@@ -98,8 +166,6 @@ async def wait_for_image(bot: Bot, event: MessageEvent):
:param event: 消息事件对象
"""
user_id = event.user_id
chat_id = event.group_id if hasattr(event, 'group_id') else event.user_id
is_group = hasattr(event, 'group_id')
# 设置超时时间
timeout = 30
@@ -138,11 +204,19 @@ async def handle_image_message(bot: Bot, event: MessageEvent):
# 查找消息中的图片
images = []
is_gif = False
for segment in event.message:
if segment.type == "image" and segment.data.get("url"):
images.append(segment.data["url"])
if segment.type == "image":
url = segment.data.get("url", "")
# 检查是否为GIF图片
if ".gif" in url.lower() or segment.data.get("sub_type", 0) == 1:
is_gif = True
if url:
images.append((url, is_gif))
if not images:
del waiting_for_image[user_id]
await event.reply("未找到图片,请重新发送")
return
# 取消等待任务
@@ -150,13 +224,16 @@ async def handle_image_message(bot: Bot, event: MessageEvent):
try:
# 获取第一张图片
image_url = images[0]
image_url, is_gif = images[0]
# 下载图片
image_bytes = await get_image_from_url(image_url)
# 处理图片
processed_image = process_avatar(image_bytes)
if is_gif:
processed_image = process_gif_avatar(image_bytes)
else:
processed_image = process_avatar(image_bytes)
# 检查是否可以发送图片
can_send = await bot.can_send_image()
@@ -189,6 +266,11 @@ async def handle_mirror(bot: Bot, event: MessageEvent, args: list[str]):
if segment.type == "at" and segment.data.get("qq"):
at_users.append(int(segment.data["qq"]))
# 检查是否为GIF模式
is_gif_mode = False
if args and args[0] == "gif":
is_gif_mode = True
if at_users:
# 获取第一个@的用户
user_id = at_users[0]
@@ -198,7 +280,10 @@ async def handle_mirror(bot: Bot, event: MessageEvent, args: list[str]):
avatar_bytes = await get_avatar(user_id)
# 处理头像
processed_avatar = process_avatar(avatar_bytes)
if is_gif_mode:
processed_avatar = process_gif_avatar(avatar_bytes)
else:
processed_avatar = process_avatar(avatar_bytes)
# 检查是否可以发送图片
can_send = await bot.can_send_image()
@@ -218,4 +303,4 @@ async def handle_mirror(bot: Bot, event: MessageEvent, args: list[str]):
else:
# 没有@用户,等待用户发送图片
# 启动等待任务
asyncio.create_task(wait_for_image(bot, event))
asyncio.create_task(wait_for_image(bot, event))