@teactjs/runtime 0.1.0-alpha.1
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 +336 -0
- package/dist/index.js +23117 -0
- package/dist/react/src/callback-registry.d.ts +11 -0
- package/dist/react/src/callback-registry.d.ts.map +1 -0
- package/dist/react/src/components.d.ts +396 -0
- package/dist/react/src/components.d.ts.map +1 -0
- package/dist/react/src/error-boundary.d.ts +19 -0
- package/dist/react/src/error-boundary.d.ts.map +1 -0
- package/dist/react/src/hooks.d.ts +37 -0
- package/dist/react/src/hooks.d.ts.map +1 -0
- package/dist/react/src/index.d.ts +11 -0
- package/dist/react/src/index.d.ts.map +1 -0
- package/dist/renderer/src/index.d.ts +5 -0
- package/dist/renderer/src/index.d.ts.map +1 -0
- package/dist/renderer/src/nodes.d.ts +23 -0
- package/dist/renderer/src/nodes.d.ts.map +1 -0
- package/dist/renderer/src/renderer.d.ts +9 -0
- package/dist/renderer/src/renderer.d.ts.map +1 -0
- package/dist/renderer/src/types.d.ts +36 -0
- package/dist/renderer/src/types.d.ts.map +1 -0
- package/dist/runtime/src/auth-session.d.ts +51 -0
- package/dist/runtime/src/auth-session.d.ts.map +1 -0
- package/dist/runtime/src/auth.d.ts +37 -0
- package/dist/runtime/src/auth.d.ts.map +1 -0
- package/dist/runtime/src/bot.d.ts +143 -0
- package/dist/runtime/src/bot.d.ts.map +1 -0
- package/dist/runtime/src/config.d.ts +38 -0
- package/dist/runtime/src/config.d.ts.map +1 -0
- package/dist/runtime/src/context.d.ts +74 -0
- package/dist/runtime/src/context.d.ts.map +1 -0
- package/dist/runtime/src/conversation.d.ts +310 -0
- package/dist/runtime/src/conversation.d.ts.map +1 -0
- package/dist/runtime/src/event-hooks.d.ts +49 -0
- package/dist/runtime/src/event-hooks.d.ts.map +1 -0
- package/dist/runtime/src/i18n.d.ts +55 -0
- package/dist/runtime/src/i18n.d.ts.map +1 -0
- package/dist/runtime/src/index.d.ts +28 -0
- package/dist/runtime/src/index.d.ts.map +1 -0
- package/dist/runtime/src/invoice.d.ts +120 -0
- package/dist/runtime/src/invoice.d.ts.map +1 -0
- package/dist/runtime/src/media-hooks.d.ts +178 -0
- package/dist/runtime/src/media-hooks.d.ts.map +1 -0
- package/dist/runtime/src/middleware.d.ts +26 -0
- package/dist/runtime/src/middleware.d.ts.map +1 -0
- package/dist/runtime/src/plugin.d.ts +32 -0
- package/dist/runtime/src/plugin.d.ts.map +1 -0
- package/dist/runtime/src/router.d.ts +168 -0
- package/dist/runtime/src/router.d.ts.map +1 -0
- package/dist/runtime/src/session.d.ts +20 -0
- package/dist/runtime/src/session.d.ts.map +1 -0
- package/dist/runtime/src/stream.d.ts +34 -0
- package/dist/runtime/src/stream.d.ts.map +1 -0
- package/dist/telegram/src/adapter.d.ts +83 -0
- package/dist/telegram/src/adapter.d.ts.map +1 -0
- package/dist/telegram/src/conversations.d.ts +175 -0
- package/dist/telegram/src/conversations.d.ts.map +1 -0
- package/dist/telegram/src/index.d.ts +8 -0
- package/dist/telegram/src/index.d.ts.map +1 -0
- package/dist/telegram/src/serialize.d.ts +80 -0
- package/dist/telegram/src/serialize.d.ts.map +1 -0
- package/dist/telegram/src/stream.d.ts +12 -0
- package/dist/telegram/src/stream.d.ts.map +1 -0
- package/package.json +33 -0
- package/src/index.ts +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# @teactjs/runtime
|
|
2
|
+
|
|
3
|
+
The bot engine powering Teact. Provides `createBot`, routing, sessions, hooks, middleware, conversations, forms, streaming, authentication, events, i18n, and payments.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @teactjs/runtime
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## createBot
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createBot } from "@teactjs/runtime";
|
|
15
|
+
import { TelegramAdapter } from "@teactjs/telegram";
|
|
16
|
+
|
|
17
|
+
const bot = createBot({
|
|
18
|
+
component: App, // root component (or use `router`)
|
|
19
|
+
adapter: new TelegramAdapter(),
|
|
20
|
+
token: process.env.BOT_TOKEN,
|
|
21
|
+
mode: "polling", // "polling" | "webhook"
|
|
22
|
+
session: { ttl: 3600_000 },
|
|
23
|
+
plugins: [storagePlugin()],
|
|
24
|
+
commands: {
|
|
25
|
+
start: { description: "Start the bot", route: "/" },
|
|
26
|
+
help: { description: "Show help", handler: (ctx) => ctx.reply("Help text") },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await bot.start();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Options:** `component` or `router` (mutually exclusive), `adapter`, `token`, `mode`, `webhook` (`{ domain, port?, path?, secretToken? }`), `session` (`{ store?, ttl? }`), `middleware`, `plugins`, `commands`, `providers`, `experimental`.
|
|
34
|
+
|
|
35
|
+
## Routing
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { createRouter } from "@teactjs/runtime";
|
|
39
|
+
|
|
40
|
+
const router = createRouter(
|
|
41
|
+
{
|
|
42
|
+
"/": Home,
|
|
43
|
+
"/settings": { component: Settings, beforeLoad: guardAuth },
|
|
44
|
+
"/pokemon/:id": PokemonDetail,
|
|
45
|
+
},
|
|
46
|
+
{ notFound: NotFoundPage }, // optional custom 404
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const bot = createBot({ router, adapter, token });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Route Guards
|
|
53
|
+
|
|
54
|
+
Guards run before a route loads. They can redirect, reply with a message, or render JSX:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// Redirect
|
|
58
|
+
beforeLoad: ({ session }) => {
|
|
59
|
+
if (!session.auth) return redirect("/login");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Reply with a message and buttons (same API as command handlers)
|
|
63
|
+
beforeLoad: ({ session, reply }) => {
|
|
64
|
+
if (!session.auth) return reply("Please log in.", {
|
|
65
|
+
buttons: [[{ text: "Login", route: "/login" }, { text: "Home", route: "/" }]],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Return JSX directly
|
|
70
|
+
beforeLoad: ({ session }) => {
|
|
71
|
+
if (!session.auth) return <LoginPage />;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Navigation Hooks
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
function Home() {
|
|
79
|
+
const navigate = useNavigate();
|
|
80
|
+
const { path, params } = useRoute();
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Message text="Home">
|
|
84
|
+
<InlineKeyboard>
|
|
85
|
+
<Button text="Settings" onClick={() => navigate("/settings")} />
|
|
86
|
+
<Button text="Back" onClick={() => navigate("/", { mode: "dismiss" })} />
|
|
87
|
+
</InlineKeyboard>
|
|
88
|
+
</Message>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- `useNavigate()` -- returns `(path, opts?) => void`. Modes: `push`, `replace`, `stack`, `dismiss`.
|
|
94
|
+
- `useParams<T>()` -- typed route params.
|
|
95
|
+
- `useRoute()` -- current `{ path, params }`.
|
|
96
|
+
|
|
97
|
+
## Context Hooks
|
|
98
|
+
|
|
99
|
+
| Hook | Returns |
|
|
100
|
+
|------|---------|
|
|
101
|
+
| `useBot()` | Bot instance and metadata |
|
|
102
|
+
| `useSession()` | `[sessionData, patchFn]` -- read and update session |
|
|
103
|
+
| `usePlatform()` | Current platform string |
|
|
104
|
+
| `useChatId()` | Current chat ID |
|
|
105
|
+
| `useText()` | Last message text |
|
|
106
|
+
| `useCallbackData()` | Last callback query data |
|
|
107
|
+
| `useCommand()` | Parsed command and args |
|
|
108
|
+
|
|
109
|
+
## Conversations
|
|
110
|
+
|
|
111
|
+
Multi-step user flows with validation.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
const { step, value, actions } = useConversation({
|
|
115
|
+
name: { prompt: "What's your name?", validate: (v) => v.length > 0 || "Name required" },
|
|
116
|
+
age: { prompt: "How old are you?", validate: (v) => Number(v) > 0 || "Must be a number" },
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Declarative alternative:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<Conversation>
|
|
124
|
+
<Conversation.Prompt text="What's your name?" step="name" />
|
|
125
|
+
<Conversation.Prompt text="How old are you?" step="age" />
|
|
126
|
+
<Conversation.Complete>
|
|
127
|
+
{(data) => <Message text={`Hi ${data.name}, age ${data.age}`} />}
|
|
128
|
+
</Conversation.Complete>
|
|
129
|
+
</Conversation>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Forms
|
|
133
|
+
|
|
134
|
+
Schema-driven forms with `useForm`:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
const form = useForm({
|
|
138
|
+
name: { type: "string", required: true },
|
|
139
|
+
email: { type: "string", validate: validateEmail },
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Events
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
useOn("message:text", (ctx) => {
|
|
147
|
+
console.log("Text received:", ctx.raw);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const photo = useEventData("message:photo");
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Supported events include `message`, `message:text`, `message:photo`, `callback_query`, `pre_checkout_query`, `successful_payment`, and `*` for all events.
|
|
154
|
+
|
|
155
|
+
## Media Hooks
|
|
156
|
+
|
|
157
|
+
Send media programmatically:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const sendPhoto = usePhoto();
|
|
161
|
+
const sendVideo = useVideo();
|
|
162
|
+
const sendDocument = useDocument();
|
|
163
|
+
const sendVoice = useVoice();
|
|
164
|
+
const sendAudio = useAudio();
|
|
165
|
+
const sendSticker = useSticker();
|
|
166
|
+
const sendLocation = useLocation();
|
|
167
|
+
const sendContact = useContact();
|
|
168
|
+
const sendPoll = usePoll();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Each returns an async function that calls the Telegram API directly.
|
|
172
|
+
|
|
173
|
+
## Streaming
|
|
174
|
+
|
|
175
|
+
Real-time text updates:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
const { text, isStreaming, stream } = useStream({ throttleMs: 100 });
|
|
179
|
+
|
|
180
|
+
async function handleStream() {
|
|
181
|
+
await stream(generateTokens());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return <Message text={isStreaming ? text : "Done: " + text} />;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Authentication
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { authPlugin, useAuth } from "@teactjs/runtime";
|
|
191
|
+
|
|
192
|
+
// Register the plugin
|
|
193
|
+
const bot = createBot({
|
|
194
|
+
plugins: [authPlugin({ admins: [123456789] })],
|
|
195
|
+
// ...
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// In a component
|
|
199
|
+
function Admin() {
|
|
200
|
+
const { isAdmin, role, is, hasAny } = useAuth();
|
|
201
|
+
if (!isAdmin) return <Message text="Unauthorized" />;
|
|
202
|
+
return <Message text="Admin panel" />;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Auth Sessions
|
|
207
|
+
|
|
208
|
+
Token-based auth backed by the session store:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { useAuthSession } from "@teactjs/runtime";
|
|
212
|
+
|
|
213
|
+
function LoginPage() {
|
|
214
|
+
const auth = useAuthSession();
|
|
215
|
+
|
|
216
|
+
if (auth.isAuthenticated) {
|
|
217
|
+
return <Message text={`Welcome back! Token: ${auth.accessToken}`} />;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<Message text="Please log in">
|
|
222
|
+
<InlineKeyboard>
|
|
223
|
+
<ButtonRow>
|
|
224
|
+
<Button
|
|
225
|
+
text="Login"
|
|
226
|
+
onClick={() => auth.login({ accessToken: "tok_abc", refreshToken: "ref_xyz" })}
|
|
227
|
+
/>
|
|
228
|
+
</ButtonRow>
|
|
229
|
+
</InlineKeyboard>
|
|
230
|
+
</Message>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- `auth.isAuthenticated` — `true` when a non-expired access token exists.
|
|
236
|
+
- `auth.login(tokens)` — store access/refresh tokens + optional `expiresAt`.
|
|
237
|
+
- `auth.logout()` — clear all auth data.
|
|
238
|
+
- `auth.setAccessToken(token, expiresAt?)` — update the token (e.g. after refresh).
|
|
239
|
+
|
|
240
|
+
## i18n
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { createI18n, useLocale } from "@teactjs/runtime";
|
|
244
|
+
|
|
245
|
+
const i18n = createI18n({
|
|
246
|
+
defaultLocale: "en",
|
|
247
|
+
resources: {
|
|
248
|
+
en: { greeting: "Hello {{name}}!" },
|
|
249
|
+
es: { greeting: "Hola {{name}}!" },
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
function Greeting() {
|
|
254
|
+
const { t, locale, setLocale } = useLocale();
|
|
255
|
+
return <Message text={t("greeting", { name: "World" })} />;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Payments
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
const { send, status, receipt, error } = useInvoice({
|
|
263
|
+
title: "Premium Plan",
|
|
264
|
+
description: "Monthly subscription",
|
|
265
|
+
currency: "USD",
|
|
266
|
+
prices: [{ label: "Premium", amount: 999 }],
|
|
267
|
+
providerToken: process.env.PAYMENT_TOKEN,
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Middleware
|
|
272
|
+
|
|
273
|
+
Middleware runs on every update before rendering. `next()` is called automatically if your middleware doesn't call it, so simple middleware like loggers just work:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
const logger: Middleware = async (ctx) => {
|
|
277
|
+
console.log(`Update from ${ctx.chatId}: ${ctx.text}`);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const bot = createBot({
|
|
281
|
+
middleware: [logger],
|
|
282
|
+
// ...
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
You can still call `next()` explicitly if you need to run code after the rest of the chain:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
const timer: Middleware = async (ctx, next) => {
|
|
290
|
+
const start = Date.now();
|
|
291
|
+
await next();
|
|
292
|
+
console.log(`Took ${Date.now() - start}ms`);
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Debugging
|
|
297
|
+
|
|
298
|
+
Enable verbose logging to diagnose stuck bots or network issues:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
const bot = createBot({
|
|
302
|
+
adapter: new TelegramAdapter(),
|
|
303
|
+
debug: true, // logs every update, render cycle, middleware execution
|
|
304
|
+
// ...
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Plugins
|
|
309
|
+
|
|
310
|
+
Implement the `TeactPlugin` interface:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
const myPlugin: TeactPlugin = {
|
|
314
|
+
name: "my-plugin",
|
|
315
|
+
onStart: (bot) => { /* setup */ },
|
|
316
|
+
middleware: (ctx, next) => { /* per-update */ return next(); },
|
|
317
|
+
Provider: ({ children }) => <MyContext.Provider>{children}</MyContext.Provider>,
|
|
318
|
+
onStop: () => { /* cleanup */ },
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Configuration
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { defineConfig } from "@teactjs/runtime";
|
|
326
|
+
|
|
327
|
+
export default defineConfig({
|
|
328
|
+
plugins: [storagePlugin(), authPlugin()],
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## See Also
|
|
333
|
+
|
|
334
|
+
- [`@teactjs/core`](../core) for the barrel import
|
|
335
|
+
- [`@teactjs/react`](../react) for UI components
|
|
336
|
+
- [`@teactjs/telegram`](../telegram) for the Telegram adapter
|