@xbibzlibrary/telebibz 0.4.4 → 3.0.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 +35 -138
- package/LICENSE +1 -1
- package/NOTICE.md +9 -4
- package/README.md +173 -242
- package/examples/01-quickstart.js +13 -0
- package/examples/02-menu-tombol.js +22 -0
- package/examples/03-wizard.js +29 -0
- package/examples/04-broadcast.js +25 -0
- package/examples/05-kirim-file.js +19 -0
- package/examples/06-menu.js +35 -0
- package/examples/07-inline-query.js +18 -0
- package/index.d.ts +81 -0
- package/index.js +42 -0
- package/lib/api.js +154 -0
- package/lib/broadcast.js +35 -0
- package/lib/composer.js +174 -0
- package/lib/context.js +190 -0
- package/lib/errors.js +35 -0
- package/lib/file.js +41 -0
- package/lib/inline-query.js +28 -0
- package/lib/keyboard.js +83 -0
- package/lib/logger.js +38 -0
- package/lib/menus.js +90 -0
- package/lib/net.js +115 -0
- package/lib/ratelimit.js +61 -0
- package/lib/runner.js +45 -0
- package/lib/session.js +36 -0
- package/lib/telebibz.js +190 -0
- package/lib/wizard.js +81 -0
- package/package.json +35 -97
- package/test/all.test.js +334 -0
- package/CODE_OF_CONDUCT.md +0 -37
- package/CONTRIBUTING.md +0 -59
- package/CONTRIBUTION_RULES.md +0 -41
- package/GOVERNANCE.md +0 -47
- package/README.id.md +0 -292
- package/README.zh-CN.md +0 -292
- package/RELEASE_AUTOMATION.md +0 -78
- package/RELEASE_POLICY.md +0 -32
- package/SECURITY.md +0 -47
- package/SHOWCASE.md +0 -29
- package/SUPPORT.md +0 -30
- package/assets/readme-preview.html +0 -75
- package/assets/telebibz-logo.png +0 -0
- package/assets/telebibz-readme-preview.png +0 -0
- package/bin/telebibz.mjs +0 -3
- package/dist/generated/api.d.ts +0 -13
- package/dist/generated/api.d.ts.map +0 -1
- package/dist/generated/api.js +0 -192
- package/dist/generated/api.js.map +0 -1
- package/dist/src/api/client.d.ts +0 -62
- package/dist/src/api/client.d.ts.map +0 -1
- package/dist/src/api/client.js +0 -104
- package/dist/src/api/client.js.map +0 -1
- package/dist/src/api/errors.d.ts +0 -45
- package/dist/src/api/errors.d.ts.map +0 -1
- package/dist/src/api/errors.js +0 -65
- package/dist/src/api/errors.js.map +0 -1
- package/dist/src/api/index.d.ts +0 -6
- package/dist/src/api/index.d.ts.map +0 -1
- package/dist/src/api/index.js +0 -6
- package/dist/src/api/index.js.map +0 -1
- package/dist/src/api/telegram-types/LICENSE +0 -21
- package/dist/src/api/telegram-types/api.d.ts +0 -22
- package/dist/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist/src/api/telegram-types/inline.d.ts +0 -692
- package/dist/src/api/telegram-types/langs.d.ts +0 -193
- package/dist/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist/src/api/telegram-types/markup.d.ts +0 -268
- package/dist/src/api/telegram-types/message.d.ts +0 -1537
- package/dist/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist/src/api/telegram-types/mod.d.ts +0 -14
- package/dist/src/api/telegram-types/passport.d.ts +0 -163
- package/dist/src/api/telegram-types/payment.d.ts +0 -570
- package/dist/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist/src/api/telegram-types/settings.d.ts +0 -120
- package/dist/src/api/telegram-types/story.d.ts +0 -89
- package/dist/src/api/telegram-types/update.d.ts +0 -84
- package/dist/src/api/telegram.d.ts +0 -7
- package/dist/src/api/telegram.d.ts.map +0 -1
- package/dist/src/api/telegram.js +0 -2
- package/dist/src/api/telegram.js.map +0 -1
- package/dist/src/api/transport.d.ts +0 -68
- package/dist/src/api/transport.d.ts.map +0 -1
- package/dist/src/api/transport.js +0 -264
- package/dist/src/api/transport.js.map +0 -1
- package/dist/src/api/types.d.ts +0 -466
- package/dist/src/api/types.d.ts.map +0 -1
- package/dist/src/api/types.js +0 -2
- package/dist/src/api/types.js.map +0 -1
- package/dist/src/branding/terminal.d.ts +0 -77
- package/dist/src/branding/terminal.d.ts.map +0 -1
- package/dist/src/branding/terminal.js +0 -328
- package/dist/src/branding/terminal.js.map +0 -1
- package/dist/src/broadcast/broadcast.d.ts +0 -50
- package/dist/src/broadcast/broadcast.d.ts.map +0 -1
- package/dist/src/broadcast/broadcast.js +0 -56
- package/dist/src/broadcast/broadcast.js.map +0 -1
- package/dist/src/cache/cache.d.ts +0 -34
- package/dist/src/cache/cache.d.ts.map +0 -1
- package/dist/src/cache/cache.js +0 -41
- package/dist/src/cache/cache.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -84
- package/dist/src/cli.js.map +0 -1
- package/dist/src/context/context.d.ts +0 -124
- package/dist/src/context/context.d.ts.map +0 -1
- package/dist/src/context/context.js +0 -302
- package/dist/src/context/context.js.map +0 -1
- package/dist/src/core/bot.d.ts +0 -204
- package/dist/src/core/bot.d.ts.map +0 -1
- package/dist/src/core/bot.js +0 -506
- package/dist/src/core/bot.js.map +0 -1
- package/dist/src/core/events.d.ts +0 -75
- package/dist/src/core/events.d.ts.map +0 -1
- package/dist/src/core/events.js +0 -35
- package/dist/src/core/events.js.map +0 -1
- package/dist/src/core/webhook-reply.d.ts +0 -34
- package/dist/src/core/webhook-reply.d.ts.map +0 -1
- package/dist/src/core/webhook-reply.js +0 -37
- package/dist/src/core/webhook-reply.js.map +0 -1
- package/dist/src/index.d.ts +0 -24
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -24
- package/dist/src/index.js.map +0 -1
- package/dist/src/keyboard/index.d.ts +0 -46
- package/dist/src/keyboard/index.d.ts.map +0 -1
- package/dist/src/keyboard/index.js +0 -55
- package/dist/src/keyboard/index.js.map +0 -1
- package/dist/src/middleware/compose.d.ts +0 -5
- package/dist/src/middleware/compose.d.ts.map +0 -1
- package/dist/src/middleware/compose.js +0 -17
- package/dist/src/middleware/compose.js.map +0 -1
- package/dist/src/observability/logger.d.ts +0 -78
- package/dist/src/observability/logger.d.ts.map +0 -1
- package/dist/src/observability/logger.js +0 -285
- package/dist/src/observability/logger.js.map +0 -1
- package/dist/src/plugins/plugin.d.ts +0 -38
- package/dist/src/plugins/plugin.d.ts.map +0 -1
- package/dist/src/plugins/plugin.js +0 -59
- package/dist/src/plugins/plugin.js.map +0 -1
- package/dist/src/queue/queue.d.ts +0 -77
- package/dist/src/queue/queue.d.ts.map +0 -1
- package/dist/src/queue/queue.js +0 -213
- package/dist/src/queue/queue.js.map +0 -1
- package/dist/src/router/router.d.ts +0 -61
- package/dist/src/router/router.d.ts.map +0 -1
- package/dist/src/router/router.js +0 -183
- package/dist/src/router/router.js.map +0 -1
- package/dist/src/state/conversation.d.ts +0 -56
- package/dist/src/state/conversation.d.ts.map +0 -1
- package/dist/src/state/conversation.js +0 -133
- package/dist/src/state/conversation.js.map +0 -1
- package/dist/src/state/forms.d.ts +0 -34
- package/dist/src/state/forms.d.ts.map +0 -1
- package/dist/src/state/forms.js +0 -44
- package/dist/src/state/forms.js.map +0 -1
- package/dist/src/state/menu.d.ts +0 -78
- package/dist/src/state/menu.d.ts.map +0 -1
- package/dist/src/state/menu.js +0 -127
- package/dist/src/state/menu.js.map +0 -1
- package/dist/src/storage/storage.d.ts +0 -146
- package/dist/src/storage/storage.d.ts.map +0 -1
- package/dist/src/storage/storage.js +0 -195
- package/dist/src/storage/storage.js.map +0 -1
- package/dist/src/telegram-features.d.ts +0 -33
- package/dist/src/telegram-features.d.ts.map +0 -1
- package/dist/src/telegram-features.js +0 -71
- package/dist/src/telegram-features.js.map +0 -1
- package/dist/src/testing.d.ts +0 -24
- package/dist/src/testing.d.ts.map +0 -1
- package/dist/src/testing.js +0 -38
- package/dist/src/testing.js.map +0 -1
- package/dist/src/utils/concurrency.d.ts +0 -25
- package/dist/src/utils/concurrency.d.ts.map +0 -1
- package/dist/src/utils/concurrency.js +0 -52
- package/dist/src/utils/concurrency.js.map +0 -1
- package/dist/src/utils/files.d.ts +0 -45
- package/dist/src/utils/files.d.ts.map +0 -1
- package/dist/src/utils/files.js +0 -53
- package/dist/src/utils/files.js.map +0 -1
- package/dist/src/utils/text.d.ts +0 -39
- package/dist/src/utils/text.d.ts.map +0 -1
- package/dist/src/utils/text.js +0 -56
- package/dist/src/utils/text.js.map +0 -1
- package/dist/src/webhook/handler.d.ts +0 -19
- package/dist/src/webhook/handler.d.ts.map +0 -1
- package/dist/src/webhook/handler.js +0 -141
- package/dist/src/webhook/handler.js.map +0 -1
- package/dist-cjs/generated/api.js +0 -194
- package/dist-cjs/package.json +0 -3
- package/dist-cjs/src/api/client.js +0 -107
- package/dist-cjs/src/api/errors.js +0 -74
- package/dist-cjs/src/api/index.js +0 -21
- package/dist-cjs/src/api/telegram-types/LICENSE +0 -21
- package/dist-cjs/src/api/telegram-types/api.d.ts +0 -22
- package/dist-cjs/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist-cjs/src/api/telegram-types/inline.d.ts +0 -692
- package/dist-cjs/src/api/telegram-types/langs.d.ts +0 -193
- package/dist-cjs/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist-cjs/src/api/telegram-types/markup.d.ts +0 -268
- package/dist-cjs/src/api/telegram-types/message.d.ts +0 -1537
- package/dist-cjs/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist-cjs/src/api/telegram-types/mod.d.ts +0 -14
- package/dist-cjs/src/api/telegram-types/passport.d.ts +0 -163
- package/dist-cjs/src/api/telegram-types/payment.d.ts +0 -570
- package/dist-cjs/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist-cjs/src/api/telegram-types/settings.d.ts +0 -120
- package/dist-cjs/src/api/telegram-types/story.d.ts +0 -89
- package/dist-cjs/src/api/telegram-types/update.d.ts +0 -84
- package/dist-cjs/src/api/telegram.js +0 -2
- package/dist-cjs/src/api/transport.js +0 -267
- package/dist-cjs/src/api/types.js +0 -2
- package/dist-cjs/src/branding/terminal.js +0 -338
- package/dist-cjs/src/broadcast/broadcast.js +0 -58
- package/dist-cjs/src/cache/cache.js +0 -45
- package/dist-cjs/src/cli.js +0 -86
- package/dist-cjs/src/context/context.js +0 -305
- package/dist-cjs/src/core/bot.js +0 -510
- package/dist-cjs/src/core/events.js +0 -38
- package/dist-cjs/src/core/webhook-reply.js +0 -42
- package/dist-cjs/src/index.js +0 -47
- package/dist-cjs/src/keyboard/index.js +0 -61
- package/dist-cjs/src/middleware/compose.js +0 -20
- package/dist-cjs/src/observability/logger.js +0 -293
- package/dist-cjs/src/plugins/plugin.js +0 -63
- package/dist-cjs/src/queue/queue.js +0 -219
- package/dist-cjs/src/router/router.js +0 -186
- package/dist-cjs/src/state/conversation.js +0 -139
- package/dist-cjs/src/state/forms.js +0 -47
- package/dist-cjs/src/state/menu.js +0 -133
- package/dist-cjs/src/storage/storage.js +0 -202
- package/dist-cjs/src/telegram-features.js +0 -76
- package/dist-cjs/src/testing.js +0 -45
- package/dist-cjs/src/utils/concurrency.js +0 -57
- package/dist-cjs/src/utils/files.js +0 -58
- package/dist-cjs/src/utils/text.js +0 -63
- package/dist-cjs/src/webhook/handler.js +0 -144
- package/docs/API.id.md +0 -1935
- package/docs/API.md +0 -1969
- package/docs/API.zh-CN.md +0 -1929
- package/docs/GETTING_STARTED.id.md +0 -85
- package/docs/GETTING_STARTED.md +0 -85
- package/docs/GETTING_STARTED.zh-CN.md +0 -85
- package/docs/GITHUB_PACKAGES.id.md +0 -82
- package/docs/GITHUB_PACKAGES.md +0 -82
- package/docs/GITHUB_PACKAGES.zh-CN.md +0 -82
- package/docs/README.md +0 -48
- package/docs/STORAGE.id.md +0 -105
- package/docs/STORAGE.md +0 -105
- package/docs/STORAGE.zh-CN.md +0 -105
- package/examples/README.md +0 -37
- package/examples/files.ts +0 -35
- package/examples/minimal.ts +0 -12
- package/examples/tsconfig.json +0 -9
- package/examples/webhook.ts +0 -42
- package/examples/wizard-registration.ts +0 -42
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// examples/07-inline-query.js — mode inline: ketik @botkamu apa saja di chat apa pun.
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { TeleBibz, iq } = require('..');
|
|
5
|
+
|
|
6
|
+
const bot = new TeleBibz(process.env.BOT_TOKEN);
|
|
7
|
+
|
|
8
|
+
bot.inlineQuery('*', async (ctx) => {
|
|
9
|
+
const q = (ctx.inline_query.query || '').trim();
|
|
10
|
+
const hasil = [
|
|
11
|
+
iq.article('echo', `Echo: ${q || '…'}`, { message_text: q || 'ketik sesuatu setelah @botkamu' }),
|
|
12
|
+
iq.article('caps', 'HURUF BESAR', { message_text: (q || 'HALO').toUpperCase() }),
|
|
13
|
+
iq.article('terbalik', 'Balik teks', { message_text: [...(q || 'halo')].reverse().join('') }),
|
|
14
|
+
];
|
|
15
|
+
await ctx.answerInlineQuery(hasil, { cache_time: 0, is_personal: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
bot.launch();
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// index.d.ts — tipe ringkas untuk autocompletion editor (autocorrect).
|
|
2
|
+
// v3: tipe longgar (JS-first) — parity grammY.
|
|
3
|
+
type Context = any; type Bot = any;
|
|
4
|
+
|
|
5
|
+
declare module '@xbibzlibrary/telebibz' {
|
|
6
|
+
type Color = 'danger' | 'success' | 'primary';
|
|
7
|
+
type Handler = (ctx: Context) => unknown;
|
|
8
|
+
|
|
9
|
+
interface WizardStep {
|
|
10
|
+
key: string;
|
|
11
|
+
ask: string | ((ctx: Context) => string | Promise<string>);
|
|
12
|
+
parse?: (text: string, ctx: Context) => unknown | Promise<unknown>;
|
|
13
|
+
validate?: (value: unknown, ctx: Context) => string | null | Promise<string | null>;
|
|
14
|
+
opts?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
interface WizardDef {
|
|
17
|
+
steps: WizardStep[];
|
|
18
|
+
done: (answers: Record<string, unknown>, ctx: Context) => unknown;
|
|
19
|
+
cancelWords?: string[];
|
|
20
|
+
onCancel?: (ctx: Context) => unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TeleBibzOpts {
|
|
24
|
+
allowedUpdates?: string[];
|
|
25
|
+
onError?: (err: unknown, ctx?: Context) => unknown;
|
|
26
|
+
silent?: boolean;
|
|
27
|
+
dropPending?: boolean;
|
|
28
|
+
grammy?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class TeleBibz {
|
|
32
|
+
constructor(token: string, opts?: TeleBibzOpts);
|
|
33
|
+
bot: Bot<Context>;
|
|
34
|
+
api: Bot<Context>['api'];
|
|
35
|
+
botInfo: any;
|
|
36
|
+
use(...mw: any[]): this;
|
|
37
|
+
cmd(names: string | string[], ...mw: Handler[]): this;
|
|
38
|
+
hears(match: string | RegExp, ...mw: Handler[]): this;
|
|
39
|
+
action(trigs: string | RegExp | string[], ...mw: Handler[]): this;
|
|
40
|
+
on(filter: string | string[], ...mw: Handler[]): this;
|
|
41
|
+
start(replies: string | Handler): this;
|
|
42
|
+
wizard(id: string, def: WizardDef, bindCommand?: boolean): this;
|
|
43
|
+
wizardStart(ctx: Context, id: string): Promise<unknown>;
|
|
44
|
+
wizardActive(ctx: Context): boolean;
|
|
45
|
+
broadcast(ids: Array<number | string>, pesan: any, opts?: { delay?: number }): Promise<{ terkirim: number; gagal: number; errors: Array<{ chatId: any; pesan: string }> }>;
|
|
46
|
+
launch(opts?: Record<string, unknown>): Promise<this>;
|
|
47
|
+
handleUpdate(update: any): Promise<void>;
|
|
48
|
+
webhook(...args: any[]): any;
|
|
49
|
+
stop(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function btn(text: string, callbackData: string | number, style?: Color, iconId?: string): any;
|
|
53
|
+
function url(text: string, link: string, style?: Color, iconId?: string): any;
|
|
54
|
+
function webApp(text: string, link: string, iconId?: string): any;
|
|
55
|
+
function copy(text: string, value: string, iconId?: string): any;
|
|
56
|
+
function kb(rows: any[][]): { reply_markup: { inline_keyboard: any[][] } };
|
|
57
|
+
namespace kb {
|
|
58
|
+
function markup(rows: any[][]): { inline_keyboard: any[][] };
|
|
59
|
+
function confirm(yesData: string, noData: string, labelYes?: string, labelNo?: string): any;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const wizard: {
|
|
63
|
+
define: (id: string, def: WizardDef) => string;
|
|
64
|
+
start: (ctx: Context, id: string) => Promise<unknown>;
|
|
65
|
+
active: (ctx: Context) => boolean;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function humanize(err: unknown): { pesan: string; saran: string | null; method?: string; code?: number };
|
|
69
|
+
const log: { info: Function; ok: Function; warn: Function; error: Function; banner: Function };
|
|
70
|
+
const say: { html: (text: string, extra?: object) => object };
|
|
71
|
+
|
|
72
|
+
class File { constructor(src: any, filename?: string, type?: string); src: any; filename: string; data(): Promise<Buffer>; }
|
|
73
|
+
const InputFile: typeof File;
|
|
74
|
+
const InlineKeyboard: any;
|
|
75
|
+
const Keyboard: any;
|
|
76
|
+
class ApiError extends Error { description: string; error_code?: number; method?: string; payload?: any; }
|
|
77
|
+
const Composer: any; const BotError: any; const session: any; const Api: any;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
import { Menu as _M } from './lib/menus';
|
|
81
|
+
export {};
|
package/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// index.js — pintu masuk @xbibzlibrary/telebibz.
|
|
2
|
+
// v3: set fitur production-grade setara grammY (lihat README -> FEATURE MATRIX).
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { TeleBibz } = require('./lib/telebibz');
|
|
6
|
+
const { ApiError, createTransport } = require('./lib/net');
|
|
7
|
+
const { File, InputFile, InputMediaBuilder } = require('./lib/file');
|
|
8
|
+
const { btn, url, webApp, copy, kb, InlineKeyboard, Keyboard } = require('./lib/keyboard');
|
|
9
|
+
const wizard = require('./lib/wizard');
|
|
10
|
+
const { humanize } = require('./lib/errors');
|
|
11
|
+
const log = require('./lib/logger');
|
|
12
|
+
const { session } = require('./lib/session');
|
|
13
|
+
const { Composer, BotError } = require('./lib/composer');
|
|
14
|
+
const { makeApi } = require('./lib/api');
|
|
15
|
+
const { Context } = require('./lib/context');
|
|
16
|
+
const { broadcast } = require('./lib/broadcast');
|
|
17
|
+
const { autoRetry, throttler, limiter } = require('./lib/ratelimit');
|
|
18
|
+
const { matchInlineQuery, iq } = require('./lib/inline-query');
|
|
19
|
+
const { Menu, MenuContainer } = require('./lib/menus');
|
|
20
|
+
|
|
21
|
+
const say = {
|
|
22
|
+
html: (text, extra = {}) => ({ ...extra, text, parse_mode: 'HTML' }),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
// kelas utama
|
|
27
|
+
TeleBibz, Context, Composer, BotError, session,
|
|
28
|
+
// keyboard & menu
|
|
29
|
+
btn, url, webApp, copy, kb, InlineKeyboard, Keyboard, Menu, MenuContainer,
|
|
30
|
+
// percakapan
|
|
31
|
+
wizard,
|
|
32
|
+
// file & media
|
|
33
|
+
File, InputFile, InputMediaBuilder,
|
|
34
|
+
// api & transport
|
|
35
|
+
makeApi, createTransport, ApiError,
|
|
36
|
+
// anti-spam & keandalan
|
|
37
|
+
autoRetry, throttler, limiter, broadcast,
|
|
38
|
+
// inline mode
|
|
39
|
+
matchInlineQuery, iq,
|
|
40
|
+
// util
|
|
41
|
+
humanize, log, say,
|
|
42
|
+
};
|
package/lib/api.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// lib/api.js — akses penuh Bot API: ~60 shortcut + PROXY untuk SEGALA metode +
|
|
2
|
+
// transformer pipeline (api.config.use) gaya grammY + download file.
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { createTransport } = require('./net');
|
|
6
|
+
|
|
7
|
+
class ApiConfig {
|
|
8
|
+
constructor() { this.transformers = []; }
|
|
9
|
+
/** Sisipkan transformer: (prev, method, payload) => result */
|
|
10
|
+
use(t) { this.transformers.push(t); return this; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class ApiBase {
|
|
14
|
+
constructor(token, transport) {
|
|
15
|
+
this.token = token;
|
|
16
|
+
this.config = new ApiConfig();
|
|
17
|
+
this._base = transport || createTransport(token, {});
|
|
18
|
+
this.raw = (method, payload = {}) => this.callApi(method, payload);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async callApi(method, payload = {}) {
|
|
22
|
+
let fn = this._base;
|
|
23
|
+
for (const t of this.config.transformers) {
|
|
24
|
+
const prev = fn;
|
|
25
|
+
fn = (m, p) => t(prev, m, p);
|
|
26
|
+
}
|
|
27
|
+
return fn(method, payload);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* =============== metode umum =============== */
|
|
31
|
+
getMe() { return this.callApi('getMe'); }
|
|
32
|
+
logOut() { return this.callApi('logOut'); }
|
|
33
|
+
close() { return this.callApi('close'); }
|
|
34
|
+
|
|
35
|
+
/* ---- pesan ---- */
|
|
36
|
+
sendMessage(chat_id, text, extra = {}) { return this.callApi('sendMessage', { chat_id, text, ...extra }); }
|
|
37
|
+
forwardMessage(chat_id, from_chat_id, message_id, extra = {}) { return this.callApi('forwardMessage', { chat_id, from_chat_id, message_id, ...extra }); }
|
|
38
|
+
forwardMessages(chat_id, from_chat_id, message_ids, extra = {}) { return this.callApi('forwardMessages', { chat_id, from_chat_id, message_ids, ...extra }); }
|
|
39
|
+
copyMessage(chat_id, from_chat_id, message_id, extra = {}) { return this.callApi('copyMessage', { chat_id, from_chat_id, message_id, ...extra }); }
|
|
40
|
+
copyMessages(chat_id, from_chat_id, message_ids, extra = {}) { return this.callApi('copyMessages', { chat_id, from_chat_id, message_ids, ...extra }); }
|
|
41
|
+
editMessageText(chat_id, message_id, text, extra = {}) { return this.callApi('editMessageText', { chat_id, message_id, text, ...extra }); }
|
|
42
|
+
editMessageCaption(chat_id, message_id, extra = {}) { return this.callApi('editMessageCaption', { chat_id, message_id, ...extra }); }
|
|
43
|
+
editMessageMedia(chat_id, message_id, media, extra = {}) { return this.callApi('editMessageMedia', { chat_id, message_id, media, ...extra }); }
|
|
44
|
+
editMessageReplyMarkup(chat_id, message_id, extra = {}) { return this.callApi('editMessageReplyMarkup', { chat_id, message_id, ...extra }); }
|
|
45
|
+
deleteMessage(chat_id, message_id) { return this.callApi('deleteMessage', { chat_id, message_id }); }
|
|
46
|
+
deleteMessages(chat_id, message_ids) { return this.callApi('deleteMessages', { chat_id, message_ids }); }
|
|
47
|
+
setMessageReaction(chat_id, message_id, reaction, extra = {}) { return this.callApi('setMessageReaction', { chat_id, message_id, reaction, ...extra }); }
|
|
48
|
+
sendChatAction(chat_id, action, extra = {}) { return this.callApi('sendChatAction', { chat_id, action, ...extra }); }
|
|
49
|
+
|
|
50
|
+
/* ---- media ---- */
|
|
51
|
+
sendPhoto(c, photo, e = {}) { return this.callApi('sendPhoto', { chat_id: c, photo, ...e }); }
|
|
52
|
+
sendVideo(c, video, e = {}) { return this.callApi('sendVideo', { chat_id: c, video, ...e }); }
|
|
53
|
+
sendAudio(c, audio, e = {}) { return this.callApi('sendAudio', { chat_id: c, audio, ...e }); }
|
|
54
|
+
sendDocument(c, document, e = {}) { return this.callApi('sendDocument', { chat_id: c, document, ...e }); }
|
|
55
|
+
sendAnimation(c, animation, e = {}) { return this.callApi('sendAnimation', { chat_id: c, animation, ...e }); }
|
|
56
|
+
sendVoice(c, voice, e = {}) { return this.callApi('sendVoice', { chat_id: c, voice, ...e }); }
|
|
57
|
+
sendVideoNote(c, vn, e = {}) { return this.callApi('sendVideoNote', { chat_id: c, video_note: vn, ...e }); }
|
|
58
|
+
sendSticker(c, sticker, e = {}) { return this.callApi('sendSticker', { chat_id: c, sticker, ...e }); }
|
|
59
|
+
sendMediaGroup(c, media, e = {}) { return this.callApi('sendMediaGroup', { chat_id: c, media, ...e }); }
|
|
60
|
+
sendLocation(c, lat, lon, e = {}) { return this.callApi('sendLocation', { chat_id: c, latitude: lat, longitude: lon, ...e }); }
|
|
61
|
+
sendVenue(c, lat, lon, title, address, e = {}) { return this.callApi('sendVenue', { chat_id: c, latitude: lat, longitude: lon, title, address, ...e }); }
|
|
62
|
+
sendContact(c, phone, first, e = {}) { return this.callApi('sendContact', { chat_id: c, phone_number: phone, first_name: first, ...e }); }
|
|
63
|
+
sendPoll(c, question, options, e = {}) { return this.callApi('sendPoll', { chat_id: c, question, options, ...e }); }
|
|
64
|
+
sendDice(c, emoji, e = {}) { return this.callApi('sendDice', { chat_id: c, ...(emoji ? { emoji } : {}), ...e }); }
|
|
65
|
+
stopPoll(c, message_id, e = {}) { return this.callApi('stopPoll', { chat_id: c, message_id, ...e }); }
|
|
66
|
+
|
|
67
|
+
/* ---- file ---- */
|
|
68
|
+
getFile(file_id) { return this.callApi('getFile', { file_id }); }
|
|
69
|
+
/** Unduh file_id ke disk; return path tujuan. */
|
|
70
|
+
async downloadFile(file_id, dest, axiosOpts = {}) {
|
|
71
|
+
const f = await this.getFile(file_id);
|
|
72
|
+
const axios = require('axios');
|
|
73
|
+
const fs = require('fs');
|
|
74
|
+
const url = `https://api.telegram.org/file/bot${this.token}/${f.file_path}`;
|
|
75
|
+
const res = await axios.get(url, { responseType: 'stream', ...axiosOpts });
|
|
76
|
+
const out = fs.createWriteStream(dest);
|
|
77
|
+
await new Promise((ok, no) => { res.data.pipe(out); out.on('finish', ok); out.on('error', no); res.data.on('error', no); });
|
|
78
|
+
return dest;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* ---- callback & inline ---- */
|
|
82
|
+
answerCallbackQuery(id, e = {}) { return this.callApi('answerCallbackQuery', { callback_query_id: id, ...e }); }
|
|
83
|
+
answerInlineQuery(id, results, e = {}) { return this.callApi('answerInlineQuery', { inline_query_id: id, results, ...e }); }
|
|
84
|
+
|
|
85
|
+
/* ---- grup & admin ---- */
|
|
86
|
+
banChatMember(c, u, e = {}) { return this.callApi('banChatMember', { chat_id: c, user_id: u, ...e }); }
|
|
87
|
+
unbanChatMember(c, u, e = {}) { return this.callApi('unbanChatMember', { chat_id: c, user_id: u, ...e }); }
|
|
88
|
+
restrictChatMember(c, u, permissions, e = {}) { return this.callApi('restrictChatMember', { chat_id: c, user_id: u, permissions, ...e }); }
|
|
89
|
+
promoteChatMember(c, u, e = {}) { return this.callApi('promoteChatMember', { chat_id: c, user_id: u, ...e }); }
|
|
90
|
+
setChatAdministratorCustomTitle(c, u, t) { return this.callApi('setChatAdministratorCustomTitle', { chat_id: c, user_id: u, custom_title: t }); }
|
|
91
|
+
approveChatJoinRequest(c, u) { return this.callApi('approveChatJoinRequest', { chat_id: c, user_id: u }); }
|
|
92
|
+
declineChatJoinRequest(c, u) { return this.callApi('declineChatJoinRequest', { chat_id: c, user_id: u }); }
|
|
93
|
+
getChat(c) { return this.callApi('getChat', { chat_id: c }); }
|
|
94
|
+
getChatAdministrators(c) { return this.callApi('getChatAdministrators', { chat_id: c }); }
|
|
95
|
+
getChatMemberCount(c) { return this.callApi('getChatMemberCount', { chat_id: c }); }
|
|
96
|
+
getChatMember(c, u) { return this.callApi('getChatMember', { chat_id: c, user_id: u }); }
|
|
97
|
+
leaveChat(c) { return this.callApi('leaveChat', { chat_id: c }); }
|
|
98
|
+
setChatTitle(c, title) { return this.callApi('setChatTitle', { chat_id: c, title }); }
|
|
99
|
+
setChatDescription(c, description) { return this.callApi('setChatDescription', { chat_id: c, description }); }
|
|
100
|
+
setChatPhoto(c, photo) { return this.callApi('setChatPhoto', { chat_id: c, photo }); }
|
|
101
|
+
setChatPermissions(c, permissions, e = {}) { return this.callApi('setChatPermissions', { chat_id: c, permissions, ...e }); }
|
|
102
|
+
pinChatMessage(c, m, e = {}) { return this.callApi('pinChatMessage', { chat_id: c, message_id: m, ...e }); }
|
|
103
|
+
unpinChatMessage(c, e = {}) { return this.callApi('unpinChatMessage', { chat_id: c, ...e }); }
|
|
104
|
+
unpinAllChatMessages(c) { return this.callApi('unpinAllChatMessages', { chat_id: c }); }
|
|
105
|
+
exportChatInviteLink(c) { return this.callApi('exportChatInviteLink', { chat_id: c }); }
|
|
106
|
+
|
|
107
|
+
/* ---- konfigurasi bot ---- */
|
|
108
|
+
setMyCommands(commands, e = {}) { return this.callApi('setMyCommands', { commands, ...e }); }
|
|
109
|
+
getMyCommands(e = {}) { return this.callApi('getMyCommands', e); }
|
|
110
|
+
deleteMyCommands(e = {}) { return this.callApi('deleteMyCommands', e); }
|
|
111
|
+
setMyName(e = {}) { return this.callApi('setMyName', e); }
|
|
112
|
+
getMyName(e = {}) { return this.callApi('getMyName', e); }
|
|
113
|
+
setMyDescription(e = {}) { return this.callApi('setMyDescription', e); }
|
|
114
|
+
getMyDescription(e = {}) { return this.callApi('getMyDescription', e); }
|
|
115
|
+
setMyShortDescription(e = {}) { return this.callApi('setMyShortDescription', e); }
|
|
116
|
+
setChatMenuButton(e = {}) { return this.callApi('setChatMenuButton', e); }
|
|
117
|
+
|
|
118
|
+
/* ---- webhook ---- */
|
|
119
|
+
setWebhook(url, e = {}) { return this.callApi('setWebhook', { url, ...e }); }
|
|
120
|
+
deleteWebhook(e = {}) { return this.callApi('deleteWebhook', e); }
|
|
121
|
+
getWebhookInfo() { return this.callApi('getWebhookInfo'); }
|
|
122
|
+
|
|
123
|
+
/* ---- forum topic ---- */
|
|
124
|
+
createForumTopic(c, name, e = {}) { return this.callApi('createForumTopic', { chat_id: c, name, ...e }); }
|
|
125
|
+
closeForumTopic(c, tid) { return this.callApi('closeForumTopic', { chat_id: c, message_thread_id: tid }); }
|
|
126
|
+
|
|
127
|
+
/* ---- pembayaran & stars ---- */
|
|
128
|
+
sendInvoice(c, title, description, payload, currency, prices, e = {}) { return this.callApi('sendInvoice', { chat_id: c, title, description, payload, currency, prices, ...e }); }
|
|
129
|
+
refundStarPayment(user_id, telegram_payment_charge_id) { return this.callApi('refundStarPayment', { user_id, telegram_payment_charge_id }); }
|
|
130
|
+
getStarTransactions(e = {}) { return this.callApi('getStarTransactions', e); }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Api = ApiBase + Proxy: metode APA PUN di Bot API bisa dipanggil
|
|
135
|
+
* api.setWebhookCustom({ url }) → callApi('setWebhookCustom', {...})
|
|
136
|
+
* Implementasi: kalau nama ada di ApiBase, pakai definisi resminya; kalau
|
|
137
|
+
* tidak, fallback jadi satu-argumen-payload.
|
|
138
|
+
*/
|
|
139
|
+
function makeApi(token, transport) {
|
|
140
|
+
const base = new ApiBase(token, transport);
|
|
141
|
+
return new Proxy(base, {
|
|
142
|
+
get(target, prop, recv) {
|
|
143
|
+
const v = Reflect.get(target, prop, recv);
|
|
144
|
+
if (v !== undefined) return typeof v === 'function' ? v.bind(target) : v;
|
|
145
|
+
if (typeof prop === 'string' && /^[a-z][a-zA-Z0-9]*$/.test(prop)) {
|
|
146
|
+
return (payload = {}) => target.callApi(prop, payload);
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
},
|
|
150
|
+
set(target, prop, value) { Reflect.set(target, prop, value); return true; },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { makeApi, ApiBase };
|
package/lib/broadcast.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// lib/broadcast.js — kirim ke banyak chat dengan pacing aman rate-limit.
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* bot.broadcast(ids, pesan, opts)
|
|
8
|
+
* ids : array chat id
|
|
9
|
+
* pesan : string → { text: string }
|
|
10
|
+
* object → payload sendMessage/salinan
|
|
11
|
+
* fungsi → (chatId) => payload (personal per penerima)
|
|
12
|
+
* opts : { delay = 35 } ms antar kirim (≈28 pesan/detik, di bawah limit Telegram)
|
|
13
|
+
*
|
|
14
|
+
* Return: { terkirim, gagal, errors: [{ chatId, pesan }] }
|
|
15
|
+
*/
|
|
16
|
+
async function broadcast(api, ids, pesan, opts = {}) {
|
|
17
|
+
const delay = Number.isFinite(opts.delay) ? opts.delay : 35;
|
|
18
|
+
const failures = [];
|
|
19
|
+
let terkirim = 0;
|
|
20
|
+
|
|
21
|
+
for (const chatId of ids) {
|
|
22
|
+
let p = typeof pesan === 'function' ? await pesan(chatId) : pesan;
|
|
23
|
+
if (typeof p === 'string') p = { text: p };
|
|
24
|
+
try {
|
|
25
|
+
await api.callApi('sendMessage', { chat_id: chatId, ...p });
|
|
26
|
+
terkirim += 1;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
failures.push({ chatId, pesan: (err && (err.description || err.message)) || String(err) });
|
|
29
|
+
}
|
|
30
|
+
if (delay > 0) await sleep(delay);
|
|
31
|
+
}
|
|
32
|
+
return { terkirim, gagal: failures.length, errors: failures };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { broadcast };
|
package/lib/composer.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// lib/composer.js — mesin middleware & routing filter (recoded, konsep gaya grammY).
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { msgOf } = require('./context');
|
|
5
|
+
|
|
6
|
+
/* ---------- matcher filter ---------- */
|
|
7
|
+
const UPDATE_FIELDS = {
|
|
8
|
+
message: (u) => u.message, edited_message: (u) => u.edited_message,
|
|
9
|
+
channel_post: (u) => u.channel_post, edited_channel_post: (u) => u.edited_channel_post,
|
|
10
|
+
business_message: (u) => u.business_message, edited_business_message: (u) => u.edited_business_message,
|
|
11
|
+
callback_query: (u) => u.callback_query, inline_query: (u) => u.inline_query,
|
|
12
|
+
chat_join_request: (u) => u.chat_join_request, chat_member: (u) => u.chat_member,
|
|
13
|
+
my_chat_member: (u) => u.my_chat_member, message_reaction: (u) => u.message_reaction,
|
|
14
|
+
message_reaction_count: (u) => u.message_reaction_count,
|
|
15
|
+
business_connection: (u) => u.business_connection, poll: (u) => u.poll, poll_answer: (u) => u.poll_answer,
|
|
16
|
+
chosen_inline_result: (u) => u.chosen_inline_result, shipping_query: (u) => u.shipping_query,
|
|
17
|
+
pre_checkout_query: (u) => u.pre_checkout_query, purchased_paid_media: (u) => u.purchased_paid_media,
|
|
18
|
+
};
|
|
19
|
+
const MSG_PROPS = {
|
|
20
|
+
text: (m) => typeof m.text === 'string', caption: (m) => typeof m.caption === 'string',
|
|
21
|
+
photo: (m) => Array.isArray(m.photo), video: (m) => !!m.video, audio: (m) => !!m.audio,
|
|
22
|
+
voice: (m) => !!m.voice, video_note: (m) => !!m.video_note, document: (m) => !!m.document,
|
|
23
|
+
sticker: (m) => !!m.sticker, animation: (m) => !!m.animation, location: (m) => !!m.location,
|
|
24
|
+
contact: (m) => !!m.contact, poll: (m) => !!m.poll, venue: (m) => !!m.venue,
|
|
25
|
+
dice: (m) => !!m.dice, game: (m) => !!m.game, invoice: (m) => !!m.invoice,
|
|
26
|
+
new_chat_members: (m) => Array.isArray(m.new_chat_members), left_chat_member: (m) => !!m.left_chat_member,
|
|
27
|
+
successful_payment: (m) => !!m.successful_payment, web_app_data: (m) => !!m.web_app_data,
|
|
28
|
+
via_bot: (m) => !!m.via_bot, reply_to_message: (m) => !!m.reply_to_message, entities: (m) => Array.isArray(m.entities),
|
|
29
|
+
media: (m) => Array.isArray(m.photo) || !!m.video || !!m.document || !!m.audio,
|
|
30
|
+
data: () => false, // khusus callback_query ditangani di bawah
|
|
31
|
+
successful_payment: (m) => !!m.successful_payment,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** filter 'message:photo', ':text', 'callback_query': → fungsi (ctx) => boolean */
|
|
35
|
+
function compileFilter(str) {
|
|
36
|
+
// ':prop' → pesan (atau edited/channel/business) dengan properti tsb — gaya grammY
|
|
37
|
+
if (String(str).startsWith(':')) {
|
|
38
|
+
const prop = String(str).slice(1);
|
|
39
|
+
const test = MSG_PROPS[prop];
|
|
40
|
+
if (test) return (ctx) => { const m = msgOf(ctx.update); return !!m && test(m); };
|
|
41
|
+
return () => false;
|
|
42
|
+
}
|
|
43
|
+
const [head, prop] = String(str).split(':');
|
|
44
|
+
if (head === 'chat_type') return (ctx) => { const c = ctx.chat; return !!c && c.type === prop; };
|
|
45
|
+
if (head === 'callback_query' && prop === 'data') return (ctx) => !!(ctx.callback_query && typeof ctx.callback_query.data === 'string');
|
|
46
|
+
if (head && prop) {
|
|
47
|
+
const get = UPDATE_FIELDS[head];
|
|
48
|
+
const test = MSG_PROPS[prop];
|
|
49
|
+
if (get && test) return (ctx) => { const m = get(ctx.update); return !!m && test(m); };
|
|
50
|
+
if (get) return (ctx) => !!get(ctx.update);
|
|
51
|
+
if (test) return (ctx) => { const m = msgOf(ctx.update); return !!m && test(m); };
|
|
52
|
+
}
|
|
53
|
+
if (UPDATE_FIELDS[head]) return (ctx) => !!UPDATE_FIELDS[head](ctx.update);
|
|
54
|
+
if (MSG_PROPS[head]) return (ctx) => { const m = msgOf(ctx.update); return !!m && MSG_PROPS[head](m); };
|
|
55
|
+
return () => false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* ---------- composer ---------- */
|
|
59
|
+
class Composer {
|
|
60
|
+
constructor(...mw) { this.stack = []; for (const m of mw) this.use(m); }
|
|
61
|
+
|
|
62
|
+
use(...mw) {
|
|
63
|
+
for (const m of mw) {
|
|
64
|
+
const fn = m instanceof Composer ? m.middleware() : m;
|
|
65
|
+
this.stack.push({ match: () => true, fns: [fn] });
|
|
66
|
+
}
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
on(filter, ...mw) { [].concat(filter).forEach((f) => this.stack.push({ match: compileFilter(f), fns: mw })); return this; }
|
|
70
|
+
hears(re, ...mw) {
|
|
71
|
+
[].concat(re).forEach((r) => this.stack.push({
|
|
72
|
+
match: (ctx) => { const m = msgOf(ctx.update); const t = m && (m.text || m.caption || ''); return !!t && r.test(t); },
|
|
73
|
+
fns: mw,
|
|
74
|
+
}));
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
command(names, ...mw) {
|
|
78
|
+
const set = [].concat(names).map((n) => String(n).toLowerCase());
|
|
79
|
+
this.stack.push({
|
|
80
|
+
match: (ctx) => {
|
|
81
|
+
const m = msgOf(ctx.update);
|
|
82
|
+
if (!m || typeof m.text !== 'string') return false;
|
|
83
|
+
const mobj = /^\/([A-Za-z0-9_]+)(?:@([A-Za-z0-9_]+))?(\s|$)/.exec(m.text);
|
|
84
|
+
if (!mobj) return false;
|
|
85
|
+
if (!set.includes(mobj[1].toLowerCase())) return false;
|
|
86
|
+
if (mobj[2] && ctx.me && ctx.me.username && mobj[2].toLowerCase() !== ctx.me.username.toLowerCase()) return false;
|
|
87
|
+
ctx.match = m.text.slice(mobj[0].length); // argumen setelah perintah
|
|
88
|
+
return true;
|
|
89
|
+
},
|
|
90
|
+
fns: mw,
|
|
91
|
+
});
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
callbackQuery(trigs, ...mw) {
|
|
95
|
+
const list = [].concat(trigs);
|
|
96
|
+
this.stack.push({
|
|
97
|
+
match: (ctx) => {
|
|
98
|
+
const q = ctx.callback_query || ctx.update.callback_query;
|
|
99
|
+
if (!q || typeof q.data !== 'string') return false;
|
|
100
|
+
return list.some((t) => t instanceof RegExp ? t.test(q.data) : String(t) === q.data);
|
|
101
|
+
},
|
|
102
|
+
fns: mw,
|
|
103
|
+
});
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Jalankan mw hanya jika pred true. */
|
|
108
|
+
filter(pred, ...mw) { const c = new Composer(...mw); this.use(async (ctx, next) => (await pred(ctx) ? c.run(ctx, next) : next())); return c; }
|
|
109
|
+
/** Lewati mw jika pred true. */
|
|
110
|
+
drop(pred, ...mw) { const c = new Composer(...mw); this.use(async (ctx, next) => (await pred(ctx) ? undefined : c.run(ctx, next))); return c; }
|
|
111
|
+
/** Cabang dua: pred ? A : B. */
|
|
112
|
+
branch(pred, a, b) { const ca = new Composer(a), cb = new Composer(b); this.use(async (ctx, next) => (await pred(ctx) ? ca.run(ctx, next) : cb.run(ctx, next))); return this; }
|
|
113
|
+
/** Petakan ctx→key ke composer berbeda: route('type',{private:c, group:c2}) */
|
|
114
|
+
route(router, by) {
|
|
115
|
+
const map = new Map(Object.entries(by).map(([k, v]) => [k, v instanceof Composer ? v : new Composer(v)]));
|
|
116
|
+
this.use(async (ctx, next) => { const k = typeof router === 'function' ? await router(ctx) : ctx[router]; const c = map.get(k); return c ? c.run(ctx, next) : next(); });
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
/** Bangun mw malas per-update: lazy((ctx)=>ctx.from?.is_bot?botmw:usermw) */
|
|
120
|
+
lazy(factory) { this.use(async (ctx, next) => { const mw = await factory(ctx); const c = new Composer(...(Array.isArray(mw) ? mw : [mw]).filter(Boolean)); return c.run(ctx, next); }); return this; }
|
|
121
|
+
/** Jalankan mw DI LATAR BELAKANG (tidak menahan next) — cocok untuk tugas lambat. */
|
|
122
|
+
fork(...mw) { const c = new Composer(...mw); this.use((ctx, next) => { c.run(ctx).then(()=>{},(e)=>ctx.api && console.error('[telebibz:fork]', e.message)); return next(); }); return this; }
|
|
123
|
+
|
|
124
|
+
/** Lindungi subpohon middleware dengan handler error — pola batas rambu grammY. */
|
|
125
|
+
errorBoundary(handler, ...mw) {
|
|
126
|
+
const sub = new Composer(...mw);
|
|
127
|
+
this.use(async (ctx, next) => {
|
|
128
|
+
let called = false;
|
|
129
|
+
const cont = () => ((called = true), Promise.resolve());
|
|
130
|
+
try { await sub.run(ctx, cont); }
|
|
131
|
+
catch (err) { called = false; await handler(new BotError(err, ctx), cont); }
|
|
132
|
+
if (called) return next();
|
|
133
|
+
});
|
|
134
|
+
return sub;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Jalankan rantai middleware terhadap satu ctx. */
|
|
138
|
+
async run(ctx, next) {
|
|
139
|
+
const entries = this.stack;
|
|
140
|
+
let idx = -1;
|
|
141
|
+
const dispatch = async (i) => {
|
|
142
|
+
if (i <= idx) throw new Error('next() dipanggil dua kali');
|
|
143
|
+
idx = i;
|
|
144
|
+
if (i >= entries.length) return next ? next() : undefined;
|
|
145
|
+
const e = entries[i];
|
|
146
|
+
if (!e.match(ctx)) return dispatch(i + 1);
|
|
147
|
+
let continued = false;
|
|
148
|
+
for (let j = 0; j < e.fns.length; j++) {
|
|
149
|
+
const isLast = j === e.fns.length - 1;
|
|
150
|
+
let stepNext = false;
|
|
151
|
+
const r = await e.fns[j](ctx, async () => {
|
|
152
|
+
stepNext = true;
|
|
153
|
+
if (isLast) { continued = true; return dispatch(i + 1); }
|
|
154
|
+
});
|
|
155
|
+
if (!stepNext) return r; // handler berhenti di sini (tak lanjut middleware)
|
|
156
|
+
}
|
|
157
|
+
if (!continued) return undefined;
|
|
158
|
+
};
|
|
159
|
+
return dispatch(0);
|
|
160
|
+
}
|
|
161
|
+
middleware() { return (ctx, next) => this.run(ctx, next); }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Error yang dibungkus konteksnya, gaya BotError grammY. */
|
|
165
|
+
class BotError extends Error {
|
|
166
|
+
constructor(err, ctx) {
|
|
167
|
+
super(err && err.message ? err.message : String(err));
|
|
168
|
+
this.name = 'BotError';
|
|
169
|
+
this.error = err;
|
|
170
|
+
this.ctx = ctx;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = { Composer, BotError, compileFilter };
|