Skip to content

Instantly share code, notes, and snippets.

@kubukoz
Last active May 21, 2025 21:48
Show Gist options
  • Save kubukoz/7305b0ccc89d25356b4c9b3fb8afdfeb to your computer and use it in GitHub Desktop.
Save kubukoz/7305b0ccc89d25356b4c9b3fb8afdfeb to your computer and use it in GitHub Desktop.
Move all events between two Google Calendars
//> using toolkit default
//> using option -no-indent
import sttp.client4.quick.*
import util.chaining.*
import upickle.default.*
import sttp.model.MediaType
case class Event(
id: String,
summary: String = "<no summary>",
// appears on occurrences of repeated events, these can't be moved
recurringEventId: String = ""
) derives ReadWriter
case class EventsResponse(items: List[Event] = Nil, nextPageToken: String = "")
derives ReadWriter
val fromCalendar = sys.env("FROM_CALENDAR_ID")
val toCalendar = sys.env("TO_CALENDAR_ID")
val apiKey = sys.env("GOOGLE_API_TOKEN")
var pageToken = None: Option[String]
while (pageToken != Some("")) {
val response = quickRequest
.get(
uri"https://www.googleapis.com/calendar/v3/calendars/$fromCalendar/events"
.param("pageToken", pageToken)
)
.auth
.bearer(apiKey)
.send()
.body
// .tap(os.write.over(os.pwd / "events.json", _))
.pipe(read[EventsResponse](_))
pageToken = Some(response.nextPageToken)
val events = response.items
.filter(_.recurringEventId.isEmpty)
println(s"Found ${events.size} events")
events.foreach { event =>
println(s"Moving event ${event.summary} (${event.id})")
quickRequest
.post(
uri"https://www.googleapis.com/calendar/v3/calendars/$fromCalendar/events/${event.id}/move?destination=$toCalendar"
)
.auth
.bearer(apiKey)
.contentType(MediaType.ApplicationJson)
.body(write(event))
.send()
.body
.pipe(read[Event](_))
.tap(e => println(s"Moved event ${e.summary} (${e.id})"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment