chat 4.18.0 → 4.20.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 +2 -0
- package/dist/{chunk-WAB7KMH4.js → chunk-JW7GYSMH.js} +3 -3
- package/dist/chunk-JW7GYSMH.js.map +1 -0
- package/dist/index.d.ts +220 -13
- package/dist/index.js +274 -56
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/adapters/whatsapp.mdx +222 -0
- package/docs/{adapters/index.mdx → adapters.mdx} +46 -65
- package/docs/api/channel.mdx +15 -0
- package/docs/api/index.mdx +1 -0
- package/docs/api/thread.mdx +50 -0
- package/docs/contributing/building.mdx +1 -0
- package/docs/error-handling.mdx +2 -2
- package/docs/getting-started.mdx +2 -0
- package/docs/guides/code-review-hono.mdx +3 -3
- package/docs/guides/discord-nuxt.mdx +3 -2
- package/docs/guides/durable-chat-sessions-nextjs.mdx +331 -0
- package/docs/guides/meta.json +7 -1
- package/docs/guides/scheduled-posts-neon.mdx +447 -0
- package/docs/guides/slack-nextjs.mdx +3 -2
- package/docs/index.mdx +3 -1
- package/docs/meta.json +3 -4
- package/docs/slash-commands.mdx +4 -4
- package/docs/{state/index.mdx → state.mdx} +24 -12
- package/docs/threads-messages-channels.mdx +17 -0
- package/docs/usage.mdx +5 -0
- package/package.json +1 -1
- package/dist/chunk-WAB7KMH4.js.map +0 -1
- package/docs/adapters/discord.mdx +0 -217
- package/docs/adapters/gchat.mdx +0 -237
- package/docs/adapters/github.mdx +0 -222
- package/docs/adapters/linear.mdx +0 -206
- package/docs/adapters/meta.json +0 -13
- package/docs/adapters/slack.mdx +0 -314
- package/docs/adapters/teams.mdx +0 -287
- package/docs/adapters/telegram.mdx +0 -161
- package/docs/state/ioredis.mdx +0 -81
- package/docs/state/memory.mdx +0 -52
- package/docs/state/meta.json +0 -4
- package/docs/state/postgres.mdx +0 -98
- package/docs/state/redis.mdx +0 -100
- package/dist/{jsx-runtime-BYavlUk9.d.ts → jsx-runtime-C2ATKxHQ.d.ts} +95 -95
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Telegram
|
|
3
|
-
description: Configure the Telegram adapter for bot webhooks and messaging.
|
|
4
|
-
type: integration
|
|
5
|
-
prerequisites:
|
|
6
|
-
- /docs/getting-started
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```sh title="Terminal"
|
|
12
|
-
pnpm add @chat-adapter/telegram
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
The adapter auto-detects `TELEGRAM_BOT_TOKEN`, `TELEGRAM_WEBHOOK_SECRET_TOKEN`, `TELEGRAM_BOT_USERNAME`, and `TELEGRAM_API_BASE_URL` from environment variables:
|
|
18
|
-
|
|
19
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
20
|
-
import { Chat } from "chat";
|
|
21
|
-
import { createTelegramAdapter } from "@chat-adapter/telegram";
|
|
22
|
-
|
|
23
|
-
const bot = new Chat({
|
|
24
|
-
userName: "mybot",
|
|
25
|
-
adapters: {
|
|
26
|
-
telegram: createTelegramAdapter(),
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
bot.onNewMention(async (thread, message) => {
|
|
31
|
-
await thread.post(`You said: ${message.text}`);
|
|
32
|
-
});
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Webhook route
|
|
36
|
-
|
|
37
|
-
```typescript title="app/api/webhooks/telegram/route.ts" lineNumbers
|
|
38
|
-
import { bot } from "@/lib/bot";
|
|
39
|
-
|
|
40
|
-
export async function POST(request: Request): Promise<Response> {
|
|
41
|
-
return bot.webhooks.telegram(request);
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Configure this URL as your bot webhook in BotFather / Telegram API:
|
|
46
|
-
|
|
47
|
-
```sh title="Terminal"
|
|
48
|
-
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
|
|
49
|
-
-H "Content-Type: application/json" \
|
|
50
|
-
-d '{
|
|
51
|
-
"url": "https://your-domain.com/api/webhooks/telegram",
|
|
52
|
-
"secret_token": "your-secret-token"
|
|
53
|
-
}'
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Polling (local development)
|
|
57
|
-
|
|
58
|
-
When developing locally you typically can't expose a public URL for Telegram to deliver webhooks to. Polling mode uses `getUpdates` to fetch messages directly from Telegram instead — no public endpoint needed.
|
|
59
|
-
|
|
60
|
-
The `longPolling` option is entirely optional. Sensible defaults are applied when omitted.
|
|
61
|
-
|
|
62
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
63
|
-
import { Chat } from "chat";
|
|
64
|
-
import { createTelegramAdapter } from "@chat-adapter/telegram";
|
|
65
|
-
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
66
|
-
|
|
67
|
-
const telegram = createTelegramAdapter({
|
|
68
|
-
mode: "polling",
|
|
69
|
-
// Optional — fine-tune polling behavior:
|
|
70
|
-
// longPolling: { timeout: 30, dropPendingUpdates: false },
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const bot = new Chat({
|
|
74
|
-
userName: "mybot",
|
|
75
|
-
adapters: { telegram },
|
|
76
|
-
state: createMemoryState(),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Optional manual lifecycle control:
|
|
80
|
-
// await telegram.resetWebhook();
|
|
81
|
-
// await telegram.startPolling();
|
|
82
|
-
// await telegram.stopPolling();
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Auto mode
|
|
86
|
-
|
|
87
|
-
With `mode: "auto"` (the default), the adapter picks the right strategy for you. When deployed to a serverless environment like Vercel it uses webhooks; everywhere else (e.g. local dev) it falls back to polling automatically.
|
|
88
|
-
|
|
89
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
90
|
-
import { Chat } from "chat";
|
|
91
|
-
import { createTelegramAdapter } from "@chat-adapter/telegram";
|
|
92
|
-
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
93
|
-
|
|
94
|
-
const telegram = createTelegramAdapter({
|
|
95
|
-
mode: "auto", // default
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
export const bot = new Chat({
|
|
99
|
-
userName: "mybot",
|
|
100
|
-
adapters: { telegram },
|
|
101
|
-
state: createMemoryState(),
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Call initialize() so polling can start in long-running local processes:
|
|
105
|
-
void bot.initialize();
|
|
106
|
-
|
|
107
|
-
console.log(telegram.runtimeMode); // "webhook" | "polling"
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Configuration
|
|
111
|
-
|
|
112
|
-
All options are auto-detected from environment variables when not provided.
|
|
113
|
-
|
|
114
|
-
| Option | Required | Description |
|
|
115
|
-
|--------|----------|-------------|
|
|
116
|
-
| `botToken` | No* | Telegram bot token. Auto-detected from `TELEGRAM_BOT_TOKEN` |
|
|
117
|
-
| `secretToken` | No | Optional webhook secret token. Auto-detected from `TELEGRAM_WEBHOOK_SECRET_TOKEN` |
|
|
118
|
-
| `mode` | No | Adapter mode: `auto` (default), `webhook`, or `polling` |
|
|
119
|
-
| `longPolling` | No | Optional long polling config for `getUpdates` (`timeout`, `limit`, `allowedUpdates`, `deleteWebhook`, `dropPendingUpdates`, `retryDelayMs`) |
|
|
120
|
-
| `userName` | No | Bot username used for mention detection. Auto-detected from `TELEGRAM_BOT_USERNAME` or `getMe` |
|
|
121
|
-
| `apiBaseUrl` | No | Telegram API base URL. Auto-detected from `TELEGRAM_API_BASE_URL` |
|
|
122
|
-
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |
|
|
123
|
-
|
|
124
|
-
*`botToken` is required — either via config or env vars.
|
|
125
|
-
|
|
126
|
-
## Environment variables
|
|
127
|
-
|
|
128
|
-
```bash title=".env.local"
|
|
129
|
-
TELEGRAM_BOT_TOKEN=123456:ABCDEF...
|
|
130
|
-
TELEGRAM_WEBHOOK_SECRET_TOKEN=your-webhook-secret
|
|
131
|
-
TELEGRAM_BOT_USERNAME=mybot
|
|
132
|
-
# Optional (self-hosted API gateway)
|
|
133
|
-
TELEGRAM_API_BASE_URL=https://api.telegram.org
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Features
|
|
137
|
-
|
|
138
|
-
| Feature | Supported |
|
|
139
|
-
|---------|-----------|
|
|
140
|
-
| Mentions | Yes |
|
|
141
|
-
| Reactions (add/remove) | Yes |
|
|
142
|
-
| Cards | Text fallback + inline keyboard buttons/link buttons |
|
|
143
|
-
| Modals | No |
|
|
144
|
-
| Streaming | Post+Edit fallback |
|
|
145
|
-
| DMs | Yes |
|
|
146
|
-
| Ephemeral messages | No |
|
|
147
|
-
| File uploads | Single file (`sendDocument`) |
|
|
148
|
-
| Typing indicator | Yes |
|
|
149
|
-
| Message history | Cached messages seen/sent by the adapter |
|
|
150
|
-
|
|
151
|
-
## Notes
|
|
152
|
-
|
|
153
|
-
- Telegram does not expose full historical message APIs to bots. `fetchMessages` / `fetchChannelMessages` return adapter-cached messages from the current process.
|
|
154
|
-
- `listThreads` is not available for Telegram chats.
|
|
155
|
-
- Polling and webhooks are mutually exclusive in Telegram.
|
|
156
|
-
- `mode: "polling"` deletes webhook by default before calling `getUpdates`.
|
|
157
|
-
- `mode: "auto"` checks `getWebhookInfo`: if a webhook URL exists it uses webhook mode; if it is empty it falls back to polling on non-serverless runtimes without deleting webhook.
|
|
158
|
-
- If `getWebhookInfo` fails in `mode: "auto"`, the adapter stays in webhook mode (safe fallback).
|
|
159
|
-
- `Button` and `LinkButton` in card `Actions` render as inline keyboard buttons.
|
|
160
|
-
- Telegram callback data is limited to 64 bytes. Keep button `id`/`value` payloads short.
|
|
161
|
-
- Other rich card elements (images/select menus/radios) render as fallback text only.
|
package/docs/state/ioredis.mdx
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: ioredis
|
|
3
|
-
description: Alternative Redis state adapter using ioredis with Cluster and Sentinel support.
|
|
4
|
-
type: reference
|
|
5
|
-
prerequisites:
|
|
6
|
-
- /docs/getting-started
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
An alternative Redis state adapter using [ioredis](https://www.npmjs.com/package/ioredis). Use this if you already have ioredis in your project or need Redis Cluster/Sentinel support.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
```sh title="Terminal"
|
|
14
|
-
pnpm add @chat-adapter/state-ioredis
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
18
|
-
|
|
19
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
20
|
-
import { Chat } from "chat";
|
|
21
|
-
import { createIORedisState } from "@chat-adapter/state-ioredis";
|
|
22
|
-
|
|
23
|
-
const bot = new Chat({
|
|
24
|
-
userName: "mybot",
|
|
25
|
-
adapters: { /* ... */ },
|
|
26
|
-
state: createIORedisState({
|
|
27
|
-
url: process.env.REDIS_URL!,
|
|
28
|
-
}),
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Using an existing client
|
|
33
|
-
|
|
34
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
35
|
-
import Redis from "ioredis";
|
|
36
|
-
|
|
37
|
-
const client = new Redis("redis://localhost:6379");
|
|
38
|
-
|
|
39
|
-
const state = createIORedisState({ client });
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Configuration
|
|
43
|
-
|
|
44
|
-
| Option | Required | Description |
|
|
45
|
-
|--------|----------|-------------|
|
|
46
|
-
| `url` | Yes* | Redis connection URL |
|
|
47
|
-
| `client` | No | Existing `ioredis` client instance |
|
|
48
|
-
| `keyPrefix` | No | Prefix for all keys (default: `"chat-sdk"`) |
|
|
49
|
-
|
|
50
|
-
*Either `url` or `client` is required.
|
|
51
|
-
|
|
52
|
-
## When to use ioredis vs redis
|
|
53
|
-
|
|
54
|
-
**Use `@chat-adapter/state-ioredis` when:**
|
|
55
|
-
|
|
56
|
-
- You already use ioredis in your project
|
|
57
|
-
- You need Redis Cluster support
|
|
58
|
-
- You need Redis Sentinel support
|
|
59
|
-
- You prefer the ioredis API
|
|
60
|
-
|
|
61
|
-
**Use `@chat-adapter/state-redis` when:**
|
|
62
|
-
|
|
63
|
-
- You want the official Redis client
|
|
64
|
-
- You're starting a new project
|
|
65
|
-
- You don't need Cluster or Sentinel
|
|
66
|
-
|
|
67
|
-
## Key structure
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
{keyPrefix}:subscriptions - SET of subscribed thread IDs
|
|
71
|
-
{keyPrefix}:lock:{threadId} - Lock key with TTL
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Features
|
|
75
|
-
|
|
76
|
-
- Persistent subscriptions across restarts
|
|
77
|
-
- Distributed locking across multiple instances
|
|
78
|
-
- Automatic reconnection
|
|
79
|
-
- Redis Cluster support
|
|
80
|
-
- Redis Sentinel support
|
|
81
|
-
- Key prefix namespacing
|
package/docs/state/memory.mdx
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Memory
|
|
3
|
-
description: In-memory state adapter for local development and testing.
|
|
4
|
-
type: reference
|
|
5
|
-
prerequisites:
|
|
6
|
-
- /docs/getting-started
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
An in-memory state adapter for development and testing. Zero configuration required.
|
|
10
|
-
|
|
11
|
-
<Callout type="warn">
|
|
12
|
-
Only use the memory adapter for local development and testing. State is lost on restart and locks don't work across multiple instances. For production, use [Redis](/docs/state/redis), [ioredis](/docs/state/ioredis), or [PostgreSQL](/docs/state/postgres).
|
|
13
|
-
</Callout>
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
```sh title="Terminal"
|
|
18
|
-
pnpm add @chat-adapter/state-memory
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
24
|
-
import { Chat } from "chat";
|
|
25
|
-
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
26
|
-
|
|
27
|
-
const bot = new Chat({
|
|
28
|
-
userName: "mybot",
|
|
29
|
-
adapters: { /* ... */ },
|
|
30
|
-
state: createMemoryState(),
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
No configuration options are needed.
|
|
35
|
-
|
|
36
|
-
## Features
|
|
37
|
-
|
|
38
|
-
- Thread subscriptions (in-memory)
|
|
39
|
-
- Locking (single-process only)
|
|
40
|
-
- Zero configuration
|
|
41
|
-
|
|
42
|
-
## Limitations
|
|
43
|
-
|
|
44
|
-
- **Not suitable for production** — state is lost on restart
|
|
45
|
-
- **Single process only** — locks don't work across multiple instances
|
|
46
|
-
- **No persistence** — subscriptions reset when the process restarts
|
|
47
|
-
|
|
48
|
-
## When to use
|
|
49
|
-
|
|
50
|
-
- Local development
|
|
51
|
-
- Unit testing
|
|
52
|
-
- Quick prototyping
|
package/docs/state/meta.json
DELETED
package/docs/state/postgres.mdx
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: PostgreSQL
|
|
3
|
-
description: Production state adapter using pg (node-postgres).
|
|
4
|
-
type: reference
|
|
5
|
-
prerequisites:
|
|
6
|
-
- /docs/getting-started
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
Production PostgreSQL state adapter built with [pg](https://www.npmjs.com/package/pg) (node-postgres). Use this when PostgreSQL is your primary datastore and you want state persistence without a separate Redis dependency.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
```sh title="Terminal"
|
|
14
|
-
pnpm add @chat-adapter/state-pg
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
18
|
-
|
|
19
|
-
`createPostgresState()` auto-detects `POSTGRES_URL` (or `DATABASE_URL`) so you can call it with no arguments:
|
|
20
|
-
|
|
21
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
22
|
-
import { Chat } from "chat";
|
|
23
|
-
import { createPostgresState } from "@chat-adapter/state-pg";
|
|
24
|
-
|
|
25
|
-
const bot = new Chat({
|
|
26
|
-
userName: "mybot",
|
|
27
|
-
adapters: { /* ... */ },
|
|
28
|
-
state: createPostgresState(),
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
To provide a URL explicitly:
|
|
33
|
-
|
|
34
|
-
```typescript title="lib/bot.ts"
|
|
35
|
-
const state = createPostgresState({
|
|
36
|
-
url: "postgres://postgres:postgres@localhost:5432/chat",
|
|
37
|
-
});
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Using an existing client
|
|
41
|
-
|
|
42
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
43
|
-
import pg from "pg";
|
|
44
|
-
|
|
45
|
-
const client = new pg.Pool({ connectionString: process.env.POSTGRES_URL! });
|
|
46
|
-
const state = createPostgresState({ client });
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Configuration
|
|
50
|
-
|
|
51
|
-
| Option | Required | Description |
|
|
52
|
-
|--------|----------|-------------|
|
|
53
|
-
| `url` | No* | Postgres connection URL |
|
|
54
|
-
| `client` | No | Existing `pg.Pool` instance |
|
|
55
|
-
| `keyPrefix` | No | Prefix for all state rows (default: `"chat-sdk"`) |
|
|
56
|
-
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info").child("postgres")`) |
|
|
57
|
-
|
|
58
|
-
*Either `url`, `POSTGRES_URL`/`DATABASE_URL`, or `client` is required.
|
|
59
|
-
|
|
60
|
-
## Environment variables
|
|
61
|
-
|
|
62
|
-
```bash title=".env.local"
|
|
63
|
-
POSTGRES_URL=postgres://postgres:postgres@localhost:5432/chat
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Data model
|
|
67
|
-
|
|
68
|
-
The adapter creates these tables automatically on `connect()`:
|
|
69
|
-
|
|
70
|
-
```sql
|
|
71
|
-
chat_state_subscriptions
|
|
72
|
-
chat_state_locks
|
|
73
|
-
chat_state_cache
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
All rows are namespaced by `key_prefix`.
|
|
77
|
-
|
|
78
|
-
## Features
|
|
79
|
-
|
|
80
|
-
- Persistent subscriptions across restarts
|
|
81
|
-
- Distributed locking across multiple instances
|
|
82
|
-
- Key-value caching with TTL
|
|
83
|
-
- Automatic table creation on first connect
|
|
84
|
-
|
|
85
|
-
## Locking considerations
|
|
86
|
-
|
|
87
|
-
The Redis state adapters use atomic `SET NX PX` for lock acquisition, which is a single atomic operation. The PostgreSQL adapter uses `INSERT ... ON CONFLICT DO UPDATE WHERE expires_at <= now()`, which relies on Postgres row-level locking. This is safe for most workloads but under extreme contention (many processes competing for the same lock simultaneously) may behave slightly differently than Redis. For high-contention distributed locking, prefer the Redis adapter.
|
|
88
|
-
|
|
89
|
-
## Expired row cleanup
|
|
90
|
-
|
|
91
|
-
Unlike Redis (which handles TTL expiry natively), PostgreSQL does not automatically delete expired rows. The adapter performs opportunistic cleanup — expired locks are overwritten on the next `acquireLock()` call, and expired cache entries are deleted on the next `get()` call for that key.
|
|
92
|
-
|
|
93
|
-
For high-throughput deployments, you may want to run a periodic cleanup job:
|
|
94
|
-
|
|
95
|
-
```sql
|
|
96
|
-
DELETE FROM chat_state_locks WHERE expires_at <= now();
|
|
97
|
-
DELETE FROM chat_state_cache WHERE expires_at <= now();
|
|
98
|
-
```
|
package/docs/state/redis.mdx
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Redis
|
|
3
|
-
description: Production state adapter using the official redis package.
|
|
4
|
-
type: reference
|
|
5
|
-
prerequisites:
|
|
6
|
-
- /docs/getting-started
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
The recommended state adapter for production. Uses the official [redis](https://www.npmjs.com/package/redis) package.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
```sh title="Terminal"
|
|
14
|
-
pnpm add @chat-adapter/state-redis
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
18
|
-
|
|
19
|
-
`createRedisState()` auto-detects the `REDIS_URL` environment variable, so you can call it with no arguments:
|
|
20
|
-
|
|
21
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
22
|
-
import { Chat } from "chat";
|
|
23
|
-
import { createRedisState } from "@chat-adapter/state-redis";
|
|
24
|
-
|
|
25
|
-
const bot = new Chat({
|
|
26
|
-
userName: "mybot",
|
|
27
|
-
adapters: { /* ... */ },
|
|
28
|
-
state: createRedisState(),
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
To provide a URL explicitly:
|
|
33
|
-
|
|
34
|
-
```typescript title="lib/bot.ts"
|
|
35
|
-
const state = createRedisState({ url: "redis://localhost:6379" });
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Using an existing client
|
|
39
|
-
|
|
40
|
-
If you already have a connected Redis client, pass it directly:
|
|
41
|
-
|
|
42
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
43
|
-
import { createClient } from "redis";
|
|
44
|
-
|
|
45
|
-
const client = createClient({ url: "redis://localhost:6379" });
|
|
46
|
-
await client.connect();
|
|
47
|
-
|
|
48
|
-
const state = createRedisState({ client });
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Key prefix
|
|
52
|
-
|
|
53
|
-
All keys are namespaced under a configurable prefix (default: `"chat-sdk"`):
|
|
54
|
-
|
|
55
|
-
```typescript title="lib/bot.ts" lineNumbers
|
|
56
|
-
const state = createRedisState({
|
|
57
|
-
url: process.env.REDIS_URL!,
|
|
58
|
-
keyPrefix: "my-bot",
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Configuration
|
|
63
|
-
|
|
64
|
-
| Option | Required | Description |
|
|
65
|
-
|--------|----------|-------------|
|
|
66
|
-
| `url` | No* | Redis connection URL (auto-detected from `REDIS_URL`) |
|
|
67
|
-
| `client` | No | Existing `redis` client instance |
|
|
68
|
-
| `keyPrefix` | No | Prefix for all keys (default: `"chat-sdk"`) |
|
|
69
|
-
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |
|
|
70
|
-
|
|
71
|
-
*Either `url`, `REDIS_URL` env var, or `client` is required.
|
|
72
|
-
|
|
73
|
-
## Environment variables
|
|
74
|
-
|
|
75
|
-
```bash title=".env.local"
|
|
76
|
-
REDIS_URL=redis://localhost:6379
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
For serverless deployments (Vercel, AWS Lambda), use a serverless-compatible Redis provider like [Upstash](https://upstash.com).
|
|
80
|
-
|
|
81
|
-
## Key structure
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
{keyPrefix}:subscriptions - SET of subscribed thread IDs
|
|
85
|
-
{keyPrefix}:lock:{threadId} - Lock key with TTL
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Production recommendations
|
|
89
|
-
|
|
90
|
-
- Use Redis 6.0+ for best performance
|
|
91
|
-
- Enable Redis persistence (RDB or AOF)
|
|
92
|
-
- Use Redis Cluster for high availability
|
|
93
|
-
- Set appropriate memory limits
|
|
94
|
-
|
|
95
|
-
## Features
|
|
96
|
-
|
|
97
|
-
- Persistent subscriptions across restarts
|
|
98
|
-
- Distributed locking across multiple instances
|
|
99
|
-
- Automatic reconnection
|
|
100
|
-
- Key prefix namespacing
|
|
@@ -1,98 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modal elements for form dialogs.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
type ModalChild = TextInputElement | SelectElement | RadioSelectElement | TextElement | FieldsElement;
|
|
6
|
-
interface ModalElement {
|
|
7
|
-
callbackId: string;
|
|
8
|
-
children: ModalChild[];
|
|
9
|
-
closeLabel?: string;
|
|
10
|
-
notifyOnClose?: boolean;
|
|
11
|
-
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
12
|
-
privateMetadata?: string;
|
|
13
|
-
submitLabel?: string;
|
|
14
|
-
title: string;
|
|
15
|
-
type: "modal";
|
|
16
|
-
}
|
|
17
|
-
interface TextInputElement {
|
|
18
|
-
id: string;
|
|
19
|
-
initialValue?: string;
|
|
20
|
-
label: string;
|
|
21
|
-
maxLength?: number;
|
|
22
|
-
multiline?: boolean;
|
|
23
|
-
optional?: boolean;
|
|
24
|
-
placeholder?: string;
|
|
25
|
-
type: "text_input";
|
|
26
|
-
}
|
|
27
|
-
interface SelectElement {
|
|
28
|
-
id: string;
|
|
29
|
-
initialOption?: string;
|
|
30
|
-
label: string;
|
|
31
|
-
optional?: boolean;
|
|
32
|
-
options: SelectOptionElement[];
|
|
33
|
-
placeholder?: string;
|
|
34
|
-
type: "select";
|
|
35
|
-
}
|
|
36
|
-
interface SelectOptionElement {
|
|
37
|
-
description?: string;
|
|
38
|
-
label: string;
|
|
39
|
-
value: string;
|
|
40
|
-
}
|
|
41
|
-
interface RadioSelectElement {
|
|
42
|
-
id: string;
|
|
43
|
-
initialOption?: string;
|
|
44
|
-
label: string;
|
|
45
|
-
optional?: boolean;
|
|
46
|
-
options: SelectOptionElement[];
|
|
47
|
-
type: "radio_select";
|
|
48
|
-
}
|
|
49
|
-
declare function isModalElement(value: unknown): value is ModalElement;
|
|
50
|
-
interface ModalOptions {
|
|
51
|
-
callbackId: string;
|
|
52
|
-
children?: ModalChild[];
|
|
53
|
-
closeLabel?: string;
|
|
54
|
-
notifyOnClose?: boolean;
|
|
55
|
-
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
56
|
-
privateMetadata?: string;
|
|
57
|
-
submitLabel?: string;
|
|
58
|
-
title: string;
|
|
59
|
-
}
|
|
60
|
-
declare function Modal(options: ModalOptions): ModalElement;
|
|
61
|
-
interface TextInputOptions {
|
|
62
|
-
id: string;
|
|
63
|
-
initialValue?: string;
|
|
64
|
-
label: string;
|
|
65
|
-
maxLength?: number;
|
|
66
|
-
multiline?: boolean;
|
|
67
|
-
optional?: boolean;
|
|
68
|
-
placeholder?: string;
|
|
69
|
-
}
|
|
70
|
-
declare function TextInput(options: TextInputOptions): TextInputElement;
|
|
71
|
-
interface SelectOptions {
|
|
72
|
-
id: string;
|
|
73
|
-
initialOption?: string;
|
|
74
|
-
label: string;
|
|
75
|
-
optional?: boolean;
|
|
76
|
-
options: SelectOptionElement[];
|
|
77
|
-
placeholder?: string;
|
|
78
|
-
}
|
|
79
|
-
declare function Select(options: SelectOptions): SelectElement;
|
|
80
|
-
declare function SelectOption(options: {
|
|
81
|
-
label: string;
|
|
82
|
-
value: string;
|
|
83
|
-
description?: string;
|
|
84
|
-
}): SelectOptionElement;
|
|
85
|
-
interface RadioSelectOptions {
|
|
86
|
-
id: string;
|
|
87
|
-
initialOption?: string;
|
|
88
|
-
label: string;
|
|
89
|
-
optional?: boolean;
|
|
90
|
-
options: SelectOptionElement[];
|
|
91
|
-
}
|
|
92
|
-
declare function RadioSelect(options: RadioSelectOptions): RadioSelectElement;
|
|
93
|
-
type AnyModalElement = ModalElement | ModalChild | SelectOptionElement;
|
|
94
|
-
declare function fromReactModalElement(element: unknown): AnyModalElement | null;
|
|
95
|
-
|
|
96
1
|
/**
|
|
97
2
|
* Card elements for cross-platform rich messaging.
|
|
98
3
|
*
|
|
@@ -458,6 +363,101 @@ declare function fromReactElement(element: unknown): AnyCardElement | null;
|
|
|
458
363
|
*/
|
|
459
364
|
declare function cardChildToFallbackText(child: CardChild): string | null;
|
|
460
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Modal elements for form dialogs.
|
|
368
|
+
*/
|
|
369
|
+
|
|
370
|
+
type ModalChild = TextInputElement | SelectElement | RadioSelectElement | TextElement | FieldsElement;
|
|
371
|
+
interface ModalElement {
|
|
372
|
+
callbackId: string;
|
|
373
|
+
children: ModalChild[];
|
|
374
|
+
closeLabel?: string;
|
|
375
|
+
notifyOnClose?: boolean;
|
|
376
|
+
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
377
|
+
privateMetadata?: string;
|
|
378
|
+
submitLabel?: string;
|
|
379
|
+
title: string;
|
|
380
|
+
type: "modal";
|
|
381
|
+
}
|
|
382
|
+
interface TextInputElement {
|
|
383
|
+
id: string;
|
|
384
|
+
initialValue?: string;
|
|
385
|
+
label: string;
|
|
386
|
+
maxLength?: number;
|
|
387
|
+
multiline?: boolean;
|
|
388
|
+
optional?: boolean;
|
|
389
|
+
placeholder?: string;
|
|
390
|
+
type: "text_input";
|
|
391
|
+
}
|
|
392
|
+
interface SelectElement {
|
|
393
|
+
id: string;
|
|
394
|
+
initialOption?: string;
|
|
395
|
+
label: string;
|
|
396
|
+
optional?: boolean;
|
|
397
|
+
options: SelectOptionElement[];
|
|
398
|
+
placeholder?: string;
|
|
399
|
+
type: "select";
|
|
400
|
+
}
|
|
401
|
+
interface SelectOptionElement {
|
|
402
|
+
description?: string;
|
|
403
|
+
label: string;
|
|
404
|
+
value: string;
|
|
405
|
+
}
|
|
406
|
+
interface RadioSelectElement {
|
|
407
|
+
id: string;
|
|
408
|
+
initialOption?: string;
|
|
409
|
+
label: string;
|
|
410
|
+
optional?: boolean;
|
|
411
|
+
options: SelectOptionElement[];
|
|
412
|
+
type: "radio_select";
|
|
413
|
+
}
|
|
414
|
+
declare function isModalElement(value: unknown): value is ModalElement;
|
|
415
|
+
interface ModalOptions {
|
|
416
|
+
callbackId: string;
|
|
417
|
+
children?: ModalChild[];
|
|
418
|
+
closeLabel?: string;
|
|
419
|
+
notifyOnClose?: boolean;
|
|
420
|
+
/** Arbitrary string passed through the modal lifecycle (e.g., JSON context). */
|
|
421
|
+
privateMetadata?: string;
|
|
422
|
+
submitLabel?: string;
|
|
423
|
+
title: string;
|
|
424
|
+
}
|
|
425
|
+
declare function Modal(options: ModalOptions): ModalElement;
|
|
426
|
+
interface TextInputOptions {
|
|
427
|
+
id: string;
|
|
428
|
+
initialValue?: string;
|
|
429
|
+
label: string;
|
|
430
|
+
maxLength?: number;
|
|
431
|
+
multiline?: boolean;
|
|
432
|
+
optional?: boolean;
|
|
433
|
+
placeholder?: string;
|
|
434
|
+
}
|
|
435
|
+
declare function TextInput(options: TextInputOptions): TextInputElement;
|
|
436
|
+
interface SelectOptions {
|
|
437
|
+
id: string;
|
|
438
|
+
initialOption?: string;
|
|
439
|
+
label: string;
|
|
440
|
+
optional?: boolean;
|
|
441
|
+
options: SelectOptionElement[];
|
|
442
|
+
placeholder?: string;
|
|
443
|
+
}
|
|
444
|
+
declare function Select(options: SelectOptions): SelectElement;
|
|
445
|
+
declare function SelectOption(options: {
|
|
446
|
+
label: string;
|
|
447
|
+
value: string;
|
|
448
|
+
description?: string;
|
|
449
|
+
}): SelectOptionElement;
|
|
450
|
+
interface RadioSelectOptions {
|
|
451
|
+
id: string;
|
|
452
|
+
initialOption?: string;
|
|
453
|
+
label: string;
|
|
454
|
+
optional?: boolean;
|
|
455
|
+
options: SelectOptionElement[];
|
|
456
|
+
}
|
|
457
|
+
declare function RadioSelect(options: RadioSelectOptions): RadioSelectElement;
|
|
458
|
+
type AnyModalElement = ModalElement | ModalChild | SelectOptionElement;
|
|
459
|
+
declare function fromReactModalElement(element: unknown): AnyModalElement | null;
|
|
460
|
+
|
|
461
461
|
/**
|
|
462
462
|
* Custom JSX runtime for chat cards.
|
|
463
463
|
*
|