SYMBOL INDEX (908 symbols across 54 files) FILE: docker/manage.py function _env_bool (line 22) | def _env_bool(name: str, default: bool) -> bool: function get_timestamp (line 33) | def get_timestamp(): function run_command (line 38) | def run_command(cmd, shell=True, capture_output=True): function manual_run (line 49) | def manual_run(): function parse_cron_schedule (line 64) | def parse_cron_schedule(cron_expr): function show_status (line 145) | def show_status(): function show_config (line 291) | def show_config(): function show_files (line 350) | def show_files(): function show_logs (line 418) | def show_logs(): function restart_supercronic (line 444) | def restart_supercronic(): function _read_proc_cmdline (line 468) | def _read_proc_cmdline(pid: int) -> str: function _is_expected_webserver_process (line 480) | def _is_expected_webserver_process(pid: int) -> bool: function _is_manual_stop_requested (line 488) | def _is_manual_stop_requested() -> bool: function _set_manual_stop_marker (line 493) | def _set_manual_stop_marker(): function _clear_manual_stop_marker (line 502) | def _clear_manual_stop_marker(): function _terminate_webserver_process (line 511) | def _terminate_webserver_process(pid: int, require_expected: bool = True... function _is_webserver_running (line 539) | def _is_webserver_running(pid: int) -> bool: function _cleanup_stale_pid (line 565) | def _cleanup_stale_pid(): function start_webserver (line 580) | def start_webserver(force: bool = False): function stop_webserver (line 651) | def stop_webserver(): function webserver_status (line 677) | def webserver_status(): function webserver_autofix (line 707) | def webserver_autofix(): function show_help (line 744) | def show_help(): function main (line 801) | def main(): FILE: docs/assets/script.js function applyHighlight (line 13) | function applyHighlight(text) { function updateBackdrop (line 25) | function updateBackdrop(textareaId, backdropId) { function syncScroll (line 34) | function syncScroll(textareaId, backdropId) { constant QR_MODAL_DATA (line 47) | const QR_MODAL_DATA = { function openQrModal (line 68) | function openQrModal(type) { function closeQrModal (line 82) | function closeQrModal() { constant MODULE_DEFS (line 89) | const MODULE_DEFS = [ constant INITIAL_YAML (line 106) | const INITIAL_YAML = `# 在此粘贴你的 config.yaml... constant STORAGE_KEY_CONFIG (line 111) | const STORAGE_KEY_CONFIG = 'trendradar_config_yaml'; constant STORAGE_KEY_FREQUENCY (line 112) | const STORAGE_KEY_FREQUENCY = 'trendradar_frequency_txt'; constant STORAGE_KEY_TIMELINE (line 113) | const STORAGE_KEY_TIMELINE = 'trendradar_timeline_yaml'; constant STORAGE_KEY_CONFIG_TIME (line 114) | const STORAGE_KEY_CONFIG_TIME = 'trendradar_config_time'; constant STORAGE_KEY_FREQUENCY_TIME (line 115) | const STORAGE_KEY_FREQUENCY_TIME = 'trendradar_frequency_time'; constant STORAGE_KEY_TIMELINE_TIME (line 116) | const STORAGE_KEY_TIMELINE_TIME = 'trendradar_timeline_time'; constant REMOTE_CONFIG_URL (line 119) | const REMOTE_CONFIG_URL = 'https://raw.githubusercontent.com/sansan0/Tre... constant REMOTE_FREQUENCY_URL (line 120) | const REMOTE_FREQUENCY_URL = 'https://raw.githubusercontent.com/sansan0/... constant REMOTE_TIMELINE_URL (line 121) | const REMOTE_TIMELINE_URL = 'https://raw.githubusercontent.com/sansan0/T... constant REMOTE_VERSION_URL (line 122) | const REMOTE_VERSION_URL = 'https://raw.githubusercontent.com/sansan0/Tr... function debounceSaveConfig (line 235) | function debounceSaveConfig() { function debounceSaveFrequency (line 243) | function debounceSaveFrequency() { function debounceSaveTimeline (line 251) | function debounceSaveTimeline() { function initDragAndDrop (line 261) | function initDragAndDrop(editor, type) { function handleFileDrop (line 318) | function handleFileDrop(e, type) { function saveConfigToLocalStorage (line 388) | function saveConfigToLocalStorage() { function saveFrequencyToLocalStorage (line 402) | function saveFrequencyToLocalStorage() { function saveTimelineToLocalStorage (line 416) | function saveTimelineToLocalStorage() { function saveAllToLocalStorage (line 430) | function saveAllToLocalStorage() { function saveToLocalStorage (line 437) | function saveToLocalStorage() { function formatSaveTime (line 442) | function formatSaveTime(isoString) { function updateSaveTimeDisplay (line 460) | function updateSaveTimeDisplay() { function showToast (line 651) | function showToast(message, type = 'info') { function renderModules (line 688) | function renderModules() { function renderModuleNav (line 723) | function renderModuleNav() { function renderControls (line 849) | function renderControls(mod) { function syncYamlToUI (line 1006) | function syncYamlToUI() { function updateYamlFromUI (line 1042) | function updateYamlFromUI(modKey, path, el) { function createToggleControl (line 1176) | function createToggleControl(mod, path, label) { function createInputControl (line 1189) | function createInputControl(mod, path, label, type = "text") { function createNumberControl (line 1198) | function createNumberControl(mod, path, label) { function createSelectControl (line 1207) | function createSelectControl(mod, path, label, options) { function parseFrequencyText (line 1340) | function parseFrequencyText(text) { function buildFrequencyText (line 1495) | function buildFrequencyText(data) { function syncFrequencyToUI (line 1666) | function syncFrequencyToUI() { function renderFrequencyPanel (line 1672) | function renderFrequencyPanel(data) { function parsePlatformsFromYaml (line 2388) | function parsePlatformsFromYaml() { function renderPlatformsList (line 2399) | function renderPlatformsList() { function reorderPlatforms (line 2448) | function reorderPlatforms(oldIndex, newIndex) { function updatePlatformsInYaml (line 2456) | function updatePlatformsInYaml(platforms) { constant DISPLAY_REGIONS_DEF (line 2561) | const DISPLAY_REGIONS_DEF = [ function parseDisplayRegionsFromYaml (line 2570) | function parseDisplayRegionsFromYaml() { function renderDisplayRegionsList (line 2616) | function renderDisplayRegionsList() { function updateDisplayRegionsInYaml (line 2677) | function updateDisplayRegionsInYaml(regions) { function parseRssFeedsFromYaml (line 2814) | function parseRssFeedsFromYaml() { function renderRssFeedsList (line 2825) | function renderRssFeedsList() { function updateRssFeedsInYaml (line 2894) | function updateRssFeedsInYaml(feeds) { function openRssModalWithData (line 3007) | function openRssModalWithData(feed, editIndex) { function parseStandaloneConfigFromYaml (line 3075) | function parseStandaloneConfigFromYaml() { function renderStandaloneLists (line 3088) | function renderStandaloneLists() { function updateStandaloneConfigInYaml (line 3154) | function updateStandaloneConfigInYaml(type, list) { function extractVersion (line 3212) | function extractVersion(text) { function compareVersions (line 3222) | function compareVersions(v1, v2) { function getCurrentTab (line 3292) | function getCurrentTab() { function showVersionComparisonModal (line 3297) | function showVersionComparisonModal(fileName, currentVersion, latestVers... constant PRESET_PLATFORMS (line 3406) | const PRESET_PLATFORMS = [ function renderAvailablePlatforms (line 3488) | function renderAvailablePlatforms() { function toggleRssTips (line 3640) | function toggleRssTips() { function fillRssUrl (line 3651) | function fillRssUrl(url) { constant PRESET_META (line 3667) | const PRESET_META = { constant DAY_NAMES (line 3675) | const DAY_NAMES = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; function getActivePreset (line 3680) | function getActivePreset() { function parseTimelineData (line 3690) | function parseTimelineData() { function getPresetConfig (line 3701) | function getPresetConfig(data, presetName) { function syncTimelineToUI (line 3710) | function syncTimelineToUI() { function renderWeekView (line 3805) | function renderWeekView(config, presetName) { function mergeWithDefault (line 3888) | function mergeWithDefault(period, defaultCfg) { function getBlockColorClass (line 3900) | function getBlockColorClass(merged) { function computeBlocks (line 3914) | function computeBlocks(startStr, endStr) { function parseTime (line 3923) | function parseTime(str) { function escapeAttr (line 3928) | function escapeAttr(s) { function showTlTooltip (line 3937) | function showTlTooltip(event, name, start, end, push, analyze, mode) { function hideTlTooltip (line 3968) | function hideTlTooltip() { function renderPeriodDetails (line 3978) | function renderPeriodDetails(config, presetName) { function renderBehaviorToggles (line 4158) | function renderBehaviorToggles(cfg, presetName, periodKey, rawCfg = null) { function updateTimelineField (line 4395) | function updateTimelineField(presetName, periodKey, field, value) { function resolveTimelineSection (line 4505) | function resolveTimelineSection(lines, presetName) { function resolveTimelineTarget (line 4556) | function resolveTimelineTarget(lines, presetName, periodKey) { function applyTimelineEditorChanges (line 4581) | function applyTimelineEditorChanges(editor, lines) { function removeTimelineField (line 4590) | function removeTimelineField(presetName, periodKey, field) { function updateTimelineSectionField (line 4637) | function updateTimelineSectionField(presetName, field, value) { function findChildKey (line 4670) | function findChildKey(lines, start, end, parentIndent, key) { function findBlockEnd (line 4687) | function findBlockEnd(lines, start, indent, maxEnd) { function replaceLineValue (line 4700) | function replaceLineValue(lines, idx, value) { function insertTimelineField (line 4733) | function insertTimelineField(lines, targetStart, targetEnd, targetIndent... function scrollTimelineEditorToPreset (line 4820) | function scrollTimelineEditorToPreset(presetName) { function buildEmptyPresetBlock (line 4982) | function buildEmptyPresetBlock(key, name, desc) { function buildPresetYamlBlock (line 5015) | function buildPresetYamlBlock(key, cfg) { constant PROTECTED_PRESETS (line 5207) | const PROTECTED_PRESETS = ['morning_evening', 'always_on', 'office_hours... function findPresetSection (line 5461) | function findPresetSection(lines, presetName) { function removePeriodFromDayPlans (line 5499) | function removePeriodFromDayPlans(lines, sectionInfo, periodKey) { function removePeriodFromDayPlanInLines (line 5532) | function removePeriodFromDayPlanInLines(lines, sectionInfo, planKey, per... function updatePresetMeta (line 5611) | function updatePresetMeta(presetName, field, value) { function initDayPlanSortable (line 5705) | function initDayPlanSortable(presetName) { function reorderDayPlanPeriods (line 5732) | function reorderDayPlanPeriods(presetName, planKey, orderedKeys) { function toggleSupportSidebar (line 5776) | function toggleSupportSidebar() { FILE: mcp_server/server.py function _get_tools (line 33) | def _get_tools(project_root: Optional[str] = None): function get_platforms_resource (line 50) | async def get_platforms_resource() -> str: function get_rss_feeds_resource (line 67) | async def get_rss_feeds_resource() -> str: function get_available_dates_resource (line 82) | async def get_available_dates_resource() -> str: function get_keywords_resource (line 99) | async def get_keywords_resource() -> str: function resolve_date_range (line 119) | async def resolve_date_range( function get_latest_news (line 190) | async def get_latest_news( function get_trending_topics (line 220) | async def get_trending_topics( function get_latest_rss (line 255) | async def get_latest_rss( function search_rss (line 288) | async def search_rss( function get_rss_feeds_status (line 328) | async def get_rss_feeds_status() -> str: function get_news_by_date (line 351) | async def get_news_by_date( function analyze_topic_trend (line 388) | async def analyze_topic_trend( function analyze_data_insights (line 440) | async def analyze_data_insights( function analyze_sentiment (line 484) | async def analyze_sentiment( function find_related_news (line 525) | async def find_related_news( function generate_summary_report (line 565) | async def generate_summary_report( function aggregate_news (line 592) | async def aggregate_news( function compare_periods (line 631) | async def compare_periods( function search_news (line 693) | async def search_news( function get_current_config (line 753) | async def get_current_config( function get_system_status (line 776) | async def get_system_status() -> str: function check_version (line 791) | async def check_version( function trigger_crawl (line 815) | async def trigger_crawl( function sync_from_remote (line 846) | async def sync_from_remote( function get_storage_status (line 887) | async def get_storage_status() -> str: function list_available_dates (line 902) | async def list_available_dates( function read_article (line 931) | async def read_article( function read_articles_batch (line 971) | async def read_articles_batch( function get_channel_format_guide (line 1013) | async def get_channel_format_guide(channel: Optional[str] = None) -> str: function get_notification_channels (line 1053) | async def get_notification_channels() -> str: function send_notification (line 1072) | async def send_notification( function run_server (line 1117) | def run_server( FILE: mcp_server/services/cache_service.py function make_cache_key (line 14) | def make_cache_key(namespace: str, **params) -> str: class CacheService (line 60) | class CacheService: method __init__ (line 63) | def __init__(self): method get (line 69) | def get(self, key: str, ttl: int = 900) -> Optional[Any]: method set (line 91) | def set(self, key: str, value: Any) -> None: method delete (line 103) | def delete(self, key: str) -> bool: method clear (line 120) | def clear(self) -> None: method cleanup_expired (line 126) | def cleanup_expired(self, ttl: int = 900) -> int: method get_stats (line 149) | def get_stats(self) -> dict: function get_cache (line 174) | def get_cache() -> CacheService: FILE: mcp_server/services/data_service.py class DataService (line 17) | class DataService: method __init__ (line 36) | def __init__(self, project_root: str = None): method get_latest_news (line 46) | def get_latest_news( method get_news_by_date (line 120) | def get_news_by_date( method search_news_by_keyword (line 200) | def search_news_by_keyword( method _extract_words_from_title (line 301) | def _extract_words_from_title(self, title: str, min_length: int = 2) -... method get_trending_topics (line 330) | def get_trending_topics( method _get_mode_description (line 448) | def _get_mode_description(self, mode: str, extract_mode: str = "keywor... method get_current_config (line 462) | def get_current_config(self, section: str = "all") -> Dict: method get_available_date_range (line 558) | def get_available_date_range(self, db_type: str = "news") -> Tuple[Opt... method get_system_status (line 575) | def get_system_status(self) -> Dict: method get_latest_rss (line 624) | def get_latest_rss( method search_rss (line 715) | def search_rss( method get_rss_feeds_status (line 798) | def get_rss_feeds_status(self) -> Dict: FILE: mcp_server/services/parser_service.py class ParserService (line 20) | class ParserService: method __init__ (line 23) | def __init__(self, project_root: str = None): method clean_title (line 43) | def clean_title(title: str) -> str: method get_date_folder_name (line 49) | def get_date_folder_name(self, date: datetime = None) -> str: method _get_db_path (line 63) | def _get_db_path(self, date: datetime = None, db_type: str = "news") -... method _read_from_sqlite (line 82) | def _read_from_sqlite( method _read_news_from_sqlite (line 124) | def _read_news_from_sqlite( method _read_rss_from_sqlite (line 225) | def _read_rss_from_sqlite( method read_all_titles_for_date (line 307) | def read_all_titles_for_date( method parse_yaml_config (line 348) | def parse_yaml_config(self, config_path: str = None) -> dict: method parse_frequency_words (line 376) | def parse_frequency_words(self, words_file: str = None) -> List[Dict]: method get_available_dates (line 425) | def get_available_dates(self, db_type: str = "news") -> List[str]: method get_available_date_range (line 447) | def get_available_date_range(self, db_type: str = "news") -> Tuple[Opt... FILE: mcp_server/tools/analytics.py function _get_weight_config (line 42) | def _get_weight_config() -> Dict: function calculate_news_weight (line 82) | def calculate_news_weight(news_data: Dict, rank_threshold: int = 5) -> f... class AnalyticsTools (line 99) | class AnalyticsTools: method __init__ (line 102) | def __init__(self, project_root: str = None): method analyze_data_insights_unified (line 111) | def analyze_data_insights_unified( method analyze_topic_trend_unified (line 178) | def analyze_topic_trend_unified( method get_topic_trend_analysis (line 266) | def get_topic_trend_analysis( method compare_platforms (line 425) | def compare_platforms( method analyze_keyword_cooccurrence (line 549) | def analyze_keyword_cooccurrence( method analyze_sentiment (line 657) | def analyze_sentiment( method _create_sentiment_analysis_prompt (line 845) | def _create_sentiment_analysis_prompt( method find_similar_news (line 937) | def find_similar_news( method search_by_entity (line 1052) | def search_by_entity( method generate_summary_report (line 1183) | def generate_summary_report( method get_platform_activity_stats (line 1363) | def get_platform_activity_stats( method analyze_topic_lifecycle (line 1490) | def analyze_topic_lifecycle( method detect_viral_topics (line 1648) | def detect_viral_topics( method predict_trending_topics (line 1787) | def predict_trending_topics( method _extract_keywords (line 1956) | def _extract_keywords(self, title: str, min_length: int = 2) -> List[s... method _calculate_similarity (line 1984) | def _calculate_similarity(self, text1: str, text2: str) -> float: method _find_unique_topics (line 1998) | def _find_unique_topics(self, platform_stats: Dict) -> Dict[str, List[... method aggregate_news (line 2033) | def aggregate_news( method _aggregate_similar_news (line 2174) | def _aggregate_similar_news( method compare_periods (line 2314) | def compare_periods( method _parse_period (line 2406) | def _parse_period(self, period: Union[Dict[str, str], str]) -> Optiona... method _collect_period_data (line 2439) | def _collect_period_data( method _compare_overview (line 2498) | def _compare_overview( method _compare_topic_shift (line 2541) | def _compare_topic_shift( method _compare_platform_activity (line 2594) | def _compare_platform_activity( FILE: mcp_server/tools/article_reader.py class ArticleReaderTools (line 24) | class ArticleReaderTools: method __init__ (line 27) | def __init__(self, project_root: str = None, jina_api_key: str = None): method _build_headers (line 39) | def _build_headers(self) -> Dict[str, str]: method _throttle (line 50) | def _throttle(self): method read_article (line 58) | def read_article( method read_articles_batch (line 139) | def read_articles_batch( FILE: mcp_server/tools/config_mgmt.py class ErrorInfo (line 14) | class ErrorInfo(TypedDict, total=False): class ConfigResult (line 21) | class ConfigResult(TypedDict): class ConfigManagementTools (line 29) | class ConfigManagementTools: method __init__ (line 32) | def __init__(self, project_root: str = None): method get_current_config (line 41) | def get_current_config(self, section: Optional[str] = None) -> ConfigR... FILE: mcp_server/tools/data_query.py class DataQueryTools (line 23) | class DataQueryTools: method __init__ (line 26) | def __init__(self, project_root: str = None): method get_latest_news (line 35) | def get_latest_news( method search_news_by_keyword (line 95) | def search_news_by_keyword( method get_trending_topics (line 159) | def get_trending_topics( method get_news_by_date (line 232) | def get_news_by_date( method get_latest_rss (line 324) | def get_latest_rss( method search_rss (line 379) | def search_rss( method get_rss_feeds_status (line 442) | def get_rss_feeds_status(self) -> Dict: FILE: mcp_server/tools/notification.py function _split_text_into_batches (line 97) | def _split_text_into_batches(text: str, max_bytes: int) -> List[str]: function _format_for_channel (line 163) | def _format_for_channel(message: str, channel_id: str) -> str: function _prepare_batches (line 195) | def _prepare_batches(message: str, channel_id: str, batch_sizes: Dict = ... function _adapt_markdown_for_feishu (line 483) | def _adapt_markdown_for_feishu(text: str) -> str: function _adapt_markdown_for_dingtalk (line 498) | def _adapt_markdown_for_dingtalk(text: str) -> str: function _adapt_markdown_for_wework (line 516) | def _adapt_markdown_for_wework(text: str) -> str: function _adapt_markdown_for_ntfy (line 535) | def _adapt_markdown_for_ntfy(text: str) -> str: function _adapt_markdown_for_bark (line 548) | def _adapt_markdown_for_bark(text: str) -> str: function _markdown_to_telegram_html (line 571) | def _markdown_to_telegram_html(text: str) -> str: function _convert_markdown_to_slack (line 651) | def _convert_markdown_to_slack(text: str) -> str: function _markdown_to_simple_html (line 677) | def _markdown_to_simple_html(text: str) -> str: function _send_feishu (line 724) | def _send_feishu(webhook_url: str, content: str, title: str) -> Dict: function _send_dingtalk (line 747) | def _send_dingtalk(webhook_url: str, content: str, title: str) -> Dict: function _send_wework (line 762) | def _send_wework(webhook_url: str, content: str, title: str, msg_type: s... function _send_telegram (line 778) | def _send_telegram(bot_token: str, chat_id: str, content: str, title: st... function _send_email (line 796) | def _send_email( function _send_ntfy (line 856) | def _send_ntfy(server_url: str, topic: str, content: str, title: str, to... function _send_bark (line 895) | def _send_bark(bark_url: str, content: str, title: str) -> Dict: function _send_slack (line 921) | def _send_slack(webhook_url: str, content: str, title: str) -> Dict: function _send_generic_webhook (line 933) | def _send_generic_webhook( class NotificationTools (line 963) | class NotificationTools: method __init__ (line 966) | def __init__(self, project_root: str = None): method _load_merged_config (line 973) | def _load_merged_config(self) -> Dict[str, Any]: method _detect_config_source (line 991) | def _detect_config_source(self, env_key: str, yaml_value: str) -> str: method get_channel_format_guide (line 1000) | def get_channel_format_guide(self, channel: Optional[str] = None) -> D... method get_notification_channels (line 1037) | def get_notification_channels(self) -> Dict: method send_notification (line 1108) | def send_notification( method _dispatch_to_channel (line 1205) | def _dispatch_to_channel( method _get_batch_sizes (line 1280) | def _get_batch_sizes(self) -> Dict: method _get_batch_interval (line 1303) | def _get_batch_interval(self) -> float: method _send_batched_multi_account (line 1315) | def _send_batched_multi_account( method _send_batched_telegram (line 1343) | def _send_batched_telegram( method _send_batched_ntfy (line 1374) | def _send_batched_ntfy( FILE: mcp_server/tools/search_tools.py class SearchTools (line 18) | class SearchTools: method __init__ (line 21) | def __init__(self, project_root: str = None): method search_news_unified (line 30) | def search_news_unified( method _search_by_keyword_mode (line 255) | def _search_by_keyword_mode( method _search_by_fuzzy_mode (line 304) | def _search_by_fuzzy_mode( method _search_by_entity_mode (line 356) | def _search_by_entity_mode( method _calculate_similarity (line 404) | def _calculate_similarity(self, text1: str, text2: str) -> float: method _fuzzy_match (line 418) | def _fuzzy_match(self, query: str, text: str, threshold: float = 0.3) ... method _extract_keywords (line 455) | def _extract_keywords(self, text: str, min_length: int = 2) -> List[str]: method _calculate_keyword_overlap (line 478) | def _calculate_keyword_overlap(self, keywords1: List[str], keywords2: ... method _jaccard_similarity (line 504) | def _jaccard_similarity(self, list1: List[str], list2: List[str]) -> f... method search_related_news_history (line 529) | def search_related_news_history( method find_related_news_unified (line 739) | def find_related_news_unified( method _search_rss_by_keyword (line 897) | def _search_rss_by_keyword( FILE: mcp_server/tools/storage_sync.py class StorageSyncTools (line 19) | class StorageSyncTools: method __init__ (line 22) | def __init__(self, project_root: str = None): method _load_config (line 38) | def _load_config(self) -> dict: method _get_storage_config (line 49) | def _get_storage_config(self) -> dict: method _get_remote_config (line 54) | def _get_remote_config(self) -> dict: method _has_remote_config (line 69) | def _has_remote_config(self) -> bool: method _get_remote_backend (line 79) | def _get_remote_backend(self): method _get_local_data_dir (line 110) | def _get_local_data_dir(self) -> Path: method _parse_date_folder_name (line 117) | def _parse_date_folder_name(self, folder_name: str) -> Optional[dateti... method _get_local_dates (line 151) | def _get_local_dates(self, db_type: str = "news") -> List[str]: method _get_all_local_dates (line 183) | def _get_all_local_dates(self) -> Dict[str, List[str]]: method _calculate_dir_size (line 204) | def _calculate_dir_size(self, path: Path) -> int: method sync_from_remote (line 213) | def sync_from_remote(self, days: int = 7) -> Dict: method get_storage_status (line 333) | def get_storage_status(self) -> Dict: method list_available_dates (line 435) | def list_available_dates(self, source: str = "both") -> Dict: FILE: mcp_server/tools/system.py class SystemManagementTools (line 15) | class SystemManagementTools: method __init__ (line 18) | def __init__(self, project_root: str = None): method get_system_status (line 33) | def get_system_status(self) -> Dict: method trigger_crawl (line 71) | def trigger_crawl(self, platforms: Optional[List[str]] = None, save_to... method _generate_simple_html (line 293) | def _generate_simple_html(self, results: Dict, id_to_name: Dict, faile... method _html_escape (line 371) | def _html_escape(self, text: str) -> str: method check_version (line 383) | def check_version(self, proxy_url: Optional[str] = None) -> Dict: FILE: mcp_server/utils/date_parser.py class DateParser (line 14) | class DateParser: method parse_date_query (line 92) | def parse_date_query(date_query: str) -> datetime: method _get_date_by_weekday (line 251) | def _get_date_by_weekday(target_weekday: int, is_last_week: bool) -> d... method format_date_folder (line 278) | def format_date_folder(date: datetime) -> str: method validate_date_not_future (line 295) | def validate_date_not_future(date: datetime) -> None: method validate_date_not_too_old (line 312) | def validate_date_not_too_old(date: datetime, max_days: int = 365) -> ... method resolve_date_range_expression (line 331) | def resolve_date_range_expression(expression: str) -> Dict: method _calculate_date_range (line 426) | def _calculate_date_range( method get_supported_expressions (line 493) | def get_supported_expressions() -> Dict[str, list]: FILE: mcp_server/utils/errors.py function _load_supported_platforms (line 15) | def _load_supported_platforms() -> List[str]: class MCPError (line 28) | class MCPError(Exception): method __init__ (line 31) | def __init__(self, message: str, code: str = "MCP_ERROR", suggestion: ... method to_dict (line 37) | def to_dict(self) -> dict: class DataNotFoundError (line 48) | class DataNotFoundError(MCPError): method __init__ (line 51) | def __init__(self, message: str, suggestion: Optional[str] = None): class InvalidParameterError (line 59) | class InvalidParameterError(MCPError): method __init__ (line 62) | def __init__(self, message: str, suggestion: Optional[str] = None): class ConfigurationError (line 70) | class ConfigurationError(MCPError): method __init__ (line 73) | def __init__(self, message: str, suggestion: Optional[str] = None): class PlatformNotSupportedError (line 81) | class PlatformNotSupportedError(MCPError): method __init__ (line 84) | def __init__(self, platform: str): class CrawlTaskError (line 94) | class CrawlTaskError(MCPError): method __init__ (line 97) | def __init__(self, message: str, suggestion: Optional[str] = None): class FileParseError (line 105) | class FileParseError(MCPError): method __init__ (line 108) | def __init__(self, file_path: str, reason: str): FILE: mcp_server/utils/validators.py function _parse_string_to_list (line 21) | def _parse_string_to_list(value: str) -> List[str]: function _parse_string_to_int (line 73) | def _parse_string_to_int(value: str, param_name: str = "参数") -> int: function _parse_string_to_float (line 105) | def _parse_string_to_float(value: str, param_name: str = "参数") -> float: function _parse_string_to_bool (line 130) | def _parse_string_to_bool(value: str) -> bool: function get_supported_platforms (line 157) | def get_supported_platforms() -> List[str]: function validate_platforms (line 196) | def validate_platforms(platforms: Optional[Union[List[str], str]]) -> Li... function validate_limit (line 257) | def validate_limit(limit: Optional[Union[int, str]], default: int = 20, ... function validate_date (line 294) | def validate_date(date_str: str) -> datetime: function normalize_date_range (line 316) | def normalize_date_range(date_range: Optional[Union[dict, str]]) -> Opti... function validate_date_range (line 363) | def validate_date_range(date_range: Optional[Union[dict, str]]) -> Optio... function validate_keyword (line 482) | def validate_keyword(keyword: str) -> str: function validate_top_n (line 515) | def validate_top_n(top_n: Optional[Union[int, str]], default: int = 10) ... function validate_mode (line 532) | def validate_mode(mode: Optional[str], valid_modes: List[str], default: ... function validate_config_section (line 562) | def validate_config_section(section: Optional[str]) -> str: function validate_threshold (line 579) | def validate_threshold( function validate_date_query (line 628) | def validate_date_query( FILE: trendradar/__main__.py function _parse_version (line 33) | def _parse_version(version_str: str) -> Tuple[int, int, int]: function _compare_version (line 44) | def _compare_version(local: str, remote: str) -> str: function _fetch_remote_version (line 57) | def _fetch_remote_version(version_url: str, proxy_url: Optional[str] = N... function _parse_config_versions (line 78) | def _parse_config_versions(content: str) -> Dict[str, str]: function check_all_versions (line 95) | def check_all_versions( class NewsAnalyzer (line 192) | class NewsAnalyzer: method __init__ (line 217) | def __init__(self, config: Optional[Dict] = None): method _init_storage_manager (line 246) | def _init_storage_manager(self) -> None: method _detect_docker_environment (line 261) | def _detect_docker_environment(self) -> bool: method _should_open_browser (line 274) | def _should_open_browser(self) -> bool: method _setup_proxy (line 278) | def _setup_proxy(self) -> None: method _set_update_info_from_config (line 288) | def _set_update_info_from_config(self) -> None: method _get_mode_strategy (line 306) | def _get_mode_strategy(self) -> Dict: method _has_notification_configured (line 310) | def _has_notification_configured(self) -> bool: method _has_valid_content (line 331) | def _has_valid_content( method _prepare_ai_analysis_data (line 351) | def _prepare_ai_analysis_data( method _run_ai_analysis (line 457) | def _run_ai_analysis( method _load_analysis_data (line 585) | def _load_analysis_data( method _prepare_current_title_info (line 624) | def _prepare_current_title_info(self, results: Dict, time_info: str) -... method _prepare_standalone_data (line 644) | def _prepare_standalone_data( method _run_analysis_pipeline (line 794) | def _run_analysis_pipeline( method _send_notification_if_needed (line 890) | def _send_notification_if_needed( method _initialize_and_check_config (line 1012) | def _initialize_and_check_config(self) -> None: method _crawl_data (line 1033) | def _crawl_data(self) -> Tuple[Dict, Dict, List]: method _crawl_rss_data (line 1070) | def _crawl_rss_data(self) -> Tuple[Optional[List[Dict]], Optional[List... method _process_rss_data_by_mode (line 1169) | def _process_rss_data_by_mode(self, rss_data) -> Tuple[Optional[List[D... method _convert_rss_items_to_list (line 1341) | def _convert_rss_items_to_list(self, items_dict: Dict, id_to_name: Dic... method _filter_rss_by_keywords (line 1413) | def _filter_rss_by_keywords(self, rss_items: List[Dict]) -> List[Dict]: method _generate_rss_html_report (line 1437) | def _generate_rss_html_report(self, rss_items: list, feeds_info: dict)... method _execute_mode_strategy (line 1467) | def _execute_mode_strategy( method run (line 1687) | def run(self) -> None: function _record_doctor_result (line 1716) | def _record_doctor_result(results: List[Tuple[str, str, str]], status: s... function _save_doctor_report (line 1728) | def _save_doctor_report( function _run_doctor (line 1765) | def _run_doctor(config_path: Optional[str] = None) -> bool: function _build_test_report_data (line 1974) | def _build_test_report_data(ctx: AppContext) -> Dict: function _create_test_html_file (line 2007) | def _create_test_html_file(ctx: AppContext) -> Optional[str]: function _run_test_notification (line 2030) | def _run_test_notification(config: Dict) -> bool: function main (line 2119) | def main(): function _handle_status_commands (line 2215) | def _handle_status_commands(config: Dict) -> None: FILE: trendradar/ai/analyzer.py class AIAnalysisResult (line 18) | class AIAnalysisResult: class AIAnalyzer (line 42) | class AIAnalyzer: method __init__ (line 45) | def __init__( method _load_prompt_template (line 86) | def _load_prompt_template(self, prompt_file: str) -> tuple: method analyze (line 117) | def analyze( method _prepare_news_content (line 262) | def _prepare_news_content( method _call_ai (line 381) | def _call_ai(self, user_prompt: str) -> str: method _retry_fix_json (line 390) | def _retry_fix_json(self, original_response: str, error_msg: str) -> O... method _format_time_range (line 433) | def _format_time_range(self, first_time: str, last_time: str) -> str: method _format_rank_timeline (line 460) | def _format_rank_timeline(self, rank_timeline: List[Dict]) -> str: method _prepare_standalone_content (line 478) | def _prepare_standalone_content(self, standalone_data: Dict) -> str: method _parse_response (line 560) | def _parse_response(self, response: str) -> AIAnalysisResult: FILE: trendradar/ai/client.py class AIClient (line 15) | class AIClient: method __init__ (line 18) | def __init__(self, config: Dict[str, Any]): method chat (line 42) | def chat( method validate_config (line 104) | def validate_config(self) -> tuple[bool, str]: FILE: trendradar/ai/filter.py class AIFilterResult (line 20) | class AIFilterResult: class AIFilter (line 35) | class AIFilter: method __init__ (line 38) | def __init__( method _load_prompt (line 62) | def _load_prompt(self, filename: str) -> tuple: method compute_interests_hash (line 89) | def compute_interests_hash(self, interests_content: str, filename: str... method load_interests_content (line 101) | def load_interests_content(self, interests_file: Optional[str] = None)... method extract_tags (line 142) | def extract_tags(self, interests_content: str) -> List[Dict]: method update_tags (line 204) | def update_tags(self, old_tags: List[Dict], interests_content: str) ->... method _parse_update_tags_response (line 269) | def _parse_update_tags_response(self, response: str) -> Optional[Dict]: method _parse_tags_response (line 313) | def _parse_tags_response(self, response: str) -> List[Dict]: method classify_batch (line 333) | def classify_batch( method _parse_classify_response (line 408) | def _parse_classify_response( method _extract_json (line 544) | def _extract_json(self, response: str) -> Optional[str]: method _print_formatted_json (line 565) | def _print_formatted_json(self, response: str) -> None: FILE: trendradar/ai/formatter.py function _escape_html (line 13) | def _escape_html(text: str) -> str: function _format_list_content (line 18) | def _format_list_content(text: str) -> str: function _format_standalone_summaries (line 68) | def _format_standalone_summaries(summaries: dict) -> str: function render_ai_analysis_markdown (line 79) | def render_ai_analysis_markdown(result: AIAnalysisResult) -> str: function render_ai_analysis_feishu (line 115) | def render_ai_analysis_feishu(result: AIAnalysisResult) -> str: function render_ai_analysis_dingtalk (line 151) | def render_ai_analysis_dingtalk(result: AIAnalysisResult) -> str: function render_ai_analysis_html (line 193) | def render_ai_analysis_html(result: AIAnalysisResult) -> str: function render_ai_analysis_plain (line 279) | def render_ai_analysis_plain(result: AIAnalysisResult) -> str: function get_ai_analysis_renderer (line 311) | def get_ai_analysis_renderer(channel: str): function render_ai_analysis_html_rich (line 326) | def render_ai_analysis_html_rich(result: AIAnalysisResult) -> str: FILE: trendradar/ai/translator.py class TranslationResult (line 17) | class TranslationResult: class BatchTranslationResult (line 26) | class BatchTranslationResult: class AITranslator (line 37) | class AITranslator: method __init__ (line 40) | def __init__(self, translation_config: Dict[str, Any], ai_config: Dict... method _load_prompt_template (line 64) | def _load_prompt_template(self, prompt_file: str) -> tuple: method translate (line 93) | def translate(self, text: str) -> TranslationResult: method translate_batch (line 138) | def translate_batch(self, texts: List[str]) -> BatchTranslationResult: method _format_batch_content (line 232) | def _format_batch_content(self, texts: List[str]) -> str: method _parse_batch_response (line 239) | def _parse_batch_response(self, response: str, expected_count: int) ->... method _call_ai (line 303) | def _call_ai(self, user_prompt: str) -> str: FILE: trendradar/context.py class AppContext (line 46) | class AppContext: method __init__ (line 68) | def __init__(self, config: Dict[str, Any]): method timezone (line 82) | def timezone(self) -> str: method rank_threshold (line 87) | def rank_threshold(self) -> int: method weight_config (line 92) | def weight_config(self) -> Dict: method platforms (line 97) | def platforms(self) -> List[Dict]: method platform_ids (line 102) | def platform_ids(self) -> List[str]: method rss_config (line 107) | def rss_config(self) -> Dict: method rss_enabled (line 112) | def rss_enabled(self) -> bool: method rss_feeds (line 117) | def rss_feeds(self) -> List[Dict]: method display_mode (line 122) | def display_mode(self) -> str: method show_new_section (line 127) | def show_new_section(self) -> bool: method region_order (line 132) | def region_order(self) -> List[str]: method filter_method (line 138) | def filter_method(self) -> str: method ai_priority_sort_enabled (line 143) | def ai_priority_sort_enabled(self) -> bool: method ai_filter_config (line 148) | def ai_filter_config(self) -> Dict: method ai_filter_enabled (line 153) | def ai_filter_enabled(self) -> bool: method get_time (line 159) | def get_time(self) -> datetime: method format_date (line 163) | def format_date(self) -> str: method format_time (line 167) | def format_time(self) -> str: method get_time_display (line 171) | def get_time_display(self) -> str: method convert_time_display (line 176) | def convert_time_display(time_str: str) -> str: method get_storage_manager (line 182) | def get_storage_manager(self): method get_output_path (line 210) | def get_output_path(self, subfolder: str, filename: str) -> str: method read_today_titles (line 218) | def read_today_titles( method detect_new_titles (line 224) | def detect_new_titles( method is_first_crawl (line 230) | def is_first_crawl(self) -> bool: method load_frequency_words (line 236) | def load_frequency_words( method matches_word_groups (line 242) | def matches_word_groups( method count_frequency (line 254) | def count_frequency( method prepare_report (line 287) | def prepare_report( method generate_html (line 309) | def generate_html( method render_html (line 342) | def render_html( method render_feishu (line 371) | def render_feishu( method render_dingtalk (line 388) | def render_dingtalk( method split_content (line 404) | def split_content( method create_notification_dispatcher (line 464) | def create_notification_dispatcher(self) -> NotificationDispatcher: method create_scheduler (line 480) | def create_scheduler(self) -> Scheduler: method _with_ordered_priorities (line 502) | def _with_ordered_priorities(tags: List[Dict], start_priority: int = 1... method run_ai_filter (line 519) | def run_ai_filter(self, interests_file: Optional[str] = None) -> Optio... method _build_filter_result (line 840) | def _build_filter_result( method convert_ai_filter_to_report_data (line 921) | def convert_ai_filter_to_report_data( method cleanup (line 1116) | def cleanup(self): FILE: trendradar/core/analyzer.py function calculate_news_weight (line 17) | def calculate_news_weight( function format_time_display (line 64) | def format_time_display( function count_word_frequency (line 91) | def count_word_frequency( function count_rss_frequency (line 492) | def count_rss_frequency( function convert_keyword_stats_to_platform_stats (line 710) | def convert_keyword_stats_to_platform_stats( FILE: trendradar/core/config.py function parse_multi_account_config (line 11) | def parse_multi_account_config(config_value: str, separator: str = ";") ... function validate_paired_configs (line 40) | def validate_paired_configs( function limit_accounts (line 97) | def limit_accounts( function get_account_at_index (line 128) | def get_account_at_index(accounts: List[str], index: int, default: str =... FILE: trendradar/core/data.py function read_all_today_titles_from_storage (line 15) | def read_all_today_titles_from_storage( function read_all_today_titles (line 83) | def read_all_today_titles( function detect_latest_new_titles_from_storage (line 113) | def detect_latest_new_titles_from_storage( function detect_latest_new_titles (line 198) | def detect_latest_new_titles( FILE: trendradar/core/frequency.py function _parse_word (line 22) | def _parse_word(word: str) -> Dict: function _word_matches (line 73) | def _word_matches(word_config: Union[str, Dict], title_lower: str) -> bool: function load_frequency_words (line 96) | def load_frequency_words( function matches_word_groups (line 246) | def matches_word_groups( FILE: trendradar/core/loader.py function _get_env_bool (line 18) | def _get_env_bool(key: str) -> Optional[bool]: function _get_env_int (line 26) | def _get_env_int(key: str, default: int = 0) -> int: function _get_env_int_or_none (line 37) | def _get_env_int_or_none(key: str) -> Optional[int]: function _get_env_str (line 48) | def _get_env_str(key: str, default: str = "") -> str: function _load_app_config (line 53) | def _load_app_config(config_data: Dict) -> Dict: function _load_crawler_config (line 66) | def _load_crawler_config(config_data: Dict) -> Dict: function _load_report_config (line 79) | def _load_report_config(config_data: Dict) -> Dict: function _load_notification_config (line 96) | def _load_notification_config(config_data: Dict) -> Dict: function _load_schedule_config (line 115) | def _load_schedule_config(config_data: Dict) -> Dict: function _load_timeline_data (line 136) | def _load_timeline_data(config_dir: str = "config") -> Dict: function _load_weight_config (line 173) | def _load_weight_config(config_data: Dict) -> Dict: function _load_rss_config (line 184) | def _load_rss_config(config_data: Dict) -> Dict: function _load_display_config (line 223) | def _load_display_config(config_data: Dict) -> Dict: function _load_ai_config (line 261) | def _load_ai_config(config_data: Dict) -> Dict: function _load_ai_analysis_config (line 285) | def _load_ai_analysis_config(config_data: Dict) -> Dict: function _load_ai_translation_config (line 303) | def _load_ai_translation_config(config_data: Dict) -> Dict: function _load_ai_filter_config (line 323) | def _load_ai_filter_config(config_data: Dict) -> Dict: function _load_filter_config (line 339) | def _load_filter_config(config_data: Dict) -> Dict: function _load_storage_config (line 362) | def _load_storage_config(config_data: Dict) -> Dict: function _load_webhook_config (line 400) | def _load_webhook_config(config_data: Dict) -> Dict: function _print_notification_sources (line 447) | def _print_notification_sources(config: Dict) -> None: function load_config (line 529) | def load_config(config_path: Optional[str] = None) -> Dict[str, Any]: FILE: trendradar/core/scheduler.py class ResolvedSchedule (line 18) | class ResolvedSchedule: class Scheduler (line 35) | class Scheduler: method __init__ (line 48) | def __init__( method _build_timeline (line 77) | def _build_timeline( method resolve (line 102) | def resolve(self) -> ResolvedSchedule: method _find_active_period (line 190) | def _find_active_period( method _in_range (line 236) | def _in_range(now_hhmm: str, start: str, end: str) -> bool: method _merge_with_default (line 255) | def _merge_with_default(self, period_key: Optional[str]) -> Dict[str, ... method _resolve_ai_mode (line 277) | def _resolve_ai_mode(cfg: Dict[str, Any]) -> str: method already_executed (line 284) | def already_executed(self, period_key: str, action: str, date_str: str... method record_execution (line 298) | def record_execution(self, period_key: str, action: str, date_str: str... method _validate_timeline (line 313) | def _validate_timeline(self, timeline: Dict[str, Any]) -> None: method _check_period_overlaps (line 363) | def _check_period_overlaps(self, timeline: Dict[str, Any]) -> None: method _ranges_overlap (line 398) | def _ranges_overlap(s1: str, e1: str, s2: str, e2: str) -> bool: method _validate_hhmm (line 425) | def _validate_hhmm(value: str, field_name: str) -> None: FILE: trendradar/crawler/fetcher.py class DataFetcher (line 20) | class DataFetcher: method __init__ (line 35) | def __init__( method fetch_data (line 50) | def fetch_data( method crawl_websites (line 117) | def crawl_websites( FILE: trendradar/crawler/rss/fetcher.py class RSSFeedConfig (line 21) | class RSSFeedConfig: class RSSFetcher (line 31) | class RSSFetcher: method __init__ (line 34) | def __init__( method _create_session (line 70) | def _create_session(self) -> requests.Session: method _filter_by_freshness (line 87) | def _filter_by_freshness( method fetch_feed (line 129) | def fetch_feed(self, feed: RSSFeedConfig) -> Tuple[List[RSSItem], Opti... method fetch_all (line 195) | def fetch_all(self) -> RSSData: method from_config (line 241) | def from_config(cls, config: Dict) -> "RSSFetcher": FILE: trendradar/crawler/rss/parser.py class ParsedRSSItem (line 25) | class ParsedRSSItem: class RSSParser (line 35) | class RSSParser: method __init__ (line 38) | def __init__(self, max_summary_length: int = 500): method parse (line 50) | def parse(self, content: str, feed_url: str = "") -> List[ParsedRSSItem]: method _is_json_feed (line 79) | def _is_json_feed(self, content: str) -> bool: method _parse_json_feed (line 96) | def _parse_json_feed(self, content: str, feed_url: str = "") -> List[P... method _parse_json_feed_item (line 126) | def _parse_json_feed_item(self, item_data: Dict[str, Any]) -> Optional... method _parse_iso_date (line 180) | def _parse_iso_date(self, date_str: str) -> Optional[str]: method parse_url (line 196) | def parse_url(self, url: str, timeout: int = 10) -> List[ParsedRSSItem]: method _parse_entry (line 216) | def _parse_entry(self, entry: Any) -> Optional[ParsedRSSItem]: method _clean_text (line 247) | def _clean_text(self, text: str) -> str: method _parse_date (line 263) | def _parse_date(self, entry: Any) -> Optional[str]: method _parse_summary (line 293) | def _parse_summary(self, entry: Any) -> Optional[str]: method _parse_author (line 314) | def _parse_author(self, entry: Any) -> Optional[str]: FILE: trendradar/notification/batch.py function get_batch_header (line 11) | def get_batch_header(format_type: str, batch_num: int, total_batches: in... function get_max_batch_header_size (line 34) | def get_max_batch_header_size(format_type: str) -> int: function truncate_to_bytes (line 50) | def truncate_to_bytes(text: str, max_bytes: int) -> str: function add_batch_headers (line 78) | def add_batch_headers( FILE: trendradar/notification/dispatcher.py class NotificationDispatcher (line 46) | class NotificationDispatcher: method __init__ (line 54) | def __init__( method _translate_content (line 76) | def _translate_content( method dispatch_all (line 216) | def dispatch_all( method _send_to_multi_accounts (line 323) | def _send_to_multi_accounts( method _send_feishu (line 357) | def _send_feishu( method _send_dingtalk (line 398) | def _send_dingtalk( method _send_wework (line 438) | def _send_wework( method _send_telegram (line 479) | def _send_telegram( method _send_ntfy (line 542) | def _send_ntfy( method _send_bark (line 604) | def _send_bark( method _send_slack (line 644) | def _send_slack( method _send_generic_webhook (line 684) | def _send_generic_webhook( method _send_email (line 746) | def _send_email( method dispatch_rss (line 769) | def dispatch_rss( method _send_rss_feishu (line 854) | def _send_rss_feishu( method _send_rss_dingtalk (line 913) | def _send_rss_dingtalk( method _send_rss_markdown (line 964) | def _send_rss_markdown( method _send_rss_wework (line 996) | def _send_rss_wework(self, content: str, proxy_url: Optional[str]) -> ... method _send_rss_telegram (line 1032) | def _send_rss_telegram(self, content: str, proxy_url: Optional[str]) -... method _send_rss_ntfy (line 1076) | def _send_rss_ntfy(self, content: str, proxy_url: Optional[str]) -> bool: method _send_rss_bark (line 1121) | def _send_rss_bark(self, content: str, proxy_url: Optional[str]) -> bool: method _send_rss_slack (line 1157) | def _send_rss_slack(self, content: str, proxy_url: Optional[str]) -> b... FILE: trendradar/notification/formatters.py function strip_markdown (line 11) | def strip_markdown(text: str) -> str: function convert_markdown_to_mrkdwn (line 72) | def convert_markdown_to_mrkdwn(content: str) -> str: FILE: trendradar/notification/renderer.py function render_feishu_content (line 18) | def render_feishu_content( function render_dingtalk_content (line 150) | def render_dingtalk_content( function render_rss_feishu_content (line 287) | def render_rss_feishu_content( function render_rss_dingtalk_content (line 360) | def render_rss_dingtalk_content( function render_rss_markdown_content (line 427) | def render_rss_markdown_content( function _render_rss_section_feishu (line 488) | def _render_rss_section_feishu(rss_items: list, separator: str = "---") ... function _render_rss_section_markdown (line 531) | def _render_rss_section_markdown(rss_items: list) -> str: FILE: trendradar/notification/senders.py function _render_ai_analysis (line 36) | def _render_ai_analysis(ai_analysis: Any, channel: str) -> str: function send_to_feishu (line 77) | def send_to_feishu( function send_to_dingtalk (line 209) | def send_to_dingtalk( function send_to_wework (line 337) | def send_to_wework( function send_to_telegram (line 476) | def send_to_telegram( function send_to_email (line 603) | def send_to_email( function send_to_ntfy (line 762) | def send_to_ntfy( function send_to_bark (line 965) | def send_to_bark( function send_to_slack (line 1139) | def send_to_slack( function send_to_generic_webhook (line 1257) | def send_to_generic_webhook( FILE: trendradar/notification/splitter.py function split_content_into_batches (line 28) | def split_content_into_batches( function _process_rss_stats_section (line 827) | def _process_rss_stats_section( function _process_rss_new_titles_section (line 1050) | def _process_rss_new_titles_section( function _format_rss_item_line (line 1236) | def _format_rss_item_line( function _process_standalone_section (line 1290) | def _process_standalone_section( function _format_standalone_platform_item (line 1503) | def _format_standalone_platform_item(item: Dict, index: int, format_type... function _format_standalone_rss_item (line 1608) | def _format_standalone_rss_item( FILE: trendradar/report/formatter.py function format_title_for_platform (line 13) | def format_title_for_platform( FILE: trendradar/report/generator.py function prepare_report_data (line 14) | def prepare_report_data( function generate_html_report (line 142) | def generate_html_report( FILE: trendradar/report/helpers.py function clean_title (line 12) | def clean_title(title: str) -> str: function html_escape (line 34) | def html_escape(text: str) -> str: function format_rank_display (line 62) | def format_rank_display(ranks: List[int], rank_threshold: int, format_ty... FILE: trendradar/report/html.py function render_html_content (line 16) | def render_html_content( FILE: trendradar/report/rss_html.py function render_rss_html_content (line 14) | def render_rss_html_content( FILE: trendradar/storage/ai_filter_schema.sql type ai_filter_tags (line 10) | CREATE TABLE IF NOT EXISTS ai_filter_tags ( type ai_filter_results (line 28) | CREATE TABLE IF NOT EXISTS ai_filter_results ( type ai_filter_analyzed_news (line 45) | CREATE TABLE IF NOT EXISTS ai_filter_analyzed_news ( type idx_ai_filter_tags_status (line 58) | CREATE INDEX IF NOT EXISTS idx_ai_filter_tags_status ON ai_filter_tags(s... type idx_ai_filter_tags_version (line 59) | CREATE INDEX IF NOT EXISTS idx_ai_filter_tags_version ON ai_filter_tags(... type idx_ai_filter_tags_file (line 60) | CREATE INDEX IF NOT EXISTS idx_ai_filter_tags_file ON ai_filter_tags(int... type idx_ai_filter_tags_priority (line 61) | CREATE INDEX IF NOT EXISTS idx_ai_filter_tags_priority ON ai_filter_tags... type idx_ai_filter_results_status (line 62) | CREATE INDEX IF NOT EXISTS idx_ai_filter_results_status ON ai_filter_res... type idx_ai_filter_results_news (line 63) | CREATE INDEX IF NOT EXISTS idx_ai_filter_results_news ON ai_filter_resul... type idx_ai_filter_results_tag (line 64) | CREATE INDEX IF NOT EXISTS idx_ai_filter_results_tag ON ai_filter_result... type idx_analyzed_news_lookup (line 65) | CREATE INDEX IF NOT EXISTS idx_analyzed_news_lookup ON ai_filter_analyze... type idx_analyzed_news_hash (line 66) | CREATE INDEX IF NOT EXISTS idx_analyzed_news_hash ON ai_filter_analyzed_... FILE: trendradar/storage/base.py class NewsItem (line 14) | class NewsItem: method to_dict (line 34) | def to_dict(self) -> Dict[str, Any]: method from_dict (line 52) | def from_dict(cls, data: Dict[str, Any]) -> "NewsItem": class RSSItem (line 71) | class RSSItem: method to_dict (line 88) | def to_dict(self) -> Dict[str, Any]: method from_dict (line 105) | def from_dict(cls, data: Dict[str, Any]) -> "RSSItem": class RSSData (line 123) | class RSSData: method to_dict (line 141) | def to_dict(self) -> Dict[str, Any]: method from_dict (line 156) | def from_dict(cls, data: Dict[str, Any]) -> "RSSData": method get_total_count (line 171) | def get_total_count(self) -> int: class NewsData (line 177) | class NewsData: method to_dict (line 195) | def to_dict(self) -> Dict[str, Any]: method from_dict (line 210) | def from_dict(cls, data: Dict[str, Any]) -> "NewsData": method get_total_count (line 225) | def get_total_count(self) -> int: method merge_with (line 229) | def merge_with(self, other: "NewsData") -> "NewsData": class StorageBackend (line 298) | class StorageBackend(ABC): method save_news_data (line 310) | def save_news_data(self, data: NewsData) -> bool: method get_today_all_data (line 323) | def get_today_all_data(self, date: Optional[str] = None) -> Optional[N... method get_latest_crawl_data (line 336) | def get_latest_crawl_data(self, date: Optional[str] = None) -> Optiona... method detect_new_titles (line 349) | def detect_new_titles(self, current_data: NewsData) -> Dict[str, Dict]: method save_txt_snapshot (line 362) | def save_txt_snapshot(self, data: NewsData) -> Optional[str]: method save_html_report (line 375) | def save_html_report(self, html_content: str, filename: str) -> Option... method is_first_crawl_today (line 389) | def is_first_crawl_today(self, date: Optional[str] = None) -> bool: method cleanup (line 402) | def cleanup(self) -> None: method cleanup_old_data (line 409) | def cleanup_old_data(self, retention_days: int) -> int: method backend_name (line 423) | def backend_name(self) -> str: method supports_txt (line 431) | def supports_txt(self) -> bool: method has_period_executed (line 439) | def has_period_executed(self, date_str: str, period_key: str, action: ... method record_period_execution (line 453) | def record_period_execution(self, date_str: str, period_key: str, acti... method begin_batch (line 469) | def begin_batch(self) -> None: method end_batch (line 473) | def end_batch(self) -> None: method get_active_ai_filter_tags (line 477) | def get_active_ai_filter_tags(self, date: Optional[str] = None, intere... method get_latest_prompt_hash (line 480) | def get_latest_prompt_hash(self, date: Optional[str] = None, interests... method get_latest_ai_filter_tag_version (line 483) | def get_latest_ai_filter_tag_version(self, date: Optional[str] = None)... method deprecate_all_ai_filter_tags (line 486) | def deprecate_all_ai_filter_tags(self, date: Optional[str] = None, int... method save_ai_filter_tags (line 489) | def save_ai_filter_tags(self, tags: List[Dict], version: int, prompt_h... method save_ai_filter_results (line 492) | def save_ai_filter_results(self, results: List[Dict], date: Optional[s... method get_active_ai_filter_results (line 495) | def get_active_ai_filter_results(self, date: Optional[str] = None, int... method deprecate_specific_ai_filter_tags (line 498) | def deprecate_specific_ai_filter_tags(self, tag_ids: List[int], date: ... method update_ai_filter_tags_hash (line 501) | def update_ai_filter_tags_hash(self, interests_file: str, new_hash: st... method update_ai_filter_tag_descriptions (line 504) | def update_ai_filter_tag_descriptions(self, tag_updates: List[Dict], d... method update_ai_filter_tag_priorities (line 507) | def update_ai_filter_tag_priorities(self, tag_priorities: List[Dict], ... method save_analyzed_news (line 510) | def save_analyzed_news(self, news_ids: List[str], source_type: str, in... method get_analyzed_news_ids (line 513) | def get_analyzed_news_ids(self, source_type: str = "hotlist", date: Op... method clear_analyzed_news (line 516) | def clear_analyzed_news(self, date: Optional[str] = None, interests_fi... method clear_unmatched_analyzed_news (line 519) | def clear_unmatched_analyzed_news(self, date: Optional[str] = None, in... method get_all_news_ids (line 522) | def get_all_news_ids(self, date: Optional[str] = None) -> List[Dict]: method get_all_rss_ids (line 525) | def get_all_rss_ids(self, date: Optional[str] = None) -> List[Dict]: function convert_crawl_results_to_news_data (line 529) | def convert_crawl_results_to_news_data( FILE: trendradar/storage/local.py class LocalStorageBackend (line 26) | class LocalStorageBackend(SQLiteStorageMixin, StorageBackend): method __init__ (line 36) | def __init__( method backend_name (line 59) | def backend_name(self) -> str: method supports_txt (line 63) | def supports_txt(self) -> bool: method _get_configured_time (line 70) | def _get_configured_time(self) -> datetime: method _format_date_folder (line 74) | def _format_date_folder(self, date: Optional[str] = None) -> str: method _format_time_filename (line 78) | def _format_time_filename(self) -> str: method _get_db_path (line 82) | def _get_db_path(self, date: Optional[str] = None, db_type: str = "new... method _get_connection (line 102) | def _get_connection(self, date: Optional[str] = None, db_type: str = "... method save_news_data (line 127) | def save_news_data(self, data: NewsData) -> bool: method get_today_all_data (line 150) | def get_today_all_data(self, date: Optional[str] = None) -> Optional[N... method get_latest_crawl_data (line 157) | def get_latest_crawl_data(self, date: Optional[str] = None) -> Optiona... method detect_new_titles (line 164) | def detect_new_titles(self, current_data: NewsData) -> Dict[str, Dict]: method is_first_crawl_today (line 168) | def is_first_crawl_today(self, date: Optional[str] = None) -> bool: method get_crawl_times (line 175) | def get_crawl_times(self, date: Optional[str] = None) -> List[str]: method has_period_executed (line 186) | def has_period_executed(self, date_str: str, period_key: str, action: ... method record_period_execution (line 190) | def record_period_execution(self, date_str: str, period_key: str, acti... method save_rss_data (line 202) | def save_rss_data(self, data: RSSData) -> bool: method get_rss_data (line 215) | def get_rss_data(self, date: Optional[str] = None) -> Optional[RSSData]: method detect_new_rss_items (line 219) | def detect_new_rss_items(self, current_data: RSSData) -> Dict[str, Lis... method get_latest_rss_data (line 223) | def get_latest_rss_data(self, date: Optional[str] = None) -> Optional[... method get_active_ai_filter_tags (line 234) | def get_active_ai_filter_tags(self, date=None, interests_file="ai_inte... method get_latest_prompt_hash (line 237) | def get_latest_prompt_hash(self, date=None, interests_file="ai_interes... method get_latest_ai_filter_tag_version (line 240) | def get_latest_ai_filter_tag_version(self, date=None): method deprecate_all_ai_filter_tags (line 243) | def deprecate_all_ai_filter_tags(self, date=None, interests_file="ai_i... method save_ai_filter_tags (line 246) | def save_ai_filter_tags(self, tags, version, prompt_hash, date=None, i... method save_ai_filter_results (line 249) | def save_ai_filter_results(self, results, date=None): method get_active_ai_filter_results (line 252) | def get_active_ai_filter_results(self, date=None, interests_file="ai_i... method deprecate_specific_ai_filter_tags (line 255) | def deprecate_specific_ai_filter_tags(self, tag_ids, date=None): method update_ai_filter_tags_hash (line 258) | def update_ai_filter_tags_hash(self, interests_file, new_hash, date=No... method update_ai_filter_tag_descriptions (line 261) | def update_ai_filter_tag_descriptions(self, tag_updates, date=None, in... method update_ai_filter_tag_priorities (line 264) | def update_ai_filter_tag_priorities(self, tag_priorities, date=None, i... method save_analyzed_news (line 267) | def save_analyzed_news(self, news_ids, source_type, interests_file, pr... method get_analyzed_news_ids (line 270) | def get_analyzed_news_ids(self, source_type="hotlist", date=None, inte... method clear_analyzed_news (line 273) | def clear_analyzed_news(self, date=None, interests_file="ai_interests.... method clear_unmatched_analyzed_news (line 276) | def clear_unmatched_analyzed_news(self, date=None, interests_file="ai_... method get_all_news_ids (line 279) | def get_all_news_ids(self, date=None): method get_all_rss_ids (line 282) | def get_all_rss_ids(self, date=None): method save_txt_snapshot (line 289) | def save_txt_snapshot(self, data: NewsData) -> Optional[str]: method save_html_report (line 347) | def save_html_report(self, html_content: str, filename: str) -> Option... method cleanup (line 384) | def cleanup(self) -> None: method cleanup_old_data (line 395) | def cleanup_old_data(self, retention_days: int) -> int: method __del__ (line 492) | def __del__(self): FILE: trendradar/storage/manager.py class StorageManager (line 19) | class StorageManager: method __init__ (line 30) | def __init__( method is_github_actions (line 73) | def is_github_actions() -> bool: method is_docker (line 78) | def is_docker() -> bool: method _resolve_backend_type (line 94) | def _resolve_backend_type(self) -> str: method _has_remote_config (line 108) | def _has_remote_config(self) -> bool: method _create_remote_backend (line 127) | def _create_remote_backend(self) -> Optional[StorageBackend]: method get_backend (line 150) | def get_backend(self) -> StorageBackend: method pull_from_remote (line 176) | def pull_from_remote(self) -> int: method save_news_data (line 201) | def save_news_data(self, data: NewsData) -> bool: method save_rss_data (line 205) | def save_rss_data(self, data: RSSData) -> bool: method get_rss_data (line 209) | def get_rss_data(self, date: Optional[str] = None) -> Optional[RSSData]: method get_latest_rss_data (line 213) | def get_latest_rss_data(self, date: Optional[str] = None) -> Optional[... method detect_new_rss_items (line 217) | def detect_new_rss_items(self, current_data: RSSData) -> dict: method get_today_all_data (line 221) | def get_today_all_data(self, date: Optional[str] = None) -> Optional[N... method get_latest_crawl_data (line 225) | def get_latest_crawl_data(self, date: Optional[str] = None) -> Optiona... method detect_new_titles (line 229) | def detect_new_titles(self, current_data: NewsData) -> dict: method save_txt_snapshot (line 233) | def save_txt_snapshot(self, data: NewsData) -> Optional[str]: method save_html_report (line 237) | def save_html_report(self, html_content: str, filename: str) -> Option... method is_first_crawl_today (line 241) | def is_first_crawl_today(self, date: Optional[str] = None) -> bool: method cleanup (line 245) | def cleanup(self) -> None: method cleanup_old_data (line 252) | def cleanup_old_data(self) -> int: method backend_name (line 275) | def backend_name(self) -> str: method supports_txt (line 280) | def supports_txt(self) -> bool: method has_period_executed (line 284) | def has_period_executed(self, date_str: str, period_key: str, action: ... method record_period_execution (line 288) | def record_period_execution(self, date_str: str, period_key: str, acti... method begin_batch (line 294) | def begin_batch(self): method end_batch (line 298) | def end_batch(self): method get_active_ai_filter_tags (line 302) | def get_active_ai_filter_tags(self, date=None, interests_file="ai_inte... method get_latest_prompt_hash (line 306) | def get_latest_prompt_hash(self, date=None, interests_file="ai_interes... method get_latest_ai_filter_tag_version (line 310) | def get_latest_ai_filter_tag_version(self, date=None): method deprecate_all_ai_filter_tags (line 314) | def deprecate_all_ai_filter_tags(self, date=None, interests_file="ai_i... method save_ai_filter_tags (line 318) | def save_ai_filter_tags(self, tags, version, prompt_hash, date=None, i... method save_ai_filter_results (line 322) | def save_ai_filter_results(self, results, date=None): method get_active_ai_filter_results (line 326) | def get_active_ai_filter_results(self, date=None, interests_file="ai_i... method deprecate_specific_ai_filter_tags (line 330) | def deprecate_specific_ai_filter_tags(self, tag_ids, date=None): method update_ai_filter_tags_hash (line 334) | def update_ai_filter_tags_hash(self, interests_file, new_hash, date=No... method update_ai_filter_tag_descriptions (line 338) | def update_ai_filter_tag_descriptions(self, tag_updates, date=None, in... method update_ai_filter_tag_priorities (line 342) | def update_ai_filter_tag_priorities(self, tag_priorities, date=None, i... method save_analyzed_news (line 346) | def save_analyzed_news(self, news_ids, source_type, interests_file, pr... method get_analyzed_news_ids (line 350) | def get_analyzed_news_ids(self, source_type="hotlist", date=None, inte... method clear_analyzed_news (line 354) | def clear_analyzed_news(self, date=None, interests_file="ai_interests.... method clear_unmatched_analyzed_news (line 358) | def clear_unmatched_analyzed_news(self, date=None, interests_file="ai_... method get_all_news_ids (line 362) | def get_all_news_ids(self, date=None): method get_all_rss_ids (line 366) | def get_all_rss_ids(self, date=None): function get_storage_manager (line 372) | def get_storage_manager( FILE: trendradar/storage/remote.py class RemoteStorageBackend (line 41) | class RemoteStorageBackend(SQLiteStorageMixin, StorageBackend): method __init__ (line 54) | def __init__( method backend_name (line 129) | def backend_name(self) -> str: method supports_txt (line 133) | def supports_txt(self) -> bool: method _get_configured_time (line 140) | def _get_configured_time(self) -> datetime: method _format_date_folder (line 144) | def _format_date_folder(self, date: Optional[str] = None) -> str: method _format_time_filename (line 148) | def _format_time_filename(self) -> str: method _get_remote_db_key (line 152) | def _get_remote_db_key(self, date: Optional[str] = None, db_type: str ... method _get_local_db_path (line 166) | def _get_local_db_path(self, date: Optional[str] = None, db_type: str ... method _check_object_exists (line 182) | def _check_object_exists(self, r2_key: str) -> bool: method _download_sqlite (line 207) | def _download_sqlite(self, date: Optional[str] = None, db_type: str = ... method begin_batch (line 255) | def begin_batch(self): method end_batch (line 260) | def end_batch(self): method _upload_sqlite (line 267) | def _upload_sqlite(self, date: Optional[str] = None, db_type: str = "n... method _get_connection (line 323) | def _get_connection(self, date: Optional[str] = None, db_type: str = "... method save_news_data (line 356) | def save_news_data(self, data: NewsData) -> bool: method get_today_all_data (line 402) | def get_today_all_data(self, date: Optional[str] = None) -> Optional[N... method get_latest_crawl_data (line 406) | def get_latest_crawl_data(self, date: Optional[str] = None) -> Optiona... method detect_new_titles (line 410) | def detect_new_titles(self, current_data: NewsData) -> Dict[str, Dict]: method is_first_crawl_today (line 414) | def is_first_crawl_today(self, date: Optional[str] = None) -> bool: method has_period_executed (line 422) | def has_period_executed(self, date_str: str, period_key: str, action: ... method record_period_execution (line 426) | def record_period_execution(self, date_str: str, period_key: str, acti... method save_rss_data (line 448) | def save_rss_data(self, data: RSSData) -> bool: method get_rss_data (line 473) | def get_rss_data(self, date: Optional[str] = None) -> Optional[RSSData]: method detect_new_rss_items (line 477) | def detect_new_rss_items(self, current_data: RSSData) -> Dict[str, Lis... method get_latest_rss_data (line 481) | def get_latest_rss_data(self, date: Optional[str] = None) -> Optional[... method get_active_ai_filter_tags (line 489) | def get_active_ai_filter_tags(self, date=None, interests_file="ai_inte... method get_latest_prompt_hash (line 492) | def get_latest_prompt_hash(self, date=None, interests_file="ai_interes... method get_latest_ai_filter_tag_version (line 495) | def get_latest_ai_filter_tag_version(self, date=None): method deprecate_all_ai_filter_tags (line 498) | def deprecate_all_ai_filter_tags(self, date=None, interests_file="ai_i... method save_ai_filter_tags (line 504) | def save_ai_filter_tags(self, tags, version, prompt_hash, date=None, i... method save_ai_filter_results (line 510) | def save_ai_filter_results(self, results, date=None): method get_active_ai_filter_results (line 516) | def get_active_ai_filter_results(self, date=None, interests_file="ai_i... method deprecate_specific_ai_filter_tags (line 519) | def deprecate_specific_ai_filter_tags(self, tag_ids, date=None): method update_ai_filter_tags_hash (line 525) | def update_ai_filter_tags_hash(self, interests_file, new_hash, date=No... method update_ai_filter_tag_descriptions (line 531) | def update_ai_filter_tag_descriptions(self, tag_updates, date=None, in... method update_ai_filter_tag_priorities (line 537) | def update_ai_filter_tag_priorities(self, tag_priorities, date=None, i... method save_analyzed_news (line 543) | def save_analyzed_news(self, news_ids, source_type, interests_file, pr... method get_analyzed_news_ids (line 549) | def get_analyzed_news_ids(self, source_type="hotlist", date=None, inte... method clear_analyzed_news (line 552) | def clear_analyzed_news(self, date=None, interests_file="ai_interests.... method clear_unmatched_analyzed_news (line 558) | def clear_unmatched_analyzed_news(self, date=None, interests_file="ai_... method get_all_news_ids (line 564) | def get_all_news_ids(self, date=None): method get_all_rss_ids (line 567) | def get_all_rss_ids(self, date=None): method save_txt_snapshot (line 574) | def save_txt_snapshot(self, data: NewsData) -> Optional[str]: method save_html_report (line 620) | def save_html_report(self, html_content: str, filename: str) -> Option... method cleanup (line 646) | def cleanup(self) -> None: method cleanup_old_data (line 680) | def cleanup_old_data(self, retention_days: int) -> int: method __del__ (line 758) | def __del__(self): method pull_recent_days (line 773) | def pull_recent_days(self, days: int, local_data_dir: str = "output") ... method list_remote_dates (line 831) | def list_remote_dates(self) -> List[str]: FILE: trendradar/storage/rss_schema.sql type rss_feeds (line 8) | CREATE TABLE IF NOT EXISTS rss_feeds ( type rss_items (line 24) | CREATE TABLE IF NOT EXISTS rss_items ( type rss_crawl_records (line 44) | CREATE TABLE IF NOT EXISTS rss_crawl_records ( type rss_crawl_status (line 55) | CREATE TABLE IF NOT EXISTS rss_crawl_status ( type rss_push_records (line 70) | CREATE TABLE IF NOT EXISTS rss_push_records ( type idx_rss_feed (line 86) | CREATE INDEX IF NOT EXISTS idx_rss_feed ON rss_items(feed_id) type idx_rss_published (line 89) | CREATE INDEX IF NOT EXISTS idx_rss_published ON rss_items(published_at D... type idx_rss_crawl_time (line 92) | CREATE INDEX IF NOT EXISTS idx_rss_crawl_time ON rss_items(last_crawl_time) type idx_rss_title (line 95) | CREATE INDEX IF NOT EXISTS idx_rss_title ON rss_items(title) type idx_rss_url_feed (line 98) | CREATE UNIQUE INDEX IF NOT EXISTS idx_rss_url_feed type idx_rss_crawl_status_record (line 102) | CREATE INDEX IF NOT EXISTS idx_rss_crawl_status_record ON rss_crawl_stat... FILE: trendradar/storage/schema.sql type platforms (line 7) | CREATE TABLE IF NOT EXISTS platforms ( type news_items (line 18) | CREATE TABLE IF NOT EXISTS news_items ( type title_changes (line 37) | CREATE TABLE IF NOT EXISTS title_changes ( type rank_history (line 50) | CREATE TABLE IF NOT EXISTS rank_history ( type crawl_records (line 63) | CREATE TABLE IF NOT EXISTS crawl_records ( type crawl_source_status (line 74) | CREATE TABLE IF NOT EXISTS crawl_source_status ( type period_executions (line 88) | CREATE TABLE IF NOT EXISTS period_executions ( type idx_news_platform (line 102) | CREATE INDEX IF NOT EXISTS idx_news_platform ON news_items(platform_id) type idx_news_crawl_time (line 105) | CREATE INDEX IF NOT EXISTS idx_news_crawl_time ON news_items(last_crawl_... type idx_news_title (line 108) | CREATE INDEX IF NOT EXISTS idx_news_title ON news_items(title) type idx_news_url_platform (line 111) | CREATE UNIQUE INDEX IF NOT EXISTS idx_news_url_platform type idx_crawl_status_record (line 115) | CREATE INDEX IF NOT EXISTS idx_crawl_status_record ON crawl_source_statu... type idx_rank_history_news (line 118) | CREATE INDEX IF NOT EXISTS idx_rank_history_news ON rank_history(news_it... type idx_period_exec_lookup (line 121) | CREATE INDEX IF NOT EXISTS idx_period_exec_lookup FILE: trendradar/storage/sqlite_mixin.py class SQLiteStorageMixin (line 18) | class SQLiteStorageMixin: method _get_connection (line 34) | def _get_connection(self, date: Optional[str] = None, db_type: str = "... method _get_configured_time (line 39) | def _get_configured_time(self) -> datetime: method _format_date_folder (line 44) | def _format_date_folder(self, date: Optional[str] = None) -> str: method _format_time_filename (line 49) | def _format_time_filename(self) -> str: method _get_schema_path (line 57) | def _get_schema_path(self, db_type: str = "news") -> Path: method _get_ai_filter_schema_path (line 71) | def _get_ai_filter_schema_path(self) -> Path: method _init_tables (line 75) | def _init_tables(self, conn: sqlite3.Connection, db_type: str = "news"... method _save_news_data_impl (line 105) | def _save_news_data_impl(self, data: NewsData, log_prefix: str = "[存储]... method _get_today_all_data_impl (line 324) | def _get_today_all_data_impl(self, date: Optional[str] = None) -> Opti... method _get_latest_crawl_data_impl (line 457) | def _get_latest_crawl_data_impl(self, date: Optional[str] = None) -> O... method _detect_new_titles_impl (line 589) | def _detect_new_titles_impl(self, current_data: NewsData) -> Dict[str,... method _is_first_crawl_today_impl (line 648) | def _is_first_crawl_today_impl(self, date: Optional[str] = None) -> bool: method _get_crawl_times_impl (line 676) | def _get_crawl_times_impl(self, date: Optional[str] = None) -> List[str]: method _has_period_executed_impl (line 706) | def _has_period_executed_impl(self, date_str: str, period_key: str, ac... method _record_period_execution_impl (line 741) | def _record_period_execution_impl(self, date_str: str, period_key: str... method _save_rss_data_impl (line 787) | def _save_rss_data_impl(self, data: RSSData, log_prefix: str = "[存储]")... method _get_rss_data_impl (line 931) | def _get_rss_data_impl(self, date: Optional[str] = None) -> Optional[R... method _detect_new_rss_items_impl (line 1016) | def _detect_new_rss_items_impl(self, current_data: RSSData) -> Dict[st... method _get_latest_rss_data_impl (line 1073) | def _get_latest_rss_data_impl(self, date: Optional[str] = None) -> Opt... method _get_active_tags_impl (line 1168) | def _get_active_tags_impl(self, date: Optional[str] = None, interests_... method _get_latest_prompt_hash_impl (line 1192) | def _get_latest_prompt_hash_impl(self, date: Optional[str] = None, int... method _get_latest_tag_version_impl (line 1210) | def _get_latest_tag_version_impl(self, date: Optional[str] = None) -> ... method _deprecate_all_tags_impl (line 1225) | def _deprecate_all_tags_impl(self, date: Optional[str] = None, interes... method _save_tags_impl (line 1266) | def _save_tags_impl( method _deprecate_specific_tags_impl (line 1304) | def _deprecate_specific_tags_impl( method _update_tags_hash_impl (line 1336) | def _update_tags_hash_impl( method _update_tag_descriptions_impl (line 1361) | def _update_tag_descriptions_impl( method _update_tag_priorities_impl (line 1389) | def _update_tag_priorities_impl( method _save_analyzed_news_impl (line 1425) | def _save_analyzed_news_impl( method _get_analyzed_news_ids_impl (line 1457) | def _get_analyzed_news_ids_impl( method _clear_analyzed_news_impl (line 1476) | def _clear_analyzed_news_impl( method _clear_unmatched_analyzed_news_impl (line 1496) | def _clear_unmatched_analyzed_news_impl( method _save_filter_results_impl (line 1520) | def _save_filter_results_impl( method _get_active_filter_results_impl (line 1553) | def _get_active_filter_results_impl(self, date: Optional[str] = None, ... method _get_all_news_ids_impl (line 1672) | def _get_all_news_ids_impl(self, date: Optional[str] = None) -> List[D... method _get_all_rss_ids_impl (line 1696) | def _get_all_rss_ids_impl(self, date: Optional[str] = None) -> List[Di... FILE: trendradar/utils/time.py function get_configured_time (line 17) | def get_configured_time(timezone: str = DEFAULT_TIMEZONE) -> datetime: function format_date_folder (line 35) | def format_date_folder( function format_time_filename (line 53) | def format_time_filename(timezone: str = DEFAULT_TIMEZONE) -> str: function get_current_time_display (line 68) | def get_current_time_display(timezone: str = DEFAULT_TIMEZONE) -> str: function convert_time_for_display (line 81) | def convert_time_for_display(time_str: str) -> str: function format_iso_time_friendly (line 96) | def format_iso_time_friendly( function is_within_days (line 175) | def is_within_days( function calculate_days_old (line 242) | def calculate_days_old(iso_time: str, timezone: str = DEFAULT_TIMEZONE) ... class TimeWindowChecker (line 289) | class TimeWindowChecker: method __init__ (line 299) | def __init__( method is_in_time_range (line 317) | def is_in_time_range(self, start_time: str, end_time: str) -> bool: method _normalize_time (line 353) | def _normalize_time(self, time_str: str) -> str: method check_window (line 371) | def check_window( method get_status (line 414) | def get_status(self, window_config: dict, check_once_per_day_func=None... FILE: trendradar/utils/url.py function normalize_url (line 38) | def normalize_url(url: str, platform_id: str = "") -> str: function get_url_signature (line 131) | def get_url_signature(url: str, platform_id: str = "") -> str: