@nubemclaw/channel-telegram 1.2.2

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.
@@ -0,0 +1,64 @@
1
+ import { type NubemClawError, type Result } from "@nubemclaw/core";
2
+ import type { ChannelLogger } from "@nubemclaw/channel-sdk";
3
+ import { type TelegramMessage, type TelegramUpdate, type TelegramUser } from "./schemas.js";
4
+ /**
5
+ * Telegram Bot API client (Phase 13, raw undici — ADR-0020 §1 chose
6
+ * library route per channel; Telegram's Bot API is REST simple enough
7
+ * that a raw client + zod boundary is lighter than pulling `grammy` or
8
+ * `telegraf` for the surface we actually use).
9
+ *
10
+ * Three methods F13 needs:
11
+ *
12
+ * • `getMe()` — smoke test for setup validation.
13
+ * • `getUpdates(...)` — long-poll fetch for the polling transport.
14
+ * • `sendMessage(...)` — outbound from the agent loop.
15
+ *
16
+ * Plus `setWebhook` / `deleteWebhook` for the webhook lifecycle; the
17
+ * polling transport calls `deleteWebhook` on start to avoid the 409
18
+ * Telegram returns if both modes are active.
19
+ *
20
+ * Boundary contract:
21
+ *
22
+ * • Token NEVER appears in logs. We log only the method name
23
+ * (`getUpdates`, `sendMessage`) — the bot path prefix is the
24
+ * secret and stays in the URL.
25
+ * • Every response is zod-parsed at this boundary. Callers see
26
+ * `Result<T, NubemClawError>` and consume typed payloads.
27
+ * • 401 → `auth.invalid_credentials`, 429 → `rate_limit.exceeded`
28
+ * with `retry_after` (when Telegram supplies it), 5xx →
29
+ * `provider.unavailable`. The polling transport's own backoff
30
+ * layer above this client handles scheduling the retry.
31
+ */
32
+ export interface TelegramApiClientOptions {
33
+ readonly token: string;
34
+ readonly logger: ChannelLogger;
35
+ /** Test seam: override the fetch implementation. Defaults to global. */
36
+ readonly fetch?: typeof fetch;
37
+ /** Test seam: override base URL (default https://api.telegram.org). */
38
+ readonly baseUrl?: string;
39
+ }
40
+ export interface SendMessageArgs {
41
+ readonly chatId: number;
42
+ readonly text: string;
43
+ readonly parseMode?: "HTML" | "MarkdownV2";
44
+ }
45
+ export interface GetUpdatesArgs {
46
+ readonly offset?: number;
47
+ readonly timeoutSec: number;
48
+ readonly allowedUpdates: readonly string[];
49
+ }
50
+ export interface SetWebhookArgs {
51
+ readonly url: string;
52
+ readonly secretToken: string;
53
+ readonly allowedUpdates: readonly string[];
54
+ readonly dropPendingUpdates?: boolean;
55
+ }
56
+ export interface TelegramApiClient {
57
+ getMe(): Promise<Result<TelegramUser, NubemClawError>>;
58
+ getUpdates(args: GetUpdatesArgs): Promise<Result<readonly TelegramUpdate[], NubemClawError>>;
59
+ sendMessage(args: SendMessageArgs): Promise<Result<TelegramMessage, NubemClawError>>;
60
+ setWebhook(args: SetWebhookArgs): Promise<Result<true, NubemClawError>>;
61
+ deleteWebhook(): Promise<Result<true, NubemClawError>>;
62
+ }
63
+ export declare const createTelegramApiClient: (opts: TelegramApiClientOptions) => TelegramApiClient;
64
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,MAAM,EACZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAGzE,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC;IACvD,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7F,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC;IACrF,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACxE,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;CACxD;AAID,eAAO,MAAM,uBAAuB,GAClC,MAAM,wBAAwB,KAC7B,iBAwIF,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { err, nubemClawError, ok, } from "@nubemsystems/nubemclaw-core";
2
+ import { z } from "zod";
3
+ import { TelegramErrEnvelope, TelegramMessageSchema, TelegramUpdateSchema, TelegramUserSchema, } from "./schemas.js";
4
+ const DEFAULT_BASE_URL = "https://api.telegram.org";
5
+ export const createTelegramApiClient = (opts) => {
6
+ const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
7
+ const doFetch = opts.fetch ?? fetch;
8
+ const callMethod = async (method, body, resultSchema) => {
9
+ const url = `${baseUrl}/bot${opts.token}/${method}`;
10
+ let res;
11
+ try {
12
+ res = await doFetch(url, {
13
+ method: "POST",
14
+ headers: { "content-type": "application/json" },
15
+ body: body === undefined ? "{}" : JSON.stringify(body),
16
+ });
17
+ }
18
+ catch (cause) {
19
+ opts.logger.warn({ method, err: cause }, "telegram api fetch failed");
20
+ return err(nubemClawError({
21
+ code: "provider.unavailable",
22
+ message: `telegram ${method}: network error`,
23
+ cause,
24
+ }));
25
+ }
26
+ let json;
27
+ try {
28
+ json = await res.json();
29
+ }
30
+ catch (cause) {
31
+ opts.logger.warn({ method, status: res.status, err: cause }, "telegram api non-JSON body");
32
+ return err(nubemClawError({
33
+ code: "provider.bad_response",
34
+ message: `telegram ${method}: non-JSON response (status ${res.status})`,
35
+ cause,
36
+ }));
37
+ }
38
+ // Failure envelope path — handled first because Telegram returns
39
+ // structured errors with HTTP 2xx and 4xx alike.
40
+ const errParse = TelegramErrEnvelope.safeParse(json);
41
+ if (errParse.success && !errParse.data.ok) {
42
+ const failure = errParse.data;
43
+ const retryAfter = failure.parameters?.retry_after;
44
+ const mapped = failure.error_code === 401
45
+ ? "auth.invalid_credentials"
46
+ : failure.error_code === 429
47
+ ? "rate_limit.exceeded"
48
+ : failure.error_code >= 500
49
+ ? "provider.unavailable"
50
+ : "provider.bad_response";
51
+ return err(nubemClawError({
52
+ code: mapped,
53
+ message: `telegram ${method}: ${failure.description}`,
54
+ meta: {
55
+ error_code: failure.error_code,
56
+ ...(retryAfter !== undefined ? { retry_after: retryAfter } : {}),
57
+ },
58
+ }));
59
+ }
60
+ const okSchema = z.object({ ok: z.literal(true), result: resultSchema });
61
+ const okParse = okSchema.safeParse(json);
62
+ if (!okParse.success) {
63
+ opts.logger.warn({ method, status: res.status, issues: okParse.error.issues }, "telegram api response failed schema validation");
64
+ return err(nubemClawError({
65
+ code: "provider.bad_response",
66
+ message: `telegram ${method}: response did not match expected schema`,
67
+ meta: { status: res.status, issues: okParse.error.issues },
68
+ }));
69
+ }
70
+ return ok(okParse.data.result);
71
+ };
72
+ return {
73
+ getMe: () => callMethod("getMe", undefined, TelegramUserSchema),
74
+ getUpdates: async (args) => {
75
+ const result = await callMethod("getUpdates", {
76
+ ...(args.offset !== undefined ? { offset: args.offset } : {}),
77
+ timeout: args.timeoutSec,
78
+ allowed_updates: args.allowedUpdates,
79
+ }, z.array(TelegramUpdateSchema));
80
+ if (!result.ok)
81
+ return result;
82
+ return ok(result.value);
83
+ },
84
+ sendMessage: (args) => callMethod("sendMessage", {
85
+ chat_id: args.chatId,
86
+ text: args.text,
87
+ ...(args.parseMode !== undefined ? { parse_mode: args.parseMode } : {}),
88
+ }, TelegramMessageSchema),
89
+ setWebhook: async (args) => {
90
+ const result = await callMethod("setWebhook", {
91
+ url: args.url,
92
+ secret_token: args.secretToken,
93
+ allowed_updates: args.allowedUpdates,
94
+ drop_pending_updates: args.dropPendingUpdates ?? false,
95
+ }, z.literal(true));
96
+ if (!result.ok)
97
+ return result;
98
+ return ok(true);
99
+ },
100
+ deleteWebhook: async () => {
101
+ const result = await callMethod("deleteWebhook", { drop_pending_updates: false }, z.literal(true));
102
+ if (!result.ok)
103
+ return result;
104
+ return ok(true);
105
+ },
106
+ };
107
+ };
108
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}