renderer.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. # coding=utf-8
  2. """
  3. 通知内容渲染模块
  4. 提供多平台通知内容渲染功能,生成格式化的推送消息
  5. """
  6. from datetime import datetime
  7. from typing import Dict, List, Optional, Callable
  8. from trendradar.report.formatter import format_title_for_platform
  9. def render_feishu_content(
  10. report_data: Dict,
  11. update_info: Optional[Dict] = None,
  12. mode: str = "daily",
  13. separator: str = "---",
  14. reverse_content_order: bool = False,
  15. get_time_func: Optional[Callable[[], datetime]] = None,
  16. ) -> str:
  17. """渲染飞书通知内容
  18. Args:
  19. report_data: 报告数据字典,包含 stats, new_titles, failed_ids, total_new_count
  20. update_info: 版本更新信息(可选)
  21. mode: 报告模式 ("daily", "incremental", "current")
  22. separator: 内容分隔符
  23. reverse_content_order: 是否反转内容顺序(新增在前)
  24. get_time_func: 获取当前时间的函数(可选,默认使用 datetime.now())
  25. Returns:
  26. 格式化的飞书消息内容
  27. """
  28. # 生成热点词汇统计部分
  29. stats_content = ""
  30. if report_data["stats"]:
  31. stats_content += "📊 **热点词汇统计**\n\n"
  32. total_count = len(report_data["stats"])
  33. for i, stat in enumerate(report_data["stats"]):
  34. word = stat["word"]
  35. count = stat["count"]
  36. sequence_display = f"<font color='grey'>[{i + 1}/{total_count}]</font>"
  37. if count >= 10:
  38. stats_content += f"🔥 {sequence_display} **{word}** : <font color='red'>{count}</font> 条\n\n"
  39. elif count >= 5:
  40. stats_content += f"📈 {sequence_display} **{word}** : <font color='orange'>{count}</font> 条\n\n"
  41. else:
  42. stats_content += f"📌 {sequence_display} **{word}** : {count} 条\n\n"
  43. for j, title_data in enumerate(stat["titles"], 1):
  44. formatted_title = format_title_for_platform(
  45. "feishu", title_data, show_source=True
  46. )
  47. stats_content += f" {j}. {formatted_title}\n"
  48. if j < len(stat["titles"]):
  49. stats_content += "\n"
  50. if i < len(report_data["stats"]) - 1:
  51. stats_content += f"\n{separator}\n\n"
  52. # 生成新增新闻部分
  53. new_titles_content = ""
  54. if report_data["new_titles"]:
  55. new_titles_content += (
  56. f"🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n"
  57. )
  58. for source_data in report_data["new_titles"]:
  59. new_titles_content += (
  60. f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n"
  61. )
  62. for j, title_data in enumerate(source_data["titles"], 1):
  63. title_data_copy = title_data.copy()
  64. title_data_copy["is_new"] = False
  65. formatted_title = format_title_for_platform(
  66. "feishu", title_data_copy, show_source=False
  67. )
  68. new_titles_content += f" {j}. {formatted_title}\n"
  69. new_titles_content += "\n"
  70. # 根据配置决定内容顺序
  71. text_content = ""
  72. if reverse_content_order:
  73. # 新增热点在前,热点词汇统计在后
  74. if new_titles_content:
  75. text_content += new_titles_content
  76. if stats_content:
  77. text_content += f"\n{separator}\n\n"
  78. if stats_content:
  79. text_content += stats_content
  80. else:
  81. # 默认:热点词汇统计在前,新增热点在后
  82. if stats_content:
  83. text_content += stats_content
  84. if new_titles_content:
  85. text_content += f"\n{separator}\n\n"
  86. if new_titles_content:
  87. text_content += new_titles_content
  88. if not text_content:
  89. if mode == "incremental":
  90. mode_text = "增量模式下暂无新增匹配的热点词汇"
  91. elif mode == "current":
  92. mode_text = "当前榜单模式下暂无匹配的热点词汇"
  93. else:
  94. mode_text = "暂无匹配的热点词汇"
  95. text_content = f"📭 {mode_text}\n\n"
  96. if report_data["failed_ids"]:
  97. if text_content and "暂无匹配" not in text_content:
  98. text_content += f"\n{separator}\n\n"
  99. text_content += "⚠️ **数据获取失败的平台:**\n\n"
  100. for i, id_value in enumerate(report_data["failed_ids"], 1):
  101. text_content += f" • <font color='red'>{id_value}</font>\n"
  102. # 获取当前时间
  103. now = get_time_func() if get_time_func else datetime.now()
  104. text_content += (
  105. f"\n\n<font color='grey'>更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}</font>"
  106. )
  107. if update_info:
  108. text_content += f"\n<font color='grey'>TrendRadar 发现新版本 {update_info['remote_version']},当前 {update_info['current_version']}</font>"
  109. return text_content
  110. def render_dingtalk_content(
  111. report_data: Dict,
  112. update_info: Optional[Dict] = None,
  113. mode: str = "daily",
  114. reverse_content_order: bool = False,
  115. get_time_func: Optional[Callable[[], datetime]] = None,
  116. ) -> str:
  117. """渲染钉钉通知内容
  118. Args:
  119. report_data: 报告数据字典,包含 stats, new_titles, failed_ids, total_new_count
  120. update_info: 版本更新信息(可选)
  121. mode: 报告模式 ("daily", "incremental", "current")
  122. reverse_content_order: 是否反转内容顺序(新增在前)
  123. get_time_func: 获取当前时间的函数(可选,默认使用 datetime.now())
  124. Returns:
  125. 格式化的钉钉消息内容
  126. """
  127. total_titles = sum(
  128. len(stat["titles"]) for stat in report_data["stats"] if stat["count"] > 0
  129. )
  130. now = get_time_func() if get_time_func else datetime.now()
  131. # 头部信息
  132. header_content = f"**总新闻数:** {total_titles}\n\n"
  133. header_content += f"**时间:** {now.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
  134. header_content += "**类型:** 热点分析报告\n\n"
  135. header_content += "---\n\n"
  136. # 生成热点词汇统计部分
  137. stats_content = ""
  138. if report_data["stats"]:
  139. stats_content += "📊 **热点词汇统计**\n\n"
  140. total_count = len(report_data["stats"])
  141. for i, stat in enumerate(report_data["stats"]):
  142. word = stat["word"]
  143. count = stat["count"]
  144. sequence_display = f"[{i + 1}/{total_count}]"
  145. if count >= 10:
  146. stats_content += f"🔥 {sequence_display} **{word}** : **{count}** 条\n\n"
  147. elif count >= 5:
  148. stats_content += f"📈 {sequence_display} **{word}** : **{count}** 条\n\n"
  149. else:
  150. stats_content += f"📌 {sequence_display} **{word}** : {count} 条\n\n"
  151. for j, title_data in enumerate(stat["titles"], 1):
  152. formatted_title = format_title_for_platform(
  153. "dingtalk", title_data, show_source=True
  154. )
  155. stats_content += f" {j}. {formatted_title}\n"
  156. if j < len(stat["titles"]):
  157. stats_content += "\n"
  158. if i < len(report_data["stats"]) - 1:
  159. stats_content += "\n---\n\n"
  160. # 生成新增新闻部分
  161. new_titles_content = ""
  162. if report_data["new_titles"]:
  163. new_titles_content += (
  164. f"🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n"
  165. )
  166. for source_data in report_data["new_titles"]:
  167. new_titles_content += f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n"
  168. for j, title_data in enumerate(source_data["titles"], 1):
  169. title_data_copy = title_data.copy()
  170. title_data_copy["is_new"] = False
  171. formatted_title = format_title_for_platform(
  172. "dingtalk", title_data_copy, show_source=False
  173. )
  174. new_titles_content += f" {j}. {formatted_title}\n"
  175. new_titles_content += "\n"
  176. # 根据配置决定内容顺序
  177. text_content = header_content
  178. if reverse_content_order:
  179. # 新增热点在前,热点词汇统计在后
  180. if new_titles_content:
  181. text_content += new_titles_content
  182. if stats_content:
  183. text_content += "\n---\n\n"
  184. if stats_content:
  185. text_content += stats_content
  186. else:
  187. # 默认:热点词汇统计在前,新增热点在后
  188. if stats_content:
  189. text_content += stats_content
  190. if new_titles_content:
  191. text_content += "\n---\n\n"
  192. if new_titles_content:
  193. text_content += new_titles_content
  194. if not stats_content and not new_titles_content:
  195. if mode == "incremental":
  196. mode_text = "增量模式下暂无新增匹配的热点词汇"
  197. elif mode == "current":
  198. mode_text = "当前榜单模式下暂无匹配的热点词汇"
  199. else:
  200. mode_text = "暂无匹配的热点词汇"
  201. text_content += f"📭 {mode_text}\n\n"
  202. if report_data["failed_ids"]:
  203. if "暂无匹配" not in text_content:
  204. text_content += "\n---\n\n"
  205. text_content += "⚠️ **数据获取失败的平台:**\n\n"
  206. for i, id_value in enumerate(report_data["failed_ids"], 1):
  207. text_content += f" • **{id_value}**\n"
  208. text_content += f"\n\n> 更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}"
  209. if update_info:
  210. text_content += f"\n> TrendRadar 发现新版本 **{update_info['remote_version']}**,当前 **{update_info['current_version']}**"
  211. return text_content