calendar
Calendar Skill
Google Calendar integration for managing events and checking availability through your agent. Built on top of the google-oauth skill for secure authentication.
Features
- List Events: Browse events with time filters and pagination
- Create & Update: Add and modify events with attendees
- Free Time Finder: Find available time slots across calendars
- Recurring Events: Support for recurring event patterns
- Multiple Calendars: Work with any calendar in your account
- Today & Upcoming: Quick access to relevant events
- SQLite Cache: Local metadata caching for performance
Installation
npm install
npm run build
Prerequisites
The calendar skill requires Calendar authorization through the google-oauth skill:
# Connect your Google account with Calendar scope
node ../google-oauth/dist/cli.js connect default calendar
CLI Usage
Check Status
# Check connection status
node dist/cli.js status
Health Check
node dist/cli.js health
List Calendars
node dist/cli.js calendars
List Events
# List events for next 7 days
node dist/cli.js list
# List more events
node dist/cli.js list --max 50 --days 14
# Search events
node dist/cli.js list --query "meeting"
# Specific calendar
node dist/cli.js list --calendar work@company.com
Today's Events
node dist/cli.js today
# Specific calendar
node dist/cli.js today --calendar work@company.com
Upcoming Events
# Next 10 events
node dist/cli.js upcoming
# Next 20 events for next 14 days
node dist/cli.js upcoming --max 20 --days 14
Get Event Details
node dist/cli.js get <event-id>
Create Event
# Quick create (1 hour, starting next hour)
node dist/cli.js create --summary "Team Meeting"
# With details
node dist/cli.js create \
--summary "Project Review" \
--description "Quarterly project review" \
--location "Conference Room A" \
--start "2024-01-15T10:00:00" \
--duration 90 \
--attendees "alice@example.com,bob@example.com"
# With end time
node dist/cli.js create \
--summary "Lunch" \
--start "2024-01-15T12:00:00" \
--end "2024-01-15T13:00:00"
Update Event
# Change title
node dist/cli.js update <event-id> --summary "New Title"
# Change time
node dist/cli.js update <event-id> \
--start "2024-01-15T14:00:00" \
--end "2024-01-15T15:00:00"
# Cancel event
node dist/cli.js update <event-id> --status cancelled
Delete Event
node dist/cli.js delete <event-id>
Find Free Time
# Find 60-minute slots in next 7 days
node dist/cli.js free
# Find 30-minute slots in next 3 days
node dist/cli.js free --duration 30 --days 3
# Check specific calendars
node dist/cli.js free --calendars "primary,work@company.com"
JavaScript/TypeScript API
Initialize
import { CalendarSkill } from '@openclaw/calendar';
// Create skill for default profile
const calendar = new CalendarSkill();
// Or for specific profile
const workCalendar = CalendarSkill.forProfile('work');
Check Status
const status = await calendar.getStatus();
console.log('Connected:', status.connected);
console.log('Email:', status.email);
console.log('Has Calendar:', status.hasCalendarScope);
List Calendars
const calendars = await calendar.listCalendars();
for (const cal of calendars) {
console.log(`${cal.summary}: ${cal.id}`);
if (cal.primary) {
console.log(' (Primary calendar)');
}
}
List Events
const result = await calendar.listEvents({
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
maxResults: 50,
singleEvents: true,
orderBy: 'startTime',
});
for (const event of result.events) {
console.log(`${event.summary}: ${event.start.dateTime}`);
}
// Pagination
const nextPage = await calendar.listEvents({
pageToken: result.nextPageToken,
// ... other options
});
Get Single Event
const event = await calendar.getEvent('event-id');
console.log('Title:', event.summary);
console.log('Description:', event.description);
console.log('Location:', event.location);
console.log('Start:', event.start.dateTime);
console.log('End:', event.end.dateTime);
// Attendees
for (const attendee of event.attendees || []) {
console.log(`${attendee.email}: ${attendee.responseStatus}`);
}
Create Event
const event = await calendar.createEvent({
summary: 'Team Meeting',
description: 'Weekly sync',
location: 'Conference Room A',
start: {
dateTime: '2024-01-15T10:00:00',
timeZone: 'America/New_York',
},
end: {
dateTime: '2024-01-15T11:00:00',
timeZone: 'America/New_York',
},
attendees: [
{ email: 'alice@example.com' },
{ email: 'bob@example.com', optional: true },
],
});
console.log('Created:', event.htmlLink);
Create Recurring Event
const event = await calendar.createEvent({
summary: 'Weekly Standup',
start: { dateTime: '2024-01-15T09:00:00' },
end: { dateTime: '2024-01-15T09:30:00' },
recurrence: [
{
frequency: 'WEEKLY',
byDay: ['MO', 'WE', 'FR'],
until: '2024-12-31',
},
],
});
Update Event
await calendar.updateEvent('event-id', {
summary: 'Updated Title',
description: 'New description',
location: 'New Location',
start: { dateTime: '2024-01-15T14:00:00' },
end: { dateTime: '2024-01-15T15:00:00' },
});
Delete Event
await calendar.deleteEvent('event-id');
Find Free Time
const freeSlots = await calendar.findFreeTime({
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
duration: 60, // 60 minutes
calendars: ['primary', 'work@company.com'],
});
for (const slot of freeSlots) {
console.log(`Free: ${slot.start} - ${slot.end}`);
}
Get Upcoming Events
const events = await calendar.getUpcomingEvents({
maxResults: 10,
days: 7,
});
for (const event of events) {
console.log(`${event.summary}: ${event.start.dateTime}`);
}
Get Today's Events
const events = await calendar.getTodayEvents();
console.log(`You have ${events.length} events today`);
Health Check
const health = await calendar.healthCheck();
if (health.status === 'healthy') {
console.log('Calendar API is accessible');
} else {
console.error('Issue:', health.message);
}
Event Types
All-Day Events
// All-day event (no time)
const event = await calendar.createEvent({
summary: 'Vacation Day',
start: { date: '2024-01-15' },
end: { date: '2024-01-16' }, // End date is exclusive
});
Timed Events
// Timed event
const event = await calendar.createEvent({
summary: 'Meeting',
start: { dateTime: '2024-01-15T10:00:00', timeZone: 'America/New_York' },
end: { dateTime: '2024-01-15T11:00:00', timeZone: 'America/New_York' },
});
Recurrence Patterns
Daily
recurrence: [{
frequency: 'DAILY',
interval: 1, // Every day
count: 10, // For 10 occurrences
}]
Weekly
recurrence: [{
frequency: 'WEEKLY',
byDay: ['MO', 'WE', 'FR'], // Monday, Wednesday, Friday
until: '2024-12-31', // Until end of year
}]
Monthly
recurrence: [{
frequency: 'MONTHLY',
byMonthDay: [15], // 15th of each month
}]
Storage
Cached event metadata is stored in:
~/.openclaw/skills/calendar/cache.db
Tables:
events- Cached event datacalendars- Calendar listsync_tokens- Sync state for incremental updates
Multi-Profile Support
Manage multiple Google accounts:
import { CalendarSkill } from '@openclaw/calendar';
// Work account
const work = CalendarSkill.forProfile('work');
// Personal account
const personal = CalendarSkill.forProfile('personal');
// Use independently
const workEvents = await work.listEvents({ maxResults: 10 });
const personalEvents = await personal.listEvents({ maxResults: 10 });
Each profile needs separate authentication:
node ../google-oauth/dist/cli.js connect work calendar
node ../google-oauth/dist/cli.js connect personal calendar
Error Handling
try {
const events = await calendar.listEvents();
} catch (error) {
if (error.message.includes('Not connected')) {
console.log('Please authenticate first');
} else if (error.message.includes('Calendar scope')) {
console.log('Re-authenticate with Calendar permissions');
} else {
console.error('Error:', error.message);
}
}
Testing
# Type checking
npm run typecheck
# Build
npm run build
# Check status
npm run status
# List calendars
npm run cli -- calendars
# List events
npm run cli -- list --max 5
# Find free time
npm run cli -- free --duration 60
Troubleshooting
"Not connected" error
Authenticate with google-oauth first:
node ../google-oauth/dist/cli.js connect default calendar
"Calendar scope not authorized"
Your Google account is connected but without Calendar permissions. Reconnect:
node ../google-oauth/dist/cli.js disconnect default
node ../google-oauth/dist/cli.js connect default calendar
API errors
Check health status:
node dist/cli.js health
Dependencies
@openclaw/google-oauth: For Calendar authentication@openclaw/auth-provider: Base authentication (via google-oauth)sqlite3: Local caching
Security Notes
- OAuth tokens stored encrypted by auth-provider
- Cache database has 0600 permissions (user read/write only)
- Uses Calendar API with least-privilege scope
- Free/busy queries don't expose event details
More from ticruz38/skills
binance-auth
Binance API authentication and key management for trading skills. Securely stores API keys, validates permissions, supports testnet vs production environments, and provides health checks with balance queries.
45reminders
Local reminder system with natural language scheduling
29meetings
Coordinate meetings using calendar and email integration. Find mutual availability, send invites, create calendar events, and manage meeting agendas with prep documents.
6auth-provider
Centralized authentication provider for OAuth 2.0 and API key management. Supports Google, Binance, QuickBooks, and Slack. Provides encrypted SQLite storage, auto-refresh, PKCE flow, and health checks.
6slack
Send messages and notifications to Slack channels. Built on top of slack-auth for authentication, supports messaging, file uploads, channel management, DM sending, and notification templates with variable substitution.
3email
Gmail integration for reading, sending, and managing emails. Built on top of google-oauth for authentication, with SQLite caching, thread support, and full Gmail API capabilities.
3