diff --git a/.gitignore b/.gitignore index 9885c04..c6416b9 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ scripts/output_all/debug_HatsuneMiku_test_group_*.pdf scripts/output/mock_report_test_group_mock_*.pdf astrbot-lark-group-daily-analysis-main/** .ace-tool/ +debug_atri.html diff --git a/_conf_schema.json b/_conf_schema.json index e741e2c..0579f96 100644 --- a/_conf_schema.json +++ b/_conf_schema.json @@ -84,6 +84,41 @@ "default": "scrapbook", "hint": "分析报告使用的HTML模板名称,QQ 端可以使用 `/设置模板` 查看使用指南,使用`/查看模板` 命令查看模板样式效果。其他平台不支持合并转发,可以到 [模板](https://github.com/SXP-Simon/astrbot_plugin_qq_group_daily_analysis/tree/main/assets) 文件夹中查看模板样式效果" }, + "profile_display_mode": { + "type": "string", + "description": "人格标签展示模式", + "options": [ + "mbti", + "sbti", + "acgti" + ], + "default": "mbti", + "hint": "只影响群友画像卡片的角色主题展示,不影响实际 MBTI 分析结果。mbti=原始 MBTI,sbti=映射为 SBTI 人格,acgti=映射为 ACGTI 角色。" + }, + "profile_image_opacity": { + "type": "float", + "description": "人格背景图透明度", + "default": 0.12, + "hint": "建议 0.08~0.18,仅作为卡片底层装饰,避免遮挡正文。" + }, + "profile_image_size_mode": { + "type": "string", + "description": "人格背景图尺寸模式", + "options": [ + "contain", + "cover" + ], + "default": "contain", + "hint": "建议默认 contain,减少角色图被裁切过多的问题。" + }, + "profile_mapping_config": { + "type": "text", + "description": "人格映射配置(JSON)", + "editor_mode": true, + "editor_language": "json", + "default": "{\n \"mbti\": {\n \"INTJ\": {\n \"code\": \"INTJ\",\n \"name_zh\": \"建筑师\"\n },\n \"INTP\": {\n \"code\": \"INTP\",\n \"name_zh\": \"逻辑学家\"\n },\n \"ENTJ\": {\n \"code\": \"ENTJ\",\n \"name_zh\": \"指挥官\"\n },\n \"ENTP\": {\n \"code\": \"ENTP\",\n \"name_zh\": \"辩论家\"\n },\n \"INFJ\": {\n \"code\": \"INFJ\",\n \"name_zh\": \"提倡者\"\n },\n \"INFP\": {\n \"code\": \"INFP\",\n \"name_zh\": \"调停者\"\n },\n \"ENFJ\": {\n \"code\": \"ENFJ\",\n \"name_zh\": \"主人公\"\n },\n \"ENFP\": {\n \"code\": \"ENFP\",\n \"name_zh\": \"竞选者\"\n },\n \"ISTJ\": {\n \"code\": \"ISTJ\",\n \"name_zh\": \"物流师\"\n },\n \"ISFJ\": {\n \"code\": \"ISFJ\",\n \"name_zh\": \"守卫者\"\n },\n \"ESTJ\": {\n \"code\": \"ESTJ\",\n \"name_zh\": \"总经理\"\n },\n \"ESTP\": {\n \"code\": \"ESTP\",\n \"name_zh\": \"企业家\"\n },\n \"ISTP\": {\n \"code\": \"ISTP\",\n \"name_zh\": \"鉴赏家\"\n },\n \"ISFP\": {\n \"code\": \"ISFP\",\n \"name_zh\": \"探险家\"\n },\n \"ESFJ\": {\n \"code\": \"ESFJ\",\n \"name_zh\": \"执政官\"\n },\n \"ESFP\": {\n \"code\": \"ESFP\",\n \"name_zh\": \"表演者\"\n }\n },\n \"sbti\": {\n \"INTJ\": {\n \"code\": \"CTRL\",\n \"name_zh\": \"拿捏者\",\n \"asset_code\": \"CTRL\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/CTRL.png\"\n },\n \"INTP\": {\n \"code\": \"THIN-K\",\n \"name_zh\": \"思考者\",\n \"asset_code\": \"THIN-K\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/THIN-K.png\"\n },\n \"ENTJ\": {\n \"code\": \"BOSS\",\n \"name_zh\": \"领导者\",\n \"asset_code\": \"BOSS\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/BOSS.png\"\n },\n \"ENTP\": {\n \"code\": \"JOKE-R\",\n \"name_zh\": \"小丑\",\n \"asset_code\": \"JOKE-R\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/JOKE-R.jpg\"\n },\n \"INFJ\": {\n \"code\": \"LOVE-R\",\n \"name_zh\": \"多情者\",\n \"asset_code\": \"LOVE-R\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/LOVE-R.png\"\n },\n \"INFP\": {\n \"code\": \"SOLO\",\n \"name_zh\": \"孤儿\",\n \"asset_code\": \"SOLO\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SOLO.png\"\n },\n \"ENFJ\": {\n \"code\": \"THAN-K\",\n \"name_zh\": \"感恩者\",\n \"asset_code\": \"THAN-K\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/THAN-K.png\"\n },\n \"ENFP\": {\n \"code\": \"GOGO\",\n \"name_zh\": \"行者\",\n \"asset_code\": \"GOGO\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/GOGO.png\"\n },\n \"ISTJ\": {\n \"code\": \"OH-NO\",\n \"name_zh\": \"哦不人\",\n \"asset_code\": \"OH-NO\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/OH-NO.png\"\n },\n \"ISTP\": {\n \"code\": \"POOR\",\n \"name_zh\": \"贫困者\",\n \"asset_code\": \"POOR\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/POOR.png\"\n },\n \"ESTJ\": {\n \"code\": \"SHIT\",\n \"name_zh\": \"愤世者\",\n \"asset_code\": \"SHIT\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SHIT.png\"\n },\n \"ESTP\": {\n \"code\": \"WOC!\",\n \"name_zh\": \"握草人\",\n \"asset_code\": \"WOC\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/WOC.png\"\n },\n \"ISFJ\": {\n \"code\": \"MUM\",\n \"name_zh\": \"妈妈\",\n \"asset_code\": \"MUM\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/MUM.png\"\n },\n \"ISFP\": {\n \"code\": \"MALO\",\n \"name_zh\": \"吗喽\",\n \"asset_code\": \"MALO\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/MALO.png\"\n },\n \"ESFJ\": {\n \"code\": \"ATM-er\",\n \"name_zh\": \"送钱者\",\n \"asset_code\": \"ATM-er\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/ATM-er.png\"\n },\n \"ESFP\": {\n \"code\": \"SEXY\",\n \"name_zh\": \"尤物\",\n \"asset_code\": \"SEXY\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SEXY.png\"\n }\n },\n \"acgti\": {\n \"INTJ\": {\n \"code\": \"MRTS-X\",\n \"name_zh\": \"Mortis\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MRTS-X.png\"\n },\n \"INTP\": {\n \"code\": \"KNAN\",\n \"name_zh\": \"江户川柯南\"\n },\n \"ENTJ\": {\n \"code\": \"SAKI\",\n \"name_zh\": \"丰川祥子\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/SAKI.png\"\n },\n \"ENTP\": {\n \"code\": \"CHKA\",\n \"name_zh\": \"藤原千花\"\n },\n \"INFJ\": {\n \"code\": \"DLRS\",\n \"name_zh\": \"三角初华\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/DLRS.png\"\n },\n \"INFP\": {\n \"code\": \"BCHI\",\n \"name_zh\": \"后藤一里\"\n },\n \"ENFJ\": {\n \"code\": \"YCYO\",\n \"name_zh\": \"月见八千代\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/YCYO.png\"\n },\n \"ENFP\": {\n \"code\": \"HTMK\",\n \"name_zh\": \"初音未来\"\n },\n \"ISTJ\": {\n \"code\": \"MRTS\",\n \"name_zh\": \"若叶睦\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MRTS.png\"\n },\n \"ISTP\": {\n \"code\": \"AYRE\",\n \"name_zh\": \"绫波丽\"\n },\n \"ESTJ\": {\n \"code\": \"MIKT\",\n \"name_zh\": \"御坂美琴\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MIKT.png\"\n },\n \"ESTP\": {\n \"code\": \"ASKA\",\n \"name_zh\": \"明日香\"\n },\n \"ISFJ\": {\n \"code\": \"SOYO\",\n \"name_zh\": \"长崎爽世\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/SOYO.png\"\n },\n \"ISFP\": {\n \"code\": \"LTYI\",\n \"name_zh\": \"洛天依\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/LTYI.png\"\n },\n \"ESFJ\": {\n \"code\": \"ANON\",\n \"name_zh\": \"千早爱音\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/ANON.png\"\n },\n \"ESFP\": {\n \"code\": \"FRNA\",\n \"name_zh\": \"芙宁娜\",\n \"image\": \"https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/FRNA.png\"\n }\n }\n}", + "hint": "按 MBTI 覆盖当前展示模式的人格名称与图片地址。留空时自动使用插件内置默认映射和 manifest。插件不负责校验图片链接可访问性。" + }, "debug_mode": { "type": "bool", "description": "调试模式(建议关闭)", @@ -524,4 +559,4 @@ } } } -} +} \ No newline at end of file diff --git a/assets/profile_assets/manifest.json b/assets/profile_assets/manifest.json new file mode 100644 index 0000000..af39b0e --- /dev/null +++ b/assets/profile_assets/manifest.json @@ -0,0 +1,246 @@ +{ + "sbti": [ + { + "code": "ATM-er", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/ATM-er.png" + }, + { + "code": "BOSS", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/BOSS.png" + }, + { + "code": "CTRL", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/CTRL.png" + }, + { + "code": "DEAD", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/DEAD.png" + }, + { + "code": "Dior-s", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/Dior-s.jpg" + }, + { + "code": "DRUNK", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/DRUNK.png" + }, + { + "code": "FAKE", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/FAKE.png" + }, + { + "code": "FUCK", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/FUCK.png" + }, + { + "code": "GOGO", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/GOGO.png" + }, + { + "code": "HHHH", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/HHHH.png" + }, + { + "code": "IMFW", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/IMFW.png" + }, + { + "code": "IMSB", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/IMSB.png" + }, + { + "code": "JOKE-R", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/JOKE-R.jpg" + }, + { + "code": "LOVE-R", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/LOVE-R.png" + }, + { + "code": "MALO", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/MALO.png" + }, + { + "code": "MONK", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/MONK.png" + }, + { + "code": "MUM", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/MUM.png" + }, + { + "code": "OH-NO", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/OH-NO.png" + }, + { + "code": "OJBK", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/OJBK.png" + }, + { + "code": "POOR", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/POOR.png" + }, + { + "code": "SEXY", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SEXY.png" + }, + { + "code": "SHIT", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SHIT.png" + }, + { + "code": "SOLO", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/SOLO.png" + }, + { + "code": "THAN-K", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/THAN-K.png" + }, + { + "code": "THIN-K", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/THIN-K.png" + }, + { + "code": "WOC", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/WOC.png" + }, + { + "code": "ZZZZ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/sbti/ZZZZ.png" + } + ], + "acgti": [ + { + "code": "CIRN", + "name": "琪露诺", + "mbti": "ESTP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/CIRN.png" + }, + { + "code": "MIKT", + "name": "御坂美琴", + "mbti": "ESTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MIKT.png" + }, + { + "code": "MADK", + "name": "鹿目圆", + "mbti": "INFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MADK.png" + }, + { + "code": "FRNA", + "name": "芙宁娜", + "mbti": "ESFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/FRNA.png" + }, + { + "code": "GGGG-A", + "name": "高松灯", + "mbti": "INFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/GGGG-A.png" + }, + { + "code": "ANON", + "name": "千早爱音", + "mbti": "ESFJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/ANON.png" + }, + { + "code": "BAFE", + "name": "要乐奈", + "mbti": "ISTP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/BAFE.png" + }, + { + "code": "SOYO", + "name": "长崎爽世", + "mbti": "ISFJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/SOYO.png" + }, + { + "code": "RIKI", + "name": "椎名立希", + "mbti": "ESTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/RIKI.png" + }, + { + "code": "SAKI", + "name": "丰川祥子", + "mbti": "ENTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/SAKI.png" + }, + { + "code": "MRTS", + "name": "若叶睦", + "mbti": "ISTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MRTS.png" + }, + { + "code": "MRTS-X", + "name": "Mortis", + "mbti": "INTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/MRTS-X.png" + }, + { + "code": "DLRS", + "name": "三角初华 / Doloris", + "mbti": "INFJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/DLRS.png" + }, + { + "code": "TMRS", + "name": "八幡海铃 / Timoris", + "mbti": "ISTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/TMRS.png" + }, + { + "code": "AMRS", + "name": "祐天寺若麦 / Amoris", + "mbti": "ENTP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/AMRS.png" + }, + { + "code": "DINA", + "name": "嘉然", + "mbti": "ENFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/DINA.png" + }, + { + "code": "TAFI", + "name": "永雏塔菲", + "mbti": "ENTP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/TAFI.png" + }, + { + "code": "NEUR", + "name": "Neuro-sama", + "mbti": "ENTP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/NEUR.png" + }, + { + "code": "EVIL", + "name": "Evil Neuro", + "mbti": "INTJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/EVIL.png" + }, + { + "code": "RINN", + "name": "镜音铃", + "mbti": "ESFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/RINN.png" + }, + { + "code": "LTYI", + "name": "洛天依", + "mbti": "ISFP", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/LTYI.png" + }, + { + "code": "YCYO", + "name": "月见八千代", + "mbti": "ENFJ", + "file": "https://fastly.jsdelivr.net/gh/SXP-Simon/profile_assets@main/acgti/characters/YCYO.png" + } + ] +} diff --git a/scripts/debug_render.py b/scripts/debug_render.py index b6f4e82..c7ed58d 100644 --- a/scripts/debug_render.py +++ b/scripts/debug_render.py @@ -64,8 +64,17 @@ from src.infrastructure.reporting.generators import ReportGenerator # noqa: E40 class MockConfigManager: - def __init__(self, template_name: str = "scrapbook") -> None: + def __init__( + self, + template_name: str = "scrapbook", + profile_mode: str = "mbti", + profile_image_opacity: float = 0.20, + profile_mapping_config: str = "", + ) -> None: self.template_name = template_name + self.profile_mode = profile_mode + self.profile_image_opacity = profile_image_opacity + self.profile_mapping_config = profile_mapping_config def get_report_template(self) -> str: return self.template_name @@ -74,16 +83,16 @@ class MockConfigManager: return 8 def get_max_user_titles(self) -> int: - return 8 + return 16 def get_max_golden_quotes(self) -> int: return 8 - def get_pdf_output_dir(self) -> str: - return "data/pdf" + def get_html_output_dir(self) -> str: + return "data/html" - def get_pdf_filename_format(self) -> str: - return "report_{group_id}_{date}.pdf" + def get_html_filename_format(self) -> str: + return "report_{group_id}_{date}.html" def get_enable_user_card(self) -> bool: return True @@ -101,6 +110,21 @@ class MockConfigManager: def get_llm_max_concurrent(self) -> int: return 2 + def get_profile_display_mode(self) -> str: + return self.profile_mode + + def get_profile_image_opacity(self) -> float: + return self.profile_image_opacity + + def get_profile_image_size_mode(self) -> str: + return "contain" + + def get_profile_mapping_config(self) -> str: + return self.profile_mapping_config + + def get_html_base_url(self) -> str: + return "" + async def mock_get_user_avatar(user_id: str) -> str: # Return a known avatar URL for testing @@ -108,10 +132,15 @@ async def mock_get_user_avatar(user_id: str) -> str: async def debug_render( - template_name: str, output_file: str = "debug_output.html" + template_name: str, + output_file: str = "debug_output.html", + profile_mode: str = "mbti", ) -> None: # 1. Setup Mock Data - config_manager = MockConfigManager(template_name) + config_manager = MockConfigManager( + template_name=template_name, + profile_mode=profile_mode, + ) # 2. Mock Analysis Result using Data Models stats = GroupStatistics( @@ -177,81 +206,48 @@ async def debug_render( SummaryTopic( topic="新出的3A大作测评", contributors=["周八", "吴九"], - detail="[123456789] 分享了最新游戏的通关体验,讨论了画面表现和剧情走向。", - ), - SummaryTopic( - topic="新出的3A大作测评", - contributors=["周八", "吴九"], - detail="[123456789] 分享了最新游戏的通关体验,讨论了画面表现和剧情走向。", - ), - SummaryTopic( - topic="新出的3A大作测评", - contributors=["周八", "吴九"], - detail="[123456789] 分享了最新游戏的通关体验,讨论了画面表现和剧情走向。", - ), - SummaryTopic( - topic="新出的3A大作测评", - contributors=["周八", "吴九"], - detail="[123456789] 分享了最新游戏的通关体验,讨论了画面表现和剧情走向。", - ), - SummaryTopic( - topic="新出的3A大作测评", - contributors=["周八", "吴九"], - detail="[123456789] 分享了最新游戏的通关体验,讨论了画面表现和剧情走向。", + detail="[200000000] 开始尝试新的游戏,发现 INTJ 类型的玩家在策略游戏中非常吃香。", ), ] - user_titles = [ - UserTitle( - name="张三", - user_id="123456789", - title="代码收割机", - mbti="INTJ", - reason="在短短一小时内提交了10个PR,效率惊人。", - ), - UserTitle( - name="李四", - user_id="987654321", - title="群聊气氛组", - mbti="ENFP", - reason="总能精准接住每一个冷笑话,让群里充满快活的气息。", - ), - UserTitle( - name="潜水员", - user_id="112233445", - title="深夜潜水员", - mbti="INFP", - reason="总是在凌晨三点出没,留下几句深奥的话语后消失。", - ), - UserTitle( - name="潜水员", - user_id="112233445", - title="深夜潜水员", - mbti="INFP", - reason="总是在凌晨三点出没,留下几句深奥的话语后消失。", - ), - UserTitle( - name="潜水员", - user_id="112233445", - title="深夜潜水员", - mbti="INFP", - reason="总是在凌晨三点出没,留下几句深奥的话语后消失。", - ), - UserTitle( - name="潜水员", - user_id="112233445", - title="深夜潜水员", - mbti="INFP", - reason="总是在凌晨三点出没,留下几句深奥的话语后消失。", - ), - UserTitle( - name="潜水员", - user_id="112233445", - title="深夜潜水员", - mbti="INFP", - reason="总是在凌晨三点出没,留下几句深奥的话语后消失。", - ), + mbti_types = [ + "INTJ", + "INTP", + "ENTJ", + "ENTP", + "INFJ", + "INFP", + "ENFJ", + "ENFP", + "ISTJ", + "ISFJ", + "ESTJ", + "ESTP", + "ISTP", + "ISFP", + "ESFJ", + "ESFP", ] + user_titles = [] + user_analysis = { + "123456789": {"nickname": "张三"}, + "987654321": {"nickname": "李四"}, + "112233445": {"nickname": "潜水员"}, + } + + for i, mbti in enumerate(mbti_types): + uid = str(200000000 + i) + uname = f"用户_{mbti}" + user_titles.append( + UserTitle( + name=uname, + user_id=uid, + title=f"{mbti} 王者", + mbti=mbti, + reason=f"在日常交流中表现出极强的 {mbti} 特征,获得了大家的认可。", + ) + ) + user_analysis[uid] = {"nickname": uname} golden_quotes = [ GoldenQuote( @@ -269,31 +265,7 @@ async def debug_render( GoldenQuote( content="PHP是世界上最好的语言!", sender="王五", - reason="引发了长达3小时的群聊大讨论", - user_id="112233445", - ), - GoldenQuote( - content="PHP是世界上最好的语言!", - sender="王五", - reason="引发了长达3小时的群聊大讨论", - user_id="112233445", - ), - GoldenQuote( - content="PHP是世界上最好的语言!", - sender="王五", - reason="引发了长达3小时的群聊大讨论", - user_id="112233445", - ), - GoldenQuote( - content="PHP是世界上最好的语言!", - sender="王五", - reason="引发了长达3小时的群聊大讨论", - user_id="112233445", - ), - GoldenQuote( - content="PHP是世界上最好的语言!", - sender="王五", - reason="引发了长达3小时的群聊大讨论", + reason="[200000001] 表示强烈赞同,并引发了后续的长篇大论。", user_id="112233445", ), ] @@ -305,15 +277,11 @@ async def debug_render( "statistics": stats, "topics": topics, "user_titles": user_titles, - "user_analysis": { - "123456789": {"nickname": "张三"}, - "987654321": {"nickname": "李四"}, - "112233445": {"nickname": "潜水员"}, - }, + "user_analysis": user_analysis, "chat_quality_review": stats.chat_quality_review, "analysis_date": "2026年02月11日", "group_id": "123456", - "group_name": "测试群组", + "group_name": "插件逻辑调试群", } # 3. Initialize Generator @@ -321,6 +289,16 @@ async def debug_render( data_dir.mkdir(parents=True, exist_ok=True) generator = ReportGenerator(config_manager, data_dir) + # Mock avatar cache to avoid KeyError in debug mode + class MockCache(dict): + def __getitem__(self, key): + return self.get(key, "") + + def set(self, key, value, expire=None): + self[key] = value + + generator._avatar_cache = MockCache() + # 4. Prepare Render Data # Note: _prepare_render_data handles converting Entities to template-friendly dicts render_payload = await generator._prepare_render_data( @@ -340,7 +318,7 @@ async def debug_render( await generator.close() print( - f"Successfully rendered template '{template_name}' to {output_path.absolute()}" + f"Successfully rendered template '{template_name}' in mode '{profile_mode}' to {output_path.absolute()}" ) print("You can now open this file with your browser to debug your HTML/CSS.") @@ -363,9 +341,17 @@ def main() -> None: default="debug_output.html", help="Output HTML file path (default: debug_output.html)", ) + parser.add_argument( + "-m", + "--mode", + type=str, + default="mbti", + choices=["mbti", "sbti", "acgti"], + help="Profile display mode to render (default: mbti)", + ) args = parser.parse_args() - asyncio.run(debug_render(args.template, args.output)) + asyncio.run(debug_render(args.template, args.output, args.mode)) if __name__ == "__main__": diff --git a/src/infrastructure/config/config_manager.py b/src/infrastructure/config/config_manager.py index a371476..68e429b 100644 --- a/src/infrastructure/config/config_manager.py +++ b/src/infrastructure/config/config_manager.py @@ -621,6 +621,34 @@ class ConfigManager: self._ensure_group("basic")["enable_analysis_reply"] = enabled self.config.save_config() + def get_profile_display_mode(self) -> str: + """获取人格标签展示模式。""" + mode = str(self._get_group("basic").get("profile_display_mode", "mbti")).lower() + if mode not in {"mbti", "sbti", "acgti"}: + return "mbti" + return mode + + def get_profile_image_opacity(self) -> float: + """获取人格背景图透明度。""" + value = self._get_group("basic").get("profile_image_opacity", 0.12) + try: + return max(0.0, min(1.0, float(value))) + except (TypeError, ValueError): + return 0.12 + + def get_profile_image_size_mode(self) -> str: + """获取人格背景图尺寸模式。""" + mode = str( + self._get_group("basic").get("profile_image_size_mode", "contain") + ).lower() + if mode not in {"contain", "cover"}: + return "contain" + return mode + + def get_profile_mapping_config(self) -> str: + """获取人格映射配置(JSON 文本)。""" + return str(self._get_group("basic").get("profile_mapping_config", "")).strip() + # ========== 群文件/群相册上传配置 ========== def get_enable_group_file_upload(self) -> bool: diff --git a/src/infrastructure/reporting/generators.py b/src/infrastructure/reporting/generators.py index 39ecbba..c51e525 100644 --- a/src/infrastructure/reporting/generators.py +++ b/src/infrastructure/reporting/generators.py @@ -6,6 +6,7 @@ import asyncio import base64 import html +import json import os import re from dataclasses import asdict, is_dataclass @@ -28,6 +29,63 @@ from .templates import HTMLTemplates MAX_CONCURRENT_DOWNLOADS = 10 AVATAR_CACHE_EXPIRE_TIME = 259200 +DEFAULT_PROFILE_MAPPING = { + "mbti": { + "INTJ": {"code": "INTJ", "name_zh": "建筑师"}, + "INTP": {"code": "INTP", "name_zh": "逻辑学家"}, + "ENTJ": {"code": "ENTJ", "name_zh": "指挥官"}, + "ENTP": {"code": "ENTP", "name_zh": "辩论家"}, + "INFJ": {"code": "INFJ", "name_zh": "提倡者"}, + "INFP": {"code": "INFP", "name_zh": "调停者"}, + "ENFJ": {"code": "ENFJ", "name_zh": "主人公"}, + "ENFP": {"code": "ENFP", "name_zh": "竞选者"}, + "ISTJ": {"code": "ISTJ", "name_zh": "物流师"}, + "ISFJ": {"code": "ISFJ", "name_zh": "守卫者"}, + "ESTJ": {"code": "ESTJ", "name_zh": "总经理"}, + "ESTP": {"code": "ESTP", "name_zh": "企业家"}, + "ISTP": {"code": "ISTP", "name_zh": "鉴赏家"}, + "ISFP": {"code": "ISFP", "name_zh": "探险家"}, + "ESFJ": {"code": "ESFJ", "name_zh": "执政官"}, + "ESFP": {"code": "ESFP", "name_zh": "表演者"}, + }, + "sbti": { + "INTJ": {"code": "CTRL", "name_zh": "拿捏者", "asset_code": "CTRL"}, + "INTP": {"code": "THIN-K", "name_zh": "思考者", "asset_code": "THIN-K"}, + "ENTJ": {"code": "BOSS", "name_zh": "领导者", "asset_code": "BOSS"}, + "ENTP": {"code": "JOKE-R", "name_zh": "小丑", "asset_code": "JOKE-R"}, + "INFJ": {"code": "LOVE-R", "name_zh": "多情者", "asset_code": "LOVE-R"}, + "INFP": {"code": "SOLO", "name_zh": "孤儿", "asset_code": "SOLO"}, + "ENFJ": {"code": "THAN-K", "name_zh": "感恩者", "asset_code": "THAN-K"}, + "ENFP": {"code": "GOGO", "name_zh": "行者", "asset_code": "GOGO"}, + "ISTJ": {"code": "OH-NO", "name_zh": "哦不人", "asset_code": "OH-NO"}, + "ISTP": {"code": "POOR", "name_zh": "贫困者", "asset_code": "POOR"}, + "ESTJ": {"code": "SHIT", "name_zh": "愤世者", "asset_code": "SHIT"}, + "ESTP": {"code": "WOC!", "name_zh": "握草人", "asset_code": "WOC"}, + "ISFJ": {"code": "MUM", "name_zh": "妈妈", "asset_code": "MUM"}, + "ISFP": {"code": "MALO", "name_zh": "吗喽", "asset_code": "MALO"}, + "ESFJ": {"code": "ATM-er", "name_zh": "送钱者", "asset_code": "ATM-er"}, + "ESFP": {"code": "SEXY", "name_zh": "尤物", "asset_code": "SEXY"}, + }, + "acgti": { + "INTJ": {"code": "MRTS-X", "name_zh": "Mortis"}, + "INTP": {"code": "KNAN", "name_zh": "江户川柯南"}, + "ENTJ": {"code": "SAKI", "name_zh": "丰川祥子"}, + "ENTP": {"code": "CHKA", "name_zh": "藤原千花"}, + "INFJ": {"code": "DLRS", "name_zh": "三角初华"}, + "INFP": {"code": "BCHI", "name_zh": "后藤一里"}, + "ENFJ": {"code": "YCYO", "name_zh": "月见八千代"}, + "ENFP": {"code": "HTMK", "name_zh": "初音未来"}, + "ISTJ": {"code": "MRTS", "name_zh": "若叶睦"}, + "ISTP": {"code": "AYRE", "name_zh": "绫波丽"}, + "ESTJ": {"code": "MIKT", "name_zh": "御坂美琴"}, + "ESTP": {"code": "ASKA", "name_zh": "明日香"}, + "ISFJ": {"code": "SOYO", "name_zh": "长崎爽世"}, + "ISFP": {"code": "LTYI", "name_zh": "洛天依"}, + "ESFJ": {"code": "ANON", "name_zh": "千早爱音"}, + "ESFP": {"code": "FRNA", "name_zh": "芙宁娜"}, + }, +} + class ReportGenerator(IReportGenerator): """报告生成器""" @@ -51,6 +109,148 @@ class ReportGenerator(IReportGenerator): MAX_CONCURRENT_DOWNLOADS ) self._avatar_session = None + self._profile_asset_manifest = self._load_profile_asset_manifest() + + def _load_profile_asset_manifest(self) -> dict[str, dict]: + """加载人格资源清单。""" + manifest_path = ( + Path(__file__).resolve().parents[3] + / "assets" + / "profile_assets" + / "manifest.json" + ) + if not manifest_path.exists(): + logger.warning(f"人格资源清单不存在: {manifest_path}") + return {"sbti": {}, "acgti": {}} + + try: + raw = json.loads(manifest_path.read_text(encoding="utf-8-sig")) + except Exception as e: + logger.warning(f"加载人格资源清单失败: {e}") + return {"sbti": {}, "acgti": {}} + + manifest: dict[str, dict] = {"sbti": {}, "acgti": {}} + for item in raw.get("sbti", []): + code = str(item.get("code", "")).strip() + if code: + manifest["sbti"][code] = item + for item in raw.get("acgti", []): + code = str(item.get("code", "")).strip() + if code: + manifest["acgti"][code] = item + return manifest + + def _get_profile_mapping_overrides(self) -> dict[str, dict]: + """解析用户配置的人格映射覆盖项。""" + raw = self.config_manager.get_profile_mapping_config() + if not raw: + return {} + + try: + data = json.loads(raw) + if isinstance(data, dict): + return data + except Exception as e: + logger.warning(f"人格映射配置 JSON 解析失败,已回退到默认映射: {e}") + return {} + + def _build_profile_image_from_manifest_pattern( + self, profile_mode: str, asset_code: str + ) -> str: + """当 manifest 缺少具体 code 时,根据已有资源路径模式推导图片地址。""" + system_manifest = self._profile_asset_manifest.get(profile_mode, {}) + for item in system_manifest.values(): + if not isinstance(item, dict): + continue + sample_code = str(item.get("code", "")).strip() + sample_file = str(item.get("file", "")).strip() + if not sample_code or not sample_file: + continue + code_token = f"/{sample_code}." + if code_token not in sample_file: + continue + return sample_file.replace(code_token, f"/{asset_code}.", 1) + return "" + + def _get_manifest_profile_item_by_mbti( + self, profile_mode: str, mbti: str + ) -> dict | None: + """按 MBTI 从 manifest 中寻找可用资源。""" + normalized_mbti = str(mbti or "").strip().upper() + system_manifest = self._profile_asset_manifest.get(profile_mode, {}) + for item in system_manifest.values(): + if not isinstance(item, dict): + continue + item_mbti = str(item.get("mbti", "")).strip().upper() + if item_mbti == normalized_mbti: + return item + return None + + def _resolve_profile_info( + self, + mbti: str, + profile_mode: str, + overrides: dict[str, dict], + ) -> dict[str, str | float]: + """根据当前展示模式解析人格标签展示信息。""" + normalized_mbti = str(mbti or "").strip().upper() + + # 1. 基础信息获取:从默认映射或用户覆盖中获取核心属性 + profile_defaults = DEFAULT_PROFILE_MAPPING.get(profile_mode, {}) + base_info = dict(profile_defaults.get(normalized_mbti, {})) + + # 用户覆盖优先级最高 + user_override = overrides.get(profile_mode, {}).get(normalized_mbti, {}) + if isinstance(user_override, dict): + base_info.update(user_override) + + code = str(base_info.get("code", normalized_mbti)).strip() or normalized_mbti + name_zh = str(base_info.get("name_zh", "")).strip() + asset_code = str(base_info.get("asset_code", code)).strip() or code + image = str(base_info.get("image", "")).strip() + + # 2. 图片与属性补全 (基于 manifest.json 可信源) + if not image: + system_manifest = self._profile_asset_manifest.get(profile_mode, {}) + # A. 优先按 asset_code 索引 + asset_item = system_manifest.get(asset_code) + if isinstance(asset_item, dict): + image = str(asset_item.get("file", "")).strip() + if not name_zh: + name_zh = str(asset_item.get("name", "")).strip() + + # B. 对于 acgti 模式,如果没找到,尝试通过 MBTI 反查第一个匹配的资源 + if not image and profile_mode == "acgti": + fallback_item = self._get_manifest_profile_item_by_mbti( + profile_mode, normalized_mbti + ) + if isinstance(fallback_item, dict): + image = str(fallback_item.get("file", "")).strip() + if not name_zh: + name_zh = str(fallback_item.get("name", "")).strip() + if not code or code == normalized_mbti: + code = str(fallback_item.get("code", code)).strip() + + # C. 最后底线:根据 manifest 路径模式推导 + if not image: + image = self._build_profile_image_from_manifest_pattern( + profile_mode, asset_code + ) + + # 3. 构造显示文本 (Code + 中文名) + display = str(base_info.get("display", "")).strip() + if not display: + display = f"{code}({name_zh})" if name_zh else code + + return { + "profile_mode": profile_mode, + "profile_code": code, + "profile_name_zh": name_zh, + "profile_display": display, + "profile_image": image, + "profile_image_opacity": self.config_manager.get_profile_image_opacity(), + "profile_image_size_mode": self.config_manager.get_profile_image_size_mode(), + } @staticmethod def _sanitize_path_component(name: str) -> str: @@ -506,11 +706,16 @@ class ReportGenerator(IReportGenerator): # 使用Jinja2模板构建用户称号HTML(批量渲染,包含头像) max_user_titles = self.config_manager.get_max_user_titles() titles_list = [] + profile_mode = self.config_manager.get_profile_display_mode() + profile_mapping_overrides = self._get_profile_mapping_overrides() for title in user_titles[:max_user_titles]: # 获取用户头像 avatar_data = await self._get_user_avatar( str(title.user_id), avatar_url_getter ) + profile_info = self._resolve_profile_info( + title.mbti, profile_mode, profile_mapping_overrides + ) title_data = { "name": title.name, "title": title.title, @@ -518,6 +723,7 @@ class ReportGenerator(IReportGenerator): "reason": title.reason, "avatar_data": avatar_data, } + title_data.update(profile_info) titles_list.append(title_data) titles_html = self.html_templates.render_template( diff --git a/src/infrastructure/reporting/templates/ATRI/user_title_item.html b/src/infrastructure/reporting/templates/ATRI/user_title_item.html index 6683915..62d16ba 100644 --- a/src/infrastructure/reporting/templates/ATRI/user_title_item.html +++ b/src/infrastructure/reporting/templates/ATRI/user_title_item.html @@ -1,39 +1,42 @@ -{% set title_emojis = [ - 'https://tc.ciallo.ccwu.cc/file/1775132813334_1774881267181_得意.gif', - 'https://tc.ciallo.ccwu.cc/file/1775132808652_1774881267385_得意-1.gif', - 'https://tc.ciallo.ccwu.cc/file/1775132804506_1774881263342_观察.gif', - 'https://tc.ciallo.ccwu.cc/file/1775132811492_1774881264336_不要.gif' -] %} -{% set title_badge_colors = [ - {'bg': 'linear-gradient(135deg, #ffcadf 0%, #ffc1cc 45%, #ffd9b8 100%)', 'text': '#b85c7b', 'shadow': 'rgba(255, 177, 203, 0.28)', 'border': 'rgba(255, 184, 210, 0.72)'}, - {'bg': 'linear-gradient(135deg, #bfe7ff 0%, #c8dbff 48%, #d8ccff 100%)', 'text': '#5d71b8', 'shadow': 'rgba(168, 198, 255, 0.26)', 'border': 'rgba(173, 204, 255, 0.74)'}, - {'bg': 'linear-gradient(135deg, #c7f3df 0%, #d3f1ff 50%, #e1ddff 100%)', 'text': '#4e8e88', 'shadow': 'rgba(161, 232, 205, 0.24)', 'border': 'rgba(178, 230, 214, 0.72)'}, - {'bg': 'linear-gradient(135deg, #ffe5b7 0%, #ffd6c7 48%, #ffd7ef 100%)', 'text': '#b57752', 'shadow': 'rgba(255, 210, 163, 0.28)', 'border': 'rgba(255, 214, 178, 0.76)'} -] %} -{% for title in titles %} -{% set badge = title_badge_colors[loop.index0 % (title_badge_colors|length)] %} -
  • -
    -
    - {% if title.avatar_data %} - 头像 - {% endif %} -
    -
    - {{ title.name }} - - {{ title.title }} - -
    -
    -

    MBTI: {{ title.mbti }}

    -

    称号理由: {{ title.reason }}

    -
    -
    -
    -
    - 称号表情 -
  • -{% endfor %} \ No newline at end of file +{% set title_emojis = [ + 'https://tc.ciallo.ccwu.cc/file/1775132813334_1774881267181_得意.gif', + 'https://tc.ciallo.ccwu.cc/file/1775132808652_1774881267385_得意-1.gif', + 'https://tc.ciallo.ccwu.cc/file/1775132804506_1774881263342_观察.gif', + 'https://tc.ciallo.ccwu.cc/file/1775132811492_1774881264336_不要.gif' +] %} +{% set title_badge_colors = [ + {'bg': 'linear-gradient(135deg, #ffcadf 0%, #ffc1cc 45%, #ffd9b8 100%)', 'text': '#b85c7b', 'shadow': 'rgba(255, 177, 203, 0.28)', 'border': 'rgba(255, 184, 210, 0.72)'}, + {'bg': 'linear-gradient(135deg, #bfe7ff 0%, #c8dbff 48%, #d8ccff 100%)', 'text': '#5d71b8', 'shadow': 'rgba(168, 198, 255, 0.26)', 'border': 'rgba(173, 204, 255, 0.74)'}, + {'bg': 'linear-gradient(135deg, #c7f3df 0%, #d3f1ff 50%, #e1ddff 100%)', 'text': '#4e8e88', 'shadow': 'rgba(161, 232, 205, 0.24)', 'border': 'rgba(178, 230, 214, 0.72)'}, + {'bg': 'linear-gradient(135deg, #ffe5b7 0%, #ffd6c7 48%, #ffd7ef 100%)', 'text': '#b57752', 'shadow': 'rgba(255, 210, 163, 0.28)', 'border': 'rgba(255, 214, 178, 0.76)'} +] %} +{% for title in titles %} +{% set badge = title_badge_colors[loop.index0 % (title_badge_colors|length)] %} +
  • +
    +
    + {% if title.avatar_data %} + 头像 + {% endif %} +
    +
    + {{ title.name }} + + {{ title.title }} + +
    +
    +

    人格标签: {{ title.profile_display or title.mbti }}

    +

    称号理由: {{ title.reason }}

    +
    +
    +
    +
    + {% if title.profile_image %} + + {% endif %} + 称号表情 +
  • +{% endfor %} diff --git a/src/infrastructure/reporting/templates/HatsuneMiku/user_title_item.html b/src/infrastructure/reporting/templates/HatsuneMiku/user_title_item.html index ba6687f..92ed2a5 100644 --- a/src/infrastructure/reporting/templates/HatsuneMiku/user_title_item.html +++ b/src/infrastructure/reporting/templates/HatsuneMiku/user_title_item.html @@ -1,7 +1,7 @@ {% if titles %}
    {% for title in titles %} -
    +
    {% if title.avatar_data %} @@ -16,8 +16,8 @@
    {{ title.name }}
    {{ title.title }} - {% if title.mbti %} - {{ title.mbti }} + {% if title.profile_display or title.mbti %} + {{ title.profile_display or title.mbti }} {% endif %}
    @@ -26,7 +26,11 @@ {{ title.reason }} -
    + +
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %}
    diff --git a/src/infrastructure/reporting/templates/format/user_title_item.html b/src/infrastructure/reporting/templates/format/user_title_item.html index d53e52d..a67c17b 100644 --- a/src/infrastructure/reporting/templates/format/user_title_item.html +++ b/src/infrastructure/reporting/templates/format/user_title_item.html @@ -3,7 +3,7 @@

    群友称号

    {% for title in titles %} -
    +
    {{ title.reason }}
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %}
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/src/infrastructure/reporting/templates/hack/user_title_item.html b/src/infrastructure/reporting/templates/hack/user_title_item.html index e885030..c242c64 100644 --- a/src/infrastructure/reporting/templates/hack/user_title_item.html +++ b/src/infrastructure/reporting/templates/hack/user_title_item.html @@ -1,7 +1,7 @@ {% if titles %}
    {% for title in titles %} -
    +
    {% if title.avatar_data %}

    {{ title.name }}

    - {{ title.mbti }} + {{ title.profile_display or title.mbti }} {{ title.title }}
    {{ title.reason }}
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %}
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/src/infrastructure/reporting/templates/retro_futurism/user_title_item.html b/src/infrastructure/reporting/templates/retro_futurism/user_title_item.html index 30d3b82..3490d6e 100644 --- a/src/infrastructure/reporting/templates/retro_futurism/user_title_item.html +++ b/src/infrastructure/reporting/templates/retro_futurism/user_title_item.html @@ -1,6 +1,6 @@ {% if titles %} {% for title in titles %} -
    +
    {% if title.avatar_data %} {{ title.name }} @@ -13,12 +13,15 @@

    {{ title.name }}

    - {{ title.mbti }} + {{ title.profile_display or title.mbti }} // {{ title.title }}
    {{ title.reason }}
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %} -{% endif %} \ No newline at end of file +{% endif %} diff --git a/src/infrastructure/reporting/templates/scrapbook/user_title_item.html b/src/infrastructure/reporting/templates/scrapbook/user_title_item.html index d196797..b1d0ff6 100644 --- a/src/infrastructure/reporting/templates/scrapbook/user_title_item.html +++ b/src/infrastructure/reporting/templates/scrapbook/user_title_item.html @@ -9,7 +9,7 @@
    {% for title in titles %} -
    +
    @@ -26,15 +26,18 @@
    {{ title.name }}
    {{ title.title }} - {{ title.mbti }} + {{ title.profile_display or title.mbti }}
    {{ title.reason }}
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %}
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/src/infrastructure/reporting/templates/simple/user_title_item.html b/src/infrastructure/reporting/templates/simple/user_title_item.html index 3449e76..54087fd 100644 --- a/src/infrastructure/reporting/templates/simple/user_title_item.html +++ b/src/infrastructure/reporting/templates/simple/user_title_item.html @@ -2,11 +2,14 @@

    群友称号

    {% for item in titles %} -
    +
    {{ item.name }} - {{ item.title }}
    -
    {{ item.mbti }}
    -
    {{ item.reason }}
    +
    {{ item.profile_display or item.mbti }}
    +
    {{ item.reason }}
    + {% if item.profile_image %} + + {% endif %}
    {% endfor %}
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/src/infrastructure/reporting/templates/spring_festival/user_title_item.html b/src/infrastructure/reporting/templates/spring_festival/user_title_item.html index 606f43b..782061e 100644 --- a/src/infrastructure/reporting/templates/spring_festival/user_title_item.html +++ b/src/infrastructure/reporting/templates/spring_festival/user_title_item.html @@ -1,7 +1,7 @@ {% if titles %}
    {% for title in titles %} -
    +
    {% if title.avatar_data %} @@ -14,13 +14,16 @@
    {{ title.reason }}
    + {% if title.profile_image %} + + {% endif %}
    {% endfor %}
    -{% endif %} \ No newline at end of file +{% endif %}