Merge pull request #3 from Nezumi-2711/feat/vietnamese-localization

feat(i18n): add Vietnamese localization
This commit is contained in:
2026-07-22 18:05:20 +07:00
committed by GitHub
47 changed files with 688 additions and 576 deletions
+199 -199
View File
File diff suppressed because one or more lines are too long
+129 -87
View File
@@ -387,7 +387,7 @@ class GroupDailyAnalysis(Star):
if (".jpg" in image_url.lower() or ".jpeg" in image_url.lower())
else ".png"
)
nice_filename = f"群分析报告_{group_id}_{date_str}_{timestamp}{ext}"
nice_filename = f"bao_cao_phan_tich_nhom_{group_id}_{date_str}_{timestamp}{ext}"
try:
# 尝试通过适配器获取群名称,使文件名更具辨识度
@@ -396,9 +396,7 @@ class GroupDailyAnalysis(Star):
# 过滤非法文件名字符:\ / : * ? " < > |
safe_name = re.sub(r'[\\/:*?"<>|]', "", group_info.group_name).strip()
if safe_name:
nice_filename = (
f"群分析报告_{safe_name}_{date_str}_{timestamp}{ext}"
)
nice_filename = f"bao_cao_phan_tich_nhom_{safe_name}_{date_str}_{timestamp}{ext}"
except Exception:
pass
@@ -493,7 +491,7 @@ class GroupDailyAnalysis(Star):
except OSError:
pass
@filter.command("群分析", alias={"group_analysis"})
@filter.command("phantichnhom", alias={"group_analysis", "群分析"})
@filter.permission_type(PermissionType.ADMIN)
async def analyze_group_daily(
self, event: AstrMessageEvent, days: int | None = None
@@ -515,7 +513,7 @@ class GroupDailyAnalysis(Star):
platform_id = self._get_platform_id_from_event(event)
if not group_id:
yield event.plain_result("请在群聊中使用此命令")
yield event.plain_result("Vui lòng sử dụng lệnh này trong nhóm chat")
return
# 更新bot实例
@@ -531,7 +529,9 @@ class GroupDailyAnalysis(Star):
# But if list item HAS colon, we need precise match.
# If prompt fails, try simple ID as fallback for permissive cases?
# No, config_manager.is_group_allowed already handles simple ID matching if whitelist item is simple ID.
yield event.plain_result("❌ 此群未启用日常分析功能")
yield event.plain_result(
"❌ Nhóm này chưa bật tính năng phân tích hàng ngày"
)
return
# 获取群名以生成语义化的 TraceID
@@ -565,7 +565,9 @@ class GroupDailyAnalysis(Star):
)
if use_text_reply:
yield event.plain_result("🔍 正在启动分析引擎,正在拉取最近消息...")
yield event.plain_result(
"🔍 Đang khởi động phân tích và lấy các tin nhắn gần đây..."
)
elif adapter and orig_msg_id:
await adapter.set_reaction(
event.get_group_id(), orig_msg_id, "analysis_started"
@@ -579,13 +581,17 @@ class GroupDailyAnalysis(Star):
if not result.get("success"):
reason = result.get("reason")
if reason == "no_messages":
yield event.plain_result("❌ 未找到足够的群聊记录")
yield event.plain_result(
"❌ Không tìm thấy đủ dữ liệu chat trong nhóm"
)
elif reason == "muted":
logger.warning(
f"{group_id} 开启了全群禁言或对 Bot 禁言,跳过回复以防抛出发送异常"
)
else:
yield event.plain_result("❌ 分析失败,原因未知")
yield event.plain_result(
"❌ Phân tích thất bại, không rõ nguyên nhân"
)
return
if not use_text_reply and adapter and orig_msg_id:
@@ -597,13 +603,16 @@ class GroupDailyAnalysis(Star):
yield res
except DuplicateGroupTaskError:
yield event.plain_result("📊 该群的分析任务正在执行中,请稍后再试哦~")
yield event.plain_result(
"📊 Phân tích cho nhóm này đang chạy, vui lòng thử lại sau nhé~"
)
except asyncio.CancelledError:
logger.info("群分析任务被取消 (插件重载或卸载)")
except Exception as e:
logger.error(f"群分析失败: {e}", exc_info=True)
yield event.plain_result(
f"分析失败: {str(e)}。请检查网络连接和LLM配置,或联系管理员"
f"Phân tích thất bại: {str(e)}. Vui lòng kiểm tra kết nối "
"mạng, cấu hình LLM hoặc liên hệ quản trị viên"
)
finally:
if current_task:
@@ -699,7 +708,7 @@ class GroupDailyAnalysis(Star):
report_url = f"{base_url.rstrip('/')}/{url_path.lstrip('/')}"
yield event.plain_result(
f"📊 今日群聊分析报告已生成:\n{report_url}"
f"📊 Báo cáo phân tích nhóm hôm nay đã sẵn sàng:\n{report_url}"
)
return # 拦截成功,直接退出,不再发文件
else:
@@ -731,7 +740,7 @@ class GroupDailyAnalysis(Star):
if caption:
yield event.plain_result(caption)
else:
yield event.plain_result("⚠️ HTML 生成失败。")
yield event.plain_result("⚠️ Tạo báo cáo HTML thất bại.")
else:
await self._send_text_reports(
@@ -763,7 +772,7 @@ class GroupDailyAnalysis(Star):
return await adapter.send_text_report(group_id, tr, fallback_content=fr)
return await adapter.send_text_report(group_id, tr)
@filter.command("设置格式", alias={"set_format"})
@filter.command("dinhdang", alias={"set_format", "设置格式"})
@filter.permission_type(PermissionType.ADMIN)
async def set_output_format(self, event: AstrMessageEvent, format_input: str = ""):
"""
@@ -775,9 +784,9 @@ class GroupDailyAnalysis(Star):
available_formats = ["image", "text", "html"]
format_display_names = {
"image": "图片格式 (默认)",
"text": "文本格式",
"html": "交互式 HTML 网页",
"image": "Định dạng ảnh (mặc định)",
"text": "Định dạng văn bản",
"html": "Trang web HTML tương tác",
}
if not format_input:
@@ -788,12 +797,12 @@ class GroupDailyAnalysis(Star):
for i, f in enumerate(available_formats, start=1)
]
)
yield event.plain_result(f"""📊 当前输出格式: {current}
yield event.plain_result(f"""📊 Định dạng đầu ra hiện tại: {current}
可用格式:
Các định dạng khả dụng:
{format_list_str}
用法: /设置格式 [名称或序号] 如 /设置格式 image,html""")
Cách dùng: /dinhdang [tên hoặc số thứ tự], ví dụ: /dinhdang image,html""")
return
target_format = None
@@ -815,24 +824,30 @@ class GroupDailyAnalysis(Star):
if all(p in available_formats for p in parts) and len(parts) > 1:
try:
self.config_manager.set_output_format(parts)
yield event.plain_result(f"✅ 输出格式已设置为: {', '.join(parts)}")
yield event.plain_result(
f"✅ Định dạng đầu ra đã đặt thành: {', '.join(parts)}"
)
except Exception as e:
yield event.plain_result(f"设置失败: {e}")
yield event.plain_result(f"Cài đặt thất bại: {e}")
return
if not target_format:
yield event.plain_result(
f"无效的格式类型 '{format_input}'。可用: {', '.join(available_formats)} 或序号 1-{len(available_formats)}"
f"Định dạng '{format_input}' không hợp lệ. Có sẵn: "
f"{', '.join(available_formats)} hoặc số thứ tự "
f"1-{len(available_formats)}"
)
return
try:
self.config_manager.set_output_format(target_format) # type: ignore[arg-type]
yield event.plain_result(f"✅ 输出格式已设置为: {target_format}")
yield event.plain_result(
f"✅ Định dạng đầu ra đã đặt thành: {target_format}"
)
except Exception as e:
yield event.plain_result(f"设置失败: {e}")
yield event.plain_result(f"Cài đặt thất bại: {e}")
@filter.command("设置模板", alias={"set_template"})
@filter.command("maubc", alias={"set_template", "设置模板"})
@filter.permission_type(PermissionType.ADMIN)
async def set_report_template(
self, event: AstrMessageEvent, template_input: str = ""
@@ -853,13 +868,13 @@ class GroupDailyAnalysis(Star):
template_list_str = "\n".join(
[f"{i}{t}" for i, t in enumerate(available_templates, start=1)]
)
yield event.plain_result(f"""🎨 当前报告模板: {current_template}
yield event.plain_result(f"""🎨 Mẫu báo cáo hiện tại: {current_template}
可用模板:
Các mẫu khả dụng:
{template_list_str}
用法: /设置模板 [模板名称或序号]
💡 使用 /查看模板 查看预览图""")
Cách dùng: /maubc [tên mẫu hoặc số thứ tự]
💡 Sử dụng /xemmau để xem ảnh xem trước""")
return
template_name, parse_error = self.template_command_service.parse_template_input(
@@ -870,17 +885,19 @@ class GroupDailyAnalysis(Star):
return
if not template_name:
yield event.plain_result(f"❌ 无法解析模板输入: {template_input}")
yield event.plain_result(
f"❌ Không thể phân tích đầu vào mẫu: {template_input}"
)
return
if not await self.template_command_service.template_exists(template_name):
yield event.plain_result(f"模板 '{template_name}' 不存在")
yield event.plain_result(f"Mẫu '{template_name}' không tồn tại")
return
self.config_manager.set_report_template(template_name)
yield event.plain_result(f"报告模板已设置为: {template_name}")
yield event.plain_result(f"Mẫu báo cáo đã đặt thành: {template_name}")
@filter.command("查看模板", alias={"view_templates"})
@filter.command("xemmau", alias={"view_templates", "查看模板"})
@filter.permission_type(PermissionType.ADMIN)
async def view_templates(self, event: AstrMessageEvent):
"""
@@ -895,7 +912,7 @@ class GroupDailyAnalysis(Star):
)
if not available_templates:
yield event.plain_result("未找到任何可用的报告模板")
yield event.plain_result("Không tìm thấy mẫu báo cáo nào khả dụng")
return
platform_id = self._get_platform_id_from_event(event)
@@ -922,7 +939,7 @@ class GroupDailyAnalysis(Star):
)
yield event.chain_result([preview_nodes])
@filter.command("分析设置", alias={"analysis_settings"})
@filter.command("caidat", alias={"analysis_settings", "分析设置"})
@filter.permission_type(PermissionType.ADMIN)
async def analysis_settings(self, event: AstrMessageEvent, action: str = "status"):
"""
@@ -939,7 +956,7 @@ class GroupDailyAnalysis(Star):
group_id = self._get_group_id_from_event(event)
if not group_id:
yield event.plain_result("请在群聊中使用此命令")
yield event.plain_result("Vui lòng sử dụng lệnh này trong nhóm chat")
return
if action == "enable":
@@ -951,7 +968,9 @@ class GroupDailyAnalysis(Star):
elif action == "reload":
self.auto_scheduler.schedule_jobs(self.context)
yield event.plain_result("✅ 已重新加载配置并重启定时任务")
yield event.plain_result(
"✅ Đã tải lại cấu hình và khởi động lại tác vụ định kỳ"
)
elif action == "test":
check_target = getattr(event, "unified_msg_origin", None)
@@ -961,35 +980,45 @@ class GroupDailyAnalysis(Star):
)
if not self.config_manager.is_group_allowed(check_target):
yield event.plain_result("❌ 请先启用当前群的分析功能")
yield event.plain_result(
"❌ Vui lòng bật tính năng phân tích cho nhóm này trước"
)
return
yield event.plain_result("🧪 开始测试自动分析功能...")
yield event.plain_result("🧪 Đang kiểm tra tính năng phân tích tự động...")
# 更新bot实例(用于测试)
self.bot_manager.update_from_event(event)
try:
await self.auto_scheduler._perform_auto_analysis_for_group(group_id)
yield event.plain_result("✅ 自动分析测试完成,请查看群消息")
yield event.plain_result(
"✅ Kiểm tra phân tích tự động hoàn tất, vui lòng xem tin nhắn nhóm"
)
except DuplicateGroupTaskError:
yield event.plain_result("📊 该群的分析任务正在执行中,请稍后再试哦~")
yield event.plain_result(
"📊 Phân tích cho nhóm này đang chạy, vui lòng thử lại sau nhé~"
)
except Exception as e:
yield event.plain_result(f"❌ 自动分析测试失败: {str(e)}")
yield event.plain_result(
f"❌ Kiểm tra phân tích tự động thất bại: {str(e)}"
)
elif action == "incremental_debug":
current_state = self.config_manager.get_incremental_report_immediately()
new_state = not current_state
self.config_manager.set_incremental_report_immediately(new_state)
status_text = "已启用" if new_state else "已禁用"
yield event.plain_result(f"✅ 增量分析立即报告模式: {status_text}")
status_text = "Đã bật" if new_state else "Đã tắt"
yield event.plain_result(
f"✅ Chế độ báo cáo ngay phân tích gia tăng: {status_text}"
)
elif action == "filter_bot":
current = self.config_manager.get_filter_bot_messages()
new_state = not current
self.config_manager.set_filter_bot_messages(new_state)
status_text = "已启用" if new_state else "已禁用"
yield event.plain_result(f"过滤机器人消息: {status_text}")
status_text = "Đã bật" if new_state else "Đã tắt"
yield event.plain_result(f"Lọc tin nhắn bot: {status_text}")
else: # status
check_target = getattr(event, "unified_msg_origin", None)
@@ -999,11 +1028,13 @@ class GroupDailyAnalysis(Star):
)
is_allowed = self.config_manager.is_group_allowed(check_target)
status = "已启用" if is_allowed else "未启用"
status = "Đã bật" if is_allowed else "Chưa bật"
mode = self.config_manager.get_group_list_mode()
auto_status = (
"已启用" if self.config_manager.is_auto_analysis_enabled() else "未启用"
"Đã bật"
if self.config_manager.is_auto_analysis_enabled()
else "Chưa bật"
)
auto_time = self.config_manager.get_auto_analysis_time()
@@ -1012,46 +1043,48 @@ class GroupDailyAnalysis(Star):
# 增量分析状态
incremental_enabled = self.config_manager.get_incremental_enabled()
incremental_status_text = "未启用"
incremental_status_text = "Chưa bật"
if incremental_enabled:
interval = self.config_manager.get_incremental_interval_minutes()
max_daily = self.config_manager.get_incremental_max_daily_analyses()
active_start = self.config_manager.get_incremental_active_start_hour()
active_end = self.config_manager.get_incremental_active_end_hour()
incremental_status_text = (
f"已启用 (间隔{interval}分钟, 最多{max_daily}次/天, "
f"活跃时段{active_start}:00-{active_end}:00)"
f"Đã bật (mỗi {interval} phút, tối đa {max_daily} lần/ngày, "
f"khung giờ hoạt động {active_start}:00-{active_end}:00)"
)
debug_report = self.config_manager.get_incremental_report_immediately()
debug_status = "开启" if debug_report else "关闭"
debug_status = "Bật" if debug_report else "Tắt"
filter_bot = self.config_manager.get_filter_bot_messages()
filter_bot_status = "开启" if filter_bot else "关闭"
filter_bot_status = "Bật" if filter_bot else "Tắt"
yield event.plain_result(f"""📊 当前群分析功能状态:
• 群分析功能: {status} (模式: {mode})
• 自动分析: {auto_status} ({auto_time})
增量分析: {incremental_status_text}
调试模式: {debug_status} (增量立即报告)
过滤机器人: {filter_bot_status}
输出格式: {output_format}
• 最小消息数: {min_threshold}
yield event.plain_result(f"""📊 Trạng thái phân tích của nhóm hiện tại:
• Phân tích nhóm: {status} (chế độ: {mode})
• Phân tích tự động: {auto_status} ({auto_time})
Phân tích gia tăng: {incremental_status_text}
Chế độ gỡ lỗi: {debug_status} (báo cáo gia tăng ngay lập tức)
Lọc bot: {filter_bot_status}
Định dạng đầu ra: {output_format}
• Số tin nhắn tối thiểu: {min_threshold}
💡 可用命令: enable, disable, status, reload, test, filter_bot, incremental_debug
💡 支持的输出格式: image, text (图片包含活跃度可视化)
💡 其他命令: /设置格式, /增量状态""")
💡 Lệnh khả dụng: enable, disable, status, reload, test, filter_bot, incremental_debug
💡 Định dạng đầu ra được hỗ trợ: image, text (ảnh có biểu đồ hoạt động)
💡 Lệnh khác: /dinhdang, /tangcuong""")
@filter.command("增量状态", alias={"incremental_status"})
@filter.command("tangcuong", alias={"incremental_status", "增量状态"})
@filter.permission_type(PermissionType.ADMIN)
async def incremental_status(self, event: AstrMessageEvent):
"""查看当前增量分析状态(滑动窗口)"""
group_id = self._get_group_id_from_event(event)
if not group_id:
yield event.plain_result("请在群聊中使用此命令")
yield event.plain_result("Vui lòng sử dụng lệnh này trong nhóm chat")
return
if not self.config_manager.get_incremental_enabled():
yield event.plain_result("ℹ️ 增量分析模式未启用,请在插件配置中开启")
yield event.plain_result(
"️ Chế độ phân tích gia tăng chưa bật, vui lòng bật trong cấu hình plugin"
)
return
import time as time_mod
@@ -1072,7 +1105,8 @@ class GroupDailyAnalysis(Star):
start_str = datetime.fromtimestamp(window_start).strftime("%m-%d %H:%M")
end_str = datetime.fromtimestamp(window_end).strftime("%m-%d %H:%M")
yield event.plain_result(
f"📊 滑动窗口 ({start_str} ~ {end_str}) 内尚无增量分析数据"
f"📊 Chưa có dữ liệu phân tích gia tăng trong cửa sổ "
f"({start_str} ~ {end_str})"
)
return
@@ -1083,13 +1117,13 @@ class GroupDailyAnalysis(Star):
summary = state.get_summary()
yield event.plain_result(
f"📊 增量分析状态 (窗口: {summary['window']})\n"
f"分析次数: {summary['total_analyses']}\n"
f"累计消息: {summary['total_messages']}\n"
f"话题数: {summary['topics_count']}\n"
f"金句数: {summary['quotes_count']}\n"
f"参与者: {summary['participants']}\n"
f"高峰时段: {summary['peak_hours']}"
f"📊 Trạng thái phân tích gia tăng (cửa sổ: {summary['window']})\n"
f"Số lần phân tích: {summary['total_analyses']}\n"
f"Tổng số tin nhắn: {summary['total_messages']}\n"
f"Số chủ đề: {summary['topics_count']}\n"
f"Số trích dẫn nổi bật: {summary['quotes_count']}\n"
f"Người tham gia: {summary['participants']}\n"
f"Khung giờ cao điểm: {summary['peak_hours']}"
)
async def _handle_settings_enable(self, event: AstrMessageEvent, group_id: str):
@@ -1102,10 +1136,12 @@ class GroupDailyAnalysis(Star):
if not self.config_manager.is_group_allowed(target_id):
glist.append(target_id)
self.config_manager.set_group_list(glist)
yield event.plain_result(f"✅ 已将当前群加入白名单\nID: {target_id}")
yield event.plain_result(
f"✅ Đã thêm nhóm hiện tại vào whitelist\nID: {target_id}"
)
self.auto_scheduler.schedule_jobs(self.context)
else:
yield event.plain_result("当前群已在白名单中")
yield event.plain_result("Nhóm hiện tại đã có trong whitelist")
elif mode == "blacklist":
glist = self.config_manager.get_group_list()
removed = False
@@ -1118,12 +1154,14 @@ class GroupDailyAnalysis(Star):
if removed:
self.config_manager.set_group_list(glist)
yield event.plain_result("已将当前群从黑名单移除")
yield event.plain_result("Đã xóa nhóm hiện tại khỏi blacklist")
self.auto_scheduler.schedule_jobs(self.context)
else:
yield event.plain_result("当前群不在黑名单中")
yield event.plain_result("Nhóm hiện tại không có trong blacklist")
else:
yield event.plain_result("ℹ️ 当前为无限制模式,所有群聊默认启用")
yield event.plain_result(
"ℹ️ Đang ở chế độ không giới hạn, tất cả nhóm mặc định được bật"
)
async def _handle_settings_disable(self, event: AstrMessageEvent, group_id: str):
"""协助逻辑:处理禁用设置的分支逻辑"""
@@ -1142,18 +1180,22 @@ class GroupDailyAnalysis(Star):
if removed:
self.config_manager.set_group_list(glist)
yield event.plain_result("已将当前群从白名单移除")
yield event.plain_result("Đã xóa nhóm hiện tại khỏi whitelist")
self.auto_scheduler.schedule_jobs(self.context)
else:
yield event.plain_result("当前群不在白名单中")
yield event.plain_result("Nhóm hiện tại không có trong whitelist")
elif mode == "blacklist":
glist = self.config_manager.get_group_list()
if self.config_manager.is_group_allowed(target_id):
glist.append(target_id)
self.config_manager.set_group_list(glist)
yield event.plain_result(f"✅ 已将当前群加入黑名单\nID: {target_id}")
yield event.plain_result(
f"✅ Đã thêm nhóm hiện tại vào blacklist\nID: {target_id}"
)
self.auto_scheduler.schedule_jobs(self.context)
else:
yield event.plain_result("当前群已在黑名单中")
yield event.plain_result("Nhóm hiện tại đã có trong blacklist")
else:
yield event.plain_result("ℹ️ 当前为无限制模式,如需禁用请切换到黑名单模式")
yield event.plain_result(
"ℹ️ Đang ở chế độ không giới hạn; để tắt, hãy chuyển sang chế độ blacklist"
)
@@ -67,7 +67,7 @@ class TemplateCommandService:
) -> tuple[str | None, str | None]:
"""解析模板输入(支持模板名或序号)。"""
if not template_input:
return None, "模板参数不能为空"
return None, "Tham số mẫu không được để trống"
if template_input.isdigit():
index = int(template_input)
@@ -75,7 +75,8 @@ class TemplateCommandService:
return available_templates[index - 1], None
return (
None,
f"无效的序号 '{template_input}',有效范围: 1-{len(available_templates)}",
f"Số thứ tự '{template_input}' không hợp lệ; phạm vi hợp lệ: "
f"1-{len(available_templates)}",
)
return template_input, None
@@ -91,10 +92,12 @@ class TemplateCommandService:
header_content = [
Plain(
f"🎨 可用报告模板列表\n📌 当前使用: {current_template}\n💡 使用 /设置模板 [序号] 切换"
"🎨 Danh sách mẫu báo cáo khả dụng\n"
f"📌 Đang sử dụng: {current_template}\n"
"💡 Dùng /maubc [số thứ tự] để chuyển mẫu"
)
]
node_list.append(Node(uin=bot_id, name="模板预览", content=header_content))
node_list.append(Node(uin=bot_id, name="Xem trước mẫu", content=header_content))
for index, template_name in enumerate(available_templates):
current_mark = "" if template_name == current_template else ""
@@ -129,7 +129,7 @@ class AnalysisApplicationService:
# 1. 获取适配器
adapter = self.bot_manager.get_adapter(platform_id)
if not adapter:
raise ValueError(f"未找到平台 {platform_id} 的适配器")
raise ValueError(f"Không tìm thấy adapter cho nền tảng {platform_id}")
# 检查群聊是否被禁言(包括全体禁言或对 Bot 自身禁言)
if hasattr(adapter, "is_group_muted"):
@@ -160,7 +160,8 @@ class AnalysisApplicationService:
)
except Exception as e:
raise ValueError(
f"飞书成员信息预检查失败,请先完成应用权限授权:{e}"
"Kiểm tra trước thông tin thành viên Feishu thất bại; "
f"vui lòng cấp đủ quyền cho ứng dụng: {e}"
) from e
# 2. 拉取消息
@@ -347,7 +348,9 @@ class AnalysisApplicationService:
"""
async with self.group_lock(group_id, "incremental"):
if not self.incremental_store:
raise RuntimeError("增量分析未初始化:缺少 IncrementalStore")
raise RuntimeError(
"Phân tích tăng cường chưa được khởi tạo: thiếu IncrementalStore"
)
logger.info(
f"开始增量分析用例: 群 {group_id}, 平台 {platform_id or '默认'}"
@@ -356,7 +359,7 @@ class AnalysisApplicationService:
# 1. 获取适配器
adapter = self.bot_manager.get_adapter(platform_id)
if not adapter:
raise ValueError(f"未找到平台 {platform_id} 的适配器")
raise ValueError(f"Không tìm thấy adapter cho nền tảng {platform_id}")
# 检查群聊是否被禁言(包括全体禁言或对 Bot 自身禁言)
if hasattr(adapter, "is_group_muted"):
@@ -626,7 +629,8 @@ class AnalysisApplicationService:
async with self.group_lock(group_id, "final"):
if not self.incremental_store or not self.incremental_merge_service:
raise RuntimeError(
"增量分析未初始化:缺少 IncrementalStore 或 IncrementalMergeService"
"Phân tích tăng cường chưa được khởi tạo: thiếu "
"IncrementalStore hoặc IncrementalMergeService"
)
logger.info(
@@ -658,7 +662,7 @@ class AnalysisApplicationService:
# 5. 获取适配器(报告发送需要)
adapter = self.bot_manager.get_adapter(platform_id)
if not adapter:
raise ValueError(f"未找到平台 {platform_id} 的适配器")
raise ValueError(f"Không tìm thấy adapter cho nền tảng {platform_id}")
# 检查群聊是否被禁言(包括全体禁言或对 Bot 自身禁言)
if hasattr(adapter, "is_group_muted"):
@@ -27,7 +27,7 @@ class GoldenQuoteAnalyzer(BaseAnalyzer[GoldenQuote, list[dict]]):
def get_data_type(self) -> str:
"""获取数据类型标识"""
return "金句"
return "Trích dẫn nổi bật"
def get_max_count(self) -> int:
"""获取最大金句数量,增量模式下使用覆盖值"""
@@ -28,7 +28,7 @@ class TopicAnalyzer(BaseAnalyzer[SummaryTopic, list[dict]]):
def get_data_type(self) -> str:
"""获取数据类型标识"""
return "话题"
return "Chủ đề"
def get_max_count(self) -> int:
"""获取最大话题数量,增量模式下使用覆盖值"""
@@ -24,7 +24,7 @@ class UserTitleAnalyzer(BaseAnalyzer[UserTitle, dict]):
def get_data_type(self) -> str:
"""获取数据类型标识"""
return "用户称号"
return "Danh hiệu thành viên"
def get_max_count(self) -> int:
"""获取最大用户称号数量"""
+124 -61
View File
@@ -39,62 +39,113 @@ TRANSPARENT_IMAGE_DATA_URI = (
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": "表演者"},
"INTJ": {"code": "INTJ", "name_zh": "Kiến trúc sư"},
"INTP": {"code": "INTP", "name_zh": "Nhà logic học"},
"ENTJ": {"code": "ENTJ", "name_zh": "Nhà chỉ huy"},
"ENTP": {"code": "ENTP", "name_zh": "Nhà tranh biện"},
"INFJ": {"code": "INFJ", "name_zh": "Người cố vấn"},
"INFP": {"code": "INFP", "name_zh": "Người hòa giải"},
"ENFJ": {"code": "ENFJ", "name_zh": "Người truyền cảm hứng"},
"ENFP": {"code": "ENFP", "name_zh": "Người vận động"},
"ISTJ": {"code": "ISTJ", "name_zh": "Nhà hậu cần"},
"ISFJ": {"code": "ISFJ", "name_zh": "Người bảo vệ"},
"ESTJ": {"code": "ESTJ", "name_zh": "Nhà điều hành"},
"ESTP": {"code": "ESTP", "name_zh": "Doanh nhân"},
"ISTP": {"code": "ISTP", "name_zh": "Nhà kỹ thuật"},
"ISFP": {"code": "ISFP", "name_zh": "Nhà thám hiểm"},
"ESFJ": {"code": "ESFJ", "name_zh": "Người lãnh sự"},
"ESFP": {"code": "ESFP", "name_zh": "Người trình diễn"},
},
"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"},
"INTJ": {"code": "CTRL", "name_zh": "Người kiểm soát", "asset_code": "CTRL"},
"INTP": {"code": "THIN-K", "name_zh": "Người suy tư", "asset_code": "THIN-K"},
"ENTJ": {"code": "BOSS", "name_zh": "Thủ lĩnh", "asset_code": "BOSS"},
"ENTP": {"code": "JOKE-R", "name_zh": "Chú hề", "asset_code": "JOKE-R"},
"INFJ": {"code": "LOVE-R", "name_zh": "Người đa tình", "asset_code": "LOVE-R"},
"INFP": {"code": "SOLO", "name_zh": "Kẻ cô độc", "asset_code": "SOLO"},
"ENFJ": {"code": "THAN-K", "name_zh": "Người biết ơn", "asset_code": "THAN-K"},
"ENFP": {"code": "GOGO", "name_zh": "Lữ khách", "asset_code": "GOGO"},
"ISTJ": {"code": "OH-NO", "name_zh": "Người hay lo", "asset_code": "OH-NO"},
"ISTP": {"code": "POOR", "name_zh": "Người khốn khó", "asset_code": "POOR"},
"ESTJ": {"code": "SHIT", "name_zh": "Người bất mãn", "asset_code": "SHIT"},
"ESTP": {"code": "WOC!", "name_zh": "Người kinh ngạc", "asset_code": "WOC"},
"ISFJ": {"code": "MUM", "name_zh": "Người chăm sóc", "asset_code": "MUM"},
"ISFP": {"code": "MALO", "name_zh": "Chú khỉ", "asset_code": "MALO"},
"ESFJ": {"code": "ATM-er", "name_zh": "Nhà tài trợ", "asset_code": "ATM-er"},
"ESFP": {"code": "SEXY", "name_zh": "Người quyến rũ", "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": "芙宁娜"},
"INTP": {"code": "KNAN", "name_zh": "Edogawa Conan"},
"ENTJ": {"code": "SAKI", "name_zh": "Togawa Sakiko"},
"ENTP": {"code": "CHKA", "name_zh": "Fujiwara Chika"},
"INFJ": {"code": "DLRS", "name_zh": "Misumi Uika"},
"INFP": {"code": "BCHI", "name_zh": "Gotoh Hitori"},
"ENFJ": {"code": "YCYO", "name_zh": "Tsukimi Yachiyo"},
"ENFP": {"code": "HTMK", "name_zh": "Hatsune Miku"},
"ISTJ": {"code": "MRTS", "name_zh": "Wakaba Mutsumi"},
"ISTP": {"code": "AYRE", "name_zh": "Ayanami Rei"},
"ESTJ": {"code": "MIKT", "name_zh": "Misaka Mikoto"},
"ESTP": {"code": "ASKA", "name_zh": "Asuka"},
"ISFJ": {"code": "SOYO", "name_zh": "Nagasaki Soyo"},
"ISFP": {"code": "LTYI", "name_zh": "Luo Tianyi"},
"ESFJ": {"code": "ANON", "name_zh": "Chihaya Anon"},
"ESFP": {"code": "FRNA", "name_zh": "Furina"},
},
}
DEFAULT_PROFILE_NAME_TRANSLATIONS = {
"建筑师": "Kiến trúc sư",
"逻辑学家": "Nhà logic học",
"指挥官": "Nhà chỉ huy",
"辩论家": "Nhà tranh biện",
"提倡者": "Người cố vấn",
"调停者": "Người hòa giải",
"主人公": "Người truyền cảm hứng",
"竞选者": "Người vận động",
"物流师": "Nhà hậu cần",
"守卫者": "Người bảo vệ",
"总经理": "Nhà điều hành",
"企业家": "Doanh nhân",
"鉴赏家": "Nhà kỹ thuật",
"探险家": "Nhà thám hiểm",
"执政官": "Người lãnh sự",
"表演者": "Người trình diễn",
"拿捏者": "Người kiểm soát",
"思考者": "Người suy tư",
"领导者": "Thủ lĩnh",
"小丑": "Chú hề",
"多情者": "Người đa tình",
"孤儿": "Kẻ cô độc",
"感恩者": "Người biết ơn",
"行者": "Lữ khách",
"哦不人": "Người hay lo",
"贫困者": "Người khốn khó",
"愤世者": "Người bất mãn",
"握草人": "Người kinh ngạc",
"妈妈": "Người chăm sóc",
"吗喽": "Chú khỉ",
"送钱者": "Nhà tài trợ",
"尤物": "Người quyến rũ",
"江户川柯南": "Edogawa Conan",
"丰川祥子": "Togawa Sakiko",
"藤原千花": "Fujiwara Chika",
"三角初华": "Misumi Uika",
"后藤一里": "Gotoh Hitori",
"月见八千代": "Tsukimi Yachiyo",
"初音未来": "Hatsune Miku",
"若叶睦": "Wakaba Mutsumi",
"绫波丽": "Ayanami Rei",
"御坂美琴": "Misaka Mikoto",
"明日香": "Asuka",
"长崎爽世": "Nagasaki Soyo",
"洛天依": "Luo Tianyi",
"千早爱音": "Chihaya Anon",
"芙宁娜": "Furina",
}
class ReportGenerator(IReportGenerator):
"""报告生成器"""
@@ -162,6 +213,18 @@ class ReportGenerator(IReportGenerator):
try:
data = json.loads(raw)
if isinstance(data, dict):
for profiles in data.values():
if not isinstance(profiles, dict):
continue
for profile in profiles.values():
if not isinstance(profile, dict):
continue
current_name = str(profile.get("name_zh", "")).strip()
translated_name = DEFAULT_PROFILE_NAME_TRANSLATIONS.get(
current_name
)
if translated_name:
profile["name_zh"] = translated_name
return data
except Exception as e:
logger.warning(f"人格映射配置 JSON 解析失败,已回退到默认映射: {e}")
@@ -649,7 +712,7 @@ class ReportGenerator(IReportGenerator):
def build_html_caption(self, html_path: str) -> str:
"""根据 html_base_url 生成 HTML 报告链接 caption。由调用方决定是否发送。"""
caption = "📊 每日群聊分析报告已生成"
caption = "📊 Đã tạo báo cáo phân tích nhóm hàng ngày"
base_url = self.config_manager.get_html_base_url()
if not base_url or not html_path:
return caption
@@ -676,33 +739,33 @@ class ReportGenerator(IReportGenerator):
user_titles = analysis_result["user_titles"]
report = f"""
🎯 群聊日常分析报告
📅 {datetime.now().strftime("%Y年%m月%d")}
🎯 Báo cáo phân tích nhóm hàng ngày
📅 {datetime.now().strftime("%d/%m/%Y")}
📊 基础统计
消息总数: {stats.message_count}
参与人数: {stats.participant_count}
总字符数: {stats.total_characters}
表情数量: {stats.emoji_count}
最活跃时段: {stats.most_active_period}
📊 Thống kê cơ bản
Tổng số tin nhắn: {stats.message_count}
Số người tham gia: {stats.participant_count}
Tổng số ký tự: {stats.total_characters}
Số biểu cảm: {stats.emoji_count}
Khung giờ sôi nổi nhất: {stats.most_active_period}
💬 热门话题
💬 Chủ đề nổi bật
"""
max_topics = self.config_manager.get_max_topics()
for i, topic in enumerate(topics[:max_topics], 1):
contributors_str = "".join(topic.contributors)
contributors_str = ", ".join(topic.contributors)
report += f"{i}. {topic.topic}\n"
report += f" 参与者: {contributors_str}\n"
report += f" Người tham gia: {contributors_str}\n"
report += f" {topic.detail}\n\n"
report += "🏆 群友称号\n"
report += "🏆 Danh hiệu thành viên\n"
max_user_titles = self.config_manager.get_max_user_titles()
for title in user_titles[:max_user_titles]:
report += f"{title.name} - {title.title} ({title.mbti})\n"
report += f" {title.reason}\n\n"
report += "💬 群圣经\n"
report += "💬 Trích dẫn nổi bật\n"
max_golden_quotes = self.config_manager.get_max_golden_quotes()
for i, golden_quote in enumerate(stats.golden_quotes[:max_golden_quotes], 1):
report += f'{i}. "{golden_quote.content}" —— {golden_quote.sender}\n'
@@ -21,10 +21,10 @@
<div class="q-bubble">
<div class="q-content">{{ quote.content }}</div>
</div>
<img class="quote-floating-emoji" src="{{ quote_emojis | random }}" alt="金句表情">
<img class="quote-floating-emoji" src="{{ quote_emojis | random }}" alt="Biểu cảm trích dẫn">
</div>
<div class="q-analysis-note">
<span class="note-label">📝 亚托莉的高性能分析:</span>
<span class="note-label">📝 Phân tích hiệu suất cao của ATRI:</span>
{{ quote.reason | safe }}
</div>
</div>
@@ -28,9 +28,9 @@
</div>
<div class="item-content">
{% if topic.topic.count is defined %}
<p><strong>讨论热度:</strong> {{ topic.topic.count }} 次提及</p>
<p><strong>Độ sôi nổi:</strong> {{ topic.topic.count }} lượt nhắc</p>
{% endif %}
<p><strong>参与的群友:</strong> {{ topic.contributors }}</p>
<p><strong>Thành viên tham gia:</strong> {{ topic.contributors }}</p>
{% if topic.detail %}
<div
style="margin-top: 8px; padding: 12px 18px; background: linear-gradient(180deg, rgba(245,250,255,0.95), rgba(236,247,255,0.88)); border-radius: 16px; border: 1.5px solid rgba(255, 255, 255, 0.8); box-shadow: 0 4px 12px rgba(85,126,169,0.03); color: var(--text-main); line-height: 1.6;">
@@ -39,6 +39,6 @@
{% endif %}
</div>
</div>
<img class="item-emoji topic-emoji" src="{{ topic_emojis | random }}" alt="话题表情">
<img class="item-emoji topic-emoji" src="{{ topic_emojis | random }}" alt="Biểu cảm chủ đề">
</li>
{% endfor %}
@@ -17,7 +17,7 @@
<div style="display: flex; align-items: center; gap: 15px;">
{% if title.avatar_data %}
<img src="{{ title.avatar_data }}"
alt="头像"
alt="Ảnh đại diện"
style="width: 50px; height: 50px; border-radius: 50%; object-fit: cover; border: 2px solid #667eea;">
{% endif %}
<div style="flex: 1;">
@@ -28,8 +28,8 @@
</span>
</div>
<div class="item-content">
<p><strong>人格标签:</strong> {{ title.profile_display or title.mbti }}</p>
<p><strong>称号理由:</strong> {{ title.reason }}</p>
<p><strong>Nhãn tính cách:</strong> {{ title.profile_display or title.mbti }}</p>
<p><strong>Lý do danh hiệu:</strong> {{ title.reason }}</p>
</div>
</div>
</div>
@@ -37,7 +37,7 @@
{% if title.profile_image %}
<img src="{{ title.profile_image }}" alt="" style="position: absolute; right: 8px; bottom: 8px; width: 36%; max-height: 78%; object-fit: {{ title.profile_image_size_mode or 'contain' }}; opacity: {{ title.profile_image_opacity or 0.12 }}; pointer-events: none; z-index: 1;">
{% endif %}
<img class="item-emoji title-emoji" src="{{ title_emojis | random }}" alt="称号表情" style="z-index: 2;">
<img class="item-emoji title-emoji" src="{{ title_emojis | random }}" alt="Biểu cảm danh hiệu" style="z-index: 2;">
</li>
{% endfor %}
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阿洛娜的今日群聊速报! · {{current_datetime}}</title>
<title>Bản tin nhóm hôm nay của Arona! · {{current_datetime}}</title>
<link rel="stylesheet" href="//cdn.xcnahida.cn/fonts/bluearchive/Blueaka/Blueaka.css">
<link rel="stylesheet" href="//cdn.xcnahida.cn/fonts/bluearchive/Blueaka_Bold/Blueaka_Bold.css">
@@ -1158,14 +1158,14 @@
<div class="header">
<div class="header-aron">
<img class="aron-img" src="https://img.dkdun.cn/v1/2026/17/7170c8669dad05dd.png" alt="阿洛娜">
<img class="aron-img" src="https://img.dkdun.cn/v1/2026/17/7170c8669dad05dd.png" alt="Arona">
<div class="aron-speech">
<p>老师,这是今天的作战报告!</p>
<p>Sensei, đây là báo cáo hoạt động hôm nay!</p>
</div>
</div>
<div class="header-content">
<div class="header-accent"></div>
<h1>阿洛娜的今日群聊速报!</h1>
<h1>Bản tin nhóm hôm nay của Arona!</h1>
<span class="date-badge">{{current_date}}</span>
</div>
</div>
@@ -1177,35 +1177,35 @@
<svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
</div>
<div class="stat-num">{{message_count}}</div>
<div class="stat-label">消息总数</div>
<div class="stat-label">Tổng tin nhắn</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
</div>
<div class="stat-num">{{participant_count}}</div>
<div class="stat-label">参与人数</div>
<div class="stat-label">Người tham gia</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg>
</div>
<div class="stat-num">{{emoji_count}}</div>
<div class="stat-label">表情统计</div>
<div class="stat-label">Biểu cảm</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
</div>
<div class="stat-num">{{total_characters}}</div>
<div class="stat-label">总字符数</div>
<div class="stat-label">Tổng ký tự</div>
</div>
</div>
<div class="highlight-section">
<div class="highlight-label">⚡ Highlight Time</div>
<div class="time-big">{{most_active_period}}</div>
<div class="time-desc">最活跃的时刻</div>
<div class="time-desc">Khung giờ sôi nổi nhất</div>
</div>
</div>
@@ -1216,7 +1216,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/></svg>
</div>
24H 活跃轨迹
Hoạt động 24 giờ
</div>
<div class="chart-content-wrapper">
{{hourly_chart_html | safe}}
@@ -1233,7 +1233,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path fill="currentColor" d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.488.488 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
</div>
群聊质量锐评
Đánh giá chất lượng trò chuyện
</div>
{{ chat_quality_html | safe }}
</div>
@@ -1248,7 +1248,7 @@
</div>
<div class="footer-card-content">
<p>{{current_datetime}}</p>
<p style="font-size: 0.8em; opacity: 0.7;">AstrBot 群分析总结插件<br>主题设计:香草味的纳西妲喵(VanillaNahida</p>
<p style="font-size: 0.8em; opacity: 0.7;">Plugin tổng hợp phân tích nhóm AstrBot<br>Thiết kế chủ đề: VanillaNahida</p>
</div>
</div>
@@ -1264,7 +1264,7 @@
<div class="footer-card stats-card">
<div class="footer-card-title">
<span class="footer-icon">⚙️</span> Token 消耗
<span class="footer-icon">⚙️</span> Token đã dùng
</div>
<div class="footer-card-content">
<p>Total: {{total_tokens}} Tokens</p>
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阿洛娜的今日群聊速报! · {{current_datetime}}</title>
<title>Bản tin nhóm hôm nay của Arona! · {{current_datetime}}</title>
<link rel="stylesheet" href="//cdn.xcnahida.cn/fonts/bluearchive/Blueaka/Blueaka.css">
<link rel="stylesheet" href="//cdn.xcnahida.cn/fonts/bluearchive/Blueaka_Bold/Blueaka_Bold.css">
@@ -1158,14 +1158,14 @@
<div class="header">
<div class="header-aron">
<img class="aron-img" src="https://img.dkdun.cn/v1/2026/17/7170c8669dad05dd.png" alt="阿洛娜">
<img class="aron-img" src="https://img.dkdun.cn/v1/2026/17/7170c8669dad05dd.png" alt="Arona">
<div class="aron-speech">
<p>老师,这是今天的作战报告!</p>
<p>Sensei, đây là báo cáo hoạt động hôm nay!</p>
</div>
</div>
<div class="header-content">
<div class="header-accent"></div>
<h1>阿洛娜的今日群聊速报!</h1>
<h1>Bản tin nhóm hôm nay của Arona!</h1>
<span class="date-badge">{{current_date}}</span>
</div>
</div>
@@ -1177,35 +1177,35 @@
<svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
</div>
<div class="stat-num">{{message_count}}</div>
<div class="stat-label">消息总数</div>
<div class="stat-label">Tổng tin nhắn</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
</div>
<div class="stat-num">{{participant_count}}</div>
<div class="stat-label">参与人数</div>
<div class="stat-label">Người tham gia</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></svg>
</div>
<div class="stat-num">{{emoji_count}}</div>
<div class="stat-label">表情统计</div>
<div class="stat-label">Biểu cảm</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<svg viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
</div>
<div class="stat-num">{{total_characters}}</div>
<div class="stat-label">总字符数</div>
<div class="stat-label">Tổng ký tự</div>
</div>
</div>
<div class="highlight-section">
<div class="highlight-label">⚡ Highlight Time</div>
<div class="time-big">{{most_active_period}}</div>
<div class="time-desc">最活跃的时刻</div>
<div class="time-desc">Khung giờ sôi nổi nhất</div>
</div>
</div>
@@ -1216,7 +1216,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/></svg>
</div>
24H 活跃轨迹
Hoạt động 24 giờ
</div>
<div class="chart-content-wrapper">
{{hourly_chart_html | safe}}
@@ -1233,7 +1233,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path fill="currentColor" d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.488.488 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
</div>
群聊质量锐评
Đánh giá chất lượng trò chuyện
</div>
{{ chat_quality_html | safe }}
</div>
@@ -1248,7 +1248,7 @@
</div>
<div class="footer-card-content">
<p>{{current_datetime}}</p>
<p style="font-size: 0.8em; opacity: 0.7;">AstrBot 群分析总结插件<br>主题设计:香草味的纳西妲喵(VanillaNahida</p>
<p style="font-size: 0.8em; opacity: 0.7;">Plugin tổng hợp phân tích nhóm AstrBot<br>Thiết kế chủ đề: VanillaNahida</p>
</div>
</div>
@@ -1264,7 +1264,7 @@
<div class="footer-card stats-card">
<div class="footer-card-title">
<span class="footer-icon">⚙️</span> Token 消耗
<span class="footer-icon">⚙️</span> Token đã dùng
</div>
<div class="footer-card-content">
<p>Total: {{total_tokens}} Tokens</p>
@@ -4,7 +4,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>
</div>
群贤毕至 Bible Quotes
Trích dẫn nổi bật · Bible Quotes
</div>
{% for quote in quotes %}
<div class="quote-wrapper avoid-break">
@@ -23,7 +23,7 @@
<div class="q-content">{{ quote.content }}</div>
</div>
<div class="q-analysis-note">
<span class="note-label">🤣👉 AI 锐评:</span>
<span class="note-label">🤣👉 Nhận xét của AI:</span>
{{ quote.reason | safe }}
</div>
</div>
@@ -4,14 +4,14 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"/></svg>
</div>
今日话题 Topics
Chủ đề hôm nay · Topics
</div>
{% for topic in topics %}
<div class="topic-item avoid-break">
<div class="topic-index">{{ "%02d" | format(topic.index) }}</div>
<div class="topic-content">
<div class="topic-title">{{ topic.topic.topic }}</div>
<div class="topic-contributors">🙋‍♀️ 参与者: {{ topic.contributors }}</div>
<div class="topic-contributors">🙋‍♀️ Người tham gia: {{ topic.contributors }}</div>
<div class="topic-detail">{{ topic.detail | safe }}</div>
</div>
</div>
@@ -4,7 +4,7 @@
<div class="section-icon">
<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
</div>
群友画像 Portraits
Chân dung thành viên · Portraits
</div>
<div class="masonry-grid">
{% for title in titles %}
@@ -12,7 +12,7 @@
<div class="user-header">
<div class="u-avatar">
{% if title.avatar_data %}
<img src="{{ title.avatar_data }}" alt="头像" style="width: 100%; height: 100%; object-fit: cover;">
<img src="{{ title.avatar_data }}" alt="Ảnh đại diện" style="width: 100%; height: 100%; object-fit: cover;">
{% else %}
<svg class="doodle" style="font-size: 2rem;" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
@@ -37,7 +37,7 @@
{% for item in chart_data %}
<div style="flex: 1; text-align: center;">
<span style="font-family: var(--font-num); font-size: 0.7rem; color: var(--text-sub); font-weight: bold; white-space: nowrap; display: inline-block;">
{{ "%02d"|format(item.hour) }}{% if loop.index0 % 4 == 0 or loop.last %}<span class="miku-hour-unit"></span>{% endif %}
{{ "%02d"|format(item.hour) }}{% if loop.index0 % 4 == 0 or loop.last %}<span class="miku-hour-unit">h</span>{% endif %}
</span>
</div>
{% endfor %}
@@ -30,7 +30,7 @@
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>
<path d="M19 10v1a7 7 0 0 1-14 0v-1"/>
<line x1="12" x2="12" y1="19" y2="22"/>
</svg> <span style="font-weight: bold; font-size: 1.1rem;">Miku 综合点评</span>
</svg> <span style="font-weight: bold; font-size: 1.1rem;">Đánh giá tổng hợp của Miku</span>
</div>
<div style="line-height: 1.8; font-size: 1rem; opacity: 0.95;">{{ summary }}</div>
</div>
@@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>群聊日常分析看板 · {{current_date}}</title>
<title>Bảng phân tích nhóm hàng ngày · {{current_date}}</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<link
@@ -1077,7 +1077,7 @@
height: clamp(38px, 12vw, 52px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(76px, 24vw, 112px) !important;
height: 100% !important;
object-fit: contain !important;
@@ -1282,7 +1282,7 @@
height: clamp(34px, 11vw, 46px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(68px, 22vw, 96px) !important;
}
@@ -1364,7 +1364,7 @@
opacity: 0.92 !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(58px, 19vw, 86px) !important;
height: 100% !important;
object-fit: contain !important;
@@ -1465,7 +1465,7 @@
height: clamp(26px, 8.5vw, 36px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(50px, 17vw, 74px) !important;
}
@@ -1504,7 +1504,7 @@
height: 24px !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: 56px !important;
}
@@ -1653,36 +1653,36 @@
<div class="deco-image"></div>
<div class="header">
<h1>群聊日常分析看板</h1>
<div class="date-line">分析日志 // {{current_date}}</div>
<h1>Bảng phân tích nhóm hàng ngày</h1>
<div class="date-line">NHẬT KÝ PHÂN TÍCH // {{current_date}}</div>
</div>
<div class="stats-row">
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="message-square" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">今日消息数</div>
<div class="stat-txt" style="margin: 0;">Tin nhắn hôm nay</div>
</div>
<div class="stat-num">{{message_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="users" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">活跃成员数</div>
<div class="stat-txt" style="margin: 0;">Thành viên hoạt động</div>
</div>
<div class="stat-num">{{participant_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="smile" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">表情互动</div>
<div class="stat-txt" style="margin: 0;">Tương tác biểu cảm</div>
</div>
<div class="stat-num">{{emoji_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="type" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">今日字数累计</div>
<div class="stat-txt" style="margin: 0;">Ký tự hôm nay</div>
</div>
<div class="stat-num">{{total_characters}}</div>
</div>
@@ -1692,7 +1692,7 @@
<div>
<div class="peak-label">
<i data-lucide="trending-up" style="width: 20px; height: 20px;"></i>
峰值活跃时间段
Khung giờ hoạt động cao điểm
</div>
<div class="peak-time-text">{{most_active_period}}</div>
</div>
@@ -1709,7 +1709,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="line-chart" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
24小时活动
Hoạt động 24 giờ
</div>
</div>
<div class="card-common" style="padding: 20px 10px;">
@@ -1725,7 +1725,7 @@
<div class="v-bar" style="background: var(--miku-pink-light);"></div>
<div class="section-title-text">
<i data-lucide="message-square" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
话题总结
Tổng hợp chủ đề
</div>
</div>
{{topics_html | safe}}
@@ -1739,7 +1739,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="users" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
群友画像
Chân dung thành viên
</div>
</div>
{{titles_html | safe}}
@@ -1753,7 +1753,7 @@
<div class="v-bar" style="background: var(--miku-pink-light);"></div>
<div class="section-title-text">
<i data-lucide="quote" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
今日圣经
Trích dẫn hôm nay
</div>
</div>
{{quotes_html | safe}}
@@ -1767,7 +1767,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="award" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
群聊锐评
Đánh giá trò chuyện
</div>
</div>
{{chat_quality_html | safe}}
@@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>群聊日常分析看板 · {{current_date}}</title>
<title>Bảng phân tích nhóm hàng ngày · {{current_date}}</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<link
@@ -894,7 +894,7 @@
height: clamp(38px, 12vw, 52px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(76px, 24vw, 112px) !important;
height: 100% !important;
object-fit: contain !important;
@@ -1099,7 +1099,7 @@
height: clamp(34px, 11vw, 46px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(68px, 22vw, 96px) !important;
}
@@ -1181,7 +1181,7 @@
opacity: 0.92 !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(58px, 19vw, 86px) !important;
height: 100% !important;
object-fit: contain !important;
@@ -1282,7 +1282,7 @@
height: clamp(26px, 8.5vw, 36px) !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: clamp(50px, 17vw, 74px) !important;
}
@@ -1321,7 +1321,7 @@
height: 24px !important;
}
.miku-topic-card img[alt="装饰图"] {
.miku-topic-card img[alt="Ảnh trang trí"] {
max-width: 56px !important;
}
@@ -1470,36 +1470,36 @@
<div class="deco-image"></div>
<div class="header">
<h1>群聊日常分析看板</h1>
<div class="date-line">分析日志 // {{current_date}}</div>
<h1>Bảng phân tích nhóm hàng ngày</h1>
<div class="date-line">NHẬT KÝ PHÂN TÍCH // {{current_date}}</div>
</div>
<div class="stats-row">
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="message-square" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">今日消息数</div>
<div class="stat-txt" style="margin: 0;">Tin nhắn hôm nay</div>
</div>
<div class="stat-num">{{message_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="users" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">活跃成员数</div>
<div class="stat-txt" style="margin: 0;">Thành viên hoạt động</div>
</div>
<div class="stat-num">{{participant_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="smile" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">表情互动</div>
<div class="stat-txt" style="margin: 0;">Tương tác biểu cảm</div>
</div>
<div class="stat-num">{{emoji_count}}</div>
</div>
<div class="stat-box">
<div style="display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: 5px;">
<i data-lucide="type" class="stat-icon"></i>
<div class="stat-txt" style="margin: 0;">今日字数累计</div>
<div class="stat-txt" style="margin: 0;">Ký tự hôm nay</div>
</div>
<div class="stat-num">{{total_characters}}</div>
</div>
@@ -1509,7 +1509,7 @@
<div>
<div class="peak-label">
<i data-lucide="trending-up" style="width: 20px; height: 20px;"></i>
峰值活跃时间段
Khung giờ hoạt động cao điểm
</div>
<div class="peak-time-text">{{most_active_period}}</div>
</div>
@@ -1526,7 +1526,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="line-chart" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
24小时活动
Hoạt động 24 giờ
</div>
</div>
<div class="card-common" style="padding: 20px 10px;">
@@ -1542,7 +1542,7 @@
<div class="v-bar" style="background: var(--miku-pink-light);"></div>
<div class="section-title-text">
<i data-lucide="message-square" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
话题总结
Tổng hợp chủ đề
</div>
</div>
{{topics_html | safe}}
@@ -1556,7 +1556,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="users" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
群友画像
Chân dung thành viên
</div>
</div>
{{titles_html | safe}}
@@ -1570,7 +1570,7 @@
<div class="v-bar" style="background: var(--miku-pink-light);"></div>
<div class="section-title-text">
<i data-lucide="quote" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
今日圣经
Trích dẫn hôm nay
</div>
</div>
{{quotes_html | safe}}
@@ -1584,7 +1584,7 @@
<div class="v-bar"></div>
<div class="section-title-text">
<i data-lucide="award" style="width: 24px; height: 24px; color: var(--miku-dark);"></i>
群聊锐评
Đánh giá trò chuyện
</div>
</div>
{{chat_quality_html | safe}}
@@ -18,7 +18,7 @@
<path d="M9 18V5l12-2v13"/>
<circle cx="6" cy="18" r="3"/>
<circle cx="18" cy="16" r="3"/>
</svg> <span style="font-weight: bold; color: var(--miku-dark); font-size: 0.95rem;">Miku 锐评</span>
</svg> <span style="font-weight: bold; color: var(--miku-dark); font-size: 0.95rem;">Nhận xét của Miku</span>
</div>
<div style="font-size: 0.9rem; color: var(--text-sub); line-height: 1.6;">{{ quote.reason | safe }}</div>
</div>
@@ -23,12 +23,12 @@
<svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" fill="none" style="width: 16px; height: 16px; color: var(--miku-dark);">
<path d="m12 8-9.04 9.06a2.82 2.82 0 1 0 3.98 3.98L16 12"/>
<circle cx="17" cy="7" r="5"/>
</svg> <span style="font-weight: 500;">参与成员:</span>
</svg> <span style="font-weight: 500;">Thành viên tham gia:</span>
<span style="color: var(--miku-dark); font-weight: 700;">{{ topic.contributors }}</span>
</div>
<div style="display: flex; align-items: center; height: 65px; opacity: 1; flex-shrink: 0;">
<img src="{{ miku_topic_images[loop.index0 % miku_topic_images|length] }}" alt="装饰图" style="height: 100%; width: auto; max-width: 150px; border: none; outline: none; background: transparent; object-fit: contain;">
<img src="{{ miku_topic_images[loop.index0 % miku_topic_images|length] }}" alt="Ảnh trang trí" style="height: 100%; width: auto; max-width: 150px; border: none; outline: none; background: transparent; object-fit: contain;">
</div>
</div>
</div>
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>群聊日常分析报告</title>
<title>Báo cáo phân tích nhóm hàng ngày</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<link
@@ -484,46 +484,46 @@
<body>
<div class="container">
<div class="header">
<h1>群聊日常分析报告</h1>
<h1>Báo cáo phân tích nhóm hàng ngày</h1>
<div class="date">{{current_date}}</div>
</div>
<div class="content">
<div class="section full-width-section">
<h2 class="section-title">基础统计</h2>
<h2 class="section-title">Thống kê cơ bản</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-number">{{message_count}}</div>
<div class="stat-label">消息总数</div>
<div class="stat-label">Tổng tin nhắn</div>
</div>
<div class="stat-card">
<div class="stat-number">{{participant_count}}</div>
<div class="stat-label">参与人数</div>
<div class="stat-label">Người tham gia</div>
</div>
<div class="stat-card">
<div class="stat-number">{{total_characters}}</div>
<div class="stat-label">总字符数</div>
<div class="stat-label">Tổng ký tự</div>
</div>
<div class="stat-card">
<div class="stat-number">{{emoji_count}}</div>
<div class="stat-label">表情数量</div>
<div class="stat-label">Số biểu cảm</div>
</div>
</div>
<div class="active-period">
<div class="time">{{most_active_period}}</div>
<div class="label">最活跃时段</div>
<div class="label">Khung giờ sôi nổi nhất</div>
</div>
</div>
<div class="activity-chart-container">
<div class="chart-header">
<div class="chart-title">24小时活跃度分布</div>
<div class="chart-title">Phân bố hoạt động 24 giờ</div>
</div>
{{hourly_chart_html | safe}}
</div>
{% if chat_quality_html %}
<div class="section full-width-section">
<h2 class="section-title">群聊质量分析</h2>
<h2 class="section-title">Phân tích chất lượng trò chuyện</h2>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -533,9 +533,9 @@
{{quotes_html | safe}}
</div>
<div class="footer">
由 AstrBot 群日常分析插件 生成 | {{current_datetime}}<br>
Được tạo bởi plugin phân tích nhóm hàng ngày AstrBot | {{current_datetime}}<br>
SXP-Simon/astrbot_plugin_qq_group_daily_analysis<br>
<span style="opacity: 0.7;">AI分析消耗:{{total_tokens}} tokens (输入: {{prompt_tokens}}, 输出: {{completion_tokens}})</span>
<span style="opacity: 0.7;">AI đã dùng: {{total_tokens}} token (đầu vào: {{prompt_tokens}}, đầu ra: {{completion_tokens}})</span>
</div>
</div>
</body>
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>群聊日常分析报告</title>
<title>Báo cáo phân tích nhóm hàng ngày</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<link
@@ -484,46 +484,46 @@
<body>
<div class="container">
<div class="header">
<h1>群聊日常分析报告</h1>
<h1>Báo cáo phân tích nhóm hàng ngày</h1>
<div class="date">{{current_date}}</div>
</div>
<div class="content">
<div class="section full-width-section">
<h2 class="section-title">基础统计</h2>
<h2 class="section-title">Thống kê cơ bản</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-number">{{message_count}}</div>
<div class="stat-label">消息总数</div>
<div class="stat-label">Tổng tin nhắn</div>
</div>
<div class="stat-card">
<div class="stat-number">{{participant_count}}</div>
<div class="stat-label">参与人数</div>
<div class="stat-label">Người tham gia</div>
</div>
<div class="stat-card">
<div class="stat-number">{{total_characters}}</div>
<div class="stat-label">总字符数</div>
<div class="stat-label">Tổng ký tự</div>
</div>
<div class="stat-card">
<div class="stat-number">{{emoji_count}}</div>
<div class="stat-label">表情数量</div>
<div class="stat-label">Số biểu cảm</div>
</div>
</div>
<div class="active-period">
<div class="time">{{most_active_period}}</div>
<div class="label">最活跃时段</div>
<div class="label">Khung giờ sôi nổi nhất</div>
</div>
</div>
<div class="activity-chart-container">
<div class="chart-header">
<div class="chart-title">24小时活跃度分布</div>
<div class="chart-title">Phân bố hoạt động 24 giờ</div>
</div>
{{hourly_chart_html | safe}}
</div>
{% if chat_quality_html %}
<div class="section full-width-section">
<h2 class="section-title">群聊质量分析</h2>
<h2 class="section-title">Phân tích chất lượng trò chuyện</h2>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -533,9 +533,9 @@
{{quotes_html | safe}}
</div>
<div class="footer">
由 AstrBot 群日常分析插件 生成 | {{current_datetime}}<br>
Được tạo bởi plugin phân tích nhóm hàng ngày AstrBot | {{current_datetime}}<br>
SXP-Simon/astrbot_plugin_qq_group_daily_analysis<br>
<span style="opacity: 0.7;">AI分析消耗:{{total_tokens}} tokens (输入: {{prompt_tokens}}, 输出: {{completion_tokens}})</span>
<span style="opacity: 0.7;">AI đã dùng: {{total_tokens}} token (đầu vào: {{prompt_tokens}}, đầu ra: {{completion_tokens}})</span>
</div>
</div>
</body>
@@ -1,6 +1,6 @@
{% if quotes %}
<div class="section">
<h2 class="section-title">群圣经</h2>
<h2 class="section-title">Trích dẫn nổi bật</h2>
{% for quote in quotes %}
<div class="quote-item">
<div class="quote-content">"{{ quote.content }}"</div>
@@ -1,6 +1,6 @@
{% if topics %}
<div class="section">
<h2 class="section-title">热门话题</h2>
<h2 class="section-title">Chủ đề nổi bật</h2>
<div class="topics-grid">
{% for topic in topics %}
<div class="topic-item">
@@ -8,7 +8,7 @@
<span class="topic-number">{{ topic.index }}</span>
<span class="topic-title">{{ topic.topic.topic }}</span>
</div>
<div class="topic-contributors">参与者: {{ topic.contributors }}</div>
<div class="topic-contributors">Người tham gia: {{ topic.contributors }}</div>
<div class="topic-detail">{{ topic.detail | safe }}</div>
</div>
{% endfor %}
@@ -1,12 +1,12 @@
{% if titles %}
<div class="section">
<h2 class="section-title">群友称号</h2>
<h2 class="section-title">Danh hiệu thành viên</h2>
<div class="users-grid">
{% for title in titles %}
<div class="user-title" style="position: relative; overflow: hidden;">
<div class="user-info">
{% if title.avatar_data %}
<img src="{{ title.avatar_data }}" class="user-avatar" alt="头像">
<img src="{{ title.avatar_data }}" class="user-avatar" alt="Ảnh đại diện">
{% else %}
<div class="user-avatar-placeholder">👤</div>
{% endif %}
@@ -117,7 +117,7 @@
TOKENS: <span class="footer-token">{{total_tokens}}</span>
[P: {{prompt_tokens}} | C: {{completion_tokens}}]
</div>
<div style="color: var(--accent-orange);">SXP-Simon/astrbot_plugin_qq_group_daily_analysis 群分析</div>
<div style="color: var(--accent-orange);">SXP-Simon/astrbot_plugin_qq_group_daily_analysis · Phân tích nhóm</div>
</footer>
</div>
</body>
@@ -117,7 +117,7 @@
TOKENS: <span class="footer-token">{{total_tokens}}</span>
[P: {{prompt_tokens}} | C: {{completion_tokens}}]
</div>
<div style="color: var(--accent-orange);">SXP-Simon/astrbot_plugin_qq_group_daily_analysis 群分析</div>
<div style="color: var(--accent-orange);">SXP-Simon/astrbot_plugin_qq_group_daily_analysis · Phân tích nhóm</div>
</footer>
</div>
</body>
@@ -729,25 +729,25 @@
<div class="stat-box">
<span class="stat-label">Messages</span>
<div class="stat-value">{{message_count}}</div>
<div class="stat-note">累计消息数量</div>
<div class="stat-note">Tổng số tin nhắn</div>
</div>
<div class="stat-box">
<span class="stat-label">Participants</span>
<div class="stat-value">{{participant_count}}</div>
<div class="stat-note">参与者计数</div>
<div class="stat-note">Số người tham gia</div>
</div>
<div class="stat-box">
<span class="stat-label">Characters</span>
<div class="stat-value">{{total_characters}}</div>
<div class="stat-note">文本总字数</div>
<div class="stat-note">Tổng số ký tự</div>
</div>
<div class="stat-box">
<span class="stat-label">Emoji</span>
<div class="stat-value">{{emoji_count}}</div>
<div class="stat-note">表情符号统计</div>
<div class="stat-note">Thống kê biểu cảm</div>
</div>
</div>
@@ -761,7 +761,7 @@
</div>
<div class="crt-label">Peak Activity Window</div>
<div class="crt-value">{{most_active_period}}</div>
<div class="crt-sub">活跃序列 SCAN // 消息流强度 {{message_count}}</div>
<div class="crt-sub">QUÉT HOẠT ĐỘNG // Cường độ tin nhắn {{message_count}}</div>
</div>
</div>
@@ -825,7 +825,7 @@
<footer>
<div class="footer-row">
<div>CRT_EXPORT_CHANNEL // AstrBot 群日常分析插件</div>
<div>CRT_EXPORT_CHANNEL // Plugin phân tích nhóm hàng ngày AstrBot</div>
<div class="footer-author">SXP-Simon / astrbot_plugin_qq_group_daily_analysis</div>
</div>
<div class="footer-row">
@@ -729,25 +729,25 @@
<div class="stat-box">
<span class="stat-label">Messages</span>
<div class="stat-value">{{message_count}}</div>
<div class="stat-note">累计消息数量</div>
<div class="stat-note">Tổng số tin nhắn</div>
</div>
<div class="stat-box">
<span class="stat-label">Participants</span>
<div class="stat-value">{{participant_count}}</div>
<div class="stat-note">参与者计数</div>
<div class="stat-note">Số người tham gia</div>
</div>
<div class="stat-box">
<span class="stat-label">Characters</span>
<div class="stat-value">{{total_characters}}</div>
<div class="stat-note">文本总字数</div>
<div class="stat-note">Tổng số ký tự</div>
</div>
<div class="stat-box">
<span class="stat-label">Emoji</span>
<div class="stat-value">{{emoji_count}}</div>
<div class="stat-note">表情符号统计</div>
<div class="stat-note">Thống kê biểu cảm</div>
</div>
</div>
@@ -761,7 +761,7 @@
</div>
<div class="crt-label">Peak Activity Window</div>
<div class="crt-value">{{most_active_period}}</div>
<div class="crt-sub">活跃序列 SCAN // 消息流强度 {{message_count}}</div>
<div class="crt-sub">QUÉT HOẠT ĐỘNG // Cường độ tin nhắn {{message_count}}</div>
</div>
</div>
@@ -825,7 +825,7 @@
<footer>
<div class="footer-row">
<div>CRT_EXPORT_CHANNEL // AstrBot 群日常分析插件</div>
<div>CRT_EXPORT_CHANNEL // Plugin phân tích nhóm hàng ngày AstrBot</div>
<div class="footer-author">SXP-Simon / astrbot_plugin_qq_group_daily_analysis</div>
</div>
<div class="footer-row">
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>五彩斑斓的一天,来看看群里发生了什么吧! · {{current_date}}</title>
<title>Một ngày đầy sắc màu — cùng xem nhóm đã có gì nhé! · {{current_date}}</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<!-- 引入手写字体和正文阅读字体 -->
@@ -1027,7 +1027,7 @@
<div class="header">
<div class="title-sticker">
<div class="tape"></div>
<h1>五彩斑斓的一天,来看看群里发生了什么吧!</h1>
<h1>Một ngày đầy sắc màu — cùng xem nhóm đã có gì nhé!</h1>
<div class="date-badge">{{current_date}}</div>
</div>
</div>
@@ -1044,7 +1044,7 @@
d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" />
</svg>
<div class="stamp-num">{{message_count}}</div>
<div class="stamp-label">消息总数</div>
<div class="stamp-label">Tổng tin nhắn</div>
</div>
</div>
<!-- Users Icon -->
@@ -1055,7 +1055,7 @@
d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" />
</svg>
<div class="stamp-num">{{participant_count}}</div>
<div class="stamp-label">参与人数</div>
<div class="stamp-label">Người tham gia</div>
</div>
</div>
<!-- Emoji Icon -->
@@ -1066,7 +1066,7 @@
d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" />
</svg>
<div class="stamp-num">{{emoji_count}}</div>
<div class="stamp-label">表情统计</div>
<div class="stamp-label">Biểu cảm</div>
</div>
</div>
<!-- Text Icon -->
@@ -1077,7 +1077,7 @@
d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z" />
</svg>
<div class="stamp-num">{{total_characters}}</div>
<div class="stamp-label">总字符数</div>
<div class="stamp-label">Tổng ký tự</div>
</div>
</div>
</div>
@@ -1090,7 +1090,7 @@
✨ Highlight Time
</div>
<div class="time-big">{{most_active_period}}</div>
<div class="time-desc">(此刻,世界色彩斑斓)</div>
<div class="time-desc">(Khoảnh khắc nhóm sôi động nhất)</div>
</div>
</div>
@@ -1103,7 +1103,7 @@
<svg class="doodle" viewBox="0 0 24 24">
<path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z" />
</svg>
24H 活跃轨迹
Hoạt động 24 giờ
</div>
<div style="margin-top: 15px;">
{{hourly_chart_html | safe}}
@@ -1126,7 +1126,7 @@
<path fill="currentColor"
d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.488.488 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" />
</svg>
群聊质量锐评
Đánh giá chất lượng trò chuyện
</div>
{{ chat_quality_html | safe }}
</div>
@@ -1148,7 +1148,7 @@
</div>
<div class="footer-card-content">
<p>{{current_datetime}}</p>
<p style="font-size: 0.85em; opacity: 0.7;">AstrBot 群分析总结插件</p>
<p style="font-size: 0.85em; opacity: 0.7;">Plugin tổng hợp phân tích nhóm AstrBot</p>
</div>
</div>
@@ -1177,7 +1177,7 @@
<path
d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z" />
</svg>
LLM Token 消耗
Token LLM đã dùng
</div>
<div class="footer-card-content">
<p>Total: {{total_tokens}} Tokens</p>
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>五彩斑斓的一天,来看看群里发生了什么吧! · {{current_date}}</title>
<title>Một ngày đầy sắc màu — cùng xem nhóm đã có gì nhé! · {{current_date}}</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<!-- 引入手写字体和正文阅读字体 -->
@@ -1027,7 +1027,7 @@
<div class="header">
<div class="title-sticker">
<div class="tape"></div>
<h1>五彩斑斓的一天,来看看群里发生了什么吧!</h1>
<h1>Một ngày đầy sắc màu — cùng xem nhóm đã có gì nhé!</h1>
<div class="date-badge">{{current_date}}</div>
</div>
</div>
@@ -1044,7 +1044,7 @@
d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" />
</svg>
<div class="stamp-num">{{message_count}}</div>
<div class="stamp-label">消息总数</div>
<div class="stamp-label">Tổng tin nhắn</div>
</div>
</div>
<!-- Users Icon -->
@@ -1055,7 +1055,7 @@
d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" />
</svg>
<div class="stamp-num">{{participant_count}}</div>
<div class="stamp-label">参与人数</div>
<div class="stamp-label">Người tham gia</div>
</div>
</div>
<!-- Emoji Icon -->
@@ -1066,7 +1066,7 @@
d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" />
</svg>
<div class="stamp-num">{{emoji_count}}</div>
<div class="stamp-label">表情统计</div>
<div class="stamp-label">Biểu cảm</div>
</div>
</div>
<!-- Text Icon -->
@@ -1077,7 +1077,7 @@
d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z" />
</svg>
<div class="stamp-num">{{total_characters}}</div>
<div class="stamp-label">总字符数</div>
<div class="stamp-label">Tổng ký tự</div>
</div>
</div>
</div>
@@ -1090,7 +1090,7 @@
✨ Highlight Time
</div>
<div class="time-big">{{most_active_period}}</div>
<div class="time-desc">(此刻,世界色彩斑斓)</div>
<div class="time-desc">(Khoảnh khắc nhóm sôi động nhất)</div>
</div>
</div>
@@ -1103,7 +1103,7 @@
<svg class="doodle" viewBox="0 0 24 24">
<path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z" />
</svg>
24H 活跃轨迹
Hoạt động 24 giờ
</div>
<div style="margin-top: 15px;">
{{hourly_chart_html | safe}}
@@ -1126,7 +1126,7 @@
<path fill="currentColor"
d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.488.488 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" />
</svg>
群聊质量锐评
Đánh giá chất lượng trò chuyện
</div>
{{ chat_quality_html | safe }}
</div>
@@ -1148,7 +1148,7 @@
</div>
<div class="footer-card-content">
<p>{{current_datetime}}</p>
<p style="font-size: 0.85em; opacity: 0.7;">AstrBot 群分析总结插件</p>
<p style="font-size: 0.85em; opacity: 0.7;">Plugin tổng hợp phân tích nhóm AstrBot</p>
</div>
</div>
@@ -1177,7 +1177,7 @@
<path
d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z" />
</svg>
LLM Token 消耗
Token LLM đã dùng
</div>
<div class="footer-card-content">
<p>Total: {{total_tokens}} Tokens</p>
@@ -4,7 +4,7 @@
<svg class="doodle" viewBox="0 0 24 24">
<path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z" />
</svg>
群贤毕至 Bible Quotes
Trích dẫn nổi bật · Bible Quotes
</div>
{% for quote in quotes %}
<div class="quote-wrapper avoid-break">
@@ -24,7 +24,7 @@
<div class="q-content">{{ quote.content }}</div>
</div>
<div class="q-analysis-note">
<span class="note-label">🤣👉 AI 锐评:</span>
<span class="note-label">🤣👉 Nhận xét của AI:</span>
{{ quote.reason | safe }}
</div>
</div>
@@ -9,7 +9,7 @@
<svg class="doodle" viewBox="0 0 24 24">
<path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z" />
</svg>
今日话题 Topics
Chủ đề hôm nay · Topics
</div>
{% for topic in topics %}
<div class="topic-item avoid-break">
@@ -24,7 +24,7 @@
</div>
<div
style="font-family: var(--font-hand); font-size: 1.05em; color: var(--ink-secondary); margin-bottom: 8px;">
🙋‍♀️ 参与者: {{ topic.contributors }}
🙋‍♀️ Người tham gia: {{ topic.contributors }}
</div>
<div class="topic-detail">{{ topic.detail | safe }}</div>
</div>
@@ -5,7 +5,7 @@
<path
d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
群友画像 Portraits
Chân dung thành viên · Portraits
</div>
<div class="masonry-grid">
{% for title in titles %}
@@ -14,7 +14,7 @@
<div class="user-header">
<div class="u-avatar">
{% if title.avatar_data %}
<img src="{{ title.avatar_data }}" alt="头像" style="width: 100%; height: 100%; object-fit: cover;">
<img src="{{ title.avatar_data }}" alt="Ảnh đại diện" style="width: 100%; height: 100%; object-fit: cover;">
{% else %}
<svg class="doodle" style="font-size: 2rem;" viewBox="0 0 24 24">
<path
@@ -27,25 +27,25 @@
</head>
<body>
<h1>群聊日报 - {{current_date}}</h1>
<h1>Báo cáo nhóm hàng ngày - {{current_date}}</h1>
<div class="section">
<h2>基础统计</h2>
<p>消息数: {{message_count}}</p>
<p>参与人数: {{participant_count}}</p>
<p>总字符数: {{total_characters}}</p>
<p>表情数: {{emoji_count}}</p>
<p>最活跃时段: {{most_active_period}}</p>
<h2>Thống kê cơ bản</h2>
<p>Số tin nhắn: {{message_count}}</p>
<p>Người tham gia: {{participant_count}}</p>
<p>Tổng ký tự: {{total_characters}}</p>
<p>Số biểu cảm: {{emoji_count}}</p>
<p>Khung giờ sôi nổi nhất: {{most_active_period}}</p>
</div>
<div class="section">
<h2>活跃度图表</h2>
<h2>Biểu đồ hoạt động</h2>
{{hourly_chart_html | safe}}
</div>
{% if chat_quality_html %}
<div class="section">
<h2>群聊质量分析</h2>
<h2>Phân tích chất lượng trò chuyện</h2>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -27,25 +27,25 @@
</head>
<body>
<h1>群聊日报 - {{current_date}}</h1>
<h1>Báo cáo nhóm hàng ngày - {{current_date}}</h1>
<div class="section">
<h2>基础统计</h2>
<p>消息数: {{message_count}}</p>
<p>参与人数: {{participant_count}}</p>
<p>总字符数: {{total_characters}}</p>
<p>表情数: {{emoji_count}}</p>
<p>最活跃时段: {{most_active_period}}</p>
<h2>Thống kê cơ bản</h2>
<p>Số tin nhắn: {{message_count}}</p>
<p>Người tham gia: {{participant_count}}</p>
<p>Tổng ký tự: {{total_characters}}</p>
<p>Số biểu cảm: {{emoji_count}}</p>
<p>Khung giờ sôi nổi nhất: {{most_active_period}}</p>
</div>
<div class="section">
<h2>活跃度图表</h2>
<h2>Biểu đồ hoạt động</h2>
{{hourly_chart_html | safe}}
</div>
{% if chat_quality_html %}
<div class="section">
<h2>群聊质量分析</h2>
<h2>Phân tích chất lượng trò chuyện</h2>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -1,6 +1,6 @@
{% if quotes %}
<div class="section">
<h2>群圣经</h2>
<h2>Trích dẫn nổi bật</h2>
{% for quote in quotes %}
<div style="margin-bottom: 10px; padding: 10px; background: #f9f9f9; border-left: 3px solid #ccc;">
<p style="font-style: italic; margin: 0;">"{{ quote.content }}"</p>
@@ -8,7 +8,7 @@
— {{ quote.sender }}
</div>
<div style="text-align: right; font-size: 0.7em; color: #888;">
入选理由: {{ quote.reason }}
Lý do được chọn: {{ quote.reason }}
</div>
</div>
{% endfor %}
@@ -1,10 +1,10 @@
{% if topics %}
<div class="section">
<h2>热门话题</h2>
<h2>Chủ đề nổi bật</h2>
{% for item in topics %}
<div style="margin-bottom: 10px; border-bottom: 1px dashed #ccc; padding-bottom: 5px;">
<strong>#{{ item.index }} {{ item.topic.topic }}</strong>
<span style="color: #666; font-size: 0.8em;">(参与者: {{ item.contributors }})</span>
<span style="color: #666; font-size: 0.8em;">(Người tham gia: {{ item.contributors }})</span>
<p style="margin: 5px 0;">{{ item.detail | safe }}</p>
</div>
{% endfor %}
@@ -1,6 +1,6 @@
{% if titles %}
<div class="section">
<h2>群友称号</h2>
<h2>Danh hiệu thành viên</h2>
{% for item in titles %}
<div style="margin-bottom: 10px; border: 1px solid #eee; padding: 5px; position: relative; overflow: hidden;">
<div style="font-weight: bold;">{{ item.name }} - {{ item.title }}</div>
@@ -137,7 +137,7 @@
{% if dim.percentage > 0 %}
<div class="sf-comment-box">
<span class="sf-comment-title">✨ {{ dim.name }}</span>
<span style="font-size: 0.82em; color: #fa541c; display: block; margin-bottom: 5px; font-weight: 600;">占比: {{ dim.percentage }}%</span>
<span style="font-size: 0.82em; color: #fa541c; display: block; margin-bottom: 5px; font-weight: 600;">Tỷ lệ: {{ dim.percentage }}%</span>
<div>{{ dim.comment }}</div>
</div>
{% endif %}
@@ -145,7 +145,7 @@
</div>
<div class="sf-summary-section">
<span class="sf-summary-label">📜 分析总结</span>
<span class="sf-summary-label">📜 Tổng kết phân tích</span>
<span>{{ summary }}</span>
</div>
</div>
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<title>春节特供 · 群聊日报</title>
<title>Đặc biệt Tết · Báo cáo nhóm hàng ngày</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<!-- 引入书法字体和节日字体 -->
@@ -593,27 +593,27 @@
<div class="sf-header">
<div class="sf-title-wrap">
<h1>🧧 春节特供日报 🧧</h1>
<h1>🧧 Báo cáo đặc biệt ngày Tết 🧧</h1>
</div>
<div class="sf-date">—— {{current_date}} · 岁在丙午 ——</div>
<div class="sf-date">—— {{current_date}} · Năm Bính Ngọ ——</div>
</div>
<div class="sf-stats-wrapper">
<div class="sf-stats-grid">
<div class="sf-hongbao">
<div class="hb-label">消息总数</div>
<div class="hb-label">Tổng tin nhắn</div>
<div class="hb-value">{{message_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">参与群友</div>
<div class="hb-label">Người tham gia</div>
<div class="hb-value">{{participant_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">表情统计</div>
<div class="hb-label">Biểu cảm</div>
<div class="hb-value">{{emoji_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">总字符数</div>
<div class="hb-label">Tổng ký tự</div>
<div class="hb-value" style="font-size: 1.8rem;">{{total_characters}}</div>
</div>
</div>
@@ -621,14 +621,14 @@
<div class="sf-peak-badge">
<div
style="font-family: var(--font-calligraphy); font-size: 2rem; color: var(--red-festive); margin-bottom: 5px;">
巅峰时刻
Khoảnh khắc cao điểm
</div>
<div
style="font-family: var(--font-heading); font-size: 3.2rem; color: var(--wood-dark); font-weight: 800; line-height: 1;">
{{most_active_period}}
</div>
<div style="font-size: 0.9rem; margin-top: 12px; font-weight: bold; color: var(--red-festive);">🏮 鸿运齐天
· 众友云集 🏮</div>
<div style="font-size: 0.9rem; margin-top: 12px; font-weight: bold; color: var(--red-festive);">🏮 May mắn ngập tràn
· Bạn bè hội tụ 🏮</div>
</div>
</div>
@@ -636,7 +636,7 @@
<!-- 0. Chat Quality Analysis -->
{% if chat_quality_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>🏺</span> 聊天质量锐评</div>
<div class="sf-section-title"><span>🏺</span> Đánh giá chất lượng trò chuyện</div>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -644,7 +644,7 @@
<!-- 1. Topics: Full Width -->
{% if topics_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>&#127982;</span> 核心话题回顾</div>
<div class="sf-section-title"><span>&#127982;</span> Nhìn lại chủ đề chính</div>
<div class="sf-topic-list">
{{topics_html | safe}}
</div>
@@ -653,7 +653,7 @@
<!-- 2. Chart: Full Width with SF window -->
<div class="sf-section-box">
<div class="sf-section-title"><span>📈</span> 活跃时序图</div>
<div class="sf-section-title"><span>📈</span> Biểu đồ hoạt động</div>
<div class="sf-window">
<div class="sf-window-title">ACTIVITY CHRONICLE</div>
{{hourly_chart_html | safe}}
@@ -663,7 +663,7 @@
<!-- 3. Characters: Masonry 2-Column Grid -->
{% if titles_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>🎖️</span> 群友风云榜</div>
<div class="sf-section-title"><span>🎖️</span> Bảng vàng thành viên</div>
{{titles_html | safe}}
</div>
{% endif %}
@@ -671,7 +671,7 @@
<!-- 4. Quotes: Staggered Timeline -->
{% if quotes_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span></span> 每日金句回响</div>
<div class="sf-section-title"><span></span> Trích dẫn nổi bật trong ngày</div>
<div class="sf-quote-timeline">
{{quotes_html | safe}}
</div>
@@ -680,8 +680,8 @@
</div>
<footer>
<div>宜 · 总结分析 // {{current_datetime}}</div>
<div style="font-size: 0.85rem; margin-top: 8px; opacity: 0.7;">Token 消耗:{{total_tokens}} (Prompt: {{prompt_tokens}}, Completion: {{completion_tokens}})</div>
<div>Tổng kết phân tích // {{current_datetime}}</div>
<div style="font-size: 0.85rem; margin-top: 8px; opacity: 0.7;">Token đã dùng: {{total_tokens}} (Prompt: {{prompt_tokens}}, Completion: {{completion_tokens}})</div>
<div class="sf-footer-tag">SXP-Simon / astrbot_plugin_qq_group_daily_analysis</div>
</footer>
</div>
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<title>春节特供 · 群聊日报</title>
<title>Đặc biệt Tết · Báo cáo nhóm hàng ngày</title>
<link rel="preconnect" href="{{ t2i_google_fonts_mirror }}">
<link rel="preconnect" href="{{ t2i_gstatic_mirror }}" crossorigin>
<link
@@ -592,27 +592,27 @@
<div class="sf-header">
<div class="sf-title-wrap">
<h1>🧧 春节特供日报 🧧</h1>
<h1>🧧 Báo cáo đặc biệt ngày Tết 🧧</h1>
</div>
<div class="sf-date">—— {{current_date}} · 岁在丙午 ——</div>
<div class="sf-date">—— {{current_date}} · Năm Bính Ngọ ——</div>
</div>
<div class="sf-stats-wrapper">
<div class="sf-stats-grid">
<div class="sf-hongbao">
<div class="hb-label">消息总数</div>
<div class="hb-label">Tổng tin nhắn</div>
<div class="hb-value">{{message_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">参与群友</div>
<div class="hb-label">Người tham gia</div>
<div class="hb-value">{{participant_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">表情统计</div>
<div class="hb-label">Biểu cảm</div>
<div class="hb-value">{{emoji_count}}</div>
</div>
<div class="sf-hongbao">
<div class="hb-label">总字符数</div>
<div class="hb-label">Tổng ký tự</div>
<div class="hb-value" style="font-size: 1.8rem;">{{total_characters}}</div>
</div>
</div>
@@ -620,14 +620,14 @@
<div class="sf-peak-badge">
<div
style="font-family: var(--font-calligraphy); font-size: 2rem; color: var(--red-festive); margin-bottom: 5px;">
巅峰时刻
Khoảnh khắc cao điểm
</div>
<div
style="font-family: var(--font-heading); font-size: 3.2rem; color: var(--wood-dark); font-weight: 800; line-height: 1;">
{{most_active_period}}
</div>
<div style="font-size: 0.9rem; margin-top: 12px; font-weight: bold; color: var(--red-festive);">🏮 鸿运齐天
· 众友云集 🏮</div>
<div style="font-size: 0.9rem; margin-top: 12px; font-weight: bold; color: var(--red-festive);">🏮 May mắn ngập tràn
· Bạn bè hội tụ 🏮</div>
</div>
</div>
@@ -635,7 +635,7 @@
<!-- 0. Chat Quality Analysis -->
{% if chat_quality_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>🏺</span> 聊天质量锐评</div>
<div class="sf-section-title"><span>🏺</span> Đánh giá chất lượng trò chuyện</div>
{{ chat_quality_html | safe }}
</div>
{% endif %}
@@ -643,7 +643,7 @@
<!-- 1. Topics: Full Width -->
{% if topics_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>&#127982;</span> 核心话题回顾</div>
<div class="sf-section-title"><span>&#127982;</span> Nhìn lại chủ đề chính</div>
<div class="sf-topic-list">
{{topics_html | safe}}
</div>
@@ -652,7 +652,7 @@
<!-- 2. Chart: Full Width with SF window -->
<div class="sf-section-box">
<div class="sf-section-title"><span>📈</span> 活跃时序图</div>
<div class="sf-section-title"><span>📈</span> Biểu đồ hoạt động</div>
<div class="sf-window">
<div class="sf-window-title">ACTIVITY CHRONICLE</div>
{{hourly_chart_html | safe}}
@@ -662,7 +662,7 @@
<!-- 3. Characters: Masonry 2-Column Grid -->
{% if titles_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span>🎖️</span> 群友风云榜</div>
<div class="sf-section-title"><span>🎖️</span> Bảng vàng thành viên</div>
{{titles_html | safe}}
</div>
{% endif %}
@@ -670,7 +670,7 @@
<!-- 4. Quotes: Staggered Timeline -->
{% if quotes_html %}
<div class="sf-section-box">
<div class="sf-section-title"><span></span> 每日金句回响</div>
<div class="sf-section-title"><span></span> Trích dẫn nổi bật trong ngày</div>
<div class="sf-quote-timeline">
{{quotes_html | safe}}
</div>
@@ -679,8 +679,8 @@
</div>
<footer>
<div>宜 · 总结分析 // {{current_datetime}}</div>
<div style="font-size: 0.85rem; margin-top: 8px; opacity: 0.7;">Token 消耗:{{total_tokens}} (Prompt: {{prompt_tokens}}, Completion: {{completion_tokens}})</div>
<div>Tổng kết phân tích // {{current_datetime}}</div>
<div style="font-size: 0.85rem; margin-top: 8px; opacity: 0.7;">Token đã dùng: {{total_tokens}} (Prompt: {{prompt_tokens}}, Completion: {{completion_tokens}})</div>
<div class="sf-footer-tag">SXP-Simon / astrbot_plugin_qq_group_daily_analysis</div>
</footer>
</div>
@@ -16,7 +16,7 @@
<div class="sf-quote-bubble">
<div class="sf-quote-content">“{{ quote.content }}”</div>
<div class="sf-quote-author">—— {{ quote.sender }}</div>
<div class="sf-quote-reason">评语】{{ quote.reason }}</div>
<div class="sf-quote-reason">Nhận xét】{{ quote.reason }}</div>
</div>
</div>
{% endfor %}
@@ -6,7 +6,7 @@
<span class="sf-lantern">🏮</span>
<span class="sf-topic-title">{{ topic.topic.topic }}</span>
</div>
<div class="sf-topic-meta">参与者: {{ topic.contributors }}</div>
<div class="sf-topic-meta">Người tham gia: {{ topic.contributors }}</div>
<div class="sf-topic-detail">{{ topic.detail | safe }}</div>
</div>
{% endfor %}
+2 -2
View File
@@ -87,7 +87,7 @@ def test_standard_text_report_keeps_existing_identity_format():
assert openid in report
assert "测试内容" in report
assert "龙王" in report
assert f"参与者: {openid}" in report
assert f"Người tham gia: {openid}" in report
assert f"{openid} - 龙王 (ENTP)" in report
assert f'1. "测试内容" —— {openid}' in report
@@ -118,7 +118,7 @@ def test_non_qq_text_report_does_not_use_qq_histogram_path():
report = generator.generate_text_report(analysis_result)
assert "🎯 群聊日常分析报告" in report
assert "🎯 Báo cáo phân tích nhóm hàng ngày" in report
assert "## ⏰ 活跃时间分布" not in report
assert "████" not in report
assert "![24小时活跃分布" not in report