@xbibzlibrary/telebibz 3.0.1 โ 3.1.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/CHANGELOG.md +18 -0
- package/README.id.md +642 -0
- package/README.md +556 -147
- package/examples/03-wizard.js +37 -8
- package/index.d.ts +28 -3
- package/index.js +2 -0
- package/lib/telebibz.js +3 -0
- package/lib/wizard.js +184 -18
- package/package.json +2 -2
- package/test/all.test.js +106 -1
package/README.md
CHANGED
|
@@ -1,232 +1,641 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[๐ฌ๐ง **English**](README.md) ยท [๐ฎ **Indonesia**](README.id.md)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
<br>
|
|
6
|
+
|
|
7
|
+
<a href="https://www.npmjs.com/package/@xbibzlibrary/telebibz" title="Open telebibz on npm">
|
|
8
|
+
<img src="https://imgbs.com/uploads/telebibz-d7b30671.png" alt="telebibz โ Telegram Bot Library" width="560">
|
|
9
|
+
</a>
|
|
10
|
+
|
|
11
|
+
<br><br>
|
|
12
|
+
|
|
13
|
+
**The easiest Telegram bot library for Node.js โ full feature set on par with grammY.**<br>
|
|
14
|
+
A standalone recode of [grammY](https://grammy.dev)'s elegant architecture, with production
|
|
15
|
+
dependencies that are *actually used*, and an Indonesia-first community.
|
|
16
|
+
|
|
17
|
+
<br>
|
|
18
|
+
|
|
19
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
20
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
21
|
+
[](https://nodejs.org)
|
|
22
|
+
[](#-testing--live-proof)
|
|
23
|
+
[](#-analytics--statistics)
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
[](https://github.com/XbibzOfficial777/telebibz)
|
|
8
26
|
|
|
9
|
-
|
|
27
|
+
<br>
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
`//โXbibz Officialโ//`
|
|
30
|
+
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## ๐ Table of Contents
|
|
36
|
+
|
|
37
|
+
| | | |
|
|
12
38
|
|---|---|---|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
39
|
+
| โก [Why telebibz?](#why) | ๐ [Feature matrix vs grammY](#matrix) | ๐ฅ [Installation & requirements](#install) |
|
|
40
|
+
| ๐ [Quick start](#quickstart) | ๐ง [How it works (architecture)](#architecture) | ๐ [Full documentation](#docs) |
|
|
41
|
+
| ๐๏ธ [Handlers & filters](#handlers) | ๐ฌ [Context shortcuts](#context) | ๐ [Keyboards & buttons](#keyboards) |
|
|
42
|
+
| ๏ธ [Interactive menus](#menus) | ๐ง [Wizard (forms + buttons + edit/delete)](#wizard) | โ [Inline mode](#inline) |
|
|
43
|
+
| ๐ฃ [Broadcast](#broadcast) | ๐ [Files & media](#files) | ๐ก๏ธ [Reliability & rate limiting](#ratelimit) |
|
|
44
|
+
| ๐๏ธ [Sessions](#sessions) | ๐ฎ๐ฉ [Human-readable errors](#errors) | ๐ธ๏ธ [Webhooks & serverless](#webhook) |
|
|
45
|
+
| ๐ [Proxy transport](#proxy) | ๐งช [Transformers](#transformers) | ๐ [Analytics & statistics](#analytics) |
|
|
46
|
+
| ๐งฉ [Examples](#examples) | ๐ฌ [Testing](#testing) | ๐ [Repo structure](#structure) |
|
|
47
|
+
| ๐ [Changelog](#changelog) | ๐ [License](#license) | |
|
|
48
|
+
|
|
49
|
+
<a id="why"></a>
|
|
50
|
+
## โก Why telebibz?
|
|
51
|
+
|
|
52
|
+
> [!TIP]
|
|
53
|
+
> **One principle:** every feature that needs a plugin in grammY is **built in** here โ
|
|
54
|
+
> wizard, menus, rate limiting, broadcast, file download โ and polling that *never dies* on 409.
|
|
55
|
+
|
|
56
|
+
- ๐ง **The grammY API you already know** โ `bot.cmd()`, `bot.hears()`, `ctx.reply()`, middleware, transformers
|
|
57
|
+
- ๐ง **Built-in Wizard v3.1** โ question-and-answer forms with **choice buttons** (reply/inline) and **edit/delete** message modes
|
|
58
|
+
- ๐ก๏ธ **Bulletproof** โ 429 auto-retry, throttler, per-user limiter, polling auto-retry on 409 conflict
|
|
59
|
+
- ๐ฎ๐ฉ **Human-readable errors** โ every Telegram error is translated into a plain-language suggestion
|
|
60
|
+
- ๐ **Proxy API for any method** โ `api.anyMethod({...})` works automatically, even for methods not released yet
|
|
61
|
+
- ๐ชถ **Light & honest** โ 4 dependencies, all genuinely used and tested
|
|
62
|
+
|
|
63
|
+
<a id="matrix"></a>
|
|
64
|
+
## ๐ Feature Matrix โ grammY parity
|
|
65
|
+
|
|
66
|
+
| Feature | grammY | telebibz |
|
|
67
|
+
|---|:---:|:---:|
|
|
68
|
+
| Proxy API for **any method** (auto-generated) | โ
| โ
|
|
|
69
|
+
| ~60 typed shortcuts (sendMessage, banChatMemberโฆ) | โ
| โ
|
|
|
70
|
+
| Full Context (~70 shortcuts reply/edit/admin/react) | โ
| โ
|
|
|
71
|
+
| Business flavor (`business_connection_id` automatic) | plugin | โ
built-in |
|
|
72
|
+
| Filters `on('message:photo' / ':text' / 'chat_type:private' โฆ)` | โ
| โ
|
|
|
18
73
|
| `cmd / hears / action / inlineQuery` | โ
| โ
(+ inlineQuery matcher) |
|
|
19
74
|
| `branch / filter / drop / route / lazy / fork` | โ
| โ
|
|
|
20
|
-
| Error boundary + catch | โ
| โ
(
|
|
21
|
-
| Session + storage
|
|
75
|
+
| Error boundary + catch | โ
| โ
(all shortcuts auto-protected) |
|
|
76
|
+
| Session + swappable storage | โ
| โ
|
|
|
22
77
|
| Transformer API (`api.config.use`) | โ
| โ
|
|
|
23
|
-
| `auto-retry` 429
|
|
24
|
-
| Throttler
|
|
25
|
-
|
|
|
26
|
-
| `InputFile` Buffer/path/stream + multipart attach
|
|
27
|
-
| `InputMedia` builder + media
|
|
28
|
-
|
|
|
29
|
-
| Keyboard & InlineKeyboard fluent
|
|
30
|
-
|
|
|
31
|
-
| Wizard/
|
|
32
|
-
|
|
|
33
|
-
| Broadcast
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
| TypeScript | โ
full | d.ts
|
|
38
|
-
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
78
|
+
| `auto-retry` on 429 honoring `retry_after` | plugin | โ
built-in `autoRetry()` |
|
|
79
|
+
| Throttler queue | plugin | โ
built-in `throttler()` |
|
|
80
|
+
| Per-user rate limit | plugin | โ
built-in `limiter()` |
|
|
81
|
+
| `InputFile` Buffer/path/stream + multipart `attach://` | โ
| โ
|
|
|
82
|
+
| `InputMedia` builder + media groups | โ
| โ
`InputMediaBuilder` |
|
|
83
|
+
| File download (`getFile`/`downloadFile`) | plugin | โ
built-in |
|
|
84
|
+
| Keyboard & InlineKeyboard fluent classes | โ
| โ
|
|
|
85
|
+
| Interactive menus | plugin | โ
built-in `Menu/MenuContainer` |
|
|
86
|
+
| Wizard/conversations | plugin | โ
built-in โ **+ choice buttons & `edit`/`delete` modes** |
|
|
87
|
+
| 409-resilient long polling | โ (fatal crash) | โ
built-in (auto-retry 5 s) |
|
|
88
|
+
| Broadcast ready to use | โ | โ
`bot.broadcast()` |
|
|
89
|
+
| Humanized errors + suggestions | โ | โ
`humanize()` |
|
|
90
|
+
| Boot banner + debug logging | โ | โ
(`DEBUG=telebibz*`) |
|
|
91
|
+
| HTTP(S) proxy for VPS | โ ๏ธ manual | โ
`proxy` transport option |
|
|
92
|
+
| TypeScript | โ
full | loose d.ts (JS-first) |
|
|
93
|
+
| Documentation language | en | **๐ฌ๐ง + ๐ฎ๐ฉ** |
|
|
94
|
+
|
|
95
|
+
<a id="install"></a>
|
|
96
|
+
## ๐ฅ Installation & Requirements
|
|
97
|
+
|
|
98
|
+
**Requirements:** Node.js โฅ 18 (uses global `FormData`/`Blob` for uploads).
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm install @xbibzlibrary/telebibz
|
|
102
|
+
# or
|
|
103
|
+
yarn add @xbibzlibrary/telebibz
|
|
104
|
+
# or
|
|
105
|
+
pnpm add @xbibzlibrary/telebibz
|
|
106
|
+
```
|
|
47
107
|
|
|
48
|
-
|
|
108
|
+
**Runtime dependencies (all used, all tested):**
|
|
49
109
|
|
|
50
|
-
|
|
110
|
+
| Package | Purpose |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `axios ^1.20` | keep-alive transport + streaming `downloadFile` |
|
|
113
|
+
| `mime-types ^3.0` | content-type detection for uploads |
|
|
114
|
+
| `https-proxy-agent ^9.1` | VPS proxy transport option |
|
|
115
|
+
| `debug ^4.4` | logging via `DEBUG=telebibz:net,telebibz:ratelimit` |
|
|
51
116
|
|
|
52
|
-
|
|
117
|
+
Get your bot token from **@BotFather** โ `/newbot`. The constructor validates the token
|
|
118
|
+
format (`123456:ABCโฆ`) and throws a helpful error if it's wrong.
|
|
119
|
+
|
|
120
|
+
<a id="quickstart"></a>
|
|
121
|
+
## ๐ Quick Start
|
|
53
122
|
|
|
54
123
|
```js
|
|
55
124
|
const { TeleBibz } = require('@xbibzlibrary/telebibz');
|
|
56
125
|
|
|
57
|
-
const bot = new TeleBibz('
|
|
126
|
+
const bot = new TeleBibz('TOKEN_FROM_BOTFATHER');
|
|
58
127
|
|
|
59
|
-
bot.cmd('start', (ctx) => ctx.reply('
|
|
60
|
-
bot.hears(/
|
|
128
|
+
bot.cmd('start', (ctx) => ctx.reply('Hello!'));
|
|
129
|
+
bot.hears(/hello|hi/i, (ctx) => ctx.reply('hello there ๐'));
|
|
130
|
+
bot.hears('ping', (ctx) => ctx.reply('pong ๐'));
|
|
61
131
|
|
|
62
132
|
bot.launch();
|
|
63
133
|
```
|
|
64
134
|
|
|
65
|
-
```
|
|
135
|
+
```bash
|
|
66
136
|
BOT_TOKEN=123:abc node index.js
|
|
67
137
|
```
|
|
68
138
|
|
|
69
|
-
Output terminal:
|
|
70
139
|
```
|
|
71
|
-
|
|
72
|
-
โ ๐ค TeleBibz ON
|
|
73
|
-
โ bot : @
|
|
74
|
-
โ
|
|
75
|
-
โ
|
|
76
|
-
|
|
77
|
-
|
|
140
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
141
|
+
โ ๐ค TeleBibz ON โ
|
|
142
|
+
โ bot : @yourbot (id 123456) โ
|
|
143
|
+
โ mode : long-polling โ
|
|
144
|
+
โ library : telebibz 3.1.0 โ
|
|
145
|
+
โ brand : //โXbibz Officialโ// โ
|
|
146
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
147
|
+
โ waiting for updatesโฆ (Ctrl+C to stop)
|
|
78
148
|
```
|
|
79
149
|
|
|
80
|
-
>
|
|
81
|
-
>
|
|
82
|
-
>
|
|
150
|
+
> [!NOTE]
|
|
151
|
+
> **On a VPS:** if another bot instance is still polling (409 Conflict โ e.g. double
|
|
152
|
+
> deploy or a hosting restart), telebibz **auto-retries every 5 seconds without
|
|
153
|
+
> crashing** and boots the moment the lane is free. No PM2 babysitting needed.
|
|
83
154
|
|
|
84
|
-
|
|
155
|
+
<a id="architecture"></a>
|
|
156
|
+
## ๐ง How It Works (Architecture)
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
160
|
+
Telegram Bot API โโโโโบโ long polling (lib/runner.js) โ
|
|
161
|
+
โ or webhook / handleUpdate (lib/telebibz.js)โ
|
|
162
|
+
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
163
|
+
โ raw update (JSON)
|
|
164
|
+
โผ
|
|
165
|
+
Context(update, api, me) lib/context.js
|
|
166
|
+
โ
|
|
167
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
168
|
+
โ Middleware tree `_root` (fixed order): โ
|
|
169
|
+
โ 1. session() lib/session.js โ
|
|
170
|
+
โ 2. wizard.middleware() lib/wizard.js โ
|
|
171
|
+
โ 3. errorBoundary(reporter) โ
|
|
172
|
+
โ โโโบ your handlers: use/cmd/hears/action/on/ โ
|
|
173
|
+
โ inlineQuery/Menu/wizard lib/composer.js โ
|
|
174
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
175
|
+
โ ctx.reply / ctx.api.*
|
|
176
|
+
โผ
|
|
177
|
+
ApiBase + Proxy + transformer pipeline lib/api.js
|
|
178
|
+
(api.config.use โ autoRetry / throttler)
|
|
179
|
+
โ
|
|
180
|
+
โผ
|
|
181
|
+
axios transport: JSON or multipart attach:// lib/net.js
|
|
182
|
+
โ
|
|
183
|
+
โผ
|
|
184
|
+
https://api.telegram.org
|
|
185
|
+
```
|
|
85
186
|
|
|
86
|
-
|
|
187
|
+
**Lifecycle of one update** (e.g. `/start`): `getUpdates` โ `pollLoop` โ
|
|
188
|
+
`handleUpdate` โ new `Context` โ session loads โ wizard middleware (no active
|
|
189
|
+
wizard โ pass) โ error boundary โ your matched handler runs โ `ctx.reply()` โ
|
|
190
|
+
transformer pipeline โ axios โ Telegram. Any thrown error becomes a `BotError`
|
|
191
|
+
and flows to `opts.onError` or the humanized reporter.
|
|
87
192
|
|
|
88
|
-
|
|
193
|
+
<a id="docs"></a>
|
|
194
|
+
## ๐ Full Documentation
|
|
195
|
+
|
|
196
|
+
### ๐๏ธ Constructor & options
|
|
89
197
|
|
|
90
198
|
```js
|
|
91
|
-
bot
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
199
|
+
const bot = new TeleBibz('TOKEN', {
|
|
200
|
+
allowedUpdates: [...], // limit update types (default: common + Business types)
|
|
201
|
+
onError: (err, ctx) {}, // custom error handler (default: humanized reporter)
|
|
202
|
+
silent: false, // hide boot banner
|
|
203
|
+
dropPending: false, // discard old updates on start
|
|
204
|
+
session: { ... }, // { initial, getKey, storage } โ see Sessions
|
|
205
|
+
transport: fn, // inject custom transport (testing / proxy)
|
|
206
|
+
});
|
|
97
207
|
```
|
|
98
208
|
|
|
99
|
-
|
|
209
|
+
<a id="handlers"></a>
|
|
210
|
+
### ๐๏ธ Handlers & filters
|
|
100
211
|
|
|
101
212
|
```js
|
|
102
|
-
|
|
213
|
+
bot.cmd('ping', (ctx) => ctx.reply('pong')); // /ping
|
|
214
|
+
bot.cmd(['a', 'b'], handler); // /a OR /b
|
|
215
|
+
bot.start('Welcome!'); // shortcut: registers /start
|
|
216
|
+
bot.hears('daftar', handler); // exact text "daftar" (case-insensitive)
|
|
217
|
+
bot.hears(/kitt?y/i, handler); // any regex
|
|
218
|
+
bot.on('message:photo', handler); // grammY-style filters
|
|
219
|
+
bot.on([':text', 'chat_type:private'], handler);
|
|
220
|
+
bot.action('menu:premium', handler); // callback_query data (string or RegExp)
|
|
221
|
+
bot.inlineQuery(/kucing/i, handler); // inline mode (regex / string / '*')
|
|
222
|
+
bot.use(middleware); // manual middleware
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Composer combinators (grammY semantics):
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
bot.branch(pred, ifTrue, ifFalse); // pick a subtree by predicate
|
|
229
|
+
bot.filter(pred, ...mw); // run only when pred(ctx) is true
|
|
230
|
+
bot.drop(pred, ...mw); // skip when pred(ctx) is true
|
|
231
|
+
bot.route('chat.type', { private: mwA, group: mwB }); // map ctxโhandler
|
|
232
|
+
bot.lazy((ctx) => ctx.from.is_bot ? botMw : userMw); // build mw per update
|
|
233
|
+
bot.fork(slowMw); // run in background, doesn't block next()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Filters supported by `on()`: update fields (`message`, `edited_message`,
|
|
237
|
+
`callback_query`, `inline_query`, `my_chat_member`, โฆ), message props
|
|
238
|
+
(`message:photo`, `message:text`, `:caption`, `:document`, `:sticker`, `:media`, โฆ),
|
|
239
|
+
chat types (`chat_type:private/group/supergroup/channel`), plus
|
|
240
|
+
`callback_query:data`, payments, reactions, join requests.
|
|
241
|
+
|
|
242
|
+
Command handlers receive arguments in `ctx.match`:
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
bot.cmd('echo', (ctx) => ctx.reply(`args: ${ctx.match}`)); // /echo hello โ "hello"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
<a id="context"></a>
|
|
249
|
+
### ๐ฌ Context shortcuts
|
|
250
|
+
|
|
251
|
+
`ctx` wraps every update kind (message, edited, channel, business, callback,
|
|
252
|
+
inlineโฆ) with unified accessors: `chat`, `from`, `chatId`, `msgId`, `msg`,
|
|
253
|
+
`senderChat`, `inlineMessageId`, `businessConnectionId`.
|
|
254
|
+
|
|
255
|
+
| Category | Shortcuts |
|
|
256
|
+
|---|---|
|
|
257
|
+
| **Reply** | `reply`, `replyWithHTML`, `replyWithMarkdown`, `replyWithPhoto/Video/Audio/Document/Animation/Voice/VideoNote/Sticker/MediaGroup/Location/Venue/Contact/Poll/Dice/Invoice/ChatAction` |
|
|
258
|
+
| **Edit & delete** | `editMessageText/Caption/Media/ReplyMarkup`, `deleteMessage`, `deleteMessages` โ callback-aware & inline-message aware |
|
|
259
|
+
| **React** | `react('๐')` |
|
|
260
|
+
| **Forward/copy** | `forwardMessage(to)`, `copyMessage(to)` (defaults to current msg) |
|
|
261
|
+
| **Callbacks & inline** | `answerCallbackQuery` (string or object), `answerInlineQuery` |
|
|
262
|
+
| **Admin** | `banChatMember`, `restrictChatMember`, `promoteChatMember`, `banAuthor`, `restrictAuthor`, `getChat*`, `getAuthor`, `leaveChat`, `setChatTitle/Description`, `pin/unpinChatMessage` |
|
|
263
|
+
| **Files** | `getFile()` (smart: largest photo), `downloadFile(dest)` |
|
|
264
|
+
|
|
265
|
+
Business accounts: replies inside a business context automatically carry
|
|
266
|
+
`business_connection_id`.
|
|
267
|
+
|
|
268
|
+
<a id="keyboards"></a>
|
|
269
|
+
### ๐ Keyboards & buttons
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
const { btn, url, webApp, copy, kb, InlineKeyboard, Keyboard } = require('@xbibzlibrary/telebibz');
|
|
103
273
|
|
|
104
274
|
bot.cmd('menu', (ctx) =>
|
|
105
|
-
ctx.reply('
|
|
106
|
-
[btn('๐ Premium', 'prem', 'primary'),
|
|
107
|
-
btn('โ
|
|
108
|
-
[url('๐ Web', 'https://
|
|
109
|
-
[btn('โ
|
|
275
|
+
ctx.reply('Pick:', kb([
|
|
276
|
+
[btn('๐ Premium', 'prem', 'primary'), // blue/purple
|
|
277
|
+
btn('โ
Register', 'reg', 'success')], // green
|
|
278
|
+
[url('๐ Web', 'https://yoursite.com')],
|
|
279
|
+
[btn('โ Close', 'close', 'danger', '5408846744727334338')], // red + ANIMATED ICON
|
|
110
280
|
])));
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
- ๐จ `style` colors (`primary`/`success`/`danger`) need Telegram apps from Feb 2026+ โ older apps render plain buttons, never an error.
|
|
284
|
+
- โจ `icon_custom_emoji_id` needs a Premium bot owner or a Fragment username.
|
|
285
|
+
- Helpers: `copy(text, value)` (copy-to-clipboard), `webApp(text, link)`, `kb.confirm(yesData, noData)`, `kb.markup(rows)`.
|
|
286
|
+
- Fluent classes: `new InlineKeyboard().text(...).url(...).row().text(...).build()` and `new Keyboard().text(...).requestContact(...).resized().build()` (real reply keyboards).
|
|
111
287
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
288
|
+
<a id="menus"></a>
|
|
289
|
+
### ๐ฝ๏ธ Interactive menus
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
const { Menu, MenuContainer } = require('@xbibzlibrary/telebibz');
|
|
293
|
+
|
|
294
|
+
const mc = new MenuContainer();
|
|
295
|
+
const main = mc.create('main'), more = mc.create('more');
|
|
296
|
+
|
|
297
|
+
main.text('๐ Toggle', async (ctx) => ctx.answerCallbackQuery('toggled!'))
|
|
298
|
+
.row()
|
|
299
|
+
.url('๐ Web', 'https://x.com')
|
|
300
|
+
.submenu('More โถ', 'more');
|
|
301
|
+
more.back('โ๏ธ Back', 'main');
|
|
302
|
+
|
|
303
|
+
bot.use(mc); // button handlers registered automatically
|
|
304
|
+
bot.cmd('cfg', (ctx) => ctx.reply('Menu:', { reply_markup: main.render(ctx) }));
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Submenus swap the keyboard in place via `editMessageReplyMarkup`; stale button
|
|
308
|
+
presses are answered with a friendly alert instead of crashing.
|
|
309
|
+
|
|
310
|
+
<a id="wizard"></a>
|
|
311
|
+
### ๐ง Wizard โ conversation forms, zero boilerplate
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
bot.wizard('register', {
|
|
315
|
+
steps: [
|
|
316
|
+
{ key: 'name', ask: 'What is your name?' },
|
|
317
|
+
{ key: 'age', ask: 'Age?', parse: Number,
|
|
318
|
+
validate: (n) => (n > 0 && n < 120 ? null : 'Numbers only, please:') },
|
|
319
|
+
],
|
|
320
|
+
done: async (ans, ctx) => ctx.reply(`Done ${ans.name} (${ans.age})!`),
|
|
115
321
|
});
|
|
322
|
+
// user runs /register โ the bot asks until finished.
|
|
323
|
+
// typing "cancel" / "batal" stops any time. Session is active automatically.
|
|
116
324
|
```
|
|
117
325
|
|
|
118
|
-
|
|
119
|
-
|
|
326
|
+
`bot.wizard(id, def, bindCommand = true)` also binds `/id` as the trigger; use
|
|
327
|
+
`bot.wizardStart(ctx, id)` from any handler (button, menu, โฆ).
|
|
120
328
|
|
|
121
|
-
|
|
329
|
+
#### ๐ v3.1 โ choice buttons + `edit`/`delete` modes
|
|
122
330
|
|
|
123
331
|
```js
|
|
124
|
-
bot.wizard('
|
|
332
|
+
bot.wizard('survey', {
|
|
333
|
+
mode: 'edit', // 'send' (default) | 'edit' | 'delete'
|
|
125
334
|
steps: [
|
|
126
|
-
|
|
127
|
-
{ key: '
|
|
128
|
-
|
|
335
|
+
// reply keyboard โ user taps, no typing
|
|
336
|
+
{ key: 'gender', ask: 'Gender?', buttons: ['๐จ Male', '๐ฉ Female'], onlyButtons: true },
|
|
337
|
+
|
|
338
|
+
// inline keyboard (callback) โ value may differ from label
|
|
339
|
+
{ key: 'island', ask: 'Which island?', inline: true, onlyButtons: true,
|
|
340
|
+
buttons: [[{ text: '๐ Java', value: 'java' }, { text: '๐ด Sumatra', value: 'sumatra' }]] },
|
|
341
|
+
|
|
342
|
+
// free typing with validation (mode can be overridden per step)
|
|
343
|
+
{ key: 'age', ask: 'Age?', parse: Number, mode: 'send',
|
|
344
|
+
validate: (n) => (n > 0 && n < 120 ? null : 'Numbers only:') },
|
|
129
345
|
],
|
|
130
|
-
done: async (ans, ctx) => ctx.reply(`
|
|
346
|
+
done: async (ans, ctx) => ctx.reply(`Saved: ${JSON.stringify(ans)}`),
|
|
131
347
|
});
|
|
132
|
-
// user tinggal /daftar โ bot bertanya-bertanya sampai selesai.
|
|
133
|
-
// ketik "batal" kapan pun untuk berhenti. Sesi otomatis aktif, tak perlu setup.
|
|
134
348
|
```
|
|
135
349
|
|
|
136
|
-
|
|
350
|
+
| Option | Level | Purpose |
|
|
351
|
+
|---|:---:|---|
|
|
352
|
+
| `buttons` | step | `['A','B']`, `[{text,value}]`, or explicit rows `[['A'],['B','C']]` |
|
|
353
|
+
| `inline` | step | `true` โ callback buttons (click = value, no typing) |
|
|
354
|
+
| `onlyButtons` | step | `true`/string โ reject free typing, must pick a button |
|
|
355
|
+
| `mode` | def/step | `'send'` new message ยท `'edit'` one message edited in place ยท `'delete'` old question deleted first |
|
|
356
|
+
| `cleanup` | def | delete the last question when done (default on with mode `'delete'`) |
|
|
357
|
+
| `removeKeyboard` | def | dismiss the reply keyboard when done (default `true` if one was shown) |
|
|
358
|
+
|
|
359
|
+
Programmatic helpers: `bot.wizardCancel(ctx)`, `bot.wizardEdit(ctx, text)`,
|
|
360
|
+
`bot.wizardDelete(ctx)`, plus module `wizard` (`cancel/editAsk/deleteAsk`).
|
|
361
|
+
Stale buttons (clicked after the wizard advanced/ended) get a safe alert โ
|
|
362
|
+
the bot never crashes. `parse`/`validate` also apply to button values.
|
|
363
|
+
|
|
364
|
+
<a id="inline"></a>
|
|
365
|
+
### โ Inline mode
|
|
366
|
+
|
|
367
|
+
```js
|
|
368
|
+
const { iq } = require('@xbibzlibrary/telebibz');
|
|
369
|
+
|
|
370
|
+
bot.inlineQuery(/cat/i, async (ctx) => {
|
|
371
|
+
await ctx.answerInlineQuery([
|
|
372
|
+
iq.article('1', 'A cat fact', { message_text: 'meong!' }),
|
|
373
|
+
iq.photo('2', 'https://x/1.jpg'),
|
|
374
|
+
], { cache_time: 0 });
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Builders: `iq.article/photo/gif/video/audio/location/sticker`.
|
|
379
|
+
|
|
380
|
+
<a id="broadcast"></a>
|
|
381
|
+
### ๐ฃ Broadcast (rate-limit safe)
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
const result = await bot.broadcast([111, 222, 333], 'Announcement!', { delay: 35 });
|
|
385
|
+
// โ { terkirim: 3, gagal: 0, errors: [] } (blocked users are listed in errors)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`pesan` may be a string, a `sendMessage` payload object, or a function
|
|
389
|
+
`(chatId) => payload` for per-recipient personalization. Default pacing is
|
|
390
|
+
35 ms (โ28 msg/s, safely under Telegram limits).
|
|
391
|
+
|
|
392
|
+
<a id="files"></a>
|
|
393
|
+
### ๐ Files & media
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
const { InputFile, InputMediaBuilder } = require('@xbibzlibrary/telebibz');
|
|
397
|
+
|
|
398
|
+
bot.cmd('foto', (ctx) => ctx.replyWithPhoto(new InputFile(buffer, 'x.jpg')));
|
|
399
|
+
bot.cmd('dok', (ctx) => ctx.replyWithDocument(new InputFile('/path/file.pdf')));
|
|
400
|
+
bot.cmd('album', (ctx) => ctx.replyWithMediaGroup([
|
|
401
|
+
InputMediaBuilder.photo('https://a/1.jpg'),
|
|
402
|
+
InputMediaBuilder.photo('https://a/2.jpg', { caption: 'two' }),
|
|
403
|
+
]));
|
|
404
|
+
bot.on('message:photo', async (ctx) => {
|
|
405
|
+
const f = await ctx.getFile(); // largest photo size, automatic
|
|
406
|
+
await ctx.downloadFile('./foto.jpg'); // streams to disk
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
`InputFile` accepts Buffer / Uint8Array / file path / fs stream / async
|
|
411
|
+
iterable; uploads are sent as `multipart` with `attach://` anywhere in the
|
|
412
|
+
payload (media groups, thumbnails, โฆ). `api.downloadFile(file_id, dest)` works
|
|
413
|
+
standalone too.
|
|
414
|
+
|
|
415
|
+
<a id="ratelimit"></a>
|
|
416
|
+
### ๐ก๏ธ Reliability & rate limiting
|
|
137
417
|
|
|
138
418
|
```js
|
|
139
|
-
const
|
|
140
|
-
|
|
419
|
+
const { autoRetry, throttler, limiter } = require('@xbibzlibrary/telebibz');
|
|
420
|
+
|
|
421
|
+
bot.api.config.use(autoRetry()); // retry 429, honor retry_after (max 5)
|
|
422
|
+
bot.api.config.use(throttler()); // global queue โค 28 calls/second
|
|
423
|
+
bot.use(limiter({ windowMs: 2000, limit: 3, onExceeded })); // per-user anti-spam
|
|
141
424
|
```
|
|
142
425
|
|
|
143
|
-
|
|
426
|
+
Long polling is resilient by default: 409 conflicts retry every 5 s
|
|
427
|
+
(`launch({ conflictDelay: 5000 })` to tune), network hiccups back off 1 s,
|
|
428
|
+
`stop()` exits cleanly (`await bot.runPromise`).
|
|
429
|
+
|
|
430
|
+
<a id="sessions"></a>
|
|
431
|
+
### ๐๏ธ Sessions
|
|
144
432
|
|
|
145
433
|
```js
|
|
146
|
-
const {
|
|
147
|
-
|
|
434
|
+
const { session } = require('@xbibzlibrary/telebibz');
|
|
435
|
+
|
|
436
|
+
const bot = new TeleBibz(token, {
|
|
437
|
+
session: {
|
|
438
|
+
initial: () => ({ count: 0 }),
|
|
439
|
+
getKey: (ctx) => `${ctx.from?.id}:${ctx.chat?.id}`, // default
|
|
440
|
+
storage: myRedisAdapter, // { read(k), write(k,v), delete(k) } โ default: in-memory Map
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
bot.on(':text', (ctx) => { ctx.session.count++; });
|
|
148
445
|
```
|
|
149
446
|
|
|
150
|
-
|
|
447
|
+
`ctx.session` is always present, even without configuration.
|
|
448
|
+
|
|
449
|
+
<a id="errors"></a>
|
|
450
|
+
### ๐ฎ๐ฉ Human-readable errors
|
|
451
|
+
|
|
452
|
+
Every error is reported with an actionable suggestion:
|
|
151
453
|
|
|
152
|
-
Default-nya setiap error dilaporkan dengan **saran penyelesaian**:
|
|
153
454
|
```
|
|
154
455
|
โ Telegram error (403): Forbidden: bot was blocked by the user
|
|
155
456
|
๐ก saran: Bot diblokir pengguna โ jangan kirim ulang, hapus dari daftar broadcast.
|
|
156
457
|
```
|
|
157
|
-
Kustom: `new TeleBibz(token, { onError: (err, ctx) => { ... } })`.
|
|
158
458
|
|
|
159
|
-
|
|
459
|
+
`humanize(err)` returns `{ pesan, saran, method, code }` covering 15+ common
|
|
460
|
+
Telegram errors (bad token, chat not found, rights, parse errors, rate limits,
|
|
461
|
+
stale callbacks, oversize files, โฆ). Override with
|
|
462
|
+
`new TeleBibz(token, { onError: (err, ctx) => {} })`.
|
|
160
463
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
464
|
+
<a id="webhook"></a>
|
|
465
|
+
### ๐ธ๏ธ Webhooks & serverless
|
|
466
|
+
|
|
467
|
+
```js
|
|
468
|
+
const http = require('http');
|
|
469
|
+
http.createServer((req, res) =>
|
|
470
|
+
req.url === '/tg' ? bot.webhook()(req, res) : res.end('ok')
|
|
471
|
+
).listen(8443);
|
|
472
|
+
|
|
473
|
+
// any framework (Express/Fastify/Hono): mount the (req, res) handler from bot.webhook()
|
|
474
|
+
// or serverless, directly:
|
|
475
|
+
await bot.handleUpdate(req.body); // one raw update in โ full pipeline
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Remember `await bot.init()` first when you don't call `launch()` (it fetches
|
|
479
|
+
bot info), and `setWebhook(url)` via `bot.api.setWebhook({ url })`.
|
|
168
480
|
|
|
169
|
-
|
|
481
|
+
<a id="proxy"></a>
|
|
482
|
+
### ๐ Proxy transport (VPS behind a proxy)
|
|
170
483
|
|
|
171
484
|
```js
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
485
|
+
const { TeleBibz, createTransport } = require('@xbibzlibrary/telebibz');
|
|
486
|
+
const bot = new TeleBibz(token, {
|
|
487
|
+
transport: createTransport(token, { proxy: 'http://user:pass@proxy:8080' }),
|
|
488
|
+
});
|
|
176
489
|
```
|
|
177
490
|
|
|
178
|
-
|
|
491
|
+
`createTransport(token, { apiRoot, proxy, timeoutMs, headers })` also lets you
|
|
492
|
+
point at a local Bot API server.
|
|
179
493
|
|
|
180
|
-
|
|
494
|
+
<a id="transformers"></a>
|
|
495
|
+
### ๐งช Transformers (escape hatch)
|
|
181
496
|
|
|
182
|
-
|
|
497
|
+
```js
|
|
498
|
+
bot.api.config.use(async (prev, method, payload) => {
|
|
499
|
+
console.log('โ', method); // observe/modify every Bot API call
|
|
500
|
+
return prev(method, payload);
|
|
501
|
+
});
|
|
183
502
|
|
|
184
|
-
|
|
503
|
+
// any method, even unreleased ones (Proxy magic):
|
|
504
|
+
await bot.api.sendDiceCustom({ chat_id: 1, emoji: '๐ฒ' });
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
<a id="analytics"></a>
|
|
508
|
+
## ๐ Analytics & Statistics
|
|
185
509
|
|
|
186
|
-
|
|
510
|
+
### ๐ This repo in numbers
|
|
511
|
+
|
|
512
|
+
| Metric | Value |
|
|
187
513
|
|---|---|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
|
514
|
+
| ๐ฆ Source modules | **16 files** in `lib/` |
|
|
515
|
+
| ๐ Total lines of code | **~1,700** (no build step) |
|
|
516
|
+
| ๐ Bot API methods | **90+** โ 75 typed shortcuts + unbounded Proxy |
|
|
517
|
+
| โจ๏ธ Context shortcuts | **50+** (reply/edit/delete/admin/reactโฆ) |
|
|
518
|
+
| ๐งช Offline tests | **30/30 passing**, zero network |
|
|
519
|
+
| ๐งฉ Ready examples | **7** in `examples/` |
|
|
520
|
+
| ๐ฆ Runtime dependencies | **4** โ all used, all tested |
|
|
521
|
+
|
|
522
|
+
### โฌ๏ธ Downloads & popularity (live from npm)
|
|
193
523
|
|
|
194
|
-
|
|
524
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
525
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
526
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
527
|
+
[](https://www.npmjs.com/package/@xbibzlibrary/telebibz)
|
|
195
528
|
|
|
529
|
+
### ๐ Module size map (lines of code)
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
wizard.js โโโโโโโโโโโโโโโโโโโโโโโโโ 247 โ forms + buttons + edit/delete
|
|
533
|
+
telebibz.js โโโโโโโโโโโโโโโโโโโโ 193 โ main class & lifecycle
|
|
534
|
+
context.js โโโโโโโโโโโโโโโโโโโ 190 โ ctx + 50-ish shortcuts
|
|
535
|
+
composer.js โโโโโโโโโโโโโโโโโโ 174 โ middleware engine & filters
|
|
536
|
+
api.js โโโโโโโโโโโโโโโโ 154 โ 75 shortcuts + Proxy + transformers
|
|
537
|
+
net.js โโโโโโโโโโโโ 115 โ axios transport + multipart
|
|
538
|
+
menus.js โโโโโโโโโ 90 โ Menu/MenuContainer
|
|
539
|
+
keyboard.js โโโโโโโโโ 83 โ btn/url/kb + fluent classes
|
|
540
|
+
ratelimit.js โโโโโโ 61 โ autoRetry ยท throttler ยท limiter
|
|
541
|
+
runner.js โโโโโ 45 โ 409-resilient polling
|
|
542
|
+
file.js โโโโ 41 โ InputFile + InputMediaBuilder
|
|
543
|
+
logger.js โโโโ 38 โ logs + banner
|
|
544
|
+
session.js โโโโ 36 โ swappable sessions
|
|
545
|
+
errors.js โโโโ 35 โ humanized errors ๐ฎ
|
|
546
|
+
broadcast.js โโโ 35 โ rate-limit-safe blast
|
|
547
|
+
inline-query.jsโโโ 28 โ matcher + result builders
|
|
196
548
|
```
|
|
197
|
-
|
|
549
|
+
|
|
550
|
+
### ๐บ๏ธ Repo health
|
|
551
|
+
|
|
552
|
+
<div align="center">
|
|
553
|
+
|
|
554
|
+
[](https://github.com/XbibzOfficial777/telebibz)
|
|
555
|
+
|
|
556
|
+
</div>
|
|
557
|
+
|
|
558
|
+
<details>
|
|
559
|
+
<summary>๐
Star history (click to open)</summary>
|
|
560
|
+
|
|
561
|
+

|
|
562
|
+
|
|
563
|
+
</details>
|
|
564
|
+
|
|
565
|
+
<a id="examples"></a>
|
|
566
|
+
## ๐งฉ Ready Examples (`examples/`)
|
|
567
|
+
|
|
568
|
+
| File | Content |
|
|
569
|
+
|---|---|
|
|
570
|
+
| `01-quickstart.js` | bot up in 6 lines |
|
|
571
|
+
| `02-menu-tombol.js` | colored keyboard + animated icons |
|
|
572
|
+
| `03-wizard.js` | registration form + **buttons + edit mode** |
|
|
573
|
+
| `04-broadcast.js` | admin blast |
|
|
574
|
+
| `05-kirim-file.js` | photos & documents from buffers |
|
|
575
|
+
| `06-menu.js` | interactive menus + submenus |
|
|
576
|
+
| `07-inline-query.js` | inline mode with result builders |
|
|
577
|
+
|
|
578
|
+
Run any of them with `BOT_TOKEN=123:abc node examples/01-quickstart.js`.
|
|
579
|
+
|
|
580
|
+
<a id="testing"></a>
|
|
581
|
+
## ๐ฌ Testing & Live Proof
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
npm test # 30 cases, NO network (transport injected)
|
|
198
585
|
```
|
|
199
586
|
|
|
200
|
-
|
|
201
|
-
|
|
587
|
+
Validated **30/30 offline + 10 live** on the production bot `@xbibzrat_bot`:
|
|
588
|
+
getMe ยท colored keyboards & real animated icons ยท multipart uploads
|
|
589
|
+
(photo+document) ยท keyboard editing ยท broadcast ยท deleteMessage ยท polling 409
|
|
590
|
+
retry ยท wizard buttons & edit/delete.
|
|
202
591
|
|
|
203
|
-
|
|
592
|
+
Debug logging: `DEBUG=telebibz:net,telebibz:ratelimit node yourbot.js`.
|
|
204
593
|
|
|
205
|
-
|
|
594
|
+
<a id="structure"></a>
|
|
595
|
+
## ๐ Repo Structure (16 core files)
|
|
596
|
+
|
|
597
|
+
| File | Role |
|
|
206
598
|
|---|---|
|
|
207
|
-
| `lib/net.js` |
|
|
208
|
-
| `lib/api.js` |
|
|
209
|
-
| `lib/composer.js` | middleware,
|
|
210
|
-
| `lib/context.js` |
|
|
211
|
-
| `lib/session.js` |
|
|
212
|
-
| `lib/runner.js` | long polling: retry
|
|
213
|
-
| `lib/
|
|
214
|
-
| `lib/
|
|
215
|
-
| `lib/
|
|
216
|
-
| `lib/
|
|
217
|
-
| `lib/
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
| |
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
599
|
+
| `lib/net.js` | axios keep-alive transport + multipart `attach://` |
|
|
600
|
+
| `lib/api.js` | Bot API methods + any-method Proxy + transformers |
|
|
601
|
+
| `lib/composer.js` | middleware, `on('message:photo')` filters, `errorBoundary` |
|
|
602
|
+
| `lib/context.js` | ctx object + 50-ish reply/edit/delete/callback shortcuts |
|
|
603
|
+
| `lib/session.js` | per user:chat sessions (swappable storage) |
|
|
604
|
+
| `lib/runner.js` | long polling: 409 retry, network backoff, drop pending |
|
|
605
|
+
| `lib/wizard.js` | conversation forms + choice buttons + edit/delete modes |
|
|
606
|
+
| `lib/menus.js` | `Menu`/`MenuContainer` interactive menus |
|
|
607
|
+
| `lib/keyboard.js` | button builders + fluent `InlineKeyboard`/`Keyboard` |
|
|
608
|
+
| `lib/ratelimit.js` | `autoRetry` 429 ยท `throttler` queue ยท per-user `limiter` |
|
|
609
|
+
| `lib/broadcast.js` | rate-limit-safe blast |
|
|
610
|
+
| `lib/file.js` | `File`/`InputFile` (Buffer/path/stream) + `InputMediaBuilder` |
|
|
611
|
+
| `lib/inline-query.js` | query matcher + inline result builders |
|
|
612
|
+
| `lib/errors.js` | humanized errors + suggestions |
|
|
613
|
+
| `lib/logger.js` | framed logs + boot banner |
|
|
614
|
+
| `index.js` / `index.d.ts` | export door + TypeScript types |
|
|
615
|
+
|
|
616
|
+
<a id="changelog"></a>
|
|
617
|
+
## ๐ Changelog
|
|
618
|
+
|
|
619
|
+
- **3.1.0** โ wizard: choice buttons (reply/inline), `edit`/`delete` modes, auto cleanup, programmatic helpers ยท tests 24 โ 30
|
|
620
|
+
- **3.0.0** โ production-grade grammY parity: axios keep-alive, transformers, menus, inline query, limiter
|
|
621
|
+
- **2.0.0** โ engine rewritten from scratch, multipart transport, native Node webhook
|
|
622
|
+
- **1.0.0** โ grammY architecture recode
|
|
623
|
+
|
|
624
|
+
> Full details in [`CHANGELOG.md`](CHANGELOG.md). Deep architecture study (๐ฎ): [`ANALISIS-telebibz.md`](ANALISIS-telebibz.md).
|
|
625
|
+
|
|
626
|
+
<a id="license"></a>
|
|
627
|
+
## ๐ License
|
|
628
|
+
|
|
629
|
+
**MIT** ยฉ Xbibz Official โ architecture inspired by [grammY](https://grammy.dev) (MIT, see [`NOTICE.md`](NOTICE.md)).
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
<div align="center">
|
|
634
|
+
|
|
635
|
+
**Made with โค๏ธ by //โXbibz Officialโ//**
|
|
636
|
+
|
|
637
|
+
If telebibz helps you, a โญ on this repo means a lot.
|
|
229
638
|
|
|
230
|
-
|
|
639
|
+
[](https://github.com/XbibzOfficial777/telebibz)
|
|
231
640
|
|
|
232
|
-
|
|
641
|
+
</div>
|