import { calcomResponseDataSchema, calcomSchema, unwrapCalcomData, } from "@automate.ax/integration-contracts/calcom" import { encodableSchema } from "@automate.ax/codec" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { CALCOM_ACCOUNT, CALCOM_DATE_TIME_SCHEMA, CALCOM_ID_SCHEMA, CALCOM_UID_SCHEMA, SCHEDULE_API_VERSION, SLOT_API_VERSION, calcomAccountRequirement, calcomApi, calcomPath, compact, } from "./lib" const APPS_READ = calcomAccountRequirement() const PUBLIC_CALCOM = calcomAccountRequirement() const SCHEDULE_READ = calcomAccountRequirement() const SCHEDULE_WRITE = calcomAccountRequirement() const SCHEDULE_ID_SCHEMA = z.object({ scheduleId: CALCOM_ID_SCHEMA }) const OOO_ID_SCHEMA = z.object({ oooId: CALCOM_ID_SCHEMA }) const RESERVATION_UID_SCHEMA = z.object({ uid: CALCOM_UID_SCHEMA }) const SLOT_DATE_SCHEMA = z.union([z.iso.date(), CALCOM_DATE_TIME_SCHEMA]) const SLOT_RESPONSE_SCHEMA = z.object({ data: z.record( z.string(), z .object({ bookingUid: z.string().optional(), end: z.string().optional(), seatsBooked: z.number().int().nonnegative().optional(), seatsRemaining: z.number().int().nonnegative().optional(), seatsTotal: z.number().int().nonnegative().optional(), start: z.string(), }) .catchall(encodableSchema) .array(), ), status: z.enum(["error", "success"]), }) const RESERVED_SLOT_RESPONSE_SCHEMA = z.object({ data: z .object({ eventTypeId: z.number().int(), reservationUid: z.string(), reservationUntil: z.string(), slotDuration: z.number().nonnegative(), slotEnd: z.string(), slotStart: z.string(), }) .nullable(), status: z.enum(["error", "success"]), }) /** Creates a Cal.com availability schedule. */ export const createCalcomSchedule = defineAction("Create Cal.com schedule") .describe( "Creates a weekly availability schedule with optional date overrides.", ) .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(calcomSchema("CreateScheduleInput_2024_06_11")) .output(calcomResponseDataSchema("CreateScheduleOutput_2024_06_11")) .retry({ replaySafety: "unsafe" }) .handler(({ account, input }) => calcomApi(account) .request("schedules", { apiVersion: SCHEDULE_API_VERSION, body: input, method: "POST", responseSchema: "CreateScheduleOutput_2024_06_11", }) .then(unwrapCalcomData), ) /** Lists the connected user's Cal.com schedules. */ export const listCalcomSchedules = defineAction("List Cal.com schedules") .describe("Lists every availability schedule owned by the connected user.") .account(CALCOM_ACCOUNT, SCHEDULE_READ) .input(z.object({})) .output(calcomResponseDataSchema("GetSchedulesOutput_2024_06_11")) .retry({ replaySafety: "safe" }) .handler(({ account }) => calcomApi(account) .request("schedules", { apiVersion: SCHEDULE_API_VERSION, responseSchema: "GetSchedulesOutput_2024_06_11", }) .then(unwrapCalcomData), ) /** Retrieves the connected user's default Cal.com schedule. */ export const getDefaultCalcomSchedule = defineAction( "Get default Cal.com schedule", ) .describe("Returns the default availability schedule for the connected user.") .account(CALCOM_ACCOUNT, SCHEDULE_READ) .input(z.object({})) .output(calcomResponseDataSchema("GetDefaultScheduleOutput_2024_06_11")) .retry({ replaySafety: "safe" }) .handler(({ account }) => calcomApi(account) .request("schedules/default", { apiVersion: SCHEDULE_API_VERSION, responseSchema: "GetDefaultScheduleOutput_2024_06_11", }) .then(unwrapCalcomData), ) /** Retrieves one Cal.com availability schedule. */ export const getCalcomSchedule = defineAction("Get Cal.com schedule") .describe("Retrieves one availability schedule by numeric ID.") .account(CALCOM_ACCOUNT, SCHEDULE_READ) .input(SCHEDULE_ID_SCHEMA) .output(calcomResponseDataSchema("GetScheduleOutput_2024_06_11")) .retry({ replaySafety: "safe" }) .handler(({ account, input }) => calcomApi(account) .request(calcomPath`schedules/${input.scheduleId}`, { apiVersion: SCHEDULE_API_VERSION, responseSchema: "GetScheduleOutput_2024_06_11", }) .then(unwrapCalcomData), ) /** Updates one Cal.com availability schedule. */ export const updateCalcomSchedule = defineAction("Update Cal.com schedule") .describe( "Updates weekly availability, date overrides, time zone, or default status.", ) .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(SCHEDULE_ID_SCHEMA.and(calcomSchema("UpdateScheduleInput_2024_06_11"))) .output(calcomResponseDataSchema("UpdateScheduleOutput_2024_06_11")) .retry({ replaySafety: "safe" }) .handler(({ account, input: { scheduleId, ...body } }) => calcomApi(account) .request(calcomPath`schedules/${scheduleId}`, { apiVersion: SCHEDULE_API_VERSION, body, method: "PATCH", responseSchema: "UpdateScheduleOutput_2024_06_11", }) .then(unwrapCalcomData), ) /** Deletes one Cal.com availability schedule. */ export const deleteCalcomSchedule = defineAction("Delete Cal.com schedule") .describe("Permanently deletes one non-default availability schedule.") .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(SCHEDULE_ID_SCHEMA) .output(calcomSchema("DeleteScheduleOutput_2024_06_11")) .retry({ replaySafety: "safe" }) .handler(({ account, input }) => calcomApi(account).request(calcomPath`schedules/${input.scheduleId}`, { apiVersion: SCHEDULE_API_VERSION, method: "DELETE", responseSchema: "DeleteScheduleOutput_2024_06_11", }), ) /** Lists the connected user's out-of-office entries. */ export const listCalcomOutOfOfficeEntries = defineAction( "List Cal.com out-of-office entries", ) .describe( "Lists out-of-office entries with optional pagination and time sorting.", ) .account(CALCOM_ACCOUNT, SCHEDULE_READ) .input( z.object({ skip: z.number().int().nonnegative().prefault(0), sortEnd: z.enum(["asc", "desc"]).optional(), sortStart: z.enum(["asc", "desc"]).optional(), take: z.number().int().positive().max(250).prefault(250), }), ) .output(calcomResponseDataSchema("UserOoosOutputResponseDto")) .retry({ replaySafety: "safe" }) .handler(({ account, input }) => calcomApi(account) .request("me/ooo", { query: input, responseSchema: "UserOoosOutputResponseDto", }) .then(unwrapCalcomData), ) /** Creates an out-of-office entry for the connected user. */ export const createCalcomOutOfOfficeEntry = defineAction( "Create Cal.com out-of-office entry", ) .describe("Blocks an absence range and optionally assigns a covering user.") .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(calcomSchema("CreateOutOfOfficeEntryDto")) .output(calcomResponseDataSchema("UserOooOutputResponseDto")) .retry({ replaySafety: "unsafe" }) .handler(({ account, input }) => calcomApi(account) .request("me/ooo", { body: input, method: "POST", responseSchema: "UserOooOutputResponseDto", }) .then(unwrapCalcomData), ) /** Updates one out-of-office entry for the connected user. */ export const updateCalcomOutOfOfficeEntry = defineAction( "Update Cal.com out-of-office entry", ) .describe("Updates an absence range, reason, note, or covering user.") .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(OOO_ID_SCHEMA.and(calcomSchema("UpdateOutOfOfficeEntryDto"))) .output(calcomResponseDataSchema("UserOooOutputResponseDto")) .retry({ replaySafety: "safe" }) .handler(({ account, input: { oooId, ...body } }) => calcomApi(account) .request(calcomPath`me/ooo/${oooId}`, { body, method: "PATCH", responseSchema: "UserOooOutputResponseDto", }) .then(unwrapCalcomData), ) /** Deletes one out-of-office entry for the connected user. */ export const deleteCalcomOutOfOfficeEntry = defineAction( "Delete Cal.com out-of-office entry", ) .describe("Permanently removes one out-of-office entry.") .account(CALCOM_ACCOUNT, SCHEDULE_WRITE) .input(OOO_ID_SCHEMA) .output(calcomResponseDataSchema("UserOooOutputResponseDto")) .retry({ replaySafety: "safe" }) .handler(({ account, input }) => calcomApi(account) .request(calcomPath`me/ooo/${input.oooId}`, { method: "DELETE", responseSchema: "UserOooOutputResponseDto", }) .then(unwrapCalcomData), ) /** Lists available slots for a personal, team, or dynamic Cal.com event. */ export const listCalcomAvailableSlots = defineAction( "List Cal.com available slots", ) .describe( "Returns available time or range slots for an event type or set of users.", ) .account(CALCOM_ACCOUNT, PUBLIC_CALCOM) .input( z.object({ bookingUidToReschedule: CALCOM_UID_SCHEMA.optional(), duration: z.number().int().positive().optional(), end: SLOT_DATE_SCHEMA, eventTypeId: CALCOM_ID_SCHEMA.optional(), eventTypeSlug: z.string().trim().min(1).optional(), format: z.enum(["range", "time"]).optional(), organizationSlug: z.string().trim().min(1).optional(), start: SLOT_DATE_SCHEMA, teamSlug: z.string().trim().min(1).optional(), timeZone: z.string().trim().min(1).optional(), username: z.string().trim().min(1).optional(), usernames: z.string().trim().min(1).array().min(2).optional(), }), ) .output(SLOT_RESPONSE_SCHEMA.shape.data) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => SLOT_RESPONSE_SCHEMA.parse( await calcomApi(account).requestJson("slots", { apiVersion: SLOT_API_VERSION, query: compact({ ...input, usernames: input.usernames?.join(","), }), }), ).data, ) /** Reserves an available Cal.com slot. */ export const reserveCalcomSlot = defineAction("Reserve Cal.com slot") .describe("Temporarily holds a slot before creating a booking.") .account(CALCOM_ACCOUNT, PUBLIC_CALCOM) .input(calcomSchema("ReserveSlotInput_2024_09_04")) .output(calcomResponseDataSchema("ReserveSlotOutputResponse_2024_09_04")) .retry({ replaySafety: "unsafe" }) .handler(({ account, input }) => calcomApi(account) .request("slots/reservations", { apiVersion: SLOT_API_VERSION, body: input, method: "POST", responseSchema: "ReserveSlotOutputResponse_2024_09_04", }) .then(unwrapCalcomData), ) /** Retrieves one Cal.com slot reservation. */ export const getCalcomSlotReservation = defineAction( "Get Cal.com slot reservation", ) .describe("Retrieves one temporary slot hold by UID.") .account(CALCOM_ACCOUNT, PUBLIC_CALCOM) .input(RESERVATION_UID_SCHEMA) .output(RESERVED_SLOT_RESPONSE_SCHEMA.shape.data) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => RESERVED_SLOT_RESPONSE_SCHEMA.parse( await calcomApi(account).requestJson( calcomPath`slots/reservations/${input.uid}`, { apiVersion: SLOT_API_VERSION, }, ), ).data, ) /** Updates one Cal.com slot reservation. */ export const updateCalcomSlotReservation = defineAction( "Update Cal.com slot reservation", ) .describe("Moves or extends one temporary slot hold.") .account(CALCOM_ACCOUNT, PUBLIC_CALCOM) .input( RESERVATION_UID_SCHEMA.and(calcomSchema("ReserveSlotInput_2024_09_04")), ) .output(calcomResponseDataSchema("ReserveSlotOutputResponse_2024_09_04")) .retry({ replaySafety: "unsafe" }) .handler(({ account, input: { uid, ...body } }) => calcomApi(account) .request(calcomPath`slots/reservations/${uid}`, { apiVersion: SLOT_API_VERSION, body, method: "PATCH", responseSchema: "ReserveSlotOutputResponse_2024_09_04", }) .then(unwrapCalcomData), ) /** Deletes one Cal.com slot reservation. */ export const deleteCalcomSlotReservation = defineAction( "Delete Cal.com slot reservation", ) .describe("Releases one temporary slot hold.") .account(CALCOM_ACCOUNT, PUBLIC_CALCOM) .input(RESERVATION_UID_SCHEMA) .output(z.object({})) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { await calcomApi(account).requestVoid( calcomPath`slots/reservations/${input.uid}`, { apiVersion: SLOT_API_VERSION, method: "DELETE", }, ) return {} }) /** Lists calendars connected to the current Cal.com account. */ export const listCalcomCalendars = defineAction("List Cal.com calendars") .describe( "Lists connected calendars, selected calendars, and destination calendars.", ) .account(CALCOM_ACCOUNT, APPS_READ) .input(z.object({})) .output(calcomResponseDataSchema("ConnectedCalendarsOutput")) .retry({ replaySafety: "safe" }) .handler(({ account }) => calcomApi(account) .request("calendars", { responseSchema: "ConnectedCalendarsOutput" }) .then(unwrapCalcomData), ) /** Retrieves busy intervals from selected connected calendars. */ export const getCalcomCalendarBusyTimes = defineAction( "Get Cal.com calendar busy times", ) .describe( "Returns busy intervals across selected external calendars in a date range.", ) .account(CALCOM_ACCOUNT, APPS_READ) .input( z.object({ calendarsToLoad: z .object({ credentialId: CALCOM_ID_SCHEMA, externalId: z.string().trim().min(1), }) .array() .min(1), dateFrom: CALCOM_DATE_TIME_SCHEMA, dateTo: CALCOM_DATE_TIME_SCHEMA, timeZone: z.string().trim().min(1).optional(), }), ) .output(calcomResponseDataSchema("GetBusyTimesOutput")) .retry({ replaySafety: "safe" }) .handler(({ account, input: { calendarsToLoad, ...query } }) => calcomApi(account) .request("calendars/busy-times", { query: { ...query, ...Object.fromEntries( calendarsToLoad.flatMap((calendar, index) => [ [ `calendarsToLoad[${index}][credentialId]`, calendar.credentialId, ], [`calendarsToLoad[${index}][externalId]`, calendar.externalId], ]), ), }, responseSchema: "GetBusyTimesOutput", }) .then(unwrapCalcomData), )