Fetching Calendar Events for a Google Group's Shared Calendar with gogcli
The gog calendar team command aggregates events from every member of a Google Group into a single deduplicated view using concurrent API calls and recursive group expansion.
gogcli (available at steipete/gogcli) solves the visibility problem of Google Workspace shared calendars by providing a dedicated team sub-command. Rather than querying a single calendar ID, it resolves all members of a specified Google Group, fetches each person's calendar events in parallel, and merges the results into a unified output. This approach handles nested groups, eliminates duplicate meeting entries, and supports both detailed event data and lightweight free-busy queries.
Understanding the Architecture
The implementation follows a layered pipeline that separates CLI parsing, group resolution, API communication, and output formatting.
-
CLI Entry Point: The
calendarcommand is registered ininternal/cmd/calendar.go, which delegates toCalendarTeamCmd.Rundefined ininternal/cmd/calendar_team.go. This struct captures flags like--group-email,--freebusy,--query, and time-range options. -
Group Resolution: The
collectGroupMemberEmailsfunction ininternal/cmd/groups.gowalks the Google Group hierarchy via the Cloud Identity API. It handles nested groups and prevents infinite loops using aseenGroupstracker. -
Service Initialization: Factory functions in
internal/googleapi/cloudidentity.goandinternal/googleapi/calendar.gocreate authenticated clients with the proper OAuth scopes required for directory reads and calendar access. -
Time Handling: Human-readable flags like
--today,--week, or ISO timestamps are parsed byResolveTimeRangeandResolveTimeRangeWithDefaultsininternal/cmd/time_helpers.go, converting them to RFC 3339 timestamps in the target time zone.
Resolving Group Members and Time Ranges
Before fetching any calendar data, the tool must expand the group into a flat list of user emails.
The collectGroupMemberEmails function recursively queries the Cloud Identity API to enumerate direct and nested members. It returns a sorted, deduplicated slice of email addresses while guarding against cyclic group memberships. Concurrently, the ResolveTimeRange function processes CLI arguments to establish precise start and end boundaries for the query window.
These preparatory steps ensure that subsequent API calls target the correct user set and time window without manual email list curation.
Fetching and Deduplicating Events
The calendar_team.go file implements two distinct retrieval strategies controlled by the --freebusy flag.
Free-Busy Mode: When --freebusy is set, the runFreeBusy function issues a single batch request covering all group members. This returns availability windows without event titles or attendees, offering maximum performance for scheduling checks.
Event Mode: By default, runEvents spawns up to 10 concurrent goroutines (limited by a semaphore channel) to execute Events.List calls for each member. The implementation filters out declined invitations, private events (unless visible), and applies case-insensitive title matching via the --query flag.
After retrieval, the dedupeTeamEvents function merges events sharing the same ICalUID or ID + start time combination. Unless --no-dedup is specified, duplicate entries are consolidated into single rows with a combined "Who" column listing all attendees from the group.
Practical Usage Examples
Fetch detailed events for the engineering group over the next two days, filtering for standups and outputting JSON:
gog calendar team [email protected] \
--from today \
--days 2 \
--query standup \
--json
Retrieve a fast free-busy view for a custom time window without event details:
gog calendar team [email protected] \
--from 2026-03-01T09:00:00Z \
--to 2026-03-01T17:00:00Z \
--freebusy
Show raw events without deduplication to debug individual attendance patterns:
gog calendar team [email protected] \
--from week \
--no-dedup
Summary
- The
calendar teamcommand treats Google Groups as logical calendar collections rather than single calendar entities. - Recursive group expansion in
internal/cmd/groups.gohandles complex organizational hierarchies and nested memberships. - Concurrent fetching with a 10-worker semaphore maximizes throughput while respecting Google API rate limits.
- The deduplication engine in
calendar_team.gouses deterministic keys (ICalUID) to merge shared meeting instances across multiple calendars. - Both high-performance free-busy queries and full event detail retrieval are supported via the
--freebusyflag.
Frequently Asked Questions
How does gogcli handle nested Google Groups?
The collectGroupMemberEmails function in internal/cmd/groups.go recursively traverses group memberships using the Cloud Identity API. It maintains a seenGroups map to detect and prevent infinite loops caused by circular group references, ensuring each member email appears only once in the final list.
What is the difference between free-busy and event mode?
Free-busy mode (--freebusy) calls the FreeBusy API endpoint, which returns time slots marked as busy without revealing event titles, descriptions, or attendee lists. Event mode (default) calls Events.List for each member to retrieve full calendar details, subject to the viewer's sharing permissions, and supports title filtering via the --query parameter.
Can I disable event deduplication when querying a group?
Yes. By default, gogcli merges events that share the same ICalUID to avoid showing the same meeting multiple times when several group members are invited. Adding the --no-dedup flag preserves each calendar's individual copy, which is useful for verifying per-person attendance or debugging sync issues.
How does the tool manage API rate limits when querying large groups?
The runEvents function implements a concurrency semaphore (sem := make(chan struct{}, 10)) that limits simultaneous API requests to 10 goroutines. This prevents overwhelming the Google Calendar API while still parallelizing requests across group members for efficient data retrieval.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →