Breakout slack into it’s own package

This commit is contained in:
Eric Allam
2023-01-30 13:25:55 +00:00
committed by Matt Aitken
parent d71c61fc56
commit 2bb7bad947
29 changed files with 2018 additions and 32 deletions
@@ -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(
+1
View File
@@ -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",
+3 -1
View File
@@ -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
}
+6
View File
@@ -35,6 +35,12 @@
],
"@trigger.dev/github/*": [
"../../integrations/github/src/*"
],
"@trigger.dev/slack": [
"../../integrations/slack/src/index"
],
"@trigger.dev/slack/*": [
"../../integrations/slack/src/*"
]
}
}
+1 -1
View File
@@ -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 -1
View File
@@ -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";
+1 -1
View File
@@ -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"
},
+5 -5
View File
@@ -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;
},
+2 -2
View File
@@ -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 -1
View File
@@ -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,
+1 -2
View File
@@ -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"
}
}
}
+5 -5
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"extends": "@trigger.dev/tsconfig/node16.json",
"include": ["./src/**/*.ts"],
"include": ["./src/**/*.ts", "tsup.config.ts"],
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
+3
View File
@@ -0,0 +1,3 @@
# Trigger.dev GitHub integration
View more documentation [here](https://docs.trigger.dev)
+35
View File
@@ -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
}
}
+41
View File
@@ -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"
}
}
+861
View File
@@ -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,
]);
+37
View File
@@ -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,
};
}
+97
View File
@@ -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;
}
+215
View File
@@ -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),
});
+26
View File
@@ -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";
+475
View File
@@ -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);
}
+119
View File
@@ -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,
]);
+20
View File
@@ -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"]
}
+23
View File
@@ -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"],
},
]);
+2
View File
@@ -1,5 +1,7 @@
import type { ReactNode } from "react";
export type { ReactNode };
export type AccessInfo =
| { type: "oauth2"; accessToken: string }
| {
+31 -8
View File
@@ -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:*