@yaebal/core 0.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/LICENSE +21 -0
- package/README.md +39 -0
- package/lib/api.d.ts +66 -0
- package/lib/api.d.ts.map +1 -0
- package/lib/api.js +176 -0
- package/lib/api.js.map +1 -0
- package/lib/bot.d.ts +58 -0
- package/lib/bot.d.ts.map +1 -0
- package/lib/bot.js +125 -0
- package/lib/bot.js.map +1 -0
- package/lib/composer.d.ts +97 -0
- package/lib/composer.d.ts.map +1 -0
- package/lib/composer.js +178 -0
- package/lib/composer.js.map +1 -0
- package/lib/context.d.ts +40 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +78 -0
- package/lib/context.js.map +1 -0
- package/lib/core.test.d.ts +2 -0
- package/lib/core.test.d.ts.map +1 -0
- package/lib/core.test.js +87 -0
- package/lib/core.test.js.map +1 -0
- package/lib/filter.test.d.ts +2 -0
- package/lib/filter.test.d.ts.map +1 -0
- package/lib/filter.test.js +158 -0
- package/lib/filter.test.js.map +1 -0
- package/lib/format.d.ts +31 -0
- package/lib/format.d.ts.map +1 -0
- package/lib/format.js +57 -0
- package/lib/format.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +14 -0
- package/lib/index.js.map +1 -0
- package/lib/media.d.ts +27 -0
- package/lib/media.d.ts.map +1 -0
- package/lib/media.js +20 -0
- package/lib/media.js.map +1 -0
- package/lib/telegram-types.d.ts +21 -0
- package/lib/telegram-types.d.ts.map +1 -0
- package/lib/telegram-types.js +2 -0
- package/lib/telegram-types.js.map +1 -0
- package/lib/webhook.d.ts +18 -0
- package/lib/webhook.d.ts.map +1 -0
- package/lib/webhook.js +82 -0
- package/lib/webhook.js.map +1 -0
- package/package.json +49 -0
- package/src/api.ts +276 -0
- package/src/bot.ts +168 -0
- package/src/composer.ts +280 -0
- package/src/context.ts +109 -0
- package/src/core.test.ts +108 -0
- package/src/filter.test.ts +202 -0
- package/src/format.ts +80 -0
- package/src/index.ts +53 -0
- package/src/media.ts +29 -0
- package/src/telegram-types.ts +25 -0
- package/src/webhook.ts +117 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 neverlane
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @yaebal/core (the framework)
|
|
2
|
+
|
|
3
|
+
the yaebal core is a type-safe, chainable telegram bot api framework. `Bot extends
|
|
4
|
+
Composer` — a koa-style middleware engine where the context type *accumulates* through
|
|
5
|
+
the chain, so `derive` / `decorate` / `install` each return a bot with an enriched
|
|
6
|
+
context and handlers see plugin-added properties with no casting.
|
|
7
|
+
|
|
8
|
+
## installing
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
pnpm add @yaebal/core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## quick start
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Bot, bold, format } from "@yaebal/core";
|
|
18
|
+
|
|
19
|
+
const bot = new Bot(process.env.BOT_TOKEN)
|
|
20
|
+
.command("start", (ctx) => ctx.send("hi 🐴"))
|
|
21
|
+
.on("message:text", (ctx) => ctx.reply(format`you said: ${bold(ctx.text)}`));
|
|
22
|
+
|
|
23
|
+
await bot.start(); // long polling — or bot.handleUpdate(update) behind a webhook
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## what's inside
|
|
27
|
+
|
|
28
|
+
- chainable `Composer` — `use` / `on` / `command` / `hears` / `callbackQuery` / `guard` / `derive` / `decorate` / `install`, all type-accumulating.
|
|
29
|
+
- filter queries — `on("message:text")`, `on("callback_query:data")`, narrowing the context type.
|
|
30
|
+
- typed plugins — `install(plugin)` checks a plugin's required context at compile time.
|
|
31
|
+
- `api` — typed methods plus a `call(method, params)` passthrough, with `before` / `after` / `onError` hooks.
|
|
32
|
+
- media — `media.path` / `url` / `buffer` / `fileId` and `ctx.sendPhoto` / `ctx.sendDocument`.
|
|
33
|
+
- both transports — long polling (`start`) and webhooks (`handleUpdate`).
|
|
34
|
+
|
|
35
|
+
## developing (in this monorepo)
|
|
36
|
+
|
|
37
|
+
- to type-check, run `pnpm typecheck`.
|
|
38
|
+
- to build to `lib/`, run `pnpm build`.
|
|
39
|
+
- to run the tests, run `pnpm test`.
|
package/lib/api.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Message, Update, User } from "./telegram-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* resolve a local `media.path()` into bytes. runtime-specific (node:fs, Bun.file,
|
|
4
|
+
* Deno.readFile), so it's injected rather than imported — that keeps `@yaebal/core`
|
|
5
|
+
* free of any `node:` import and loadable on edge/web. the `yaebal` package wires an
|
|
6
|
+
* auto-detecting default; absent ⇒ `media.path()` throws (e.g. on Cloudflare Workers).
|
|
7
|
+
*/
|
|
8
|
+
export type FileReader = (path: string) => Promise<Uint8Array>;
|
|
9
|
+
/** thrown when telegram replies with `ok: false`. */
|
|
10
|
+
export declare class TelegramError extends Error {
|
|
11
|
+
readonly method: string;
|
|
12
|
+
readonly code: number;
|
|
13
|
+
constructor(method: string, code: number, description: string);
|
|
14
|
+
}
|
|
15
|
+
/** inspect/rewrite params before a request. return new params to replace them. */
|
|
16
|
+
export type BeforeHook = (method: string, params: Record<string, unknown> | undefined) => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
|
|
17
|
+
/** inspect/rewrite the result after a successful request. return a value to replace it. */
|
|
18
|
+
export type AfterHook = (method: string, result: unknown) => unknown | Promise<unknown>;
|
|
19
|
+
/** what an error hook can ask the client to do. */
|
|
20
|
+
export interface ErrorAction {
|
|
21
|
+
/** re-run the same call. */
|
|
22
|
+
retry?: boolean;
|
|
23
|
+
/** wait this many ms before retrying. */
|
|
24
|
+
delayMs?: number;
|
|
25
|
+
}
|
|
26
|
+
/** runs when a request throws. `attempt` is the (1-based) attempt that just failed. */
|
|
27
|
+
export type ErrorHook = (method: string, error: unknown, attempt: number) => ErrorAction | undefined | Promise<ErrorAction | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* the API client. known methods are typed; everything else goes through `call`
|
|
30
|
+
* (the puregram passthrough — a new Bot API method works before its types ship).
|
|
31
|
+
* `before` / `after` / `onError` are the extension points plugins hang off of.
|
|
32
|
+
*/
|
|
33
|
+
export interface Api {
|
|
34
|
+
call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
35
|
+
getMe(): Promise<User>;
|
|
36
|
+
getUpdates(params?: Record<string, unknown>): Promise<Update[]>;
|
|
37
|
+
sendMessage(params: Record<string, unknown>): Promise<Message>;
|
|
38
|
+
answerCallbackQuery(params: Record<string, unknown>): Promise<boolean>;
|
|
39
|
+
/** build the download URL for a `file_path` from `getFile`. contains the bot token — don't log it. */
|
|
40
|
+
fileUrl(filePath: string): string;
|
|
41
|
+
/** register a hook that runs before every request; may rewrite params. */
|
|
42
|
+
before(hook: BeforeHook): Api;
|
|
43
|
+
/** register a hook that runs after every successful request; may rewrite the result. */
|
|
44
|
+
after(hook: AfterHook): Api;
|
|
45
|
+
/** register a hook that runs when a request throws; may request a retry. */
|
|
46
|
+
onError(hook: ErrorHook): Api;
|
|
47
|
+
}
|
|
48
|
+
export interface ApiOptions {
|
|
49
|
+
apiRoot?: string;
|
|
50
|
+
/** resolve `media.path()` to bytes. injected per runtime; absent ⇒ path media throws (e.g. edge). */
|
|
51
|
+
readFile?: FileReader;
|
|
52
|
+
}
|
|
53
|
+
interface EncodedRequest {
|
|
54
|
+
body: string | FormData | undefined;
|
|
55
|
+
contentType?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* encode request params. Plain params (and `url`/`fileId` media) go as JSON;
|
|
59
|
+
* if any `path`/`buffer` media is present the whole request becomes multipart,
|
|
60
|
+
* with each upload attached via `attach://`. exported for testing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function encodeRequest(params: Record<string, unknown> | undefined, readFile?: FileReader): Promise<EncodedRequest>;
|
|
63
|
+
/** builds a callable API client with before/after/error hooks. */
|
|
64
|
+
export declare function createApi(token: string, options?: ApiOptions): Api;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=api.d.ts.map
|
package/lib/api.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/lib/api.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { isMediaSource } from "./media.js";
|
|
2
|
+
/** thrown when telegram replies with `ok: false`. */
|
|
3
|
+
export class TelegramError extends Error {
|
|
4
|
+
method;
|
|
5
|
+
code;
|
|
6
|
+
constructor(method, code, description) {
|
|
7
|
+
super(`[${method}] ${code}: ${description}`);
|
|
8
|
+
this.name = "TelegramError";
|
|
9
|
+
this.method = method;
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function containsMedia(v) {
|
|
14
|
+
if (isMediaSource(v))
|
|
15
|
+
return true;
|
|
16
|
+
if (Array.isArray(v))
|
|
17
|
+
return v.some(containsMedia);
|
|
18
|
+
if (v !== null && typeof v === "object")
|
|
19
|
+
return Object.values(v).some(containsMedia);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
/** basename without node:path — keeps core free of node: imports. */
|
|
23
|
+
const baseName = (p) => p.split(/[\\/]/).pop() || "file";
|
|
24
|
+
const toArrayBuffer = (bytes) => {
|
|
25
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
26
|
+
copy.set(bytes);
|
|
27
|
+
return copy.buffer;
|
|
28
|
+
};
|
|
29
|
+
async function mediaToBlob(m, readFile) {
|
|
30
|
+
if (m.kind === "path") {
|
|
31
|
+
if (!readFile)
|
|
32
|
+
throw new Error("media.path() needs a filesystem — use the `yaebal` package (auto-detects node/bun/deno), " +
|
|
33
|
+
"pass `readFile` to the Bot, or send media.buffer()/url() instead (e.g. on edge).");
|
|
34
|
+
const bytes = await readFile(m.path);
|
|
35
|
+
return { blob: new Blob([toArrayBuffer(bytes)]), filename: baseName(m.path) };
|
|
36
|
+
}
|
|
37
|
+
if (m.kind === "buffer") {
|
|
38
|
+
return { blob: new Blob([toArrayBuffer(m.buffer)]), filename: m.filename ?? "file" };
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`mediaToBlob: ${m.kind} is not an uploadable source`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* encode request params. Plain params (and `url`/`fileId` media) go as JSON;
|
|
44
|
+
* if any `path`/`buffer` media is present the whole request becomes multipart,
|
|
45
|
+
* with each upload attached via `attach://`. exported for testing.
|
|
46
|
+
*/
|
|
47
|
+
export async function encodeRequest(params, readFile) {
|
|
48
|
+
if (!params)
|
|
49
|
+
return { body: undefined, contentType: "application/json" };
|
|
50
|
+
// ponytail: media is only handled at the top level. media nested inside arrays
|
|
51
|
+
// or objects (e.g. sendMediaGroup's `media[]`) would serialize to garbage, so
|
|
52
|
+
// fail loud until that method is actually supported.
|
|
53
|
+
if (Object.values(params).some((v) => !isMediaSource(v) && containsMedia(v))) {
|
|
54
|
+
throw new Error("encodeRequest: nested MediaSource (e.g. inside sendMediaGroup) is not supported yet — pass media as a top-level param");
|
|
55
|
+
}
|
|
56
|
+
const needsUpload = Object.values(params).some((v) => isMediaSource(v) && (v.kind === "path" || v.kind === "buffer"));
|
|
57
|
+
if (!needsUpload) {
|
|
58
|
+
const inlined = {};
|
|
59
|
+
for (const [k, v] of Object.entries(params)) {
|
|
60
|
+
inlined[k] = isMediaSource(v)
|
|
61
|
+
? v.kind === "url"
|
|
62
|
+
? v.url
|
|
63
|
+
: v.fileId
|
|
64
|
+
: v;
|
|
65
|
+
}
|
|
66
|
+
return { body: JSON.stringify(inlined), contentType: "application/json" };
|
|
67
|
+
}
|
|
68
|
+
const form = new FormData();
|
|
69
|
+
let n = 0;
|
|
70
|
+
for (const [k, v] of Object.entries(params)) {
|
|
71
|
+
if (v === undefined || v === null)
|
|
72
|
+
continue;
|
|
73
|
+
if (isMediaSource(v)) {
|
|
74
|
+
if (v.kind === "fileId")
|
|
75
|
+
form.set(k, v.fileId);
|
|
76
|
+
else if (v.kind === "url")
|
|
77
|
+
form.set(k, v.url);
|
|
78
|
+
else {
|
|
79
|
+
const field = `_file${n++}`;
|
|
80
|
+
const { blob, filename } = await mediaToBlob(v, readFile);
|
|
81
|
+
form.set(field, blob, filename);
|
|
82
|
+
form.set(k, `attach://${field}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (typeof v === "string") {
|
|
86
|
+
form.set(k, v);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
form.set(k, JSON.stringify(v));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { body: form };
|
|
93
|
+
}
|
|
94
|
+
/** builds a callable API client with before/after/error hooks. */
|
|
95
|
+
export function createApi(token, options = {}) {
|
|
96
|
+
const apiRoot = options.apiRoot ?? "https://api.telegram.org";
|
|
97
|
+
const beforeHooks = [];
|
|
98
|
+
const afterHooks = [];
|
|
99
|
+
const errorHooks = [];
|
|
100
|
+
const rawCall = async (method, params) => {
|
|
101
|
+
const { body, contentType } = await encodeRequest(params, options.readFile);
|
|
102
|
+
const res = await fetch(`${apiRoot}/bot${token}/${method}`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
// for multipart, let fetch set the content-type (with its boundary).
|
|
105
|
+
headers: contentType ? { "content-type": contentType } : undefined,
|
|
106
|
+
body,
|
|
107
|
+
});
|
|
108
|
+
const data = (await res.json());
|
|
109
|
+
if (!data.ok)
|
|
110
|
+
throw new TelegramError(method, data.error_code, data.description);
|
|
111
|
+
return data.result;
|
|
112
|
+
};
|
|
113
|
+
const call = async (method, params) => {
|
|
114
|
+
let p = params;
|
|
115
|
+
for (const hook of beforeHooks) {
|
|
116
|
+
const next = await hook(method, p);
|
|
117
|
+
if (next !== undefined)
|
|
118
|
+
p = next;
|
|
119
|
+
}
|
|
120
|
+
// ponytail: retry loop is bounded by the error hooks themselves (e.g. again caps
|
|
121
|
+
// attempts). with no hook requesting a retry it throws on the first failure.
|
|
122
|
+
for (let attempt = 1;; attempt++) {
|
|
123
|
+
try {
|
|
124
|
+
let result = await rawCall(method, p);
|
|
125
|
+
for (const hook of afterHooks) {
|
|
126
|
+
const next = await hook(method, result);
|
|
127
|
+
if (next !== undefined)
|
|
128
|
+
result = next;
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
let retry;
|
|
134
|
+
for (const hook of errorHooks) {
|
|
135
|
+
const action = await hook(method, error, attempt);
|
|
136
|
+
if (action?.retry) {
|
|
137
|
+
retry = action;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!retry)
|
|
142
|
+
throw error;
|
|
143
|
+
if (retry.delayMs)
|
|
144
|
+
await new Promise((r) => setTimeout(r, retry.delayMs));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const registrar = {
|
|
149
|
+
call,
|
|
150
|
+
fileUrl: (filePath) => `${apiRoot}/file/bot${token}/${filePath}`,
|
|
151
|
+
before(hook) {
|
|
152
|
+
beforeHooks.push(hook);
|
|
153
|
+
return api;
|
|
154
|
+
},
|
|
155
|
+
after(hook) {
|
|
156
|
+
afterHooks.push(hook);
|
|
157
|
+
return api;
|
|
158
|
+
},
|
|
159
|
+
onError(hook) {
|
|
160
|
+
errorHooks.push(hook);
|
|
161
|
+
return api;
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const api = new Proxy(registrar, {
|
|
165
|
+
get(obj, prop) {
|
|
166
|
+
if (prop in obj)
|
|
167
|
+
return obj[prop];
|
|
168
|
+
// lazily materialise `api.<method>(params)` → call(method, params).
|
|
169
|
+
const method = (params) => call(prop, params);
|
|
170
|
+
obj[prop] = method;
|
|
171
|
+
return method;
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
return api;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=api.js.map
|
package/lib/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,aAAa,EAAE,MAAM,YAAY,CAAC;AAW7D,qDAAqD;AACrD,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC9B,MAAM,CAAS;IACf,IAAI,CAAS;IAEtB,YAAY,MAAc,EAAE,IAAY,EAAE,WAAmB;QAC5D,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI,KAAK,WAAW,EAAE,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AA0DD,SAAS,aAAa,CAAC,CAAU;IAChC,IAAI,aAAa,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAErF,OAAO,KAAK,CAAC;AACd,CAAC;AAED,qEAAqE;AACrE,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC;AACzE,MAAM,aAAa,GAAG,CAAC,KAAiB,EAAe,EAAE;IACxD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhB,OAAO,IAAI,CAAC,MAAM,CAAC;AACpB,CAAC,CAAC;AAEF,KAAK,UAAU,WAAW,CACzB,CAAc,EACd,QAAqB;IAErB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ;YACZ,MAAM,IAAI,KAAK,CACd,2FAA2F;gBAC3F,kFAAkF,CAClF,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAC/E,CAAC;IAED,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,MAAM,EAAE,CAAC;IACtF,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,8BAA8B,CAAC,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,MAA2C,EAC3C,QAAqB;IAErB,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAEzE,+EAA+E;IAC/E,8EAA8E;IAC9E,qDAAqD;IACrD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,KAAK,CACd,uHAAuH,CACvH,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAC7C,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CACrE,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,OAAO,GAA4B,EAAE,CAAC;QAE5C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;gBAC5B,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK;oBACjB,CAAC,CAAC,CAAC,CAAC,GAAG;oBACP,CAAC,CAAE,CAAwB,CAAC,MAAM;gBACnC,CAAC,CAAC,CAAC,CAAC;QACN,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;IAEV,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,SAAS;QAE5C,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;gBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;iBAC1C,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;iBACzC,CAAC;gBACL,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAC5B,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAE1D,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,KAAK,EAAE,CAAC,CAAC;YAClC,CAAC;QACF,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChB,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;IACF,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,UAAsB,EAAE;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,0BAA0B,CAAC;IAC9D,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,MAAM,OAAO,GAAG,KAAK,EAAK,MAAc,EAAE,MAAgC,EAAc,EAAE;QACzF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,OAAO,KAAK,IAAI,MAAM,EAAE,EAAE;YAC3D,MAAM,EAAE,MAAM;YACd,qEAAqE;YACrE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;YAClE,IAAI;SACJ,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAEjF,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,EACjB,MAAc,EACd,MAAgC,EACnB,EAAE;QACf,IAAI,CAAC,GAAG,MAAM,CAAC;QACf,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YACnC,IAAI,IAAI,KAAK,SAAS;gBAAE,CAAC,GAAG,IAAI,CAAC;QAClC,CAAC;QAED,iFAAiF;QACjF,6EAA6E;QAC7E,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YACnC,IAAI,CAAC;gBACJ,IAAI,MAAM,GAAG,MAAM,OAAO,CAAI,MAAM,EAAE,CAAC,CAAC,CAAC;gBAEzC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;oBAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACxC,IAAI,IAAI,KAAK,SAAS;wBAAE,MAAM,GAAG,IAAkB,CAAC;gBACrD,CAAC;gBAED,OAAO,MAAM,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,KAA8B,CAAC;gBAEnC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;oBAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;oBAClD,IAAI,MAAM,EAAE,KAAK,EAAE,CAAC;wBACnB,KAAK,GAAG,MAAM,CAAC;wBACf,MAAM;oBACP,CAAC;gBACF,CAAC;gBAED,IAAI,CAAC,KAAK;oBAAE,MAAM,KAAK,CAAC;gBACxB,IAAI,KAAK,CAAC,OAAO;oBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAC3E,CAAC;QACF,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,SAAS,GAA4B;QAC1C,IAAI;QACJ,OAAO,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,GAAG,OAAO,YAAY,KAAK,IAAI,QAAQ,EAAE;QACxE,MAAM,CAAC,IAAgB;YACtB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvB,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,KAAK,CAAC,IAAe;YACpB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,IAAe;YACtB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,GAAG,CAAC;QACZ,CAAC;KACD,CAAC;IAEF,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE;QAChC,GAAG,CAAC,GAAG,EAAE,IAAY;YACpB,IAAI,IAAI,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;YAElC,oEAAoE;YACpE,MAAM,MAAM,GAAG,CAAC,MAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAExE,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;YACnB,OAAO,MAAM,CAAC;QACf,CAAC;KACD,CAAmB,CAAC;IAErB,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
package/lib/bot.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Api, type FileReader } from "./api.js";
|
|
2
|
+
import { Composer } from "./composer.js";
|
|
3
|
+
import { Context } from "./context.js";
|
|
4
|
+
import type { Update, UpdateName, User } from "./telegram-types.js";
|
|
5
|
+
export interface BotOptions {
|
|
6
|
+
apiRoot?: string;
|
|
7
|
+
/**
|
|
8
|
+
* resolve `media.path()` into bytes. injected per runtime so core stays free of any
|
|
9
|
+
* `node:` import. the `yaebal` package wires an auto-detecting default; on bare core
|
|
10
|
+
* (or edge) leave it unset and `media.path()` throws — send `media.buffer()`/`url()`.
|
|
11
|
+
*/
|
|
12
|
+
readFile?: FileReader;
|
|
13
|
+
/** update types to request; `undefined` = telegram default. */
|
|
14
|
+
allowedUpdates?: UpdateName[];
|
|
15
|
+
/**
|
|
16
|
+
* build the context for each update. defaults to the base {@link Context}.
|
|
17
|
+
* gigher-level packages (e.g. the `yaebal` meta-package) inject a factory here
|
|
18
|
+
* to produce richer per-update contexts with auto-generated shortcut methods.
|
|
19
|
+
*/
|
|
20
|
+
contextFactory?: (api: Api, update: Update, updateType: UpdateName) => Context;
|
|
21
|
+
}
|
|
22
|
+
type StartHandler = (info: User) => unknown | Promise<unknown>;
|
|
23
|
+
type ErrorHandler = (error: unknown, ctx: Context) => unknown | Promise<unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* the bot. extends {@link Composer}, so the whole chainable, type-accumulating
|
|
26
|
+
* surface is available — and `derive` / `decorate` / `extend` keep returning a
|
|
27
|
+
* `Bot` (not a bare `Composer`) so lifecycle methods stay reachable down the chain.
|
|
28
|
+
*/
|
|
29
|
+
export declare class Bot<C extends Context = Context> extends Composer<C> {
|
|
30
|
+
#private;
|
|
31
|
+
readonly api: Api;
|
|
32
|
+
constructor(token: string, options?: BotOptions);
|
|
33
|
+
/** bot account info, available after `start()`. */
|
|
34
|
+
get info(): User | undefined;
|
|
35
|
+
derive<D extends object>(fn: (ctx: C) => D | Promise<D>): Bot<C & D>;
|
|
36
|
+
derive<D extends object>(updates: UpdateName | UpdateName[], fn: (ctx: C) => D | Promise<D>): Bot<C & Partial<D>>;
|
|
37
|
+
decorate<D extends object>(value: D): Bot<C & D>;
|
|
38
|
+
extend<C2 extends Context>(other: Composer<C2>): Bot<C & C2>;
|
|
39
|
+
install<Add extends object>(plugin: (composer: Composer<C>) => Composer<C & Add>): Bot<C & Add>;
|
|
40
|
+
/** register a callback fired once the bot has started. */
|
|
41
|
+
onStart(handler: StartHandler): this;
|
|
42
|
+
/** replace the default error handler. */
|
|
43
|
+
onError(handler: ErrorHandler): this;
|
|
44
|
+
/**
|
|
45
|
+
* run the middleware chain for a single update. this is the webhook entry
|
|
46
|
+
* point — call it from your HTTP handler. errors go to the error handler.
|
|
47
|
+
*
|
|
48
|
+
* the chain is realized (and frozen) on the first call, so register all
|
|
49
|
+
* middleware/plugins before the first `handleUpdate` / `start`.
|
|
50
|
+
*/
|
|
51
|
+
handleUpdate(update: Update): Promise<void>;
|
|
52
|
+
/** start long polling. resolves only when `stop()` is called. */
|
|
53
|
+
start(): Promise<void>;
|
|
54
|
+
/** stop the polling loop. */
|
|
55
|
+
stop(): void;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=bot.d.ts.map
|
package/lib/bot.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,UAAU,EAAa,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAmB,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEpE,MAAM,WAAW,UAAU;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,UAAU,EAAE,CAAC;IAC9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC;CAC/E;AAED,KAAK,YAAY,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAC/D,KAAK,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAWjF;;;;GAIG;AACH,qBAAa,GAAG,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;;IAChE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;gBAWN,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe;IAQnD,mDAAmD;IACnD,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAEQ,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACpE,MAAM,CAAC,CAAC,SAAS,MAAM,EAC/B,OAAO,EAAE,UAAU,GAAG,UAAU,EAAE,EAClC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC5B,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAQb,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAKhD,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAK5D,OAAO,CAAC,GAAG,SAAS,MAAM,EAClC,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC,GAClD,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IAKf,0DAA0D;IAC1D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAKpC,yCAAyC;IACzC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAKpC;;;;;;OAMG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAejD,iEAAiE;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC5B,6BAA6B;IAC7B,IAAI,IAAI,IAAI;CAGZ"}
|
package/lib/bot.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createApi } from "./api.js";
|
|
2
|
+
import { Composer } from "./composer.js";
|
|
3
|
+
import { Context } from "./context.js";
|
|
4
|
+
function detectUpdateType(update) {
|
|
5
|
+
for (const key of Object.keys(update)) {
|
|
6
|
+
if (key === "update_id")
|
|
7
|
+
continue;
|
|
8
|
+
return key;
|
|
9
|
+
}
|
|
10
|
+
return "message";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* the bot. extends {@link Composer}, so the whole chainable, type-accumulating
|
|
14
|
+
* surface is available — and `derive` / `decorate` / `extend` keep returning a
|
|
15
|
+
* `Bot` (not a bare `Composer`) so lifecycle methods stay reachable down the chain.
|
|
16
|
+
*/
|
|
17
|
+
export class Bot extends Composer {
|
|
18
|
+
api;
|
|
19
|
+
#running = false;
|
|
20
|
+
#offset = 0;
|
|
21
|
+
#info;
|
|
22
|
+
#options;
|
|
23
|
+
#startHandlers = [];
|
|
24
|
+
#errorHandler = (error) => {
|
|
25
|
+
console.error("[yaebal] unhandled error in middleware:", error);
|
|
26
|
+
};
|
|
27
|
+
#handle;
|
|
28
|
+
constructor(token, options = {}) {
|
|
29
|
+
super();
|
|
30
|
+
if (!token)
|
|
31
|
+
throw new Error("Bot(token): token is required");
|
|
32
|
+
this.#options = options;
|
|
33
|
+
this.api = createApi(token, { apiRoot: options.apiRoot, readFile: options.readFile });
|
|
34
|
+
}
|
|
35
|
+
/** bot account info, available after `start()`. */
|
|
36
|
+
get info() {
|
|
37
|
+
return this.#info;
|
|
38
|
+
}
|
|
39
|
+
// biome-ignore lint/suspicious/noExplicitAny: overload implementation
|
|
40
|
+
derive(a, b) {
|
|
41
|
+
// biome-ignore lint/suspicious/noExplicitAny: forwarding to the overloaded base
|
|
42
|
+
super.derive(a, b);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
decorate(value) {
|
|
46
|
+
super.decorate(value);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
extend(other) {
|
|
50
|
+
super.extend(other);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
install(plugin) {
|
|
54
|
+
plugin(this);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/** register a callback fired once the bot has started. */
|
|
58
|
+
onStart(handler) {
|
|
59
|
+
this.#startHandlers.push(handler);
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
/** replace the default error handler. */
|
|
63
|
+
onError(handler) {
|
|
64
|
+
this.#errorHandler = handler;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* run the middleware chain for a single update. this is the webhook entry
|
|
69
|
+
* point — call it from your HTTP handler. errors go to the error handler.
|
|
70
|
+
*
|
|
71
|
+
* the chain is realized (and frozen) on the first call, so register all
|
|
72
|
+
* middleware/plugins before the first `handleUpdate` / `start`.
|
|
73
|
+
*/
|
|
74
|
+
async handleUpdate(update) {
|
|
75
|
+
if (!this.#handle)
|
|
76
|
+
this.#handle = this.toMiddleware();
|
|
77
|
+
const updateType = detectUpdateType(update);
|
|
78
|
+
const ctx = this.#options.contextFactory
|
|
79
|
+
? this.#options.contextFactory(this.api, update, updateType)
|
|
80
|
+
: new Context({ api: this.api, update, updateType });
|
|
81
|
+
try {
|
|
82
|
+
await this.#handle(ctx, async () => { });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
await this.#errorHandler(error, ctx);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** start long polling. resolves only when `stop()` is called. */
|
|
89
|
+
async start() {
|
|
90
|
+
if (this.#running)
|
|
91
|
+
return;
|
|
92
|
+
this.#running = true;
|
|
93
|
+
this.#info = await this.api.getMe();
|
|
94
|
+
for (const handler of this.#startHandlers)
|
|
95
|
+
await handler(this.#info);
|
|
96
|
+
while (this.#running) {
|
|
97
|
+
let updates;
|
|
98
|
+
try {
|
|
99
|
+
updates = await this.api.getUpdates({
|
|
100
|
+
offset: this.#offset,
|
|
101
|
+
timeout: 30,
|
|
102
|
+
...(this.#options.allowedUpdates
|
|
103
|
+
? { allowed_updates: this.#options.allowedUpdates }
|
|
104
|
+
: {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (!this.#running)
|
|
109
|
+
break;
|
|
110
|
+
console.error("[yaebal] getUpdates failed, retrying in 3s:", error);
|
|
111
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
for (const update of updates) {
|
|
115
|
+
this.#offset = update.update_id + 1;
|
|
116
|
+
await this.handleUpdate(update);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** stop the polling loop. */
|
|
121
|
+
stop() {
|
|
122
|
+
this.#running = false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=bot.js.map
|
package/lib/bot.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Context } from "./context.js";
|
|
2
|
+
import type { CallbackQuery, MessageEntity, UpdateName } from "./telegram-types.js";
|
|
3
|
+
export type NextFn = () => Promise<void>;
|
|
4
|
+
export type Middleware<C> = (ctx: C, next: NextFn) => unknown | Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* a plugin enriches the context. dependencies are expressed by the type it
|
|
7
|
+
* requires (`In`), so installing a plugin before its dependency is a compile
|
|
8
|
+
* error — not a runtime surprise (core invariant #4).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const session: Plugin<Context, { session: Session }>;
|
|
12
|
+
* const auth: Plugin<Context & { session: Session }, { user: User }>;
|
|
13
|
+
* bot.install(auth); // ❌ no `session` on the context
|
|
14
|
+
* bot.install(session).install(auth); // ✅
|
|
15
|
+
*/
|
|
16
|
+
export type Plugin<In extends Context = Context, Out extends object = Record<never, never>> = <C extends In>(composer: Composer<C>) => Composer<C & Out>;
|
|
17
|
+
/**
|
|
18
|
+
* a composable filter (the mtcute idea). `test` is a type guard, so a matching
|
|
19
|
+
* filter narrows the context to `C & Add`; filters may also attach `Add` fields
|
|
20
|
+
* onto the context as a side effect (e.g. `regex` exposes `ctx.match`). combine
|
|
21
|
+
* with `and` / `or` / `not` from `@yaebal/filters`.
|
|
22
|
+
*/
|
|
23
|
+
export interface Filter<C = Context, Add extends object = Record<never, never>> {
|
|
24
|
+
test(ctx: C): ctx is C & Add;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* filter query mini-language (the grammY idea), e.g.
|
|
28
|
+
* `"message:text"`, `"callback_query:data"`, `":photo"`.
|
|
29
|
+
*/
|
|
30
|
+
export type FilterQuery = UpdateName | `${UpdateName}:${string}` | `:${string}`;
|
|
31
|
+
/** narrows the context type for known queries so handlers get non-optional fields. */
|
|
32
|
+
export type Filtered<C, Q extends string> = Q extends `${string}:text` | `${string}:caption` ? C & {
|
|
33
|
+
text: string;
|
|
34
|
+
} : Q extends `${string}:data` | "callback_query" ? C & {
|
|
35
|
+
callbackQuery: CallbackQuery;
|
|
36
|
+
} : Q extends `${string}:entities${string}` ? C & {
|
|
37
|
+
entities: MessageEntity[];
|
|
38
|
+
} : C;
|
|
39
|
+
/** koa-style middleware composer with single-`next()` protection. */
|
|
40
|
+
export declare function compose<C>(middlewares: Middleware<C>[]): (ctx: C, next?: NextFn) => Promise<void>;
|
|
41
|
+
export declare function matchQuery(ctx: Context, query: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* the chainable middleware pipeline. every context-enriching method returns a
|
|
44
|
+
* composer whose context type carries the new properties — types flow through
|
|
45
|
+
* the whole chain (the GramIO idea). `Composer` is also usable standalone, so
|
|
46
|
+
* feature files can be plain composers with no `Bot` and no token.
|
|
47
|
+
*/
|
|
48
|
+
export declare class Composer<C extends Context = Context> {
|
|
49
|
+
protected middlewares: Middleware<C>[];
|
|
50
|
+
/** raw middleware. call `next()` to continue the chain. */
|
|
51
|
+
use(...middleware: Middleware<C>[]): this;
|
|
52
|
+
/** handle a specific update, optionally narrowed by a filter query. */
|
|
53
|
+
on<Q extends FilterQuery>(query: Q, ...handlers: Middleware<Filtered<C, Q>>[]): this;
|
|
54
|
+
/** handle `/<name>` commands. Strips a trailing `@botname` and parses args. */
|
|
55
|
+
command(name: string, ...handlers: Middleware<C & {
|
|
56
|
+
command: string;
|
|
57
|
+
args: string[];
|
|
58
|
+
}>[]): this;
|
|
59
|
+
/** match message text/caption against a string or regex; exposes `ctx.match`. */
|
|
60
|
+
hears(trigger: string | RegExp, ...handlers: Middleware<C & {
|
|
61
|
+
match: string | RegExpMatchArray;
|
|
62
|
+
}>[]): this;
|
|
63
|
+
/** match callback-query data against a string or regex; exposes `ctx.match`. */
|
|
64
|
+
callbackQuery(trigger: string | RegExp, ...handlers: Middleware<C & {
|
|
65
|
+
match: string | RegExpMatchArray;
|
|
66
|
+
callbackQuery: CallbackQuery;
|
|
67
|
+
}>[]): this;
|
|
68
|
+
/** continue only if the predicate holds. */
|
|
69
|
+
guard(predicate: (ctx: C) => boolean | Promise<boolean>): this;
|
|
70
|
+
/**
|
|
71
|
+
* run `handlers` only when `filter` matches. the filter narrows the context
|
|
72
|
+
* (and may attach data), so handlers see `C & Add`. compose filters with
|
|
73
|
+
* `and` / `or` / `not` from `@yaebal/filters`.
|
|
74
|
+
*/
|
|
75
|
+
filter<Add extends object>(filter: Filter<Context, Add>, ...handlers: Middleware<C & Add>[]): this;
|
|
76
|
+
/** apply a plugin. its required context (`In`) is checked at compile time. */
|
|
77
|
+
install<Add extends object>(plugin: (composer: Composer<C>) => Composer<C & Add>): Composer<C & Add>;
|
|
78
|
+
/** async, per-request context enrichment. adds `D` to the context type. */
|
|
79
|
+
derive<D extends object>(fn: (ctx: C) => D | Promise<D>): Composer<C & D>;
|
|
80
|
+
/**
|
|
81
|
+
* scoped enrichment (the GramIO idea): `fn` runs only for the listed update
|
|
82
|
+
* types, so irrelevant updates pay nothing. the fields are typed as optional
|
|
83
|
+
* (`Partial<D>`) since they are absent on other update types.
|
|
84
|
+
*/
|
|
85
|
+
derive<D extends object>(updates: UpdateName | UpdateName[], fn: (ctx: C) => D | Promise<D>): Composer<C & Partial<D>>;
|
|
86
|
+
/**
|
|
87
|
+
* static context enrichment. adds `D` to the context type.
|
|
88
|
+
* NOTE: a production build would hoist this out of the per-request path entirely;
|
|
89
|
+
* here it is applied once at the top of the chain for simplicity.
|
|
90
|
+
*/
|
|
91
|
+
decorate<D extends object>(value: D): Composer<C & D>;
|
|
92
|
+
/** merge another composer in, inheriting its full context type. */
|
|
93
|
+
extend<C2 extends Context>(other: Composer<C2>): Composer<C & C2>;
|
|
94
|
+
/** collapse this composer into a single middleware (used by `extend` and `Bot`). */
|
|
95
|
+
toMiddleware(): Middleware<C>;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=composer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEpF,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,MAAM,CAAC,EAAE,SAAS,OAAO,GAAG,OAAO,EAAE,GAAG,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAC7F,CAAC,SAAS,EAAE,EAEZ,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,KACjB,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC;IAC7E,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,CAAC;AAEhF,sFAAsF;AACtF,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,OAAO,GAAG,GAAG,MAAM,UAAU,GACzF,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACpB,CAAC,SAAS,GAAG,MAAM,OAAO,GAAG,gBAAgB,GAC5C,CAAC,GAAG;IAAE,aAAa,EAAE,aAAa,CAAA;CAAE,GACpC,CAAC,SAAS,GAAG,MAAM,YAAY,MAAM,EAAE,GACtC,CAAC,GAAG;IAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,GACjC,CAAC,CAAC;AAEP,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAgBjG;AAkBD,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAS/D;AAED;;;;;GAKG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IAChD,SAAS,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAM;IAE5C,2DAA2D;IAC3D,GAAG,CAAC,GAAG,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAKzC,uEAAuE;IACvE,EAAE,CAAC,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAUpF,+EAA+E;IAC/E,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,EAAE,GAAG,IAAI;IAkB/F,iFAAiF;IACjF,KAAK,CACJ,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,GAAG;QAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAA;KAAE,CAAC,EAAE,GACjE,IAAI;IAwBP,gFAAgF;IAChF,aAAa,CACZ,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,GAAG,QAAQ,EAAE,UAAU,CACtB,CAAC,GAAG;QAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAC;QAAC,aAAa,EAAE,aAAa,CAAA;KAAE,CACtE,EAAE,GACD,IAAI;IAyBP,4CAA4C;IAC5C,KAAK,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI;IAQ9D;;;;OAIG;IACH,MAAM,CAAC,GAAG,SAAS,MAAM,EACxB,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAC5B,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,GAChC,IAAI;IAOP,8EAA8E;IAC9E,OAAO,CAAC,GAAG,SAAS,MAAM,EACzB,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC,GAClD,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC;IAIpB,2EAA2E;IAC3E,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC;IACzE;;;;OAIG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,EACtB,OAAO,EAAE,UAAU,GAAG,UAAU,EAAE,EAClC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC5B,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAe3B;;;;OAIG;IACH,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC;IASrD,mEAAmE;IACnE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;IAKjE,oFAAoF;IACpF,YAAY,IAAI,UAAU,CAAC,CAAC,CAAC;CAI7B"}
|