diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4b105..1f4ec10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## v3.9 - 2025-09-12 - 增强TBD事件清理功能 +- **自动删除已结束的TBD事件**: + - 检查事件的结束时间,如果已过且仍包含TBD则自动删除 + - 避免日历中保留已过期但未更新的TBD占位符 + - 例如:删除已结束的 "BB vs TBD"、"Tidebd vs TBD" 等事件 +- **智能删除TBD vs TBD占位符**: + - 当同一时间段(30分钟窗口)存在确认的比赛时,自动删除TBD vs TBD事件 + - 解决了TBD vs TBD与确认比赛重复的问题 + - 例如:9月11日的多个TBD vs TBD事件被正确清理 +- **改进的事件分组逻辑**: + - 按30分钟时间窗口分组事件,更准确地识别同时间的比赛 + - 优化了TBD事件与确认比赛的匹配算法 + ## v3.8 - 2025-09-08 - 修复重复比分显示问题 - **修复标题中重复比分的问题**: - 修复了在更新比赛比分时可能出现重复比分的bug(如 "0-2 0-1") @@ -143,6 +156,7 @@ | v3.6 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | v3.7 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | v3.8 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| v3.9 | ✓ | ✓ | ✓ | ✓ | ✓+ | ✓ | ## 使用建议 diff --git a/README.md b/README.md index d1c15eb..48aef42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Dota 2 Calendar Sync v3.8 +# Dota 2 Calendar Sync v3.9 -自动从 Liquipedia 获取 Dota 2 Tier 1 比赛信息并同步到 Google Calendar,支持自动更新比赛结果、时间变更、智能管理TBD占位事件和自动清理重复比赛。 +自动从 Liquipedia 获取 Dota 2 Tier 1 比赛信息并同步到 Google Calendar,支持自动更新比赛结果、时间变更、智能管理TBD占位事件、自动清理过期和重复比赛。 ## 功能 @@ -11,6 +11,7 @@ - **检测并更新比赛时间变更**(赛程调整时自动同步) - **智能管理TBD占位事件**(自动更新队伍信息,删除过期和被取代的事件) - **自动清理重复比赛**(优先保留已完成的比赛记录) +- **增强的TBD事件清理**(删除已结束但仍包含TBD的事件,删除与确认比赛同时间的TBD占位符) - 避免重复添加已存在的比赛 - 支持 dry-run 模式进行测试 @@ -87,6 +88,8 @@ python sync_dota2_matches.py --dry-run - **改进的TBD匹配**:1小时时间窗口匹配,更好处理赛程调整 - **重复比赛清理**:自动检测并删除同队伍的重复事件 - **TBD事件自动删除**:当队伍确定后自动删除对应的TBD占位符 + - **增强的过期事件清理**:自动删除已结束但仍包含TBD的事件 + - **智能TBD vs TBD处理**:删除与确认比赛在同一时间段的TBD vs TBD占位符 2. **日历事件管理**: - 自动设置比赛时长(根据 Bo 格式估算) diff --git a/sync_dota2_matches.py b/sync_dota2_matches.py index c9f4479..8c0996f 100644 --- a/sync_dota2_matches.py +++ b/sync_dota2_matches.py @@ -998,10 +998,18 @@ class Dota2CalendarSync: if event_id in updated_tbd_events: continue - # Get event time + # Get event time and end time event_start = event['start'].get('dateTime', event['start'].get('date')) event_dt = datetime.fromisoformat(event_start.replace('Z', '+00:00')) + # Get event end time for checking if match has ended + event_end = event.get('end', {}).get('dateTime', event.get('end', {}).get('date')) + if event_end: + event_end_dt = datetime.fromisoformat(event_end.replace('Z', '+00:00')) + else: + # If no end time, assume 3 hours duration + event_end_dt = event_dt + timedelta(hours=3) + # Use 30-minute window for "same time" time_key = (event_dt.year, event_dt.month, event_dt.day, event_dt.hour, event_dt.minute // 30) @@ -1013,14 +1021,14 @@ class Dota2CalendarSync: if 'vs TBD' in summary or 'TBD vs' in summary: events_by_time[time_key]['tbd'].append(event) - # Also check if it's expired (for TBD vs TBD only) - if 'TBD vs TBD' in summary and event_dt < now - timedelta(hours=2): + # Delete if match has ended (end time has passed) and still contains TBD + if event_end_dt < now: if self.delete_calendar_event(event_id): - print(f"🗑️ Deleted expired TBD event: {summary} ({event_dt.strftime('%Y-%m-%d %H:%M UTC')})") + print(f"🗑️ Deleted ended TBD event: {summary} (ended at {event_end_dt.strftime('%Y-%m-%d %H:%M UTC')})") deleted_tbd_count += 1 time.sleep(0.2) else: - print(f"✗ Failed to delete TBD event: {summary}") + print(f"✗ Failed to delete ended TBD event: {summary}") error_count += 1 continue # Don't process this event further @@ -1040,25 +1048,42 @@ class Dota2CalendarSync: for tbd_event in events['tbd']: tbd_summary = tbd_event.get('summary', '') - # Extract team from TBD event - team_match = re.search(r'(\w+)\s+vs\s+TBD|TBD\s+vs\s+(\w+)', tbd_summary) - if team_match: - team_in_tbd = team_match.group(1) or team_match.group(2) - - # Check if this team has a confirmed match - for confirmed_event in events['confirmed']: + # Check if this is a complete TBD vs TBD event + if 'TBD vs TBD' in tbd_summary: + # Delete TBD vs TBD when there's any confirmed match at the same time + # Since TBD vs TBD is a placeholder, any confirmed match supersedes it + if events['confirmed']: + confirmed_event = events['confirmed'][0] # Use first confirmed match for logging confirmed_summary = confirmed_event.get('summary', '') - if team_in_tbd and team_in_tbd in confirmed_summary: - # This TBD event has been superseded - if self.delete_calendar_event(tbd_event['id']): - print(f"🗑️ Deleted superseded TBD event: {tbd_summary}") - print(f" Replaced by: {confirmed_summary}") - deleted_tbd_count += 1 - time.sleep(0.2) - else: - print(f"✗ Failed to delete TBD event: {tbd_summary}") - error_count += 1 - break + if self.delete_calendar_event(tbd_event['id']): + print(f"🗑️ Deleted TBD vs TBD event at same time as confirmed match") + print(f" TBD event: {tbd_summary}") + print(f" Confirmed match: {confirmed_summary}") + deleted_tbd_count += 1 + time.sleep(0.2) + else: + print(f"✗ Failed to delete TBD vs TBD event: {tbd_summary}") + error_count += 1 + else: + # For partial TBD events (one team is known), check for team match + team_match = re.search(r'(\w+)\s+vs\s+TBD|TBD\s+vs\s+(\w+)', tbd_summary) + if team_match: + team_in_tbd = team_match.group(1) or team_match.group(2) + + # Check if this team has a confirmed match + for confirmed_event in events['confirmed']: + confirmed_summary = confirmed_event.get('summary', '') + if team_in_tbd and team_in_tbd in confirmed_summary: + # This TBD event has been superseded + if self.delete_calendar_event(tbd_event['id']): + print(f"🗑️ Deleted superseded TBD event: {tbd_summary}") + print(f" Replaced by: {confirmed_summary}") + deleted_tbd_count += 1 + time.sleep(0.2) + else: + print(f"✗ Failed to delete TBD event: {tbd_summary}") + error_count += 1 + break # Delete duplicate TBD vs TBD events at the same time for time_key, events in tbd_by_time.items():