from nonebot import on_command, get_plugin_config, get_driver, logger from nonebot.rule import Rule, fullmatch from .config import Config import os import io import aiohttp from PIL import Image as PILImage from nonebot_plugin_saa import Text, Image, MessageFactory from nonebot.adapters.onebot.v11 import Bot as V11Bot, Message as V11Message, MessageSegment as V11MessageSegment from nonebot.adapters.onebot.v11.event import GroupMessageEvent import random import asyncio ALLOWED_GROUPS = [621016172] async def _group_check(e: GroupMessageEvent) -> bool: """Check if message is from an allowed group.""" return e.group_id in ALLOWED_GROUPS _group_rule = Rule(_group_check) plugin_config = get_plugin_config(Config) help = on_command("帮助", rule=_group_rule & fullmatch(('帮助','help')), aliases={"help"}, priority=1, block=True) @help.handle() async def _handle_help(): current_dir = os.path.dirname(__file__) image_path = os.path.join(current_dir, "img", "帮助菜单.jpg") try: with open(image_path, "rb") as f: image_bytes = f.read() await asyncio.sleep(random.uniform(2, 3)) await MessageFactory([Image(image_bytes)]).send(at_sender=True, reply=True) except FileNotFoundError: logger.warning(f"[Help] 帮助菜单图片不存在: {image_path}") await help.finish("帮助菜单图片暂时不可用,请联系管理员") downdload = on_command("下载", rule=_group_rule & fullmatch(('下载',"dl","downdload")), aliases={"dl","downdload"}, priority=1, block=True) @downdload.handle() async def _handle_download(): await asyncio.sleep(random.uniform(2, 3)) await downdload.finish(plugin_config.DowndLoadStr) wd = on_command("帮助文档", rule=_group_rule & fullmatch(('帮助文档',"wd")), aliases={"wd"}, priority=1, block=True) @wd.handle() async def _handle_wd(): await asyncio.sleep(random.uniform(2, 3)) await wd.finish("https://www.danding.vip") free = on_command("公益版", rule=_group_rule & fullmatch(('公益版',"free","gyb")), aliases={"free","gyb"}, priority=1, block=True) @free.handle() async def _handle_free(): await asyncio.sleep(random.uniform(2, 3)) await help.finish(plugin_config.FreeStr) pro = on_command("正式版", rule=_group_rule & fullmatch(('正式版',"pro","zsb")), aliases={"pro","zsb"}, priority=1, block=True) @pro.handle() async def _handle_pro(): await asyncio.sleep(random.uniform(2, 3)) await help.finish(plugin_config.ProStr) dyh = on_command("正式版御魂双开", rule=_group_rule & fullmatch(('正式版御魂双开',"dyh")), aliases={"dyh"}, priority=1, block=True) @dyh.handle() async def _handle_dyh(): current_dir = os.path.dirname(__file__) image_path = os.path.join(current_dir, "img", "御魂双开方法.jpg") try: with open(image_path, "rb") as f: image_bytes = f.read() await asyncio.sleep(random.uniform(2, 3)) await MessageFactory([Text("御魂双开方法见下图"), Image(image_bytes)]).send(at_sender=True, reply=True) except FileNotFoundError: logger.warning(f"[Help] 御魂双开图片不存在: {image_path}") await dyh.finish("教程图片暂时不可用,请联系管理员") htr = on_command("正式版如何运行", rule=_group_rule & fullmatch(('正式版如何运行',"htr")), aliases={"htr"}, priority=1, block=True) @htr.handle() async def _handle_htr(): current_dir = os.path.dirname(__file__) image_path = os.path.join(current_dir, "img", "开软件教程.jpg") try: with open(image_path, "rb") as f: image_bytes = f.read() await asyncio.sleep(random.uniform(2, 3)) await MessageFactory([Text("How To Run? Look!"), Image(image_bytes)]).send(at_sender=True, reply=True) except FileNotFoundError: logger.warning(f"[Help] 运行教程图片不存在: {image_path}") await htr.finish("教程图片暂时不可用,请联系管理员") order = on_command("下单", rule=_group_rule & fullmatch(('下单',"xd")), aliases={"xd"}, priority=1, block=True) @order.handle() async def _handle_order(): await asyncio.sleep(random.uniform(2, 3)) await order.finish(plugin_config.OrderStr) daily_trial = on_command("每日试用", rule=_group_rule & fullmatch(('每日试用',"mrss")), aliases={"mrss"}, priority=1, block=True) @daily_trial.handle() async def _handle_daily_trial(): await asyncio.sleep(random.uniform(2, 3)) await daily_trial.finish(plugin_config.DailyTrialStr) activity = on_command("活动", rule=_group_rule & fullmatch(('活动', '最新活动', '群活动', 'hd')), aliases={"最新活动", "群活动", "hd"}, priority=1, block=True) @activity.handle() async def _handle_activity(bot: V11Bot, event: GroupMessageEvent): await asyncio.sleep(random.uniform(1.5, 2.5)) driver_config = get_driver().config api_host = ( getattr(driver_config, "danding_api_host", None) or os.environ.get("DANDING_API_HOST") or getattr(plugin_config, "DDApi_Host", "https://api.danding.vip/DD/") ).rstrip("/") target_url = f"{api_host}/get_active_activities" try: async with aiohttp.ClientSession() as session: async with session.get(target_url, timeout=aiohttp.ClientTimeout(total=10)) as resp: if resp.status != 200: logger.error(f"[Help] 获取活动列表状态码异常: {resp.status}") await activity.finish("获取活动信息失败,请稍后再试") res = await resp.json() except Exception as e: logger.error(f"[Help] 获取活动列表请求失败: {e}") await activity.finish("活动服务暂时不可用,请稍后再试") return items = res.get("data", []) if not isinstance(items, list) or not items: await activity.finish(plugin_config.NoActivityStr) return # 严格过滤出处于发布状态且未结束的活动,排除草稿(draft)、已结束(ended)、已暂停(paused) valid_items = [] for it in items: lifecycle = str(it.get("lifecycle") or "").lower() pub_status = str(it.get("publication_status") or "").lower() if lifecycle == "ended" or pub_status in ("draft", "archived", "paused"): continue valid_items.append(it) if not valid_items: await activity.finish(plugin_config.NoActivityStr) return poster_items = [it for it in valid_items if it.get("poster_url") or it.get("poster_path")] # 无海报时回退为文字列表 if not poster_items: msg_lines = ["📢 当前运营活动:"] for it in valid_items: name = it.get("活动名", "未知活动") start = it.get("活动开始时间", "") end = it.get("活动截止时间", "") notice = it.get("活动公告", "") msg_lines.append(f"\n【{name}】\n时间:{start} ~ {end}\n说明:{notice}") await MessageFactory([Text("\n".join(msg_lines))]).send(at_sender=True, reply=True) return # 单个活动直接以图文消息发送 if len(poster_items) == 1: it = poster_items[0] poster_url = it.get("poster_url") if not poster_url and it.get("poster_path"): poster_url = f"{api_host}/activity-posters/{it['poster_path'].replace('activity-posters/', '')}" name = it.get("活动名", "运营活动") start = it.get("活动开始时间", "") end = it.get("活动截止时间", "") announcement = it.get("活动公告", "") desc = f"🎉【{name}】\n⏰ 活动时间:{start} 至 {end}" if announcement: desc += f"\n📝 活动说明:{announcement}" try: async with aiohttp.ClientSession() as session: async with session.get(poster_url, timeout=aiohttp.ClientTimeout(total=15)) as img_resp: if img_resp.status == 200: img_bytes = await img_resp.read() try: with PILImage.open(io.BytesIO(img_bytes)) as pil_img: out_io = io.BytesIO() pil_img.convert("RGB").save(out_io, format="JPEG", quality=90) img_bytes = out_io.getvalue() except Exception as conv_err: logger.warning(f"[Help] 海报图片格式转换跳过: {conv_err}") await MessageFactory([Text(desc), Image(img_bytes)]).send(at_sender=True, reply=True) return except Exception as e: logger.error(f"[Help] 单活动海报发送异常: {e}") await activity.finish("当前活动海报加载失败,请稍后重试") return # 多个活动:打包为合并转发消息发送,避免多图刷屏 nodes = [] bot_info = await bot.get_login_info() bot_id = bot_info.get("user_id", event.self_id) bot_name = bot_info.get("nickname", "蛋定助手") # 首节点:概览提示 summary_text = f"📢 当前共有 {len(poster_items)} 个运营活动进行中:\n" + "\n".join( [f"{idx + 1}. {it.get('活动名', '未知活动')}" for idx, it in enumerate(poster_items)] ) nodes.append(V11MessageSegment.node_custom( user_id=bot_id, nickname=bot_name, content=V11Message([V11MessageSegment.text(summary_text)]) )) async with aiohttp.ClientSession() as session: for it in poster_items: poster_url = it.get("poster_url") if not poster_url and it.get("poster_path"): poster_url = f"{api_host}/activity-posters/{it['poster_path'].replace('activity-posters/', '')}" name = it.get("活动名", "运营活动") start = it.get("活动开始时间", "") end = it.get("活动截止时间", "") announcement = it.get("活动公告", "") card_msg = V11Message() card_desc = f"🎉【{name}】\n⏰ 活动时间:{start} 至 {end}" if announcement: card_desc += f"\n📝 活动说明:{announcement}" card_msg.append(V11MessageSegment.text(card_desc)) try: async with session.get(poster_url, timeout=aiohttp.ClientTimeout(total=15)) as img_resp: if img_resp.status == 200: img_bytes = await img_resp.read() try: with PILImage.open(io.BytesIO(img_bytes)) as pil_img: out_io = io.BytesIO() pil_img.convert("RGB").save(out_io, format="JPEG", quality=90) img_bytes = out_io.getvalue() except Exception as conv_err: logger.warning(f"[Help] 海报图片转换跳过: {conv_err}") card_msg.append(V11MessageSegment.image(img_bytes)) except Exception as dl_err: logger.warning(f"[Help] 多活动海报拉取失败({name}): {dl_err}") nodes.append(V11MessageSegment.node_custom( user_id=bot_id, nickname=bot_name, content=card_msg )) try: await bot.send_group_forward_msg(group_id=event.group_id, messages=nodes) except Exception as fwd_err: logger.error(f"[Help] 发送合并转发失败: {fwd_err},尝试降级逐条发送") # 降级处理 for node in nodes[1:]: try: await bot.send_group_msg(group_id=event.group_id, message=node.data["content"]) await asyncio.sleep(1) except Exception as snd_err: logger.error(f"[Help] 降级发送单条失败: {snd_err}")