@prajwalshete/telegram-plugin 0.0.0-stage → 0.1.0

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.
Files changed (295) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +21 -0
  3. package/README.md +109 -2
  4. package/dist/adapters/clock.d.ts +3 -0
  5. package/dist/adapters/clock.js +4 -0
  6. package/dist/adapters/console-log.d.ts +13 -0
  7. package/dist/adapters/console-log.js +41 -0
  8. package/dist/adapters/d1-db.d.ts +5 -0
  9. package/dist/adapters/d1-db.js +37 -0
  10. package/dist/adapters/ids.d.ts +5 -0
  11. package/dist/adapters/ids.js +25 -0
  12. package/dist/adapters/index.d.ts +7 -0
  13. package/dist/adapters/keyring.d.ts +50 -0
  14. package/dist/adapters/keyring.js +174 -0
  15. package/dist/adapters/metrics.d.ts +28 -0
  16. package/dist/adapters/metrics.js +48 -0
  17. package/dist/adapters/r2-files.d.ts +20 -0
  18. package/dist/adapters/r2-files.js +53 -0
  19. package/dist/bin/cli.d.ts +1 -0
  20. package/dist/bin/cli.js +45 -0
  21. package/dist/client/broadcasts.d.ts +35 -0
  22. package/dist/client/commands.d.ts +23 -0
  23. package/dist/client/endpoint.d.ts +34 -0
  24. package/dist/client/events.d.ts +33 -0
  25. package/dist/client/index.d.ts +11 -0
  26. package/dist/client/index.js +3 -0
  27. package/dist/client/schedules.d.ts +42 -0
  28. package/dist/client/schema.d.ts +181 -0
  29. package/dist/client/send.d.ts +15 -0
  30. package/dist/client/templates.d.ts +47 -0
  31. package/dist/contracts/bots.d.ts +129 -0
  32. package/dist/contracts/bots.js +70 -0
  33. package/dist/contracts/broadcasts.d.ts +116 -0
  34. package/dist/contracts/broadcasts.js +63 -0
  35. package/dist/contracts/chats.d.ts +97 -0
  36. package/dist/contracts/chats.js +66 -0
  37. package/dist/contracts/commands.d.ts +52 -0
  38. package/dist/contracts/commands.js +53 -0
  39. package/dist/contracts/common.d.ts +38 -0
  40. package/dist/contracts/common.js +41 -0
  41. package/dist/contracts/deliveries.d.ts +103 -0
  42. package/dist/contracts/deliveries.js +74 -0
  43. package/dist/contracts/destinations.d.ts +24 -0
  44. package/dist/contracts/destinations.js +28 -0
  45. package/dist/contracts/events.d.ts +165 -0
  46. package/dist/contracts/events.js +98 -0
  47. package/dist/contracts/features.d.ts +20 -0
  48. package/dist/contracts/features.js +21 -0
  49. package/dist/contracts/health.d.ts +55 -0
  50. package/dist/contracts/health.js +58 -0
  51. package/dist/contracts/index.d.ts +22 -0
  52. package/dist/contracts/index.js +22 -0
  53. package/dist/contracts/labels.d.ts +38 -0
  54. package/dist/contracts/labels.js +52 -0
  55. package/dist/contracts/links.d.ts +51 -0
  56. package/dist/contracts/links.js +50 -0
  57. package/dist/contracts/meta.d.ts +132 -0
  58. package/dist/contracts/meta.js +85 -0
  59. package/dist/contracts/permissions.d.ts +24 -0
  60. package/dist/contracts/permissions.js +27 -0
  61. package/dist/contracts/problems.d.ts +106 -0
  62. package/dist/contracts/problems.js +101 -0
  63. package/dist/contracts/recipients.d.ts +36 -0
  64. package/dist/contracts/recipients.js +22 -0
  65. package/dist/contracts/schedules.d.ts +276 -0
  66. package/dist/contracts/schedules.js +143 -0
  67. package/dist/contracts/send.d.ts +126 -0
  68. package/dist/contracts/send.js +69 -0
  69. package/dist/contracts/subscriptions.d.ts +38 -0
  70. package/dist/contracts/subscriptions.js +31 -0
  71. package/dist/contracts/templates.d.ts +358 -0
  72. package/dist/contracts/templates.js +174 -0
  73. package/dist/contracts/users.d.ts +15 -0
  74. package/dist/contracts/users.js +19 -0
  75. package/dist/core/guards.d.ts +22 -0
  76. package/dist/core/guards.js +47 -0
  77. package/dist/core/html/escape.d.ts +2 -0
  78. package/dist/core/html/escape.js +36 -0
  79. package/dist/core/html/tokenize.js +65 -0
  80. package/dist/core/index.d.ts +18 -0
  81. package/dist/core/pacing.d.ts +1 -0
  82. package/dist/core/pacing.js +86 -0
  83. package/dist/core/random.d.ts +2 -0
  84. package/dist/core/random.js +38 -0
  85. package/dist/core/recipients.d.ts +1 -0
  86. package/dist/core/recipients.js +86 -0
  87. package/dist/core/schedule/describe.d.ts +3 -0
  88. package/dist/core/schedule/describe.js +74 -0
  89. package/dist/core/schedule/index.d.ts +4 -0
  90. package/dist/core/schedule/policy.d.ts +1 -0
  91. package/dist/core/schedule/policy.js +37 -0
  92. package/dist/core/schedule/rule.d.ts +25 -0
  93. package/dist/core/schedule/rule.js +142 -0
  94. package/dist/core/schedule/tz.d.ts +2 -0
  95. package/dist/core/schedule/tz.js +59 -0
  96. package/dist/core/split/plain.d.ts +2 -0
  97. package/dist/core/split/plain.js +23 -0
  98. package/dist/core/split/split.d.ts +15 -0
  99. package/dist/core/split/split.js +158 -0
  100. package/dist/core/template/ast.d.ts +58 -0
  101. package/dist/core/template/ast.js +5 -0
  102. package/dist/core/template/expr.js +108 -0
  103. package/dist/core/template/filters.d.ts +20 -0
  104. package/dist/core/template/filters.js +127 -0
  105. package/dist/core/template/format.d.ts +17 -0
  106. package/dist/core/template/format.js +88 -0
  107. package/dist/core/template/index.d.ts +20 -0
  108. package/dist/core/template/index.js +29 -0
  109. package/dist/core/template/parse.d.ts +8 -0
  110. package/dist/core/template/parse.js +369 -0
  111. package/dist/core/template/render.d.ts +17 -0
  112. package/dist/core/template/render.js +118 -0
  113. package/dist/core/template/shape.d.ts +28 -0
  114. package/dist/core/template/shape.js +151 -0
  115. package/dist/core/template/tags.js +105 -0
  116. package/dist/core/template/validate.d.ts +10 -0
  117. package/dist/core/template/validate.js +144 -0
  118. package/dist/core/template/variables.d.ts +15 -0
  119. package/dist/core/template/variables.js +41 -0
  120. package/dist/do/index.d.ts +3 -0
  121. package/dist/do/index.js +3 -0
  122. package/dist/do/telegram-bot-do.d.ts +45 -0
  123. package/dist/do/telegram-bot-do.js +219 -0
  124. package/dist/engine/actions/callback.js +256 -0
  125. package/dist/engine/actions/tokens.d.ts +2 -0
  126. package/dist/engine/actions/tokens.js +51 -0
  127. package/dist/engine/bot-api.js +20 -0
  128. package/dist/engine/bot-features.js +39 -0
  129. package/dist/engine/chat-state.js +66 -0
  130. package/dist/engine/content/audience.js +54 -0
  131. package/dist/engine/content/buttons.d.ts +1 -0
  132. package/dist/engine/content/buttons.js +113 -0
  133. package/dist/engine/content/fanout.js +284 -0
  134. package/dist/engine/content/intersect.d.ts +6 -0
  135. package/dist/engine/content/intersect.js +26 -0
  136. package/dist/engine/content/providers.d.ts +6 -0
  137. package/dist/engine/content/providers.js +142 -0
  138. package/dist/engine/content/recipients.js +128 -0
  139. package/dist/engine/content/render.js +138 -0
  140. package/dist/engine/content/reply.js +156 -0
  141. package/dist/engine/content/report-files.js +78 -0
  142. package/dist/engine/content/template-rows.js +74 -0
  143. package/dist/engine/dialogs/dialog.d.ts +2 -0
  144. package/dist/engine/dialogs/dialog.js +375 -0
  145. package/dist/engine/dispatch/builtins.js +198 -0
  146. package/dist/engine/dispatch/commands.js +194 -0
  147. package/dist/engine/dispatch/context.d.ts +2 -0
  148. package/dist/engine/dispatch/context.js +194 -0
  149. package/dist/engine/dispatch/dispatcher.js +66 -0
  150. package/dist/engine/dispatch/handlers.d.ts +1 -0
  151. package/dist/engine/dispatch/handlers.js +103 -0
  152. package/dist/engine/dispatch/lifecycle.js +85 -0
  153. package/dist/engine/dispatch/link-chat.js +31 -0
  154. package/dist/engine/dispatch/linking.js +273 -0
  155. package/dist/engine/errors.d.ts +60 -0
  156. package/dist/engine/errors.js +72 -0
  157. package/dist/engine/events/filters.d.ts +5 -0
  158. package/dist/engine/events/filters.js +82 -0
  159. package/dist/engine/events/kick.js +30 -0
  160. package/dist/engine/events/payload.d.ts +3 -0
  161. package/dist/engine/events/payload.js +16 -0
  162. package/dist/engine/events/tick.js +208 -0
  163. package/dist/engine/i18n.d.ts +12 -0
  164. package/dist/engine/i18n.js +55 -0
  165. package/dist/engine/inbound/files.js +121 -0
  166. package/dist/engine/inbound/handler.js +46 -0
  167. package/dist/engine/jobs.d.ts +32 -0
  168. package/dist/engine/jobs.js +85 -0
  169. package/dist/engine/link-codes.js +46 -0
  170. package/dist/engine/metrics.js +12 -0
  171. package/dist/engine/options.d.ts +369 -0
  172. package/dist/engine/options.js +81 -0
  173. package/dist/engine/outbox/dead-letter.js +78 -0
  174. package/dist/engine/outbox/do-registry.d.ts +10 -0
  175. package/dist/engine/outbox/do-registry.js +10 -0
  176. package/dist/engine/outbox/do-transport.d.ts +33 -0
  177. package/dist/engine/outbox/do-transport.js +73 -0
  178. package/dist/engine/outbox/drain.d.ts +13 -0
  179. package/dist/engine/outbox/drain.js +381 -0
  180. package/dist/engine/outbox/enqueue.d.ts +1 -0
  181. package/dist/engine/outbox/enqueue.js +63 -0
  182. package/dist/engine/outbox/select.js +24 -0
  183. package/dist/engine/outbox/send.js +362 -0
  184. package/dist/engine/outbox/transport.d.ts +35 -0
  185. package/dist/engine/outbox/transport.js +175 -0
  186. package/dist/engine/outbox/wake.js +28 -0
  187. package/dist/engine/registry.d.ts +502 -0
  188. package/dist/engine/registry.js +258 -0
  189. package/dist/engine/rows.d.ts +25 -0
  190. package/dist/engine/rows.js +131 -0
  191. package/dist/engine/runtime.d.ts +95 -0
  192. package/dist/engine/runtime.js +149 -0
  193. package/dist/engine/schedules/fire.js +208 -0
  194. package/dist/engine/schedules/resume.js +75 -0
  195. package/dist/engine/schedules/rows.js +69 -0
  196. package/dist/engine/schedules/tick.js +30 -0
  197. package/dist/engine/schedules/wake.js +50 -0
  198. package/dist/engine/state.d.ts +11 -0
  199. package/dist/engine/state.js +78 -0
  200. package/dist/locales/en.js +339 -0
  201. package/dist/locales/en.json +341 -0
  202. package/dist/locales/mr.js +339 -0
  203. package/dist/locales/mr.json +341 -0
  204. package/dist/ports/db.d.ts +22 -0
  205. package/dist/ports/index.d.ts +4 -0
  206. package/dist/ports/ports.d.ts +257 -0
  207. package/dist/ports/sql.d.ts +39 -0
  208. package/dist/ports/sql.js +76 -0
  209. package/dist/renderers/csv.d.ts +14 -0
  210. package/dist/renderers/csv.js +51 -0
  211. package/dist/renderers/html.d.ts +44 -0
  212. package/dist/renderers/html.js +81 -0
  213. package/dist/renderers/index.d.ts +5 -0
  214. package/dist/renderers/index.js +4 -0
  215. package/dist/renderers/table.d.ts +30 -0
  216. package/dist/renderers/table.js +114 -0
  217. package/dist/routes/admin.js +146 -0
  218. package/dist/routes/app.d.ts +3111 -0
  219. package/dist/routes/app.js +9 -0
  220. package/dist/routes/broadcasts.js +37 -0
  221. package/dist/routes/commands.js +35 -0
  222. package/dist/routes/content.js +12 -0
  223. package/dist/routes/events.js +36 -0
  224. package/dist/routes/http.d.ts +30 -0
  225. package/dist/routes/http.js +223 -0
  226. package/dist/routes/me.js +59 -0
  227. package/dist/routes/schedules.js +62 -0
  228. package/dist/routes/send.js +24 -0
  229. package/dist/routes/templates.js +70 -0
  230. package/dist/routes/webhook.js +147 -0
  231. package/dist/schema/columns.js +307 -0
  232. package/dist/schema/ddl.d.ts +7 -0
  233. package/dist/schema/ddl.js +45 -0
  234. package/dist/schema/index.d.ts +4255 -0
  235. package/dist/schema/index.js +44 -0
  236. package/dist/schema/reference.sql +359 -0
  237. package/dist/schema/tables.d.ts +4243 -0
  238. package/dist/schema/tables.js +327 -0
  239. package/dist/schema/version.d.ts +5 -0
  240. package/dist/schema/version.js +7 -0
  241. package/dist/server/index.d.ts +55 -0
  242. package/dist/server/index.js +46 -0
  243. package/dist/server/plugin.d.ts +81 -0
  244. package/dist/server/plugin.js +247 -0
  245. package/dist/server/version.d.ts +2 -0
  246. package/dist/server/version.js +4 -0
  247. package/dist/services/ai-tool.d.ts +26 -0
  248. package/dist/services/ai-tool.js +148 -0
  249. package/dist/services/bots.js +300 -0
  250. package/dist/services/broadcasts.js +390 -0
  251. package/dist/services/chats.js +83 -0
  252. package/dist/services/cleanup.js +83 -0
  253. package/dist/services/commands.js +140 -0
  254. package/dist/services/deliveries.js +53 -0
  255. package/dist/services/destinations.js +91 -0
  256. package/dist/services/event-rules.js +229 -0
  257. package/dist/services/events.js +20 -0
  258. package/dist/services/idempotency.js +64 -0
  259. package/dist/services/links.js +200 -0
  260. package/dist/services/menu.js +95 -0
  261. package/dist/services/ops.js +90 -0
  262. package/dist/services/rekey.js +63 -0
  263. package/dist/services/reports.js +87 -0
  264. package/dist/services/schedules.js +214 -0
  265. package/dist/services/send.d.ts +14 -0
  266. package/dist/services/send.js +421 -0
  267. package/dist/services/subscriptions.js +28 -0
  268. package/dist/services/templates.js +259 -0
  269. package/dist/telegram/classify.d.ts +30 -0
  270. package/dist/telegram/classify.js +164 -0
  271. package/dist/telegram/client.d.ts +73 -0
  272. package/dist/telegram/client.js +135 -0
  273. package/dist/telegram/errors.d.ts +37 -0
  274. package/dist/telegram/errors.js +62 -0
  275. package/dist/telegram/inbound.d.ts +40 -0
  276. package/dist/telegram/inbound.js +72 -0
  277. package/dist/telegram/index.d.ts +8 -0
  278. package/dist/telegram/multipart.d.ts +5 -0
  279. package/dist/telegram/multipart.js +47 -0
  280. package/dist/telegram/types.d.ts +271 -0
  281. package/dist/telegram/update.d.ts +833 -0
  282. package/dist/telegram/update.js +129 -0
  283. package/dist/testing/fake-html.d.ts +9 -0
  284. package/dist/testing/fake-html.js +83 -0
  285. package/dist/testing/fake-telegram.d.ts +206 -0
  286. package/dist/testing/fake-telegram.js +856 -0
  287. package/dist/testing/index.d.ts +7 -0
  288. package/dist/testing/index.js +7 -0
  289. package/dist/testing/reference-sql.d.ts +4 -0
  290. package/dist/testing/reference-sql.js +8 -0
  291. package/dist/testing/test-plugin.d.ts +126 -0
  292. package/dist/testing/test-plugin.js +182 -0
  293. package/dist/testing/updates.d.ts +100 -0
  294. package/dist/testing/updates.js +212 -0
  295. package/package.json +113 -3
@@ -0,0 +1,37 @@
1
+ /** Telegram answered with `ok: false` (or a non-JSON error body). */
2
+ export declare class TelegramApiError extends Error {
3
+ readonly name = "TelegramApiError";
4
+ /** Bot API method name (`sendMessage`). */
5
+ readonly method: string;
6
+ /** HTTP status. */
7
+ readonly status: number;
8
+ /** `error_code` from the body (usually equals `status`). */
9
+ readonly errorCode: number;
10
+ /** Telegram's `description` text. */
11
+ readonly description: string;
12
+ /** Seconds, from `parameters.retry_after` (429). */
13
+ readonly retryAfter: number | undefined;
14
+ /** From `parameters.migrate_to_chat_id` (group became a supergroup). */
15
+ readonly migrateToChatId: number | undefined;
16
+ constructor(init: {
17
+ method: string;
18
+ status: number;
19
+ errorCode: number;
20
+ description: string;
21
+ retryAfter?: number | undefined;
22
+ migrateToChatId?: number | undefined;
23
+ });
24
+ toJSON(): Record<string, unknown>;
25
+ }
26
+ /** The request never got a Telegram answer: connection failure or our own timeout. */
27
+ export declare class TelegramNetworkError extends Error {
28
+ readonly name = "TelegramNetworkError";
29
+ /** Bot API method name. */
30
+ readonly method: string;
31
+ /** `network` = connection failed, `timeout` = our timeout fired, `aborted` = the caller's signal aborted. */
32
+ readonly reason: "network" | "timeout" | "aborted";
33
+ constructor(method: string, reason: "network" | "timeout" | "aborted");
34
+ toJSON(): Record<string, unknown>;
35
+ }
36
+ /** Type guard for {@link TelegramApiError}. */
37
+ export declare function isTelegramApiError(e: unknown): e is TelegramApiError;
@@ -0,0 +1,62 @@
1
+ // src/telegram/errors.ts
2
+ /** Telegram answered with `ok: false` (or a non-JSON error body). */
3
+ var TelegramApiError = class extends Error {
4
+ name = "TelegramApiError";
5
+ /** Bot API method name (`sendMessage`). */
6
+ method;
7
+ /** HTTP status. */
8
+ status;
9
+ /** `error_code` from the body (usually equals `status`). */
10
+ errorCode;
11
+ /** Telegram's `description` text. */
12
+ description;
13
+ /** Seconds, from `parameters.retry_after` (429). */
14
+ retryAfter;
15
+ /** From `parameters.migrate_to_chat_id` (group became a supergroup). */
16
+ migrateToChatId;
17
+ constructor(init) {
18
+ super(`Telegram ${init.method} failed: ${init.errorCode} ${init.description}`);
19
+ this.method = init.method;
20
+ this.status = init.status;
21
+ this.errorCode = init.errorCode;
22
+ this.description = init.description;
23
+ this.retryAfter = init.retryAfter;
24
+ this.migrateToChatId = init.migrateToChatId;
25
+ }
26
+ toJSON() {
27
+ return {
28
+ name: this.name,
29
+ method: this.method,
30
+ status: this.status,
31
+ errorCode: this.errorCode,
32
+ description: this.description,
33
+ retryAfter: this.retryAfter,
34
+ migrateToChatId: this.migrateToChatId
35
+ };
36
+ }
37
+ };
38
+ /** The request never got a Telegram answer: connection failure or our own timeout. */
39
+ var TelegramNetworkError = class extends Error {
40
+ name = "TelegramNetworkError";
41
+ /** Bot API method name. */
42
+ method;
43
+ /** `network` = connection failed, `timeout` = our timeout fired, `aborted` = the caller's signal aborted. */
44
+ reason;
45
+ constructor(method, reason) {
46
+ super(`Telegram ${method} ${reason === "network" ? "network error" : reason}`);
47
+ this.method = method;
48
+ this.reason = reason;
49
+ }
50
+ toJSON() {
51
+ return {
52
+ name: this.name,
53
+ method: this.method,
54
+ reason: this.reason
55
+ };
56
+ }
57
+ };
58
+ /** Type guard for {@link TelegramApiError}. */
59
+ function isTelegramApiError(e) {
60
+ return e instanceof TelegramApiError;
61
+ }
62
+ export { TelegramApiError, TelegramNetworkError, isTelegramApiError };
@@ -0,0 +1,40 @@
1
+ import { TgChat, TgMessage, TgMessageEntity, TgUpdate, TgUser } from "./update.js";
2
+ /** Dispatcher route for an update (see {@link classifyUpdate}). */
3
+ export type UpdateClass = "lifecycle" | "command" | "callback" | "text" | "media" | "other";
4
+ /** Top-level update field name (`message`, `callback_query`, …), stored as `tg_update.kind`. */
5
+ export declare function updateType(update: TgUpdate): string;
6
+ /** True when the message is a service message about chat membership or migration. */
7
+ export declare function isLifecycleMessage(m: TgMessage): boolean;
8
+ /**
9
+ * Routes an update. Order follows docs/02 §4.1: lifecycle → command → callback → text → media → other.
10
+ * Edited messages are `other` so an edit never re-runs a command.
11
+ */
12
+ export declare function classifyUpdate(update: TgUpdate): UpdateClass;
13
+ /** The message an update carries (message, edited message, or the callback's message). */
14
+ export declare function updateMessage(update: TgUpdate): TgMessage | undefined;
15
+ /** The chat an update belongs to (message, callback message, or membership change). */
16
+ export declare function updateChat(update: TgUpdate): TgChat | undefined;
17
+ /** The user who caused an update (callback, message, edit, or membership change). */
18
+ export declare function updateFrom(update: TgUpdate): TgUser | undefined;
19
+ /** A parsed `/command` message (see {@link parseCommand}). */
20
+ export interface ParsedCommand {
21
+ /** Lower-case command without `/` and without `@bot`. */
22
+ command: string;
23
+ /** Whitespace-separated arguments. */
24
+ args: string[];
25
+ /** Everything after the command, trimmed (for free-form args). */
26
+ argsText: string;
27
+ /** `@username` suffix if present (without `@`). */
28
+ mention: string | null;
29
+ /** The command is addressed to a different bot: ignore it. */
30
+ mentionedOther: boolean;
31
+ }
32
+ /**
33
+ * Parses `/cmd`, `/cmd args`, `/cmd@Bot args`. Returns null for non-commands. When entities are given, a
34
+ * `bot_command` entity must start at offset 0 (as Telegram marks it); otherwise the text shape decides.
35
+ */
36
+ export declare function parseCommand(text: string | undefined, entities: readonly TgMessageEntity[] | undefined, botUsername: string | null | undefined): ParsedCommand | null;
37
+ /** Deep-link payload of `/start <payload>`, or null when absent or not `[A-Za-z0-9_-]{1,64}`. */
38
+ export declare function parseStartPayload(cmd: ParsedCommand | null): string | null;
39
+ /** True when `payload` is a valid `/start` deep-link payload (`[A-Za-z0-9_-]{1,64}`). */
40
+ export declare function isValidStartPayload(payload: string): boolean;
@@ -0,0 +1,72 @@
1
+ // src/telegram/inbound.ts
2
+ /** Top-level update field name (`message`, `callback_query`, …), stored as `tg_update.kind`. */
3
+ function updateType(update) {
4
+ for (const key of Object.keys(update)) if (key !== "update_id") return key;
5
+ return "unknown";
6
+ }
7
+ /** True when the message is a service message about chat membership or migration. */
8
+ function isLifecycleMessage(m) {
9
+ return m.migrate_to_chat_id !== void 0 || m.migrate_from_chat_id !== void 0 || m.new_chat_members !== void 0 || m.left_chat_member !== void 0 || m.group_chat_created === true || m.supergroup_chat_created === true || m.forum_topic_created !== void 0;
10
+ }
11
+ /**
12
+ * Routes an update. Order follows docs/02 §4.1: lifecycle → command → callback → text → media → other.
13
+ * Edited messages are `other` so an edit never re-runs a command.
14
+ */
15
+ function classifyUpdate(update) {
16
+ if (update.my_chat_member || update.chat_member) return "lifecycle";
17
+ if (update.callback_query) return "callback";
18
+ const m = update.message;
19
+ if (!m) return "other";
20
+ if (isLifecycleMessage(m)) return "lifecycle";
21
+ if (m.text !== void 0) return commandEntity(m.text, m.entities) ? "command" : "text";
22
+ if (m.photo !== void 0 || m.document !== void 0) return "media";
23
+ return "other";
24
+ }
25
+ /** The message an update carries (message, edited message, or the callback's message). */
26
+ function updateMessage(update) {
27
+ return update.message ?? update.edited_message ?? update.callback_query?.message;
28
+ }
29
+ /** The chat an update belongs to (message, callback message, or membership change). */
30
+ function updateChat(update) {
31
+ return updateMessage(update)?.chat ?? update.my_chat_member?.chat ?? update.chat_member?.chat;
32
+ }
33
+ /** The user who caused an update (callback, message, edit, or membership change). */
34
+ function updateFrom(update) {
35
+ return update.callback_query?.from ?? update.message?.from ?? update.edited_message?.from ?? update.my_chat_member?.from ?? update.chat_member?.from;
36
+ }
37
+ const COMMAND_RE = /^\/([A-Za-z0-9_]{1,32})(?:@([A-Za-z0-9_]{3,64}))?(?=\s|$)/;
38
+ function commandEntity(text, entities) {
39
+ if (entities) return entities.some((e) => e.type === "bot_command" && e.offset === 0);
40
+ return COMMAND_RE.test(text);
41
+ }
42
+ /**
43
+ * Parses `/cmd`, `/cmd args`, `/cmd@Bot args`. Returns null for non-commands. When entities are given, a
44
+ * `bot_command` entity must start at offset 0 (as Telegram marks it); otherwise the text shape decides.
45
+ */
46
+ function parseCommand(text, entities, botUsername) {
47
+ if (!text || !commandEntity(text, entities)) return null;
48
+ const m = COMMAND_RE.exec(text);
49
+ if (!m?.[1]) return null;
50
+ const mention = m[2] ?? null;
51
+ const self = botUsername?.replace(/^@/, "").toLowerCase();
52
+ const argsText = text.slice(m[0].length).trim();
53
+ return {
54
+ command: m[1].toLowerCase(),
55
+ args: argsText === "" ? [] : argsText.split(/\s+/),
56
+ argsText,
57
+ mention,
58
+ mentionedOther: mention !== null && mention.toLowerCase() !== self
59
+ };
60
+ }
61
+ const START_PAYLOAD_RE = /^[A-Za-z0-9_-]{1,64}$/;
62
+ /** Deep-link payload of `/start <payload>`, or null when absent or not `[A-Za-z0-9_-]{1,64}`. */
63
+ function parseStartPayload(cmd) {
64
+ if (cmd?.command !== "start" || cmd.args.length !== 1) return null;
65
+ const payload = cmd.args[0] ?? "";
66
+ return START_PAYLOAD_RE.test(payload) ? payload : null;
67
+ }
68
+ /** True when `payload` is a valid `/start` deep-link payload (`[A-Za-z0-9_-]{1,64}`). */
69
+ function isValidStartPayload(payload) {
70
+ return START_PAYLOAD_RE.test(payload);
71
+ }
72
+ export { classifyUpdate, isLifecycleMessage, isValidStartPayload, parseCommand, parseStartPayload, updateChat, updateFrom, updateMessage, updateType };
@@ -0,0 +1,8 @@
1
+ import { ParsedUpdate, TgCallbackQuery, TgChat, TgChatMember, TgChatMemberUpdated, TgDocument, TgForumTopicCreated, TgMessage, TgMessageEntity, TgPhotoSize, TgUpdate, TgUser, parseUpdate } from "./update.js";
2
+ import { AnswerCallbackQueryParams, BotCommand, BotCommandScope, BotUser, ButtonStyle, ChatAction, ChatFullInfo, ChatId, CommandScopeParams, DeleteMessageParams, DeleteWebhookParams, EditMessageReplyMarkupParams, EditMessageTextParams, ForceReply, GetUpdatesParams, InlineKeyboardButton, InlineKeyboardMarkup, InputFile, InputFileUpload, KeyboardButton, LinkPreviewOptions, MethodName, MethodResults, ParseMode, ReplyKeyboardMarkup, ReplyKeyboardRemove, ReplyMarkup, ReplyParameters, SendChatActionParams, SendDocumentParams, SendMessageParams, SendOptions, SendPhotoParams, SetMyCommandsParams, SetWebhookParams, TgFile, UpdateType, WebhookInfo } from "./types.js";
3
+ import { BotApi, CallOptions, DEFAULT_TELEGRAM_BASE_URL, DEFAULT_TELEGRAM_TIMEOUT_MS, FetchLike, TelegramClient, TelegramClientOptions, TokenGetter, fetchTelegram } from "./client.js";
4
+ import { ParsedCommand, UpdateClass, classifyUpdate, isLifecycleMessage, isValidStartPayload, parseCommand, parseStartPayload, updateChat, updateFrom, updateMessage, updateType } from "./inbound.js";
5
+ import { TelegramOutcome, TelegramOutcomeKind, TelegramOutcomeReason, classifyTelegramError, settle } from "./classify.js";
6
+ import { TelegramApiError, TelegramNetworkError, isTelegramApiError } from "./errors.js";
7
+ import { inputFile, isInputFileUpload } from "./multipart.js";
8
+ export { type AnswerCallbackQueryParams, BotApi, type BotCommand, type BotCommandScope, type BotUser, type ButtonStyle, CallOptions, type ChatAction, type ChatFullInfo, type ChatId, type CommandScopeParams, DEFAULT_TELEGRAM_BASE_URL, DEFAULT_TELEGRAM_TIMEOUT_MS, type DeleteMessageParams, type DeleteWebhookParams, type EditMessageReplyMarkupParams, type EditMessageTextParams, FetchLike, type ForceReply, type GetUpdatesParams, type InlineKeyboardButton, type InlineKeyboardMarkup, type InputFile, type InputFileUpload, type KeyboardButton, type LinkPreviewOptions, type MethodName, type MethodResults, type ParseMode, ParsedCommand, ParsedUpdate, type ReplyKeyboardMarkup, type ReplyKeyboardRemove, type ReplyMarkup, type ReplyParameters, type SendChatActionParams, type SendDocumentParams, type SendMessageParams, type SendOptions, type SendPhotoParams, type SetMyCommandsParams, type SetWebhookParams, TelegramApiError, TelegramClient, TelegramClientOptions, TelegramNetworkError, TelegramOutcome, TelegramOutcomeKind, TelegramOutcomeReason, TgCallbackQuery, TgChat, TgChatMember, TgChatMemberUpdated, TgDocument, type TgFile, TgForumTopicCreated, TgMessage, TgMessageEntity, TgPhotoSize, TgUpdate, TgUser, TokenGetter, UpdateClass, type UpdateType, type WebhookInfo, classifyTelegramError, classifyUpdate, fetchTelegram, inputFile, isInputFileUpload, isLifecycleMessage, isTelegramApiError, isValidStartPayload, parseCommand, parseStartPayload, parseUpdate, settle, updateChat, updateFrom, updateMessage, updateType };
@@ -0,0 +1,5 @@
1
+ import { InputFileUpload } from "./types.js";
2
+ /** Type guard: is `v` an {@link InputFileUpload} (`{ data, filename }`)? */
3
+ export declare function isInputFileUpload(v: unknown): v is InputFileUpload;
4
+ /** Builds an upload for `sendDocument` / `sendPhoto`. */
5
+ export declare function inputFile(data: InputFileUpload["data"], filename: string, mime?: string): InputFileUpload;
@@ -0,0 +1,47 @@
1
+ // src/telegram/multipart.ts
2
+ /** Type guard: is `v` an {@link InputFileUpload} (`{ data, filename }`)? */
3
+ function isInputFileUpload(v) {
4
+ if (typeof v !== "object" || v === null) return false;
5
+ const o = v;
6
+ return typeof o.filename === "string" && o.data !== void 0 && o.data !== null;
7
+ }
8
+ /** Builds an upload for `sendDocument` / `sendPhoto`. */
9
+ function inputFile(data, filename, mime) {
10
+ return mime === void 0 ? {
11
+ data,
12
+ filename
13
+ } : {
14
+ data,
15
+ filename,
16
+ mime
17
+ };
18
+ }
19
+ async function toBlob(file) {
20
+ const type = file.mime ?? "application/octet-stream";
21
+ const { data } = file;
22
+ if (data instanceof Blob) return file.mime && data.type !== file.mime ? new Blob([data], { type }) : data;
23
+ if (data instanceof ReadableStream) {
24
+ const buf = await new Response(data).arrayBuffer();
25
+ return new Blob([buf], { type });
26
+ }
27
+ if (data instanceof Uint8Array) return new Blob([data.slice()], { type });
28
+ return new Blob([data], { type });
29
+ }
30
+ /** Encodes Bot API params: JSON, or `multipart/form-data` when any param is a file upload (`contentType` null then: fetch sets the boundary). */
31
+ async function encodeBody(params) {
32
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
33
+ if (!entries.some(([, v]) => isInputFileUpload(v))) return {
34
+ body: JSON.stringify(Object.fromEntries(entries)),
35
+ contentType: "application/json"
36
+ };
37
+ const form = new FormData();
38
+ for (const [key, value] of entries) if (isInputFileUpload(value)) form.append(key, await toBlob(value), value.filename);
39
+ else if (typeof value === "string") form.append(key, value);
40
+ else if (typeof value === "number" || typeof value === "boolean") form.append(key, String(value));
41
+ else form.append(key, JSON.stringify(value));
42
+ return {
43
+ body: form,
44
+ contentType: null
45
+ };
46
+ }
47
+ export { encodeBody, inputFile, isInputFileUpload };
@@ -0,0 +1,271 @@
1
+ import { TgChatMember, TgMessage, TgUser } from "./update.js";
2
+ /** Telegram ids fit in 52 bits; a JS number holds them. Strings are accepted for ids stored as TEXT. */
3
+ export type ChatId = number | string;
4
+ /** Bot API `parse_mode`; the plugin always sends `HTML`. */
5
+ export type ParseMode = "HTML" | "MarkdownV2";
6
+ /** 9.4 button colours. */
7
+ export type ButtonStyle = "danger" | "success" | "primary";
8
+ interface ButtonBase {
9
+ text: string;
10
+ style?: ButtonStyle;
11
+ icon_custom_emoji_id?: string;
12
+ /** 10.3: shown but not pressable. */
13
+ disabled?: boolean;
14
+ }
15
+ /** Exactly one action field per button (the ones the plugin uses). */
16
+ export type InlineKeyboardButton = ButtonBase & ({
17
+ callback_data: string;
18
+ } | {
19
+ url: string;
20
+ } | {
21
+ copy_text: {
22
+ text: string;
23
+ };
24
+ } | {
25
+ switch_inline_query_current_chat: string;
26
+ });
27
+ /** Bot API `InlineKeyboardMarkup`. */
28
+ export interface InlineKeyboardMarkup {
29
+ inline_keyboard: InlineKeyboardButton[][];
30
+ }
31
+ /** Bot API `KeyboardButton` (text-only subset). */
32
+ export interface KeyboardButton {
33
+ text: string;
34
+ style?: ButtonStyle;
35
+ }
36
+ /** Bot API `ReplyKeyboardMarkup` (custom reply keyboard). */
37
+ export interface ReplyKeyboardMarkup {
38
+ keyboard: KeyboardButton[][];
39
+ is_persistent?: boolean;
40
+ resize_keyboard?: boolean;
41
+ one_time_keyboard?: boolean;
42
+ input_field_placeholder?: string;
43
+ selective?: boolean;
44
+ }
45
+ /** Bot API `ReplyKeyboardRemove`. */
46
+ export interface ReplyKeyboardRemove {
47
+ remove_keyboard: true;
48
+ selective?: boolean;
49
+ }
50
+ /** Bot API `ForceReply` (used by dialogs to ask for an answer). */
51
+ export interface ForceReply {
52
+ force_reply: true;
53
+ input_field_placeholder?: string;
54
+ selective?: boolean;
55
+ }
56
+ /** Any `reply_markup` value. */
57
+ export type ReplyMarkup = InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply;
58
+ /** Bot API `LinkPreviewOptions`. */
59
+ export interface LinkPreviewOptions {
60
+ is_disabled?: boolean;
61
+ url?: string;
62
+ prefer_small_media?: boolean;
63
+ prefer_large_media?: boolean;
64
+ show_above_text?: boolean;
65
+ }
66
+ /** Bot API `ReplyParameters` (reply to a message). */
67
+ export interface ReplyParameters {
68
+ message_id: number;
69
+ chat_id?: ChatId;
70
+ allow_sending_without_reply?: boolean;
71
+ }
72
+ /** Options shared by the send methods. */
73
+ export interface SendOptions {
74
+ message_thread_id?: number;
75
+ disable_notification?: boolean;
76
+ protect_content?: boolean;
77
+ reply_parameters?: ReplyParameters;
78
+ reply_markup?: ReplyMarkup;
79
+ }
80
+ /** `sendMessage` params (text ≤ 4096 chars after entity parsing). */
81
+ export interface SendMessageParams extends SendOptions {
82
+ chat_id: ChatId;
83
+ text: string;
84
+ parse_mode?: ParseMode;
85
+ link_preview_options?: LinkPreviewOptions;
86
+ }
87
+ /** A file to upload with multipart. `data` is read once; a stream is buffered into a Blob before sending. */
88
+ export interface InputFileUpload {
89
+ data: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array>;
90
+ filename: string;
91
+ mime?: string;
92
+ }
93
+ /** An upload, or a `file_id` / HTTP URL string already known to Telegram. */
94
+ export type InputFile = InputFileUpload | string;
95
+ /** `sendDocument` params (caption ≤ 1024 chars; uploads ≤ 50 MB). */
96
+ export interface SendDocumentParams extends SendOptions {
97
+ chat_id: ChatId;
98
+ document: InputFile;
99
+ caption?: string;
100
+ parse_mode?: ParseMode;
101
+ disable_content_type_detection?: boolean;
102
+ }
103
+ /** `sendPhoto` params (caption ≤ 1024 chars). */
104
+ export interface SendPhotoParams extends SendOptions {
105
+ chat_id: ChatId;
106
+ photo: InputFile;
107
+ caption?: string;
108
+ parse_mode?: ParseMode;
109
+ has_spoiler?: boolean;
110
+ }
111
+ /** `sendChatAction` actions the plugin uses. */
112
+ export type ChatAction = "typing" | "upload_document" | "upload_photo" | "record_voice" | "find_location";
113
+ /** `sendChatAction` params. */
114
+ export interface SendChatActionParams {
115
+ chat_id: ChatId;
116
+ action: ChatAction;
117
+ message_thread_id?: number;
118
+ }
119
+ /** `editMessageText` params. */
120
+ export interface EditMessageTextParams {
121
+ chat_id: ChatId;
122
+ message_id: number;
123
+ text: string;
124
+ parse_mode?: ParseMode;
125
+ link_preview_options?: LinkPreviewOptions;
126
+ reply_markup?: InlineKeyboardMarkup;
127
+ }
128
+ /** `editMessageReplyMarkup` params; omit `reply_markup` to remove the keyboard. */
129
+ export interface EditMessageReplyMarkupParams {
130
+ chat_id: ChatId;
131
+ message_id: number;
132
+ reply_markup?: InlineKeyboardMarkup;
133
+ }
134
+ /** `deleteMessage` params. */
135
+ export interface DeleteMessageParams {
136
+ chat_id: ChatId;
137
+ message_id: number;
138
+ }
139
+ /** `answerCallbackQuery` params (`text` ≤ 200 chars, shown as a toast or alert). */
140
+ export interface AnswerCallbackQueryParams {
141
+ callback_query_id: string;
142
+ text?: string;
143
+ show_alert?: boolean;
144
+ url?: string;
145
+ cache_time?: number;
146
+ }
147
+ /** Update field names for `allowed_updates` (open-ended for newer types). */
148
+ export type UpdateType = "message" | "edited_message" | "callback_query" | "my_chat_member" | "chat_member" | "channel_post" | "edited_channel_post" | "inline_query" | "chat_join_request" | (string & {});
149
+ /** `setWebhook` params; `secret_token` comes back as `X-Telegram-Bot-Api-Secret-Token`. */
150
+ export interface SetWebhookParams {
151
+ url: string;
152
+ secret_token?: string;
153
+ allowed_updates?: UpdateType[];
154
+ max_connections?: number;
155
+ drop_pending_updates?: boolean;
156
+ }
157
+ /** `deleteWebhook` params. */
158
+ export interface DeleteWebhookParams {
159
+ drop_pending_updates?: boolean;
160
+ }
161
+ /** `getWebhookInfo` result (dates are Unix seconds). */
162
+ export interface WebhookInfo {
163
+ url: string;
164
+ has_custom_certificate: boolean;
165
+ pending_update_count: number;
166
+ ip_address?: string;
167
+ last_error_date?: number;
168
+ last_error_message?: string;
169
+ last_synchronization_error_date?: number;
170
+ max_connections?: number;
171
+ allowed_updates?: string[];
172
+ }
173
+ /** `getUpdates` params (polling mode). */
174
+ export interface GetUpdatesParams {
175
+ offset?: number;
176
+ limit?: number;
177
+ /** Long-poll seconds; keep it below the client timeout. */
178
+ timeout?: number;
179
+ allowed_updates?: UpdateType[];
180
+ }
181
+ /** One menu command for `setMyCommands`. */
182
+ export interface BotCommand {
183
+ command: string;
184
+ description: string;
185
+ /** 10.2: the user's command is hidden from other group members. */
186
+ is_ephemeral?: boolean;
187
+ }
188
+ /** Bot API `BotCommandScope`: which chats/users a command list applies to. */
189
+ export type BotCommandScope = {
190
+ type: "default";
191
+ } | {
192
+ type: "all_private_chats";
193
+ } | {
194
+ type: "all_group_chats";
195
+ } | {
196
+ type: "all_chat_administrators";
197
+ } | {
198
+ type: "chat";
199
+ chat_id: ChatId;
200
+ } | {
201
+ type: "chat_administrators";
202
+ chat_id: ChatId;
203
+ } | {
204
+ type: "chat_member";
205
+ chat_id: ChatId;
206
+ user_id: number;
207
+ };
208
+ /** `setMyCommands` params (`language_code` = ISO 639-1, or omitted for the default list). */
209
+ export interface SetMyCommandsParams {
210
+ commands: BotCommand[];
211
+ scope?: BotCommandScope;
212
+ language_code?: string;
213
+ }
214
+ /** Scope + language selector for `getMyCommands` / `deleteMyCommands`. */
215
+ export interface CommandScopeParams {
216
+ scope?: BotCommandScope;
217
+ language_code?: string;
218
+ }
219
+ /** Bot user from getMe. */
220
+ export interface BotUser extends TgUser {
221
+ is_bot: true;
222
+ username: string;
223
+ can_join_groups?: boolean;
224
+ can_read_all_group_messages?: boolean;
225
+ supports_inline_queries?: boolean;
226
+ }
227
+ /** Bot API `File` from `getFile`; download it with `BotApi.downloadFile(file_path)`. */
228
+ export interface TgFile {
229
+ file_id: string;
230
+ file_unique_id: string;
231
+ file_size?: number;
232
+ file_path?: string;
233
+ }
234
+ /** Bot API `ChatFullInfo` from `getChat` (subset; other fields pass through). */
235
+ export interface ChatFullInfo {
236
+ id: number;
237
+ type: string;
238
+ title?: string;
239
+ username?: string;
240
+ first_name?: string;
241
+ last_name?: string;
242
+ is_forum?: boolean;
243
+ [key: string]: unknown;
244
+ }
245
+ /** Results of every typed method (keys are Bot API method names). */
246
+ export interface MethodResults {
247
+ getMe: BotUser;
248
+ setWebhook: true;
249
+ deleteWebhook: true;
250
+ getWebhookInfo: WebhookInfo;
251
+ getUpdates: unknown[];
252
+ setMyCommands: true;
253
+ deleteMyCommands: true;
254
+ getMyCommands: BotCommand[];
255
+ sendMessage: TgMessage;
256
+ sendDocument: TgMessage;
257
+ sendPhoto: TgMessage;
258
+ sendChatAction: true;
259
+ /** A Message, or `true` for inline messages. */
260
+ editMessageText: TgMessage | true;
261
+ editMessageReplyMarkup: TgMessage | true;
262
+ deleteMessage: true;
263
+ answerCallbackQuery: true;
264
+ getFile: TgFile;
265
+ getChat: ChatFullInfo;
266
+ getChatMember: TgChatMember;
267
+ leaveChat: true;
268
+ }
269
+ /** Names of the typed Bot API methods. */
270
+ export type MethodName = keyof MethodResults;
271
+ export {};