feat: enrich douyu daily summary cards

This commit is contained in:
liuwei
2026-04-08 14:26:46 +08:00
parent b73594975d
commit ea3a088c63
2 changed files with 85 additions and 8 deletions

View File

@@ -1284,7 +1284,7 @@ class DouyuPlugin(MessagePluginInterface):
user_prompt = (
"请输出一段适合放在日报图片上半部分的弹幕总结,要求:\n"
"1. 先用 1 段总述直播氛围与主线。\n"
"2. 再用 3-5 条要点总结观众关注点、情绪变化、反复出现的梗。\n"
"2. 再用 5 条要点总结观众关注点、情绪变化、反复出现的梗、节奏变化和额外反馈,每条只写一句\n"
"3. 语言像运营复盘,简洁自然。\n"
"4. 不要写标题,不要写“根据数据”。\n\n"
f"主播:{meta.get('nickname') or meta.get('room_name') or meta.get('room_id')}\n"

View File

@@ -39,12 +39,90 @@ def _split_summary_blocks(danmu_summary: str) -> tuple[str, List[str]]:
return lead, insight_items
def _normalize_summary_bullets(payload: Dict[str, Any], items: List[str], target_count: int = 5) -> List[str]:
normalized = [str(item or "").strip() for item in items if str(item or "").strip()]
if len(normalized) >= target_count:
return normalized[:target_count]
top_terms = [str(item.get("term") or "").strip() for item in (payload.get("top_terms", []) or []) if str(item.get("term") or "").strip()]
merged_templates = [str(item.get("text") or "").strip() for item in (payload.get("merged_templates", []) or []) if str(item.get("text") or "").strip()]
peak_buckets = payload.get("peak_buckets", []) or []
representative_messages = payload.get("representative_messages", []) or []
supplements: List[str] = []
if top_terms:
supplements.append(f"讨论焦点比较集中,弹幕反复围绕 {''.join(top_terms[:5])} 展开。")
if merged_templates:
sample_templates = "".join(text[:24] for text in merged_templates[:3])
supplements.append(f"复读和共识梗比较强,重复内容主要集中在 {sample_templates}")
if peak_buckets:
top_bucket = peak_buckets[0]
bucket_terms = [str(term.get('term') or '').strip() for term in (top_bucket.get("top_terms", []) or []) if str(term.get('term') or '').strip()]
if bucket_terms:
supplements.append(
f"高峰时段出现在 {str(top_bucket.get('start_time') or '')[-8:-3]} 前后,话题明显偏向 {''.join(bucket_terms[:4])}"
)
if representative_messages:
supplements.append("代表性发言里既有操作反馈,也有玩梗调侃和情绪宣泄,互动意愿比较强。")
existing = set(normalized)
for item in supplements:
if item not in existing:
normalized.append(item)
existing.add(item)
if len(normalized) >= target_count:
break
return normalized[:target_count]
def _build_template_items(payload: Dict[str, Any], limit: int = 8) -> List[str]:
items: List[str] = []
seen = set()
def push(text: str, suffix: str = "") -> None:
value = str(text or "").strip()
if not value:
return
normalized_key = value
if normalized_key in seen:
return
seen.add(normalized_key)
items.append(f"{value}{suffix}".strip())
for item in (payload.get("merged_templates", []) or [])[:6]:
text = str(item.get("text") or "").strip()
count = int(item.get("count", 0) or 0)
if text:
push(text[:72], f"{count}次)")
for item in (payload.get("repeated_messages", []) or [])[:6]:
text = str(item.get("text") or "").strip()
count = int(item.get("count", 0) or 0)
if text:
push(text[:72], f"{count}次)")
for item in (payload.get("burst_terms", []) or [])[:6]:
text = str(item.get("text") or "").strip()
count = int(item.get("count", 0) or 0)
if text:
push(text[:36], f"(爆发 {count} 次)")
for item in (payload.get("top_terms", []) or [])[:6]:
term = str(item.get("term") or "").strip()
count = int(item.get("count", 0) or 0)
if term:
push(term, f"{count}次提及)")
return items[:limit]
def _render_insight_cards(items: List[str]) -> str:
labels = ["主线", "情绪", "梗点", "节奏", "反馈", "补充"]
blocks = []
for idx, item in enumerate(items[:6]):
extra_class = " full-span" if len(items[:6]) % 2 == 1 and idx == len(items[:6]) - 1 else ""
blocks.append(
'<div class="insight-card">'
f'<div class="insight-card{extra_class}">'
f'<div class="insight-kicker">{_escape(labels[idx] if idx < len(labels) else "观察")}</div>'
f'<div class="insight-text">{_escape(item)}</div>'
"</div>"
@@ -103,12 +181,7 @@ def render_daily_report_html(
_render_metric_card("30+等级用户", operator.get("high_room_level_user_count", 0), "高等级老观众"),
])
merged_templates = payload.get("merged_templates", []) or []
template_items = [
f"{str(item.get('text') or '').strip()[:72]}{int(item.get('count', 0) or 0)}次)"
for item in merged_templates[:5]
if str(item.get("text") or "").strip()
]
template_items = _build_template_items(payload)
top_active_users = payload.get("operator_metrics", {}).get("top_active_users", []) or []
active_user_items = []
for item in top_active_users[:10]:
@@ -121,6 +194,7 @@ def render_daily_report_html(
active_user_items.append(f"{nickname} | {message_count}")
lead_summary, danmu_bullets = _split_summary_blocks(danmu_summary)
danmu_bullets = _normalize_summary_bullets(payload, danmu_bullets, target_count=5)
html_doc = f"""<html>
<head>
@@ -314,6 +388,9 @@ def render_daily_report_html(
border: 1px solid rgba(125, 145, 186, 0.14);
min-height: 110px;
}}
.insight-card.full-span {{
grid-column: 1 / -1;
}}
.insight-kicker {{
color: #2b59ff;
font-size: 12px;