@particle-academy/telegram-js 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@particle-academy/telegram-ui`,
4
+ `@particle-academy/telegram-js`, `particle-academy/telegram-php` and
5
+ `fancy-telegram`.
6
+
7
+ The four packages share one version, because they are generated from one
8
+ `provider/` definition and a version that meant something different in each
9
+ would be a version nobody could reason about.
10
+
11
+ ## [0.1.0] — 2026-08-20
12
+
13
+ First release. Ported from the vendored connector at
14
+ `px-ui-sandbox/resources/flow-nodes/_telegram`.
15
+
16
+ ### Added
17
+
18
+ - `get_updates` — start a run when a bot receives an update. A **poll**
19
+ trigger, not a webhook: `GET /getUpdates` with a persisted `offset` cursor.
20
+ - A faker for it, so the node runs on a canvas before any bot exists.
21
+
22
+ ### The reason this provider exists in the set
23
+
24
+ **It breaks three assumptions Stripe never tested.**
25
+
26
+ 1. **The trigger is not a webhook.** Telegram offers `getUpdates` long polling
27
+ OR `setWebhook`, never both for one bot. So the host's obligation here is a
28
+ schedule and a persisted cursor rather than a route and a signature — and a
29
+ poll trigger *calls* the provider, where a webhook trigger only republishes
30
+ what the host already verified. `delivery` was a declaration rather than an
31
+ assumption for exactly this case.
32
+
33
+ 2. **The credential is a path segment.** `https://api.telegram.org/bot<token>/getUpdates`
34
+ — and the test environment is a further `/test` after the token. Auth and
35
+ estate are the same decision expressed in the URL, which is why `authorize`
36
+ has always been handed the resolved mode even though Stripe never used it.
37
+
38
+ 3. **A rejection arrives as HTTP 200.** Telegram answers `200` with
39
+ `{"ok": false, "description": "..."}`. A status check alone reads that as
40
+ success and publishes an empty batch — a poll that silently finds nothing,
41
+ forever.
42
+
43
+ ### The cursor is the part that bites
44
+
45
+ `offset` means "the first update id I have NOT handled", so the next cursor is
46
+ the highest `update_id` seen plus one. Off by one in either direction is a real
47
+ bug with no error attached: **too low replays updates forever, too high drops
48
+ one silently**. The trigger computes it and publishes it as `cursor`; the host
49
+ must persist it, because nothing else will.
50
+
51
+ An empty poll is the NORMAL case and gets its own `empty` port, so the main
52
+ path keeps meaning "something happened" while the host still has somewhere to
53
+ hang cursor bookkeeping.
54
+
55
+ ### On not taking a dependency
56
+
57
+ There is no official Telegram SDK in any language. Calling the REST API
58
+ directly is therefore the correct choice here rather than a shortcut — the
59
+ alternative is a community wrapper, which is exactly the dependency the kit's
60
+ rules say not to take on a consumer's behalf.
61
+
62
+ [0.1.0]: https://github.com/Fancy-Friends/telegram/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Particle Academy
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,51 @@
1
+ # Telegram
2
+
3
+ Telegram for [fancy-flow][flow] — as **four imported, versioned packages**, one
4
+ per runtime. Not vendored source: a copy cannot be upgraded, and third-party APIs
5
+ change.
6
+
7
+ [flow]: https://github.com/Particle-Academy/fancy-flow
8
+
9
+ | Runtime | Package | Install |
10
+ |---|---|---|
11
+ | Authoring surface (every host) | `@particle-academy/telegram-ui` | `npm install @particle-academy/telegram-ui` |
12
+ | Node | `@particle-academy/telegram-js` | `npm install @particle-academy/telegram-js` |
13
+ | PHP 8.4+ | `particle-academy/telegram-php` | `composer require particle-academy/telegram-php` |
14
+ | Python 3.11+ | `fancy-telegram` | `pip install fancy-telegram` |
15
+
16
+ The `ui` package is the editor surface and is React on every host — a PHP or
17
+ Python project installs it *and* its own runtime package, and never the `js` one.
18
+
19
+ ## What it costs you
20
+
21
+ One dependency: `@particle-academy/fancy-connector-core` (or
22
+ `particle-academy/fancy-connector-core` on Composer), which the `js` and `php`
23
+ packages pull in themselves. The Python package has **zero** runtime
24
+ dependencies.
25
+
26
+ **No Telegram SDK.** Plain HTTP, deliberately: a vendor SDK is third-party code
27
+ subject to the kit's full approval bar, and one per provider is hundreds of
28
+ dependencies nobody is tracking.
29
+
30
+ ## Run it before you have credentials
31
+
32
+ Every operation ships a **faker**, whether or not Telegram has a sandbox. Set a
33
+ node's mode to `fake` and it returns the shape Telegram actually publishes — the
34
+ same field names, deterministically — so you can wire the downstream nodes before
35
+ touching an account, a key, or a network.
36
+
37
+ ## This repository is generated
38
+
39
+ `provider/` is the source. Everything under `packages/` is emitted from it and
40
+ **must not be hand-edited** — CI regenerates and diffs on every push, and the
41
+ next protocol sync destroys anything it finds. See [`AGENTS.md`](AGENTS.md).
42
+
43
+ ## Two namespaces, which do not match on purpose
44
+
45
+ The repo is `github.com/Fancy-Friends/telegram`; the packages publish under
46
+ `particle-academy`. Nothing derives one from the other — the names come from
47
+ weaver's `friends.json` and nowhere else.
48
+
49
+ ## Licence
50
+
51
+ MIT.
@@ -0,0 +1,124 @@
1
+ import { callConnector } from '@particle-academy/fancy-connector-core';
2
+
3
+ // src/faker.ts
4
+ function fakeGetUpdates({ config, fake }) {
5
+ const boundChatId = fake.int(1e8, 999999999);
6
+ return {
7
+ "ok": true,
8
+ "result": [
9
+ {
10
+ "update_id": fake.int(1e5, 999999),
11
+ "message": {
12
+ "message_id": fake.int(1, 9999),
13
+ "date": 1767225600,
14
+ "text": config.sampleText !== void 0 && config.sampleText !== null && config.sampleText !== "" ? String(config.sampleText) : "hello from the faker",
15
+ "chat": {
16
+ "id": boundChatId,
17
+ "type": "private",
18
+ "first_name": "Ada",
19
+ "username": "ada_example"
20
+ },
21
+ "from": {
22
+ "id": boundChatId,
23
+ "is_bot": false,
24
+ "first_name": "Ada",
25
+ "username": "ada_example",
26
+ "language_code": "en"
27
+ }
28
+ }
29
+ }
30
+ ]
31
+ };
32
+ }
33
+ var telegramFaker = (operation, request) => {
34
+ switch (operation) {
35
+ case "get_updates":
36
+ return fakeGetUpdates(request);
37
+ default:
38
+ throw new Error(
39
+ `telegram: no fake response is defined for "${operation}". Add a fixture under provider/fixtures/ and regenerate \u2014 a connector without a faker cannot be developed against, tested, or demonstrated.`
40
+ );
41
+ }
42
+ };
43
+
44
+ // src/service.ts
45
+ var CONNECTOR_API_VERSION = 1;
46
+ var TELEGRAM_BASE_URLS = {
47
+ "live": "https://api.telegram.org",
48
+ "sandbox": "https://api.telegram.org"
49
+ };
50
+ var TELEGRAM_REQUIRES = [
51
+ "botToken"
52
+ ];
53
+ function telegramAuthorize(credentials, request, mode) {
54
+ const url = new URL(request.url);
55
+ const segment = mode === "sandbox" ? `/bot${credentials.botToken ?? ""}/test` : `/bot${credentials.botToken ?? ""}`;
56
+ url.pathname = `${segment}${url.pathname}`;
57
+ request.url = url.toString();
58
+ }
59
+ var TELEGRAM = {
60
+ service: "telegram",
61
+ title: "Telegram",
62
+ sandbox: "separate-account",
63
+ baseUrls: { ...TELEGRAM_BASE_URLS },
64
+ requires: [...TELEGRAM_REQUIRES],
65
+ authorize: telegramAuthorize,
66
+ faker: telegramFaker
67
+ };
68
+ var TELEGRAM_GET_UPDATES = {
69
+ service: "telegram",
70
+ operation: "get_updates",
71
+ delivery: "poll",
72
+ setup: "The host polls getUpdates on a schedule and persists the `offset` cursor between calls, passing the last `cursor` back in \u2014 Telegram queues nothing once an offset has acknowledged it, so a lost cursor is lost updates. getUpdates and setWebhook are MUTUALLY EXCLUSIVE for one bot: a host running both gets neither.",
73
+ minPollSeconds: 1,
74
+ faker: telegramFaker
75
+ };
76
+ var GET_UPDATES_OPERATION = "get_updates";
77
+ async function telegramGetUpdates(options) {
78
+ const config = options.config ?? {};
79
+ {
80
+ const n = Number(config.offset);
81
+ const given = config.offset !== void 0 && config.offset !== null && config.offset !== "";
82
+ if (given && !Number.isInteger(n)) {
83
+ throw new Error(
84
+ `get_updates: "offset" must be a integer, got ${JSON.stringify(config.offset)}.`
85
+ );
86
+ }
87
+ }
88
+ {
89
+ const n = Number(config.limit);
90
+ const given = config.limit !== void 0 && config.limit !== null && config.limit !== "";
91
+ if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {
92
+ throw new Error(
93
+ `get_updates: "limit" must be a integer, got ${JSON.stringify(config.limit)}.`
94
+ );
95
+ }
96
+ }
97
+ return callConnector(TELEGRAM, {
98
+ operation: GET_UPDATES_OPERATION,
99
+ config,
100
+ input: options.input,
101
+ ...options.credentials === void 0 ? {} : { credentials: options.credentials },
102
+ ...options.mode === void 0 ? {} : { mode: options.mode },
103
+ ...options.connectionId === void 0 ? {} : { connectionId: options.connectionId },
104
+ ...options.attempts === void 0 ? {} : { attempts: options.attempts },
105
+ ...options.transport === void 0 ? {} : { transport: options.transport },
106
+ request: {
107
+ method: "GET",
108
+ path: "/getUpdates",
109
+ query: {
110
+ ...config.offset !== void 0 && config.offset !== null && config.offset !== "" ? { "offset": Math.trunc(Number(config.offset)) } : {},
111
+ "limit": config.limit !== void 0 && config.limit !== null && config.limit !== "" ? Math.trunc(Number(config.limit)) : 100,
112
+ ...config.allowedUpdates !== void 0 && config.allowedUpdates !== null && config.allowedUpdates !== "" ? { "allowed_updates": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}
113
+ }
114
+ }
115
+ });
116
+ }
117
+ function allowedUpdatesList(value) {
118
+ const items = Array.isArray(value) ? value.map(String) : typeof value === "string" ? value.split(",") : [];
119
+ return items.map((item) => item.trim()).filter(Boolean);
120
+ }
121
+
122
+ export { CONNECTOR_API_VERSION, GET_UPDATES_OPERATION, TELEGRAM, TELEGRAM_BASE_URLS, TELEGRAM_GET_UPDATES, TELEGRAM_REQUIRES, telegramAuthorize, telegramFaker, telegramGetUpdates };
123
+ //# sourceMappingURL=chunk-LFMQKSUM.js.map
124
+ //# sourceMappingURL=chunk-LFMQKSUM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/faker.ts","../src/service.ts","../src/triggers/get-updates.ts"],"names":[],"mappings":"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","file":"chunk-LFMQKSUM.js","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/fixtures/ by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/fixtures/ (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * The Telegram faker.\n *\n * Shapes, not behaviour: the goal is that a downstream node sees the field\n * NAMES Telegram actually publishes, so an author can wire {{ $json.data.id }}\n * against a fake and have it keep working against the real thing.\n *\n * Deterministic — same inputs, same output. A faker returning a fresh uuid\n * every call cannot be asserted on, so its fixtures degrade to \"it did not\n * throw\", which is the assertion that catches nothing.\n */\n\nimport type { ConnectorFaker, FakeRequest } from \"@particle-academy/fancy-connector-core\";\n\nfunction fakeGetUpdates({ config, fake }: FakeRequest): unknown {\n const boundChatId = fake.int(100000000, 999999999);\n\n return {\n \"ok\": true,\n \"result\": [\n {\n \"update_id\": fake.int(100000, 999999),\n \"message\": {\n \"message_id\": fake.int(1, 9999),\n \"date\": 1767225600,\n \"text\": (config.sampleText !== undefined && config.sampleText !== null && config.sampleText !== \"\" ? String(config.sampleText) : \"hello from the faker\"),\n \"chat\": {\n \"id\": boundChatId,\n \"type\": \"private\",\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n },\n \"from\": {\n \"id\": boundChatId,\n \"is_bot\": false,\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n \"language_code\": \"en\",\n },\n },\n },\n ],\n };\n}\n\nexport const telegramFaker: ConnectorFaker = (operation, request) => {\n switch (operation) {\n case \"get_updates\":\n return fakeGetUpdates(request);\n\n default:\n // A faker asked for an operation it has no shape for must SAY so. Making\n // something up would produce a green run whose output silently has none\n // of the fields the author is about to reference.\n throw new Error(\n `telegram: no fake response is defined for \"${operation}\". ` +\n \"Add a fixture under provider/fixtures/ and regenerate — a connector without a faker \" +\n \"cannot be developed against, tested, or demonstrated.\",\n );\n }\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram, as one service descriptor shared by every Telegram operation.\n *\n * @particle-academy/fancy-connector-core carries what is true of ALL\n * connectors. This carries what is true of Telegram: its base URL, its auth\n * scheme, its idempotency header, and its faker.\n *\n * ## The sandbox trap, written down where it is used\n *\n * Telegram's test environment is a genuinely SEPARATE ACCOUNT: you create a\n * new account inside it and register a new bot there, so the sandbox\n * credential is a different token rather than the same one pointed elsewhere.\n * The `/test` path segment is how you reach it; the account is what makes it\n * separate. Flood limits are NOT relaxed there, so it is a place to test, not\n * a place to hammer.\n */\n\nimport type { ConnectorMode, PreparedRequest, ServiceDescriptor } from \"@particle-academy/fancy-connector-core\";\n\nimport { telegramFaker } from \"./faker.js\";\n\n/**\n * The connector API version this package was GENERATED against.\n *\n * A literal, never imported. An imported constant lets an upgrade rewrite the\n * very claim it exists to detect, after which the copy agrees with itself\n * forever.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\nexport const TELEGRAM_BASE_URLS = {\n \"live\": \"https://api.telegram.org\",\n \"sandbox\": \"https://api.telegram.org\"\n} as const;\n\n/** Credential keys a remote call cannot proceed without. */\nexport const TELEGRAM_REQUIRES = [\n \"botToken\"\n] as const;\n\n/**\n * Apply Telegram's auth scheme to an outgoing request.\n *\n * The bot token is a PATH SEGMENT, not a header --\n * https://api.telegram.org/bot<token>/getUpdates -- and the test environment\n * is a further `/test` AFTER the token. So this is the first provider whose\n * auth and whose estate are the same decision expressed in the URL, which is\n * why `authorize` has always been handed the resolved mode. The token\n * therefore ends up in the request URL, where access logs and error reporters\n * will record it. That is Telegram's design, not ours; a host should keep its\n * own logging away from it.\n *\n * The mode is USED here: for this provider auth and estate are the same\n * decision expressed in the URL, which is what this parameter has always been\n * for.\n */\nexport function telegramAuthorize(\n credentials: Record<string, string | undefined>,\n request: PreparedRequest,\n mode: ConnectorMode,\n): void {\n const url = new URL(request.url);\n\n // The estate is the SAME decision as the credential here, and it lives in\n // a further segment AFTER the token. A token pointed at a node marked\n // \"sandbox\" would otherwise reach the live bot, and succeed.\n const segment = mode === \"sandbox\"\n ? `/bot${credentials.botToken ?? \"\"}/test`\n : `/bot${credentials.botToken ?? \"\"}`;\n\n url.pathname = `${segment}${url.pathname}`;\n request.url = url.toString();\n}\n\n/** The Telegram service, for the TypeScript runtime. */\nexport const TELEGRAM: ServiceDescriptor = {\n service: \"telegram\",\n title: \"Telegram\",\n sandbox: \"separate-account\",\n baseUrls: { ...TELEGRAM_BASE_URLS },\n requires: [...TELEGRAM_REQUIRES],\n authorize: telegramAuthorize,\n faker: telegramFaker,\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/triggers/get-updates.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/triggers/get-updates.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram's poll trigger — the delivery contract.\n *\n * A POLL trigger, so there is no signature and no route: Telegram does not\n * push, the host asks on a schedule and persists a cursor between calls.\n * Nothing queues what a stopped poller did not collect, which is why that\n * obligation is written down rather than assumed.\n */\n\nimport {\n callConnector,\n type ConnectorResult,\n type RequestedMode,\n type Transport,\n type TriggerDescriptor,\n} from \"@particle-academy/fancy-connector-core\";\nimport { telegramFaker } from \"../faker.js\";\nimport { TELEGRAM } from \"../service.js\";\n\nexport const TELEGRAM_GET_UPDATES: TriggerDescriptor = {\n service: \"telegram\",\n operation: \"get_updates\",\n delivery: \"poll\",\n setup:\n \"The host polls getUpdates on a schedule and persists the `offset` cursor between calls, passing the last `cursor` back in — Telegram queues nothing once an offset has acknowledged it, so a lost cursor is lost updates. getUpdates and setWebhook are MUTUALLY EXCLUSIVE for one bot: a host running both gets neither.\",\n minPollSeconds: 1,\n faker: telegramFaker,\n};\n\n/**\n * Poll Telegram once.\n *\n * GET /getUpdates — https://core.telegram.org/bots/api#getupdates\n *\n * In `fake` mode this resolves to the faker, so the node is runnable on a\n * canvas before any credential exists — with the same envelope a real poll\n * returns.\n */\n\nexport const GET_UPDATES_OPERATION = \"get_updates\";\n\nexport type GetUpdatesOptions = {\n /** The node's resolved config. Keys: offset, limit, allowedUpdates, sampleText. */\n config: Record<string, unknown>;\n credentials?: Record<string, string | undefined>;\n mode?: RequestedMode;\n connectionId?: string | null;\n input?: unknown;\n attempts?: number;\n /** Override the transport. The only way to exercise this without a network. */\n transport?: Transport;\n};\n\nexport async function telegramGetUpdates(options: GetUpdatesOptions): Promise<ConnectorResult> {\n const config = options.config ?? {};\n\n {\n const n = Number(config.offset);\n const given = config.offset !== undefined && config.offset !== null && config.offset !== \"\";\n if (given && !(Number.isInteger(n))) {\n throw new Error(\n `get_updates: \"offset\" must be a integer, got ${JSON.stringify(config.offset)}.`,\n );\n }\n }\n\n {\n const n = Number(config.limit);\n const given = config.limit !== undefined && config.limit !== null && config.limit !== \"\";\n if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {\n throw new Error(\n `get_updates: \"limit\" must be a integer, got ${JSON.stringify(config.limit)}.`,\n );\n }\n }\n\n return callConnector(TELEGRAM, {\n operation: GET_UPDATES_OPERATION,\n config,\n input: options.input,\n ...(options.credentials === undefined ? {} : { credentials: options.credentials }),\n ...(options.mode === undefined ? {} : { mode: options.mode }),\n ...(options.connectionId === undefined ? {} : { connectionId: options.connectionId }),\n ...(options.attempts === undefined ? {} : { attempts: options.attempts }),\n ...(options.transport === undefined ? {} : { transport: options.transport }),\n request: {\n method: \"GET\",\n path: \"/getUpdates\",\n query: {\n ...(config.offset !== undefined && config.offset !== null && config.offset !== \"\" ? { \"offset\": Math.trunc(Number(config.offset)) } : {}),\n \"limit\": config.limit !== undefined && config.limit !== null && config.limit !== \"\" ? Math.trunc(Number(config.limit)) : 100,\n ...(config.allowedUpdates !== undefined && config.allowedUpdates !== null && config.allowedUpdates !== \"\" ? { \"allowed_updates\": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}),\n },\n },\n });\n}\n\n/** One value, a \",\"-separated string, or an array — all end up a list. */\nfunction allowedUpdatesList(value: unknown): string[] {\n const items = Array.isArray(value)\n ? value.map(String)\n : typeof value === \"string\"\n ? value.split(\",\")\n : [];\n\n return items.map((item) => item.trim()).filter(Boolean);\n}\n"]}
package/dist/flow.cjs ADDED
@@ -0,0 +1,158 @@
1
+ 'use strict';
2
+
3
+ var fancyConnectorCore = require('@particle-academy/fancy-connector-core');
4
+ var telegramUi = require('@particle-academy/telegram-ui');
5
+
6
+ // src/flow.ts
7
+
8
+ // src/faker.ts
9
+ function fakeGetUpdates({ config, fake }) {
10
+ const boundChatId = fake.int(1e8, 999999999);
11
+ return {
12
+ "ok": true,
13
+ "result": [
14
+ {
15
+ "update_id": fake.int(1e5, 999999),
16
+ "message": {
17
+ "message_id": fake.int(1, 9999),
18
+ "date": 1767225600,
19
+ "text": config.sampleText !== void 0 && config.sampleText !== null && config.sampleText !== "" ? String(config.sampleText) : "hello from the faker",
20
+ "chat": {
21
+ "id": boundChatId,
22
+ "type": "private",
23
+ "first_name": "Ada",
24
+ "username": "ada_example"
25
+ },
26
+ "from": {
27
+ "id": boundChatId,
28
+ "is_bot": false,
29
+ "first_name": "Ada",
30
+ "username": "ada_example",
31
+ "language_code": "en"
32
+ }
33
+ }
34
+ }
35
+ ]
36
+ };
37
+ }
38
+ var telegramFaker = (operation, request) => {
39
+ switch (operation) {
40
+ case "get_updates":
41
+ return fakeGetUpdates(request);
42
+ default:
43
+ throw new Error(
44
+ `telegram: no fake response is defined for "${operation}". Add a fixture under provider/fixtures/ and regenerate \u2014 a connector without a faker cannot be developed against, tested, or demonstrated.`
45
+ );
46
+ }
47
+ };
48
+
49
+ // src/service.ts
50
+ var TELEGRAM_BASE_URLS = {
51
+ "live": "https://api.telegram.org",
52
+ "sandbox": "https://api.telegram.org"
53
+ };
54
+ var TELEGRAM_REQUIRES = [
55
+ "botToken"
56
+ ];
57
+ function telegramAuthorize(credentials, request, mode) {
58
+ const url = new URL(request.url);
59
+ const segment = mode === "sandbox" ? `/bot${credentials.botToken ?? ""}/test` : `/bot${credentials.botToken ?? ""}`;
60
+ url.pathname = `${segment}${url.pathname}`;
61
+ request.url = url.toString();
62
+ }
63
+ var TELEGRAM = {
64
+ service: "telegram",
65
+ title: "Telegram",
66
+ sandbox: "separate-account",
67
+ baseUrls: { ...TELEGRAM_BASE_URLS },
68
+ requires: [...TELEGRAM_REQUIRES],
69
+ authorize: telegramAuthorize,
70
+ faker: telegramFaker
71
+ };
72
+ var GET_UPDATES_OPERATION = "get_updates";
73
+ async function telegramGetUpdates(options) {
74
+ const config = options.config ?? {};
75
+ {
76
+ const n = Number(config.offset);
77
+ const given = config.offset !== void 0 && config.offset !== null && config.offset !== "";
78
+ if (given && !Number.isInteger(n)) {
79
+ throw new Error(
80
+ `get_updates: "offset" must be a integer, got ${JSON.stringify(config.offset)}.`
81
+ );
82
+ }
83
+ }
84
+ {
85
+ const n = Number(config.limit);
86
+ const given = config.limit !== void 0 && config.limit !== null && config.limit !== "";
87
+ if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {
88
+ throw new Error(
89
+ `get_updates: "limit" must be a integer, got ${JSON.stringify(config.limit)}.`
90
+ );
91
+ }
92
+ }
93
+ return fancyConnectorCore.callConnector(TELEGRAM, {
94
+ operation: GET_UPDATES_OPERATION,
95
+ config,
96
+ input: options.input,
97
+ ...options.credentials === void 0 ? {} : { credentials: options.credentials },
98
+ ...options.mode === void 0 ? {} : { mode: options.mode },
99
+ ...options.connectionId === void 0 ? {} : { connectionId: options.connectionId },
100
+ ...options.attempts === void 0 ? {} : { attempts: options.attempts },
101
+ ...options.transport === void 0 ? {} : { transport: options.transport },
102
+ request: {
103
+ method: "GET",
104
+ path: "/getUpdates",
105
+ query: {
106
+ ...config.offset !== void 0 && config.offset !== null && config.offset !== "" ? { "offset": Math.trunc(Number(config.offset)) } : {},
107
+ "limit": config.limit !== void 0 && config.limit !== null && config.limit !== "" ? Math.trunc(Number(config.limit)) : 100,
108
+ ...config.allowedUpdates !== void 0 && config.allowedUpdates !== null && config.allowedUpdates !== "" ? { "allowed_updates": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}
109
+ }
110
+ }
111
+ });
112
+ }
113
+ function allowedUpdatesList(value) {
114
+ const items = Array.isArray(value) ? value.map(String) : typeof value === "string" ? value.split(",") : [];
115
+ return items.map((item) => item.trim()).filter(Boolean);
116
+ }
117
+
118
+ // src/flow.ts
119
+ var telegramUpdatesTriggerExecutor = async (ctx) => {
120
+ const config = ctx.node.data?.config ?? {};
121
+ const result = await telegramGetUpdates({ config });
122
+ const data = result.data;
123
+ if (data?.ok === false) {
124
+ throw new Error(
125
+ `get_updates: getUpdates was rejected \u2014 ` + String(data.description ?? "no reason given")
126
+ );
127
+ }
128
+ const items = Array.isArray(data?.result) ? data.result : [];
129
+ const seen = items.map((item) => item?.update_id).filter((id) => typeof id === "number");
130
+ const previous = config.offset === void 0 || config.offset === null || config.offset === "" ? void 0 : Number(config.offset);
131
+ const cursor = seen.length === 0 ? previous : Math.max(...seen) + 1;
132
+ const value = {
133
+ mode: result.mode,
134
+ connection: result.connection,
135
+ cursor,
136
+ count: items.length,
137
+ updates: items,
138
+ update: items[0] ?? null
139
+ };
140
+ if (items.length === 0) {
141
+ return { __port: "empty", value };
142
+ }
143
+ ctx.emit({
144
+ type: "log",
145
+ level: "info",
146
+ nodeId: ctx.node.id,
147
+ message: `telegram: ${items.length} item(s), next cursor ${cursor} (${result.mode})`
148
+ });
149
+ return { __port: "out", value };
150
+ };
151
+ var TELEGRAM_RUNNABLE_KINDS = [
152
+ { ...telegramUi.telegramUpdatesTriggerKind, executor: telegramUpdatesTriggerExecutor }
153
+ ];
154
+
155
+ exports.TELEGRAM_RUNNABLE_KINDS = TELEGRAM_RUNNABLE_KINDS;
156
+ exports.telegramUpdatesTriggerExecutor = telegramUpdatesTriggerExecutor;
157
+ //# sourceMappingURL=flow.cjs.map
158
+ //# sourceMappingURL=flow.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/faker.ts","../src/service.ts","../src/triggers/get-updates.ts","../src/flow.ts"],"names":["callConnector","telegramUpdatesTriggerKind"],"mappings":"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","file":"flow.cjs","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/fixtures/ by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/fixtures/ (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * The Telegram faker.\n *\n * Shapes, not behaviour: the goal is that a downstream node sees the field\n * NAMES Telegram actually publishes, so an author can wire {{ $json.data.id }}\n * against a fake and have it keep working against the real thing.\n *\n * Deterministic — same inputs, same output. A faker returning a fresh uuid\n * every call cannot be asserted on, so its fixtures degrade to \"it did not\n * throw\", which is the assertion that catches nothing.\n */\n\nimport type { ConnectorFaker, FakeRequest } from \"@particle-academy/fancy-connector-core\";\n\nfunction fakeGetUpdates({ config, fake }: FakeRequest): unknown {\n const boundChatId = fake.int(100000000, 999999999);\n\n return {\n \"ok\": true,\n \"result\": [\n {\n \"update_id\": fake.int(100000, 999999),\n \"message\": {\n \"message_id\": fake.int(1, 9999),\n \"date\": 1767225600,\n \"text\": (config.sampleText !== undefined && config.sampleText !== null && config.sampleText !== \"\" ? String(config.sampleText) : \"hello from the faker\"),\n \"chat\": {\n \"id\": boundChatId,\n \"type\": \"private\",\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n },\n \"from\": {\n \"id\": boundChatId,\n \"is_bot\": false,\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n \"language_code\": \"en\",\n },\n },\n },\n ],\n };\n}\n\nexport const telegramFaker: ConnectorFaker = (operation, request) => {\n switch (operation) {\n case \"get_updates\":\n return fakeGetUpdates(request);\n\n default:\n // A faker asked for an operation it has no shape for must SAY so. Making\n // something up would produce a green run whose output silently has none\n // of the fields the author is about to reference.\n throw new Error(\n `telegram: no fake response is defined for \"${operation}\". ` +\n \"Add a fixture under provider/fixtures/ and regenerate — a connector without a faker \" +\n \"cannot be developed against, tested, or demonstrated.\",\n );\n }\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram, as one service descriptor shared by every Telegram operation.\n *\n * @particle-academy/fancy-connector-core carries what is true of ALL\n * connectors. This carries what is true of Telegram: its base URL, its auth\n * scheme, its idempotency header, and its faker.\n *\n * ## The sandbox trap, written down where it is used\n *\n * Telegram's test environment is a genuinely SEPARATE ACCOUNT: you create a\n * new account inside it and register a new bot there, so the sandbox\n * credential is a different token rather than the same one pointed elsewhere.\n * The `/test` path segment is how you reach it; the account is what makes it\n * separate. Flood limits are NOT relaxed there, so it is a place to test, not\n * a place to hammer.\n */\n\nimport type { ConnectorMode, PreparedRequest, ServiceDescriptor } from \"@particle-academy/fancy-connector-core\";\n\nimport { telegramFaker } from \"./faker.js\";\n\n/**\n * The connector API version this package was GENERATED against.\n *\n * A literal, never imported. An imported constant lets an upgrade rewrite the\n * very claim it exists to detect, after which the copy agrees with itself\n * forever.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\nexport const TELEGRAM_BASE_URLS = {\n \"live\": \"https://api.telegram.org\",\n \"sandbox\": \"https://api.telegram.org\"\n} as const;\n\n/** Credential keys a remote call cannot proceed without. */\nexport const TELEGRAM_REQUIRES = [\n \"botToken\"\n] as const;\n\n/**\n * Apply Telegram's auth scheme to an outgoing request.\n *\n * The bot token is a PATH SEGMENT, not a header --\n * https://api.telegram.org/bot<token>/getUpdates -- and the test environment\n * is a further `/test` AFTER the token. So this is the first provider whose\n * auth and whose estate are the same decision expressed in the URL, which is\n * why `authorize` has always been handed the resolved mode. The token\n * therefore ends up in the request URL, where access logs and error reporters\n * will record it. That is Telegram's design, not ours; a host should keep its\n * own logging away from it.\n *\n * The mode is USED here: for this provider auth and estate are the same\n * decision expressed in the URL, which is what this parameter has always been\n * for.\n */\nexport function telegramAuthorize(\n credentials: Record<string, string | undefined>,\n request: PreparedRequest,\n mode: ConnectorMode,\n): void {\n const url = new URL(request.url);\n\n // The estate is the SAME decision as the credential here, and it lives in\n // a further segment AFTER the token. A token pointed at a node marked\n // \"sandbox\" would otherwise reach the live bot, and succeed.\n const segment = mode === \"sandbox\"\n ? `/bot${credentials.botToken ?? \"\"}/test`\n : `/bot${credentials.botToken ?? \"\"}`;\n\n url.pathname = `${segment}${url.pathname}`;\n request.url = url.toString();\n}\n\n/** The Telegram service, for the TypeScript runtime. */\nexport const TELEGRAM: ServiceDescriptor = {\n service: \"telegram\",\n title: \"Telegram\",\n sandbox: \"separate-account\",\n baseUrls: { ...TELEGRAM_BASE_URLS },\n requires: [...TELEGRAM_REQUIRES],\n authorize: telegramAuthorize,\n faker: telegramFaker,\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/triggers/get-updates.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/triggers/get-updates.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram's poll trigger — the delivery contract.\n *\n * A POLL trigger, so there is no signature and no route: Telegram does not\n * push, the host asks on a schedule and persists a cursor between calls.\n * Nothing queues what a stopped poller did not collect, which is why that\n * obligation is written down rather than assumed.\n */\n\nimport {\n callConnector,\n type ConnectorResult,\n type RequestedMode,\n type Transport,\n type TriggerDescriptor,\n} from \"@particle-academy/fancy-connector-core\";\nimport { telegramFaker } from \"../faker.js\";\nimport { TELEGRAM } from \"../service.js\";\n\nexport const TELEGRAM_GET_UPDATES: TriggerDescriptor = {\n service: \"telegram\",\n operation: \"get_updates\",\n delivery: \"poll\",\n setup:\n \"The host polls getUpdates on a schedule and persists the `offset` cursor between calls, passing the last `cursor` back in — Telegram queues nothing once an offset has acknowledged it, so a lost cursor is lost updates. getUpdates and setWebhook are MUTUALLY EXCLUSIVE for one bot: a host running both gets neither.\",\n minPollSeconds: 1,\n faker: telegramFaker,\n};\n\n/**\n * Poll Telegram once.\n *\n * GET /getUpdates — https://core.telegram.org/bots/api#getupdates\n *\n * In `fake` mode this resolves to the faker, so the node is runnable on a\n * canvas before any credential exists — with the same envelope a real poll\n * returns.\n */\n\nexport const GET_UPDATES_OPERATION = \"get_updates\";\n\nexport type GetUpdatesOptions = {\n /** The node's resolved config. Keys: offset, limit, allowedUpdates, sampleText. */\n config: Record<string, unknown>;\n credentials?: Record<string, string | undefined>;\n mode?: RequestedMode;\n connectionId?: string | null;\n input?: unknown;\n attempts?: number;\n /** Override the transport. The only way to exercise this without a network. */\n transport?: Transport;\n};\n\nexport async function telegramGetUpdates(options: GetUpdatesOptions): Promise<ConnectorResult> {\n const config = options.config ?? {};\n\n {\n const n = Number(config.offset);\n const given = config.offset !== undefined && config.offset !== null && config.offset !== \"\";\n if (given && !(Number.isInteger(n))) {\n throw new Error(\n `get_updates: \"offset\" must be a integer, got ${JSON.stringify(config.offset)}.`,\n );\n }\n }\n\n {\n const n = Number(config.limit);\n const given = config.limit !== undefined && config.limit !== null && config.limit !== \"\";\n if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {\n throw new Error(\n `get_updates: \"limit\" must be a integer, got ${JSON.stringify(config.limit)}.`,\n );\n }\n }\n\n return callConnector(TELEGRAM, {\n operation: GET_UPDATES_OPERATION,\n config,\n input: options.input,\n ...(options.credentials === undefined ? {} : { credentials: options.credentials }),\n ...(options.mode === undefined ? {} : { mode: options.mode }),\n ...(options.connectionId === undefined ? {} : { connectionId: options.connectionId }),\n ...(options.attempts === undefined ? {} : { attempts: options.attempts }),\n ...(options.transport === undefined ? {} : { transport: options.transport }),\n request: {\n method: \"GET\",\n path: \"/getUpdates\",\n query: {\n ...(config.offset !== undefined && config.offset !== null && config.offset !== \"\" ? { \"offset\": Math.trunc(Number(config.offset)) } : {}),\n \"limit\": config.limit !== undefined && config.limit !== null && config.limit !== \"\" ? Math.trunc(Number(config.limit)) : 100,\n ...(config.allowedUpdates !== undefined && config.allowedUpdates !== null && config.allowedUpdates !== \"\" ? { \"allowed_updates\": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}),\n },\n },\n });\n}\n\n/** One value, a \",\"-separated string, or an array — all end up a list. */\nfunction allowedUpdatesList(value: unknown): string[] {\n const items = Array.isArray(value)\n ? value.map(String)\n : typeof value === \"string\"\n ? value.split(\",\")\n : [];\n\n return items.map((item) => item.trim()).filter(Boolean);\n}\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/actions/ + triggers/ by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/actions/ + triggers/ (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram's node kinds with their TypeScript executors attached — for hosts\n * that EXECUTE on TS.\n *\n * The authoring surface in @particle-academy/telegram-ui carries no executor:\n * the editor is React on every host, so a PHP or Python project installs the\n * ui package and never this one.\n */\n\nimport type { NodeExecutor, NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport {\n idempotencyKeyFor,\n NO_IDEMPOTENCY_KEY_WARNING,\n resolveConnection,\n triggerEvent,\n type RequestedMode,\n} from \"@particle-academy/fancy-connector-core\";\nimport { TELEGRAM } from \"./service.js\";\n\nimport {\n telegramUpdatesTriggerKind,\n} from \"@particle-academy/telegram-ui\";\n\nimport { TELEGRAM_GET_UPDATES, telegramGetUpdates } from \"./triggers/get-updates.js\";\n\nexport const telegramUpdatesTriggerExecutor: NodeExecutor = async (ctx) => {\n const config = ((ctx.node.data as { config?: Record<string, unknown> })?.config ?? {});\n\n const result = await telegramGetUpdates({ config });\n const data = result.data as Record<string, unknown> | undefined;\n\n // Telegram answers HTTP 200 with `{ok: false}` for an application-level\n // failure. A status check alone reads that as success and publishes an\n // empty batch — a poll that silently finds nothing, forever, which is\n // indistinguishable from a quiet channel. The connector core cannot know\n // this; it is exactly the per-provider knowledge a definition exists to\n // hold.\n if (data?.ok === false) {\n throw new Error(\n `get_updates: getUpdates was rejected — ` +\n String(data.description ?? \"no reason given\"),\n );\n }\n\n const items = Array.isArray(data?.result) ? (data.result as unknown[]) : [];\n\n // `offset` means \"the first one I have NOT handled\", so the next cursor\n // is the highest id seen plus one. Off by one in either direction is a\n // real bug with no error attached: too low replays every item forever,\n // too high drops one silently. The HOST persists this and passes it back\n // in — nothing else will.\n const seen = items\n .map((item) => (item as { update_id?: unknown })?.update_id)\n .filter((id): id is number => typeof id === \"number\");\n const previous = config.offset === undefined || config.offset === null || config.offset === \"\"\n ? undefined\n : Number(config.offset);\n const cursor = seen.length === 0 ? previous : Math.max(...seen) + 1;\n\n const value = {\n mode: result.mode,\n connection: result.connection,\n cursor,\n count: items.length,\n updates: items,\n update: items[0] ?? null,\n };\n\n // A poll that found nothing is the NORMAL case, not a failure. Routing\n // it to its own port keeps the main path meaning \"something happened\",\n // and still gives the host somewhere to hang its cursor bookkeeping.\n if (items.length === 0) {\n return { __port: \"empty\", value };\n }\n\n ctx.emit({\n type: \"log\",\n level: \"info\",\n nodeId: ctx.node.id,\n message: `telegram: ${items.length} item(s), next cursor ${cursor} (${result.mode})`,\n });\n\n return { __port: \"out\", value };\n};\n\n/** The kinds a TypeScript host registers. */\nexport const TELEGRAM_RUNNABLE_KINDS: NodeKindDefinition[] = [\n { ...telegramUpdatesTriggerKind, executor: telegramUpdatesTriggerExecutor },\n];\n"]}
@@ -0,0 +1,26 @@
1
+ import { NodeKindDefinition, NodeExecutor } from '@particle-academy/fancy-flow/engine';
2
+
3
+ /**
4
+ * GENERATED FILE — do not edit.
5
+ *
6
+ * Emitted from provider/actions/ + triggers/ by weaver's generator.
7
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
8
+ * being rejected, because it works until it silently does not. Fix
9
+ * provider/actions/ + triggers/ (or weaver's template/) and regenerate:
10
+ *
11
+ * npm run provider -- telegram
12
+ */
13
+ /**
14
+ * Telegram's node kinds with their TypeScript executors attached — for hosts
15
+ * that EXECUTE on TS.
16
+ *
17
+ * The authoring surface in @particle-academy/telegram-ui carries no executor:
18
+ * the editor is React on every host, so a PHP or Python project installs the
19
+ * ui package and never this one.
20
+ */
21
+
22
+ declare const telegramUpdatesTriggerExecutor: NodeExecutor;
23
+ /** The kinds a TypeScript host registers. */
24
+ declare const TELEGRAM_RUNNABLE_KINDS: NodeKindDefinition[];
25
+
26
+ export { TELEGRAM_RUNNABLE_KINDS, telegramUpdatesTriggerExecutor };
package/dist/flow.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { NodeKindDefinition, NodeExecutor } from '@particle-academy/fancy-flow/engine';
2
+
3
+ /**
4
+ * GENERATED FILE — do not edit.
5
+ *
6
+ * Emitted from provider/actions/ + triggers/ by weaver's generator.
7
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
8
+ * being rejected, because it works until it silently does not. Fix
9
+ * provider/actions/ + triggers/ (or weaver's template/) and regenerate:
10
+ *
11
+ * npm run provider -- telegram
12
+ */
13
+ /**
14
+ * Telegram's node kinds with their TypeScript executors attached — for hosts
15
+ * that EXECUTE on TS.
16
+ *
17
+ * The authoring surface in @particle-academy/telegram-ui carries no executor:
18
+ * the editor is React on every host, so a PHP or Python project installs the
19
+ * ui package and never this one.
20
+ */
21
+
22
+ declare const telegramUpdatesTriggerExecutor: NodeExecutor;
23
+ /** The kinds a TypeScript host registers. */
24
+ declare const TELEGRAM_RUNNABLE_KINDS: NodeKindDefinition[];
25
+
26
+ export { TELEGRAM_RUNNABLE_KINDS, telegramUpdatesTriggerExecutor };
package/dist/flow.js ADDED
@@ -0,0 +1,43 @@
1
+ import { telegramGetUpdates } from './chunk-LFMQKSUM.js';
2
+ import '@particle-academy/fancy-connector-core';
3
+ import { telegramUpdatesTriggerKind } from '@particle-academy/telegram-ui';
4
+
5
+ var telegramUpdatesTriggerExecutor = async (ctx) => {
6
+ const config = ctx.node.data?.config ?? {};
7
+ const result = await telegramGetUpdates({ config });
8
+ const data = result.data;
9
+ if (data?.ok === false) {
10
+ throw new Error(
11
+ `get_updates: getUpdates was rejected \u2014 ` + String(data.description ?? "no reason given")
12
+ );
13
+ }
14
+ const items = Array.isArray(data?.result) ? data.result : [];
15
+ const seen = items.map((item) => item?.update_id).filter((id) => typeof id === "number");
16
+ const previous = config.offset === void 0 || config.offset === null || config.offset === "" ? void 0 : Number(config.offset);
17
+ const cursor = seen.length === 0 ? previous : Math.max(...seen) + 1;
18
+ const value = {
19
+ mode: result.mode,
20
+ connection: result.connection,
21
+ cursor,
22
+ count: items.length,
23
+ updates: items,
24
+ update: items[0] ?? null
25
+ };
26
+ if (items.length === 0) {
27
+ return { __port: "empty", value };
28
+ }
29
+ ctx.emit({
30
+ type: "log",
31
+ level: "info",
32
+ nodeId: ctx.node.id,
33
+ message: `telegram: ${items.length} item(s), next cursor ${cursor} (${result.mode})`
34
+ });
35
+ return { __port: "out", value };
36
+ };
37
+ var TELEGRAM_RUNNABLE_KINDS = [
38
+ { ...telegramUpdatesTriggerKind, executor: telegramUpdatesTriggerExecutor }
39
+ ];
40
+
41
+ export { TELEGRAM_RUNNABLE_KINDS, telegramUpdatesTriggerExecutor };
42
+ //# sourceMappingURL=flow.js.map
43
+ //# sourceMappingURL=flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow.ts"],"names":[],"mappings":";;;;AAoCO,IAAM,8BAAA,GAA+C,OAAO,GAAA,KAAQ;AACzE,EAAA,MAAM,MAAA,GAAW,GAAA,CAAI,IAAA,CAAK,IAAA,EAA+C,UAAU,EAAC;AAEpF,EAAA,MAAM,MAAA,GAAS,MAAM,kBAAA,CAAmB,EAAE,QAAQ,CAAA;AAClD,EAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AAQpB,EAAA,IAAI,IAAA,EAAM,OAAO,KAAA,EAAO;AACtB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,4CAAA,CAAA,GACE,MAAA,CAAO,IAAA,CAAK,WAAA,IAAe,iBAAiB;AAAA,KAChD;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,IAAA,EAAM,MAAM,CAAA,GAAK,IAAA,CAAK,SAAuB,EAAC;AAO1E,EAAA,MAAM,IAAA,GAAO,KAAA,CACV,GAAA,CAAI,CAAC,IAAA,KAAU,IAAA,EAAkC,SAAS,CAAA,CAC1D,MAAA,CAAO,CAAC,EAAA,KAAqB,OAAO,OAAO,QAAQ,CAAA;AACtD,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,MAAA,KAAW,MAAA,IAAa,MAAA,CAAO,MAAA,KAAW,IAAA,IAAQ,MAAA,CAAO,MAAA,KAAW,EAAA,GACxF,MAAA,GACA,MAAA,CAAO,OAAO,MAAM,CAAA;AACxB,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,KAAW,CAAA,GAAI,WAAW,IAAA,CAAK,GAAA,CAAI,GAAG,IAAI,CAAA,GAAI,CAAA;AAElE,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,MAAA;AAAA,IACA,OAAO,KAAA,CAAM,MAAA;AAAA,IACb,OAAA,EAAS,KAAA;AAAA,IACT,MAAA,EAAQ,KAAA,CAAM,CAAC,CAAA,IAAK;AAAA,GACtB;AAKA,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAM;AAAA,EAClC;AAEA,EAAA,GAAA,CAAI,IAAA,CAAK;AAAA,IACP,IAAA,EAAM,KAAA;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,IAAI,IAAA,CAAK,EAAA;AAAA,IACjB,OAAA,EAAS,aAAa,KAAA,CAAM,MAAM,yBAAyB,MAAM,CAAA,EAAA,EAAK,OAAO,IAAI,CAAA,CAAA;AAAA,GAClF,CAAA;AAED,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAM;AAChC;AAGO,IAAM,uBAAA,GAAgD;AAAA,EAC3D,EAAE,GAAG,0BAAA,EAA4B,QAAA,EAAU,8BAAA;AAC7C","file":"flow.js","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/actions/ + triggers/ by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/actions/ + triggers/ (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram's node kinds with their TypeScript executors attached — for hosts\n * that EXECUTE on TS.\n *\n * The authoring surface in @particle-academy/telegram-ui carries no executor:\n * the editor is React on every host, so a PHP or Python project installs the\n * ui package and never this one.\n */\n\nimport type { NodeExecutor, NodeKindDefinition } from \"@particle-academy/fancy-flow/engine\";\nimport {\n idempotencyKeyFor,\n NO_IDEMPOTENCY_KEY_WARNING,\n resolveConnection,\n triggerEvent,\n type RequestedMode,\n} from \"@particle-academy/fancy-connector-core\";\nimport { TELEGRAM } from \"./service.js\";\n\nimport {\n telegramUpdatesTriggerKind,\n} from \"@particle-academy/telegram-ui\";\n\nimport { TELEGRAM_GET_UPDATES, telegramGetUpdates } from \"./triggers/get-updates.js\";\n\nexport const telegramUpdatesTriggerExecutor: NodeExecutor = async (ctx) => {\n const config = ((ctx.node.data as { config?: Record<string, unknown> })?.config ?? {});\n\n const result = await telegramGetUpdates({ config });\n const data = result.data as Record<string, unknown> | undefined;\n\n // Telegram answers HTTP 200 with `{ok: false}` for an application-level\n // failure. A status check alone reads that as success and publishes an\n // empty batch — a poll that silently finds nothing, forever, which is\n // indistinguishable from a quiet channel. The connector core cannot know\n // this; it is exactly the per-provider knowledge a definition exists to\n // hold.\n if (data?.ok === false) {\n throw new Error(\n `get_updates: getUpdates was rejected — ` +\n String(data.description ?? \"no reason given\"),\n );\n }\n\n const items = Array.isArray(data?.result) ? (data.result as unknown[]) : [];\n\n // `offset` means \"the first one I have NOT handled\", so the next cursor\n // is the highest id seen plus one. Off by one in either direction is a\n // real bug with no error attached: too low replays every item forever,\n // too high drops one silently. The HOST persists this and passes it back\n // in — nothing else will.\n const seen = items\n .map((item) => (item as { update_id?: unknown })?.update_id)\n .filter((id): id is number => typeof id === \"number\");\n const previous = config.offset === undefined || config.offset === null || config.offset === \"\"\n ? undefined\n : Number(config.offset);\n const cursor = seen.length === 0 ? previous : Math.max(...seen) + 1;\n\n const value = {\n mode: result.mode,\n connection: result.connection,\n cursor,\n count: items.length,\n updates: items,\n update: items[0] ?? null,\n };\n\n // A poll that found nothing is the NORMAL case, not a failure. Routing\n // it to its own port keeps the main path meaning \"something happened\",\n // and still gives the host somewhere to hang its cursor bookkeeping.\n if (items.length === 0) {\n return { __port: \"empty\", value };\n }\n\n ctx.emit({\n type: \"log\",\n level: \"info\",\n nodeId: ctx.node.id,\n message: `telegram: ${items.length} item(s), next cursor ${cursor} (${result.mode})`,\n });\n\n return { __port: \"out\", value };\n};\n\n/** The kinds a TypeScript host registers. */\nexport const TELEGRAM_RUNNABLE_KINDS: NodeKindDefinition[] = [\n { ...telegramUpdatesTriggerKind, executor: telegramUpdatesTriggerExecutor },\n];\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ var fancyConnectorCore = require('@particle-academy/fancy-connector-core');
4
+
5
+ // src/faker.ts
6
+ function fakeGetUpdates({ config, fake }) {
7
+ const boundChatId = fake.int(1e8, 999999999);
8
+ return {
9
+ "ok": true,
10
+ "result": [
11
+ {
12
+ "update_id": fake.int(1e5, 999999),
13
+ "message": {
14
+ "message_id": fake.int(1, 9999),
15
+ "date": 1767225600,
16
+ "text": config.sampleText !== void 0 && config.sampleText !== null && config.sampleText !== "" ? String(config.sampleText) : "hello from the faker",
17
+ "chat": {
18
+ "id": boundChatId,
19
+ "type": "private",
20
+ "first_name": "Ada",
21
+ "username": "ada_example"
22
+ },
23
+ "from": {
24
+ "id": boundChatId,
25
+ "is_bot": false,
26
+ "first_name": "Ada",
27
+ "username": "ada_example",
28
+ "language_code": "en"
29
+ }
30
+ }
31
+ }
32
+ ]
33
+ };
34
+ }
35
+ var telegramFaker = (operation, request) => {
36
+ switch (operation) {
37
+ case "get_updates":
38
+ return fakeGetUpdates(request);
39
+ default:
40
+ throw new Error(
41
+ `telegram: no fake response is defined for "${operation}". Add a fixture under provider/fixtures/ and regenerate \u2014 a connector without a faker cannot be developed against, tested, or demonstrated.`
42
+ );
43
+ }
44
+ };
45
+
46
+ // src/service.ts
47
+ var CONNECTOR_API_VERSION = 1;
48
+ var TELEGRAM_BASE_URLS = {
49
+ "live": "https://api.telegram.org",
50
+ "sandbox": "https://api.telegram.org"
51
+ };
52
+ var TELEGRAM_REQUIRES = [
53
+ "botToken"
54
+ ];
55
+ function telegramAuthorize(credentials, request, mode) {
56
+ const url = new URL(request.url);
57
+ const segment = mode === "sandbox" ? `/bot${credentials.botToken ?? ""}/test` : `/bot${credentials.botToken ?? ""}`;
58
+ url.pathname = `${segment}${url.pathname}`;
59
+ request.url = url.toString();
60
+ }
61
+ var TELEGRAM = {
62
+ service: "telegram",
63
+ title: "Telegram",
64
+ sandbox: "separate-account",
65
+ baseUrls: { ...TELEGRAM_BASE_URLS },
66
+ requires: [...TELEGRAM_REQUIRES],
67
+ authorize: telegramAuthorize,
68
+ faker: telegramFaker
69
+ };
70
+ var TELEGRAM_GET_UPDATES = {
71
+ service: "telegram",
72
+ operation: "get_updates",
73
+ delivery: "poll",
74
+ setup: "The host polls getUpdates on a schedule and persists the `offset` cursor between calls, passing the last `cursor` back in \u2014 Telegram queues nothing once an offset has acknowledged it, so a lost cursor is lost updates. getUpdates and setWebhook are MUTUALLY EXCLUSIVE for one bot: a host running both gets neither.",
75
+ minPollSeconds: 1,
76
+ faker: telegramFaker
77
+ };
78
+ var GET_UPDATES_OPERATION = "get_updates";
79
+ async function telegramGetUpdates(options) {
80
+ const config = options.config ?? {};
81
+ {
82
+ const n = Number(config.offset);
83
+ const given = config.offset !== void 0 && config.offset !== null && config.offset !== "";
84
+ if (given && !Number.isInteger(n)) {
85
+ throw new Error(
86
+ `get_updates: "offset" must be a integer, got ${JSON.stringify(config.offset)}.`
87
+ );
88
+ }
89
+ }
90
+ {
91
+ const n = Number(config.limit);
92
+ const given = config.limit !== void 0 && config.limit !== null && config.limit !== "";
93
+ if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {
94
+ throw new Error(
95
+ `get_updates: "limit" must be a integer, got ${JSON.stringify(config.limit)}.`
96
+ );
97
+ }
98
+ }
99
+ return fancyConnectorCore.callConnector(TELEGRAM, {
100
+ operation: GET_UPDATES_OPERATION,
101
+ config,
102
+ input: options.input,
103
+ ...options.credentials === void 0 ? {} : { credentials: options.credentials },
104
+ ...options.mode === void 0 ? {} : { mode: options.mode },
105
+ ...options.connectionId === void 0 ? {} : { connectionId: options.connectionId },
106
+ ...options.attempts === void 0 ? {} : { attempts: options.attempts },
107
+ ...options.transport === void 0 ? {} : { transport: options.transport },
108
+ request: {
109
+ method: "GET",
110
+ path: "/getUpdates",
111
+ query: {
112
+ ...config.offset !== void 0 && config.offset !== null && config.offset !== "" ? { "offset": Math.trunc(Number(config.offset)) } : {},
113
+ "limit": config.limit !== void 0 && config.limit !== null && config.limit !== "" ? Math.trunc(Number(config.limit)) : 100,
114
+ ...config.allowedUpdates !== void 0 && config.allowedUpdates !== null && config.allowedUpdates !== "" ? { "allowed_updates": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}
115
+ }
116
+ }
117
+ });
118
+ }
119
+ function allowedUpdatesList(value) {
120
+ const items = Array.isArray(value) ? value.map(String) : typeof value === "string" ? value.split(",") : [];
121
+ return items.map((item) => item.trim()).filter(Boolean);
122
+ }
123
+
124
+ exports.CONNECTOR_API_VERSION = CONNECTOR_API_VERSION;
125
+ exports.GET_UPDATES_OPERATION = GET_UPDATES_OPERATION;
126
+ exports.TELEGRAM = TELEGRAM;
127
+ exports.TELEGRAM_BASE_URLS = TELEGRAM_BASE_URLS;
128
+ exports.TELEGRAM_GET_UPDATES = TELEGRAM_GET_UPDATES;
129
+ exports.TELEGRAM_REQUIRES = TELEGRAM_REQUIRES;
130
+ exports.telegramAuthorize = telegramAuthorize;
131
+ exports.telegramFaker = telegramFaker;
132
+ exports.telegramGetUpdates = telegramGetUpdates;
133
+ //# sourceMappingURL=index.cjs.map
134
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/faker.ts","../src/service.ts","../src/triggers/get-updates.ts"],"names":["callConnector"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/fixtures/ by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/fixtures/ (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * The Telegram faker.\n *\n * Shapes, not behaviour: the goal is that a downstream node sees the field\n * NAMES Telegram actually publishes, so an author can wire {{ $json.data.id }}\n * against a fake and have it keep working against the real thing.\n *\n * Deterministic — same inputs, same output. A faker returning a fresh uuid\n * every call cannot be asserted on, so its fixtures degrade to \"it did not\n * throw\", which is the assertion that catches nothing.\n */\n\nimport type { ConnectorFaker, FakeRequest } from \"@particle-academy/fancy-connector-core\";\n\nfunction fakeGetUpdates({ config, fake }: FakeRequest): unknown {\n const boundChatId = fake.int(100000000, 999999999);\n\n return {\n \"ok\": true,\n \"result\": [\n {\n \"update_id\": fake.int(100000, 999999),\n \"message\": {\n \"message_id\": fake.int(1, 9999),\n \"date\": 1767225600,\n \"text\": (config.sampleText !== undefined && config.sampleText !== null && config.sampleText !== \"\" ? String(config.sampleText) : \"hello from the faker\"),\n \"chat\": {\n \"id\": boundChatId,\n \"type\": \"private\",\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n },\n \"from\": {\n \"id\": boundChatId,\n \"is_bot\": false,\n \"first_name\": \"Ada\",\n \"username\": \"ada_example\",\n \"language_code\": \"en\",\n },\n },\n },\n ],\n };\n}\n\nexport const telegramFaker: ConnectorFaker = (operation, request) => {\n switch (operation) {\n case \"get_updates\":\n return fakeGetUpdates(request);\n\n default:\n // A faker asked for an operation it has no shape for must SAY so. Making\n // something up would produce a green run whose output silently has none\n // of the fields the author is about to reference.\n throw new Error(\n `telegram: no fake response is defined for \"${operation}\". ` +\n \"Add a fixture under provider/fixtures/ and regenerate — a connector without a faker \" +\n \"cannot be developed against, tested, or demonstrated.\",\n );\n }\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/manifest.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/manifest.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram, as one service descriptor shared by every Telegram operation.\n *\n * @particle-academy/fancy-connector-core carries what is true of ALL\n * connectors. This carries what is true of Telegram: its base URL, its auth\n * scheme, its idempotency header, and its faker.\n *\n * ## The sandbox trap, written down where it is used\n *\n * Telegram's test environment is a genuinely SEPARATE ACCOUNT: you create a\n * new account inside it and register a new bot there, so the sandbox\n * credential is a different token rather than the same one pointed elsewhere.\n * The `/test` path segment is how you reach it; the account is what makes it\n * separate. Flood limits are NOT relaxed there, so it is a place to test, not\n * a place to hammer.\n */\n\nimport type { ConnectorMode, PreparedRequest, ServiceDescriptor } from \"@particle-academy/fancy-connector-core\";\n\nimport { telegramFaker } from \"./faker.js\";\n\n/**\n * The connector API version this package was GENERATED against.\n *\n * A literal, never imported. An imported constant lets an upgrade rewrite the\n * very claim it exists to detect, after which the copy agrees with itself\n * forever.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\nexport const TELEGRAM_BASE_URLS = {\n \"live\": \"https://api.telegram.org\",\n \"sandbox\": \"https://api.telegram.org\"\n} as const;\n\n/** Credential keys a remote call cannot proceed without. */\nexport const TELEGRAM_REQUIRES = [\n \"botToken\"\n] as const;\n\n/**\n * Apply Telegram's auth scheme to an outgoing request.\n *\n * The bot token is a PATH SEGMENT, not a header --\n * https://api.telegram.org/bot<token>/getUpdates -- and the test environment\n * is a further `/test` AFTER the token. So this is the first provider whose\n * auth and whose estate are the same decision expressed in the URL, which is\n * why `authorize` has always been handed the resolved mode. The token\n * therefore ends up in the request URL, where access logs and error reporters\n * will record it. That is Telegram's design, not ours; a host should keep its\n * own logging away from it.\n *\n * The mode is USED here: for this provider auth and estate are the same\n * decision expressed in the URL, which is what this parameter has always been\n * for.\n */\nexport function telegramAuthorize(\n credentials: Record<string, string | undefined>,\n request: PreparedRequest,\n mode: ConnectorMode,\n): void {\n const url = new URL(request.url);\n\n // The estate is the SAME decision as the credential here, and it lives in\n // a further segment AFTER the token. A token pointed at a node marked\n // \"sandbox\" would otherwise reach the live bot, and succeed.\n const segment = mode === \"sandbox\"\n ? `/bot${credentials.botToken ?? \"\"}/test`\n : `/bot${credentials.botToken ?? \"\"}`;\n\n url.pathname = `${segment}${url.pathname}`;\n request.url = url.toString();\n}\n\n/** The Telegram service, for the TypeScript runtime. */\nexport const TELEGRAM: ServiceDescriptor = {\n service: \"telegram\",\n title: \"Telegram\",\n sandbox: \"separate-account\",\n baseUrls: { ...TELEGRAM_BASE_URLS },\n requires: [...TELEGRAM_REQUIRES],\n authorize: telegramAuthorize,\n faker: telegramFaker,\n};\n","/**\n * GENERATED FILE — do not edit.\n *\n * Emitted from provider/triggers/get-updates.json by weaver's generator.\n * A hand-edit here is destroyed by the next protocol sync, which is worse than\n * being rejected, because it works until it silently does not. Fix\n * provider/triggers/get-updates.json (or weaver's template/) and regenerate:\n *\n * npm run provider -- telegram\n */\n\n/**\n * Telegram's poll trigger — the delivery contract.\n *\n * A POLL trigger, so there is no signature and no route: Telegram does not\n * push, the host asks on a schedule and persists a cursor between calls.\n * Nothing queues what a stopped poller did not collect, which is why that\n * obligation is written down rather than assumed.\n */\n\nimport {\n callConnector,\n type ConnectorResult,\n type RequestedMode,\n type Transport,\n type TriggerDescriptor,\n} from \"@particle-academy/fancy-connector-core\";\nimport { telegramFaker } from \"../faker.js\";\nimport { TELEGRAM } from \"../service.js\";\n\nexport const TELEGRAM_GET_UPDATES: TriggerDescriptor = {\n service: \"telegram\",\n operation: \"get_updates\",\n delivery: \"poll\",\n setup:\n \"The host polls getUpdates on a schedule and persists the `offset` cursor between calls, passing the last `cursor` back in — Telegram queues nothing once an offset has acknowledged it, so a lost cursor is lost updates. getUpdates and setWebhook are MUTUALLY EXCLUSIVE for one bot: a host running both gets neither.\",\n minPollSeconds: 1,\n faker: telegramFaker,\n};\n\n/**\n * Poll Telegram once.\n *\n * GET /getUpdates — https://core.telegram.org/bots/api#getupdates\n *\n * In `fake` mode this resolves to the faker, so the node is runnable on a\n * canvas before any credential exists — with the same envelope a real poll\n * returns.\n */\n\nexport const GET_UPDATES_OPERATION = \"get_updates\";\n\nexport type GetUpdatesOptions = {\n /** The node's resolved config. Keys: offset, limit, allowedUpdates, sampleText. */\n config: Record<string, unknown>;\n credentials?: Record<string, string | undefined>;\n mode?: RequestedMode;\n connectionId?: string | null;\n input?: unknown;\n attempts?: number;\n /** Override the transport. The only way to exercise this without a network. */\n transport?: Transport;\n};\n\nexport async function telegramGetUpdates(options: GetUpdatesOptions): Promise<ConnectorResult> {\n const config = options.config ?? {};\n\n {\n const n = Number(config.offset);\n const given = config.offset !== undefined && config.offset !== null && config.offset !== \"\";\n if (given && !(Number.isInteger(n))) {\n throw new Error(\n `get_updates: \"offset\" must be a integer, got ${JSON.stringify(config.offset)}.`,\n );\n }\n }\n\n {\n const n = Number(config.limit);\n const given = config.limit !== undefined && config.limit !== null && config.limit !== \"\";\n if (given && !(Number.isInteger(n) && n >= 1 && n <= 100)) {\n throw new Error(\n `get_updates: \"limit\" must be a integer, got ${JSON.stringify(config.limit)}.`,\n );\n }\n }\n\n return callConnector(TELEGRAM, {\n operation: GET_UPDATES_OPERATION,\n config,\n input: options.input,\n ...(options.credentials === undefined ? {} : { credentials: options.credentials }),\n ...(options.mode === undefined ? {} : { mode: options.mode }),\n ...(options.connectionId === undefined ? {} : { connectionId: options.connectionId }),\n ...(options.attempts === undefined ? {} : { attempts: options.attempts }),\n ...(options.transport === undefined ? {} : { transport: options.transport }),\n request: {\n method: \"GET\",\n path: \"/getUpdates\",\n query: {\n ...(config.offset !== undefined && config.offset !== null && config.offset !== \"\" ? { \"offset\": Math.trunc(Number(config.offset)) } : {}),\n \"limit\": config.limit !== undefined && config.limit !== null && config.limit !== \"\" ? Math.trunc(Number(config.limit)) : 100,\n ...(config.allowedUpdates !== undefined && config.allowedUpdates !== null && config.allowedUpdates !== \"\" ? { \"allowed_updates\": JSON.stringify(allowedUpdatesList(config.allowedUpdates)) } : {}),\n },\n },\n });\n}\n\n/** One value, a \",\"-separated string, or an array — all end up a list. */\nfunction allowedUpdatesList(value: unknown): string[] {\n const items = Array.isArray(value)\n ? value.map(String)\n : typeof value === \"string\"\n ? value.split(\",\")\n : [];\n\n return items.map((item) => item.trim()).filter(Boolean);\n}\n"]}
@@ -0,0 +1,131 @@
1
+ import { ServiceDescriptor, PreparedRequest, ConnectorMode, ConnectorFaker, RequestedMode, Transport, TriggerDescriptor, ConnectorResult } from '@particle-academy/fancy-connector-core';
2
+
3
+ /**
4
+ * GENERATED FILE — do not edit.
5
+ *
6
+ * Emitted from provider/manifest.json by weaver's generator.
7
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
8
+ * being rejected, because it works until it silently does not. Fix
9
+ * provider/manifest.json (or weaver's template/) and regenerate:
10
+ *
11
+ * npm run provider -- telegram
12
+ */
13
+ /**
14
+ * Telegram, as one service descriptor shared by every Telegram operation.
15
+ *
16
+ * @particle-academy/fancy-connector-core carries what is true of ALL
17
+ * connectors. This carries what is true of Telegram: its base URL, its auth
18
+ * scheme, its idempotency header, and its faker.
19
+ *
20
+ * ## The sandbox trap, written down where it is used
21
+ *
22
+ * Telegram's test environment is a genuinely SEPARATE ACCOUNT: you create a
23
+ * new account inside it and register a new bot there, so the sandbox
24
+ * credential is a different token rather than the same one pointed elsewhere.
25
+ * The `/test` path segment is how you reach it; the account is what makes it
26
+ * separate. Flood limits are NOT relaxed there, so it is a place to test, not
27
+ * a place to hammer.
28
+ */
29
+
30
+ /**
31
+ * The connector API version this package was GENERATED against.
32
+ *
33
+ * A literal, never imported. An imported constant lets an upgrade rewrite the
34
+ * very claim it exists to detect, after which the copy agrees with itself
35
+ * forever.
36
+ */
37
+ declare const CONNECTOR_API_VERSION = 1;
38
+ declare const TELEGRAM_BASE_URLS: {
39
+ readonly live: "https://api.telegram.org";
40
+ readonly sandbox: "https://api.telegram.org";
41
+ };
42
+ /** Credential keys a remote call cannot proceed without. */
43
+ declare const TELEGRAM_REQUIRES: readonly ["botToken"];
44
+ /**
45
+ * Apply Telegram's auth scheme to an outgoing request.
46
+ *
47
+ * The bot token is a PATH SEGMENT, not a header --
48
+ * https://api.telegram.org/bot<token>/getUpdates -- and the test environment
49
+ * is a further `/test` AFTER the token. So this is the first provider whose
50
+ * auth and whose estate are the same decision expressed in the URL, which is
51
+ * why `authorize` has always been handed the resolved mode. The token
52
+ * therefore ends up in the request URL, where access logs and error reporters
53
+ * will record it. That is Telegram's design, not ours; a host should keep its
54
+ * own logging away from it.
55
+ *
56
+ * The mode is USED here: for this provider auth and estate are the same
57
+ * decision expressed in the URL, which is what this parameter has always been
58
+ * for.
59
+ */
60
+ declare function telegramAuthorize(credentials: Record<string, string | undefined>, request: PreparedRequest, mode: ConnectorMode): void;
61
+ /** The Telegram service, for the TypeScript runtime. */
62
+ declare const TELEGRAM: ServiceDescriptor;
63
+
64
+ /**
65
+ * GENERATED FILE — do not edit.
66
+ *
67
+ * Emitted from provider/fixtures/ by weaver's generator.
68
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
69
+ * being rejected, because it works until it silently does not. Fix
70
+ * provider/fixtures/ (or weaver's template/) and regenerate:
71
+ *
72
+ * npm run provider -- telegram
73
+ */
74
+ /**
75
+ * The Telegram faker.
76
+ *
77
+ * Shapes, not behaviour: the goal is that a downstream node sees the field
78
+ * NAMES Telegram actually publishes, so an author can wire {{ $json.data.id }}
79
+ * against a fake and have it keep working against the real thing.
80
+ *
81
+ * Deterministic — same inputs, same output. A faker returning a fresh uuid
82
+ * every call cannot be asserted on, so its fixtures degrade to "it did not
83
+ * throw", which is the assertion that catches nothing.
84
+ */
85
+
86
+ declare const telegramFaker: ConnectorFaker;
87
+
88
+ /**
89
+ * GENERATED FILE — do not edit.
90
+ *
91
+ * Emitted from provider/triggers/get-updates.json by weaver's generator.
92
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
93
+ * being rejected, because it works until it silently does not. Fix
94
+ * provider/triggers/get-updates.json (or weaver's template/) and regenerate:
95
+ *
96
+ * npm run provider -- telegram
97
+ */
98
+ /**
99
+ * Telegram's poll trigger — the delivery contract.
100
+ *
101
+ * A POLL trigger, so there is no signature and no route: Telegram does not
102
+ * push, the host asks on a schedule and persists a cursor between calls.
103
+ * Nothing queues what a stopped poller did not collect, which is why that
104
+ * obligation is written down rather than assumed.
105
+ */
106
+
107
+ declare const TELEGRAM_GET_UPDATES: TriggerDescriptor;
108
+ /**
109
+ * Poll Telegram once.
110
+ *
111
+ * GET /getUpdates — https://core.telegram.org/bots/api#getupdates
112
+ *
113
+ * In `fake` mode this resolves to the faker, so the node is runnable on a
114
+ * canvas before any credential exists — with the same envelope a real poll
115
+ * returns.
116
+ */
117
+ declare const GET_UPDATES_OPERATION = "get_updates";
118
+ type GetUpdatesOptions = {
119
+ /** The node's resolved config. Keys: offset, limit, allowedUpdates, sampleText. */
120
+ config: Record<string, unknown>;
121
+ credentials?: Record<string, string | undefined>;
122
+ mode?: RequestedMode;
123
+ connectionId?: string | null;
124
+ input?: unknown;
125
+ attempts?: number;
126
+ /** Override the transport. The only way to exercise this without a network. */
127
+ transport?: Transport;
128
+ };
129
+ declare function telegramGetUpdates(options: GetUpdatesOptions): Promise<ConnectorResult>;
130
+
131
+ export { CONNECTOR_API_VERSION, GET_UPDATES_OPERATION, type GetUpdatesOptions, TELEGRAM, TELEGRAM_BASE_URLS, TELEGRAM_GET_UPDATES, TELEGRAM_REQUIRES, telegramAuthorize, telegramFaker, telegramGetUpdates };
@@ -0,0 +1,131 @@
1
+ import { ServiceDescriptor, PreparedRequest, ConnectorMode, ConnectorFaker, RequestedMode, Transport, TriggerDescriptor, ConnectorResult } from '@particle-academy/fancy-connector-core';
2
+
3
+ /**
4
+ * GENERATED FILE — do not edit.
5
+ *
6
+ * Emitted from provider/manifest.json by weaver's generator.
7
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
8
+ * being rejected, because it works until it silently does not. Fix
9
+ * provider/manifest.json (or weaver's template/) and regenerate:
10
+ *
11
+ * npm run provider -- telegram
12
+ */
13
+ /**
14
+ * Telegram, as one service descriptor shared by every Telegram operation.
15
+ *
16
+ * @particle-academy/fancy-connector-core carries what is true of ALL
17
+ * connectors. This carries what is true of Telegram: its base URL, its auth
18
+ * scheme, its idempotency header, and its faker.
19
+ *
20
+ * ## The sandbox trap, written down where it is used
21
+ *
22
+ * Telegram's test environment is a genuinely SEPARATE ACCOUNT: you create a
23
+ * new account inside it and register a new bot there, so the sandbox
24
+ * credential is a different token rather than the same one pointed elsewhere.
25
+ * The `/test` path segment is how you reach it; the account is what makes it
26
+ * separate. Flood limits are NOT relaxed there, so it is a place to test, not
27
+ * a place to hammer.
28
+ */
29
+
30
+ /**
31
+ * The connector API version this package was GENERATED against.
32
+ *
33
+ * A literal, never imported. An imported constant lets an upgrade rewrite the
34
+ * very claim it exists to detect, after which the copy agrees with itself
35
+ * forever.
36
+ */
37
+ declare const CONNECTOR_API_VERSION = 1;
38
+ declare const TELEGRAM_BASE_URLS: {
39
+ readonly live: "https://api.telegram.org";
40
+ readonly sandbox: "https://api.telegram.org";
41
+ };
42
+ /** Credential keys a remote call cannot proceed without. */
43
+ declare const TELEGRAM_REQUIRES: readonly ["botToken"];
44
+ /**
45
+ * Apply Telegram's auth scheme to an outgoing request.
46
+ *
47
+ * The bot token is a PATH SEGMENT, not a header --
48
+ * https://api.telegram.org/bot<token>/getUpdates -- and the test environment
49
+ * is a further `/test` AFTER the token. So this is the first provider whose
50
+ * auth and whose estate are the same decision expressed in the URL, which is
51
+ * why `authorize` has always been handed the resolved mode. The token
52
+ * therefore ends up in the request URL, where access logs and error reporters
53
+ * will record it. That is Telegram's design, not ours; a host should keep its
54
+ * own logging away from it.
55
+ *
56
+ * The mode is USED here: for this provider auth and estate are the same
57
+ * decision expressed in the URL, which is what this parameter has always been
58
+ * for.
59
+ */
60
+ declare function telegramAuthorize(credentials: Record<string, string | undefined>, request: PreparedRequest, mode: ConnectorMode): void;
61
+ /** The Telegram service, for the TypeScript runtime. */
62
+ declare const TELEGRAM: ServiceDescriptor;
63
+
64
+ /**
65
+ * GENERATED FILE — do not edit.
66
+ *
67
+ * Emitted from provider/fixtures/ by weaver's generator.
68
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
69
+ * being rejected, because it works until it silently does not. Fix
70
+ * provider/fixtures/ (or weaver's template/) and regenerate:
71
+ *
72
+ * npm run provider -- telegram
73
+ */
74
+ /**
75
+ * The Telegram faker.
76
+ *
77
+ * Shapes, not behaviour: the goal is that a downstream node sees the field
78
+ * NAMES Telegram actually publishes, so an author can wire {{ $json.data.id }}
79
+ * against a fake and have it keep working against the real thing.
80
+ *
81
+ * Deterministic — same inputs, same output. A faker returning a fresh uuid
82
+ * every call cannot be asserted on, so its fixtures degrade to "it did not
83
+ * throw", which is the assertion that catches nothing.
84
+ */
85
+
86
+ declare const telegramFaker: ConnectorFaker;
87
+
88
+ /**
89
+ * GENERATED FILE — do not edit.
90
+ *
91
+ * Emitted from provider/triggers/get-updates.json by weaver's generator.
92
+ * A hand-edit here is destroyed by the next protocol sync, which is worse than
93
+ * being rejected, because it works until it silently does not. Fix
94
+ * provider/triggers/get-updates.json (or weaver's template/) and regenerate:
95
+ *
96
+ * npm run provider -- telegram
97
+ */
98
+ /**
99
+ * Telegram's poll trigger — the delivery contract.
100
+ *
101
+ * A POLL trigger, so there is no signature and no route: Telegram does not
102
+ * push, the host asks on a schedule and persists a cursor between calls.
103
+ * Nothing queues what a stopped poller did not collect, which is why that
104
+ * obligation is written down rather than assumed.
105
+ */
106
+
107
+ declare const TELEGRAM_GET_UPDATES: TriggerDescriptor;
108
+ /**
109
+ * Poll Telegram once.
110
+ *
111
+ * GET /getUpdates — https://core.telegram.org/bots/api#getupdates
112
+ *
113
+ * In `fake` mode this resolves to the faker, so the node is runnable on a
114
+ * canvas before any credential exists — with the same envelope a real poll
115
+ * returns.
116
+ */
117
+ declare const GET_UPDATES_OPERATION = "get_updates";
118
+ type GetUpdatesOptions = {
119
+ /** The node's resolved config. Keys: offset, limit, allowedUpdates, sampleText. */
120
+ config: Record<string, unknown>;
121
+ credentials?: Record<string, string | undefined>;
122
+ mode?: RequestedMode;
123
+ connectionId?: string | null;
124
+ input?: unknown;
125
+ attempts?: number;
126
+ /** Override the transport. The only way to exercise this without a network. */
127
+ transport?: Transport;
128
+ };
129
+ declare function telegramGetUpdates(options: GetUpdatesOptions): Promise<ConnectorResult>;
130
+
131
+ export { CONNECTOR_API_VERSION, GET_UPDATES_OPERATION, type GetUpdatesOptions, TELEGRAM, TELEGRAM_BASE_URLS, TELEGRAM_GET_UPDATES, TELEGRAM_REQUIRES, telegramAuthorize, telegramFaker, telegramGetUpdates };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { CONNECTOR_API_VERSION, GET_UPDATES_OPERATION, TELEGRAM, TELEGRAM_BASE_URLS, TELEGRAM_GET_UPDATES, TELEGRAM_REQUIRES, telegramAuthorize, telegramFaker, telegramGetUpdates } from './chunk-LFMQKSUM.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@particle-academy/telegram-js",
3
+ "version": "0.1.0",
4
+ "description": "Telegram for Node — the service descriptor, its faker, its webhook verification, and one function per operation. Plain HTTP on @particle-academy/fancy-connector-core; no vendor SDK.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Fancy-Friends/telegram.git"
8
+ },
9
+ "homepage": "https://github.com/Fancy-Friends/telegram#readme",
10
+ "bugs": "https://github.com/Fancy-Friends/telegram/issues",
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
25
+ },
26
+ "./flow": {
27
+ "import": {
28
+ "types": "./dist/flow.d.ts",
29
+ "default": "./dist/flow.js"
30
+ },
31
+ "require": {
32
+ "types": "./dist/flow.d.cts",
33
+ "default": "./dist/flow.cjs"
34
+ }
35
+ },
36
+ "./package.json": "./package.json"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "CHANGELOG.md",
42
+ "LICENSE"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "lint": "tsc --noEmit",
47
+ "test": "node --import tsx --test tests/*.test.ts",
48
+ "prepublishOnly": "tsup"
49
+ },
50
+ "dependencies": {
51
+ "@particle-academy/fancy-connector-core": ">=0.3.1 <2.0",
52
+ "@particle-academy/telegram-ui": "0.1.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@particle-academy/fancy-flow": ">=0.47.1 <2.0"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "@particle-academy/fancy-flow": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@particle-academy/fancy-flow": ">=0.47.1 <2.0",
64
+ "@types/node": "^26.2.0",
65
+ "tsup": "^8.5.0",
66
+ "tsx": "^4.19.0",
67
+ "typescript": "^5.8.0"
68
+ },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "keywords": [
73
+ "telegram",
74
+ "messaging",
75
+ "connector",
76
+ "api",
77
+ "fancy-ui",
78
+ "particle-academy"
79
+ ],
80
+ "license": "MIT",
81
+ "sideEffects": false,
82
+ "engines": {
83
+ "node": ">=22"
84
+ }
85
+ }