chat 4.14.0 → 4.16.0
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 +12 -0
- package/dist/{chunk-THM4ACIE.js → chunk-7S5DLTN2.js} +372 -9
- package/dist/chunk-7S5DLTN2.js.map +1 -0
- package/dist/index.d.ts +122 -5
- package/dist/index.js +367 -235
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-Bdt1Dwzf.d.ts → jsx-runtime-Bokk9xw5.d.ts} +80 -5
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +3 -1
- package/docs/adapters/discord.mdx +1 -0
- package/docs/adapters/index.mdx +51 -34
- package/docs/adapters/meta.json +1 -0
- package/docs/adapters/slack.mdx +16 -0
- package/docs/adapters/telegram.mdx +161 -0
- package/docs/api/cards.mdx +57 -1
- package/docs/api/channel.mdx +4 -1
- package/docs/api/chat.mdx +5 -0
- package/docs/api/markdown.mdx +42 -0
- package/docs/api/thread.mdx +4 -1
- package/docs/cards.mdx +44 -1
- package/docs/contributing/building.mdx +626 -0
- package/docs/contributing/documenting.mdx +218 -0
- package/docs/contributing/meta.json +4 -0
- package/docs/contributing/publishing.mdx +161 -0
- package/docs/contributing/testing.mdx +494 -0
- package/docs/error-handling.mdx +1 -1
- package/docs/getting-started.mdx +23 -1
- package/docs/handling-events.mdx +375 -0
- package/docs/index.mdx +4 -2
- package/docs/meta.json +6 -1
- package/docs/posting-messages.mdx +7 -7
- package/docs/slash-commands.mdx +23 -0
- package/docs/state/meta.json +1 -6
- package/docs/streaming.mdx +32 -0
- package/docs/threads-messages-channels.mdx +237 -0
- package/docs/usage.mdx +82 -276
- package/package.json +4 -3
- package/dist/chunk-THM4ACIE.js.map +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Threads, Messages, and Channels
|
|
3
|
+
description: Work with threads, messages, and channels across platforms.
|
|
4
|
+
type: guide
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/usage
|
|
7
|
+
related:
|
|
8
|
+
- /docs/handling-events
|
|
9
|
+
- /docs/posting-messages
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Threads
|
|
13
|
+
|
|
14
|
+
A `Thread` represents a conversation thread on any platform. It provides methods for posting messages, managing subscriptions, and accessing message history.
|
|
15
|
+
|
|
16
|
+
### Post a message
|
|
17
|
+
|
|
18
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
19
|
+
// Plain text
|
|
20
|
+
await thread.post("Hello world");
|
|
21
|
+
|
|
22
|
+
// Markdown (converted to each platform's format)
|
|
23
|
+
await thread.post("**Bold** and _italic_ text");
|
|
24
|
+
|
|
25
|
+
// Structured message with attachments
|
|
26
|
+
await thread.post({
|
|
27
|
+
markdown: "Here's a file:",
|
|
28
|
+
files: [{ data: buffer, filename: "report.pdf" }],
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Subscribe and unsubscribe
|
|
33
|
+
|
|
34
|
+
Subscriptions persist across restarts (stored in your state adapter). When a thread is subscribed, all messages route to `onSubscribedMessage`.
|
|
35
|
+
|
|
36
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
37
|
+
await thread.subscribe();
|
|
38
|
+
await thread.unsubscribe();
|
|
39
|
+
|
|
40
|
+
const subscribed = await thread.isSubscribed();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Typing indicator
|
|
44
|
+
|
|
45
|
+
```typescript title="lib/bot.ts"
|
|
46
|
+
await thread.startTyping();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
<Callout type="info">
|
|
50
|
+
Not all platforms support typing indicators. The call is a no-op on unsupported platforms. See the [adapter feature matrix](/docs/adapters) for details.
|
|
51
|
+
</Callout>
|
|
52
|
+
|
|
53
|
+
### Message history
|
|
54
|
+
|
|
55
|
+
Access recent messages or iterate through full history:
|
|
56
|
+
|
|
57
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
58
|
+
// Cached messages from the webhook payload
|
|
59
|
+
const recent = thread.recentMessages;
|
|
60
|
+
|
|
61
|
+
// Newest first (auto-paginates)
|
|
62
|
+
for await (const msg of thread.messages) {
|
|
63
|
+
console.log(msg.text);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Oldest first (auto-paginates)
|
|
67
|
+
for await (const msg of thread.allMessages) {
|
|
68
|
+
console.log(msg.text);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Thread state
|
|
73
|
+
|
|
74
|
+
Store typed, per-thread state that persists across requests. Pass a generic type parameter to `Chat` to get typed thread state across all handlers:
|
|
75
|
+
|
|
76
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
77
|
+
interface ThreadState {
|
|
78
|
+
aiMode?: boolean;
|
|
79
|
+
context?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const bot = new Chat<typeof adapters, ThreadState>({
|
|
83
|
+
// ...config
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
bot.onNewMention(async (thread) => {
|
|
87
|
+
await thread.setState({ aiMode: true });
|
|
88
|
+
|
|
89
|
+
const state = await thread.state; // ThreadState | null
|
|
90
|
+
if (state?.aiMode) {
|
|
91
|
+
// AI mode is enabled
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
State is stored in your state adapter with a 30-day TTL. Use `{ replace: true }` to replace state entirely instead of merging:
|
|
97
|
+
|
|
98
|
+
```typescript title="lib/bot.ts"
|
|
99
|
+
await thread.setState({ aiMode: false }, { replace: true });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Messages
|
|
103
|
+
|
|
104
|
+
Incoming messages are normalized across platforms into a consistent format:
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
|----------|------|-------------|
|
|
108
|
+
| `id` | `string` | Platform message ID |
|
|
109
|
+
| `threadId` | `string` | Thread ID in `adapter:channel:thread` format |
|
|
110
|
+
| `text` | `string` | Plain text content |
|
|
111
|
+
| `formatted` | `Root` | mdast AST representation |
|
|
112
|
+
| `raw` | `unknown` | Original platform-specific payload |
|
|
113
|
+
| `author` | `Author` | Message author info |
|
|
114
|
+
| `metadata` | `MessageMetadata` | Timestamps and edit status |
|
|
115
|
+
| `attachments` | `Attachment[]` (optional) | File attachments |
|
|
116
|
+
| `isMention` | `boolean` (optional) | Whether the bot was @-mentioned |
|
|
117
|
+
|
|
118
|
+
### Author
|
|
119
|
+
|
|
120
|
+
```typescript lineNumbers
|
|
121
|
+
interface Author {
|
|
122
|
+
userId: string;
|
|
123
|
+
userName: string;
|
|
124
|
+
fullName: string;
|
|
125
|
+
isBot: boolean | "unknown";
|
|
126
|
+
isMe: boolean; // true if message is from the bot itself
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Sent messages
|
|
131
|
+
|
|
132
|
+
When you post a message, you get back a `SentMessage` with methods to edit, delete, and react:
|
|
133
|
+
|
|
134
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
135
|
+
const sent = await thread.post("Processing...");
|
|
136
|
+
// Do some work...
|
|
137
|
+
await sent.edit("Done!");
|
|
138
|
+
|
|
139
|
+
// Or delete
|
|
140
|
+
await sent.delete();
|
|
141
|
+
|
|
142
|
+
// Add/remove reactions
|
|
143
|
+
await sent.addReaction(emoji.check);
|
|
144
|
+
await sent.removeReaction(emoji.check);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Channels
|
|
148
|
+
|
|
149
|
+
A `Channel` represents the container that holds threads (e.g., a Slack channel, a Teams conversation). Navigate to a channel from a thread or get one directly:
|
|
150
|
+
|
|
151
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
152
|
+
// From a thread
|
|
153
|
+
const channel = thread.channel;
|
|
154
|
+
|
|
155
|
+
// Directly by ID
|
|
156
|
+
const channel = bot.channel("slack:C123ABC");
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### List threads
|
|
160
|
+
|
|
161
|
+
Iterate threads in a channel, most recently active first:
|
|
162
|
+
|
|
163
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
164
|
+
for await (const thread of channel.threads()) {
|
|
165
|
+
console.log(thread.rootMessage.text, thread.replyCount);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Channel messages
|
|
170
|
+
|
|
171
|
+
Iterate top-level messages (not thread replies):
|
|
172
|
+
|
|
173
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
174
|
+
for await (const msg of channel.messages) {
|
|
175
|
+
console.log(msg.text);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Post to a channel
|
|
180
|
+
|
|
181
|
+
Post a top-level message (not inside a thread):
|
|
182
|
+
|
|
183
|
+
```typescript title="lib/bot.ts"
|
|
184
|
+
await channel.post("Hello channel!");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Channel metadata
|
|
188
|
+
|
|
189
|
+
```typescript title="lib/bot.ts"
|
|
190
|
+
const info = await channel.fetchMetadata();
|
|
191
|
+
console.log(info.name, info.memberCount);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Thread ID format
|
|
195
|
+
|
|
196
|
+
All thread IDs follow the pattern `{adapter}:{channel}:{thread}`:
|
|
197
|
+
|
|
198
|
+
- **Slack**: `slack:C123ABC:1234567890.123456`
|
|
199
|
+
- **Teams**: `teams:{base64(conversationId)}:{base64(serviceUrl)}`
|
|
200
|
+
- **Google Chat**: `gchat:spaces/ABC123:{base64(threadName)}`
|
|
201
|
+
- **Discord**: `discord:{guildId}:{channelId}/{messageId}`
|
|
202
|
+
|
|
203
|
+
You typically don't need to construct these yourself — they're provided by the SDK in event handlers.
|
|
204
|
+
|
|
205
|
+
## Logging
|
|
206
|
+
|
|
207
|
+
The `logger` option is optional — if omitted, Chat SDK uses `ConsoleLogger("info")` by default. Each adapter also creates its own child logger automatically.
|
|
208
|
+
|
|
209
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
210
|
+
// Use defaults (ConsoleLogger at "info" level)
|
|
211
|
+
const bot = new Chat({
|
|
212
|
+
// ...
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Or set a specific log level
|
|
216
|
+
const bot = new Chat({
|
|
217
|
+
// ...
|
|
218
|
+
logger: "debug", // "debug" | "info" | "warn" | "error" | "silent"
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Or use a custom ConsoleLogger for child loggers
|
|
222
|
+
import { ConsoleLogger } from "chat";
|
|
223
|
+
|
|
224
|
+
const logger = new ConsoleLogger("info");
|
|
225
|
+
const bot = new Chat({
|
|
226
|
+
// ...
|
|
227
|
+
logger,
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
You can pass child loggers to adapters for prefixed log output, but adapters create their own child loggers by default:
|
|
232
|
+
|
|
233
|
+
```typescript title="lib/bot.ts"
|
|
234
|
+
createSlackAdapter({
|
|
235
|
+
logger: logger.child("slack"), // optional — auto-created if omitted
|
|
236
|
+
});
|
|
237
|
+
```
|
package/docs/usage.mdx
CHANGED
|
@@ -1,342 +1,148 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description:
|
|
2
|
+
title: Creating a Chat Instance
|
|
3
|
+
description: Initialize the Chat class with adapters, state, and configuration options.
|
|
4
4
|
type: guide
|
|
5
5
|
prerequisites:
|
|
6
6
|
- /docs/getting-started
|
|
7
|
+
related:
|
|
8
|
+
- /docs/handling-events
|
|
9
|
+
- /docs/adapters
|
|
10
|
+
- /docs/state
|
|
7
11
|
---
|
|
8
12
|
|
|
9
|
-
Chat
|
|
13
|
+
The `Chat` class is the main entry point for your bot. It coordinates adapters, routes events to your handlers, and manages thread state.
|
|
10
14
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
### Mention handler
|
|
14
|
-
|
|
15
|
-
`onNewMention` fires when your bot is @-mentioned in a thread it hasn't subscribed to yet. This is the primary entry point for new conversations.
|
|
15
|
+
## Basic setup
|
|
16
16
|
|
|
17
17
|
```typescript title="lib/bot.ts" lineNumbers
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Subscribed message handler
|
|
18
|
+
import { Chat } from "chat";
|
|
19
|
+
import { createSlackAdapter } from "@chat-adapter/slack";
|
|
20
|
+
import { createRedisState } from "@chat-adapter/state-redis";
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
await thread.post("You mentioned me!");
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
await thread.post(`Got your message: ${message.text}`);
|
|
22
|
+
const bot = new Chat({
|
|
23
|
+
userName: "mybot",
|
|
24
|
+
adapters: {
|
|
25
|
+
slack: createSlackAdapter(),
|
|
26
|
+
},
|
|
27
|
+
state: createRedisState(),
|
|
37
28
|
});
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Pattern handler
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
bot.onNewMessage(/^help$/i, async (thread, message) => {
|
|
46
|
-
await thread.post("Here's how I can help...");
|
|
30
|
+
bot.onNewMention(async (thread) => {
|
|
31
|
+
await thread.subscribe();
|
|
32
|
+
await thread.post("Hello! I'm listening to this thread.");
|
|
47
33
|
});
|
|
48
34
|
```
|
|
49
35
|
|
|
50
|
-
|
|
36
|
+
Each adapter factory auto-detects credentials from environment variables (`SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, `REDIS_URL`, etc.), so you can get started with zero config. Pass explicit values to override.
|
|
51
37
|
|
|
52
|
-
|
|
38
|
+
## Multiple adapters
|
|
53
39
|
|
|
54
|
-
|
|
55
|
-
bot.onSlashCommand("/status", async (event) => {
|
|
56
|
-
await event.channel.post("All systems operational!");
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Reaction handler
|
|
61
|
-
|
|
62
|
-
`onReaction` fires when users add or remove emoji reactions to messages.
|
|
40
|
+
Register multiple adapters to deploy your bot across platforms simultaneously:
|
|
63
41
|
|
|
64
42
|
```typescript title="lib/bot.ts" lineNumbers
|
|
65
|
-
import {
|
|
43
|
+
import { Chat } from "chat";
|
|
44
|
+
import { createSlackAdapter } from "@chat-adapter/slack";
|
|
45
|
+
import { createTeamsAdapter } from "@chat-adapter/teams";
|
|
46
|
+
import { createDiscordAdapter } from "@chat-adapter/discord";
|
|
47
|
+
import { createRedisState } from "@chat-adapter/state-redis";
|
|
66
48
|
|
|
67
|
-
bot
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
The `event` object includes:
|
|
79
|
-
|
|
80
|
-
| Property | Type | Description |
|
|
81
|
-
|----------|------|-------------|
|
|
82
|
-
| `emoji` | `EmojiValue` | Normalized emoji for comparison |
|
|
83
|
-
| `rawEmoji` | `string` | Platform-specific emoji string |
|
|
84
|
-
| `added` | `boolean` | `true` if added, `false` if removed |
|
|
85
|
-
| `user` | `Author` | The user who reacted |
|
|
86
|
-
| `message` | `Message` (optional) | The message that was reacted to |
|
|
87
|
-
| `thread` | `Thread` | Thread for posting replies |
|
|
88
|
-
| `adapter` | `Adapter` | The platform adapter |
|
|
89
|
-
|
|
90
|
-
### Assistant thread handler
|
|
91
|
-
|
|
92
|
-
`onAssistantThreadStarted` fires when a user opens a new assistant thread in Slack. Use it with the Slack Assistants API to set suggested prompts and status indicators. See [Slack Assistants API](/docs/adapters/slack#slack-assistants-api) for details.
|
|
93
|
-
|
|
94
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
95
|
-
bot.onAssistantThreadStarted(async (event) => {
|
|
96
|
-
const slack = bot.getAdapter("slack") as SlackAdapter;
|
|
97
|
-
await slack.setSuggestedPrompts(event.channelId, event.threadTs, [
|
|
98
|
-
{ title: "Get started", message: "What can you help me with?" },
|
|
99
|
-
]);
|
|
49
|
+
const bot = new Chat({
|
|
50
|
+
userName: "mybot",
|
|
51
|
+
adapters: {
|
|
52
|
+
slack: createSlackAdapter(),
|
|
53
|
+
teams: createTeamsAdapter(),
|
|
54
|
+
discord: createDiscordAdapter(),
|
|
55
|
+
},
|
|
56
|
+
state: createRedisState(),
|
|
100
57
|
});
|
|
101
58
|
```
|
|
102
59
|
|
|
103
|
-
|
|
60
|
+
Your event handlers work identically across all registered adapters — the SDK normalizes messages, threads, and reactions into a consistent format.
|
|
104
61
|
|
|
105
|
-
|
|
62
|
+
## Configuration options
|
|
106
63
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
64
|
+
| Option | Type | Default | Description |
|
|
65
|
+
|--------|------|---------|-------------|
|
|
66
|
+
| `userName` | `string` | *required* | Default bot username across all adapters |
|
|
67
|
+
| `adapters` | `Record<string, Adapter>` | *required* | Map of adapter name to adapter instance |
|
|
68
|
+
| `state` | `StateAdapter` | *required* | State adapter for subscriptions and locking |
|
|
69
|
+
| `logger` | `Logger \| LogLevel` | `"info"` | Logger instance or log level (`"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"`) |
|
|
70
|
+
| `dedupeTtlMs` | `number` | `300000` | TTL in ms for message deduplication (5 minutes) |
|
|
71
|
+
| `streamingUpdateIntervalMs` | `number` | `500` | Update interval in ms for post+edit streaming |
|
|
72
|
+
| `fallbackStreamingPlaceholderText` | `string \| null` | `"..."` | Placeholder text while streaming starts. Set to `null` to skip |
|
|
116
73
|
|
|
117
|
-
##
|
|
74
|
+
## Accessing adapters
|
|
118
75
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
### Post a message
|
|
76
|
+
Use `getAdapter` to access platform-specific APIs when you need functionality beyond the unified interface:
|
|
122
77
|
|
|
123
78
|
```typescript title="lib/bot.ts" lineNumbers
|
|
124
|
-
|
|
125
|
-
await thread.post("Hello world");
|
|
126
|
-
|
|
127
|
-
// Markdown (converted to each platform's format)
|
|
128
|
-
await thread.post("**Bold** and _italic_ text");
|
|
79
|
+
import type { SlackAdapter } from "@chat-adapter/slack";
|
|
129
80
|
|
|
130
|
-
|
|
131
|
-
await
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
81
|
+
const slack = bot.getAdapter("slack") as SlackAdapter;
|
|
82
|
+
await slack.setSuggestedPrompts(channelId, threadTs, [
|
|
83
|
+
{ title: "Get started", message: "What can you help me with?" },
|
|
84
|
+
]);
|
|
135
85
|
```
|
|
136
86
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
Subscriptions persist across restarts (stored in your state adapter). When a thread is subscribed, all messages route to `onSubscribedMessage`.
|
|
140
|
-
|
|
141
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
142
|
-
await thread.subscribe();
|
|
143
|
-
await thread.unsubscribe();
|
|
87
|
+
## Webhook routing
|
|
144
88
|
|
|
145
|
-
|
|
146
|
-
```
|
|
89
|
+
The `webhooks` property provides type-safe handlers for each registered adapter. Wire these up to your HTTP framework's routes:
|
|
147
90
|
|
|
148
|
-
|
|
91
|
+
```typescript title="app/api/webhooks/slack/route.ts" lineNumbers
|
|
92
|
+
import { bot } from "@/lib/bot";
|
|
149
93
|
|
|
150
|
-
|
|
151
|
-
await thread.startTyping();
|
|
94
|
+
export const POST = bot.webhooks.slack;
|
|
152
95
|
```
|
|
153
96
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</Callout>
|
|
157
|
-
|
|
158
|
-
### Message history
|
|
97
|
+
```typescript title="app/api/webhooks/teams/route.ts" lineNumbers
|
|
98
|
+
import { bot } from "@/lib/bot";
|
|
159
99
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
163
|
-
// Cached messages from the webhook payload
|
|
164
|
-
const recent = thread.recentMessages;
|
|
165
|
-
|
|
166
|
-
// Newest first (auto-paginates)
|
|
167
|
-
for await (const msg of thread.messages) {
|
|
168
|
-
console.log(msg.text);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Oldest first (auto-paginates)
|
|
172
|
-
for await (const msg of thread.allMessages) {
|
|
173
|
-
console.log(msg.text);
|
|
174
|
-
}
|
|
100
|
+
export const POST = bot.webhooks.teams;
|
|
175
101
|
```
|
|
176
102
|
|
|
177
|
-
|
|
103
|
+
## Lifecycle
|
|
178
104
|
|
|
179
|
-
|
|
105
|
+
The Chat instance initializes lazily on the first webhook. You can also initialize manually:
|
|
180
106
|
|
|
181
107
|
```typescript title="lib/bot.ts" lineNumbers
|
|
182
|
-
|
|
183
|
-
aiMode?: boolean;
|
|
184
|
-
context?: string;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const bot = new Chat<typeof adapters, ThreadState>({
|
|
188
|
-
// ...config
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
bot.onNewMention(async (thread) => {
|
|
192
|
-
await thread.setState({ aiMode: true });
|
|
193
|
-
|
|
194
|
-
const state = await thread.state; // ThreadState | null
|
|
195
|
-
if (state?.aiMode) {
|
|
196
|
-
// AI mode is enabled
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
State is stored in your state adapter with a 30-day TTL. Use `{ replace: true }` to replace state entirely instead of merging:
|
|
202
|
-
|
|
203
|
-
```typescript title="lib/bot.ts"
|
|
204
|
-
await thread.setState({ aiMode: false }, { replace: true });
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## Messages
|
|
208
|
-
|
|
209
|
-
Incoming messages are normalized across platforms into a consistent format:
|
|
210
|
-
|
|
211
|
-
| Property | Type | Description |
|
|
212
|
-
|----------|------|-------------|
|
|
213
|
-
| `id` | `string` | Platform message ID |
|
|
214
|
-
| `threadId` | `string` | Thread ID in `adapter:channel:thread` format |
|
|
215
|
-
| `text` | `string` | Plain text content |
|
|
216
|
-
| `formatted` | `Root` | mdast AST representation |
|
|
217
|
-
| `raw` | `unknown` | Original platform-specific payload |
|
|
218
|
-
| `author` | `Author` | Message author info |
|
|
219
|
-
| `metadata` | `MessageMetadata` | Timestamps and edit status |
|
|
220
|
-
| `attachments` | `Attachment[]` (optional) | File attachments |
|
|
221
|
-
| `isMention` | `boolean` (optional) | Whether the bot was @-mentioned |
|
|
222
|
-
|
|
223
|
-
### Author
|
|
224
|
-
|
|
225
|
-
```typescript lineNumbers
|
|
226
|
-
interface Author {
|
|
227
|
-
userId: string;
|
|
228
|
-
userName: string;
|
|
229
|
-
fullName: string;
|
|
230
|
-
isBot: boolean | "unknown";
|
|
231
|
-
isMe: boolean; // true if message is from the bot itself
|
|
232
|
-
}
|
|
108
|
+
await bot.initialize();
|
|
233
109
|
```
|
|
234
110
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
When you post a message, you get back a `SentMessage` with methods to edit, delete, and react:
|
|
111
|
+
For graceful shutdown (e.g. in serverless teardown), call `shutdown`:
|
|
238
112
|
|
|
239
113
|
```typescript title="lib/bot.ts" lineNumbers
|
|
240
|
-
|
|
241
|
-
// Do some work...
|
|
242
|
-
await sent.edit("Done!");
|
|
243
|
-
|
|
244
|
-
// Or delete
|
|
245
|
-
await sent.delete();
|
|
246
|
-
|
|
247
|
-
// Add/remove reactions
|
|
248
|
-
await sent.addReaction(emoji.check);
|
|
249
|
-
await sent.removeReaction(emoji.check);
|
|
114
|
+
await bot.shutdown();
|
|
250
115
|
```
|
|
251
116
|
|
|
252
|
-
##
|
|
117
|
+
## Singleton pattern
|
|
253
118
|
|
|
254
|
-
|
|
119
|
+
Register a singleton when you need to access the Chat instance from multiple files:
|
|
255
120
|
|
|
256
121
|
```typescript title="lib/bot.ts" lineNumbers
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// Directly by ID
|
|
261
|
-
const channel = bot.channel("slack:C123ABC");
|
|
122
|
+
const bot = new Chat({ /* ...config */ }).registerSingleton();
|
|
123
|
+
export default bot;
|
|
262
124
|
```
|
|
263
125
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Iterate threads in a channel, most recently active first:
|
|
126
|
+
```typescript title="lib/utils.ts" lineNumbers
|
|
127
|
+
import { Chat } from "chat";
|
|
267
128
|
|
|
268
|
-
|
|
269
|
-
for await (const thread of channel.threads()) {
|
|
270
|
-
console.log(thread.rootMessage.text, thread.replyCount);
|
|
271
|
-
}
|
|
129
|
+
const bot = Chat.getSingleton();
|
|
272
130
|
```
|
|
273
131
|
|
|
274
|
-
|
|
132
|
+
## Direct messaging
|
|
275
133
|
|
|
276
|
-
|
|
134
|
+
Open a DM thread with a user by passing their platform user ID or an `Author` object:
|
|
277
135
|
|
|
278
136
|
```typescript title="lib/bot.ts" lineNumbers
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
137
|
+
const dm = await bot.openDM("slack:U123ABC");
|
|
138
|
+
await dm.post("Hey! Just wanted to follow up on your request.");
|
|
282
139
|
```
|
|
283
140
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
Post a top-level message (not inside a thread):
|
|
141
|
+
## Channel access
|
|
287
142
|
|
|
288
|
-
|
|
289
|
-
await channel.post("Hello channel!");
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Channel metadata
|
|
293
|
-
|
|
294
|
-
```typescript title="lib/bot.ts"
|
|
295
|
-
const info = await channel.fetchMetadata();
|
|
296
|
-
console.log(info.name, info.memberCount);
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
## Thread ID format
|
|
300
|
-
|
|
301
|
-
All thread IDs follow the pattern `{adapter}:{channel}:{thread}`:
|
|
302
|
-
|
|
303
|
-
- **Slack**: `slack:C123ABC:1234567890.123456`
|
|
304
|
-
- **Teams**: `teams:{base64(conversationId)}:{base64(serviceUrl)}`
|
|
305
|
-
- **Google Chat**: `gchat:spaces/ABC123:{base64(threadName)}`
|
|
306
|
-
- **Discord**: `discord:{guildId}:{channelId}/{messageId}`
|
|
307
|
-
|
|
308
|
-
You typically don't need to construct these yourself — they're provided by the SDK in event handlers.
|
|
309
|
-
|
|
310
|
-
## Logging
|
|
311
|
-
|
|
312
|
-
The `logger` option is optional — if omitted, Chat SDK uses `ConsoleLogger("info")` by default. Each adapter also creates its own child logger automatically.
|
|
143
|
+
Get a channel directly by its ID:
|
|
313
144
|
|
|
314
145
|
```typescript title="lib/bot.ts" lineNumbers
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// ...
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// Or set a specific log level
|
|
321
|
-
const bot = new Chat({
|
|
322
|
-
// ...
|
|
323
|
-
logger: "debug", // "debug" | "info" | "warn" | "error" | "silent"
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
// Or use a custom ConsoleLogger for child loggers
|
|
327
|
-
import { ConsoleLogger } from "chat";
|
|
328
|
-
|
|
329
|
-
const logger = new ConsoleLogger("info");
|
|
330
|
-
const bot = new Chat({
|
|
331
|
-
// ...
|
|
332
|
-
logger,
|
|
333
|
-
});
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
You can pass child loggers to adapters for prefixed log output, but adapters create their own child loggers by default:
|
|
337
|
-
|
|
338
|
-
```typescript title="lib/bot.ts"
|
|
339
|
-
createSlackAdapter({
|
|
340
|
-
logger: logger.child("slack"), // optional — auto-created if omitted
|
|
341
|
-
});
|
|
146
|
+
const channel = bot.channel("slack:C123ABC");
|
|
147
|
+
await channel.post("Announcement: deploy complete!");
|
|
342
148
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.16.0",
|
|
4
4
|
"description": "Unified chat abstraction for Slack, Teams, Google Chat, and Discord",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,14 +30,15 @@
|
|
|
30
30
|
"remark-gfm": "^4.0.0",
|
|
31
31
|
"remark-parse": "^11.0.0",
|
|
32
32
|
"remark-stringify": "^11.0.0",
|
|
33
|
+
"remend": "^1.2.1",
|
|
33
34
|
"unified": "^11.0.5"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/mdast": "^4.0.4",
|
|
37
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^25.3.2",
|
|
38
39
|
"tsup": "^8.3.5",
|
|
39
40
|
"typescript": "^5.7.2",
|
|
40
|
-
"vitest": "^
|
|
41
|
+
"vitest": "^4.0.18"
|
|
41
42
|
},
|
|
42
43
|
"repository": {
|
|
43
44
|
"type": "git",
|