import { DISCORD_INVITE_SCHEMA, DISCORD_PROVIDER_INVITE_SCHEMA, DISCORD_PROVIDER_WEBHOOK_SCHEMA, DISCORD_WEBHOOK_SCHEMA, toDiscordInvite, toDiscordWebhook, } from "@automate.ax/integration-contracts/discord" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getDiscordApi } from "../lib/api" const WEBHOOK_NAME_SCHEMA = z .string() .trim() .min(1) .max(80) .refine((name) => !/(?:discord|clyde)/i.test(name), { message: "Webhook names cannot contain Discord or Clyde.", }) /** Lists invites created for a Discord channel. */ export const listDiscordChannelInvites = defineAction( "List Discord channel invites", ) .describe("Lists active invites that target one channel.") .account("discord") .input(z.object({ channelId: z.string().regex(/^\d+$/) })) .output(DISCORD_INVITE_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getDiscordApi(account.secret).request( `channels/${input.channelId}/invites`, { responseSchema: DISCORD_PROVIDER_INVITE_SCHEMA.array() }, ) ).map(toDiscordInvite), ) /** Creates an invite to a Discord channel. */ export const createDiscordInvite = defineAction("Create Discord invite") .describe("Creates a channel invite with expiry and usage controls.") .account("discord") .input( z.object({ channelId: z.string().regex(/^\d+$/), maxAge: z.number().int().min(0).max(604_800).prefault(86_400), maxUses: z.number().int().min(0).max(100).prefault(0), reason: z.string().max(512).optional(), temporary: z.boolean().prefault(false), unique: z.boolean().prefault(false), }), ) .output(DISCORD_INVITE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toDiscordInvite( await getDiscordApi(account.secret).request( `channels/${input.channelId}/invites`, { body: { max_age: input.maxAge, max_uses: input.maxUses, temporary: input.temporary, unique: input.unique, }, method: "POST", reason: input.reason, responseSchema: DISCORD_PROVIDER_INVITE_SCHEMA, }, ), ), ) /** Revokes a Discord invite. */ export const deleteDiscordInvite = defineAction("Delete Discord invite") .describe("Revokes an invite by its code.") .account("discord") .input( z.object({ code: z.string().trim().min(1), reason: z.string().max(512).optional(), }), ) .output(DISCORD_INVITE_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toDiscordInvite( await getDiscordApi(account.secret).request( `invites/${encodeURIComponent(input.code)}`, { method: "DELETE", reason: input.reason, responseSchema: DISCORD_PROVIDER_INVITE_SCHEMA, }, ), ), ) /** Lists webhooks created in a Discord channel. */ export const listDiscordChannelWebhooks = defineAction( "List Discord channel webhooks", ) .describe( "Lists incoming and application webhooks configured for one channel.", ) .account("discord") .input(z.object({ channelId: z.string().regex(/^\d+$/) })) .output(DISCORD_WEBHOOK_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => ( await getDiscordApi(account.secret).request( `channels/${input.channelId}/webhooks`, { responseSchema: DISCORD_PROVIDER_WEBHOOK_SCHEMA.array() }, ) ).map(toDiscordWebhook), ) /** Retrieves one Discord webhook without exposing its token. */ export const getDiscordWebhook = defineAction("Get Discord webhook") .describe("Retrieves a webhook by ID without exposing its token.") .account("discord") .input(z.object({ webhookId: z.string().regex(/^\d+$/) })) .output(DISCORD_WEBHOOK_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toDiscordWebhook( await getDiscordApi(account.secret).request( `webhooks/${input.webhookId}`, { responseSchema: DISCORD_PROVIDER_WEBHOOK_SCHEMA }, ), ), ) /** Creates an incoming webhook in a Discord channel. */ export const createDiscordWebhook = defineAction("Create Discord webhook") .describe( "Creates an incoming webhook without exposing its token in action output.", ) .account("discord") .input( z.object({ avatarDataUri: z.string().startsWith("data:image/").optional(), channelId: z.string().regex(/^\d+$/), name: WEBHOOK_NAME_SCHEMA, reason: z.string().max(512).optional(), }), ) .output(DISCORD_WEBHOOK_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => toDiscordWebhook( await getDiscordApi(account.secret).request( `channels/${input.channelId}/webhooks`, { body: { avatar: input.avatarDataUri, name: input.name }, method: "POST", reason: input.reason, responseSchema: DISCORD_PROVIDER_WEBHOOK_SCHEMA, }, ), ), ) /** Updates a Discord webhook. */ export const updateDiscordWebhook = defineAction("Update Discord webhook") .describe("Changes a webhook name, avatar, or destination channel.") .account("discord") .input( z.object({ avatarDataUri: z.string().startsWith("data:image/").nullable().optional(), channelId: z.string().regex(/^\d+$/).optional(), name: WEBHOOK_NAME_SCHEMA.optional(), reason: z.string().max(512).optional(), webhookId: z.string().regex(/^\d+$/), }), ) .output(DISCORD_WEBHOOK_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => toDiscordWebhook( await getDiscordApi(account.secret).request( `webhooks/${input.webhookId}`, { body: { avatar: input.avatarDataUri, channel_id: input.channelId, name: input.name, }, method: "PATCH", reason: input.reason, responseSchema: DISCORD_PROVIDER_WEBHOOK_SCHEMA, }, ), ), ) /** Deletes a Discord webhook. */ export const deleteDiscordWebhook = defineAction("Delete Discord webhook") .describe("Deletes a webhook by ID.") .account("discord") .input( z.object({ reason: z.string().max(512).optional(), webhookId: z.string().regex(/^\d+$/), }), ) .output(z.void()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getDiscordApi(account.secret).request(`webhooks/${input.webhookId}`, { method: "DELETE", reason: input.reason, responseSchema: z.void(), }), )