Skip to content

Calendaring

To create a named calendar with storage, create or update a YAML file named calendars and add:

- title: Booking
  name: booking

This will create a new constant (@booking) that can be passed as the calendar argument to the various calendaring functions. The data will be persisted in a database and is visible in the studio, or from other conversations.

Alternatively, you can create a temporary in-memory calendar using Calendaring.new_in_memory_calendar(), where the data is only visible to the conversation that has the calendar.

Calendaring.add_event(calendar, event)

Add an event to the calendar, returning either the create event, or nil if the event was not added to the calendar. This can happen if a slot was passed and the slot is already at max capacity.

Options:

  • :title: A required string with a title for the event.
  • :description: An optional Markdown text field.
  • :from: The required starting date or datetime.
  • :until: The required ending date or datetime.
  • :metadata: A map with arbitrary data about the event. If a note_id key is set, it will be rendered as note in the calendar view.
  • :recurrence: Whether, and how often, the slot should recur. Defaults to no recurrence. You can either pass a list of weekdays such as ["monday", "tuesday"], or the alias :weekdays or :daily.
  • :recurrence_until: ISO date or datetime until when the recurrence rule should last (exclusive).
  • :exclude_dates: A list of dates to exclude from the recurrence series.
  • :location: A string indicating a location where the event takes place.

Examples:

# Normal event.
add_event(title: "Lunch with Elvis", from: "2024-03-01T13:00:00Z", until: "2024-03-01T14:00:00Z")

# All day event.
add_event(title: "Kingsday", from: "2024-04-27", until: "2024-04-27")

# Recurring event, see add_slot for more examples.
add_event(
  title: "Lunch",
  from: "2024-01-01T12:00:00Z",
  until: "2024-01-01T13:00:00Z",
  recurrence: :weekdays
)

Calendaring.add_event(calendar, slot, opts)

Same as add_event/2 but inherits the :from and :until options from the slot. Unlike add_event/2, this will also check that the given slot has capacity for the event to planned.

Calendaring.add_slot(calendar, slot)

Add a slot to the calendar, returning the updated calendar.

Examples:

# From 10:00 to 11:00, mon-fri, starting today.
Calendar.add_slot(cal, from: "10:00", until: "11:00", recurrence: :weekdays)

# From 10:00, until an hour and a half later (11:30)
Calendar.add_slot(cal, from: "10:00", until: [hours: 1, minutes: 30])

# The month of April every weekday from 10:00 to 11:00 (UTC), except the 25th of April.
Calendar.add_slot(
  cal,
  from: "2024-04-01T10:00:00Z",
  until: [hours: 1],
  recurrence: :weekdays,
  recurrence_until: "2024-05-01",
  exclude_dates: ["2024-05-25"]
)

Options:

  • :from: Either a time string (ex: "10:00"), or an ISO datetime string. Required. When a time string is given, the bot's timezone will be used.
  • :until: Either a time string (ex: "11:00"), an ISO datetime string, or a duration shifted from the :from option. For example: until: [hours: 1]. Required. When a time string is given, the bot's timezone will be used.
  • :capacity: The number of events that fit within the slot.
  • :recurrence: Whether, and how often, the slot should recur. Defaults to no recurrence. You can either pass a list of weekdays such as ["monday", "tuesday"], or the alias :weekdays or :daily.
  • :recurrence_until: ISO date or datetime until when the recurrence rule should last (exclusive).
  • :exclude_dates: A list of dates to exclude from the recurrence series.

Calendaring.add_todo(calendar, opts)

Add a new todo to the calendar.

Options:

  • :title: An optional string title for the todo.
  • :description: An optional markdown text field.
  • :start_at: A datetime from when the todo becomes relevant.
  • :due_at: A datetime for the todo's deadline.
  • :tags: A list of tag strings.
  • :location: A string indicating a location where the todo takes place.
  • :metadata: A map with arbitrary data about the todo.

Calendaring.add_todo(calendar, slot, opts)

Same as add_todo/2, except the start_at and due_at are autofilled by the slot.

Calendaring.available_slots(calendar, opts \\ [])

Return a list of slots that can still be filled.

Options:

  • :amount: The maximum number of slots to return. The actual returned amount may be less. Defaults to 5.
  • :skip: The number of slots to skip. Defaults to 0.
  • :until: Until when to generate the slots. Defaults to [weeks: 2].
  • :from: From when to start fetching the slots. Defaults to today.
  • :now: The current datetime to use as "now".
  • :ignore_capacity?: By default this function will ignore slots that have reached their capacity (the number of events planned within the slot is equal to its capacity). With this option you can ignore this check.
  • :constraints: Additional calendars to consider. The resulting slots won't overlap with events on any of the given calendars.
  • :capacity_calendars: The calendars that occupy the capacity of the listed slots. Defaults to the same calendar that was passed in.

Calendaring.delete_event(calendar, event)

Deletes an event.

event = Calendaring.get_event(cal, id)
result = Calendaring.delete_event(event)

Calendaring.delete_slot(calendar, slot)

Delete a slot from the calendar.

cal = Calendaring.new_in_memory_calendar()
slot = Calendaring.add_slot(cal, from: "10:00", until: [hours: 1])
success = Calendaring.delete_slot(cal, slot)

Calendaring.delete_todo(calendar, todo)

Delete a todo.

Returns a boolean indicating if the deleting was successful.

Calendaring.event_to_ical_attachment(event_attrs, opts \\ [])

Creates a calendar (ICS) attachment that can be used with the mail function.

Options:

  • :filename: The attachment filename. Defaults to "Event.ics".

Examples:

# Using the same options as `add_event`:
_attachment = event_to_ical_attachment(
  title: "Lunch with Elvis",
  from: "2024-03-01T13:00:00Z",
  until: "2024-03-01T14:00:00Z"
)

# Using an event.
_event = Calendaring.add_event(@booking, ...)
_attachment = Calendaring.event_to_ical_attachment(_event)
mail("person@example.com", "Invite", "Meeting invite details", attachments: [_attachment])

Calendaring.get_event(calendar, id)

Get an event by ID.

Calendaring.get_slot(calendar, id)

Get a slot using it's ID.

Calendaring.get_todo(calendar, id)

Get a todo by ID.

Calendaring.list_events(calendar, opts \\ [])

Lists the events for the given calendar.

Options:

  • :from: a datetime from where to start searching. Defaults to now.
  • :until: an end datetime until where to search. Defaults to [weeks: 2].
  • :amount: The maximum amount of events to return. Defaults to nil.
  • :skip: The number of events to skip. Defaults to 0.
  • :user_id: The user to look for.

Calendaring.list_todos(calendar, opts \\ [])

List notes for the given calendar.

Options:

  • :amount: The number of todos to fetch.
  • :skip: The number of todos to skip from the front.
  • :due_before: A datetime before which the notes must be due.
  • :user_id: The user to look for.

Calendaring.new_in_memory_calendar()

Create a new temporary calendar.

Calendaring.suggest_events(calendar_or_calendars, duration, opts \\ [])

Find holes in the calendar where an event of the given size would still fit. This does not look at the slots of the calendar, just the events.

The resulting slots should not overlap with any other events in the same calendar.

Options:

  • :constraints: Additional calendars to consider. The resulting slots won't overlap with events on any of the given calendars.
  • :from: The moment from where to start looking for a slot. Defaults to now.
  • :until: Until when to start looking for holes. Example: [weeks: 2] for 2 hours after :from. Defaults to [weeks: 2].
  • :amount: The number of slots to return.
  • :skip: The number of slots to skip from the front.
  • :availability_slots: Constrain the events to only be contained within the slots defined on this calendar. Used automatically by the Planning module.
  • :now: The current datetime to use as "now".

Calendaring.to_ics_attachment(calendar, opts \\ [])

Creates a calendar (ICS) attachment that can be used with the mail function.

Options:

  • :filename: The attachment filename. Defaults to "Calendar.ics".

Examples:

_attachment = Calendaring.to_ics_attachment(@my_calendar)
mail("person@example.com", "Calendar", "All events and todos in calendar", attachments: [_attachment])

Calendaring.to_ics_string(calendar)

Formats a calendar to a string using the iCalendar (.ics) spec.

Examples:

cal = new_in_memory_calendar()
add_event(cal, ...)
add_todo(cal, ...)
_ics = Calendaring.to_ics_string(cal)
# _ics contains "BEGIN:VCALENDAR
..." with one VEVENT and one VTODO.