import httpx from nonebot import logger from nonebot.internal.params import ArgPlainText from nonebot.typing import T_State from nonebot import on_command from nonebot.adapters.onebot.v11 import MessageEvent, Bot, MessageSegment from nonebot.plugin import PluginMetadata from typing import Optional, Union from .equipment import get_random_equipment, get_equipment_by_combination from .hd2_api import get_briefing_data from .war_renderer import render_war_briefing from .utils import * from .equipment_store import init_db as _init_equipment_db from . import stratagem_admin as _stratagem_admin from hexi.web_plugin_registry import register_web_plugin from nonebot.matcher import Matcher __plugin_meta__ = PluginMetadata( name="绝地潜兵小助手", description="绝地潜兵小助手,提供一些小功能,让超级地球更加美好!", usage="简报:获取星系战争简要概况(每个阵营取人数前 4 张卡片)\n" "详报:全量绘制当前进行的战役\n" "随机战备:让机器人帮你随机一套战备\n" "战备管理后台:/hub/ 统一管理台内可视化编辑", type="application", homepage="https://github.com/sansenhoshi/nonebot_plugin_helldivers_tools" ) basic_path = os.path.dirname(__file__) save_path = os.path.join(basic_path, "temp") img_path = os.path.join(basic_path, "img") data_path = os.path.join(basic_path, "data") # 注册到统一 Web:hub 启动时会自动挂载 /api/helldivers,无需手动配置 _init_equipment_db() register_web_plugin( "helldivers", "战备管理", "rocket", lambda: _stratagem_admin.build_admin_app(), module_name=__name__, ) logger.info("战备管理 Web 插件已注册(/api/helldivers)") # 统一配置标准:把 API 相关配置暴露给 Web 管理台(/hub 插件配置) from hexi.config_standard import register_model_config # noqa: E402 from .config import config as _hd2_config # noqa: E402 register_model_config( __name__, _hd2_config, fields=[ "hd2_api_base_url", "hd2_api_language", "hd2_cache_ttl", "hd2_super_client", "hd2_super_contact", ], labels={ "hd2_api_base_url": "API 根地址", "hd2_api_language": "本地化语言", "hd2_cache_ttl": "数据缓存秒数", "hd2_super_client": "请求头 Client", "hd2_super_contact": "请求头 Contact", }, descriptions={ "hd2_api_base_url": "自托管 helldivers-2/api 根地址;不填则用公共社区 API", "hd2_api_language": "请求本地化语言,自托管实例支持 zh-Hans/zh-Hant", "hd2_cache_ttl": "战况快照约几分钟更新一次,靠这个避免频繁打接口触发限流", "hd2_super_client": "公共/自托管都要求 X-Super-Client 头", "hd2_super_contact": "公共/自托管都要求 X-Super-Contact 头", }, ) async def _send_war_card(matcher: Matcher, ev: MessageEvent, top_per_race: Optional[int]) -> None: await matcher.send("正在获取前线战况,请民主的等待!") try: data = await get_briefing_data() png = await render_war_briefing(data, top_per_race) except Exception as e: logger.exception("获取战况失败") await matcher.finish( MessageSegment.reply(ev.message_id) + f"战况获取失败:{type(e).__name__}:{e}" ) return mes = (MessageSegment.reply(ev.message_id), gen_ms_img(png)) await matcher.send(mes) war_situation = on_command("简报", aliases={"简报"}) war_detail = on_command("详报", aliases={"详报"}) @war_situation.handle() async def get_war_info(ev: MessageEvent): await _send_war_card(war_situation, ev, top_per_race=4) @war_detail.handle() async def get_war_detail(ev: MessageEvent): await _send_war_card(war_detail, ev, top_per_race=None) async def download_url(url: str) -> bytes | None: async with httpx.AsyncClient() as client: for i in range(3): try: resp = await client.get(url) if resp.status_code != 200: continue return resp.content except Exception as e: print(f"Error downloading {url}, retry {i}/3: {str(e)}") return None random_helldivers = on_command("随机战备", aliases={"随机战备"}, block=True) # PROMPT = """ ***超级地球武装部*** # 请发送你需要的随机规则 # # 1:纯随机(不推荐) # # 套路随机(按一定规则随机) # # 2:2红/1蓝/1绿 # 3:2绿/1红/1蓝 # 4:2蓝/1绿/1红 # # 5:3红/1蓝 # 6:3绿/1蓝 # # 7:2红/2蓝 # 8:2蓝/2绿 # 9:2绿/2红 # # 10:4红 # 11:4绿""" board_path = f"{img_path}/board2.png" PROMPT = MessageSegment.image(pic2b64(Image.open(board_path))) error_count = 0 # 用户选择 @random_helldivers.got("pick_type", prompt=PROMPT) async def got_random_helldivers(event: MessageEvent, pick_type: str = ArgPlainText()): global error_count if error_count >= 2: await random_helldivers.finish("已回归平民生活——") logger.info("持续输错退出随机战备") error_count = 0 return logger.info(f"用户选择的战备类型: {pick_type}") if not is_number(pick_type): error_count += 1 await random_helldivers.reject(f"您输入的 {pick_type} 非数字,请重新输入1到11,或者输入0退出") elif int(pick_type) not in range(12): error_count += 1 await random_helldivers.reject(f"您输入的 {pick_type} 不在范围内,请重新输入1到11,或者输入0退出") elif int(pick_type) == 0: await random_helldivers.finish("已回归平民生活——") logger.info("用户选择退出随机战备") error_count = 0 return mix_msg = (MessageSegment.reply(event.message_id),) type_combinations = { 2: {'red': 2, 'blue': 1, 'green': 1}, 3: {'green': 2, 'red': 1, 'blue': 1}, 4: {'blue': 2, 'green': 1, 'red': 1}, 5: {'red': 3, 'blue': 1}, 6: {'green': 3, 'blue': 1}, 7: {'red': 2, 'blue': 2}, 8: {'blue': 2, 'green': 2}, 9: {'green': 2, 'red': 2}, 10: {'red': 4}, 11: {'green': 4} } if int(pick_type) == 1: logger.info("用户选择纯随机战备") result = await get_random_equipment(4) # 4 random equipments else: combination = type_combinations.get(int(pick_type)) logger.info(f"用户选择的战备组合: {combination}") result = await get_equipment_by_combination(combination) final_msg = MessageSegment.text("您的随机结果是:\n") image_tuple = gen_ms_img(result) mix_msg += (final_msg, image_tuple) await random_helldivers.finish(mix_msg) def is_number(s): return bool(re.match(r'^[0-9]+$', s))