@xbibzlibrary/telebibz 0.4.2 → 0.4.4
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/CHANGELOG.md +22 -0
- package/README.id.md +6 -3
- package/README.md +6 -3
- package/README.zh-CN.md +6 -3
- package/dist/src/api/client.d.ts +28 -0
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +28 -0
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/transport.d.ts +14 -0
- package/dist/src/api/transport.d.ts.map +1 -1
- package/dist/src/api/transport.js +67 -2
- package/dist/src/api/transport.js.map +1 -1
- package/dist/src/api/types.d.ts +25 -0
- package/dist/src/api/types.d.ts.map +1 -1
- package/dist/src/context/context.d.ts +13 -3
- package/dist/src/context/context.d.ts.map +1 -1
- package/dist/src/context/context.js +15 -0
- package/dist/src/context/context.js.map +1 -1
- package/dist/src/core/bot.d.ts +12 -0
- package/dist/src/core/bot.d.ts.map +1 -1
- package/dist/src/core/bot.js +16 -0
- package/dist/src/core/bot.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/testing.d.ts +6 -0
- package/dist/src/testing.d.ts.map +1 -1
- package/dist/src/testing.js +6 -0
- package/dist/src/testing.js.map +1 -1
- package/dist/src/utils/files.d.ts +45 -0
- package/dist/src/utils/files.d.ts.map +1 -0
- package/dist/src/utils/files.js +53 -0
- package/dist/src/utils/files.js.map +1 -0
- package/dist-cjs/src/api/client.js +28 -0
- package/dist-cjs/src/api/transport.js +67 -2
- package/dist-cjs/src/context/context.js +15 -0
- package/dist-cjs/src/core/bot.js +16 -0
- package/dist-cjs/src/index.js +1 -0
- package/dist-cjs/src/testing.js +6 -0
- package/dist-cjs/src/utils/files.js +58 -0
- package/docs/API.id.md +70 -0
- package/docs/API.md +70 -0
- package/docs/API.zh-CN.md +70 -0
- package/docs/STORAGE.id.md +105 -0
- package/docs/STORAGE.md +105 -0
- package/docs/STORAGE.zh-CN.md +105 -0
- package/examples/files.ts +35 -0
- package/package.json +1 -1
package/docs/STORAGE.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Storage quick start (English)
|
|
2
|
+
|
|
3
|
+
telebibz ships a generic `Storage<K, V>` interface with five adapters. The core package has **zero runtime dependencies**: the Redis, SQL, and Mongo adapters accept a small driver interface you already have, so you pick the driver and version.
|
|
4
|
+
|
|
5
|
+
All adapters share one contract — `get` / `set` / `delete` / `has` / `clear` / `keys()` / `entries()` — plus **`update(key, updater, { ttlMs })`**, which serializes writes per key so concurrent updates to the same key never interleave. TTL is set per write through `{ ttlMs }`.
|
|
6
|
+
|
|
7
|
+
## MemoryStorage (default — nothing to configure)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Bot } from "@xbibzlibrary/telebibz";
|
|
11
|
+
|
|
12
|
+
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN! });
|
|
13
|
+
// bot.session is a MemoryStorage<string, S> by default.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## JsonFileStorage (single-file persistence, still zero dependencies)
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { Bot, JsonFileStorage } from "@xbibzlibrary/telebibz";
|
|
20
|
+
|
|
21
|
+
const bot = new Bot({
|
|
22
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
23
|
+
session: new JsonFileStorage("state/sessions.json"),
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## RedisStorage (bring your own client)
|
|
28
|
+
|
|
29
|
+
The adapter needs exactly the five callback-style methods every Redis client exposes — `node-redis` works as-is:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Bot, RedisStorage } from "@xbibzlibrary/telebibz";
|
|
33
|
+
import { createClient } from "redis"; // your choice of driver and version
|
|
34
|
+
|
|
35
|
+
const redis = createClient({ url: process.env.REDIS_URL });
|
|
36
|
+
await redis.connect();
|
|
37
|
+
|
|
38
|
+
const bot = new Bot({
|
|
39
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
40
|
+
session: new RedisStorage(redis, "mybot:"), // prefix for your keys
|
|
41
|
+
});
|
|
42
|
+
// Per-write TTL: await bot.session.set(key, value, { ttlMs: 24 * 60 * 60 * 1000 });
|
|
43
|
+
// (Redis PX expiry is applied automatically.)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## SqlStorage (any SQL database)
|
|
47
|
+
|
|
48
|
+
Implement the five-method driver over your SQL library; the example uses `better-sqlite3`:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { Bot, SqlStorage } from "@xbibzlibrary/telebibz";
|
|
52
|
+
import Database from "better-sqlite3";
|
|
53
|
+
|
|
54
|
+
const db = new Database("state/bot.db");
|
|
55
|
+
db.exec("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL, expires_at INTEGER)");
|
|
56
|
+
|
|
57
|
+
const storage = new SqlStorage({
|
|
58
|
+
async get(key) {
|
|
59
|
+
const row = db.prepare("SELECT value, expires_at FROM kv WHERE key = ?").get(key) as { value: string; expires_at: number | null } | undefined;
|
|
60
|
+
return row === undefined ? undefined : JSON.parse(row.value);
|
|
61
|
+
},
|
|
62
|
+
async set(key, value, expiresAt) {
|
|
63
|
+
db.prepare("INSERT INTO kv (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at")
|
|
64
|
+
.run(key, JSON.stringify(value), expiresAt ?? null);
|
|
65
|
+
},
|
|
66
|
+
async delete(key) { return db.prepare("DELETE FROM kv WHERE key = ?").run(key).changes > 0; },
|
|
67
|
+
async has(key) { return db.prepare("SELECT 1 FROM kv WHERE key = ?").get(key) !== undefined; },
|
|
68
|
+
async clear() { db.prepare("DELETE FROM kv").run(); },
|
|
69
|
+
async entries() {
|
|
70
|
+
const rows = db.prepare("SELECT key, value, expires_at FROM kv").all() as Array<{ key: string; value: string; expires_at: number | null }>;
|
|
71
|
+
return rows.map((row) => [row.key, JSON.parse(row.value), row.expiresAt ?? undefined] as [string, unknown, number | undefined]);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN!, session: storage });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## MongoStorage (bring your own collection)
|
|
79
|
+
|
|
80
|
+
The adapter talks to a standard MongoDB collection shape — pass your collection directly:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { Bot, MongoStorage } from "@xbibzlibrary/telebibz";
|
|
84
|
+
import { MongoClient } from "mongodb";
|
|
85
|
+
|
|
86
|
+
const client = new MongoClient(process.env.MONGODB_URL!);
|
|
87
|
+
await client.connect();
|
|
88
|
+
|
|
89
|
+
const bot = new Bot({
|
|
90
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
91
|
+
session: new MongoStorage(client.db("mybot").collection("sessions")),
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Choosing
|
|
96
|
+
|
|
97
|
+
| Adapter | Use when | Persistence | Extra dependency |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `MemoryStorage` | single-process bots, tests | process lifetime | none |
|
|
100
|
+
| `JsonFileStorage` | small bots, simple deploys | file on disk | none |
|
|
101
|
+
| `RedisStorage` | multi-instance, shared state | Redis | your Redis client |
|
|
102
|
+
| `SqlStorage` | SQL-backed apps | any SQL database | your SQL driver |
|
|
103
|
+
| `MongoStorage` | existing Mongo stack | MongoDB | your Mongo driver |
|
|
104
|
+
|
|
105
|
+
Full API signatures: [API.md](API.md). Bahasa Indonesia: [STORAGE.id.md](STORAGE.id.md) · 简体中文: [STORAGE.zh-CN.md](STORAGE.zh-CN.md).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# 存储快速上手(简体中文)
|
|
2
|
+
|
|
3
|
+
telebibz 提供一个通用的 `Storage<K, V>` 接口和五个适配器。核心包**零运行时依赖**:Redis、SQL 和 Mongo 适配器只要求一个你已经拥有的小型 driver interface,由你自己选择驱动和版本。
|
|
4
|
+
|
|
5
|
+
所有适配器共享同一契约 —— `get` / `set` / `delete` / `has` / `clear` / `keys()` / `entries()` —— 以及 **`update(key, updater, { ttlMs })`**,它按 key 串行化写入,因此对同一 key 的并发更新永远不会交错。TTL 通过 `{ ttlMs }` 按每次写入设置。
|
|
6
|
+
|
|
7
|
+
## MemoryStorage(默认 —— 无需配置)
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Bot } from "@xbibzlibrary/telebibz";
|
|
11
|
+
|
|
12
|
+
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN! });
|
|
13
|
+
// bot.session 默认就是 MemoryStorage<string, S>。
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## JsonFileStorage(单文件持久化,依然零依赖)
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { Bot, JsonFileStorage } from "@xbibzlibrary/telebibz";
|
|
20
|
+
|
|
21
|
+
const bot = new Bot({
|
|
22
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
23
|
+
session: new JsonFileStorage("state/sessions.json"),
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## RedisStorage(自带客户端)
|
|
28
|
+
|
|
29
|
+
适配器只需要每个 Redis 客户端都有的五个回调式方法 —— `node-redis` 可以直接使用:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Bot, RedisStorage } from "@xbibzlibrary/telebibz";
|
|
33
|
+
import { createClient } from "redis"; // 由你选择驱动和版本
|
|
34
|
+
|
|
35
|
+
const redis = createClient({ url: process.env.REDIS_URL });
|
|
36
|
+
await redis.connect();
|
|
37
|
+
|
|
38
|
+
const bot = new Bot({
|
|
39
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
40
|
+
session: new RedisStorage(redis, "mybot:"), // 你的 key 前缀
|
|
41
|
+
});
|
|
42
|
+
// 按次写入的 TTL:await bot.session.set(key, value, { ttlMs: 24 * 60 * 60 * 1000 });
|
|
43
|
+
// (Redis PX 过期会自动应用。)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## SqlStorage(任意 SQL 数据库)
|
|
47
|
+
|
|
48
|
+
在你的 SQL 库之上实现这五个方法的 driver;示例使用 `better-sqlite3`:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { Bot, SqlStorage } from "@xbibzlibrary/telebibz";
|
|
52
|
+
import Database from "better-sqlite3";
|
|
53
|
+
|
|
54
|
+
const db = new Database("state/bot.db");
|
|
55
|
+
db.exec("CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL, expires_at INTEGER)");
|
|
56
|
+
|
|
57
|
+
const storage = new SqlStorage({
|
|
58
|
+
async get(key) {
|
|
59
|
+
const row = db.prepare("SELECT value, expires_at FROM kv WHERE key = ?").get(key) as { value: string; expires_at: number | null } | undefined;
|
|
60
|
+
return row === undefined ? undefined : JSON.parse(row.value);
|
|
61
|
+
},
|
|
62
|
+
async set(key, value, expiresAt) {
|
|
63
|
+
db.prepare("INSERT INTO kv (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at")
|
|
64
|
+
.run(key, JSON.stringify(value), expiresAt ?? null);
|
|
65
|
+
},
|
|
66
|
+
async delete(key) { return db.prepare("DELETE FROM kv WHERE key = ?").run(key).changes > 0; },
|
|
67
|
+
async has(key) { return db.prepare("SELECT 1 FROM kv WHERE key = ?").get(key) !== undefined; },
|
|
68
|
+
async clear() { db.prepare("DELETE FROM kv").run(); },
|
|
69
|
+
async entries() {
|
|
70
|
+
const rows = db.prepare("SELECT key, value, expires_at FROM kv").all() as Array<{ key: string; value: string; expires_at: number | null }>;
|
|
71
|
+
return rows.map((row) => [row.key, JSON.parse(row.value), row.expiresAt ?? undefined] as [string, unknown, number | undefined]);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN!, session: storage });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## MongoStorage(自带 collection)
|
|
79
|
+
|
|
80
|
+
适配器直接对接标准 MongoDB collection 形状 —— 直接传入你的 collection:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { Bot, MongoStorage } from "@xbibzlibrary/telebibz";
|
|
84
|
+
import { MongoClient } from "mongodb";
|
|
85
|
+
|
|
86
|
+
const client = new MongoClient(process.env.MONGODB_URL!);
|
|
87
|
+
await client.connect();
|
|
88
|
+
|
|
89
|
+
const bot = new Bot({
|
|
90
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
91
|
+
session: new MongoStorage(client.db("mybot").collection("sessions")),
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 如何选择
|
|
96
|
+
|
|
97
|
+
| 适配器 | 适用场景 | 持久化 | 额外依赖 |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `MemoryStorage` | 单进程 bot、测试 | 进程生命周期 | 无 |
|
|
100
|
+
| `JsonFileStorage` | 小型 bot、简单部署 | 磁盘文件 | 无 |
|
|
101
|
+
| `RedisStorage` | 多实例、共享状态 | Redis | 你的 Redis 客户端 |
|
|
102
|
+
| `SqlStorage` | 基于 SQL 的应用 | 任意 SQL 数据库 | 你的 SQL 驱动 |
|
|
103
|
+
| `MongoStorage` | 已有 Mongo 技术栈 | MongoDB | 你的 Mongo 驱动 |
|
|
104
|
+
|
|
105
|
+
完整 API 签名:[API.zh-CN.md](API.zh-CN.md)。English: [STORAGE.md](STORAGE.md) · Bahasa Indonesia: [STORAGE.id.md](STORAGE.id.md)。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File upload and download in one bot:
|
|
3
|
+
* - /doc <path> validates the file, then uploads it as multipart
|
|
4
|
+
* - /photo downloads the largest photo of the message back to disk
|
|
5
|
+
*
|
|
6
|
+
* Run: TELEGRAM_BOT_TOKEN=<token> npx tsx examples/files.ts
|
|
7
|
+
*/
|
|
8
|
+
import { stat } from "node:fs/promises";
|
|
9
|
+
import { Bot, assertValidUpload } from "@xbibzlibrary/telebibz";
|
|
10
|
+
|
|
11
|
+
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
12
|
+
|
|
13
|
+
bot.command("doc", async (ctx) => {
|
|
14
|
+
const filePath = ctx.message?.text?.split(/\s+/)[1];
|
|
15
|
+
if (!filePath) {
|
|
16
|
+
await ctx.reply("Usage: /doc <path-to-file>");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const info = await stat(filePath);
|
|
20
|
+
assertValidUpload(
|
|
21
|
+
{ sizeBytes: info.size, fileName: filePath },
|
|
22
|
+
{ maxBytes: 50 * 1024 * 1024 },
|
|
23
|
+
);
|
|
24
|
+
const message = await ctx.replyWithDocument({ source: filePath });
|
|
25
|
+
await ctx.reply(`Sent as document (message_id ${message.message_id}).`);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
bot.on("message:photo", async (ctx) => {
|
|
29
|
+
const photo = ctx.message?.photo?.at(-1);
|
|
30
|
+
if (!photo) return;
|
|
31
|
+
const downloaded = await ctx.downloadFile(photo.file_id, { destination: `downloads/${photo.file_unique_id}.jpg` });
|
|
32
|
+
await ctx.reply(`Downloaded ${downloaded.fileName} (${downloaded.sizeBytes} bytes) to ${downloaded.savedTo}.`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await bot.start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbibzlibrary/telebibz",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Telegram Bot framework for Node.js and TypeScript with a typed API client, routing, middleware, webhooks, keyboards, conversations, plugins, queues, and a polished terminal experience.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telegram",
|