Files
2026-07-13 13:34:48 +08:00

425 lines
12 KiB
TypeScript

import * as sdk from '@botpress/sdk'
const plainTextSchema = sdk.z.object({ type: sdk.z.literal('plain_text'), text: sdk.z.string() }).strict()
const markdownSchema = sdk.z.object({ type: sdk.z.literal('mrkdwn'), text: sdk.z.string() }).strict()
const plainOrMarkdown = sdk.z.discriminatedUnion('type', [markdownSchema, plainTextSchema])
const imageElement = sdk.z
.object({
type: sdk.z.literal('image'),
image_url: sdk.z.string().describe('The full URL to the image file'),
alt_text: sdk.z.string().describe('Plain text summary of the image'),
})
.strict()
.describe('Display an image to a user')
const imageBlock = sdk.z
.object({
type: sdk.z.literal('image'),
image_url: sdk.z.string().describe('The full URL to the image file'),
alt_text: sdk.z.string().describe('Plain text summary of the image'),
title: plainTextSchema.optional(),
})
.strict()
.describe('Display an image to a user')
const buttonSchema = sdk.z
.object({
type: sdk.z.literal('button'),
action_id: sdk.z.string().describe('A unique identifier for the button'),
text: plainTextSchema,
url: sdk.z.string().optional().describe('An external URL to open when the button is clicked'),
value: sdk.z.string().optional().describe('The value to send along with the interaction payload'),
style: sdk.z
.enum(['primary', 'danger'])
.optional()
.describe('Decorates buttons with alternative visual color schemes. Leave empty for default.'),
})
.strict()
.describe('Button Block. Display a button')
const staticSelectSchema = sdk.z
.object({
type: sdk.z.literal('static_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
options: sdk.z.array(
sdk.z
.object({
text: plainTextSchema,
value: sdk.z.string(),
})
.strict()
),
})
.strict()
const contextBlock = sdk.z
.object({
type: sdk.z.literal('context'),
elements: sdk.z.array(sdk.z.discriminatedUnion('type', [imageElement, ...plainOrMarkdown.options])).max(10),
})
.strict()
.describe('Display multiple elements in a group')
const dividerBlock = sdk.z.object({ type: sdk.z.literal('divider') }).describe('A simple divider block')
const headerBlock = sdk.z
.object({
type: sdk.z.literal('header'),
text: plainTextSchema,
})
.strict()
.describe(
'A header is a plain-text block that displays in a larger, bold font. Use it to delineate between different groups of content in your app surface'
)
const fileBlock = sdk.z
.object({
type: sdk.z.literal('file'),
external_id: sdk.z.string(),
source: sdk.z.literal('remote'),
})
.strict()
.describe('A file block')
const radioButtonsSchema = sdk.z
.object({
type: sdk.z.literal('radio_buttons'),
options: sdk.z.array(
sdk.z
.object({
text: plainTextSchema,
value: sdk.z.string(),
})
.strict()
),
action_id: sdk.z.string(),
})
.strict()
.describe('A radio buttons block')
const checkboxesSchema = sdk.z
.object({
type: sdk.z.literal('checkboxes'),
options: sdk.z.array(
sdk.z.object({
text: plainTextSchema,
value: sdk.z.string(),
})
),
action_id: sdk.z.string(),
})
.strict()
.describe('A checkboxes block')
const overflowSchema = sdk.z
.object({
type: sdk.z.literal('overflow'),
options: sdk.z
.array(
sdk.z
.object({
text: plainTextSchema,
value: sdk.z.string().max(75),
description: plainTextSchema.optional(),
url: sdk.z
.string()
.optional()
.describe(
"A URL to load in the user's browser when the option is clicked. The url attribute is only available in overflow menus."
),
})
.strict()
)
.max(5),
action_id: sdk.z.string(),
})
.strict()
.describe('An overflow block')
const actionId = sdk.z
.string()
.max(255)
.describe(
'An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block. '
)
const datePickerSchema = sdk.z
.object({
type: sdk.z.literal('datepicker'),
action_id: actionId,
placeholder: plainTextSchema.optional(),
initial_date: sdk.z.string().optional(),
})
.strict()
.describe('A date picker block')
const dateTimePickerSchema = sdk.z
.object({
type: sdk.z.literal('datetimepicker'),
action_id: actionId,
initial_date_time: sdk.z
.number()
.describe(
'The initial date and time that is selected when the element is loaded, represented as a UNUIX timestamp in seconds. This should be in the format of 10 digits, for example 1628633820 represents the date and time August 10th, 2021 at 03:17pm PST'
)
.optional(),
// confirm: // TODO:
focus_on_load: sdk.z.boolean().optional(),
})
.strict()
.describe('A date picker block')
const timePickerSchema = sdk.z
.object({
type: sdk.z.literal('timepicker'),
action_id: actionId,
placeholder: plainTextSchema.optional(),
initial_time: sdk.z.string().optional(),
})
.strict()
.describe('A time picker block')
const optionSchema = sdk.z
.object({
text: plainTextSchema,
value: sdk.z.string(),
})
.strict()
const optionGroupSchema = sdk.z
.object({
label: plainTextSchema,
options: sdk.z.array(optionSchema),
})
.strict()
const multiSelectStaticMenuSchema = sdk.z
.object({
type: sdk.z.literal('multi_static_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
options: sdk.z.array(optionSchema).optional(),
option_groups: sdk.z.array(optionGroupSchema).optional(),
})
.strict()
.refine((obj) => !!obj.options || !!obj.option_groups, {
message: 'At least one of options or option_groups must be provided',
})
.describe('A multi-select menu block')
const conversationsSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('conversations_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A conversations select menu block')
const channelsSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('channels_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A channels select menu block')
const usersSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('users_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A users select menu block')
const externalSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('external_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('An external select menu block')
const conversationsMultiSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('conversations_multi_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A conversations multi-select menu block')
const channelsMultiSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('channels_multi_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A channels multi-select menu block')
const usersMultiSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('users_multi_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('A users multi-select menu block')
const externalMultiSelectMenuSchema = sdk.z
.object({
type: sdk.z.literal('external_multi_select'),
placeholder: plainTextSchema,
action_id: sdk.z.string(),
})
.strict()
.describe('An external multi-select menu block')
const plainTextInput = sdk.z
.object({
type: sdk.z.literal('plain_text_input'),
action_id: sdk.z
.string()
.max(255)
.describe(
'An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block.'
),
initial_value: sdk.z.string().describe('The initial value in the plain-text input when it is loaded.').optional(),
multiline: sdk.z
.boolean()
.optional()
.describe('Indicates whether the input will be a single line (false) or a larger textarea (true'),
min_length: sdk.z.number().min(0).max(3000).optional(),
max_length: sdk.z.number().min(0).max(3000).optional(),
focus_on_load: sdk.z.boolean().optional(),
placeholder: plainTextSchema.optional(),
// TODO: dispatch_action_config
})
.strict()
const multiSelectMenus = sdk.z.discriminatedUnion('type', [
multiSelectStaticMenuSchema.sourceType(),
externalMultiSelectMenuSchema,
usersMultiSelectMenuSchema,
conversationsMultiSelectMenuSchema,
channelsMultiSelectMenuSchema,
])
const selectMenus = sdk.z.discriminatedUnion('type', [
staticSelectSchema,
externalSelectMenuSchema,
usersSelectMenuSchema,
conversationsSelectMenuSchema,
channelsSelectMenuSchema,
])
const inputBlock = sdk.z
.object({
type: sdk.z.literal('input'),
label: plainTextSchema,
element: sdk.z.discriminatedUnion('type', [
plainTextInput,
checkboxesSchema,
radioButtonsSchema,
...selectMenus.options,
...multiSelectMenus.options,
datePickerSchema,
]),
dispatch_action: sdk.z.boolean().optional(),
block_id: sdk.z.string().max(255).optional(),
hint: plainTextSchema.optional(),
optional: sdk.z.boolean().optional(),
})
.strict()
.describe('An input block')
const sectionSchema = sdk.z
.object({
type: sdk.z.literal('section'),
text: plainOrMarkdown.optional(),
fields: sdk.z.array(plainOrMarkdown).min(1).max(10).optional(),
accessory: sdk.z
.discriminatedUnion('type', [
buttonSchema,
checkboxesSchema,
datePickerSchema,
imageElement,
overflowSchema,
radioButtonsSchema,
...multiSelectMenus.options,
...selectMenus.options,
timePickerSchema,
])
.optional(),
})
.strict()
.describe('Show a message using markdown')
const actionsBlock = sdk.z
.object({
type: sdk.z.literal('actions'),
elements: sdk.z
.array(
sdk.z.discriminatedUnion('type', [
buttonSchema,
checkboxesSchema,
datePickerSchema,
dateTimePickerSchema,
overflowSchema,
radioButtonsSchema,
...selectMenus.options,
timePickerSchema,
])
)
.max(5),
})
.describe('Display multiple elements in a group')
.strict()
const blocks = sdk.z.discriminatedUnion('type', [
actionsBlock,
contextBlock,
dividerBlock,
fileBlock,
headerBlock,
imageBlock,
inputBlock,
sectionSchema,
// video // TODO:
])
const mention = sdk.z.object({
type: sdk.z.string(),
start: sdk.z.number(), // position in string
end: sdk.z.number(),
user: sdk.z.object({
id: sdk.z.string(),
name: sdk.z.string(),
}),
})
export const textSchema = sdk.z
.object({
text: sdk.z
.string()
.describe(
'Field text must be defined but it is ignored if blocks are provided. In this situation, the text must be provided in the blocks array'
),
blocks: sdk.z
.array(blocks)
.max(50)
.optional()
.describe(
'Multiple blocks can be added to this array. If a block is provided, the text field is ignored and the text must be added as a block'
),
mentions: sdk.z.array(mention).optional(),
})
.strict()