Files
HeXi/hexi/plugins/nonebot_plugin_helldivers_tools/__init__.py
T
sansenhoshiandClaude b61d09f09f Add HeXi bot codebase: custom plugins, web frontends, tests
- hexi core: message handling, rate limiting, cooldown, plugin manager
- Custom plugins: BF stats, daily check-in, quotes, persona cards, etc.
- Community plugins vendored under hexi/plugins with local fixes
- Web admin frontends (learning-chat, persona-admin), unified hexi/web
- Tests for rate_limit/cooldown/memes/persona; poetry.lock

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-01 13:13:40 +08:00

204 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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))