@volter/twin-telegram 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +2 -2
  2. package/dist/journeys/customer-life.json +21 -21
  3. package/dist/journeys/customer-life.outline.md +17 -17
  4. package/dist/journeys/customer-life.outline.verdict.json +17 -52
  5. package/dist/journeys/customer-life.verdict.json +24 -98
  6. package/dist/journeys/decisions.json +49 -13
  7. package/dist/journeys/demand.json +0 -91
  8. package/dist/journeys/first-use.json +21 -0
  9. package/dist/journeys/unreachable.json +1 -1
  10. package/dist/journeys/vendor-examples.json +14 -6
  11. package/dist/spec/SOURCE.md +5 -6
  12. package/dist/spec/patches.json +20 -20
  13. package/dist/spec/sources.json +15 -0
  14. package/dist/src/cli.js +1 -1
  15. package/dist/src/engine/media.d.ts +6 -12
  16. package/dist/src/engine/media.js +28 -66
  17. package/dist/src/engine/text.d.ts +0 -7
  18. package/dist/src/engine/text.js +2 -2
  19. package/dist/src/index.js +2 -1
  20. package/dist/src/manifest.js +7 -6
  21. package/dist/src/semantics/available-methods.js +50 -40
  22. package/dist/src/semantics/doors.d.ts +0 -1
  23. package/dist/src/semantics/doors.js +82 -5
  24. package/dist/src/semantics/shared.d.ts +10 -6
  25. package/dist/src/semantics/shared.js +29 -8
  26. package/dist/src/server.js +2 -19
  27. package/generated/pack-facts.json +10 -0
  28. package/journeys/customer-life.json +21 -21
  29. package/journeys/customer-life.outline.md +17 -17
  30. package/journeys/customer-life.outline.verdict.json +17 -52
  31. package/journeys/customer-life.verdict.json +24 -98
  32. package/journeys/decisions.json +49 -13
  33. package/journeys/demand.json +0 -91
  34. package/journeys/first-use.json +21 -0
  35. package/journeys/unreachable.json +1 -1
  36. package/journeys/vendor-examples.json +14 -6
  37. package/package.json +12 -9
  38. package/spec/SOURCE.md +5 -6
  39. package/spec/patches.json +20 -20
  40. package/spec/sources.json +15 -0
  41. package/src/cli.ts +1 -1
  42. package/src/engine/media.ts +21 -58
  43. package/src/engine/text.ts +2 -2
  44. package/src/index.ts +2 -1
  45. package/src/manifest.ts +7 -6
  46. package/src/semantics/available-methods.ts +55 -41
  47. package/src/semantics/doors.ts +74 -8
  48. package/src/semantics/shared.ts +27 -8
  49. package/src/server.ts +2 -11
  50. package/dist/generated/pack-facts.json +0 -53
  51. package/dist/spec/page-to-openapi.ts +0 -258
  52. package/spec/page-to-openapi.ts +0 -258
@@ -1,9 +1,9 @@
1
1
  {
2
- "about": "telegram's decision table (the P3 procedure: demand, then this table, then the code): each method the pack serves, the demand it answers (journeys/demand.json, or the manifest's refresh scope) and the mechanism that serves it. The Bot API is an RPC wire (POST /bot<token>/<method>, every answer {ok, result}), so no method is a create, read, update or delete the derived core could serve: each is a handler over the bot the token names (./src/semantics/around.ts resolves it, before routing, as the Bot API server does). Every other method of the page answers the server's unknown method (the manifest's gap), the URL-media sends only an opt-in Postiz storage makes (demand.json's optional) included: a URL the World cannot reach is refused as Telegram refuses one it cannot download. People, bots and chats are made through the World's doors (the Telegram app and BotFather, which no Bot API method stands for).",
2
+ "about": "telegram's decision table (the P3 procedure: demand, then this table, then the code): each method the pack serves, the demand it answers (journeys/demand.json, or the manifest's refresh scope) and the mechanism that serves it. The Bot API is an RPC wire (POST /bot<token>/<method>, every answer {ok, result}), so no method is a create, read, update or delete the derived core could serve: each is a handler over the bot the token names (./src/semantics/around.ts resolves it, before routing, as the Bot API server does). Every other method of the page answers the server's unknown method (the manifest's gap), the URL-media sends included: a URL the World cannot reach is refused as Telegram refuses one it cannot download. People, bots and chats are made through the World's doors (the Telegram app and BotFather, which no Bot API method stands for).",
3
3
  "decisions": [
4
4
  {
5
5
  "operation": "getMe",
6
- "demand": "Volter Harness's adapter reads its bot as it starts; Postiz reads its bot's id when the /connect message is found",
6
+ "demand": "Volter Harness's adapter reads its bot as it starts",
7
7
  "mechanism": "handler",
8
8
  "why": "a computed read: the bot the token names, its User with the bot-only fields (can_join_groups, can_read_all_group_messages for privacy mode, supports_inline_queries, …)"
9
9
  },
@@ -15,7 +15,7 @@
15
15
  },
16
16
  {
17
17
  "operation": "getUpdates",
18
- "demand": "Volter Harness's adapter polls in a loop (timeout 30, limit 100); Postiz's connect screen polls with allowed_updates [message, channel_post] and an offset",
18
+ "demand": "Volter Harness's adapter polls in a loop (timeout 30, limit 100)",
19
19
  "mechanism": "handler",
20
20
  "why": "an action: the bot's queue read from the offset (confirming, and so deleting, every update below it), filtered by the allowed_updates in force when each was made, at most limit; with nothing waiting and a timeout, the read held until an update is written (the kernel's held read)"
21
21
  },
@@ -27,7 +27,7 @@
27
27
  },
28
28
  {
29
29
  "operation": "sendMessage",
30
- "demand": "Volter Harness posts its answers and its scheduled reports; Postiz posts a text (HTML), its connect notices and a comment as a reply",
30
+ "demand": "Volter Harness posts its answers and its scheduled reports",
31
31
  "mechanism": "handler",
32
32
  "why": "a message made in a chat the bot may write in: its message_id counted per chat, its text parsed for the parse_mode's entities (HTML, MarkdownV2 or Markdown), checked for length (1-4096 after parsing), a reply resolved; it writes no update for its own bot (bots do not receive their own messages)",
33
33
  "sourceRules": [
@@ -39,57 +39,93 @@
39
39
  },
40
40
  {
41
41
  "operation": "sendPhoto",
42
- "demand": "Postiz posts one image with its caption (HTML), uploaded",
42
+ "demand": "No scanned application calls it; the customer life walks it.",
43
43
  "mechanism": "handler",
44
44
  "why": "a message with a photo: the upload's bytes stored as a file (its width and height read from the image), the caption parsed and checked (0-1024); a URL the World cannot reach is refused as the server refuses one it cannot download"
45
45
  },
46
46
  {
47
47
  "operation": "sendVideo",
48
- "demand": "Postiz posts one video with its caption, uploaded",
48
+ "demand": "No scanned application calls it; the customer life walks it.",
49
49
  "mechanism": "handler",
50
50
  "why": "a message with a video: the bytes stored, its width, height and duration read from the file's own header where it is an MP4"
51
51
  },
52
52
  {
53
53
  "operation": "sendDocument",
54
- "demand": "Postiz posts one file that is neither an image nor a video, uploaded; Volter Harness's board sends a done card's artifact with its notice as a MarkdownV2 caption",
54
+ "demand": "Volter Harness's board sends a done card's artifact with its notice as a MarkdownV2 caption",
55
55
  "mechanism": "handler",
56
56
  "why": "a message with a document: the bytes stored with its name and media type; its caption parsed as sendMessage's text is (HTML, MarkdownV2 or Markdown) and checked (0-1024)"
57
57
  },
58
58
  {
59
59
  "operation": "sendMediaGroup",
60
- "demand": "Postiz posts several images or videos as one album, the caption on the first, uploaded (attach://<name>); Volter Harness's board sends a done card's several artifacts as one album of documents",
60
+ "demand": "Volter Harness's board sends a done card's several artifacts as one album of documents",
61
61
  "mechanism": "handler",
62
62
  "why": "2-10 messages sharing one media_group_id, each its own file; the array of sent messages answered"
63
63
  },
64
64
  {
65
65
  "operation": "deleteMessage",
66
- "demand": "Postiz deletes the /connect message and its own success notice",
66
+ "demand": "No scanned application calls it; the customer life walks it.",
67
67
  "mechanism": "handler",
68
68
  "why": "a message deleted where the bot may delete it: its own, or another's in a channel or group where it is an administrator with can_delete_messages; 'message to delete not found' for one that is not there"
69
69
  },
70
70
  {
71
71
  "operation": "getChat",
72
- "demand": "Postiz reads the channel it is connecting (id, title, username, photo); the vendor-backed refresh reads each chat again (the manifest's refresh of chat)",
72
+ "demand": "the vendor-backed refresh reads each chat again (the manifest's refresh of chat)",
73
73
  "mechanism": "handler",
74
74
  "why": "a computed read: the chat as ChatFullInfo, its access checked as the server checks a read"
75
75
  },
76
76
  {
77
77
  "operation": "getChatMember",
78
- "demand": "Postiz checks its bot is an administrator that can delete messages in the channel",
78
+ "demand": "No scanned application calls it; the customer life walks it.",
79
79
  "mechanism": "handler",
80
80
  "why": "a computed read: the member's status and rights with its user"
81
81
  },
82
82
  {
83
83
  "operation": "getFile",
84
- "demand": "Postiz reads the chat photo's file to make its link",
84
+ "demand": "No scanned application calls it; the customer life walks it.",
85
85
  "mechanism": "handler",
86
86
  "why": "an action: the file's download path issued (file_path), valid for the bot that asked"
87
87
  },
88
88
  {
89
89
  "operation": "downloadFile",
90
- "demand": "Postiz copies the chat photo into its storage from the file's link",
90
+ "demand": "No scanned application calls it; the customer life walks it.",
91
91
  "mechanism": "handler",
92
92
  "why": "the stored bytes of the file a getFile path names, for the bot whose token is in the path"
93
+ },
94
+ {
95
+ "operation": "door:people",
96
+ "demand": "the World's own door, POST /_twin/people: { first_name, last_name?, username?, language_code? }: a Telegram account, as a person signs up in the app",
97
+ "mechanism": "door",
98
+ "why": "{ first_name, last_name?, username?, language_code? }: a Telegram account, as a person signs up in the app, which no vendor API makes."
99
+ },
100
+ {
101
+ "operation": "door:bots",
102
+ "demand": "the World's own door, POST /_twin/bots: { owner?, name?, username? }: BotFather's /newbot, for the person `owner` (a user id): the bot and its token; the same username answers the same bot",
103
+ "mechanism": "door",
104
+ "why": "{ owner?, name?, username? }: BotFather's /newbot, for the person `owner` (a user id): the bot and its token; the same username answers the same bot, which no vendor API makes."
105
+ },
106
+ {
107
+ "operation": "door:botToken",
108
+ "demand": "the World's own door, POST /_twin/bots/{username}/token: { owner }: BotFather's /token: a new token, the old one revoked",
109
+ "mechanism": "door",
110
+ "why": "{ owner }: BotFather's /token: a new token, the old one revoked, which no vendor API makes."
111
+ },
112
+ {
113
+ "operation": "door:chats",
114
+ "demand": "the World's own door, POST /_twin/chats: { creator, type: group | supergroup | channel, title, username?, description?, photo? (base64 bytes), members?: [{ user, status?: member | administrator, rights? }] }: a person makes a chat in the app",
115
+ "mechanism": "door",
116
+ "why": "{ creator, type: group | supergroup | channel, title, username?, description?, photo? (base64 bytes), members?: [{ user, status?: member | administrator, rights? }] }: a person makes a chat in the app, which no vendor API makes."
117
+ },
118
+ {
119
+ "operation": "door:members",
120
+ "demand": "the World's own door, POST /_twin/chats/{chat_id}/members: { by, user, status: member | administrator | kicked, rights? }: a person adds, promotes or removes a member in the app",
121
+ "mechanism": "door",
122
+ "why": "{ by, user, status: member | administrator | kicked, rights? }: a person adds, promotes or removes a member in the app, which no vendor API makes."
123
+ },
124
+ {
125
+ "operation": "door:messages",
126
+ "demand": "the World's own door, POST /_twin/messages: { from, chat (a chat id, a chat's @username, or a bot's @username for a private chat with it), text, reply_to_message_id? }: a person writes in the app",
127
+ "mechanism": "door",
128
+ "why": "{ from, chat (a chat id, a chat's @username, or a bot's @username for a private chat with it), text, reply_to_message_id? }: a person writes in the app, which no vendor API makes."
93
129
  }
94
130
  ]
95
131
  }
@@ -55,97 +55,6 @@
55
55
  "client": "@chat-adapter/telegram@4.39.0:dist/index.js:2802"
56
56
  }
57
57
  ]
58
- },
59
- {
60
- "commit": "e0a08a7d9592fdc1ef1e82cb7cb781489fceade3",
61
- "client": "node-telegram-bot-api 0.66.0 (POST https://api.telegram.org/bot<token>/<method>, form-urlencoded, multipart for a file; rows marked node-telegram-bot-api@0.66.0: are its published src), TELEGRAM_TOKEN",
62
- "backend": [
63
- {
64
- "call": "the connect screen polls for the person's /connect message (allowed_updates message and channel_post, from the last offset)",
65
- "operation": "getUpdates",
66
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:87",
67
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:849"
68
- },
69
- {
70
- "call": "found: the bot's own id",
71
- "operation": "getMe",
72
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:105",
73
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:931"
74
- },
75
- {
76
- "call": "...whether it is an administrator that can delete messages there",
77
- "operation": "getChatMember",
78
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:356",
79
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1966"
80
- },
81
- {
82
- "call": "an administrator: the /connect message deleted, a success notice posted and deleted ten seconds later",
83
- "operation": "deleteMessage",
84
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:120",
85
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:3093"
86
- },
87
- {
88
- "call": "the success notice (or, without the right to delete, the notice asking the person to remove the messages)",
89
- "operation": "sendMessage",
90
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:122",
91
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
92
- },
93
- {
94
- "call": "the channel added: its chat read (id, title, username, photo)",
95
- "operation": "getChat",
96
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:62",
97
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1924"
98
- },
99
- {
100
- "call": "...and its photo's file, whose path makes the channel's picture link",
101
- "operation": "getFile",
102
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:71",
103
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1522"
104
- },
105
- {
106
- "call": "...and Postiz copies the picture into its own storage from that link",
107
- "operation": "downloadFile",
108
- "evidence": "libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts:119",
109
- "client": "libraries/nestjs-libraries/src/upload/local.storage.ts:62"
110
- },
111
- {
112
- "call": "a post with no media: its text, parse_mode HTML",
113
- "operation": "sendMessage",
114
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:224",
115
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
116
- },
117
- {
118
- "call": "a post with one image: the photo uploaded with the text as its caption",
119
- "operation": "sendPhoto",
120
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:247",
121
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1075"
122
- },
123
- {
124
- "call": "a post with one video",
125
- "operation": "sendVideo",
126
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:240",
127
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1164"
128
- },
129
- {
130
- "call": "a post with one file that is neither",
131
- "operation": "sendDocument",
132
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:253",
133
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1136"
134
- },
135
- {
136
- "call": "a post with several media: an album of up to ten, the text on the first",
137
- "operation": "sendMediaGroup",
138
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:272",
139
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1296"
140
- },
141
- {
142
- "call": "a comment: the next post sent as a reply to the last (reply_to_message_id)",
143
- "operation": "sendMessage",
144
- "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:226",
145
- "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
146
- }
147
- ],
148
- "name": "gitroomhq/postiz-app"
149
58
  }
150
59
  ],
151
60
  "optional": []
@@ -0,0 +1,21 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "source": "https://chat-sdk.dev/docs/adapters/telegram",
4
+ "about": "Volter Harness's Telegram channel as it ships (supercode 62620578c014, sdk/orchestrator/adapters/chat-sdk.mjs): the unchanged Chat SDK (chat 4.39.0, @chat-adapter/telegram 4.39.0, @chat-adapter/state-memory 4.39.0) polls with the bot token the World's credential door issues as TELEGRAM_BOT_TOKEN (getMe, deleteWebhook, getUpdates), hears Ada's private message (the World's people and messages doors stand in for the Telegram app) and answers in her chat (thread.post: sendMessage). The readback reads her private chat back (getChat) without polling or sending.",
5
+ "files": {
6
+ "package.json": "{\n \"name\": \"telegram-harness-customer\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"chat\": \"4.39.0\",\n \"@chat-adapter/telegram\": \"4.39.0\",\n \"@chat-adapter/state-memory\": \"4.39.0\"\n }\n}\n",
7
+ ".env.example": "TELEGRAM_BOT_TOKEN=\nTELEGRAM_BOT_USERNAME=\n",
8
+ "agent.mjs": "import assert from 'node:assert/strict';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { Chat } from 'chat';\nimport { createTelegramAdapter } from '@chat-adapter/telegram';\nimport { createMemoryState } from '@chat-adapter/state-memory';\nif (!process.env.VOLTER_WORLD) throw new Error('Run this app through its World.');\n// Volter Harness's Telegram channel: the Chat SDK's adapter from TELEGRAM_BOT_TOKEN, polling (sdk/orchestrator/adapters/chat-sdk.mjs)\nconst telegram = createTelegramAdapter({ botToken: process.env.TELEGRAM_BOT_TOKEN, mode: 'polling', logger: console });\nconst receiptFile = 'telegram-chat.json';\nif (process.argv[2] === 'readback') {\n const saved = JSON.parse(readFileSync(receiptFile, 'utf8'));\n const thread = await telegram.fetchThread(saved.thread);\n assert.equal(thread.id, saved.thread);\n assert.equal(thread.isDM, true);\n assert.equal(thread.metadata.chat.first_name, 'Ada');\n console.log(JSON.stringify({ retained: true, thread: thread.id, chat: thread.metadata.chat }));\n} else {\n // Ada opens the bot in the Telegram app and writes to it: the World's doors stand in for the app\n const api = (process.env.TELEGRAM_API_BASE_URL ?? 'https://api.telegram.org').replace(/\\/+$/, '');\n const door = async (path, body) => {\n const response = await fetch(api + path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });\n if (!response.ok) throw new Error(path + ' was refused: ' + response.status + ' ' + await response.text());\n return response.json();\n };\n const ada = await door('/_twin/people', { first_name: 'Ada', username: 'ada_lovelace' });\n await door('/_twin/messages', { from: ada.id, chat: '@' + process.env.TELEGRAM_BOT_USERNAME, text: 'Hi! Can you keep notes of our standups?' });\n const answered = new Promise((resolve, reject) => {\n const chat = new Chat({ adapters: { telegram }, state: createMemoryState(), concurrency: 'concurrent', logger: 'silent' });\n chat.onDirectMessage(async (thread, message) => {\n try {\n const sent = await thread.post('Noted, Ada: I will keep the standup notes here.');\n resolve({ chat, thread, message, sent });\n } catch (error) { reject(error); }\n });\n chat.initialize().catch(reject);\n });\n const { chat, thread, message, sent } = await answered;\n await telegram.stopPolling();\n await chat.shutdown();\n assert.equal(thread.isDM, true);\n assert.equal(message.text, 'Hi! Can you keep notes of our standups?');\n assert.equal(message.author.userId, String(ada.id));\n assert.equal(message.author.isMe, false);\n assert.equal(thread.id, 'telegram:' + ada.id);\n assert.match(String(sent.id), new RegExp('^' + ada.id + ':\\\\d+$'), 'the answer names the message Telegram gave it in her chat');\n assert.equal(sent.text, 'Noted, Ada: I will keep the standup notes here.');\n writeFileSync(receiptFile, JSON.stringify({ thread: thread.id, message: sent.id }) + '\\n');\n console.log(JSON.stringify({ answered: true, thread: thread.id, heard: message.text, sent: sent.id }));\n}\n"
9
+ },
10
+ "run": [
11
+ "node",
12
+ "agent.mjs"
13
+ ],
14
+ "retention": {
15
+ "readback": [
16
+ "node",
17
+ "agent.mjs",
18
+ "readback"
19
+ ]
20
+ }
21
+ }
@@ -22,7 +22,7 @@
22
22
  "urlUnreachable"
23
23
  ],
24
24
  "world": "sealed-refusal",
25
- "why": "A photo, video or document sent by an HTTP URL is downloaded by Telegram; a World is sealed, so the twin reaches no URL outside it and answers Telegram's failure for a URL it cannot get. No World of this surface replays the download (spec/doc-examples.json's sendPhoto: an HTTP URL is recorded not replayed for the same reason), and the only application that sends URLs is Postiz with its opt-in Cloudflare storage (journeys/demand.json, optional)."
25
+ "why": "A photo, video or document sent by an HTTP URL is downloaded by Telegram; a World is sealed, so the twin reaches no URL outside it and answers Telegram's failure for a URL it cannot get. No World of this surface replays the download (spec/doc-examples.json's sendPhoto: an HTTP URL is recorded not replayed for the same reason)."
26
26
  }
27
27
  ],
28
28
  "state": []
@@ -1221,12 +1221,20 @@
1221
1221
  },
1222
1222
  "status": 200,
1223
1223
  "capture": {
1224
- "notes_photo": "result.photo.0.file_id",
1225
- "notes_unique": "result.photo.0.file_unique_id"
1224
+ "notes_photo": "result.photo.1.file_id",
1225
+ "notes_unique": "result.photo.1.file_unique_id"
1226
1226
  },
1227
1227
  "expect": [
1228
1228
  {
1229
- "path": "result.photo.1",
1229
+ "path": "result.photo.0.width",
1230
+ "equals": 100
1231
+ },
1232
+ {
1233
+ "path": "result.photo.1.width",
1234
+ "equals": 320
1235
+ },
1236
+ {
1237
+ "path": "result.photo.2",
1230
1238
  "exists": false
1231
1239
  }
1232
1240
  ]
@@ -1242,15 +1250,15 @@
1242
1250
  },
1243
1251
  "status": 200,
1244
1252
  "undecided": {
1245
- "result.photo.0.file_id": "a resent file may answer another valid file_id; the twin answers the one sent, which only coincides (Sending files: \"a file can have different valid file_ids even for the same bot\")"
1253
+ "result.photo.1.file_id": "a resent file may answer another valid file_id; the twin answers the one sent, which only coincides (Sending files: \"a file can have different valid file_ids even for the same bot\")"
1246
1254
  },
1247
1255
  "expect": [
1248
1256
  {
1249
- "path": "result.photo.0.file_unique_id",
1257
+ "path": "result.photo.1.file_unique_id",
1250
1258
  "equals": "{notes_unique}"
1251
1259
  },
1252
1260
  {
1253
- "path": "result.photo.1",
1261
+ "path": "result.photo.2",
1254
1262
  "exists": false
1255
1263
  },
1256
1264
  {
@@ -4,15 +4,14 @@
4
4
  from its reference page, https://core.telegram.org/bots/api, as Telegram serves it (fetched 2026-10-02, HTML,
5
5
  860074 bytes, sha256 `9da688c4213864d7911fcf0f501d2d7a8c3bbc351b8b4325e85e509bcc9d969f`; the page's "Recent changes"
6
6
  head it **Bot API 10.3**, August 24, 2026).
7
- - **How it was written:** mechanically from the page's HTML by `page-to-openapi.ts`, kept here
8
- (`gunzip -c spec/bot-api.html.gz > page.html; bun spec/page-to-openapi.ts page.html openapi.json`, then `gzip -n -9`), from the bytes named above, kept here as `bot-api.html.gz`, every
9
- section of methods and types it documents (Getting updates, Available types, Available methods, Updating messages,
10
- Stickers, Rich messages, Inline mode, Payments, Telegram Passport, Games): 185 methods and 400 types.
7
+ - **How it was written:** mechanically from the page's HTML, captured as `bot-api.html.gz` with the bytes named above.
8
+ It covers the page's methods and types across Getting updates, Available types, Available methods, Updating messages,
9
+ Stickers, Rich messages, Inline mode, Payments, Telegram Passport and Games: 185 methods and 400 types.
11
10
  - A method (an `h4` with a lower-case name and a Parameter/Type/Required/Description table) is
12
11
  `POST /bot{token}/<method>` (the page's "https://api.telegram.org/bot<token>/METHOD_NAME"), its operationId the
13
12
  method's name, its tag the page's section; its parameters a body taken as JSON, form or multipart, and the same
14
13
  parameters again in the query string, none required there (the page's "four ways of passing parameters", the
15
- first of them the URL query string: Postiz's node-telegram-bot-api sends a medium's chat_id and caption in the
14
+ first of them the URL query string: node-telegram-bot-api sends a medium's chat_id and caption in the
16
15
  query beside its multipart file), `Required: Yes` required in the body; its answer `{ ok: true, result }` with the result the
17
16
  method's text says is returned (a linked type, "Array of", True, String or Integer; several, `oneOf`), and any
18
17
  other answer the error envelope `{ ok: false, error_code, description, parameters? }` (Making requests).
@@ -35,7 +34,7 @@
35
34
  - **Corrections:** `patches.json` (RFC 6902, each with its reason and source): `reply_to_message_id` added to the
36
35
  bodies and the query parameters of sendMessage, sendPhoto, sendVideo, sendDocument and sendMediaGroup. The page no longer lists it, but the Bot
37
36
  API server still reads it when a request carries no `reply_parameters` (tdlib/telegram-bot-api, Client.cpp at
38
- e3e9dd8), and node-telegram-bot-api 0.66.0, as Postiz calls it, sends it.
37
+ e3e9dd8), and node-telegram-bot-api 0.66.0 sends it.
39
38
  - **Grouping:** `grouping.json` (`{ "by": "tag" }`): every path begins `/bot{token}/`, so an operation's family is the
40
39
  page's section it is documented in.
41
40
  - **Other sources read:** the Bot API server's own source, which the page names ("Using a Local Bot API Server"),
@@ -7,7 +7,7 @@
7
7
  "type": "integer",
8
8
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
9
9
  },
10
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMessage).",
10
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMessage).",
11
11
  "source": {
12
12
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
13
13
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -20,7 +20,7 @@
20
20
  "type": "integer",
21
21
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
22
22
  },
23
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMessage).",
23
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMessage).",
24
24
  "source": {
25
25
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
26
26
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -33,7 +33,7 @@
33
33
  "type": "integer",
34
34
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
35
35
  },
36
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMessage).",
36
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMessage).",
37
37
  "source": {
38
38
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
39
39
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -46,7 +46,7 @@
46
46
  "type": "integer",
47
47
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
48
48
  },
49
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendPhoto).",
49
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendPhoto).",
50
50
  "source": {
51
51
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
52
52
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -59,7 +59,7 @@
59
59
  "type": "integer",
60
60
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
61
61
  },
62
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendPhoto).",
62
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendPhoto).",
63
63
  "source": {
64
64
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
65
65
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -72,7 +72,7 @@
72
72
  "type": "integer",
73
73
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
74
74
  },
75
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendPhoto).",
75
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendPhoto).",
76
76
  "source": {
77
77
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
78
78
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -85,7 +85,7 @@
85
85
  "type": "integer",
86
86
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
87
87
  },
88
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendVideo).",
88
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendVideo).",
89
89
  "source": {
90
90
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
91
91
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -98,7 +98,7 @@
98
98
  "type": "integer",
99
99
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
100
100
  },
101
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendVideo).",
101
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendVideo).",
102
102
  "source": {
103
103
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
104
104
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -111,7 +111,7 @@
111
111
  "type": "integer",
112
112
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
113
113
  },
114
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendVideo).",
114
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendVideo).",
115
115
  "source": {
116
116
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
117
117
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -124,7 +124,7 @@
124
124
  "type": "integer",
125
125
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
126
126
  },
127
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendDocument).",
127
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendDocument).",
128
128
  "source": {
129
129
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
130
130
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -137,7 +137,7 @@
137
137
  "type": "integer",
138
138
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
139
139
  },
140
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendDocument).",
140
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendDocument).",
141
141
  "source": {
142
142
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
143
143
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -150,7 +150,7 @@
150
150
  "type": "integer",
151
151
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
152
152
  },
153
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendDocument).",
153
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendDocument).",
154
154
  "source": {
155
155
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
156
156
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -163,7 +163,7 @@
163
163
  "type": "integer",
164
164
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
165
165
  },
166
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMediaGroup).",
166
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMediaGroup).",
167
167
  "source": {
168
168
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
169
169
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -176,7 +176,7 @@
176
176
  "type": "integer",
177
177
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
178
178
  },
179
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMediaGroup).",
179
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMediaGroup).",
180
180
  "source": {
181
181
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
182
182
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -189,7 +189,7 @@
189
189
  "type": "integer",
190
190
  "description": "If the message is a reply, ID of the original message (read when reply_parameters is absent)"
191
191
  },
192
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMediaGroup).",
192
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMediaGroup).",
193
193
  "source": {
194
194
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
195
195
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -207,7 +207,7 @@
207
207
  "type": "integer"
208
208
  }
209
209
  },
210
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMessage).",
210
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMessage).",
211
211
  "source": {
212
212
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
213
213
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -225,7 +225,7 @@
225
225
  "type": "integer"
226
226
  }
227
227
  },
228
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendPhoto).",
228
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendPhoto).",
229
229
  "source": {
230
230
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
231
231
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -243,7 +243,7 @@
243
243
  "type": "integer"
244
244
  }
245
245
  },
246
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendVideo).",
246
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendVideo).",
247
247
  "source": {
248
248
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
249
249
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -261,7 +261,7 @@
261
261
  "type": "integer"
262
262
  }
263
263
  },
264
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendDocument).",
264
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendDocument).",
265
265
  "source": {
266
266
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
267
267
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -279,7 +279,7 @@
279
279
  "type": "integer"
280
280
  }
281
281
  },
282
- "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 as Postiz calls it sends reply_to_message_id (sendMediaGroup).",
282
+ "why": "The page lists only reply_parameters, but the Bot API server still reads reply_to_message_id (and allow_sending_without_reply) when a request carries no reply_parameters, in the query string as in the body, and node-telegram-bot-api 0.66.0 sends reply_to_message_id (sendMediaGroup).",
283
283
  "source": {
284
284
  "url": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
285
285
  "quote": "result.reply_to_message_id = get_message_id(query, \"reply_to_message_id\");"
@@ -1,4 +1,12 @@
1
1
  {
2
+ "https://core.telegram.org/api/files": {
3
+ "status": 200,
4
+ "checked": "2026-10-15",
5
+ "quotes": {
6
+ "s - Server-side resized image, bounded by 100x100 pixels.": true,
7
+ "w - Server-side resized image, bounded by 2560x2560 pixels.": true
8
+ }
9
+ },
2
10
  "https://core.telegram.org/bots/api#accent-colors": {
3
11
  "status": 200,
4
12
  "checked": "2026-10-02",
@@ -355,6 +363,13 @@
355
363
  "Wrong file identifier/HTTP URL specified": true
356
364
  }
357
365
  },
366
+ "https://raw.githubusercontent.com/tdlib/td/42e6a5259551178d1dab54a22ad96d14bd906e20/td/telegram/PhotoSize.cpp": {
367
+ "status": 200,
368
+ "checked": "2026-10-15",
369
+ "quotes": {
370
+ "get_photo_sizes_object": true
371
+ }
372
+ },
358
373
  "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp": {
359
374
  "status": 200,
360
375
  "checked": "2026-10-02",
package/dist/src/cli.js CHANGED
@@ -7,7 +7,7 @@ const [cmd, ...rest] = process.argv.slice(2);
7
7
  const port = Number(optionValue(rest, '--port', '0')) || undefined;
8
8
  const root = optionValue(rest, '--root') || undefined;
9
9
  const readOnly = hasFlag(rest, '--read-only');
10
- // the World's managed Postgres, which the runtime binds a pack declaring managedDatabase to
10
+ // the selected twin's private Postgres, which the runtime binds a pack declaring managedDatabase to
11
11
  const database = optionValue(rest, '--database') || undefined;
12
12
  // the World's scenario file, for a model vendor's turns
13
13
  const scenarioPath = optionValue(rest, '--scenario') || undefined;
@@ -1,17 +1,11 @@
1
- export type ImageSize = {
1
+ /** Telegram reads PNG's IHDR, GIF's logical screen and JPEG's first frame; other formats have no photo size. */
2
+ export declare function imageSize(bytes: Uint8Array): {
2
3
  width: number;
3
4
  height: number;
4
- mime: string;
5
- };
6
- export type VideoInfo = {
5
+ } | undefined;
6
+ /** Whole-second duration from the last usable movie header, and the first track whose floored width is nonzero. */
7
+ export declare function mp4Info(bytes: Uint8Array): {
7
8
  width: number;
8
9
  height: number;
9
10
  duration: number;
10
- };
11
- /** An image's size from its header: PNG (IHDR), GIF (logical screen), JPEG (the first start-of-frame); undefined for
12
- * anything else (a photo Telegram takes is a .jpg, .jpeg, .gif, .png, .tif or .bmp, MessagesManager.cpp's
13
- * PHOTO_EXT_INVALID; the twin reads the first three). */
14
- export declare function imageSize(b: Uint8Array): ImageSize | undefined;
15
- /** An MP4's (ISO base media) duration in whole seconds from its movie header (mvhd), and the first track with a size
16
- * from its track header (tkhd: 16.16 fixed-point width and height); undefined for a file that is not one. */
17
- export declare function mp4Info(b: Uint8Array): VideoInfo | undefined;
11
+ } | undefined;