⭐AI-driven public opinion & trend monitor with multi-platform aggregation, RSS, and smart alerts.🎯 告别信息过载,你的 AI 舆情监控助手与热点筛选工具!聚合多平台热点 + RSS 订阅,支持关键词精准筛选。AI 智能筛选新闻 + AI 翻译 + AI 分析简报直推手机,也支持接入 MCP 架构,赋能 AI 自然语言对话分析、情感洞察与趋势预测等。支持 Docker ,数据本地/云端自持。集成微信/飞书/钉钉/Telegram/邮件/ntfy/bark/slack 等渠道智能推送。
849 matches across 9 categories. Click a row to expand file-level details.
| Severity | File | Line | Snippet |
|---|---|---|---|
| MEDIUM | config/ai_translation_prompt.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_translation_prompt.txt | 4 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_translation_prompt.txt | 12 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_interests.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_interests.txt | 4 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_analysis_prompt.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_analysis_prompt.txt | 4 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/ai_analysis_prompt.txt | 21 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/config.yaml | 1 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/config.yaml | 4 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/config.yaml | 10 | # =============================================================== |
| MEDIUM | config/config.yaml | 12 | # =============================================================== |
| MEDIUM | config/config.yaml | 41 | # =============================================================== |
| MEDIUM | config/config.yaml | 47 | # =============================================================== |
| MEDIUM | config/config.yaml | 54 | # =============================================================== |
| MEDIUM | config/config.yaml | 24 | # =============================================================== |
| MEDIUM | config/config.yaml | 83 | # =============================================================== |
| MEDIUM | config/config.yaml | 90 | # =============================================================== |
| MEDIUM | config/config.yaml | 130 | # =============================================================== |
| MEDIUM | config/config.yaml | 132 | # =============================================================== |
| MEDIUM | config/config.yaml | 152 | # =============================================================== |
| MEDIUM | config/config.yaml | 154 | # =============================================================== |
| MEDIUM | config/config.yaml | 167 | # =============================================================== |
| MEDIUM | config/config.yaml | 169 | # =============================================================== |
| MEDIUM | config/config.yaml | 197 | # =============================================================== |
| MEDIUM | config/config.yaml | 201 | # =============================================================== |
| MEDIUM | config/config.yaml | 231 | # =============================================================== |
| MEDIUM | config/config.yaml | 239 | # =============================================================== |
| MEDIUM | config/config.yaml | 285 | # =============================================================== |
| MEDIUM | config/config.yaml | 287 | # =============================================================== |
| MEDIUM | config/config.yaml | 329 | # =============================================================== |
| MEDIUM | config/config.yaml | 331 | # =============================================================== |
| MEDIUM | config/config.yaml | 388 | # =============================================================== |
| MEDIUM | config/config.yaml | 390 | # =============================================================== |
| MEDIUM | config/config.yaml | 415 | # =============================================================== |
| MEDIUM | config/config.yaml | 419 | # =============================================================== |
| MEDIUM | config/config.yaml | 432 | # =============================================================== |
| MEDIUM | config/config.yaml | 434 | # =============================================================== |
| MEDIUM | config/timeline.yaml | 101 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/timeline.yaml | 104 | # ─────────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 106 | # ─────────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 109 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 117 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 509 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 515 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 523 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 527 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 1 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/timeline.yaml | 4 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/timeline.yaml | 152 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 160 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 211 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 222 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 295 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 304 | # ─────────────────────────────────────────────────────────── |
| MEDIUM | config/timeline.yaml | 353 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/timeline.yaml | 362 | # ═══════════════════════════════════════════════════════════════ |
| MEDIUM | config/timeline.yaml | 401 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 406 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| MEDIUM | config/timeline.yaml | 448 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 94 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | trendradar/report/html.py | 2379 | var timer = null; |
| HIGH | trendradar/report/html.py | 2386 | item.style.display = (!query || title.toLowerCase().indexOf(query) !== -1) ? '' : 'none'; |
| HIGH | trendradar/report/html.py | 2390 | item.style.display = (!query || title.toLowerCase().indexOf(query) !== -1) ? '' : 'none'; |
| HIGH | trendradar/report/html.py | 2867 | lines.push('# ' + (headerTitle ? headerTitle.textContent.trim() : 'TrendRadar')); |
| HIGH | trendradar/report/html.py | 2868 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2876 | if (label && value) { |
| HIGH | trendradar/report/html.py | 2877 | lines.push('- **' + label.textContent.trim() + '**: ' + value.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2880 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2899 | if (rank && rank.textContent.trim() && rank.textContent.trim() !== '?') meta.push('#' + rank.textCon |
| HIGH | trendradar/report/html.py | 2899 | if (rank && rank.textContent.trim() && rank.textContent.trim() !== '?') meta.push('#' + rank.textCon |
| HIGH | trendradar/report/html.py | 2901 | if (source) meta.push(source.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2903 | if (keyword) meta.push(keyword.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2905 | if (time) meta.push(time.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2907 | if (count) meta.push(count.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2922 | lines.push('## 热点新闻'); |
| HIGH | trendradar/report/html.py | 2923 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2928 | lines.push('### ' + wordName.textContent.trim() + (wordCount ? ' (' + wordCount.textContent. |
| HIGH | trendradar/report/html.py | 2929 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2934 | if (line) lines.push(line); |
| HIGH | trendradar/report/html.py | 2936 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2944 | lines.push('## ' + (newTitle ? newTitle.textContent.trim() : '本次新增热点')); |
| HIGH | trendradar/report/html.py | 2945 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2950 | lines.push('### ' + srcTitle.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2951 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2956 | if (line) lines.push(line); |
| HIGH | trendradar/report/html.py | 2958 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2966 | lines.push('## ' + (rssSectionTitle ? rssSectionTitle.textContent.trim() : 'RSS 订阅更新')); |
| HIGH | trendradar/report/html.py | 2967 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2973 | lines.push('### ' + feedName.textContent.trim() + (feedCount ? ' (' + feedCount.textContent. |
| HIGH | trendradar/report/html.py | 2974 | lines.push(''); |
| HIGH | trendradar/report/html.py | 2984 | if (time) meta.push(time.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2986 | if (author) meta.push(author.textContent.trim()); |
| HIGH | trendradar/report/html.py | 2991 | lines.push(line); |
| HIGH | trendradar/report/html.py | 2993 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3003 | lines.push('## AI 分析'); |
| HIGH | trendradar/report/html.py | 3004 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3005 | lines.push('> ' + aiError.textContent.trim()); |
| HIGH | trendradar/report/html.py | 3006 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3011 | lines.push('## ' + (aiTitle ? aiTitle.textContent.trim() : 'AI 热点分析')); |
| HIGH | trendradar/report/html.py | 3012 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3018 | lines.push('### ' + blockTitle.textContent.trim()); |
| HIGH | trendradar/report/html.py | 3019 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3022 | lines.push(blockContent.textContent.trim()); |
| HIGH | trendradar/report/html.py | 3023 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3033 | lines.push('## ' + (standaloneTitle ? standaloneTitle.textContent.trim() : '独立展示区')); |
| HIGH | trendradar/report/html.py | 3034 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3040 | lines.push('### ' + name.textContent.trim() + (cnt ? ' (' + cnt.textContent.trim() + ')' : ' |
| HIGH | trendradar/report/html.py | 3041 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3046 | if (line) lines.push(line); |
| HIGH | trendradar/report/html.py | 3048 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3057 | lines.push('## 抓取异常'); |
| HIGH | trendradar/report/html.py | 3058 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3060 | lines.push('- ' + item.textContent.trim()); |
| HIGH | trendradar/report/html.py | 3062 | lines.push(''); |
| HIGH | trendradar/report/html.py | 3067 | lines.push('---'); |
| HIGH | trendradar/report/html.py | 3068 | lines.push('*Generated by TrendRadar*'); |
| HIGH | trendradar/report/html.py | 3087 | var savedMode = null; |
| HIGH | trendradar/report/html.py | 3089 | if (savedMode === '1' || (savedMode === null && window.innerWidth > 768)) { |
| HIGH | trendradar/report/html.py | 3096 | var savedDark = null; |
| HIGH | trendradar/report/html.py | 2299 | var tabBar = wrapper ? wrapper.querySelector('.tab-bar') : null; |
| 22 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | trendradar/core/frequency.py | 99 | 加载频率词配置 配置文件格式说明: - 每个词组由空行分隔 - [GLOBAL_FILTER] 区域定义全局过滤词 - [WORD_GROUPS] 区域定义词组(默认) 词组语法: |
| HIGH | trendradar/core/config.py | 12 | 解析多账号配置,返回账号列表 Args: config_value: 配置值字符串,多个账号用分隔符分隔 separator: 分隔符,默认为 ; Returns: |
| HIGH | trendradar/core/config.py | 45 | 验证配对配置的数量是否一致 对于需要多个配置项配对的渠道(如 Telegram 的 token 和 chat_id), 验证所有配置项的账号数量是否一致。 Args: configs: |
| HIGH | trendradar/core/config.py | 102 | 限制账号数量 当配置的账号数量超过最大限制时,只使用前 N 个账号, 并输出警告信息。 Args: accounts: 账号列表 max_count: 最大账号数量 |
| HIGH | trendradar/core/config.py | 129 | 安全获取指定索引的账号值 当索引超出范围或账号值为空时,返回默认值。 Args: accounts: 账号列表 index: 索引 default: 默认值 |
| HIGH | trendradar/core/loader.py | 530 | 加载配置文件 Args: config_path: 配置文件路径,默认从环境变量 CONFIG_PATH 获取或使用 config/config.yaml Returns: 包含 |
| HIGH | trendradar/utils/url.py | 39 | 标准化 URL,去除动态参数 用于数据库去重,确保同一条新闻的不同 URL 变体能被正确识别为同一条。 处理规则: 1. 去除平台特定的动态参数(如微博的 band_rank) 2. 去除通用追 |
| HIGH | trendradar/ai/client.py | 47 | 调用 AI 模型进行对话 Args: messages: 消息列表,格式: [{"role": "system/user/assistant", "content": "..."} |
| HIGH | mcp_server/server.py | 122 | 【推荐优先调用】将自然语言日期表达式解析为标准日期范围 **为什么需要这个工具?** 用户经常使用"本周"、"最近7天"等自然语言表达日期,但 AI 模型自己计算日期 可能导致不一致的结果。此工具在服务器 |
| HIGH | mcp_server/server.py | 225 | 获取热点话题统计 Args: top_n: 返回TOP N话题,默认10 mode: 时间模式 - "daily": 当日累计数据统计 - |
| HIGH | mcp_server/server.py | 261 | 获取最新的 RSS 订阅数据(支持多日查询) RSS 数据与热榜新闻分开存储,按时间流展示,适合获取特定来源的最新内容。 Args: feeds: RSS 源 ID 列表,如 ['hacker- |
| HIGH | mcp_server/server.py | 295 | 搜索 RSS 数据 在 RSS 订阅数据中搜索包含指定关键词的文章。 Args: keyword: 搜索关键词(必需) feeds: RSS 源 ID 列表,如 ['hacker |
| HIGH | mcp_server/server.py | 398 | 统一话题趋势分析工具 - 整合多种趋势分析模式 建议:使用自然语言日期时,先调用 resolve_date_range 获取精确日期范围。 Args: topic: 话题关键词(必需) |
| HIGH | mcp_server/server.py | 447 | 统一数据洞察分析工具 - 整合多种数据分析模式 Args: insight_type: 洞察类型,可选值: - "platform_compare": 平台对比分析(对比不同平台对 |
| HIGH | mcp_server/server.py | 492 | 分析新闻的情感倾向和热度趋势 建议:使用自然语言日期时,先调用 resolve_date_range 获取精确日期范围。 Args: topic: 话题关键词(可选) platf |
| HIGH | mcp_server/server.py | 532 | 查找与指定新闻标题相关的其他新闻(支持当天和历史数据) Args: reference_title: 参考新闻标题(完整或部分) date_range: 日期范围(可选) |
| HIGH | mcp_server/server.py | 599 | 跨平台新闻聚合 - 对相似新闻进行去重合并 将不同平台报道的同一事件合并为一条聚合新闻,显示跨平台覆盖情况和综合热度。 Args: date_range: 日期范围,不指定则查询今天 |
| HIGH | mcp_server/server.py | 639 | 时期对比分析 - 比较两个时间段的新闻数据 对比不同时期的热点话题、平台活跃度、新闻数量等维度。 **使用场景:** - 对比本周和上周的热点变化 - 分析某个话题在两个时期的热度差异 |
| HIGH | mcp_server/server.py | 705 | 统一搜索接口,支持多种搜索模式,可同时搜索热榜和RSS 建议:使用自然语言日期时,先调用 resolve_date_range 获取精确日期范围。 Args: query: 搜索关键词或内容片段 |
| HIGH | mcp_server/server.py | 794 | 检查版本更新(同时检查 TrendRadar 和 MCP Server) 比较本地版本与 GitHub 远程版本,判断是否需要更新。 Args: proxy_url: 可选的代理URL,用于访问 |
| HIGH | mcp_server/server.py | 820 | 手动触发一次爬取任务(可选持久化) Args: platforms: 平台ID列表,如 ['zhihu', 'weibo'],不指定则使用所有平台 save_to_local: 是否保存到 |
| HIGH | mcp_server/server.py | 849 | 从远程存储拉取数据到本地 用于 MCP Server 等场景:爬虫存到远程云存储(如 Cloudflare R2), MCP Server 拉取到本地进行分析查询。 Args: days |
| HIGH | mcp_server/server.py | 905 | 列出本地/远程可用的日期范围 查看本地和远程存储中有哪些日期的数据可用。 Args: source: 数据来源 - "local": 仅本地 - |
| HIGH | mcp_server/server.py | 935 | 读取指定 URL 的文章内容,返回 LLM 友好的 Markdown 格式 通过 Jina AI Reader 将网页转换为干净的 Markdown,自动去除广告、导航栏等噪音内容。 适合用于:阅读新闻正文、获取 |
| HIGH | mcp_server/server.py | 975 | 批量读取多篇文章内容(最多 5 篇,间隔 5 秒) 逐篇请求文章内容,每篇之间自动间隔 5 秒以遵守速率限制。 **典型使用流程:** 1. 先用 search_news(include_url=Tru |
| HIGH | mcp_server/server.py | 1014 | 获取通知渠道的格式化策略指南 返回各渠道支持的 Markdown 特性、格式限制和最佳格式化提示词。 在调用 send_notification 之前使用此工具,可以了解目标渠道的格式要求, 从而生成最佳 |
| HIGH | mcp_server/server.py | 1077 | 向已配置的通知渠道发送消息 接受 markdown 格式内容,内部自动适配各渠道的格式要求和限制: - 飞书:Markdown 卡片消息(支持 **粗体**、<font color>彩色文本、[链接](url)、 |
| HIGH | mcp_server/tools/search_tools.py | 43 | 统一新闻搜索工具 - 整合多种搜索模式,支持同时搜索热榜和RSS Args: query: 查询内容(必需)- 关键词、内容片段或实体名称 search_m |
| HIGH | mcp_server/tools/analytics.py | 119 | 统一数据洞察分析工具 - 整合多种数据分析模式 Args: insight_type: 洞察类型,可选值: - "platform_compare" |
| HIGH | mcp_server/tools/analytics.py | 272 | 热度趋势分析 - 追踪特定话题的热度变化趋势 Args: topic: 话题关键词 date_range: 日期范围(可选) |
| HIGH | mcp_server/tools/analytics.py | 430 | 平台对比分析 - 对比不同平台对同一话题的关注度 Args: topic: 话题关键词(可选,不指定则对比整体活跃度) date_range: 日期范围,格 |
| HIGH | mcp_server/tools/analytics.py | 554 | 关键词共现分析 - 分析哪些关键词经常同时出现 Args: min_frequency: 最小共现频次 top_n: 返回TOP N关键词对 |
| HIGH | mcp_server/tools/analytics.py | 666 | 情感倾向分析 - 生成用于 AI 情感分析的结构化提示词 本工具收集新闻数据并生成优化的 AI 提示词,你可以将其发送给 AI 进行深度情感分析。 Args: t |
| HIGH | mcp_server/tools/analytics.py | 944 | 相似新闻查找 - 基于标题相似度查找相关新闻 Args: reference_title: 参考标题 threshold: 相似度阈值(0-1之间) |
| HIGH | mcp_server/tools/analytics.py | 1059 | 实体识别搜索 - 搜索包含特定人物/地点/机构的新闻 Args: entity: 实体名称 entity_type: 实体类型(person/locatio |
| HIGH | mcp_server/tools/analytics.py | 1188 | 每日/每周摘要生成器 - 自动生成热点摘要报告 Args: report_type: 报告类型(daily/weekly) date_range: 自定义日 |
| HIGH | mcp_server/tools/analytics.py | 1367 | 平台活跃度统计 - 统计各平台的发布频率和活跃时间段 Args: date_range: 日期范围(可选) Returns: 平台活跃度统 |
| HIGH | mcp_server/tools/analytics.py | 1495 | 话题生命周期分析 - 追踪话题从出现到消失的完整周期 Args: topic: 话题关键词 date_range: 日期范围(可选) |
| HIGH | mcp_server/tools/analytics.py | 1653 | 异常热度检测 - 自动识别突然爆火的话题 Args: threshold: 热度突增倍数阈值 time_window: 检测时间窗口(小时) |
| HIGH | mcp_server/tools/analytics.py | 1792 | 话题预测 - 基于历史数据预测未来可能的热点 Args: lookahead_hours: 预测未来多少小时 confidence_threshold: 置 |
| HIGH | mcp_server/utils/validators.py | 22 | 将字符串解析为列表 支持格式: - JSON 数组: '["zhihu", "weibo"]' - Python 列表字符串: "['zhihu', 'weibo']" - 逗号分隔: "zhih |
| HIGH | mcp_server/utils/validators.py | 74 | 将字符串解析为整数 Args: value: 字符串值 param_name: 参数名(用于错误消息) Returns: 解析后的整数 Raises: |
| HIGH | mcp_server/utils/validators.py | 106 | 将字符串解析为浮点数 Args: value: 字符串值 param_name: 参数名(用于错误消息) Returns: 解析后的浮点数 Raises |
| HIGH | mcp_server/utils/validators.py | 197 | 验证平台列表 Args: platforms: 平台ID列表或字符串,None表示使用 config.yaml 中配置的所有平台 支持多种格式: |
| HIGH | mcp_server/utils/validators.py | 258 | 验证数量限制参数 Args: limit: 限制数量(整数或字符串) default: 默认值 max_limit: 最大限制 Returns: |
| HIGH | mcp_server/utils/validators.py | 295 | 验证日期格式 Args: date_str: 日期字符串 (YYYY-MM-DD) Returns: datetime对象 Raises: Invali |
| HIGH | mcp_server/utils/validators.py | 317 | 规范化 date_range 参数 某些 MCP 客户端(特别是 HTTP 方式)会将 JSON 对象序列化为字符串传入。 此函数尝试将 JSON 字符串解析为 dict,如果不是 JSON 格式则保持原样。 |
| HIGH | mcp_server/utils/validators.py | 364 | 验证日期范围 Args: date_range: 日期范围,支持多种格式: - dict: {"start": "YYYY-MM-DD", "end": "YYYY-MM-DD"} |
| HIGH | mcp_server/utils/validators.py | 483 | 验证关键词 Args: keyword: 搜索关键词 Returns: 处理后的关键词 Raises: InvalidParameterError: 关 |
| HIGH | mcp_server/utils/validators.py | 516 | 验证TOP N参数 Args: top_n: TOP N数量(整数或字符串) default: 默认值 Returns: 验证后的值 Raises: |
| HIGH | mcp_server/utils/validators.py | 533 | 验证模式参数 Args: mode: 模式字符串 valid_modes: 有效模式列表 default: 默认模式 Returns: 验证后的模 |
| HIGH | mcp_server/utils/validators.py | 563 | 验证配置节参数 Args: section: 配置节名称 Returns: 验证后的配置节 Raises: InvalidParameterError: |
| HIGH | mcp_server/utils/validators.py | 586 | 验证阈值参数(浮点数) Args: threshold: 阈值(浮点数、整数或字符串) default: 默认值 min_value: 最小值 max_va |
| HIGH | mcp_server/utils/validators.py | 633 | 验证并解析日期查询字符串 Args: date_query: 日期查询字符串 allow_future: 是否允许未来日期 max_days_ago: 允许查询的最大天数 |
| HIGH | mcp_server/utils/date_parser.py | 93 | 解析日期查询字符串 支持的格式: - 相对日期(中文):今天、昨天、前天、大前天、N天前 - 相对日期(英文):today、yesterday、N days ago |
| HIGH | mcp_server/utils/date_parser.py | 279 | 将日期格式化为文件夹名称 Args: date: datetime对象 Returns: 文件夹名称,格式: YYYY-MM-DD |
| HIGH | mcp_server/utils/date_parser.py | 332 | 将自然语言日期表达式解析为标准日期范围 这是专门为 MCP 工具设计的方法,用于在服务器端解析日期表达式, 避免 AI 模型自己计算日期导致的不一致问题。 Args: |
| HIGH | mcp_server/services/data_service.py | 52 | 获取最新一批爬取的新闻数据 Args: platforms: 平台ID列表,None表示所有平台 limit: 返回条数限制 inc |
| HIGH | mcp_server/services/data_service.py | 127 | 按指定日期获取新闻 Args: target_date: 目标日期 platforms: 平台ID列表,None表示所有平台 lim |
| HIGH | mcp_server/services/data_service.py | 207 | 按关键词搜索新闻 Args: keyword: 搜索关键词 date_range: 日期范围 (start_date, end_date) |
| 8 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | docker/manage.py | 466 | def _is_expected_webserver_process(pid: int) -> bool: |
| LOW | docker/manage.py | 474 | def _terminate_webserver_process(pid: int, require_expected: bool = True) -> bool: |
| LOW | docs/assets/script.js | 452 | function saveFrequencyToLocalStorage() { |
| LOW | docs/assets/script.js | 456 | function saveTimelineToLocalStorage() { |
| LOW | docs/assets/script.js | 2614 | function parseDisplayRegionsFromYaml() { |
| LOW | docs/assets/script.js | 2721 | function updateDisplayRegionsInYaml(regions) { |
| LOW | docs/assets/script.js | 3119 | function parseStandaloneConfigFromYaml() { |
| LOW | docs/assets/script.js | 3198 | function updateStandaloneConfigInYaml(type, list) { |
| LOW | docs/assets/script.js | 3341 | function showVersionComparisonModal(fileName, currentVersion, latestVersion) { |
| LOW | docs/assets/script.js | 4625 | function applyTimelineEditorChanges(editor, lines) { |
| LOW | docs/assets/script.js | 4681 | function updateTimelineSectionField(presetName, field, value) { |
| LOW | docs/assets/script.js | 4865 | function scrollTimelineEditorToPreset(presetName) { |
| LOW | docs/assets/script.js | 5577 | function removePeriodFromDayPlanInLines(lines, sectionInfo, planKey, periodKey) { |
| LOW | trendradar/context.py | 462 | def create_notification_dispatcher(self) -> NotificationDispatcher: |
| LOW | trendradar/context.py | 919 | def convert_ai_filter_to_report_data( |
| LOW | trendradar/__main__.py | 253 | def _detect_docker_environment(self) -> bool: |
| LOW | trendradar/__main__.py | 280 | def _set_update_info_from_config(self) -> None: |
| LOW | trendradar/__main__.py | 302 | def _has_notification_configured(self) -> bool: |
| LOW | trendradar/__main__.py | 343 | def _prepare_ai_analysis_data( |
| LOW | trendradar/__main__.py | 618 | def _prepare_current_title_info(self, results: Dict, time_info: str) -> Dict: |
| LOW | trendradar/__main__.py | 915 | def _send_notification_if_needed( |
| LOW | trendradar/__main__.py | 1047 | def _initialize_and_check_config(self) -> bool: |
| LOW | trendradar/__main__.py | 1208 | def _process_rss_data_by_mode(self, rss_data) -> Tuple[Optional[List[Dict]], Optional[List[Dict]], Optional[List[Dic |
| LOW | trendradar/__main__.py | 1388 | def _convert_rss_items_to_list(self, items_dict: Dict, id_to_name: Dict) -> List[Dict]: |
| LOW | trendradar/__main__.py | 1484 | def _generate_rss_html_report(self, rss_items: list, feeds_info: dict) -> str: |
| LOW | trendradar/core/config.py | 11 | def parse_multi_account_config(config_value: str, separator: str = ";") -> List[str]: |
| LOW | trendradar/core/analyzer.py | 712 | def convert_keyword_stats_to_platform_stats( |
| LOW | trendradar/core/loader.py | 96 | def _load_notification_config(config_data: Dict) -> Dict: |
| LOW | trendradar/core/loader.py | 303 | def _load_ai_translation_config(config_data: Dict) -> Dict: |
| LOW | trendradar/core/loader.py | 447 | def _print_notification_sources(config: Dict) -> None: |
| LOW | trendradar/core/data.py | 15 | def read_all_today_titles_from_storage( |
| LOW | trendradar/core/data.py | 113 | def detect_latest_new_titles_from_storage( |
| LOW | trendradar/notification/formatters.py | 72 | def convert_markdown_to_mrkdwn(content: str) -> str: |
| LOW | trendradar/notification/renderer.py | 287 | def _render_rss_section_feishu(rss_items: list, separator: str = "---") -> str: |
| LOW | trendradar/notification/renderer.py | 330 | def _render_rss_section_markdown(rss_items: list) -> str: |
| LOW | trendradar/notification/batch.py | 34 | def get_max_batch_header_size(format_type: str) -> int: |
| LOW | trendradar/notification/batch.py | 73 | def truncate_at_line_boundary(text: str, max_bytes: int) -> str: |
| LOW | trendradar/notification/batch.py | 95 | def truncate_preserving_footer(content: str, max_bytes: int) -> str: |
| LOW | trendradar/notification/splitter.py | 135 | def split_content_into_batches( |
| LOW | trendradar/notification/splitter.py | 617 | def process_new_titles_section(current_batch, current_batch_has_content, batches, add_separator=True): |
| LOW | trendradar/notification/splitter.py | 845 | def process_standalone_section_wrapper(current_batch, current_batch_has_content, batches, add_separator=True): |
| LOW | trendradar/notification/splitter.py | 856 | def process_rss_stats_wrapper(current_batch, current_batch_has_content, batches, add_separator=True): |
| LOW | trendradar/notification/splitter.py | 995 | def _process_rss_stats_section( |
| LOW | trendradar/notification/splitter.py | 1226 | def _process_rss_new_titles_section( |
| LOW | trendradar/notification/splitter.py | 1474 | def _process_standalone_section( |
| LOW | trendradar/notification/splitter.py | 1701 | def _format_standalone_platform_item(item: Dict, index: int, format_type: str, rank_threshold: int = 10) -> str: |
| LOW | trendradar/notification/splitter.py | 1807 | def _format_standalone_rss_item( |
| LOW | trendradar/storage/remote.py | 489 | def get_active_ai_filter_tags(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/remote.py | 495 | def get_latest_ai_filter_tag_version(self, date=None): |
| LOW | trendradar/storage/remote.py | 498 | def deprecate_all_ai_filter_tags(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/remote.py | 516 | def get_active_ai_filter_results(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/remote.py | 519 | def deprecate_specific_ai_filter_tags(self, tag_ids, date=None): |
| LOW | trendradar/storage/remote.py | 525 | def update_ai_filter_tags_hash(self, interests_file, new_hash, date=None): |
| LOW | trendradar/storage/remote.py | 531 | def update_ai_filter_tag_descriptions(self, tag_updates, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/remote.py | 537 | def update_ai_filter_tag_priorities(self, tag_priorities, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/remote.py | 558 | def clear_unmatched_analyzed_news(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/local.py | 234 | def get_active_ai_filter_tags(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/local.py | 240 | def get_latest_ai_filter_tag_version(self, date=None): |
| LOW | trendradar/storage/local.py | 243 | def deprecate_all_ai_filter_tags(self, date=None, interests_file="ai_interests.txt"): |
| LOW | trendradar/storage/local.py | 252 | def get_active_ai_filter_results(self, date=None, interests_file="ai_interests.txt"): |
| 75 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | docker/manage.py | 462 | except Exception: |
| LOW | docker/manage.py | 31 | except Exception as e: |
| LOW | docker/manage.py | 46 | except Exception as e: |
| LOW | docker/manage.py | 127 | except Exception as e: |
| LOW | docker/manage.py | 149 | except Exception as e: |
| LOW | docker/manage.py | 236 | except Exception as e: |
| LOW | docker/manage.py | 330 | except Exception as e: |
| LOW | docker/manage.py | 425 | except Exception as e: |
| LOW | docker/manage.py | 449 | except Exception as e: |
| LOW | docker/manage.py | 517 | except Exception: |
| LOW | docker/manage.py | 524 | except Exception: |
| LOW | docker/manage.py | 539 | except Exception: |
| LOW | docker/manage.py | 566 | except Exception as e: |
| LOW | docker/manage.py | 602 | except Exception as e: |
| LOW | docker/manage.py | 620 | except Exception as e: |
| LOW | docker/manage.py | 653 | except Exception as e: |
| LOW | docker/manage.py | 738 | except Exception as e: |
| LOW | trendradar/__main__.py | 75 | except Exception as e: |
| LOW | trendradar/__main__.py | 164 | except Exception as e: |
| LOW | trendradar/__main__.py | 263 | except Exception: |
| LOW | trendradar/__main__.py | 295 | except Exception as e: |
| LOW | trendradar/__main__.py | 442 | except Exception as e: |
| LOW | trendradar/__main__.py | 565 | except Exception as e: |
| LOW | trendradar/__main__.py | 614 | except Exception as e: |
| LOW | trendradar/__main__.py | 1204 | except Exception as e: |
| LOW | trendradar/__main__.py | 1510 | except Exception as e: |
| LOW | trendradar/__main__.py | 1755 | except Exception as e: |
| LOW | trendradar/__main__.py | 1809 | except Exception as e: |
| LOW | trendradar/__main__.py | 1858 | except Exception as e: |
| LOW | trendradar/__main__.py | 1869 | except Exception as e: |
| LOW | trendradar/__main__.py | 1890 | except Exception as e: |
| LOW | trendradar/__main__.py | 1918 | except Exception as e: |
| LOW | trendradar/__main__.py | 2001 | except Exception as e: |
| LOW | trendradar/__main__.py | 2073 | except Exception as e: |
| LOW | trendradar/__main__.py | 2310 | except Exception as e: |
| LOW | trendradar/__main__.py | 2257 | except Exception as e: |
| LOW | trendradar/core/cdn.py | 69 | except Exception as e: |
| LOW | trendradar/core/cdn.py | 86 | except Exception: |
| LOW | trendradar/core/data.py | 78 | except Exception as e: |
| LOW | trendradar/core/data.py | 193 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 221 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 337 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 464 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 579 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 740 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 923 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 1085 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 1197 | except Exception as e: |
| LOW | trendradar/notification/senders.py | 1321 | except Exception as e: |
| LOW | trendradar/crawler/fetcher.py | 103 | except Exception as e: |
| LOW | trendradar/crawler/fetcher.py | 171 | except Exception as e: |
| LOW | trendradar/crawler/rss/fetcher.py | 191 | except Exception as e: |
| LOW | trendradar/utils/time.py | 164 | except Exception: |
| LOW | trendradar/utils/time.py | 237 | except Exception: |
| LOW | trendradar/utils/time.py | 285 | except Exception: |
| LOW | trendradar/utils/url.py | 126 | except Exception: |
| LOW | trendradar/storage/remote.py | 638 | except Exception as e: |
| LOW | trendradar/storage/remote.py | 765 | except Exception: |
| LOW | trendradar/storage/remote.py | 203 | except Exception as e: |
| LOW | trendradar/storage/remote.py | 251 | except Exception as e: |
| 118 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | docker/manage.py | 50 | |
| LOW | docker/manage.py | 131 | |
| LOW | docker/manage.py | 336 | |
| LOW | trendradar/context.py | 517 | |
| LOW | trendradar/context.py | 919 | |
| LOW | trendradar/__main__.py | 80 | |
| LOW | trendradar/__main__.py | 1813 | |
| LOW | trendradar/__main__.py | 343 | |
| LOW | trendradar/__main__.py | 449 | |
| LOW | trendradar/__main__.py | 638 | |
| LOW | trendradar/__main__.py | 915 | |
| LOW | trendradar/__main__.py | 1106 | |
| LOW | trendradar/__main__.py | 1388 | |
| LOW | trendradar/__main__.py | 1460 | |
| LOW | trendradar/core/frequency.py | 96 | |
| LOW | trendradar/core/analyzer.py | 93 | |
| LOW | trendradar/core/analyzer.py | 494 | |
| LOW | trendradar/core/scheduler.py | 363 | |
| LOW | trendradar/core/data.py | 113 | |
| LOW | trendradar/notification/dispatcher.py | 72 | |
| LOW | trendradar/notification/dispatcher.py | 719 | |
| LOW | trendradar/notification/renderer.py | 18 | |
| LOW | trendradar/notification/renderer.py | 150 | |
| LOW | trendradar/notification/splitter.py | 135 | |
| LOW | trendradar/notification/splitter.py | 995 | |
| LOW | trendradar/notification/splitter.py | 1226 | |
| LOW | trendradar/notification/splitter.py | 1474 | |
| LOW | trendradar/notification/splitter.py | 1701 | |
| LOW | trendradar/notification/splitter.py | 1807 | |
| LOW | trendradar/notification/splitter.py | 376 | |
| LOW | trendradar/notification/splitter.py | 617 | |
| LOW | trendradar/notification/splitter.py | 791 | |
| LOW | trendradar/notification/senders.py | 96 | |
| LOW | trendradar/notification/senders.py | 230 | |
| LOW | trendradar/notification/senders.py | 346 | |
| LOW | trendradar/notification/senders.py | 473 | |
| LOW | trendradar/notification/senders.py | 588 | |
| LOW | trendradar/notification/senders.py | 747 | |
| LOW | trendradar/notification/senders.py | 938 | |
| LOW | trendradar/notification/senders.py | 1100 | |
| LOW | trendradar/notification/senders.py | 1206 | |
| LOW | trendradar/crawler/fetcher.py | 117 | |
| LOW | trendradar/crawler/rss/fetcher.py | 242 | |
| LOW | trendradar/utils/time.py | 96 | |
| LOW | trendradar/utils/time.py | 175 | |
| LOW | trendradar/utils/time.py | 242 | |
| LOW | trendradar/storage/remote.py | 574 | |
| LOW | trendradar/storage/remote.py | 680 | |
| LOW | trendradar/storage/remote.py | 773 | |
| LOW | trendradar/storage/remote.py | 831 | |
| LOW | trendradar/storage/local.py | 289 | |
| LOW | trendradar/storage/local.py | 395 | |
| LOW | trendradar/storage/sqlite_mixin.py | 119 | |
| LOW | trendradar/storage/sqlite_mixin.py | 343 | |
| LOW | trendradar/storage/sqlite_mixin.py | 482 | |
| LOW | trendradar/storage/sqlite_mixin.py | 620 | |
| LOW | trendradar/storage/sqlite_mixin.py | 818 | |
| LOW | trendradar/storage/sqlite_mixin.py | 1044 | |
| LOW | trendradar/storage/sqlite_mixin.py | 1581 | |
| LOW | trendradar/storage/base.py | 230 | |
| 50 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | trendradar/__init__.py | 10 | |
| LOW | trendradar/__main__.py | 20 | |
| LOW | trendradar/core/__init__.py | 6 | |
| LOW | trendradar/core/__init__.py | 6 | |
| LOW | trendradar/core/__init__.py | 6 | |
| LOW | trendradar/core/__init__.py | 6 | |
| LOW | trendradar/core/__init__.py | 12 | |
| LOW | trendradar/core/__init__.py | 13 | |
| LOW | trendradar/core/__init__.py | 13 | |
| LOW | trendradar/core/__init__.py | 14 | |
| LOW | trendradar/core/__init__.py | 14 | |
| LOW | trendradar/core/__init__.py | 15 | |
| LOW | trendradar/core/__init__.py | 15 | |
| LOW | trendradar/core/__init__.py | 15 | |
| LOW | trendradar/core/__init__.py | 15 | |
| LOW | trendradar/core/__init__.py | 21 | |
| LOW | trendradar/core/__init__.py | 21 | |
| LOW | trendradar/core/__init__.py | 21 | |
| LOW | trendradar/core/__init__.py | 21 | |
| LOW | trendradar/notification/dispatcher.py | 13 | |
| LOW | trendradar/notification/dispatcher.py | 39 | |
| LOW | trendradar/notification/__init__.py | 19 | |
| LOW | trendradar/notification/__init__.py | 19 | |
| LOW | trendradar/notification/__init__.py | 23 | |
| LOW | trendradar/notification/__init__.py | 23 | |
| LOW | trendradar/notification/__init__.py | 23 | |
| LOW | trendradar/notification/__init__.py | 23 | |
| LOW | trendradar/notification/__init__.py | 29 | |
| LOW | trendradar/notification/__init__.py | 29 | |
| LOW | trendradar/notification/__init__.py | 33 | |
| LOW | trendradar/notification/__init__.py | 33 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 37 | |
| LOW | trendradar/notification/__init__.py | 48 | |
| LOW | trendradar/crawler/__init__.py | 6 | |
| LOW | trendradar/crawler/rss/__init__.py | 8 | |
| LOW | trendradar/crawler/rss/__init__.py | 9 | |
| LOW | trendradar/crawler/rss/__init__.py | 9 | |
| LOW | trendradar/utils/__init__.py | 6 | |
| LOW | trendradar/utils/__init__.py | 6 | |
| LOW | trendradar/utils/__init__.py | 6 | |
| LOW | trendradar/utils/__init__.py | 6 | |
| LOW | trendradar/utils/__init__.py | 6 | |
| LOW | trendradar/utils/__init__.py | 13 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 11 | |
| LOW | trendradar/storage/__init__.py | 19 | |
| LOW | trendradar/storage/__init__.py | 20 | |
| LOW | trendradar/storage/__init__.py | 21 | |
| 24 more matches not shown… | |||
| Severity | File | Line | Snippet |
|---|---|---|---|
| LOW | config/ai_translation_prompt.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/ai_analysis_prompt.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/config.yaml | 1 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/config.yaml | 21 | show_version_update: true # 是否显示版本更新提示(true=显示, false=隐藏) |
| LOW | config/config.yaml | 81 | |
| LOW | config/config.yaml | 121 | |
| LOW | config/config.yaml | 221 | ai_analysis: true # AI 分析区域(true=显示, false=隐藏) |
| LOW | config/config.yaml | 281 | # 示例:{"content": "{content}"} |
| LOW | config/config.yaml | 321 | region: "" # 区域(可选,部分服务商需要) |
| LOW | config/config.yaml | 341 | # 完整列表: https://docs.litellm.ai/docs/providers |
| LOW | config/config.yaml | 381 | # 额外参数(一般无需修改,删掉 # 号即可启用) |
| LOW | config/timeline.yaml | 1 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/timeline.yaml | 21 | # |
| LOW | config/timeline.yaml | 41 | # 调用 AI 大模型对采集到的新闻进行深度分析(可选,需配置 API Key) |
| LOW | config/timeline.yaml | 61 | # |
| LOW | config/timeline.yaml | 81 | # ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ |
| LOW | config/timeline.yaml | 101 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/timeline.yaml | 361 | # |
| LOW | config/timeline.yaml | 381 | # |
| LOW | config/timeline.yaml | 401 | # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| LOW | config/timeline.yaml | 421 | |
| LOW | config/timeline.yaml | 441 | # 例如晚间汇总用 AI 筛选: |
| LOW | config/timeline.yaml | 541 | # 什么是「冲突」? |
| LOW | config/frequency_words.txt | 1 | # ═══════════════════════════════════════════════════════════════ |
| LOW | config/frequency_words.txt | 21 | # 全局过滤区 |
| LOW | config/frequency_words.txt | 41 | # |
| LOW | config/frequency_words.txt | 61 | # ┌─────────────────────────────────────────────────────────────┐ |
| LOW | config/frequency_words.txt | 81 | # 3. 给词组起个名字(推荐) |
| LOW | config/frequency_words.txt | 101 | # |
| LOW | config/frequency_words.txt | 121 | # [苹果公司] |
| LOW | config/frequency_words.txt | 141 | # ──────────────────── |
| LOW | docs/assets/style.css | 1 | /* 编辑器区域滚动条 */ |
| LOW | .github/workflows/crawler.yml | 1 | name: Get Hot News |
| LOW | .github/workflows/crawler.yml | 21 | # 🙏 珍惜资源 / Respect shared resources: |
| LOW | .github/workflows/crawler.yml | 101 | # Critical for load balancing. |
| Severity | File | Line | Snippet |
|---|---|---|---|
| HIGH | trendradar/storage/sqlite_mixin.py | 0 | insert into rank_history (news_item_id, rank, crawl_time, created_at) values (?, ?, ?, ?) |
| HIGH | trendradar/storage/sqlite_mixin.py | 0 | insert into rank_history (news_item_id, rank, crawl_time, created_at) values (?, ?, ?, ?) |
| HIGH | trendradar/storage/sqlite_mixin.py | 0 | insert into rank_history (news_item_id, rank, crawl_time, created_at) values (?, ?, ?, ?) |