feat(help): 新增「活动」关键词获取服务器最新运营活动海报 (支持合并转发与无活动引导)
This commit is contained in:
@@ -141,7 +141,8 @@
|
|||||||
|
|
||||||
系统的官方指引手册。
|
系统的官方指引手册。
|
||||||
|
|
||||||
- **主要命令**: `帮助`、`下载`、`正式版如何运行` 等。
|
- **主要命令**: `帮助`、`下载`、`活动`(别名:`最新活动`、`群活动`、`hd`)、`每日试用`、`正式版如何运行` 等。
|
||||||
|
- **活动海报推送**: 发送 `活动` 时自动请求服务端接口获取当前正在进行中的运营活动,并向群内发送对应的活动宣传海报及说明。
|
||||||
- **限制**: 仅在特定群 `621016172` 可用。
|
- **限制**: 仅在特定群 `621016172` 可用。
|
||||||
|
|
||||||
### 13. command_list - 指令列表
|
### 13. command_list - 指令列表
|
||||||
|
|||||||
@@ -11,5 +11,6 @@
|
|||||||
2. 正式版如何运行(htr)
|
2. 正式版如何运行(htr)
|
||||||
|
|
||||||
## 活动🔥🔥
|
## 活动🔥🔥
|
||||||
1. 每日试用(mrss) - 永久活动,单设备每日可试用1小时!
|
1. 活动(hd) - 获取当前最新活动海报与详情!
|
||||||
|
2. 每日试用(mrss) - 永久活动,单设备每日可试用1小时!
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ class Config(BaseModel):
|
|||||||
2. 正式版如何运行
|
2. 正式版如何运行
|
||||||
|
|
||||||
## 活动
|
## 活动
|
||||||
1. 每日试用"""
|
1. 活动
|
||||||
|
2. 每日试用"""
|
||||||
|
|
||||||
DowndLoadStr:str = """\
|
DowndLoadStr:str = """\
|
||||||
公益版提供群文件下载一种方式;
|
公益版提供群文件下载一种方式;
|
||||||
@@ -43,3 +44,12 @@ class Config(BaseModel):
|
|||||||
DailyTrialStr:str = """\
|
DailyTrialStr:str = """\
|
||||||
永久活动-每日试用:单设备支持每日试用1小时的脚本时长!
|
永久活动-每日试用:单设备支持每日试用1小时的脚本时长!
|
||||||
请在购买或支持蛋定助手前,一定要先试用,确保自己可以正常使用脚本!"""
|
请在购买或支持蛋定助手前,一定要先试用,确保自己可以正常使用脚本!"""
|
||||||
|
|
||||||
|
NoActivityStr: str = """\
|
||||||
|
📢 当前暂无进行中的限时运营活动哦~
|
||||||
|
🎁 常规福利:
|
||||||
|
1. 发送【每日试用】获取单设备每日1小时试用时长!
|
||||||
|
2. 发送【签到】获取每日免费积分奖励!
|
||||||
|
📖 发送【帮助】查看更多功能菜单。"""
|
||||||
|
|
||||||
|
DDApi_Host: str = "https://api.danding.vip/DD/"
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
from nonebot import on_command, get_plugin_config,logger
|
from nonebot import on_command, get_plugin_config, get_driver, logger
|
||||||
from nonebot.rule import Rule, fullmatch
|
from nonebot.rule import Rule, fullmatch
|
||||||
from .config import Config
|
from .config import Config
|
||||||
import os
|
import os
|
||||||
|
import io
|
||||||
|
import aiohttp
|
||||||
|
from PIL import Image as PILImage
|
||||||
from nonebot_plugin_saa import Text, Image, MessageFactory
|
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
|
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||||
import random
|
import random
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -95,3 +99,148 @@ daily_trial = on_command("每日试用", rule=_group_rule & fullmatch(('每日
|
|||||||
async def _handle_daily_trial():
|
async def _handle_daily_trial():
|
||||||
await asyncio.sleep(random.uniform(2, 3))
|
await asyncio.sleep(random.uniform(2, 3))
|
||||||
await daily_trial.finish(plugin_config.DailyTrialStr)
|
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
|
||||||
|
|
||||||
|
poster_items = [it for it in items if it.get("poster_url") or it.get("poster_path")]
|
||||||
|
|
||||||
|
# 无海报时回退为文字列表
|
||||||
|
if not poster_items:
|
||||||
|
msg_lines = ["📢 当前运营活动:"]
|
||||||
|
for it in 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}")
|
||||||
|
|||||||
Reference in New Issue
Block a user