Initial backup 2026-02-17

This commit is contained in:
Krilly
2026-02-17 15:50:53 +00:00
commit 8902a93add
941 changed files with 131420 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "calendar",
"installedVersion": "1.0.0",
"installedAt": 1770184125851
}

98
skills/calendar/README.md Normal file
View File

@@ -0,0 +1,98 @@
# Calendar 📅
Calendar management and scheduling. Create events, manage meetings, and sync across calendar providers.
## Features
- Create events
- Schedule meetings
- Set reminders
- View availability
- Recurring events
- Calendar sync
## Supported Providers
- Google Calendar
- Apple Calendar (iCloud)
- Work/Corporate Calendars
## Quick Start
### Setup Google Calendar
```bash
export CALENDAR_TYPE=google
./cal.sh list
```
### Setup iCloud Calendar
```bash
export CALENDAR_TYPE=icloud
export CALENDAR_ICLOUD_ID='Anthony@martinwa.org'
export CALENDAR_ICLOUD_PASS='mvas-vwsk-ktiv-anex'
./cal.sh list
```
### Setup Work/Corporate Calendar
```bash
export CALENDAR_TYPE=work
export CALENDAR_WORK_EMAIL='your@email.com'
export CALENDAR_WORK_URL='https://your-calendar-server.com/calendars'
./cal.sh list
```
## Usage Examples
**View today's events:**
```bash
./cal.sh today
```
**View this week's agenda:**
```bash
./cal.sh agenda --days 7
```
**Schedule a meeting:**
```bash
./cal.sh create "Team Sync" "2026-02-05 10:00" "2026-02-05 11:00"
```
## Multiple Calendar Support
Now supports **multiple calendar sources**! Once configured, you can view events from all calendars or filter by type.
### Using iCloud
```bash
# Your credentials are already set:
export CALENDAR_TYPE=icloud
export CALENDAR_ICLOUD_ID='Anthony@martinwa.org'
export CALENDAR_ICLOUD_PASS='mvas-vwsk-ktiv-anex'
# View your iCloud calendar
./cal.sh today
# Or view combined with Google (if you add it later)
# Switch back to Google:
# unset CALENDAR_TYPE
# ./cal.sh today
```
### Using Work Calendar
```bash
# Set up your work calendar:
export CALENDAR_TYPE=work
export CALENDAR_WORK_EMAIL='anthony@pacificenergy.com.au'
export CALENDAR_WORK_URL='https://outlook.office365.com/EWS/Exchange.asmx'
./cal.sh today
```
### Viewing All Calendars
Want to see events from Google + iCloud + Work all at once? Ask me to combine them!
## Calendar Commands
- `./cal.sh today` - Show today's events
- `./cal.sh agenda [days]` - Show upcoming events
- `./cal.sh list` - List all configured calendars
- `./cal.sh create <title> <start> <end> [options]` - Create new event

32
skills/calendar/SKILL.md Normal file
View File

@@ -0,0 +1,32 @@
---
name: calendar
description: Calendar management and scheduling. Create events, manage meetings, and sync across calendar providers.
metadata: {"clawdbot":{"emoji":"📅","requires":{"bins":["curl","jq"]}}}
---
# Calendar 📅
Calendar and scheduling management.
## Features
- Create events
- Schedule meetings
- Set reminders
- View availability
- Recurring events
- Calendar sync
## Supported Providers
- Google Calendar
- Apple Calendar
- Outlook Calendar
## Usage Examples
```
"Schedule meeting tomorrow at 2pm"
"Show my calendar for this week"
"Find free time for a 1-hour meeting"
```

315
skills/calendar/cal.py Normal file
View File

@@ -0,0 +1,315 @@
#!/usr/bin/env python3
"""
Simple CalDAV Calendar Tool for Google Calendar
Works with Gmail app passwords - no OAuth needed!
"""
import sys
import argparse
from datetime import datetime, timedelta
from pathlib import Path
# This will be run with: uv run --with caldav cal.py
def get_credentials():
"""Get credentials from environment or .env file"""
import os
# Try to load from skills/imap-smtp-email/.env since we already have Gmail creds there
env_file = Path(__file__).parent.parent / 'imap-smtp-email' / '.env'
if env_file.exists():
for line in env_file.read_text().splitlines():
if line.strip() and not line.startswith('#') and '=' in line:
key, _, value = line.partition('=')
key = key.strip()
value = value.strip()
if key not in os.environ:
os.environ[key] = value
email = os.environ.get('IMAP_USER') or os.environ.get('SMTP_USER')
password = os.environ.get('IMAP_PASS') or os.environ.get('SMTP_PASS')
if not email or not password:
print("Error: Email credentials not found. Set IMAP_USER and IMAP_PASS.", file=sys.stderr)
sys.exit(1)
return email, password
def connect_caldav():
"""Connect to Calendar via CalDAV (Google, iCloud, or Work)"""
import caldav
import os
calendar_type = os.environ.get('CALENDAR_TYPE', 'google')
if calendar_type == 'icloud':
# iCloud CalDAV
email = os.environ.get('CALENDAR_ICLOUD_ID', 'anthonym_au@icloud.com')
password = os.environ.get('CALENDAR_ICLOUD_PASS', 'mvas-vwsk-ktiv-anex')
url = "https://caldav.icloud.com/"
print(f"Connecting to iCloud calendar for {email}...", file=sys.stderr)
client = caldav.DAVClient(url=url, username=email, password=password)
principal = client.principal()
return principal
elif calendar_type == 'work':
# Work calendar (Pacific Energy M365)
email = os.environ.get('CALENDAR_WORK_EMAIL', 'Anthony.martin@pacificenergy.com.au')
password = os.environ.get('CALENDAR_WORK_PASS', 'RecOvery2026!')
url = os.environ.get('CALENDAR_WORK_URL', 'https://outlook.office365.com/EWS/Exchange.asmx')
if not all([email, password, url]):
print("Error: Work calendar credentials not configured", file=sys.stderr)
sys.exit(1)
print(f"Connecting to work calendar ({email})...", file=sys.stderr)
client = caldav.DAVClient(url=url, username=email, password=password)
principal = client.principal()
return principal
else:
# Google Calendar (default)
email, password = get_credentials()
url = f"https://calendar.google.com/calendar/dav/{email}/events/"
print(f"Connecting to Google calendar ({email})...", file=sys.stderr)
client = caldav.DAVClient(url=url, username=email, password=password)
principal = client.principal()
return principal
def cmd_list(args):
"""List all calendars"""
principal = connect_caldav()
calendars = principal.calendars()
if not calendars:
print("No calendars found")
return
print("Available calendars:")
for cal in calendars:
print(f" {cal.name}")
if args.verbose:
print(f" URL: {cal.url}")
print()
def cmd_agenda(args):
"""Show upcoming events"""
principal = connect_caldav()
calendars = principal.calendars()
# Time range
start = datetime.now()
if args.days:
end = start + timedelta(days=int(args.days))
else:
end = start + timedelta(days=7)
print(f"Events from {start.strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}:\n")
for calendar in calendars:
if args.calendar and args.calendar.lower() not in calendar.name.lower():
continue
events = calendar.search(start=start, end=end, event=True, expand=True)
if not events:
continue
print(f"📅 {calendar.name}")
print("-" * 80)
for event in events:
try:
vevent = event.icalendar_component
summary = str(vevent.get('SUMMARY', 'No title'))
dtstart = vevent.get('DTSTART')
dtend = vevent.get('DTEND')
location = vevent.get('LOCATION', '')
description = vevent.get('DESCRIPTION', '')
# Format datetime
if hasattr(dtstart.dt, 'strftime'):
start_str = dtstart.dt.strftime('%Y-%m-%d %H:%M')
else:
start_str = str(dtstart.dt)
if hasattr(dtend.dt, 'strftime'):
end_str = dtend.dt.strftime('%H:%M')
else:
end_str = str(dtend.dt)
print(f"\n {summary}")
print(f" When: {start_str} - {end_str}")
if location:
print(f" Where: {location}")
if args.details and description:
print(f" Details: {description[:200]}{'...' if len(str(description)) > 200 else ''}")
except Exception as e:
print(f" [Error parsing event: {e}]")
print()
def cmd_today(args):
"""Show today's events"""
principal = connect_caldav()
calendars = principal.calendars()
start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=1)
print(f"Today's events ({start.strftime('%Y-%m-%d')}):\n")
all_events = []
for calendar in calendars:
if args.calendar and args.calendar.lower() not in calendar.name.lower():
continue
events = calendar.search(start=start, end=end, event=True, expand=True)
for event in events:
try:
vevent = event.icalendar_component
summary = str(vevent.get('SUMMARY', 'No title'))
dtstart = vevent.get('DTSTART')
dtend = vevent.get('DTEND')
location = vevent.get('LOCATION', '')
all_events.append({
'summary': summary,
'start': dtstart.dt,
'end': dtend.dt,
'location': location,
'calendar': calendar.name
})
except:
pass
# Sort by start time
all_events.sort(key=lambda x: x['start'])
if not all_events:
print("No events today")
return
for evt in all_events:
if hasattr(evt['start'], 'strftime'):
start_str = evt['start'].strftime('%H:%M')
end_str = evt['end'].strftime('%H:%M')
print(f" {start_str}-{end_str} {evt['summary']}")
else:
print(f" All day {evt['summary']}")
if evt['location']:
print(f" 📍 {evt['location']}")
print(f" 📅 {evt['calendar']}")
print()
def cmd_create(args):
"""Create a new event"""
from icalendar import Calendar, Event as ICalEvent
from datetime import datetime
import pytz
principal = connect_caldav()
calendars = principal.calendars()
# Find calendar
target_cal = None
if args.calendar:
for cal in calendars:
if args.calendar.lower() in cal.name.lower():
target_cal = cal
break
else:
# Use first calendar
target_cal = calendars[0] if calendars else None
if not target_cal:
print(f"Error: Calendar '{args.calendar}' not found", file=sys.stderr)
sys.exit(1)
# Parse datetime
try:
start_dt = datetime.fromisoformat(args.start)
end_dt = datetime.fromisoformat(args.end)
except:
print("Error: Invalid datetime format. Use YYYY-MM-DD HH:MM", file=sys.stderr)
sys.exit(1)
# Create event
cal = Calendar()
event = ICalEvent()
event.add('summary', args.summary)
event.add('dtstart', start_dt)
event.add('dtend', end_dt)
if args.location:
event.add('location', args.location)
if args.description:
event.add('description', args.description)
cal.add_component(event)
# Save to calendar
target_cal.save_event(cal.to_ical())
print(f"✅ Event created: {args.summary}")
print(f" Calendar: {target_cal.name}")
print(f" When: {start_dt} - {end_dt}")
def main():
parser = argparse.ArgumentParser(description='Simple CalDAV Calendar Tool')
subparsers = parser.add_subparsers(dest='command', help='Command')
# list
list_parser = subparsers.add_parser('list', help='List all calendars')
list_parser.add_argument('-v', '--verbose', action='store_true', help='Show URLs')
# agenda
agenda_parser = subparsers.add_parser('agenda', help='Show upcoming events')
agenda_parser.add_argument('--days', default='7', help='Days ahead (default: 7)')
agenda_parser.add_argument('--calendar', help='Filter by calendar name')
agenda_parser.add_argument('--details', action='store_true', help='Show descriptions')
# today
today_parser = subparsers.add_parser('today', help='Show today\'s events')
today_parser.add_argument('--calendar', help='Filter by calendar name')
# create
create_parser = subparsers.add_parser('create', help='Create new event')
create_parser.add_argument('summary', help='Event title')
create_parser.add_argument('start', help='Start time (YYYY-MM-DD HH:MM)')
create_parser.add_argument('end', help='End time (YYYY-MM-DD HH:MM)')
create_parser.add_argument('--calendar', help='Calendar name')
create_parser.add_argument('--location', help='Location')
create_parser.add_argument('--description', help='Description')
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
if args.command == 'list':
cmd_list(args)
elif args.command == 'agenda':
cmd_agenda(args)
elif args.command == 'today':
cmd_today(args)
elif args.command == 'create':
cmd_create(args)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
if '--verbose' in sys.argv or '-v' in sys.argv:
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == '__main__':
main()

44
skills/calendar/cal.sh Normal file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
#!/bin/bash
# CalDAV Calendar Tool - Supports Google, iCloud, and Work Calendars
cd "$(dirname "$0")"
# Get Apple ID and iCloud password from environment
# Note: Use original iCloud email (anthonym_au@icloud.com), not the alias
APPLE_ID="${CALENDAR_ICLOUD_ID:-anthonym_au@icloud.com}"
APPLE_PASS="${CALENDAR_ICLOUD_PASS:-mvas-vwsk-ktiv-anex}"
# Get work calendar credentials from environment
WORK_EMAIL="${CALENDAR_WORK_EMAIL:-Anthony.martin@pacificenergy.com.au}"
WORK_PASS="${CALENDAR_WORK_PASS:-RecOvery2026!}"
WORK_URL="${CALENDAR_WORK_URL:-https://outlook.office365.com/EWS/Exchange.asmx}"
# Choose which calendar to use
CALENDAR_TYPE="${1:-google}" # Default to Google
CALENDAR_URL=""
if [ "$CALENDAR_TYPE" = "icloud" ]; then
if [ -z "$APPLE_ID" ] || [ -z "$APPLE_PASS" ]; then
echo "Error: CALENDAR_ICLOUD_ID and CALENDAR_ICLOUD_PASS must be set for iCloud" >&2
echo "Run: export CALENDAR_ICLOUD_ID='your@email.com' CALENDAR_ICLOUD_PASS='password'" >&2
exit 1
fi
CALENDAR_URL="https://caldav.icloud.com/${APPLE_ID}/calendars/"
elif [ "$CALENDAR_TYPE" = "work" ]; then
if [ -z "$WORK_EMAIL" ] || [ -z "$WORK_URL" ]; then
echo "Error: CALENDAR_WORK_EMAIL and CALENDAR_WORK_URL must be set for work calendar" >&2
exit 1
fi
CALENDAR_URL="$WORK_URL"
else
# Google Calendar (default)
CALENDAR_URL="https://calendar.google.com/calendar/dav/"
fi
echo "📅 Using $CALENDAR_TYPE calendar" >&2
# Add calendar type to env for Python script
export CALENDAR_TYPE
export CALENDAR_URL
/home/openclaw/.local/bin/uv run --with caldav --with icalendar --with pytz cal.py "$@"

View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Environment setup for Calendar skill with iCloud
# Add this to your ~/.bashrc or ~/.zshrc to persist
# iCloud Calendar (Anthony Martin)
export CALENDAR_TYPE=icloud
export CALENDAR_ICLOUD_ID='Anthony@martinwa.org'
export CALENDAR_ICLOUD_PASS='mvas-vwsk-ktiv-anex'
# Work Calendar (Pacific Energy) - will set up when Anthony provides details
# export CALENDAR_TYPE=work
# export CALENDAR_WORK_EMAIL='anthony@pacificenergy.com.au'
# export CALENDAR_WORK_URL='https://pacificenergy.com/calendars'
echo "✅ Calendar credentials loaded for Anthony Martin"
echo " • Google Calendar: configured"
echo " • iCloud Calendar: configured (Anthony@martinwa.org)"
echo " • Work Calendar: ready (set up when Pacific Energy email provided)"