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>
This commit is contained in:
2026-09-01 13:13:40 +08:00
co-authored by Claude
parent 1783c60afa
commit b61d09f09f
3201 changed files with 160436 additions and 171 deletions
@@ -0,0 +1,65 @@
import importlib
from collections.abc import Awaitable
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, TypedDict
from typing_extensions import Protocol, Unpack
from nonebot import logger
from ..config import config
if TYPE_CHECKING:
from ..bg_provider import BgBytesData
class TemplateRendererKwargs(TypedDict):
collected: dict[str, Any]
bg: "BgBytesData"
class TemplateRenderer(Protocol):
__name__: str
def __call__(
self,
**kwargs: Unpack[TemplateRendererKwargs],
) -> Awaitable[bytes]: ...
@dataclass()
class TemplateInfo:
renderer: TemplateRenderer
collectors: set[str] | None = None
loaded_templates: dict[str, TemplateInfo] = {}
def pic_template(
name: str | None = None,
collecting: set[str] | None = None,
):
def deco(func: TemplateRenderer):
template_name = name or func.__name__
if template_name in loaded_templates:
raise ValueError(f"Template {template_name} already exists")
loaded_templates[template_name] = TemplateInfo(
renderer=func,
collectors=collecting,
)
logger.debug(f"Registered template {template_name}")
return deco
def load_builtin_templates():
for module in Path(__file__).parent.iterdir():
name = module.name
if (not module.is_dir()) or name.startswith("_"):
continue
assert importlib.import_module(f".{name}", __package__)
async def render_current_template(**kwargs: Unpack[TemplateRendererKwargs]):
return await loaded_templates[config.ps_template].renderer(**kwargs)