import { CLOSEBOT_ID_SCHEMA, CLOSEBOT_LEAD_AI_STATUS_SCHEMA, CLOSEBOT_LEAD_HISTORY_EVENT_SCHEMA, CLOSEBOT_LEAD_PAGE_SCHEMA, CLOSEBOT_LEAD_SCHEMA, CLOSEBOT_LEAD_SEARCH_RESULT_SCHEMA, CLOSEBOT_NOTE_RESULT_SCHEMA, CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA, CLOSEBOT_SEND_MESSAGE_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 LEAD_FIELD_INPUT_SCHEMA = z.object({ field: z.string().trim().min(1), value: z.string(), }) /** Lists CloseBot leads with page-based pagination. */ export const listCloseBotLeads = defineAction("List CloseBot leads") .describe("Lists leads with page-based pagination and source filtering.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ leadId: CLOSEBOT_ID_SCHEMA.optional(), page: z.number().int().nonnegative().prefault(0), pageSize: z.number().int().positive().prefault(20), sourceId: CLOSEBOT_ID_SCHEMA.optional(), }), ) .output(CLOSEBOT_LEAD_PAGE_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request("lead", { query: input, responseSchema: CLOSEBOT_LEAD_PAGE_SCHEMA, }), ) /** Searches CloseBot leads with conversation and ownership filters. */ export const searchCloseBotLeads = defineAction("Search CloseBot leads") .describe( "Searches leads with source, bot, persona, and conversation filters.", ) .account(CLOSEBOT_ACCOUNT) .input( z.object({ botIds: CLOSEBOT_ID_SCHEMA.array(), channels: z.string().array(), count: z.number().int().nonnegative().optional(), followUpScheduled: z.boolean().optional(), lastMessageDirection: z.string().optional(), mentionedOnly: z.boolean().optional(), minimumResponses: z.number().int().nonnegative().optional(), offset: z.number().int().nonnegative().optional(), personaIds: CLOSEBOT_ID_SCHEMA.array(), search: z.string().optional(), sourceIds: CLOSEBOT_ID_SCHEMA.array(), sourceOnlyFast: z.boolean().optional(), starredOnly: z.boolean().optional(), totalOnly: z.boolean().optional(), }), ) .output(CLOSEBOT_LEAD_SEARCH_RESULT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request("lead/search", { body: input, method: "POST", responseSchema: CLOSEBOT_LEAD_SEARCH_RESULT_SCHEMA, }), ) /** Retrieves one CloseBot lead. */ export const getCloseBotLead = defineAction("Get CloseBot lead") .describe("Retrieves one lead, optionally including external contact data.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ includeExternal: z.boolean().prefault(false), leadId: CLOSEBOT_ID_SCHEMA, }), ) .output(CLOSEBOT_LEAD_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}`, { query: { includeExternal: input.includeExternal }, responseSchema: CLOSEBOT_LEAD_SCHEMA, }, ), ) /** Updates fields and conversation flags on a CloseBot lead. */ export const updateCloseBotLead = defineAction("Update CloseBot lead") .describe("Updates lead fields and conversation flags.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ fields: LEAD_FIELD_INPUT_SCHEMA.array(), leadId: CLOSEBOT_ID_SCHEMA, starred: z.boolean().nullable().optional(), unread: z.boolean().nullable().optional(), wontRespondUntil: z.date().nullable().optional(), }), ) .output(CLOSEBOT_LEAD_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { leadId, ...body } = input return getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(leadId)}`, { body, method: "PUT", responseSchema: CLOSEBOT_LEAD_SCHEMA }, ) }) /** Deletes one CloseBot lead. */ export const deleteCloseBotLead = defineAction("Delete CloseBot lead") .describe("Deletes one CloseBot lead by ID.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ leadId: CLOSEBOT_ID_SCHEMA })) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}`, { method: "DELETE", responseSchema: z.null() }, ), ) /** Gets whether CloseBot AI can respond to one lead. */ export const getCloseBotLeadAiStatus = defineAction( "Get CloseBot lead AI status", ) .describe("Gets whether AI responses are enabled and applicable for a lead.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ leadId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_LEAD_AI_STATUS_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/ai-toggle`, { responseSchema: CLOSEBOT_LEAD_AI_STATUS_SCHEMA }, ), ) /** Enables or disables CloseBot AI responses for one lead. */ export const updateCloseBotLeadAiStatus = defineAction( "Update CloseBot lead AI status", ) .describe("Enables or disables AI responses for one lead.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ enabled: z.boolean(), leadId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_LEAD_AI_STATUS_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/ai-toggle`, { body: { enabled: input.enabled }, method: "PUT", responseSchema: CLOSEBOT_LEAD_AI_STATUS_SCHEMA, }, ), ) /** Lists recent page and scoring history for a CloseBot lead. */ export const listCloseBotLeadHistory = defineAction( "List CloseBot lead history", ) .describe("Lists recent page visits and scoring events for a lead.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ leadId: CLOSEBOT_ID_SCHEMA, limit: z.number().int().positive().prefault(20), }), ) .output(CLOSEBOT_LEAD_HISTORY_EVENT_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/page-history`, { query: { limit: input.limit }, responseSchema: CLOSEBOT_LEAD_HISTORY_EVENT_SCHEMA.array(), }, ), ) /** Creates an internal note on a CloseBot conversation. */ export const createCloseBotLeadNote = defineAction("Create CloseBot lead note") .describe("Creates an internal note that is never sent to the contact.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ aiVisible: z.boolean(), body: z.string().trim().min(1), leadId: CLOSEBOT_ID_SCHEMA, mentionedAccountIds: CLOSEBOT_ID_SCHEMA.array().optional(), }), ) .output(CLOSEBOT_NOTE_RESULT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { leadId, ...body } = input return getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(leadId)}/note`, { body, method: "POST", responseSchema: CLOSEBOT_NOTE_RESULT_SCHEMA, }, ) }) /** Updates an internal CloseBot conversation note. */ export const updateCloseBotLeadNote = defineAction("Update CloseBot lead note") .describe("Updates a note body, AI visibility, or mentions.") .account(CLOSEBOT_ACCOUNT) .input( z .object({ aiVisible: z.boolean().nullable().optional(), body: z.string().trim().min(1).nullable().optional(), leadId: CLOSEBOT_ID_SCHEMA, mentionedAccountIds: CLOSEBOT_ID_SCHEMA.array().nullable().optional(), noteId: CLOSEBOT_ID_SCHEMA, }) .refine( (input) => hasCloseBotUpdate(input, ["leadId", "noteId"]), "Provide a note field to update.", ), ) .output(CLOSEBOT_NOTE_RESULT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { leadId, noteId, ...body } = input return getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(leadId)}/note/${encodeURIComponent(noteId)}`, { body, method: "PATCH", responseSchema: CLOSEBOT_NOTE_RESULT_SCHEMA, }, ) }) /** Deletes an internal CloseBot conversation note. */ export const deleteCloseBotLeadNote = defineAction("Delete CloseBot lead note") .describe("Deletes a note when the connected user is its author.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ leadId: CLOSEBOT_ID_SCHEMA, noteId: CLOSEBOT_ID_SCHEMA })) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/note/${encodeURIComponent(input.noteId)}`, { method: "DELETE", responseSchema: z.null() }, ), ) /** Sends a message to a CloseBot lead. */ export const sendCloseBotLeadMessage = defineAction( "Send CloseBot lead message", ) .describe("Sends a message to a lead through its connected channel.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ body: z.string().trim().min(1), leadId: CLOSEBOT_ID_SCHEMA }), ) .output(CLOSEBOT_SEND_MESSAGE_RESULT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/send-message`, { body: { body: input.body }, method: "POST", responseSchema: CLOSEBOT_SEND_MESSAGE_RESULT_SCHEMA, }, ), ) /** Lists pending and recently failed scheduled messages for a lead. */ export const listCloseBotScheduledMessages = defineAction( "List CloseBot scheduled messages", ) .describe("Lists pending and recently failed scheduled messages for a lead.") .account(CLOSEBOT_ACCOUNT) .input(z.object({ leadId: CLOSEBOT_ID_SCHEMA })) .output(CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/scheduled-message`, { responseSchema: CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA.array() }, ), ) /** Schedules a manual or AI-generated message for a CloseBot lead. */ export const createCloseBotScheduledMessage = defineAction( "Create CloseBot scheduled message", ) .describe("Schedules a manual or AI-generated message for a lead.") .account(CLOSEBOT_ACCOUNT) .input( z .object({ body: z.string().trim().min(1).optional(), cancelOnReply: z.boolean().optional(), channel: z.string().trim().min(1).optional(), kind: z.enum(["manual", "ai"]), leadId: CLOSEBOT_ID_SCHEMA, sendAt: z.date(), }) .refine(({ body, kind }) => kind === "ai" || body !== undefined, { message: "Provide body for a manual scheduled message.", path: ["body"], }), ) .output(CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { leadId, ...body } = input return getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(leadId)}/scheduled-message`, { body, method: "POST", responseSchema: CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA, }, ) }) /** Updates a pending CloseBot scheduled message. */ export const updateCloseBotScheduledMessage = defineAction( "Update CloseBot scheduled message", ) .describe("Updates a pending scheduled message's time or behavior.") .account(CLOSEBOT_ACCOUNT) .input( z .object({ body: z.string().trim().min(1).nullable().optional(), cancelOnReply: z.boolean().nullable().optional(), leadId: CLOSEBOT_ID_SCHEMA, scheduledMessageId: CLOSEBOT_ID_SCHEMA, sendAt: z.date().nullable().optional(), }) .refine( (input) => hasCloseBotUpdate(input, ["leadId", "scheduledMessageId"]), "Provide a scheduled message field to update.", ), ) .output(CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { leadId, scheduledMessageId, ...body } = input return getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(leadId)}/scheduled-message/${encodeURIComponent(scheduledMessageId)}`, { body, method: "PATCH", responseSchema: CLOSEBOT_SCHEDULED_MESSAGE_SCHEMA, }, ) }) /** Cancels a pending CloseBot scheduled message or dismisses a failure. */ export const cancelCloseBotScheduledMessage = defineAction( "Cancel CloseBot scheduled message", ) .describe("Cancels a pending message or dismisses a failed message.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ leadId: CLOSEBOT_ID_SCHEMA, scheduledMessageId: CLOSEBOT_ID_SCHEMA, }), ) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/scheduled-message/${encodeURIComponent(input.scheduledMessageId)}`, { method: "DELETE", responseSchema: z.null() }, ), ) /** Updates the follow-up time for one lead's bot instance. */ export const updateCloseBotLeadInstance = defineAction( "Update CloseBot lead instance", ) .describe("Updates the follow-up time for one lead's bot instance.") .account(CLOSEBOT_ACCOUNT) .input( z.object({ botId: CLOSEBOT_ID_SCHEMA, followUpTime: z.date().nullable(), leadId: CLOSEBOT_ID_SCHEMA, }), ) .output(z.null()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getCloseBotApi(account.secret).request( `lead/${encodeURIComponent(input.leadId)}/instance/${encodeURIComponent(input.botId)}`, { body: { followUpTime: input.followUpTime }, method: "PUT", responseSchema: z.null(), }, ), )