Breakout slack into it’s own package
This commit is contained in:
@@ -4,7 +4,8 @@ import type {
|
||||
NormalizedResponse,
|
||||
PerformedRequestResponse,
|
||||
} from "@trigger.dev/integration-sdk";
|
||||
import { resend, shopify, slack } from "internal-integrations";
|
||||
import { resend, shopify } from "internal-integrations";
|
||||
import * as slack from "@trigger.dev/slack/internal";
|
||||
import type { PrismaClient } from "~/db.server";
|
||||
import { prisma } from "~/db.server";
|
||||
import type { IntegrationRequest } from "~/models/integrationRequest.server";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { slack } from "@trigger.dev/providers";
|
||||
import { schemas } from "@trigger.dev/slack/internal";
|
||||
import { ulid } from "ulid";
|
||||
import { generateErrorMessage } from "zod-error";
|
||||
import type { PrismaClient } from "~/db.server";
|
||||
@@ -15,7 +15,7 @@ export class HandleSlackInteractivity {
|
||||
public async call(payload: unknown) {
|
||||
console.log("payload", JSON.stringify(payload, null, 2));
|
||||
|
||||
const parsedPayload = slack.schemas.blockAction.safeParse(payload);
|
||||
const parsedPayload = schemas.blockAction.safeParse(payload);
|
||||
|
||||
if (!parsedPayload.success) {
|
||||
console.error(
|
||||
|
||||
@@ -15,6 +15,7 @@ module.exports = {
|
||||
"internal-bridge",
|
||||
"@trigger.dev/providers",
|
||||
"@trigger.dev/github",
|
||||
"@trigger.dev/slack",
|
||||
"@trigger.dev/common-schemas",
|
||||
"@trigger.dev/sdk",
|
||||
"@trigger.dev/integrations",
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
"../../packages/integration-sdk/src/*"
|
||||
],
|
||||
"@trigger.dev/github": ["../../integrations/github/src/index"],
|
||||
"@trigger.dev/github/*": ["../../integrations/github/src/*"]
|
||||
"@trigger.dev/github/*": ["../../integrations/github/src/*"],
|
||||
"@trigger.dev/slack": ["../../integrations/slack/src/index"],
|
||||
"@trigger.dev/slack/*": ["../../integrations/slack/src/*"]
|
||||
},
|
||||
"noEmit": true
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@
|
||||
],
|
||||
"@trigger.dev/github/*": [
|
||||
"../../integrations/github/src/*"
|
||||
],
|
||||
"@trigger.dev/slack": [
|
||||
"../../integrations/slack/src/index"
|
||||
],
|
||||
"@trigger.dev/slack/*": [
|
||||
"../../integrations/slack/src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "A fetch playground for testing Trigger.dev functionality",
|
||||
"dependencies": {
|
||||
"@trigger.dev/integrations": "workspace:*",
|
||||
"@trigger.dev/slack": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { slack } from "@trigger.dev/integrations";
|
||||
import * as slack from "@trigger.dev/slack";
|
||||
import { Trigger, customEvent, webhookEvent } from "@trigger.dev/sdk";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Send a message to slack on a schedule",
|
||||
"dependencies": {
|
||||
"@trigger.dev/integrations": "workspace:*",
|
||||
"@trigger.dev/slack": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Trigger, scheduleEvent } from "@trigger.dev/sdk";
|
||||
import { slack } from "@trigger.dev/integrations";
|
||||
import * as slack from "@trigger.dev/slack";
|
||||
|
||||
const trigger = new Trigger({
|
||||
id: "schedule-to-slack-2",
|
||||
@@ -11,10 +11,10 @@ const trigger = new Trigger({
|
||||
run: async (event, ctx) => {
|
||||
await ctx.logger.info("It's me, the annoying slack bot!");
|
||||
|
||||
// const response = await slack.postMessage("slaaaaaack", {
|
||||
// channelName: "test-integrations",
|
||||
// text: `Hello, the time is ${event.scheduledTime}, and I was last run at ${event.lastRunAt}!`,
|
||||
// });
|
||||
const response = await slack.postMessage("slaaaaaack", {
|
||||
channelName: "test-integrations",
|
||||
text: `Hello, the time is ${event.scheduledTime}, and I was last run at ${event.lastRunAt}!`,
|
||||
});
|
||||
|
||||
return event;
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Send a message to slack when a customer creates a new custom domain",
|
||||
"dependencies": {
|
||||
"@trigger.dev/integrations": "workspace:*",
|
||||
"@trigger.dev/slack": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"jsx-slack": "^5.3.0",
|
||||
"zod": "^3.20.2"
|
||||
@@ -17,4 +17,4 @@
|
||||
"scripts": {
|
||||
"dev": "tsx src/index.tsx"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Trigger, customEvent, scheduleEvent } from "@trigger.dev/sdk";
|
||||
import { slack } from "@trigger.dev/integrations";
|
||||
import * as slack from "@trigger.dev/slack";
|
||||
import JSXSlack, {
|
||||
Actions,
|
||||
Blocks,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Very basic smoke test for @trigger.dev/sdk",
|
||||
"dependencies": {
|
||||
"@trigger.dev/integrations": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"ulid": "^2.3.0",
|
||||
"zod": "^3.20.2"
|
||||
@@ -17,4 +16,4 @@
|
||||
"scripts": {
|
||||
"dev": "tsx src/index.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { GitHubWebhookIntegration } from "./webhooks";
|
||||
import * as schemas from "./schemas";
|
||||
import { GitHubWebhookIntegration } from "./internal/webhooks";
|
||||
|
||||
export const provider = {
|
||||
export const webhooks = new GitHubWebhookIntegration();
|
||||
|
||||
export const metadata = {
|
||||
name: "GitHub",
|
||||
slug: "github",
|
||||
icon: "/integrations/github.png",
|
||||
@@ -10,7 +11,6 @@ export const provider = {
|
||||
type: "oauth",
|
||||
scopes: ["repo"],
|
||||
},
|
||||
schemas,
|
||||
};
|
||||
|
||||
export const webhooks = new GitHubWebhookIntegration();
|
||||
export * as schemas from "./schemas";
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
WebhookOrganizationSourceSchema,
|
||||
WebhookRepoSourceSchema,
|
||||
WebhookSourceSchema,
|
||||
} from "./schemas";
|
||||
} from "../schemas";
|
||||
|
||||
export class GitHubWebhookIntegration implements WebhookIntegration {
|
||||
keyForSource(source: unknown): string {
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "@trigger.dev/tsconfig/node16.json",
|
||||
"include": ["./src/**/*.ts"],
|
||||
"include": ["./src/**/*.ts", "tsup.config.ts"],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Trigger.dev GitHub integration
|
||||
|
||||
View more documentation [here](https://docs.trigger.dev)
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"display_information": {
|
||||
"name": "Trigger.dev"
|
||||
},
|
||||
"features": {
|
||||
"bot_user": {
|
||||
"display_name": "Trigger.dev",
|
||||
"always_online": false
|
||||
}
|
||||
},
|
||||
"oauth_config": {
|
||||
"redirect_urls": [
|
||||
"https://auth.trigger.dev/oauth/callback"
|
||||
],
|
||||
"scopes": {
|
||||
"user": [
|
||||
"channels:read",
|
||||
"groups:read",
|
||||
"im:read",
|
||||
"mpim:read",
|
||||
"chat:write",
|
||||
"reactions:write"
|
||||
],
|
||||
"bot": [
|
||||
"chat:write",
|
||||
"reactions:write"
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"org_deploy_enabled": false,
|
||||
"socket_mode_enabled": false,
|
||||
"token_rotation_enabled": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "0.1.16",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@trigger.dev/integration-sdk": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/node": "16",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsup": "^6.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:*"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npm run build:tsup",
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/webhooks": "^10.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"zod": "^3.20.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,861 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const imageElementSchema = z.object({
|
||||
type: z.literal("image"),
|
||||
image_url: z.string(),
|
||||
alt_text: z.string(),
|
||||
});
|
||||
|
||||
export const plainTextElementSchema = z.object({
|
||||
type: z.literal("plain_text"),
|
||||
text: z.string(),
|
||||
emoji: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const mrkdwnElementSchema = z.object({
|
||||
type: z.literal("mrkdwn"),
|
||||
text: z.string(),
|
||||
verbatim: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const mrkdwnOptionSchema = z.object({
|
||||
text: mrkdwnElementSchema,
|
||||
value: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
description: plainTextElementSchema.optional(),
|
||||
});
|
||||
|
||||
export const plainTextOptionSchema = z.object({
|
||||
text: plainTextElementSchema,
|
||||
value: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
description: plainTextElementSchema.optional(),
|
||||
});
|
||||
|
||||
export const optionSchema = z.union([
|
||||
mrkdwnOptionSchema,
|
||||
plainTextOptionSchema,
|
||||
]);
|
||||
|
||||
export const confirmSchema = z.object({
|
||||
title: plainTextElementSchema.optional(),
|
||||
text: z.discriminatedUnion("type", [
|
||||
plainTextElementSchema,
|
||||
mrkdwnElementSchema,
|
||||
]),
|
||||
confirm: plainTextElementSchema.optional(),
|
||||
deny: plainTextElementSchema.optional(),
|
||||
style: z.union([z.literal("primary"), z.literal("danger")]).optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Determines when an input element will return a
|
||||
* {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions` interaction payload}.
|
||||
*/
|
||||
export const dispatchActionConfigSchema = z.object({
|
||||
/**
|
||||
* @description An array of interaction types that you would like to receive a
|
||||
* {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions` payload} for. Should be
|
||||
* one or both of:
|
||||
* `on_enter_pressed` — payload is dispatched when user presses the enter key while the input is in focus. Hint
|
||||
* text will appear underneath the input explaining to the user to press enter to submit.
|
||||
* `on_character_entered` — payload is dispatched when a character is entered (or removed) in the input.
|
||||
*/
|
||||
trigger_actions_on: z
|
||||
.array(
|
||||
z.union([
|
||||
z.literal("on_enter_pressed"),
|
||||
z.literal("on_character_entered"),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const actionSchema = z.object({
|
||||
type: z.string(),
|
||||
/**
|
||||
* @description: An identifier for this action. You can use this when you receive an interaction payload to
|
||||
* {@link https://api.slack.com/interactivity/hmergeling#payloads identify the source of the action}. Should be unique
|
||||
* among all other `action_id`s in the containing block. Maximum length for this field is 255 characters.
|
||||
*/
|
||||
action_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const actionIdSchema = z.object({
|
||||
/**
|
||||
* @description: An identifier for this action. You can use this when you receive an interaction payload to
|
||||
* {@link https://api.slack.com/interactivity/hmergeling#payloads identify the source of the action}. Should be unique
|
||||
* among all other `action_id`s in the containing block. Maximum length for this field is 255 characters.
|
||||
*/
|
||||
action_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const confirmableSchema = z.object({
|
||||
/**
|
||||
* @description A {@see Confirm} object that defines an optional confirmation dialog after the element is interacted
|
||||
* with.
|
||||
*/
|
||||
confirm: confirmSchema.optional(),
|
||||
});
|
||||
|
||||
export const focusableSchema = z.object({
|
||||
/**
|
||||
* @description Indicates whether the element will be set to auto focus within the
|
||||
* {@link https://api.slack.com/reference/surfaces/views `view` object}. Only one element can be set to `true`.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
focus_on_load: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const placeholdableSchema = z.object({
|
||||
/**
|
||||
* @description A {@see PlainTextElement} object that defines the placeholder text shown on the element. Maximum
|
||||
* length for the `text` field in this object is 150 characters.
|
||||
*/
|
||||
placeholder: plainTextElementSchema.optional(),
|
||||
});
|
||||
|
||||
export const dispatchableSchema = z.object({
|
||||
/**
|
||||
* @description A {@see DispatchActionConfig} object that determines when during text input the element returns a
|
||||
* {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions` payload}.
|
||||
*/
|
||||
dispatch_action_config: dispatchActionConfigSchema.optional(),
|
||||
});
|
||||
|
||||
export const usersSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("users_select"),
|
||||
initial_user: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const multiUsersSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("multi_users_select"),
|
||||
initial_users: z.array(z.string()).optional(),
|
||||
max_selected_items: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const staticSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("static_select"),
|
||||
initial_option: plainTextOptionSchema.optional(),
|
||||
options: z.array(plainTextOptionSchema).optional(),
|
||||
option_groups: z
|
||||
.array(
|
||||
z.object({
|
||||
label: plainTextElementSchema,
|
||||
options: z.array(plainTextOptionSchema),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const multiStaticSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("multi_static_select"),
|
||||
initial_options: z.array(plainTextOptionSchema).optional(),
|
||||
options: z.array(plainTextOptionSchema).optional(),
|
||||
option_groups: z
|
||||
.array(
|
||||
z.object({
|
||||
label: plainTextElementSchema,
|
||||
options: z.array(plainTextOptionSchema),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
max_selected_items: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const conversationsSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("conversations_select"),
|
||||
initial_conversation: z.string().optional(),
|
||||
response_url_enabled: z.boolean().optional(),
|
||||
default_to_current_conversation: z.boolean().optional(),
|
||||
filter: z
|
||||
.object({
|
||||
include: z
|
||||
.array(
|
||||
z.union([
|
||||
z.literal("im"),
|
||||
z.literal("mpim"),
|
||||
z.literal("private"),
|
||||
z.literal("public"),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
exclude_external_shared_channels: z.boolean().optional(),
|
||||
exclude_bot_users: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const multiConversationsSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("multi_conversations_select"),
|
||||
initial_conversations: z.array(z.string()).optional(),
|
||||
max_selected_items: z.number().optional(),
|
||||
default_to_current_conversation: z.boolean().optional(),
|
||||
filter: z
|
||||
.object({
|
||||
include: z
|
||||
.array(
|
||||
z.union([
|
||||
z.literal("im"),
|
||||
z.literal("mpim"),
|
||||
z.literal("private"),
|
||||
z.literal("public"),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
exclude_external_shared_channels: z.boolean().optional(),
|
||||
exclude_bot_users: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const channelsSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("channels_select"),
|
||||
initial_channel: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const multiChannelsSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("multi_channels_select"),
|
||||
initial_channels: z.array(z.string()).optional(),
|
||||
max_selected_items: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const externalSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("external_select"),
|
||||
initial_option: plainTextOptionSchema.optional(),
|
||||
min_query_length: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const multiExternalSelectSchema = z
|
||||
.object({
|
||||
type: z.literal("multi_external_select"),
|
||||
initial_options: z.array(plainTextOptionSchema).optional(),
|
||||
min_query_length: z.number().optional(),
|
||||
max_selected_items: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const buttonSchema = z
|
||||
.object({
|
||||
type: z.literal("button"),
|
||||
text: plainTextElementSchema,
|
||||
value: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
style: z.union([z.literal("danger"), z.literal("primary")]).optional(),
|
||||
accessibility_label: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema);
|
||||
|
||||
export const overflowSchema = z
|
||||
.object({
|
||||
type: z.literal("overflow"),
|
||||
options: z.array(plainTextOptionSchema),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema);
|
||||
|
||||
export const datepickerSchema = z
|
||||
.object({
|
||||
type: z.literal("datepicker"),
|
||||
initial_date: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const timepickerSchema = z
|
||||
.object({
|
||||
type: z.literal("timepicker"),
|
||||
initial_time: z.string().optional(),
|
||||
timezone: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const radioButtonsSchema = z
|
||||
.object({
|
||||
type: z.literal("radio_buttons"),
|
||||
initial_option: optionSchema.optional(),
|
||||
options: z.array(optionSchema),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema);
|
||||
|
||||
/**
|
||||
* @description An element that allows the selection of a time of day formatted as a UNIX timestamp. On desktop
|
||||
* clients, this time picker will take the form of a dropdown list merge the date picker will take the form of a dropdown
|
||||
* calendar. Both options will have free-text entry for precise choices. On mobile clients, the time picker merge date
|
||||
* picker will use native UIs.
|
||||
* {@link https://api.slack.com/reference/block-kit/block-elements#datetimepicker}
|
||||
*/
|
||||
export const dateTimepickerSchema = z
|
||||
.object({
|
||||
type: z.literal("datetimepicker"),
|
||||
/**
|
||||
* @description The initial date merge time that is selected when the element is loaded, represented as a UNIX
|
||||
* timestamp in seconds. This should be in the format of 10 digits, for example 1628633820 represents the date merge
|
||||
* time August 10th, 2021 at 03:17pm PST.
|
||||
*/
|
||||
initial_date_time: z.number().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema);
|
||||
|
||||
export const checkboxesSchema = z
|
||||
.object({
|
||||
type: z.literal("checkboxes"),
|
||||
initial_options: z.array(optionSchema).optional(),
|
||||
options: z.array(optionSchema),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(confirmableSchema)
|
||||
.merge(focusableSchema);
|
||||
|
||||
export const plainTextInputSchema = z
|
||||
.object({
|
||||
type: z.literal("plain_text_input"),
|
||||
initial_value: z.string().optional(),
|
||||
multiline: z.boolean().optional(),
|
||||
min_length: z.number().optional(),
|
||||
max_length: z.number().optional(),
|
||||
dispatch_action_config: dispatchActionConfigSchema.optional(),
|
||||
focus_on_load: z.boolean().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(dispatchableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
/**
|
||||
* @description A URL input element, similar to the {@see PlainTextInput} element, creates a single line field where
|
||||
* a user can enter URL-encoded data.
|
||||
* {@link https://api.slack.com/reference/block-kit/block-elements#url}
|
||||
*/
|
||||
export const uRLInputSchema = z
|
||||
.object({
|
||||
type: z.literal("url_text_input"),
|
||||
/**
|
||||
* @description The initial value in the URL input when it is loaded.
|
||||
*/
|
||||
initial_value: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(dispatchableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
/**
|
||||
* @description An email input element, similar to the {@see PlainTextInput} element, creates a single line field where
|
||||
* a user can enter an email address.
|
||||
* {@link https://api.slack.com/reference/block-kit/block-elements#email}
|
||||
*/
|
||||
export const emailInputSchema = z
|
||||
.object({
|
||||
type: z.literal("email_text_input"),
|
||||
/**
|
||||
* @description The initial value in the email input when it is loaded.
|
||||
*/
|
||||
initial_value: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(dispatchableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
/**
|
||||
* @description A number input element, similar to the {@see PlainTextInput} element, creates a single line field where
|
||||
* a user can a number. This input elements accepts floating point numbers, for example, 0.25, 5.5, merge -10 are all
|
||||
* valid input values. Decimal numbers are only allowed when `is_decimal_allowed` is equal to `true`.
|
||||
* {@link https://api.slack.com/reference/block-kit/block-elements#number}
|
||||
*/
|
||||
export const numberInputSchema = z
|
||||
.object({
|
||||
type: z.literal("number_input"),
|
||||
/**
|
||||
* @description Decimal numbers are allowed if this property is `true`, set the value to `false` otherwise.
|
||||
*/
|
||||
is_decimal_allowed: z.boolean(),
|
||||
/**
|
||||
* @description The initial value in the input when it is loaded.
|
||||
*/
|
||||
initial_value: z.string().optional(),
|
||||
/**
|
||||
* @description The minimum value, cannot be greater than `max_value`.
|
||||
*/
|
||||
min_value: z.string().optional(),
|
||||
/**
|
||||
* @description The maximum value, cannot be less than `min_value`.
|
||||
*/
|
||||
max_value: z.string().optional(),
|
||||
})
|
||||
.merge(actionIdSchema)
|
||||
.merge(dispatchableSchema)
|
||||
.merge(focusableSchema)
|
||||
.merge(placeholdableSchema);
|
||||
|
||||
export const blockSchema = z.object({
|
||||
type: z.string(),
|
||||
block_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const imageBlockSchema = blockSchema.extend({
|
||||
type: z.literal("image"),
|
||||
image_url: z.string(),
|
||||
alt_text: z.string(),
|
||||
title: plainTextElementSchema.optional(),
|
||||
});
|
||||
|
||||
export const contextBlockSchema = blockSchema.extend({
|
||||
type: z.literal("context"),
|
||||
elements: z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
imageElementSchema,
|
||||
plainTextElementSchema,
|
||||
mrkdwnElementSchema,
|
||||
])
|
||||
),
|
||||
});
|
||||
|
||||
export const dividerBlockSchema = blockSchema.extend({
|
||||
type: z.literal("divider"),
|
||||
});
|
||||
|
||||
export const fileBlockSchema = blockSchema.extend({
|
||||
type: z.literal("file"),
|
||||
source: z.string(),
|
||||
external_id: z.string(),
|
||||
});
|
||||
|
||||
export const headerBlockSchema = blockSchema.extend({
|
||||
type: z.literal("header"),
|
||||
text: plainTextElementSchema,
|
||||
});
|
||||
|
||||
export const messageMetadataEventPayloadObjectSchema = z.record(
|
||||
z.union([z.string(), z.number(), z.boolean()])
|
||||
);
|
||||
|
||||
export const messageAttachmentPreviewSchema = z.object({
|
||||
type: z.string().optional(),
|
||||
can_remove: z.boolean().optional(),
|
||||
title: plainTextElementSchema.optional(),
|
||||
subtitle: plainTextElementSchema.optional(),
|
||||
iconUrl: z.string().optional(),
|
||||
});
|
||||
|
||||
export const optionFieldSchema = z.object({
|
||||
description: z.string().optional(),
|
||||
text: z.string(),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
export const confirmationSchema = z.object({
|
||||
dismiss_text: z.string().optional(),
|
||||
ok_text: z.string().optional(),
|
||||
text: z.string(),
|
||||
title: z.string().optional(),
|
||||
});
|
||||
|
||||
export const selectOptionSchema = z.object({
|
||||
label: z.string(),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
export const callUserSlackSchema = z.object({
|
||||
slack_id: z.string(),
|
||||
});
|
||||
|
||||
export const callUserExternalSchema = z.object({
|
||||
external_id: z.string(),
|
||||
display_name: z.string(),
|
||||
avatar_url: z.string(),
|
||||
});
|
||||
|
||||
export const videoBlockSchema = blockSchema.extend({
|
||||
type: z.literal("video"),
|
||||
video_url: z.string(),
|
||||
thumbnail_url: z.string(),
|
||||
alt_text: z.string(),
|
||||
title: plainTextElementSchema,
|
||||
title_url: z.string().optional(),
|
||||
author_name: z.string().optional(),
|
||||
provider_name: z.string().optional(),
|
||||
provider_icon_url: z.string().optional(),
|
||||
description: plainTextElementSchema.optional(),
|
||||
});
|
||||
|
||||
export const dialogSchema = z.object({
|
||||
title: z.string(),
|
||||
callback_id: z.string(),
|
||||
elements: z.array(
|
||||
z.object({
|
||||
type: z.union([
|
||||
z.literal("text"),
|
||||
z.literal("textarea"),
|
||||
z.literal("select"),
|
||||
]),
|
||||
name: z.string(),
|
||||
label: z.string(),
|
||||
optional: z.boolean().optional(),
|
||||
placeholder: z.string().optional(),
|
||||
value: z.string().optional(),
|
||||
max_length: z.number().optional(),
|
||||
min_length: z.number().optional(),
|
||||
hint: z.string().optional(),
|
||||
subtype: z
|
||||
.union([
|
||||
z.literal("email"),
|
||||
z.literal("number"),
|
||||
z.literal("tel"),
|
||||
z.literal("url"),
|
||||
])
|
||||
.optional(),
|
||||
data_source: z
|
||||
.union([
|
||||
z.literal("users"),
|
||||
z.literal("channels"),
|
||||
z.literal("conversations"),
|
||||
z.literal("external"),
|
||||
])
|
||||
.optional(),
|
||||
selected_options: z.array(selectOptionSchema).optional(),
|
||||
options: z.array(selectOptionSchema).optional(),
|
||||
option_groups: z
|
||||
.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
options: z.array(selectOptionSchema),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
min_query_length: z.number().optional(),
|
||||
})
|
||||
),
|
||||
submit_label: z.string().optional(),
|
||||
notify_on_cancel: z.boolean().optional(),
|
||||
state: z.string().optional(),
|
||||
});
|
||||
|
||||
const selectSchemas = [
|
||||
usersSelectSchema,
|
||||
staticSelectSchema,
|
||||
conversationsSelectSchema,
|
||||
channelsSelectSchema,
|
||||
externalSelectSchema,
|
||||
];
|
||||
|
||||
export const selectSchema = z.discriminatedUnion("type", [
|
||||
usersSelectSchema,
|
||||
staticSelectSchema,
|
||||
conversationsSelectSchema,
|
||||
channelsSelectSchema,
|
||||
externalSelectSchema,
|
||||
]);
|
||||
|
||||
const multiSelectSchemas = [
|
||||
multiUsersSelectSchema,
|
||||
multiStaticSelectSchema,
|
||||
multiConversationsSelectSchema,
|
||||
multiChannelsSelectSchema,
|
||||
multiExternalSelectSchema,
|
||||
];
|
||||
export const multiSelectSchema = z.discriminatedUnion("type", [
|
||||
multiUsersSelectSchema,
|
||||
multiStaticSelectSchema,
|
||||
multiConversationsSelectSchema,
|
||||
multiChannelsSelectSchema,
|
||||
multiExternalSelectSchema,
|
||||
]);
|
||||
|
||||
export const actionsBlockSchema = blockSchema.extend({
|
||||
type: z.literal("actions"),
|
||||
elements: z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
buttonSchema,
|
||||
overflowSchema,
|
||||
datepickerSchema,
|
||||
timepickerSchema,
|
||||
dateTimepickerSchema,
|
||||
...selectSchemas,
|
||||
radioButtonsSchema,
|
||||
checkboxesSchema,
|
||||
])
|
||||
),
|
||||
});
|
||||
|
||||
export const sectionBlockSchema = blockSchema.extend({
|
||||
type: z.literal("section"),
|
||||
text: z
|
||||
.discriminatedUnion("type", [plainTextElementSchema, mrkdwnElementSchema])
|
||||
.optional(),
|
||||
fields: z
|
||||
.array(
|
||||
z.discriminatedUnion("type", [
|
||||
plainTextElementSchema,
|
||||
mrkdwnElementSchema,
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
accessory: z
|
||||
.discriminatedUnion("type", [
|
||||
buttonSchema,
|
||||
overflowSchema,
|
||||
datepickerSchema,
|
||||
timepickerSchema,
|
||||
...selectSchemas,
|
||||
...multiSelectSchemas,
|
||||
imageElementSchema,
|
||||
radioButtonsSchema,
|
||||
checkboxesSchema,
|
||||
])
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const inputBlockSchema = blockSchema.extend({
|
||||
type: z.literal("input"),
|
||||
label: plainTextElementSchema,
|
||||
hint: plainTextElementSchema.optional(),
|
||||
optional: z.boolean().optional(),
|
||||
element: z.discriminatedUnion("type", [
|
||||
datepickerSchema,
|
||||
...selectSchemas,
|
||||
...multiSelectSchemas,
|
||||
timepickerSchema,
|
||||
dateTimepickerSchema,
|
||||
plainTextInputSchema,
|
||||
uRLInputSchema,
|
||||
emailInputSchema,
|
||||
numberInputSchema,
|
||||
radioButtonsSchema,
|
||||
checkboxesSchema,
|
||||
]),
|
||||
dispatch_action: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const messageMetadataSchema = z.object({
|
||||
event_type: z.string(),
|
||||
event_payload: z.record(
|
||||
z.union([
|
||||
z.string(),
|
||||
z.number(),
|
||||
z.boolean(),
|
||||
messageMetadataEventPayloadObjectSchema,
|
||||
z.array(messageMetadataEventPayloadObjectSchema),
|
||||
])
|
||||
),
|
||||
});
|
||||
|
||||
export const attachmentActionSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
confirm: confirmationSchema.optional(),
|
||||
data_source: z
|
||||
.union([
|
||||
z.literal("static"),
|
||||
z.literal("channels"),
|
||||
z.literal("conversations"),
|
||||
z.literal("users"),
|
||||
z.literal("external"),
|
||||
])
|
||||
.optional(),
|
||||
min_query_length: z.number().optional(),
|
||||
name: z.string().optional(),
|
||||
options: z.array(optionFieldSchema).optional(),
|
||||
option_groups: z
|
||||
.array(
|
||||
z.object({
|
||||
text: z.string(),
|
||||
options: z.array(optionFieldSchema),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
selected_options: z.array(optionFieldSchema).optional(),
|
||||
style: z
|
||||
.union([z.literal("default"), z.literal("primary"), z.literal("danger")])
|
||||
.optional(),
|
||||
text: z.string(),
|
||||
type: z.union([z.literal("button"), z.literal("select")]),
|
||||
value: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
});
|
||||
|
||||
export const callUserSchema = z.union([
|
||||
callUserSlackSchema,
|
||||
callUserExternalSchema,
|
||||
]);
|
||||
|
||||
const knownBlocks = [
|
||||
imageBlockSchema,
|
||||
contextBlockSchema,
|
||||
actionsBlockSchema,
|
||||
dividerBlockSchema,
|
||||
sectionBlockSchema,
|
||||
inputBlockSchema,
|
||||
fileBlockSchema,
|
||||
headerBlockSchema,
|
||||
videoBlockSchema,
|
||||
];
|
||||
|
||||
export const knownBlockSchema = z.discriminatedUnion("type", [
|
||||
imageBlockSchema,
|
||||
contextBlockSchema,
|
||||
actionsBlockSchema,
|
||||
dividerBlockSchema,
|
||||
sectionBlockSchema,
|
||||
inputBlockSchema,
|
||||
fileBlockSchema,
|
||||
headerBlockSchema,
|
||||
videoBlockSchema,
|
||||
]);
|
||||
|
||||
const anyBlockSchema = z.discriminatedUnion("type", [
|
||||
imageBlockSchema,
|
||||
contextBlockSchema,
|
||||
actionsBlockSchema,
|
||||
dividerBlockSchema,
|
||||
sectionBlockSchema,
|
||||
inputBlockSchema,
|
||||
fileBlockSchema,
|
||||
headerBlockSchema,
|
||||
videoBlockSchema,
|
||||
]);
|
||||
|
||||
export const messageAttachmentSchema = z.object({
|
||||
blocks: z.array(anyBlockSchema).optional(),
|
||||
fallback: z.string().optional(),
|
||||
color: z
|
||||
.union([
|
||||
z.literal("good"),
|
||||
z.literal("warning"),
|
||||
z.literal("danger"),
|
||||
z.string(),
|
||||
])
|
||||
.optional(),
|
||||
pretext: z.string().optional(),
|
||||
author_name: z.string().optional(),
|
||||
author_link: z.string().optional(),
|
||||
author_icon: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
title_link: z.string().optional(),
|
||||
text: z.string().optional(),
|
||||
fields: z
|
||||
.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
value: z.string(),
|
||||
short: z.boolean().optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
image_url: z.string().optional(),
|
||||
thumb_url: z.string().optional(),
|
||||
footer: z.string().optional(),
|
||||
footer_icon: z.string().optional(),
|
||||
ts: z.string().optional(),
|
||||
actions: z.array(attachmentActionSchema).optional(),
|
||||
callback_id: z.string().optional(),
|
||||
mrkdwn_in: z
|
||||
.array(
|
||||
z.union([z.literal("pretext"), z.literal("text"), z.literal("fields")])
|
||||
)
|
||||
.optional(),
|
||||
app_unfurl_url: z.string().optional(),
|
||||
is_app_unfurl: z.boolean().optional(),
|
||||
app_id: z.string().optional(),
|
||||
bot_id: z.string().optional(),
|
||||
preview: messageAttachmentPreviewSchema.optional(),
|
||||
});
|
||||
|
||||
export const linkUnfurlsSchema = z.record(messageAttachmentSchema);
|
||||
|
||||
export const homeViewSchema = z.object({
|
||||
type: z.literal("home"),
|
||||
blocks: z.array(anyBlockSchema),
|
||||
private_metadata: z.string().optional(),
|
||||
callback_id: z.string().optional(),
|
||||
external_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const modalViewSchema = z.object({
|
||||
type: z.literal("modal"),
|
||||
title: plainTextElementSchema,
|
||||
blocks: z.array(anyBlockSchema),
|
||||
close: plainTextElementSchema.optional(),
|
||||
submit: plainTextElementSchema.optional(),
|
||||
private_metadata: z.string().optional(),
|
||||
callback_id: z.string().optional(),
|
||||
clear_on_close: z.boolean().optional(),
|
||||
notify_on_close: z.boolean().optional(),
|
||||
external_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const workflowStepViewSchema = z.object({
|
||||
type: z.literal("workflow_step"),
|
||||
blocks: z.array(anyBlockSchema),
|
||||
private_metadata: z.string().optional(),
|
||||
callback_id: z.string().optional(),
|
||||
submit_disabled: z.boolean().optional(),
|
||||
external_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const viewSchema: z.ZodDiscriminatedUnion<
|
||||
"type",
|
||||
[typeof homeViewSchema, typeof modalViewSchema, typeof workflowStepViewSchema]
|
||||
> = z.discriminatedUnion("type", [
|
||||
homeViewSchema,
|
||||
modalViewSchema,
|
||||
workflowStepViewSchema,
|
||||
]);
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { TriggerEvent } from "@trigger.dev/sdk";
|
||||
import { blockAction } from "./interactivity";
|
||||
|
||||
export function blockActionInteraction(params: {
|
||||
blockId: string;
|
||||
actionId?: string | string[];
|
||||
}): TriggerEvent<typeof blockAction> {
|
||||
const actionIds =
|
||||
typeof params.actionId === "undefined"
|
||||
? []
|
||||
: Array.isArray(params.actionId)
|
||||
? params.actionId
|
||||
: [params.actionId];
|
||||
|
||||
return {
|
||||
metadata: {
|
||||
type: "SLACK_INTERACTION",
|
||||
service: "slack",
|
||||
name: "block.action",
|
||||
filter: {
|
||||
service: ["slack"],
|
||||
payload: {
|
||||
actions: {
|
||||
block_id: [params.blockId],
|
||||
action_id: actionIds,
|
||||
},
|
||||
},
|
||||
event: ["block.action"],
|
||||
},
|
||||
source: {
|
||||
blockId: params.blockId,
|
||||
actionIds,
|
||||
},
|
||||
},
|
||||
schema: blockAction,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { getTriggerRun } from "@trigger.dev/sdk";
|
||||
import { z } from "zod";
|
||||
import * as events from "./events";
|
||||
import * as schemas from "./schemas";
|
||||
|
||||
export { events };
|
||||
|
||||
export type PostMessageOptions = z.infer<
|
||||
typeof schemas.PostMessageOptionsSchema
|
||||
>;
|
||||
|
||||
export type PostMessageResponse = z.infer<
|
||||
typeof schemas.PostMessageSuccessResponseSchema
|
||||
>;
|
||||
|
||||
export async function postMessage(
|
||||
key: string,
|
||||
message: PostMessageOptions
|
||||
): Promise<PostMessageResponse> {
|
||||
const run = getTriggerRun();
|
||||
|
||||
if (!run) {
|
||||
throw new Error("Cannot call postMessage outside of a trigger run");
|
||||
}
|
||||
|
||||
const output = await run.performRequest(key, {
|
||||
service: "slack",
|
||||
endpoint: "chat.postMessage",
|
||||
params: message,
|
||||
response: {
|
||||
schema: schemas.PostMessageSuccessResponseSchema,
|
||||
},
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export type PostMessageResponseOptions = z.infer<
|
||||
typeof schemas.PostMessageResponseOptionsSchema
|
||||
>;
|
||||
|
||||
export type PostMessageResponseResponse = z.infer<
|
||||
typeof schemas.PostMessageResponseSuccessResponseSchema
|
||||
>;
|
||||
|
||||
export async function postMessageResponse(
|
||||
key: string,
|
||||
responseUrl: string,
|
||||
message: PostMessageResponseOptions
|
||||
): Promise<PostMessageResponseResponse> {
|
||||
const run = getTriggerRun();
|
||||
|
||||
if (!run) {
|
||||
throw new Error("Cannot call postMessageResponse outside of a trigger run");
|
||||
}
|
||||
|
||||
const output = await run.performRequest(key, {
|
||||
service: "slack",
|
||||
endpoint: "chat.postMessageResponse",
|
||||
params: { message, responseUrl },
|
||||
response: {
|
||||
schema: schemas.PostMessageResponseSuccessResponseSchema,
|
||||
},
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export type AddReactionOptions = z.infer<
|
||||
typeof schemas.AddReactionOptionsSchema
|
||||
>;
|
||||
|
||||
export type AddReactionResponse = z.infer<
|
||||
typeof schemas.AddReactionSuccessResponseSchema
|
||||
>;
|
||||
|
||||
export async function addReaction(
|
||||
key: string,
|
||||
options: AddReactionOptions
|
||||
): Promise<AddReactionResponse> {
|
||||
const run = getTriggerRun();
|
||||
|
||||
if (!run) {
|
||||
throw new Error("Cannot call addReaction outside of a trigger run");
|
||||
}
|
||||
|
||||
const output = await run.performRequest(key, {
|
||||
service: "slack",
|
||||
endpoint: "reactions.add",
|
||||
params: options,
|
||||
response: {
|
||||
schema: schemas.AddReactionSuccessResponseSchema,
|
||||
},
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
knownBlockSchema,
|
||||
mrkdwnElementSchema,
|
||||
optionFieldSchema,
|
||||
plainTextElementSchema,
|
||||
viewSchema,
|
||||
} from "./blocks";
|
||||
|
||||
const textSchema = z.discriminatedUnion("type", [
|
||||
plainTextElementSchema,
|
||||
mrkdwnElementSchema,
|
||||
]);
|
||||
const blockActionType = z.union([
|
||||
z.literal("block_actions"),
|
||||
z.literal("interactive_message"),
|
||||
]);
|
||||
|
||||
const sourceType = z.literal("message");
|
||||
|
||||
const commonActionSchema = z.object({
|
||||
action_id: z.string(),
|
||||
block_id: z.string(),
|
||||
action_ts: z.string(),
|
||||
});
|
||||
|
||||
const buttonAction = z.object({
|
||||
type: z.literal("button"),
|
||||
text: textSchema.optional(),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const selectedOptionSchema = z.object({
|
||||
text: z.object({
|
||||
type: z.string(),
|
||||
text: z.string(),
|
||||
emoji: z.boolean().optional(),
|
||||
}),
|
||||
value: z.string(),
|
||||
});
|
||||
const placeholderSchema = z.object({
|
||||
type: z.string(),
|
||||
text: z.string(),
|
||||
emoji: z.boolean(),
|
||||
});
|
||||
const staticSelectAction = z.object({
|
||||
type: z.literal("static_select"),
|
||||
selected_option: selectedOptionSchema.nullable(),
|
||||
placeholder: placeholderSchema.optional(),
|
||||
});
|
||||
|
||||
const userSelectAction = z.object({
|
||||
type: z.literal("users_select"),
|
||||
selected_user: z.string().nullable(),
|
||||
initial_user: z.string().optional(),
|
||||
});
|
||||
|
||||
const conversationsSelectAction = z.object({
|
||||
type: z.literal("conversations_select"),
|
||||
selected_conversation: z.string().nullable(),
|
||||
initial_conversation: z.string().optional(),
|
||||
});
|
||||
const channelSelectAction = z.object({
|
||||
type: z.literal("channels_select"),
|
||||
selected_channel: z.string().nullable(),
|
||||
initial_channel: z.string().optional(),
|
||||
});
|
||||
|
||||
const datePickerAction = z.object({
|
||||
type: z.literal("datepicker"),
|
||||
selected_date: z.string().nullable(),
|
||||
initial_date: z.string().optional(),
|
||||
});
|
||||
|
||||
const checkboxesAction = z.object({
|
||||
type: z.literal("checkboxes"),
|
||||
selected_options: z.array(optionFieldSchema),
|
||||
});
|
||||
|
||||
const radioButtonsSchema = z.object({
|
||||
type: z.literal("radio_buttons"),
|
||||
selectedOption: optionFieldSchema,
|
||||
});
|
||||
|
||||
const timePickerSchema = z.object({
|
||||
type: z.literal("timepicker"),
|
||||
selected_time: z.string().nullable(),
|
||||
initial_time: z.string().optional(),
|
||||
});
|
||||
|
||||
const plainTextInputSchema = z.object({
|
||||
type: z.literal("plain_text_input"),
|
||||
value: z.string().nullable(),
|
||||
initial_value: z.string().optional(),
|
||||
});
|
||||
|
||||
const multiUsersSelectSchema = z.object({
|
||||
type: z.literal("multi_users_select"),
|
||||
selected_users: z.array(z.string()),
|
||||
initial_users: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const multiStaticSelectSchema = z.object({
|
||||
type: z.literal("multi_static_select"),
|
||||
selected_options: z.array(selectedOptionSchema),
|
||||
placeholder: placeholderSchema.optional(),
|
||||
});
|
||||
|
||||
const possibleActionsSchema = z.discriminatedUnion("type", [
|
||||
buttonAction,
|
||||
staticSelectAction,
|
||||
userSelectAction,
|
||||
conversationsSelectAction,
|
||||
channelSelectAction,
|
||||
datePickerAction,
|
||||
checkboxesAction,
|
||||
radioButtonsSchema,
|
||||
timePickerSchema,
|
||||
plainTextInputSchema,
|
||||
multiUsersSelectSchema,
|
||||
multiStaticSelectSchema,
|
||||
]);
|
||||
|
||||
const actionSchema = possibleActionsSchema.and(commonActionSchema);
|
||||
|
||||
//state.values.issue.action.block.rating.selected_option
|
||||
const stateSchema = z.object({
|
||||
values: z.record(z.record(possibleActionsSchema)),
|
||||
});
|
||||
|
||||
const userSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
name: z.string(),
|
||||
team_id: z.string(),
|
||||
});
|
||||
|
||||
const containerSchema = z.object({
|
||||
type: sourceType,
|
||||
message_ts: z.string(),
|
||||
channel_id: z.string(),
|
||||
is_ephemeral: z.boolean(),
|
||||
});
|
||||
|
||||
const teamSchema = z.object({ id: z.string(), domain: z.string() });
|
||||
const channelSchema = z.object({ id: z.string(), name: z.string() });
|
||||
|
||||
const viewActionDataSchema = z.object({
|
||||
id: z.string(),
|
||||
team_id: z.string(),
|
||||
state: z
|
||||
.object({
|
||||
values: z.record(z.record(z.any())),
|
||||
})
|
||||
.optional(),
|
||||
hash: z.string(),
|
||||
previous_view_id: z.string().optional(),
|
||||
root_view_id: z.string().optional(),
|
||||
app_id: z.string().optional(),
|
||||
app_installed_team_id: z.string().optional(),
|
||||
bot_id: z.string().optional(),
|
||||
});
|
||||
const viewActionSchema: Zod.ZodIntersection<
|
||||
typeof viewSchema,
|
||||
typeof viewActionDataSchema
|
||||
> = viewSchema.and(viewActionDataSchema);
|
||||
|
||||
const messageActionSchema = z.object({
|
||||
bot_id: z.string(),
|
||||
type: sourceType,
|
||||
text: z.string().optional(),
|
||||
user: z.string().optional(),
|
||||
ts: z.string(),
|
||||
app_id: z.string().optional(),
|
||||
blocks: z.array(knownBlockSchema).optional(),
|
||||
team: z.string().optional(),
|
||||
metadata: z
|
||||
.object({
|
||||
event_type: z.string(),
|
||||
event_payload: z.object({ requestId: z.string() }),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const blockAction: Zod.ZodObject<{
|
||||
type: typeof blockActionType;
|
||||
user: typeof userSchema;
|
||||
api_app_id: Zod.ZodString;
|
||||
container: typeof containerSchema;
|
||||
trigger_id: z.ZodOptional<Zod.ZodString>;
|
||||
team: typeof teamSchema;
|
||||
enterprise: Zod.ZodAny;
|
||||
is_enterprise_install: Zod.ZodBoolean;
|
||||
channel: typeof channelSchema;
|
||||
view: z.ZodOptional<typeof viewActionSchema>;
|
||||
message: z.ZodOptional<typeof messageActionSchema>;
|
||||
state: z.ZodOptional<typeof stateSchema>;
|
||||
response_url: Zod.ZodString;
|
||||
actions: Zod.ZodArray<typeof actionSchema>;
|
||||
}> = z.object({
|
||||
type: blockActionType,
|
||||
user: userSchema,
|
||||
api_app_id: z.string(),
|
||||
container: containerSchema,
|
||||
trigger_id: z.string().optional(),
|
||||
team: teamSchema,
|
||||
enterprise: z.any(),
|
||||
is_enterprise_install: z.boolean(),
|
||||
channel: channelSchema,
|
||||
view: viewActionSchema.optional(),
|
||||
message: messageActionSchema.optional(),
|
||||
state: stateSchema.optional(),
|
||||
response_url: z.string(),
|
||||
actions: z.array(actionSchema),
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { SlackRequestIntegration } from "./internal/requests";
|
||||
|
||||
export const requests = new SlackRequestIntegration();
|
||||
|
||||
export const metadata = {
|
||||
name: "Slack",
|
||||
slug: "slack",
|
||||
icon: "/integrations/slack.png",
|
||||
enabledFor: "all",
|
||||
authentication: {
|
||||
type: "oauth",
|
||||
scopes: [
|
||||
"channels:read",
|
||||
"channels:join",
|
||||
"channels:manage",
|
||||
"chat:write",
|
||||
"groups:write",
|
||||
"im:write",
|
||||
"mpim:write",
|
||||
"chat:write.customize",
|
||||
"reactions:write",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export * as schemas from "./schemas";
|
||||
@@ -0,0 +1,475 @@
|
||||
import { HttpEndpoint, HttpService } from "@trigger.dev/integration-sdk";
|
||||
import type {
|
||||
DisplayProperties,
|
||||
CacheService,
|
||||
PerformedRequestResponse,
|
||||
PerformRequestOptions,
|
||||
RequestIntegration,
|
||||
AccessInfo,
|
||||
ReactNode,
|
||||
} from "@trigger.dev/integration-sdk";
|
||||
import debug from "debug";
|
||||
import { getAccessToken } from "@trigger.dev/integration-sdk";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AddReactionOptionsSchema,
|
||||
AddReactionResponseSchema,
|
||||
ChannelNameOrIdSchema,
|
||||
JoinConversationBodySchema,
|
||||
JoinConversationResponseSchema,
|
||||
ListConversationsResponseSchema,
|
||||
PostMessageBodySchema,
|
||||
PostMessageOptionsSchema,
|
||||
PostMessageResponseOptionsSchema,
|
||||
PostMessageResponseSchema,
|
||||
} from "../schemas";
|
||||
|
||||
const log = debug("trigger:integrations:slack");
|
||||
|
||||
const SendSlackMessageRequestBodySchema = PostMessageBodySchema.extend({
|
||||
link_names: z.literal(1),
|
||||
metadata: z
|
||||
.object({ event_type: z.string(), event_payload: z.any() })
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export class SlackRequestIntegration implements RequestIntegration {
|
||||
#joinChannelEndpoint = new HttpEndpoint<
|
||||
typeof JoinConversationResponseSchema,
|
||||
typeof JoinConversationBodySchema
|
||||
>({
|
||||
response: JoinConversationResponseSchema,
|
||||
method: "POST",
|
||||
path: "/conversations.join",
|
||||
});
|
||||
|
||||
#listConversationsEndpoint = new HttpEndpoint({
|
||||
response: ListConversationsResponseSchema,
|
||||
method: "GET",
|
||||
path: "/conversations.list",
|
||||
});
|
||||
|
||||
#postMessageEndpoint = new HttpEndpoint<
|
||||
typeof PostMessageResponseSchema,
|
||||
typeof SendSlackMessageRequestBodySchema
|
||||
>({
|
||||
response: PostMessageResponseSchema,
|
||||
method: "POST",
|
||||
path: "/chat.postMessage",
|
||||
});
|
||||
|
||||
#addReactionEndpoint = new HttpEndpoint<
|
||||
typeof AddReactionResponseSchema,
|
||||
typeof AddReactionOptionsSchema
|
||||
>({
|
||||
response: AddReactionResponseSchema,
|
||||
method: "POST",
|
||||
path: "/reactions.add",
|
||||
});
|
||||
|
||||
constructor(private readonly baseUrl: string = "https://slack.com/api") {}
|
||||
|
||||
perform(options: PerformRequestOptions): Promise<PerformedRequestResponse> {
|
||||
switch (options.endpoint) {
|
||||
case "chat.postMessage": {
|
||||
return this.#postMessage(
|
||||
options.accessInfo,
|
||||
options.params,
|
||||
options.cache,
|
||||
options.metadata
|
||||
);
|
||||
}
|
||||
case "chat.postMessageResponse": {
|
||||
return this.#postMessageResponse(
|
||||
options.accessInfo,
|
||||
options.params,
|
||||
options.cache,
|
||||
options.metadata
|
||||
);
|
||||
}
|
||||
case "reactions.add": {
|
||||
return this.#addReaction(
|
||||
options.accessInfo,
|
||||
options.params,
|
||||
options.cache,
|
||||
options.metadata
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown endpoint: ${options.endpoint}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
displayProperties(endpoint: string, params: any): DisplayProperties {
|
||||
switch (endpoint) {
|
||||
case "chat.postMessage": {
|
||||
return {
|
||||
title: `Post message to ${
|
||||
"channelName" in params ? params.channelName : params.channelId
|
||||
}`,
|
||||
properties: [
|
||||
{
|
||||
key: "Text",
|
||||
value: params.text,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case "chat.postMessageResponse": {
|
||||
return {
|
||||
title: `Post response`,
|
||||
properties: [],
|
||||
};
|
||||
}
|
||||
case "reactions.add": {
|
||||
return {
|
||||
title: `Add reaction to message ${params.timestamp}`,
|
||||
properties: [
|
||||
{
|
||||
key: "Reaction",
|
||||
value: params.name,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Unknown endpoint: ${endpoint}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderComponent(input: any, output: any): ReactNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
async #postMessage(
|
||||
accessInfo: AccessInfo,
|
||||
params: any,
|
||||
cache?: CacheService,
|
||||
metadata?: Record<string, string>
|
||||
): Promise<PerformedRequestResponse> {
|
||||
const parsedParams = PostMessageOptionsSchema.parse(params);
|
||||
|
||||
log("chat.postMessage %O", parsedParams);
|
||||
|
||||
const accessToken = getAccessToken(accessInfo);
|
||||
|
||||
const service = new HttpService({
|
||||
accessToken,
|
||||
baseUrl: this.baseUrl,
|
||||
});
|
||||
|
||||
const channelId = await this.#findChannelId(service, params, cache);
|
||||
|
||||
if (!channelId) {
|
||||
return {
|
||||
ok: false,
|
||||
isRetryable: false,
|
||||
response: {
|
||||
output: {
|
||||
message: `channelId not found`,
|
||||
},
|
||||
context: {
|
||||
statusCode: 404,
|
||||
headers: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
log("found channelId %s", channelId);
|
||||
|
||||
const response = await service.performRequest(this.#postMessageEndpoint, {
|
||||
...parsedParams,
|
||||
link_names: 1,
|
||||
channel: channelId,
|
||||
metadata: metadata
|
||||
? { event_type: "post_message", event_payload: metadata }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
log("chat.postMessage failed %O", response);
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
isRetryable: this.#isRetryable(response.statusCode),
|
||||
response: {
|
||||
output: response.error,
|
||||
context: {
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!response.data.ok && response.data.error === "not_in_channel") {
|
||||
log(
|
||||
"chat.postMessage failed with not_in_channel, attempting to join channel %s",
|
||||
channelId
|
||||
);
|
||||
|
||||
// Attempt to join the channel, and then retry the request
|
||||
const joinResponse = await service.performRequest(
|
||||
this.#joinChannelEndpoint,
|
||||
{
|
||||
channel: channelId,
|
||||
}
|
||||
);
|
||||
|
||||
if (joinResponse.success && joinResponse.data.ok) {
|
||||
log("joined channel %s, retrying postMessage", channelId);
|
||||
|
||||
return this.#postMessage(accessInfo, params);
|
||||
}
|
||||
}
|
||||
|
||||
const ok = response.data.ok;
|
||||
|
||||
const performedRequest = {
|
||||
ok,
|
||||
isRetryable: this.#isRetryable(response.statusCode),
|
||||
response: {
|
||||
output: response.data,
|
||||
context: {
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
log("chat.postMessage performedRequest %O", performedRequest);
|
||||
|
||||
return performedRequest;
|
||||
}
|
||||
|
||||
async #addReaction(
|
||||
accessInfo: AccessInfo,
|
||||
params: any,
|
||||
cache?: CacheService,
|
||||
metadata?: Record<string, string>
|
||||
): Promise<PerformedRequestResponse> {
|
||||
const parsedParams = AddReactionOptionsSchema.parse(params);
|
||||
|
||||
log("reactions.add %O", parsedParams);
|
||||
|
||||
const accessToken = getAccessToken(accessInfo);
|
||||
|
||||
const service = new HttpService({
|
||||
accessToken,
|
||||
baseUrl: this.baseUrl,
|
||||
});
|
||||
|
||||
const channelId = await this.#findChannelId(service, parsedParams, cache);
|
||||
|
||||
if (!channelId) {
|
||||
return {
|
||||
ok: false,
|
||||
isRetryable: false,
|
||||
response: {
|
||||
output: {
|
||||
message: `channelId not found`,
|
||||
},
|
||||
context: {
|
||||
statusCode: 404,
|
||||
headers: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
log("found channelId %s", channelId);
|
||||
|
||||
const response = await service.performRequest(this.#addReactionEndpoint, {
|
||||
...parsedParams,
|
||||
// @ts-ignore
|
||||
channel: channelId,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
log("reactions.add failed %O", response);
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
isRetryable: this.#isRetryable(response.statusCode),
|
||||
response: {
|
||||
output: response.error,
|
||||
context: {
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!response.data.ok && response.data.error === "not_in_channel") {
|
||||
log(
|
||||
"reactions.add failed with not_in_channel, attempting to join channel %s",
|
||||
channelId
|
||||
);
|
||||
|
||||
// Attempt to join the channel, and then retry the request
|
||||
const joinResponse = await service.performRequest(
|
||||
this.#joinChannelEndpoint,
|
||||
{
|
||||
channel: channelId,
|
||||
}
|
||||
);
|
||||
|
||||
if (joinResponse.success && joinResponse.data.ok) {
|
||||
log("joined channel %s, retrying reactions.add", channelId);
|
||||
|
||||
return this.#addReaction(accessInfo, params);
|
||||
}
|
||||
}
|
||||
|
||||
const ok = response.data.ok;
|
||||
|
||||
const performedRequest = {
|
||||
ok,
|
||||
isRetryable: this.#isRetryable(response.statusCode),
|
||||
response: {
|
||||
output: response.data,
|
||||
context: {
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
log("chat.postMessage performedRequest %O", performedRequest);
|
||||
|
||||
return performedRequest;
|
||||
}
|
||||
|
||||
async #postMessageResponse(
|
||||
accessInfo: AccessInfo,
|
||||
params: any,
|
||||
cache?: CacheService,
|
||||
metadata?: Record<string, string>
|
||||
): Promise<PerformedRequestResponse> {
|
||||
const parsedParams = z
|
||||
.object({
|
||||
message: PostMessageResponseOptionsSchema,
|
||||
responseUrl: z.string(),
|
||||
})
|
||||
.parse(params);
|
||||
|
||||
log("chat.postMessageResponse %O", parsedParams);
|
||||
|
||||
const response = await fetch(parsedParams.responseUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...parsedParams.message,
|
||||
metadata: metadata
|
||||
? { event_type: "post_message_response", event_payload: metadata }
|
||||
: undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
log("chat.postMessageResponse failed %O", response);
|
||||
|
||||
const error = await safeGetJson(response);
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
isRetryable: this.#isRetryable(response.status),
|
||||
response: {
|
||||
output: error
|
||||
? error
|
||||
: { name: `${response.status}`, message: response.statusText },
|
||||
context: {
|
||||
statusCode: response.status,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const output = await safeGetJson(response);
|
||||
|
||||
const performedRequest = {
|
||||
ok: response.ok,
|
||||
isRetryable: this.#isRetryable(response.status),
|
||||
response: {
|
||||
output,
|
||||
context: {
|
||||
statusCode: response.status,
|
||||
headers: response.headers,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
log("chat.postMessage performedRequest %O", performedRequest);
|
||||
|
||||
return performedRequest;
|
||||
}
|
||||
|
||||
#isRetryable(statusCode: number): boolean {
|
||||
return (
|
||||
statusCode === 408 ||
|
||||
statusCode === 429 ||
|
||||
statusCode === 500 ||
|
||||
statusCode === 502 ||
|
||||
statusCode === 503 ||
|
||||
statusCode === 504
|
||||
);
|
||||
}
|
||||
|
||||
// Will use the conversations.list API (using fetch) to find the channel ID
|
||||
// unless the channel is already provided in the format of a channelID (for example: "D8572TUFR" or "C01BQJZLJGZ")
|
||||
async #findChannelId(
|
||||
service: HttpService,
|
||||
params: z.infer<typeof ChannelNameOrIdSchema>,
|
||||
cache?: CacheService
|
||||
): Promise<string | undefined> {
|
||||
if ("channelId" in params) {
|
||||
return params.channelId;
|
||||
}
|
||||
|
||||
if (!("channelName" in params)) {
|
||||
throw new Error("Invalid params, mising channelId and channelName");
|
||||
}
|
||||
|
||||
//if the channelName starts with a #, remove it
|
||||
if (params.channelName.startsWith("#")) {
|
||||
params.channelName = params.channelName.substring(1);
|
||||
}
|
||||
|
||||
const cachedChannelId = await cache?.get(params.channelName);
|
||||
|
||||
if (cachedChannelId) {
|
||||
return cachedChannelId;
|
||||
}
|
||||
|
||||
const response = await service.performRequest(
|
||||
this.#listConversationsEndpoint
|
||||
);
|
||||
|
||||
if (response.success && response.data.ok) {
|
||||
const { channels } = response.data;
|
||||
|
||||
const channelInfo = channels.find(
|
||||
(c: any) => c.name === params.channelName
|
||||
);
|
||||
|
||||
if (channelInfo) {
|
||||
await cache?.set(params.channelName, channelInfo.id, 60 * 60 * 24);
|
||||
return channelInfo.id;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function safeGetJson(response: Response): Promise<unknown> {
|
||||
return response.json().catch(() => null);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { z } from "zod";
|
||||
import { knownBlockSchema } from "./blocks";
|
||||
import { blockAction } from "./interactivity";
|
||||
|
||||
export { blockAction };
|
||||
|
||||
export const PostMessageSuccessResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
channel: z.string(),
|
||||
ts: z.string(),
|
||||
message: z.object({
|
||||
text: z.string(),
|
||||
user: z.string().optional(),
|
||||
bot_id: z.string(),
|
||||
attachments: z.array(z.unknown()).optional(),
|
||||
type: z.string(),
|
||||
subtype: z.string().optional(),
|
||||
ts: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ErrorResponseSchema = z.object({
|
||||
ok: z.literal(false),
|
||||
error: z.string(),
|
||||
});
|
||||
|
||||
export const PostMessageResponseSchema = z.discriminatedUnion("ok", [
|
||||
PostMessageSuccessResponseSchema,
|
||||
ErrorResponseSchema,
|
||||
]);
|
||||
|
||||
export const PostMessageBodySchema = z.object({
|
||||
channel: z.string(),
|
||||
text: z.string(),
|
||||
blocks: z.array(knownBlockSchema).optional(),
|
||||
username: z.string().optional(),
|
||||
icon_emoji: z.string().optional(),
|
||||
icon_url: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ChannelNameOrIdSchema = z.union([
|
||||
z.object({ channelId: z.string() }),
|
||||
z.object({ channelName: z.string() }),
|
||||
]);
|
||||
|
||||
export const PostMessageOptionsSchema = z
|
||||
.object({
|
||||
text: z.string(),
|
||||
blocks: z.array(knownBlockSchema).optional(),
|
||||
username: z.string().optional(),
|
||||
icon_emoji: z.string().optional(),
|
||||
icon_url: z.string().optional(),
|
||||
})
|
||||
.and(ChannelNameOrIdSchema);
|
||||
|
||||
export const AddReactionOptionsSchema = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
timestamp: z.string(),
|
||||
})
|
||||
.and(ChannelNameOrIdSchema);
|
||||
|
||||
export const JoinConversationSuccessResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
channel: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const JoinConversationResponseSchema = z.discriminatedUnion("ok", [
|
||||
JoinConversationSuccessResponseSchema,
|
||||
ErrorResponseSchema,
|
||||
]);
|
||||
|
||||
export const JoinConversationBodySchema = z.object({
|
||||
channel: z.string(),
|
||||
});
|
||||
|
||||
export const ListConversationsSuccessResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
channels: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export const ListConversationsResponseSchema = z.discriminatedUnion("ok", [
|
||||
ListConversationsSuccessResponseSchema,
|
||||
ErrorResponseSchema,
|
||||
]);
|
||||
|
||||
export const PostMessageResponseOptionsSchema = z.object({
|
||||
text: z.string().optional(),
|
||||
blocks: z.array(knownBlockSchema).optional(),
|
||||
response_type: z.enum(["in_channel"]).optional(),
|
||||
replace_original: z.boolean().optional(),
|
||||
delete_original: z.boolean().optional(),
|
||||
thread_ts: z.string().optional(),
|
||||
});
|
||||
|
||||
export const PostMessageResponseSuccessResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
});
|
||||
|
||||
export const PostMessageResponseResponseSchema = z.discriminatedUnion("ok", [
|
||||
PostMessageResponseSuccessResponseSchema,
|
||||
ErrorResponseSchema,
|
||||
]);
|
||||
|
||||
export const AddReactionSuccessResponseSchema = z.object({
|
||||
ok: z.literal(true),
|
||||
});
|
||||
|
||||
export const AddReactionResponseSchema = z.discriminatedUnion("ok", [
|
||||
AddReactionSuccessResponseSchema,
|
||||
ErrorResponseSchema,
|
||||
]);
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@trigger.dev/tsconfig/node16.json",
|
||||
"include": ["./src/**/*.ts", "tsup.config.ts"],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2019"],
|
||||
"paths": {
|
||||
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
|
||||
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
|
||||
"@trigger.dev/integration-sdk/*": [
|
||||
"../../packages/integration-sdk/src/*"
|
||||
],
|
||||
"@trigger.dev/integration-sdk": [
|
||||
"../../packages/integration-sdk/src/index"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
name: "main",
|
||||
entry: ["./src/index.ts"],
|
||||
outDir: "./dist",
|
||||
platform: "node",
|
||||
format: ["cjs"],
|
||||
legacyOutput: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
bundle: true,
|
||||
splitting: false,
|
||||
dts: true,
|
||||
treeshake: {
|
||||
preset: "smallest",
|
||||
},
|
||||
external: ["http", "https", "util", "events", "tty", "os", "timers"],
|
||||
esbuildPlugins: [],
|
||||
noExternal: ["@trigger.dev/common-schemas"],
|
||||
},
|
||||
]);
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export type { ReactNode };
|
||||
|
||||
export type AccessInfo =
|
||||
| { type: "oauth2"; accessToken: string }
|
||||
| {
|
||||
|
||||
Generated
+31
-8
@@ -393,15 +393,15 @@ importers:
|
||||
|
||||
examples/fetch-playground:
|
||||
specifiers:
|
||||
'@trigger.dev/integrations': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/slack': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/node': '16'
|
||||
tsx: ^3.12.0
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
'@trigger.dev/integrations': link:../../packages/trigger-integrations
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
'@trigger.dev/slack': link:../../integrations/slack
|
||||
zod: 3.20.2
|
||||
devDependencies:
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
@@ -491,15 +491,15 @@ importers:
|
||||
|
||||
examples/schedule-to-slack:
|
||||
specifiers:
|
||||
'@trigger.dev/integrations': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/slack': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/node': '16'
|
||||
tsx: ^3.12.0
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
'@trigger.dev/integrations': link:../../packages/trigger-integrations
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
'@trigger.dev/slack': link:../../integrations/slack
|
||||
zod: 3.20.2
|
||||
devDependencies:
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
@@ -508,16 +508,16 @@ importers:
|
||||
|
||||
examples/send-to-slack:
|
||||
specifiers:
|
||||
'@trigger.dev/integrations': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/slack': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/node': '16'
|
||||
jsx-slack: ^5.3.0
|
||||
tsx: ^3.12.0
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
'@trigger.dev/integrations': link:../../packages/trigger-integrations
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
'@trigger.dev/slack': link:../../integrations/slack
|
||||
jsx-slack: 5.3.0
|
||||
zod: 3.20.2
|
||||
devDependencies:
|
||||
@@ -544,7 +544,6 @@ importers:
|
||||
|
||||
examples/smoke-test:
|
||||
specifiers:
|
||||
'@trigger.dev/integrations': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/node': '16'
|
||||
@@ -552,7 +551,6 @@ importers:
|
||||
ulid: ^2.3.0
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
'@trigger.dev/integrations': link:../../packages/trigger-integrations
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
ulid: 2.3.0
|
||||
zod: 3.20.2
|
||||
@@ -582,6 +580,31 @@ importers:
|
||||
rimraf: 3.0.2
|
||||
tsup: 6.5.0
|
||||
|
||||
integrations/slack:
|
||||
specifiers:
|
||||
'@octokit/webhooks': ^10.4.0
|
||||
'@trigger.dev/integration-sdk': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@types/debug': ^4.1.7
|
||||
'@types/node': '16'
|
||||
debug: ^4.3.4
|
||||
rimraf: ^3.0.2
|
||||
tsup: ^6.5.0
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
'@octokit/webhooks': 10.5.1
|
||||
debug: 4.3.4
|
||||
zod: 3.20.2
|
||||
devDependencies:
|
||||
'@trigger.dev/integration-sdk': link:../../packages/integration-sdk
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
'@types/debug': 4.1.7
|
||||
'@types/node': 16.18.11
|
||||
rimraf: 3.0.2
|
||||
tsup: 6.5.0
|
||||
|
||||
packages/common-schemas:
|
||||
specifiers:
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
|
||||
Reference in New Issue
Block a user