chat 4.13.0 → 4.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -31
- package/dist/{chunk-WKJEG4FE.js → chunk-THM4ACIE.js} +12 -4
- package/dist/chunk-THM4ACIE.js.map +1 -0
- package/dist/index.d.ts +409 -415
- package/dist/index.js +63 -29
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-COVsDskT.d.ts → jsx-runtime-Bdt1Dwzf.d.ts} +71 -71
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/actions.mdx +98 -0
- package/docs/adapters/discord.mdx +217 -0
- package/docs/adapters/gchat.mdx +232 -0
- package/docs/adapters/github.mdx +225 -0
- package/docs/adapters/index.mdx +110 -0
- package/docs/adapters/linear.mdx +207 -0
- package/docs/adapters/meta.json +12 -0
- package/docs/adapters/slack.mdx +293 -0
- package/docs/adapters/teams.mdx +225 -0
- package/docs/api/cards.mdx +217 -0
- package/docs/api/channel.mdx +176 -0
- package/docs/api/chat.mdx +469 -0
- package/docs/api/index.mdx +29 -0
- package/docs/api/markdown.mdx +235 -0
- package/docs/api/message.mdx +163 -0
- package/docs/api/meta.json +14 -0
- package/docs/api/modals.mdx +222 -0
- package/docs/api/postable-message.mdx +166 -0
- package/docs/api/thread.mdx +186 -0
- package/docs/cards.mdx +213 -0
- package/docs/direct-messages.mdx +56 -0
- package/docs/emoji.mdx +77 -0
- package/docs/ephemeral-messages.mdx +77 -0
- package/docs/error-handling.mdx +147 -0
- package/docs/files.mdx +77 -0
- package/docs/getting-started.mdx +12 -0
- package/docs/guides/code-review-hono.mdx +248 -0
- package/docs/guides/discord-nuxt.mdx +237 -0
- package/docs/guides/meta.json +4 -0
- package/docs/guides/slack-nextjs.mdx +245 -0
- package/docs/index.mdx +92 -0
- package/docs/meta.json +20 -0
- package/docs/modals.mdx +208 -0
- package/docs/posting-messages.mdx +177 -0
- package/docs/slash-commands.mdx +110 -0
- package/docs/state/index.mdx +31 -0
- package/docs/state/ioredis.mdx +81 -0
- package/docs/state/memory.mdx +52 -0
- package/docs/state/meta.json +9 -0
- package/docs/state/redis.mdx +93 -0
- package/docs/streaming.mdx +99 -0
- package/docs/usage.mdx +338 -0
- package/package.json +10 -10
- package/dist/chunk-WKJEG4FE.js.map +0 -1
|
@@ -4,77 +4,77 @@
|
|
|
4
4
|
|
|
5
5
|
type ModalChild = TextInputElement | SelectElement | RadioSelectElement | TextElement | FieldsElement;
|
|
6
6
|
interface ModalElement {
|
|
7
|
-
type: "modal";
|
|
8
7
|
callbackId: string;
|
|
9
|
-
|
|
10
|
-
submitLabel?: string;
|
|
8
|
+
children: ModalChild[];
|
|
11
9
|
closeLabel?: string;
|
|
12
10
|
notifyOnClose?: boolean;
|
|
13
11
|
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
14
12
|
privateMetadata?: string;
|
|
15
|
-
|
|
13
|
+
submitLabel?: string;
|
|
14
|
+
title: string;
|
|
15
|
+
type: "modal";
|
|
16
16
|
}
|
|
17
17
|
interface TextInputElement {
|
|
18
|
-
type: "text_input";
|
|
19
18
|
id: string;
|
|
20
|
-
label: string;
|
|
21
|
-
placeholder?: string;
|
|
22
19
|
initialValue?: string;
|
|
20
|
+
label: string;
|
|
21
|
+
maxLength?: number;
|
|
23
22
|
multiline?: boolean;
|
|
24
23
|
optional?: boolean;
|
|
25
|
-
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
type: "text_input";
|
|
26
26
|
}
|
|
27
27
|
interface SelectElement {
|
|
28
|
-
type: "select";
|
|
29
28
|
id: string;
|
|
30
|
-
label: string;
|
|
31
|
-
placeholder?: string;
|
|
32
|
-
options: SelectOptionElement[];
|
|
33
29
|
initialOption?: string;
|
|
30
|
+
label: string;
|
|
34
31
|
optional?: boolean;
|
|
32
|
+
options: SelectOptionElement[];
|
|
33
|
+
placeholder?: string;
|
|
34
|
+
type: "select";
|
|
35
35
|
}
|
|
36
36
|
interface SelectOptionElement {
|
|
37
|
+
description?: string;
|
|
37
38
|
label: string;
|
|
38
39
|
value: string;
|
|
39
|
-
description?: string;
|
|
40
40
|
}
|
|
41
41
|
interface RadioSelectElement {
|
|
42
|
-
type: "radio_select";
|
|
43
42
|
id: string;
|
|
44
|
-
label: string;
|
|
45
|
-
options: SelectOptionElement[];
|
|
46
43
|
initialOption?: string;
|
|
44
|
+
label: string;
|
|
47
45
|
optional?: boolean;
|
|
46
|
+
options: SelectOptionElement[];
|
|
47
|
+
type: "radio_select";
|
|
48
48
|
}
|
|
49
49
|
declare function isModalElement(value: unknown): value is ModalElement;
|
|
50
50
|
interface ModalOptions {
|
|
51
51
|
callbackId: string;
|
|
52
|
-
|
|
53
|
-
submitLabel?: string;
|
|
52
|
+
children?: ModalChild[];
|
|
54
53
|
closeLabel?: string;
|
|
55
54
|
notifyOnClose?: boolean;
|
|
56
55
|
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
57
56
|
privateMetadata?: string;
|
|
58
|
-
|
|
57
|
+
submitLabel?: string;
|
|
58
|
+
title: string;
|
|
59
59
|
}
|
|
60
60
|
declare function Modal(options: ModalOptions): ModalElement;
|
|
61
61
|
interface TextInputOptions {
|
|
62
62
|
id: string;
|
|
63
|
-
label: string;
|
|
64
|
-
placeholder?: string;
|
|
65
63
|
initialValue?: string;
|
|
64
|
+
label: string;
|
|
65
|
+
maxLength?: number;
|
|
66
66
|
multiline?: boolean;
|
|
67
67
|
optional?: boolean;
|
|
68
|
-
|
|
68
|
+
placeholder?: string;
|
|
69
69
|
}
|
|
70
70
|
declare function TextInput(options: TextInputOptions): TextInputElement;
|
|
71
71
|
interface SelectOptions {
|
|
72
72
|
id: string;
|
|
73
|
-
label: string;
|
|
74
|
-
placeholder?: string;
|
|
75
|
-
options: SelectOptionElement[];
|
|
76
73
|
initialOption?: string;
|
|
74
|
+
label: string;
|
|
77
75
|
optional?: boolean;
|
|
76
|
+
options: SelectOptionElement[];
|
|
77
|
+
placeholder?: string;
|
|
78
78
|
}
|
|
79
79
|
declare function Select(options: SelectOptions): SelectElement;
|
|
80
80
|
declare function SelectOption(options: {
|
|
@@ -84,10 +84,10 @@ declare function SelectOption(options: {
|
|
|
84
84
|
}): SelectOptionElement;
|
|
85
85
|
interface RadioSelectOptions {
|
|
86
86
|
id: string;
|
|
87
|
-
label: string;
|
|
88
|
-
options: SelectOptionElement[];
|
|
89
87
|
initialOption?: string;
|
|
88
|
+
label: string;
|
|
90
89
|
optional?: boolean;
|
|
90
|
+
options: SelectOptionElement[];
|
|
91
91
|
}
|
|
92
92
|
declare function RadioSelect(options: RadioSelectOptions): RadioSelectElement;
|
|
93
93
|
type AnyModalElement = ModalElement | ModalChild | SelectOptionElement;
|
|
@@ -145,41 +145,41 @@ type ButtonStyle = "primary" | "danger" | "default";
|
|
|
145
145
|
type TextStyle = "plain" | "bold" | "muted";
|
|
146
146
|
/** Button element for interactive actions */
|
|
147
147
|
interface ButtonElement {
|
|
148
|
-
type: "button";
|
|
149
148
|
/** Unique action ID for callback routing */
|
|
150
149
|
id: string;
|
|
151
150
|
/** Button label text */
|
|
152
151
|
label: string;
|
|
153
152
|
/** Visual style */
|
|
154
153
|
style?: ButtonStyle;
|
|
154
|
+
type: "button";
|
|
155
155
|
/** Optional payload value sent with action callback */
|
|
156
156
|
value?: string;
|
|
157
157
|
}
|
|
158
158
|
/** Link button element that opens a URL */
|
|
159
159
|
interface LinkButtonElement {
|
|
160
|
-
type: "link-button";
|
|
161
|
-
/** URL to open when clicked */
|
|
162
|
-
url: string;
|
|
163
160
|
/** Button label text */
|
|
164
161
|
label: string;
|
|
165
162
|
/** Visual style */
|
|
166
163
|
style?: ButtonStyle;
|
|
164
|
+
type: "link-button";
|
|
165
|
+
/** URL to open when clicked */
|
|
166
|
+
url: string;
|
|
167
167
|
}
|
|
168
168
|
/** Text content element */
|
|
169
169
|
interface TextElement {
|
|
170
|
-
type: "text";
|
|
171
170
|
/** Text content (supports markdown in some platforms) */
|
|
172
171
|
content: string;
|
|
173
172
|
/** Text style */
|
|
174
173
|
style?: TextStyle;
|
|
174
|
+
type: "text";
|
|
175
175
|
}
|
|
176
176
|
/** Image element */
|
|
177
177
|
interface ImageElement {
|
|
178
|
+
/** Alt text for accessibility */
|
|
179
|
+
alt?: string;
|
|
178
180
|
type: "image";
|
|
179
181
|
/** Image URL */
|
|
180
182
|
url: string;
|
|
181
|
-
/** Alt text for accessibility */
|
|
182
|
-
alt?: string;
|
|
183
183
|
}
|
|
184
184
|
/** Visual divider/separator */
|
|
185
185
|
interface DividerElement {
|
|
@@ -187,29 +187,29 @@ interface DividerElement {
|
|
|
187
187
|
}
|
|
188
188
|
/** Container for action buttons and selects */
|
|
189
189
|
interface ActionsElement {
|
|
190
|
-
type: "actions";
|
|
191
190
|
/** Button, link button, select, and radio select elements */
|
|
192
191
|
children: (ButtonElement | LinkButtonElement | SelectElement | RadioSelectElement)[];
|
|
192
|
+
type: "actions";
|
|
193
193
|
}
|
|
194
194
|
/** Section container for grouping elements */
|
|
195
195
|
interface SectionElement {
|
|
196
|
-
type: "section";
|
|
197
196
|
/** Section children */
|
|
198
197
|
children: CardChild[];
|
|
198
|
+
type: "section";
|
|
199
199
|
}
|
|
200
200
|
/** Field for key-value display */
|
|
201
201
|
interface FieldElement {
|
|
202
|
-
type: "field";
|
|
203
202
|
/** Field label */
|
|
204
203
|
label: string;
|
|
204
|
+
type: "field";
|
|
205
205
|
/** Field value */
|
|
206
206
|
value: string;
|
|
207
207
|
}
|
|
208
208
|
/** Fields container for multi-column layout */
|
|
209
209
|
interface FieldsElement {
|
|
210
|
-
type: "fields";
|
|
211
210
|
/** Field elements */
|
|
212
211
|
children: FieldElement[];
|
|
212
|
+
type: "fields";
|
|
213
213
|
}
|
|
214
214
|
/** Union of all card child element types */
|
|
215
215
|
type CardChild = TextElement | ImageElement | DividerElement | ActionsElement | SectionElement | FieldsElement;
|
|
@@ -217,24 +217,24 @@ type CardChild = TextElement | ImageElement | DividerElement | ActionsElement |
|
|
|
217
217
|
type AnyCardElement = CardChild | CardElement | ButtonElement | LinkButtonElement | FieldElement | SelectElement | RadioSelectElement;
|
|
218
218
|
/** Root card element */
|
|
219
219
|
interface CardElement {
|
|
220
|
-
type: "card";
|
|
221
|
-
/** Card title */
|
|
222
|
-
title?: string;
|
|
223
|
-
/** Card subtitle */
|
|
224
|
-
subtitle?: string;
|
|
225
|
-
/** Header image URL */
|
|
226
|
-
imageUrl?: string;
|
|
227
220
|
/** Card content */
|
|
228
221
|
children: CardChild[];
|
|
222
|
+
/** Header image URL */
|
|
223
|
+
imageUrl?: string;
|
|
224
|
+
/** Card subtitle */
|
|
225
|
+
subtitle?: string;
|
|
226
|
+
/** Card title */
|
|
227
|
+
title?: string;
|
|
228
|
+
type: "card";
|
|
229
229
|
}
|
|
230
230
|
/** Type guard for CardElement */
|
|
231
231
|
declare function isCardElement(value: unknown): value is CardElement;
|
|
232
232
|
/** Options for Card */
|
|
233
233
|
interface CardOptions {
|
|
234
|
-
title?: string;
|
|
235
|
-
subtitle?: string;
|
|
236
|
-
imageUrl?: string;
|
|
237
234
|
children?: CardChild[];
|
|
235
|
+
imageUrl?: string;
|
|
236
|
+
subtitle?: string;
|
|
237
|
+
title?: string;
|
|
238
238
|
}
|
|
239
239
|
/**
|
|
240
240
|
* Create a Card element.
|
|
@@ -331,12 +331,12 @@ interface ButtonOptions {
|
|
|
331
331
|
declare function Button(options: ButtonOptions): ButtonElement;
|
|
332
332
|
/** Options for LinkButton */
|
|
333
333
|
interface LinkButtonOptions {
|
|
334
|
-
/** URL to open when clicked */
|
|
335
|
-
url: string;
|
|
336
334
|
/** Button label text */
|
|
337
335
|
label: string;
|
|
338
336
|
/** Visual style */
|
|
339
337
|
style?: ButtonStyle;
|
|
338
|
+
/** URL to open when clicked */
|
|
339
|
+
url: string;
|
|
340
340
|
}
|
|
341
341
|
/**
|
|
342
342
|
* Create a LinkButton element that opens a URL when clicked.
|
|
@@ -427,35 +427,35 @@ declare function fromReactElement(element: unknown): AnyCardElement | null;
|
|
|
427
427
|
declare const JSX_ELEMENT: unique symbol;
|
|
428
428
|
/** Props for Card component in JSX */
|
|
429
429
|
interface CardProps {
|
|
430
|
-
title?: string;
|
|
431
|
-
subtitle?: string;
|
|
432
|
-
imageUrl?: string;
|
|
433
430
|
children?: unknown;
|
|
431
|
+
imageUrl?: string;
|
|
432
|
+
subtitle?: string;
|
|
433
|
+
title?: string;
|
|
434
434
|
}
|
|
435
435
|
/** Props for Text component in JSX */
|
|
436
436
|
interface TextProps {
|
|
437
|
-
style?: TextStyle;
|
|
438
437
|
children?: string | number;
|
|
438
|
+
style?: TextStyle;
|
|
439
439
|
}
|
|
440
440
|
/** Props for Button component in JSX */
|
|
441
441
|
interface ButtonProps {
|
|
442
|
+
children?: string | number;
|
|
442
443
|
id: string;
|
|
443
444
|
label?: string;
|
|
444
445
|
style?: ButtonStyle;
|
|
445
446
|
value?: string;
|
|
446
|
-
children?: string | number;
|
|
447
447
|
}
|
|
448
448
|
/** Props for LinkButton component in JSX */
|
|
449
449
|
interface LinkButtonProps {
|
|
450
|
-
|
|
450
|
+
children?: string | number;
|
|
451
451
|
label?: string;
|
|
452
452
|
style?: ButtonStyle;
|
|
453
|
-
|
|
453
|
+
url: string;
|
|
454
454
|
}
|
|
455
455
|
/** Props for Image component in JSX */
|
|
456
456
|
interface ImageProps {
|
|
457
|
-
url: string;
|
|
458
457
|
alt?: string;
|
|
458
|
+
url: string;
|
|
459
459
|
}
|
|
460
460
|
/** Props for Field component in JSX */
|
|
461
461
|
interface FieldProps {
|
|
@@ -471,37 +471,37 @@ type DividerProps = Record<string, never>;
|
|
|
471
471
|
/** Props for Modal component in JSX */
|
|
472
472
|
interface ModalProps {
|
|
473
473
|
callbackId: string;
|
|
474
|
-
|
|
475
|
-
submitLabel?: string;
|
|
474
|
+
children?: unknown;
|
|
476
475
|
closeLabel?: string;
|
|
477
476
|
notifyOnClose?: boolean;
|
|
478
477
|
privateMetadata?: string;
|
|
479
|
-
|
|
478
|
+
submitLabel?: string;
|
|
479
|
+
title: string;
|
|
480
480
|
}
|
|
481
481
|
/** Props for TextInput component in JSX */
|
|
482
482
|
interface TextInputProps {
|
|
483
483
|
id: string;
|
|
484
|
-
label: string;
|
|
485
|
-
placeholder?: string;
|
|
486
484
|
initialValue?: string;
|
|
485
|
+
label: string;
|
|
486
|
+
maxLength?: number;
|
|
487
487
|
multiline?: boolean;
|
|
488
488
|
optional?: boolean;
|
|
489
|
-
|
|
489
|
+
placeholder?: string;
|
|
490
490
|
}
|
|
491
491
|
/** Props for Select component in JSX */
|
|
492
492
|
interface SelectProps {
|
|
493
|
+
children?: unknown;
|
|
493
494
|
id: string;
|
|
494
|
-
label: string;
|
|
495
|
-
placeholder?: string;
|
|
496
495
|
initialOption?: string;
|
|
496
|
+
label: string;
|
|
497
497
|
optional?: boolean;
|
|
498
|
-
|
|
498
|
+
placeholder?: string;
|
|
499
499
|
}
|
|
500
500
|
/** Props for SelectOption component in JSX */
|
|
501
501
|
interface SelectOptionProps {
|
|
502
|
+
description?: string;
|
|
502
503
|
label: string;
|
|
503
504
|
value: string;
|
|
504
|
-
description?: string;
|
|
505
505
|
}
|
|
506
506
|
/** Union of all valid JSX props */
|
|
507
507
|
type CardJSXProps = CardProps | TextProps | ButtonProps | LinkButtonProps | ImageProps | FieldProps | ContainerProps | DividerProps | ModalProps | TextInputProps | SelectProps | SelectOptionProps;
|
|
@@ -513,9 +513,9 @@ type CardComponentFunction = typeof Card | typeof Text | typeof Button | typeof
|
|
|
513
513
|
*/
|
|
514
514
|
interface CardJSXElement<P extends CardJSXProps = CardJSXProps> {
|
|
515
515
|
$$typeof: typeof JSX_ELEMENT;
|
|
516
|
-
type: CardComponentFunction;
|
|
517
|
-
props: P;
|
|
518
516
|
children: unknown[];
|
|
517
|
+
props: P;
|
|
518
|
+
type: CardComponentFunction;
|
|
519
519
|
}
|
|
520
520
|
type JSXElement = CardJSXElement;
|
|
521
521
|
/**
|
|
@@ -560,4 +560,4 @@ declare namespace JSX {
|
|
|
560
560
|
}
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
-
export { type SelectElement as $, Actions as A, Button as B, type CardElement as C, Divider as D, type
|
|
563
|
+
export { type SelectElement as $, Actions as A, Button as B, type CardElement as C, Divider as D, type SectionElement as E, Field as F, type TextElement as G, type TextStyle as H, Image as I, type ButtonProps as J, type CardJSXProps as K, LinkButton as L, type ModalElement as M, type CardProps as N, type ContainerProps as O, type DividerProps as P, type FieldProps as Q, RadioSelect as R, Section as S, Text as T, type ImageProps as U, type LinkButtonProps as V, type TextProps as W, type ModalChild as X, type ModalOptions as Y, type RadioSelectElement as Z, type RadioSelectOptions as _, type CardJSXElement as a, type SelectOptionElement as a0, type SelectOptions as a1, type TextInputElement as a2, type TextInputOptions as a3, type ModalProps as a4, type TextInputProps as a5, type SelectProps as a6, type SelectOptionProps as a7, jsx as a8, jsxs as a9, jsxDEV as aa, Fragment as ab, JSX as ac, type CardChild as b, Card as c, Fields as d, isJSX as e, fromReactElement as f, toModalElement as g, fromReactModalElement as h, isCardElement as i, isModalElement as j, Modal as k, Select as l, SelectOption as m, TextInput as n, type ActionsElement as o, type ButtonElement as p, type ButtonOptions as q, type ButtonStyle as r, type CardOptions as s, toCardElement as t, type DividerElement as u, type FieldElement as v, type FieldsElement as w, type ImageElement as x, type LinkButtonElement as y, type LinkButtonOptions as z };
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { J as ButtonProps, a as CardJSXElement, K as CardJSXProps, N as CardProps, O as ContainerProps, P as DividerProps, Q as FieldProps, ab as Fragment, U as ImageProps, ac as JSX, V as LinkButtonProps, a4 as ModalProps, a7 as SelectOptionProps, a6 as SelectProps, a5 as TextInputProps, W as TextProps, e as isJSX, a8 as jsx, aa as jsxDEV, a9 as jsxs, t as toCardElement, g as toModalElement } from './jsx-runtime-Bdt1Dwzf.js';
|
package/dist/jsx-runtime.js
CHANGED
package/docs/actions.mdx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Actions
|
|
3
|
+
description: Handle button clicks and interactive card events across platforms.
|
|
4
|
+
type: guide
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/cards
|
|
7
|
+
related:
|
|
8
|
+
- /docs/modals
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Actions let you handle button clicks, dropdown selections, and other interactive events from [cards](/docs/cards). Register handlers with `onAction` to respond when users interact with your cards.
|
|
12
|
+
|
|
13
|
+
## Handle a specific action
|
|
14
|
+
|
|
15
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
16
|
+
bot.onAction("approve", async (event) => {
|
|
17
|
+
await event.thread.post(`Order approved by ${event.user.fullName}!`);
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Handle multiple actions
|
|
22
|
+
|
|
23
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
24
|
+
bot.onAction(["approve", "reject"], async (event) => {
|
|
25
|
+
const action = event.actionId === "approve" ? "approved" : "rejected";
|
|
26
|
+
await event.thread.post(`Order ${action} by ${event.user.fullName}`);
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Catch-all handler
|
|
31
|
+
|
|
32
|
+
Register a handler without an action ID to catch all actions:
|
|
33
|
+
|
|
34
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
35
|
+
bot.onAction(async (event) => {
|
|
36
|
+
console.log(`Action: ${event.actionId}, Value: ${event.value}`);
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## ActionEvent
|
|
41
|
+
|
|
42
|
+
The `event` object passed to action handlers:
|
|
43
|
+
|
|
44
|
+
| Property | Type | Description |
|
|
45
|
+
|----------|------|-------------|
|
|
46
|
+
| `actionId` | `string` | The `id` from the Button or Select component |
|
|
47
|
+
| `value` | `string` (optional) | The `value` from the Button or selected option |
|
|
48
|
+
| `user` | `Author` | The user who clicked |
|
|
49
|
+
| `thread` | `Thread` | The thread containing the card |
|
|
50
|
+
| `messageId` | `string` | The message containing the card |
|
|
51
|
+
| `threadId` | `string` | Thread ID |
|
|
52
|
+
| `adapter` | `Adapter` | The platform adapter |
|
|
53
|
+
| `triggerId` | `string` (optional) | Platform trigger ID (used for opening modals) |
|
|
54
|
+
| `openModal` | `(modal) => Promise<void>` | Open a modal dialog |
|
|
55
|
+
| `raw` | `unknown` | Platform-specific event payload |
|
|
56
|
+
|
|
57
|
+
## Pass data with buttons
|
|
58
|
+
|
|
59
|
+
Use the `value` prop on buttons to pass extra context to your handler:
|
|
60
|
+
|
|
61
|
+
```tsx title="lib/bot.tsx"
|
|
62
|
+
<Button id="report" value="bug">Report Bug</Button>
|
|
63
|
+
<Button id="report" value="feature">Request Feature</Button>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
67
|
+
bot.onAction("report", async (event) => {
|
|
68
|
+
if (event.value === "bug") {
|
|
69
|
+
// Open bug report flow
|
|
70
|
+
} else if (event.value === "feature") {
|
|
71
|
+
// Open feature request flow
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Open a modal from an action
|
|
77
|
+
|
|
78
|
+
Use `event.openModal()` to open a [modal](/docs/modals) in response to a button click:
|
|
79
|
+
|
|
80
|
+
```tsx title="lib/bot.tsx" lineNumbers
|
|
81
|
+
import { Modal, TextInput, Select, SelectOption } from "chat";
|
|
82
|
+
|
|
83
|
+
bot.onAction("feedback", async (event) => {
|
|
84
|
+
await event.openModal(
|
|
85
|
+
<Modal callbackId="feedback_form" title="Send Feedback" submitLabel="Send">
|
|
86
|
+
<TextInput id="message" label="Your Feedback" multiline />
|
|
87
|
+
<Select id="category" label="Category">
|
|
88
|
+
<SelectOption label="Bug" value="bug" />
|
|
89
|
+
<SelectOption label="Feature" value="feature" />
|
|
90
|
+
</Select>
|
|
91
|
+
</Modal>
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
<Callout type="info">
|
|
97
|
+
Modals are currently supported on Slack. Other platforms will receive a no-op or fallback behavior.
|
|
98
|
+
</Callout>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Discord
|
|
3
|
+
description: Configure the Discord adapter with HTTP Interactions and Gateway WebSocket support.
|
|
4
|
+
type: integration
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/getting-started
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```sh title="Terminal"
|
|
12
|
+
pnpm add @chat-adapter/discord
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
18
|
+
import { Chat } from "chat";
|
|
19
|
+
import { createDiscordAdapter } from "@chat-adapter/discord";
|
|
20
|
+
|
|
21
|
+
const bot = new Chat({
|
|
22
|
+
userName: "mybot",
|
|
23
|
+
adapters: {
|
|
24
|
+
discord: createDiscordAdapter({
|
|
25
|
+
botToken: process.env.DISCORD_BOT_TOKEN!,
|
|
26
|
+
publicKey: process.env.DISCORD_PUBLIC_KEY!,
|
|
27
|
+
applicationId: process.env.DISCORD_APPLICATION_ID!,
|
|
28
|
+
mentionRoleIds: process.env.DISCORD_MENTION_ROLE_IDS?.split(","),
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
bot.onNewMention(async (thread, message) => {
|
|
34
|
+
await thread.post("Hello from Discord!");
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Discord application setup
|
|
39
|
+
|
|
40
|
+
### 1. Create application
|
|
41
|
+
|
|
42
|
+
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
|
43
|
+
2. Click **New Application** and give it a name
|
|
44
|
+
3. Note the **Application ID** from the General Information page
|
|
45
|
+
4. Copy the **Public Key** from the General Information page
|
|
46
|
+
|
|
47
|
+
### 2. Create bot
|
|
48
|
+
|
|
49
|
+
1. Go to the **Bot** section in the left sidebar
|
|
50
|
+
2. Click **Reset Token** to generate a new bot token
|
|
51
|
+
3. Copy and save the token (you won't see it again)
|
|
52
|
+
4. Enable these **Privileged Gateway Intents**:
|
|
53
|
+
- Message Content Intent
|
|
54
|
+
- Server Members Intent (if needed)
|
|
55
|
+
|
|
56
|
+
### 3. Configure interactions endpoint
|
|
57
|
+
|
|
58
|
+
1. Go to **General Information**
|
|
59
|
+
2. Set **Interactions Endpoint URL** to `https://your-domain.com/api/webhooks/discord`
|
|
60
|
+
3. Discord sends a PING to verify the endpoint
|
|
61
|
+
|
|
62
|
+
### 4. Add bot to server
|
|
63
|
+
|
|
64
|
+
1. Go to **OAuth2** then **URL Generator**
|
|
65
|
+
2. Select scopes: `bot`, `applications.commands`
|
|
66
|
+
3. Select bot permissions: Send Messages, Send Messages in Threads, Create Public Threads, Read Message History, Add Reactions, Attach Files
|
|
67
|
+
4. Copy the generated URL and open it to invite the bot to your server
|
|
68
|
+
|
|
69
|
+
## Architecture: HTTP Interactions vs Gateway
|
|
70
|
+
|
|
71
|
+
Discord has two ways to receive events:
|
|
72
|
+
|
|
73
|
+
**HTTP Interactions (default):**
|
|
74
|
+
- Receives button clicks, slash commands, and verification pings
|
|
75
|
+
- Works out of the box with serverless
|
|
76
|
+
- Does **not** receive regular messages
|
|
77
|
+
|
|
78
|
+
**Gateway WebSocket (required for messages):**
|
|
79
|
+
- Receives regular messages and reactions
|
|
80
|
+
- Requires a persistent connection
|
|
81
|
+
- In serverless environments, use a cron job to maintain the connection
|
|
82
|
+
|
|
83
|
+
## Gateway setup for serverless
|
|
84
|
+
|
|
85
|
+
### 1. Create Gateway route
|
|
86
|
+
|
|
87
|
+
```typescript title="app/api/discord/gateway/route.ts"
|
|
88
|
+
import { after } from "next/server";
|
|
89
|
+
import { bot } from "@/lib/bot";
|
|
90
|
+
|
|
91
|
+
export const maxDuration = 800;
|
|
92
|
+
|
|
93
|
+
export async function GET(request: Request): Promise<Response> {
|
|
94
|
+
const cronSecret = process.env.CRON_SECRET;
|
|
95
|
+
if (!cronSecret) {
|
|
96
|
+
return new Response("CRON_SECRET not configured", { status: 500 });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const authHeader = request.headers.get("authorization");
|
|
100
|
+
if (authHeader !== `Bearer ${cronSecret}`) {
|
|
101
|
+
return new Response("Unauthorized", { status: 401 });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const durationMs = 600 * 1000;
|
|
105
|
+
const webhookUrl = `https://${process.env.VERCEL_URL}/api/webhooks/discord`;
|
|
106
|
+
|
|
107
|
+
return bot.adapters.discord.startGatewayListener(
|
|
108
|
+
{ waitUntil: (task) => after(() => task) },
|
|
109
|
+
durationMs,
|
|
110
|
+
undefined,
|
|
111
|
+
webhookUrl
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 2. Configure Vercel Cron
|
|
117
|
+
|
|
118
|
+
```json title="vercel.json"
|
|
119
|
+
{
|
|
120
|
+
"crons": [
|
|
121
|
+
{
|
|
122
|
+
"path": "/api/discord/gateway",
|
|
123
|
+
"schedule": "*/9 * * * *"
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This runs every 9 minutes, ensuring overlap with the 10-minute listener duration.
|
|
130
|
+
|
|
131
|
+
### 3. Add environment variables
|
|
132
|
+
|
|
133
|
+
Add `CRON_SECRET` to your Vercel project settings.
|
|
134
|
+
|
|
135
|
+
## Role mentions
|
|
136
|
+
|
|
137
|
+
By default, only direct user mentions (`@BotName`) trigger `onNewMention` handlers. To also trigger on role mentions (e.g., `@AI`):
|
|
138
|
+
|
|
139
|
+
1. Create a role in your Discord server (e.g., "AI")
|
|
140
|
+
2. Assign the role to your bot
|
|
141
|
+
3. Copy the role ID (right-click role in server settings with Developer Mode enabled)
|
|
142
|
+
4. Add to `mentionRoleIds`:
|
|
143
|
+
|
|
144
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
145
|
+
createDiscordAdapter({
|
|
146
|
+
botToken: process.env.DISCORD_BOT_TOKEN!,
|
|
147
|
+
publicKey: process.env.DISCORD_PUBLIC_KEY!,
|
|
148
|
+
applicationId: process.env.DISCORD_APPLICATION_ID!,
|
|
149
|
+
mentionRoleIds: ["1457473602180878604"],
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Or set `DISCORD_MENTION_ROLE_IDS` as a comma-separated string.
|
|
154
|
+
|
|
155
|
+
## Configuration
|
|
156
|
+
|
|
157
|
+
| Option | Required | Description |
|
|
158
|
+
|--------|----------|-------------|
|
|
159
|
+
| `botToken` | Yes | Discord bot token |
|
|
160
|
+
| `publicKey` | Yes | Application public key (for webhook signature verification) |
|
|
161
|
+
| `applicationId` | Yes | Discord application ID |
|
|
162
|
+
| `mentionRoleIds` | No | Array of role IDs that trigger mention handlers |
|
|
163
|
+
|
|
164
|
+
## Environment variables
|
|
165
|
+
|
|
166
|
+
```bash title=".env.local"
|
|
167
|
+
DISCORD_BOT_TOKEN=your-bot-token
|
|
168
|
+
DISCORD_PUBLIC_KEY=your-application-public-key
|
|
169
|
+
DISCORD_APPLICATION_ID=your-application-id
|
|
170
|
+
DISCORD_MENTION_ROLE_IDS=1234567890,0987654321 # Optional
|
|
171
|
+
CRON_SECRET=your-random-secret # For Gateway cron
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Features
|
|
175
|
+
|
|
176
|
+
| Feature | Supported |
|
|
177
|
+
|---------|-----------|
|
|
178
|
+
| Mentions | Yes |
|
|
179
|
+
| Reactions (add/remove) | Yes |
|
|
180
|
+
| Cards (Embeds) | Yes |
|
|
181
|
+
| Modals | No |
|
|
182
|
+
| Streaming | Post+Edit fallback |
|
|
183
|
+
| DMs | Yes |
|
|
184
|
+
| Ephemeral messages | No (DM fallback) |
|
|
185
|
+
| File uploads | Yes |
|
|
186
|
+
| Typing indicator | Yes |
|
|
187
|
+
| Message history | Yes |
|
|
188
|
+
|
|
189
|
+
## Testing
|
|
190
|
+
|
|
191
|
+
Run a local tunnel (e.g., ngrok) to test webhooks locally:
|
|
192
|
+
|
|
193
|
+
```sh title="Terminal"
|
|
194
|
+
ngrok http 3000
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Update the Interactions Endpoint URL in the Discord Developer Portal to your ngrok URL.
|
|
198
|
+
|
|
199
|
+
## Troubleshooting
|
|
200
|
+
|
|
201
|
+
### Bot not responding to messages
|
|
202
|
+
|
|
203
|
+
1. **Check Gateway connection**: Messages require the Gateway WebSocket, not just HTTP interactions
|
|
204
|
+
2. **Verify Message Content Intent**: Enable this in the Bot settings
|
|
205
|
+
3. **Check bot permissions**: Ensure the bot can read messages in the channel
|
|
206
|
+
|
|
207
|
+
### Role mentions not triggering
|
|
208
|
+
|
|
209
|
+
1. **Verify role ID**: Enable Developer Mode in Discord settings, then right-click the role
|
|
210
|
+
2. **Check `mentionRoleIds` config**: Ensure the role ID is in the array
|
|
211
|
+
3. **Confirm bot has the role**: The bot must have the role assigned
|
|
212
|
+
|
|
213
|
+
### Signature verification failing
|
|
214
|
+
|
|
215
|
+
1. **Check public key format**: Should be a 64-character hex string (lowercase)
|
|
216
|
+
2. **Verify endpoint URL**: Must exactly match what's configured in Discord Developer Portal
|
|
217
|
+
3. **Check for body parsing**: Don't parse the request body before verification
|