import { CLOSEBOT_BOT_SCHEMA, CLOSEBOT_BOT_TEMPLATE_SCHEMA, CLOSEBOT_ID_SCHEMA, CLOSEBOT_PUBLISH_BOT_RESULT_SCHEMA, } from "@automate.ax/integration-contracts/closebot" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { CLOSEBOT_ACCOUNT, getCloseBotApi, hasCloseBotUpdate } from "./lib" const OPTIONAL_TEXT = z.string().trim().min(1).optional() const FOLLOW_UP_SEQUENCE_INPUT_SCHEMA = z.object({ duration: z.number().int().nonnegative(), order: z.number().int().nonnegative(), unit: z.string().trim().min(1), }) const FOLLOW_UP_STATE_INPUT_SCHEMA = z.object({ extraPrompt: z.string().optional(), isDefault: z.boolean(), name: z.string().trim().min(1), order: z.number().int().nonnegative(), repeatFinal: z.boolean(), sequences: FOLLOW_UP_SEQUENCE_INPUT_SCHEMA.array(), }) const PERSONA_SPLIT_TEST_INPUT_SCHEMA = z.object({ isActive: z.boolean().optional(), personas: z .object({ order: z.number().int().nonnegative().optional(), personaId: CLOSEBOT_ID_SCHEMA.nullable().optional(), weight: z.number().int().nonnegative().optional(), }) .array() .nullable() .optional(), }) const BOT_UPDATE_FIELDS = { category: z.string().trim().min(1).nullable().optional(), favorite: z.boolean().nullable().optional(), folderId: CLOSEBOT_ID_SCHEMA.nullable().optional(), followUpActive: z.boolean().nullable().optional(), followUpExtraPrompt: z.string().nullable().optional(), followUpRepeat: z.boolean().nullable().optional(), followUpSequences: FOLLOW_UP_SEQUENCE_INPUT_SCHEMA.array() .nullable() .optional(), followUpStates: FOLLOW_UP_STATE_INPUT_SCHEMA.array().nullable().optional(), followUpVarianceMinutes: z.number().int().nonnegative().nullable().optional(), locked: z.boolean().nullable().optional(), name: z.string().trim().min(1).nullable().optional(), personaIds: CLOSEBOT_ID_SCHEMA.array().max(1).nullable().optional(), personaSplitTest: PERSONA_SPLIT_TEST_INPUT_SCHEMA.optional(), rescheduling: z.boolean().nullable().optional(), smartFollowUp: z.boolean().nullable().optional(), trash: z.boolean().nullable().optional(), } const CONTACT_TAG_INPUT_SCHEMA = z.object({ approveDeny: z.boolean(), id: CLOSEBOT_ID_SCHEMA, name: z.string(), }) const TAG_FILTER_INPUT_SCHEMA = z.object({ groups: z .object({ operator: z.string().trim().min(1), rules: z .object({ condition: z.string().trim().min(1), tagId: CLOSEBOT_ID_SCHEMA, tagName: z.string(), }) .array(), }) .array(), operator: z.string().trim().min(1), }) const CHANNEL_RULES_INPUT_SCHEMA = z.object({ channels: z .object({ channel: z.string().trim().min(1), rules: z .object({ operator: z.string().trim().min(1), value: z.string(), }) .array(), type: z.string().trim().min(1), }) .array(), }) /** Lists CloseBot bots available to the connected account. */ export const listCloseBotBots = defineAction("List CloseBot bots") .describe("Lists bots available to the connected CloseBot account.") .account(CLOSEBOT_ACCOUNT) .output(CLOSEBOT_BOT_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account }) => getCloseBotApi(account.secret).request("bot", { responseSchema: CLOSEBOT_BOT_SCHEMA.array(), }), ) /** Lists templates available when creating a CloseBot bot. */ export const listCloseBotBotTemplates = defineAction( "List CloseBot bot templates", ) .describe("Lists templates available when creating a bot.") .account(CLOSEBOT_ACCOUNT) .output(CLOSEBOT_BOT_TEMPLATE_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account }) => getCloseBotApi(account.secret).request("botTemplates", { responseSchema: CLOSEBOT_BOT_TEMPLATE_SCHEMA.array(), }), ) /** Retrieves one CloseBot bot. */ export const getCloseBotBot = defineAction("Get CloseBot bot") .describe("Retrieves one CloseBot bot by ID.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}`, { responseSchema: CLOSEBOT_BOT_SCHEMA }, ), ) /** Creates a CloseBot bot from a template or imported KDL. */ export const createCloseBotBot = defineAction("Create CloseBot bot") .describe("Creates a bot from a template, imported KDL, or a blank flow.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ businessInformation: z.string().optional(), category: OPTIONAL_TEXT, conversationReason: z.string().optional(), folderId: OPTIONAL_TEXT, importKdl: z.string().optional(), name: z.string().trim().min(1), personaIds: CLOSEBOT_ID_SCHEMA.array().length(1).optional(), templateId: OPTIONAL_TEXT, }), ) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request("bot", { body: input, method: "POST", responseSchema: CLOSEBOT_BOT_SCHEMA, }), ) /** Creates a CloseBot bot from a natural-language description. */ export const createCloseBotBotWithAi = defineAction( "Create CloseBot bot with AI", ) .describe("Creates a bot from a natural-language description.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ category: OPTIONAL_TEXT, description: z.string().trim().min(1), folderId: OPTIONAL_TEXT, name: z.string().trim().min(1), }), ) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request("bot/ai", { body: input, method: "POST", responseSchema: CLOSEBOT_BOT_SCHEMA, }), ) /** Updates mutable CloseBot bot settings. */ export const updateCloseBotBot = defineAction("Update CloseBot bot") .describe("Updates mutable bot settings, personas, and follow-up behavior.") .account(CLOSEBOT_ACCOUNT) .input( z .object({ botId: CLOSEBOT_ID_SCHEMA, ...BOT_UPDATE_FIELDS }) .refine( (input) => hasCloseBotUpdate(input, ["botId"]), "Provide a bot field to update.", ), ) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { botId, ...body } = input return getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(botId)}`, { body, method: "PUT", responseSchema: CLOSEBOT_BOT_SCHEMA }, ) }) /** Deletes one CloseBot bot. */ export const deleteCloseBotBot = defineAction("Delete CloseBot bot") .describe("Deletes one CloseBot bot by ID.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA })) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}`, { method: "DELETE", responseSchema: z.null() }, ), ) /** Duplicates one CloseBot bot. */ export const duplicateCloseBotBot = defineAction("Duplicate CloseBot bot") .describe("Duplicates one CloseBot bot and returns the new bot.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}/duplicate`, { method: "POST", responseSchema: CLOSEBOT_BOT_SCHEMA }, ), ) /** Attaches a source and its routing rules to a CloseBot bot. */ export const attachCloseBotSourceToBot = defineAction( "Attach CloseBot source to bot", ) .describe("Attaches a source and its routing rules to a bot.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ botId: CLOSEBOT_ID_SCHEMA, channelRules: CHANNEL_RULES_INPUT_SCHEMA.optional(), channels: z.string().array(), enabled: z.boolean().optional(), flowVariableValues: z.record(z.string(), z.string()).optional(), personaNameOverride: z.string().optional(), sourceId: CLOSEBOT_ID_SCHEMA, tagFilterConfig: TAG_FILTER_INPUT_SCHEMA.optional(), tags: CONTACT_TAG_INPUT_SCHEMA.array().optional(), }), ) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { botId, sourceId, ...body } = input return getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(botId)}/source/${encodeURIComponent(sourceId)}`, { body, method: "POST", responseSchema: z.null() }, ) }) /** Detaches a source from a CloseBot bot. */ export const detachCloseBotSourceFromBot = defineAction( "Detach CloseBot source from bot", ) .describe("Detaches a source from a bot.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA, sourceId: CLOSEBOT_ID_SCHEMA })) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}/source/${encodeURIComponent(input.sourceId)}`, { method: "DELETE", responseSchema: z.null() }, ), ) /** Ends a CloseBot persona split test and promotes its winner. */ export const endCloseBotPersonaSplitTest = defineAction( "End CloseBot persona split test", ) .describe("Ends a bot's persona split test and promotes its winner.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_BOT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}/persona-split-test/end`, { method: "POST", responseSchema: CLOSEBOT_BOT_SCHEMA }, ), ) /** Publishes the current CloseBot bot draft. */ export const publishCloseBotBot = defineAction("Publish CloseBot bot") .describe("Publishes the current draft of a CloseBot bot.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ botId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_PUBLISH_BOT_RESULT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `bot/${encodeURIComponent(input.botId)}/publish`, { method: "POST", responseSchema: CLOSEBOT_PUBLISH_BOT_RESULT_SCHEMA }, ), )