formatter.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # coding=utf-8
  2. """
  3. 平台标题格式化模块
  4. 提供多平台标题格式化功能
  5. """
  6. from typing import Dict
  7. from trendradar.report.helpers import clean_title, html_escape, format_rank_display
  8. def format_title_for_platform(
  9. platform: str, title_data: Dict, show_source: bool = True, show_keyword: bool = False
  10. ) -> str:
  11. """统一的标题格式化方法
  12. 为不同平台生成对应格式的标题字符串。
  13. Args:
  14. platform: 目标平台,支持:
  15. - "feishu": 飞书
  16. - "dingtalk": 钉钉
  17. - "wework": 企业微信
  18. - "bark": Bark
  19. - "telegram": Telegram
  20. - "ntfy": ntfy
  21. - "slack": Slack
  22. - "html": HTML 报告
  23. title_data: 标题数据字典,包含以下字段:
  24. - title: 标题文本
  25. - source_name: 来源名称
  26. - time_display: 时间显示
  27. - count: 出现次数
  28. - ranks: 排名列表
  29. - rank_threshold: 高亮阈值
  30. - url: PC端链接
  31. - mobile_url: 移动端链接(优先使用)
  32. - is_new: 是否为新增标题(可选)
  33. - matched_keyword: 匹配的关键词(可选,platform 模式使用)
  34. show_source: 是否显示来源名称(keyword 模式使用)
  35. show_keyword: 是否显示关键词标签(platform 模式使用)
  36. Returns:
  37. 格式化后的标题字符串
  38. """
  39. rank_display = format_rank_display(
  40. title_data["ranks"], title_data["rank_threshold"], platform
  41. )
  42. link_url = title_data["mobile_url"] or title_data["url"]
  43. cleaned_title = clean_title(title_data["title"])
  44. if not cleaned_title:
  45. cleaned_title = link_url or title_data["url"] or ""
  46. # 获取关键词标签(platform 模式使用)
  47. keyword = title_data.get("matched_keyword", "") if show_keyword else ""
  48. if platform == "feishu":
  49. if link_url:
  50. formatted_title = f"[{cleaned_title}]({link_url})"
  51. else:
  52. formatted_title = cleaned_title
  53. title_prefix = "🆕 " if title_data.get("is_new") else ""
  54. if show_source:
  55. result = f"<font color='grey'>[{title_data['source_name']}]</font> {title_prefix}{formatted_title}"
  56. elif show_keyword and keyword:
  57. result = f"<font color='blue'>[{keyword}]</font> {title_prefix}{formatted_title}"
  58. else:
  59. result = f"{title_prefix}{formatted_title}"
  60. if rank_display:
  61. result += f" {rank_display}"
  62. if title_data["time_display"]:
  63. result += f" <font color='grey'>- {title_data['time_display']}</font>"
  64. if title_data["count"] > 1:
  65. result += f" <font color='green'>({title_data['count']}次)</font>"
  66. return result
  67. elif platform == "dingtalk":
  68. if link_url:
  69. formatted_title = f"[{cleaned_title}]({link_url})"
  70. else:
  71. formatted_title = cleaned_title
  72. title_prefix = "🆕 " if title_data.get("is_new") else ""
  73. if show_source:
  74. result = f"[{title_data['source_name']}] {title_prefix}{formatted_title}"
  75. elif show_keyword and keyword:
  76. result = f"[{keyword}] {title_prefix}{formatted_title}"
  77. else:
  78. result = f"{title_prefix}{formatted_title}"
  79. if rank_display:
  80. result += f" {rank_display}"
  81. if title_data["time_display"]:
  82. result += f" - {title_data['time_display']}"
  83. if title_data["count"] > 1:
  84. result += f" ({title_data['count']}次)"
  85. return result
  86. elif platform in ("wework", "bark"):
  87. # WeWork 和 Bark 使用 markdown 格式
  88. if link_url:
  89. formatted_title = f"[{cleaned_title}]({link_url})"
  90. else:
  91. formatted_title = cleaned_title
  92. title_prefix = "🆕 " if title_data.get("is_new") else ""
  93. if show_source:
  94. result = f"[{title_data['source_name']}] {title_prefix}{formatted_title}"
  95. elif show_keyword and keyword:
  96. result = f"[{keyword}] {title_prefix}{formatted_title}"
  97. else:
  98. result = f"{title_prefix}{formatted_title}"
  99. if rank_display:
  100. result += f" {rank_display}"
  101. if title_data["time_display"]:
  102. result += f" - {title_data['time_display']}"
  103. if title_data["count"] > 1:
  104. result += f" ({title_data['count']}次)"
  105. return result
  106. elif platform == "telegram":
  107. if link_url:
  108. formatted_title = f'<a href="{link_url}">{html_escape(cleaned_title)}</a>'
  109. else:
  110. formatted_title = cleaned_title
  111. title_prefix = "🆕 " if title_data.get("is_new") else ""
  112. if show_source:
  113. result = f"[{title_data['source_name']}] {title_prefix}{formatted_title}"
  114. elif show_keyword and keyword:
  115. result = f"<b>[{html_escape(keyword)}]</b> {title_prefix}{formatted_title}"
  116. else:
  117. result = f"{title_prefix}{formatted_title}"
  118. if rank_display:
  119. result += f" {rank_display}"
  120. if title_data["time_display"]:
  121. result += f" <code>- {title_data['time_display']}</code>"
  122. if title_data["count"] > 1:
  123. result += f" <code>({title_data['count']}次)</code>"
  124. return result
  125. elif platform == "ntfy":
  126. if link_url:
  127. formatted_title = f"[{cleaned_title}]({link_url})"
  128. else:
  129. formatted_title = cleaned_title
  130. title_prefix = "🆕 " if title_data.get("is_new") else ""
  131. if show_source:
  132. result = f"[{title_data['source_name']}] {title_prefix}{formatted_title}"
  133. elif show_keyword and keyword:
  134. result = f"[{keyword}] {title_prefix}{formatted_title}"
  135. else:
  136. result = f"{title_prefix}{formatted_title}"
  137. if rank_display:
  138. result += f" {rank_display}"
  139. if title_data["time_display"]:
  140. result += f" `- {title_data['time_display']}`"
  141. if title_data["count"] > 1:
  142. result += f" `({title_data['count']}次)`"
  143. return result
  144. elif platform == "slack":
  145. # Slack 使用 mrkdwn 格式
  146. if link_url:
  147. # Slack 链接格式: <url|text>
  148. formatted_title = f"<{link_url}|{cleaned_title}>"
  149. else:
  150. formatted_title = cleaned_title
  151. title_prefix = "🆕 " if title_data.get("is_new") else ""
  152. if show_source:
  153. result = f"[{title_data['source_name']}] {title_prefix}{formatted_title}"
  154. elif show_keyword and keyword:
  155. result = f"*[{keyword}]* {title_prefix}{formatted_title}"
  156. else:
  157. result = f"{title_prefix}{formatted_title}"
  158. # 排名(使用 * 加粗)
  159. rank_display = format_rank_display(
  160. title_data["ranks"], title_data["rank_threshold"], "slack"
  161. )
  162. if rank_display:
  163. result += f" {rank_display}"
  164. if title_data["time_display"]:
  165. result += f" `- {title_data['time_display']}`"
  166. if title_data["count"] > 1:
  167. result += f" `({title_data['count']}次)`"
  168. return result
  169. elif platform == "html":
  170. rank_display = format_rank_display(
  171. title_data["ranks"], title_data["rank_threshold"], "html"
  172. )
  173. link_url = title_data["mobile_url"] or title_data["url"]
  174. escaped_title = html_escape(cleaned_title)
  175. escaped_source_name = html_escape(title_data["source_name"])
  176. # 构建前缀(来源或关键词)
  177. if show_source:
  178. prefix = f'<span class="source-tag">[{escaped_source_name}]</span> '
  179. elif show_keyword and keyword:
  180. escaped_keyword = html_escape(keyword)
  181. prefix = f'<span class="keyword-tag">[{escaped_keyword}]</span> '
  182. else:
  183. prefix = ""
  184. if link_url:
  185. escaped_url = html_escape(link_url)
  186. formatted_title = f'{prefix}<a href="{escaped_url}" target="_blank" class="news-link">{escaped_title}</a>'
  187. else:
  188. formatted_title = f'{prefix}<span class="no-link">{escaped_title}</span>'
  189. if rank_display:
  190. formatted_title += f" {rank_display}"
  191. if title_data["time_display"]:
  192. escaped_time = html_escape(title_data["time_display"])
  193. formatted_title += f" <font color='grey'>- {escaped_time}</font>"
  194. if title_data["count"] > 1:
  195. formatted_title += f" <font color='green'>({title_data['count']}次)</font>"
  196. if title_data.get("is_new"):
  197. formatted_title = f"<div class='new-title'>🆕 {formatted_title}</div>"
  198. return formatted_title
  199. else:
  200. return cleaned_title