From b8ba4adec5e70f4e9f51b0f0a24b30e0ba18d520 Mon Sep 17 00:00:00 2001 From: Yanxin Lu Date: Wed, 18 Mar 2026 14:44:08 -0700 Subject: [PATCH] calendar-invite: add VALARM reminder, fix terminology, remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 1-day reminder (VALARM) to all sent invites - Fix datetime.utcnow() deprecation → datetime.now(timezone.utc) - Rename "message ID" → "envelope ID" in SKILL.md for consistency - Remove unused _himalaya() and _himalaya_with_account() helpers --- skills/calendar-invite/SKILL.md | 6 ++-- .../scripts/calendar_invite.py | 33 +++++++------------ 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/skills/calendar-invite/SKILL.md b/skills/calendar-invite/SKILL.md index 890b0b4..75fcc54 100644 --- a/skills/calendar-invite/SKILL.md +++ b/skills/calendar-invite/SKILL.md @@ -27,7 +27,7 @@ Calendar invites are outbound emails. Follow the workspace email rules: ## Owner Auto-Attendee -When sending invites, `mail@luyx.org` (owner's SimpleLogin alias) is **always added as an attendee automatically**. This ensures the owner receives every invite and can Accept/Decline from their own email client. No need to include it in `--to` — it's added by the script. +When sending invites, `mail@luyx.org` (owner's SimpleLogin alias) is **always added as an attendee automatically**. All invites include a **1-day reminder** (VALARM) by default. This ensures the owner receives every invite and can Accept/Decline from their own email client. No need to include it in `--to` — it's added by the script. When accepting or tentatively accepting a received invite, the original invite is **automatically forwarded to `mail@luyx.org`** so the event lands on the owner's calendar too. @@ -113,7 +113,7 @@ $SKILL_DIR/scripts/calendar-invite.sh send \ ## Replying to Invites ```bash -# Accept by himalaya message ID +# Accept by himalaya envelope ID $SKILL_DIR/scripts/calendar-invite.sh reply \ --envelope-id 42 \ --action accept @@ -135,7 +135,7 @@ $SKILL_DIR/scripts/calendar-invite.sh reply \ | Flag | Required | Description | |-----------------|----------|-----------------------------------------------------| | `--action` | Yes | `accept`, `decline`, or `tentative` | -| `--envelope-id` | * | Himalaya message ID containing the .ics attachment | +| `--envelope-id` | * | Himalaya envelope ID containing the .ics attachment | | `--ics-file` | * | Path to an .ics file (alternative to `--envelope-id`) | | `--from` | No | Your email (default: `youlu@luyanxin.com`) | | `--account` | No | Himalaya account name | diff --git a/skills/calendar-invite/scripts/calendar_invite.py b/skills/calendar-invite/scripts/calendar_invite.py index cab6686..2f73f07 100644 --- a/skills/calendar-invite/scripts/calendar_invite.py +++ b/skills/calendar-invite/scripts/calendar_invite.py @@ -14,14 +14,14 @@ import argparse import subprocess import sys import uuid -from datetime import datetime +from datetime import datetime, timedelta, timezone from pathlib import Path from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from icalendar import Calendar, Event, vCalAddress, vText +from icalendar import Alarm, Calendar, Event, vCalAddress, vText # --------------------------------------------------------------------------- # Config @@ -38,24 +38,6 @@ PRODID = "-//OpenClaw//CalendarInvite//EN" # Helpers # --------------------------------------------------------------------------- -def _himalaya(*args): - """Run a himalaya command and return stdout.""" - result = subprocess.run( - ["himalaya", *args], - capture_output=True, text=True, check=True, - ) - return result.stdout - - -def _himalaya_with_account(account, *args): - """Run a himalaya command with optional account flag.""" - cmd = ["himalaya"] - if account: - cmd += ["--account", account] - cmd += list(args) - result = subprocess.run(cmd, capture_output=True, text=True, check=True) - return result.stdout - def _sync_calendar(): """Sync local calendar to CalDAV server via vdirsyncer.""" @@ -123,7 +105,7 @@ def cmd_send(args): event = Event() event.add("uid", uid) - event.add("dtstamp", datetime.utcnow()) + event.add("dtstamp", datetime.now(timezone.utc)) event.add("dtstart", start, parameters={"TZID": args.timezone}) event.add("dtend", end, parameters={"TZID": args.timezone}) event.add("summary", args.summary) @@ -151,6 +133,13 @@ def cmd_send(args): "RSVP": "TRUE", }) + # 1-day reminder + alarm = Alarm() + alarm.add("action", "DISPLAY") + alarm.add("description", f"Reminder: {args.summary}") + alarm.add("trigger", timedelta(days=-1)) + event.add_component(alarm) + cal.add_component(event) ics_bytes = cal.to_ical() @@ -286,7 +275,7 @@ def cmd_reply(args): reply_event = Event() reply_event.add("uid", uid) - reply_event.add("dtstamp", datetime.utcnow()) + reply_event.add("dtstamp", datetime.now(timezone.utc)) # Copy timing from original if original_event.get("dtstart"):