add calendar change detection to daemon mode
Enhances the sync daemon to monitor both email and calendar changes, automatically triggering syncs when local calendar events are added or deleted in VDIR format. 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode <noreply@opencode.ai>
This commit is contained in:
116
src/cli/sync.py
116
src/cli/sync.py
@@ -10,6 +10,8 @@ from src.utils.calendar_utils import save_events_to_vdir, save_events_to_file
|
||||
from src.services.microsoft_graph.calendar import (
|
||||
fetch_calendar_events,
|
||||
sync_local_calendar_changes,
|
||||
get_last_sync_time,
|
||||
detect_deleted_events,
|
||||
)
|
||||
from src.services.microsoft_graph.mail import (
|
||||
fetch_mail_async,
|
||||
@@ -420,6 +422,72 @@ def sync(
|
||||
)
|
||||
|
||||
|
||||
def check_calendar_changes(vdir_path, org):
|
||||
"""
|
||||
Check if there are local calendar changes that need syncing.
|
||||
|
||||
Args:
|
||||
vdir_path (str): Base vdir path
|
||||
org (str): Organization name
|
||||
|
||||
Returns:
|
||||
tuple: (has_changes, change_description)
|
||||
"""
|
||||
if not vdir_path:
|
||||
return False, "No vdir path configured"
|
||||
|
||||
org_vdir_path = os.path.join(os.path.expanduser(vdir_path), org)
|
||||
|
||||
if not os.path.exists(org_vdir_path):
|
||||
return False, "Calendar directory does not exist"
|
||||
|
||||
try:
|
||||
# Get last sync time
|
||||
last_sync_time = get_last_sync_time(org_vdir_path)
|
||||
|
||||
# Check if vdir directory has been modified since last sync
|
||||
vdir_mtime = os.path.getmtime(org_vdir_path)
|
||||
|
||||
if vdir_mtime > last_sync_time:
|
||||
# Check for specific types of changes
|
||||
deleted_events = detect_deleted_events(org_vdir_path)
|
||||
|
||||
# Count .ics files to detect new events
|
||||
import glob
|
||||
|
||||
ics_files = glob.glob(os.path.join(org_vdir_path, "*.ics"))
|
||||
|
||||
# Load previous state to compare
|
||||
state_file = os.path.join(org_vdir_path, ".sync_state.json")
|
||||
previous_state = {}
|
||||
if os.path.exists(state_file):
|
||||
try:
|
||||
import json
|
||||
|
||||
with open(state_file, "r") as f:
|
||||
previous_state = json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
new_event_count = len(ics_files) - len(previous_state) + len(deleted_events)
|
||||
|
||||
if deleted_events or new_event_count > 0:
|
||||
changes = []
|
||||
if new_event_count > 0:
|
||||
changes.append(f"{new_event_count} new events")
|
||||
if deleted_events:
|
||||
changes.append(f"{len(deleted_events)} deleted events")
|
||||
|
||||
return True, ", ".join(changes)
|
||||
else:
|
||||
return True, "directory modified"
|
||||
|
||||
return False, "no changes detected"
|
||||
|
||||
except Exception as e:
|
||||
return False, f"error checking calendar: {str(e)}"
|
||||
|
||||
|
||||
async def daemon_mode(
|
||||
dry_run,
|
||||
vdir,
|
||||
@@ -432,7 +500,7 @@ async def daemon_mode(
|
||||
two_way_calendar,
|
||||
):
|
||||
"""
|
||||
Run the script in daemon mode, periodically syncing emails.
|
||||
Run the script in daemon mode, periodically syncing emails and calendar.
|
||||
"""
|
||||
from src.services.microsoft_graph.mail import get_inbox_count_async
|
||||
from rich.console import Console
|
||||
@@ -455,7 +523,10 @@ async def daemon_mode(
|
||||
content.append(status_text, style=status_color)
|
||||
|
||||
return Panel(
|
||||
content, title="📧 Email Sync Daemon", border_style="blue", padding=(0, 1)
|
||||
content,
|
||||
title="📧 Email & Calendar Sync Daemon",
|
||||
border_style="blue",
|
||||
padding=(0, 1),
|
||||
)
|
||||
|
||||
# Initial display
|
||||
@@ -486,10 +557,10 @@ async def daemon_mode(
|
||||
else:
|
||||
# Show checking status
|
||||
console.clear()
|
||||
console.print(create_status_display("Checking for new messages...", "cyan"))
|
||||
console.print(create_status_display("Checking for changes...", "cyan"))
|
||||
|
||||
try:
|
||||
# Authenticate and get access token
|
||||
# Authenticate and get access token for mail check
|
||||
scopes = ["https://graph.microsoft.com/Mail.Read"]
|
||||
access_token, headers = get_access_token(scopes)
|
||||
remote_message_count = await get_inbox_count_async(headers)
|
||||
@@ -509,14 +580,41 @@ async def daemon_mode(
|
||||
[f for f in os.listdir(cur_dir) if ".eml" in f]
|
||||
)
|
||||
|
||||
if remote_message_count != local_message_count:
|
||||
mail_changes = remote_message_count != local_message_count
|
||||
|
||||
# Check for calendar changes if two-way sync is enabled
|
||||
calendar_changes = False
|
||||
calendar_change_desc = ""
|
||||
if two_way_calendar and vdir:
|
||||
calendar_changes, calendar_change_desc = check_calendar_changes(
|
||||
vdir, org
|
||||
)
|
||||
|
||||
# Determine what changed and show appropriate status
|
||||
if mail_changes and calendar_changes:
|
||||
console.print(
|
||||
create_status_display(
|
||||
f"Changes detected! Mail: Remote {remote_message_count}, Local {local_message_count} | Calendar: {calendar_change_desc}. Starting sync...",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
elif mail_changes:
|
||||
console.print(
|
||||
create_status_display(
|
||||
f"New messages detected! Remote: {remote_message_count}, Local: {local_message_count}. Starting sync...",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
elif calendar_changes:
|
||||
console.print(
|
||||
create_status_display(
|
||||
f"Calendar changes detected! {calendar_change_desc}. Starting sync...",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
|
||||
# Sync if any changes detected
|
||||
if mail_changes or calendar_changes:
|
||||
await _sync_outlook_data(
|
||||
dry_run,
|
||||
vdir,
|
||||
@@ -531,9 +629,15 @@ async def daemon_mode(
|
||||
last_sync_time = time.time()
|
||||
console.print(create_status_display("Sync completed ✅", "green"))
|
||||
else:
|
||||
status_parts = [
|
||||
f"Mail: Remote {remote_message_count}, Local {local_message_count}"
|
||||
]
|
||||
if two_way_calendar:
|
||||
status_parts.append(f"Calendar: {calendar_change_desc}")
|
||||
|
||||
console.print(
|
||||
create_status_display(
|
||||
f"No new messages (Remote: {remote_message_count}, Local: {local_message_count})",
|
||||
f"No changes detected ({', '.join(status_parts)})",
|
||||
"green",
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user