@mseep/telegram-api-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/dist/circuit-breaker.d.ts +34 -0
  4. package/dist/circuit-breaker.d.ts.map +1 -0
  5. package/dist/circuit-breaker.js +64 -0
  6. package/dist/circuit-breaker.js.map +1 -0
  7. package/dist/config.d.ts +29 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +22 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +15 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/method-registry.d.ts +78 -0
  16. package/dist/method-registry.d.ts.map +1 -0
  17. package/dist/method-registry.js +117 -0
  18. package/dist/method-registry.js.map +1 -0
  19. package/dist/methods/bot.d.ts +3 -0
  20. package/dist/methods/bot.d.ts.map +1 -0
  21. package/dist/methods/bot.js +258 -0
  22. package/dist/methods/bot.js.map +1 -0
  23. package/dist/methods/business.d.ts +3 -0
  24. package/dist/methods/business.d.ts.map +1 -0
  25. package/dist/methods/business.js +137 -0
  26. package/dist/methods/business.js.map +1 -0
  27. package/dist/methods/chat.d.ts +3 -0
  28. package/dist/methods/chat.d.ts.map +1 -0
  29. package/dist/methods/chat.js +458 -0
  30. package/dist/methods/chat.js.map +1 -0
  31. package/dist/methods/editing.d.ts +3 -0
  32. package/dist/methods/editing.d.ts.map +1 -0
  33. package/dist/methods/editing.js +153 -0
  34. package/dist/methods/editing.js.map +1 -0
  35. package/dist/methods/forum.d.ts +3 -0
  36. package/dist/methods/forum.d.ts.map +1 -0
  37. package/dist/methods/forum.js +184 -0
  38. package/dist/methods/forum.js.map +1 -0
  39. package/dist/methods/forwarding.d.ts +3 -0
  40. package/dist/methods/forwarding.d.ts.map +1 -0
  41. package/dist/methods/forwarding.js +84 -0
  42. package/dist/methods/forwarding.js.map +1 -0
  43. package/dist/methods/games.d.ts +3 -0
  44. package/dist/methods/games.d.ts.map +1 -0
  45. package/dist/methods/games.js +43 -0
  46. package/dist/methods/games.js.map +1 -0
  47. package/dist/methods/gifts.d.ts +3 -0
  48. package/dist/methods/gifts.d.ts.map +1 -0
  49. package/dist/methods/gifts.js +87 -0
  50. package/dist/methods/gifts.js.map +1 -0
  51. package/dist/methods/index.d.ts +13 -0
  52. package/dist/methods/index.d.ts.map +1 -0
  53. package/dist/methods/index.js +57 -0
  54. package/dist/methods/index.js.map +1 -0
  55. package/dist/methods/inline.d.ts +3 -0
  56. package/dist/methods/inline.d.ts.map +1 -0
  57. package/dist/methods/inline.js +32 -0
  58. package/dist/methods/inline.js.map +1 -0
  59. package/dist/methods/managed-bots.d.ts +3 -0
  60. package/dist/methods/managed-bots.d.ts.map +1 -0
  61. package/dist/methods/managed-bots.js +33 -0
  62. package/dist/methods/managed-bots.js.map +1 -0
  63. package/dist/methods/messages.d.ts +3 -0
  64. package/dist/methods/messages.d.ts.map +1 -0
  65. package/dist/methods/messages.js +357 -0
  66. package/dist/methods/messages.js.map +1 -0
  67. package/dist/methods/other.d.ts +3 -0
  68. package/dist/methods/other.d.ts.map +1 -0
  69. package/dist/methods/other.js +247 -0
  70. package/dist/methods/other.js.map +1 -0
  71. package/dist/methods/passport.d.ts +3 -0
  72. package/dist/methods/passport.d.ts.map +1 -0
  73. package/dist/methods/passport.js +15 -0
  74. package/dist/methods/passport.js.map +1 -0
  75. package/dist/methods/payments.d.ts +3 -0
  76. package/dist/methods/payments.d.ts.map +1 -0
  77. package/dist/methods/payments.js +128 -0
  78. package/dist/methods/payments.js.map +1 -0
  79. package/dist/methods/stickers.d.ts +3 -0
  80. package/dist/methods/stickers.d.ts.map +1 -0
  81. package/dist/methods/stickers.js +208 -0
  82. package/dist/methods/stickers.js.map +1 -0
  83. package/dist/methods/stories.d.ts +3 -0
  84. package/dist/methods/stories.d.ts.map +1 -0
  85. package/dist/methods/stories.js +58 -0
  86. package/dist/methods/stories.js.map +1 -0
  87. package/dist/methods/updates.d.ts +3 -0
  88. package/dist/methods/updates.d.ts.map +1 -0
  89. package/dist/methods/updates.js +48 -0
  90. package/dist/methods/updates.js.map +1 -0
  91. package/dist/post-log.d.ts +14 -0
  92. package/dist/post-log.d.ts.map +1 -0
  93. package/dist/post-log.js +48 -0
  94. package/dist/post-log.js.map +1 -0
  95. package/dist/rate-limiter.d.ts +25 -0
  96. package/dist/rate-limiter.d.ts.map +1 -0
  97. package/dist/rate-limiter.js +93 -0
  98. package/dist/rate-limiter.js.map +1 -0
  99. package/dist/server.d.ts +3 -0
  100. package/dist/server.d.ts.map +1 -0
  101. package/dist/server.js +287 -0
  102. package/dist/server.js.map +1 -0
  103. package/dist/telegram-client.d.ts +28 -0
  104. package/dist/telegram-client.d.ts.map +1 -0
  105. package/dist/telegram-client.js +254 -0
  106. package/dist/telegram-client.js.map +1 -0
  107. package/dist/trail.d.ts +86 -0
  108. package/dist/trail.d.ts.map +1 -0
  109. package/dist/trail.js +185 -0
  110. package/dist/trail.js.map +1 -0
  111. package/package.json +61 -0
  112. package/server.json +18 -0
  113. package/smithery.yaml +27 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 timoncool
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,247 @@
1
+ <div align="center">
2
+
3
+ # telegram-api-mcp
4
+
5
+ **Ultimate MCP server for Telegram Bot API — 169 methods, full v9.6 coverage, meta-mode, rate limiting, circuit breaker.**
6
+
7
+ [![Stars](https://img.shields.io/github/stars/timoncool/telegram-api-mcp?style=flat-square)](https://github.com/timoncool/telegram-api-mcp/stargazers)
8
+ [![npm](https://img.shields.io/npm/v/telegram-api-mcp?style=flat-square)](https://www.npmjs.com/package/telegram-api-mcp)
9
+ [![License](https://img.shields.io/github/license/timoncool/telegram-api-mcp?style=flat-square)](LICENSE)
10
+ [![Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-9.6-26A5E4?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api)
11
+ [![TRAIL](https://img.shields.io/badge/TRAIL-v2.1-6366f1?style=flat-square)](https://github.com/timoncool/trail-spec)
12
+
13
+ </div>
14
+
15
+ 169/169 Bot API methods with Zod validation, token masking, tool annotations, and zero bloat (2 dependencies).
16
+
17
+ ## Features
18
+
19
+ - **169/169 Bot API methods** — messages, media, polls, chats, forums, stickers, payments, business, stories, gifts, games, inline, managed bots
20
+ - **Bot API 9.6** (April 2026) — managed bots, revoting polls, shuffle options, poll descriptions
21
+ - **Meta-mode** — 2 tools instead of 169, saves ~99% context tokens
22
+ - **Rate limiting** — global (30 req/sec) + per-chat (20 msg/min), token bucket with async mutex
23
+ - **Circuit breaker** — 3-state (closed/open/half-open), auto-recovery
24
+ - **Retry with backoff** — respects Telegram 429 `retry_after`, exponential backoff on 5xx
25
+ - **Zod validation** — every parameter validated before hitting Telegram API
26
+ - **Token masking** — bot token never appears in responses, logs, or error messages
27
+ - **File upload security** — path traversal protection, configurable allowed directories
28
+ - **Tool annotations** — all 169 methods annotated (readOnly, destructive, idempotent, openWorld)
29
+ - **Response truncation** — 100K char limit to prevent context overflow
30
+ - **Zero bloat** — only 2 dependencies: `@modelcontextprotocol/sdk` + `zod`
31
+
32
+ ## Quick Start
33
+
34
+ ### Claude Code
35
+
36
+ ```bash
37
+ claude mcp add telegram -- npx telegram-api-mcp -e TELEGRAM_BOT_TOKEN=your_token
38
+ ```
39
+
40
+ With meta-mode (recommended for large conversations):
41
+
42
+ ```bash
43
+ claude mcp add telegram -- npx telegram-api-mcp \
44
+ -e TELEGRAM_BOT_TOKEN=your_token \
45
+ -e TELEGRAM_META_MODE=true
46
+ ```
47
+
48
+ ### Claude Desktop
49
+
50
+ Add to `claude_desktop_config.json`:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "telegram": {
56
+ "command": "npx",
57
+ "args": ["telegram-api-mcp"],
58
+ "env": {
59
+ "TELEGRAM_BOT_TOKEN": "your_token_from_botfather"
60
+ }
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### With default chat (skip chat_id in every call)
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "telegram": {
72
+ "command": "npx",
73
+ "args": ["telegram-api-mcp"],
74
+ "env": {
75
+ "TELEGRAM_BOT_TOKEN": "your_token",
76
+ "TELEGRAM_DEFAULT_CHAT_ID": "-1001234567890"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### From source
84
+
85
+ ```bash
86
+ git clone https://github.com/timoncool/telegram-api-mcp.git
87
+ cd telegram-api-mcp
88
+ npm install && npm run build
89
+ TELEGRAM_BOT_TOKEN=your_token node dist/index.js
90
+ ```
91
+
92
+ ## Environment Variables
93
+
94
+ | Variable | Required | Default | Description |
95
+ |----------|:---:|:---:|-------------|
96
+ | `TELEGRAM_BOT_TOKEN` | **Yes** | — | Bot token from [@BotFather](https://t.me/BotFather) |
97
+ | `TELEGRAM_DEFAULT_CHAT_ID` | No | — | Default chat ID for all tools |
98
+ | `TELEGRAM_DEFAULT_THREAD_ID` | No | — | Default forum topic thread ID |
99
+ | `TELEGRAM_META_MODE` | No | `false` | Use 2 meta-tools instead of 169 |
100
+ | `TELEGRAM_GLOBAL_RATE_LIMIT` | No | `30` | Max requests/sec ([Telegram limit](https://core.telegram.org/bots/faq#my-bot-is-hitting-limits)) |
101
+ | `TELEGRAM_PER_CHAT_RATE_LIMIT` | No | `20` | Max messages/min per group ([Telegram limit](https://core.telegram.org/bots/faq#my-bot-is-hitting-limits)) |
102
+ | `TELEGRAM_MAX_RETRIES` | No | `3` | Retry attempts on transient errors |
103
+ | `TELEGRAM_CB_THRESHOLD` | No | `5` | Failures before circuit opens |
104
+ | `TELEGRAM_CB_COOLDOWN` | No | `30000` | Circuit breaker cooldown (ms) |
105
+ | `TELEGRAM_ALLOWED_UPLOAD_DIRS` | No | — | Comma-separated allowed upload paths |
106
+ | `TELEGRAM_MAX_FILE_SIZE` | No | `52428800` | Max upload file size (50MB) |
107
+
108
+ ## Meta Mode
109
+
110
+ When `TELEGRAM_META_MODE=true`, the server exposes only 2 tools instead of 169:
111
+
112
+ - **`telegram_find`** — search methods by keyword or category
113
+ - **`telegram_call`** — call any method by name with JSON params
114
+
115
+ This saves ~99% of context tokens while keeping full API access:
116
+
117
+ ```
118
+ User: "Post a poll in my channel"
119
+ AI: → telegram_find(query: "poll")
120
+ AI: → telegram_call(method: "sendPoll", params: { chat_id: ..., question: "...", options: [...] })
121
+ ```
122
+
123
+ ## API Coverage
124
+
125
+ 169/169 methods — **100% Bot API 9.6** (April 2026)
126
+
127
+ | Category | Count | Key methods |
128
+ |----------|:---:|-------------|
129
+ | Bot | 21 | getMe, setMyCommands, setMyProfilePhoto, getFile, getUserProfilePhotos |
130
+ | Stickers | 16 | sendSticker, createNewStickerSet, uploadStickerFile, setStickerKeywords |
131
+ | Chat | 15 | getChat, setChatTitle, setChatPermissions, pinChatMessage, leaveChat |
132
+ | Business | 14 | readBusinessMessage, setBusinessAccountName, getBusinessConnection |
133
+ | Forum | 13 | createForumTopic, editForumTopic, closeForumTopic, deleteForumTopic |
134
+ | Editing | 10 | editMessageText, editMessageMedia, deleteMessage, deleteMessages, stopPoll |
135
+ | Messages | 9 | sendMessage, sendMessageDraft, sendLocation, sendContact, sendChecklist |
136
+ | Media | 9 | sendPhoto, sendVideo, sendAudio, sendDocument, sendMediaGroup, sendPaidMedia |
137
+ | Members | 9 | banChatMember, promoteChatMember, setChatMemberTag, restrictChatMember |
138
+ | Invite | 8 | createChatInviteLink, createChatSubscriptionInviteLink, approveChatJoinRequest |
139
+ | Payments | 8 | sendInvoice, createInvoiceLink, getStarTransactions, getMyStarBalance |
140
+ | Gifts | 8 | sendGift, getUserGifts, getChatGifts, giftPremiumSubscription, upgradeGift |
141
+ | Other | 5 | verifyUser, verifyChat, setUserEmojiStatus, savePreparedInlineMessage |
142
+ | Forwarding | 4 | forwardMessage, forwardMessages, copyMessage, copyMessages |
143
+ | Stories | 4 | postStory, editStory, deleteStory, repostStory |
144
+ | Inline | 4 | answerInlineQuery, answerCallbackQuery, answerWebAppQuery, savePreparedInlineMessage |
145
+ | Updates | 4 | getUpdates, setWebhook, deleteWebhook, getWebhookInfo |
146
+ | Games | 3 | sendGame, setGameScore, getGameHighScores |
147
+ | Managed Bots | 3 | getManagedBotToken, replaceManagedBotToken, savePreparedKeyboardButton |
148
+ | Polls | 1 | sendPoll (v9.6: revoting, shuffle, multiple correct, descriptions) |
149
+ | Passport | 1 | setPassportDataErrors |
150
+
151
+ ## Architecture
152
+
153
+ ```
154
+ src/
155
+ ├── index.ts # Entry point
156
+ ├── config.ts # Environment config with validation
157
+ ├── server.ts # MCP server (standard + meta mode)
158
+ ├── telegram-client.ts # HTTP client with retry, rate limit, circuit breaker
159
+ ├── rate-limiter.ts # Token bucket: global + per-chat
160
+ ├── circuit-breaker.ts # 3-state circuit breaker (closed/open/half-open)
161
+ ├── method-registry.ts # Declarative method definitions + Zod schema builder
162
+ └── methods/
163
+ ├── index.ts # Aggregator + search
164
+ ├── messages.ts # sendMessage, sendDice, sendChecklist, ...
165
+ ├── forwarding.ts # forwardMessage, copyMessage, ...
166
+ ├── editing.ts # editMessageText, deleteMessage, ...
167
+ ├── chat.ts # getChat, setChatTitle, banChatMember, ...
168
+ ├── bot.ts # getMe, setMyCommands, getFile, ...
169
+ ├── forum.ts # createForumTopic, editForumTopic, ...
170
+ ├── stickers.ts # sendSticker, createNewStickerSet, ...
171
+ ├── payments.ts # sendInvoice, getStarTransactions, ...
172
+ ├── business.ts # readBusinessMessage, setBusinessAccount*, ...
173
+ ├── stories.ts # postStory, editStory, deleteStory, ...
174
+ ├── gifts.ts # sendGift, getUserGifts, convertGiftToStars, ...
175
+ ├── games.ts # sendGame, setGameScore, ...
176
+ ├── inline.ts # answerInlineQuery, answerCallbackQuery
177
+ ├── managed-bots.ts # getManagedBotToken, replaceManagedBotToken
178
+ ├── updates.ts # getUpdates, setWebhook, ...
179
+ ├── passport.ts # setPassportDataErrors
180
+ └── other.ts # verifyUser, setChatMenuButton, ...
181
+ ```
182
+
183
+ ### Design principles
184
+
185
+ - **Declarative registry** — each method is pure data (name, params, types, annotations). One generic handler serves all 169 methods. Adding a new method = one array entry.
186
+ - **Zod validation** — every parameter validated before reaching Telegram. Clear error messages with hints instead of opaque API 400s.
187
+ - **Token bucket rate limiting** — no race conditions (async mutex). Defaults match [Telegram's official limits](https://core.telegram.org/bots/faq#my-bot-is-hitting-limits): 30 req/sec global, 20 msg/min per group.
188
+ - **Circuit breaker** — 429 (rate limit) is NOT counted as failure. Only real errors (5xx, network) trip the breaker. Half-open probe recovers automatically.
189
+ - **Tool annotations** — every method has MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so AI clients know which tools are safe to auto-approve.
190
+ - **Response truncation** — responses capped at 100K chars to prevent context window overflow.
191
+
192
+ ## Security
193
+
194
+ - Bot token never appears in MCP tool responses or error messages (masked as `***`)
195
+ - File upload paths validated against allowed directories (`TELEGRAM_ALLOWED_UPLOAD_DIRS`)
196
+ - Path traversal attacks blocked (resolve + normalize + separator check)
197
+ - No `eval()`, no `Function()`, no dynamic imports
198
+ - No external requests except `api.telegram.org`
199
+ - No telemetry, no analytics, no phone-home
200
+ - Zero bloat: only 2 runtime dependencies (`@modelcontextprotocol/sdk` + `zod`)
201
+
202
+ ## Development
203
+
204
+ ```bash
205
+ npm install
206
+ npm run build # TypeScript compilation
207
+ npm run typecheck # Type checking without emit
208
+ npm test # Run all tests (vitest)
209
+ npm run test:watch # Watch mode
210
+ npm run lint # ESLint
211
+ ```
212
+
213
+ ## Other Projects by [@timoncool](https://github.com/timoncool)
214
+
215
+ | Project | Description |
216
+ |---------|-------------|
217
+ | [civitai-mcp-ultimate](https://github.com/timoncool/civitai-mcp-ultimate) | Civitai API as MCP server |
218
+ | [trail-spec](https://github.com/timoncool/trail-spec) | TRAIL — cross-MCP content tracking protocol |
219
+ | [ACE-Step Studio](https://github.com/timoncool/ACE-Step-Studio) | AI music studio — songs, vocals, covers, videos |
220
+ | [VideoSOS](https://github.com/timoncool/videosos) | AI video production in the browser |
221
+ | [tg-challenge-bot](https://github.com/timoncool/tg-challenge-bot) | AI anti-spam bot for Telegram |
222
+ | [Bulka](https://github.com/timoncool/Bulka) | Live-coding music platform |
223
+
224
+ ## Support the Author
225
+
226
+ I build open-source software and do AI research. Most of what I create is free and available to everyone. Your donations help me keep creating without worrying about where the next meal comes from =)
227
+
228
+ **[All donation methods](https://github.com/timoncool/ACE-Step-Studio/blob/master/DONATE.md)** | **[dalink.to/nerual_dreming](https://dalink.to/nerual_dreming)** | **[boosty.to/neuro_art](https://boosty.to/neuro_art)**
229
+
230
+ - **BTC:** `1E7dHL22RpyhJGVpcvKdbyZgksSYkYeEBC`
231
+ - **ETH (ERC20):** `0xb5db65adf478983186d4897ba92fe2c25c594a0c`
232
+ - **USDT (TRC20):** `TQST9Lp2TjK6FiVkn4fwfGUee7NmkxEE7C`
233
+
234
+
235
+ ## Star History
236
+
237
+ <a href="https://www.star-history.com/?repos=timoncool%2Ftelegram-api-mcp&type=date&legend=top-left">
238
+ <picture>
239
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=timoncool/telegram-api-mcp&type=date&theme=dark&legend=top-left" />
240
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=timoncool/telegram-api-mcp&type=date&legend=top-left" />
241
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=timoncool/telegram-api-mcp&type=date&legend=top-left" />
242
+ </picture>
243
+ </a>
244
+
245
+ ## License
246
+
247
+ MIT
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Circuit breaker to prevent cascading failures when Telegram API is down.
3
+ *
4
+ * States:
5
+ * - CLOSED: normal operation, requests pass through
6
+ * - OPEN: too many failures, requests fail immediately
7
+ * - HALF_OPEN: cooldown expired, allow one probe request
8
+ *
9
+ * 429 (rate limit) does NOT count as a failure — it's expected behavior.
10
+ */
11
+ export type CircuitState = "closed" | "open" | "half_open";
12
+ export declare class CircuitBreaker {
13
+ private state;
14
+ private failureCount;
15
+ private lastFailureTime;
16
+ private threshold;
17
+ private cooldownMs;
18
+ constructor(threshold: number, cooldownMs: number);
19
+ /** Check if request is allowed. Throws if circuit is open. */
20
+ check(): void;
21
+ /** Record a successful response. Resets the breaker. */
22
+ recordSuccess(): void;
23
+ /**
24
+ * Record a failure. Does NOT count 429 as failure.
25
+ * Returns true if the circuit just opened.
26
+ */
27
+ recordFailure(statusCode?: number): boolean;
28
+ getState(): CircuitState;
29
+ getFailureCount(): number;
30
+ }
31
+ export declare class CircuitOpenError extends Error {
32
+ constructor(message: string);
33
+ }
34
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;gBAEf,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAKjD,8DAA8D;IAC9D,KAAK,IAAI,IAAI;IAiBb,wDAAwD;IACxD,aAAa,IAAI,IAAI;IAKrB;;;OAGG;IACH,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO;IAqB3C,QAAQ,IAAI,YAAY;IAIxB,eAAe,IAAI,MAAM;CAG1B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,64 @@
1
+ export class CircuitBreaker {
2
+ state = "closed";
3
+ failureCount = 0;
4
+ lastFailureTime = 0;
5
+ threshold;
6
+ cooldownMs;
7
+ constructor(threshold, cooldownMs) {
8
+ this.threshold = threshold;
9
+ this.cooldownMs = cooldownMs;
10
+ }
11
+ /** Check if request is allowed. Throws if circuit is open. */
12
+ check() {
13
+ if (this.state === "closed")
14
+ return;
15
+ if (this.state === "open") {
16
+ if (Date.now() - this.lastFailureTime >= this.cooldownMs) {
17
+ this.state = "half_open";
18
+ return; // Allow probe request
19
+ }
20
+ throw new CircuitOpenError(`Circuit breaker is OPEN. ${this.failureCount} consecutive failures. ` +
21
+ `Will retry in ${Math.ceil((this.cooldownMs - (Date.now() - this.lastFailureTime)) / 1000)}s.`);
22
+ }
23
+ // half_open — allow through
24
+ }
25
+ /** Record a successful response. Resets the breaker. */
26
+ recordSuccess() {
27
+ this.failureCount = 0;
28
+ this.state = "closed";
29
+ }
30
+ /**
31
+ * Record a failure. Does NOT count 429 as failure.
32
+ * Returns true if the circuit just opened.
33
+ */
34
+ recordFailure(statusCode) {
35
+ // 429 is rate limiting, not a server failure
36
+ if (statusCode === 429)
37
+ return false;
38
+ this.failureCount++;
39
+ this.lastFailureTime = Date.now();
40
+ // In half_open, any failure immediately reopens
41
+ if (this.state === "half_open") {
42
+ this.state = "open";
43
+ return true;
44
+ }
45
+ if (this.failureCount >= this.threshold) {
46
+ this.state = "open";
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ getState() {
52
+ return this.state;
53
+ }
54
+ getFailureCount() {
55
+ return this.failureCount;
56
+ }
57
+ }
58
+ export class CircuitOpenError extends Error {
59
+ constructor(message) {
60
+ super(message);
61
+ this.name = "CircuitOpenError";
62
+ }
63
+ }
64
+ //# sourceMappingURL=circuit-breaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,cAAc;IACjB,KAAK,GAAiB,QAAQ,CAAC;IAC/B,YAAY,GAAG,CAAC,CAAC;IACjB,eAAe,GAAG,CAAC,CAAC;IACpB,SAAS,CAAS;IAClB,UAAU,CAAS;IAE3B,YAAY,SAAiB,EAAE,UAAkB;QAC/C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,8DAA8D;IAC9D,KAAK;QACH,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO;QAEpC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACzD,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;gBACzB,OAAO,CAAC,sBAAsB;YAChC,CAAC;YACD,MAAM,IAAI,gBAAgB,CACxB,4BAA4B,IAAI,CAAC,YAAY,yBAAyB;gBACpE,iBAAiB,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CACjG,CAAC;QACJ,CAAC;QAED,4BAA4B;IAC9B,CAAC;IAED,wDAAwD;IACxD,aAAa;QACX,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,UAAmB;QAC/B,6CAA6C;QAC7C,IAAI,UAAU,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QAErC,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAElC,gDAAgD;QAChD,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Server configuration from environment variables.
3
+ */
4
+ export interface Config {
5
+ /** Telegram Bot token from @BotFather */
6
+ botToken: string;
7
+ /** Default chat_id for all tools (optional) */
8
+ defaultChatId?: string;
9
+ /** Default message_thread_id for forum topics (optional) */
10
+ defaultThreadId?: number;
11
+ /** Global rate limit: max requests per second (default: 30, Telegram FAQ limit) */
12
+ globalRateLimit: number;
13
+ /** Per-chat rate limit: max messages per minute to same group (default: 20, Telegram FAQ limit) */
14
+ perChatRateLimit: number;
15
+ /** Max retry attempts on transient errors (default: 3) */
16
+ maxRetries: number;
17
+ /** Circuit breaker: consecutive failures to open circuit (default: 5) */
18
+ circuitBreakerThreshold: number;
19
+ /** Circuit breaker: cooldown in ms before half-open (default: 30000) */
20
+ circuitBreakerCooldown: number;
21
+ /** Allowed directories for file uploads (comma-separated, empty = no restriction) */
22
+ allowedUploadDirs: string[];
23
+ /** Max file size in bytes (default: 50MB) */
24
+ maxFileSize: number;
25
+ /** Run in meta-mode with 2 tools instead of all (default: false) */
26
+ metaMode: boolean;
27
+ }
28
+ export declare function loadConfig(): Config;
29
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,eAAe,EAAE,MAAM,CAAC;IACxB,mGAAmG;IACnG,gBAAgB,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qFAAqF;IACrF,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,MAAM,CAyBnC"}
package/dist/config.js ADDED
@@ -0,0 +1,22 @@
1
+ export function loadConfig() {
2
+ const token = process.env.TELEGRAM_BOT_TOKEN;
3
+ if (!token) {
4
+ throw new Error("TELEGRAM_BOT_TOKEN is required. Get one from @BotFather: https://t.me/BotFather");
5
+ }
6
+ const threadId = process.env.TELEGRAM_DEFAULT_THREAD_ID;
7
+ const uploadDirs = process.env.TELEGRAM_ALLOWED_UPLOAD_DIRS;
8
+ return {
9
+ botToken: token,
10
+ defaultChatId: process.env.TELEGRAM_DEFAULT_CHAT_ID || undefined,
11
+ defaultThreadId: threadId ? parseInt(threadId, 10) : undefined,
12
+ globalRateLimit: parseInt(process.env.TELEGRAM_GLOBAL_RATE_LIMIT || "30", 10),
13
+ perChatRateLimit: parseInt(process.env.TELEGRAM_PER_CHAT_RATE_LIMIT || "20", 10),
14
+ maxRetries: parseInt(process.env.TELEGRAM_MAX_RETRIES || "3", 10),
15
+ circuitBreakerThreshold: parseInt(process.env.TELEGRAM_CB_THRESHOLD || "5", 10),
16
+ circuitBreakerCooldown: parseInt(process.env.TELEGRAM_CB_COOLDOWN || "30000", 10),
17
+ allowedUploadDirs: uploadDirs ? uploadDirs.split(",").map((d) => d.trim()) : [],
18
+ maxFileSize: parseInt(process.env.TELEGRAM_MAX_FILE_SIZE || String(50 * 1024 * 1024), 10),
19
+ metaMode: process.env.TELEGRAM_META_MODE === "true",
20
+ };
21
+ }
22
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA4BA,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IAExD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IAE5D,OAAO;QACL,QAAQ,EAAE,KAAK;QACf,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,SAAS;QAChE,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QAC9D,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,IAAI,EAAE,EAAE,CAAC;QAC7E,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,IAAI,EAAE,EAAE,CAAC;QAChF,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,GAAG,EAAE,EAAE,CAAC;QACjE,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,EAAE,EAAE,CAAC;QAC/E,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,EAAE,EAAE,CAAC;QACjF,iBAAiB,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QAC/E,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,MAAM,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;QACzF,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM;KACpD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig } from "./config.js";
3
+ import { startServer } from "./server.js";
4
+ async function main() {
5
+ try {
6
+ const config = loadConfig();
7
+ await startServer(config);
8
+ }
9
+ catch (error) {
10
+ process.stderr.write(`Fatal: ${error.message}\n`);
11
+ process.exit(1);
12
+ }
13
+ }
14
+ main();
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAW,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { z, ZodTypeAny } from "zod";
2
+ /**
3
+ * Declarative method definition.
4
+ * Each Bot API method is described as data — not as a handler.
5
+ * The MCP server auto-generates tools from these definitions.
6
+ */
7
+ export interface MethodDef {
8
+ /** Telegram Bot API method name (camelCase) */
9
+ apiMethod: string;
10
+ /** MCP tool name (snake_case) */
11
+ toolName: string;
12
+ /** Human-readable description for AI agents */
13
+ description: string;
14
+ /** Category for grouping and meta-mode search */
15
+ category: MethodCategory;
16
+ /** Parameter definitions */
17
+ params: ParamDef[];
18
+ /** Does this method need chat_id? (for rate limiting and defaults) */
19
+ needsChatId: boolean;
20
+ /** Can this method upload files? */
21
+ canUploadFiles: boolean;
22
+ /** Return type description */
23
+ returns: string;
24
+ /** MCP tool annotations — hints for clients about tool behavior */
25
+ annotations?: ToolAnnotations;
26
+ }
27
+ /** MCP Tool Annotations per spec 2025-06-18 */
28
+ export interface ToolAnnotations {
29
+ /** Tool only reads data, doesn't modify anything */
30
+ readOnlyHint?: boolean;
31
+ /** Tool may perform destructive/irreversible actions (default: true!) */
32
+ destructiveHint?: boolean;
33
+ /** Calling with same args gives same result */
34
+ idempotentHint?: boolean;
35
+ /** Tool interacts with external entities */
36
+ openWorldHint?: boolean;
37
+ }
38
+ /** Common annotation presets for DRY */
39
+ export declare const ANNOTATIONS: {
40
+ /** GET methods — read only, no side effects */
41
+ readonly readOnly: ToolAnnotations;
42
+ /** SEND methods — create content, not destructive */
43
+ readonly send: ToolAnnotations;
44
+ /** SET/EDIT methods — modify existing, idempotent */
45
+ readonly modify: ToolAnnotations;
46
+ /** DELETE/BAN methods — destructive, irreversible */
47
+ readonly destructive: ToolAnnotations;
48
+ };
49
+ export type MethodCategory = "updates" | "bot" | "messages" | "editing" | "forwarding" | "media" | "polls" | "chat" | "members" | "invite" | "forum" | "stickers" | "inline" | "payments" | "business" | "stories" | "gifts" | "games" | "passport" | "managed_bots" | "other";
50
+ export interface ParamDef {
51
+ /** Parameter name as in Bot API */
52
+ name: string;
53
+ /** Zod type for validation */
54
+ type: ZodTypeAny;
55
+ /** Is this parameter required? */
56
+ required: boolean;
57
+ /** Human-readable description */
58
+ description: string;
59
+ }
60
+ export declare const ChatId: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
61
+ export declare const MessageId: z.ZodNumber;
62
+ export declare const UserId: z.ZodNumber;
63
+ export declare const Text: z.ZodString;
64
+ export declare const Caption: z.ZodString;
65
+ export declare const ParseMode: z.ZodEnum<["HTML", "Markdown", "MarkdownV2"]>;
66
+ export declare const FileInput: z.ZodString;
67
+ export declare const ReplyMarkup: z.ZodAny;
68
+ export declare const ReplyParameters: z.ZodAny;
69
+ export declare const MessageEntities: z.ZodAny;
70
+ export declare const LinkPreviewOptions: z.ZodAny;
71
+ export declare const BooleanFlag: z.ZodBoolean;
72
+ export declare const PositiveInt: z.ZodNumber;
73
+ export declare function commonSendParams(): ParamDef[];
74
+ export declare function commonMediaParams(): ParamDef[];
75
+ export declare function commonEditParams(): ParamDef[];
76
+ export declare function buildZodSchema(params: ParamDef[]): z.ZodObject<Record<string, ZodTypeAny>>;
77
+ export declare function buildJsonSchema(params: ParamDef[]): Record<string, unknown>;
78
+ //# sourceMappingURL=method-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method-registry.d.ts","sourceRoot":"","sources":["../src/method-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEpC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,EAAE,cAAc,CAAC;IACzB,4BAA4B;IAC5B,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,oCAAoC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wCAAwC;AACxC,eAAO,MAAM,WAAW;IACtB,+CAA+C;uBACwD,eAAe;IACtH,qDAAqD;mBACgD,eAAe;IACpH,qDAAqD;qBACiD,eAAe;IACrH,qDAAqD;0BACsD,eAAe;CAClH,CAAC;AAEX,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,KAAK,GACL,UAAU,GACV,SAAS,GACT,YAAY,GACZ,OAAO,GACP,OAAO,GACP,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,GACP,UAAU,GACV,QAAQ,GACR,UAAU,GACV,UAAU,GACV,SAAS,GACT,OAAO,GACP,OAAO,GACP,UAAU,GACV,cAAc,GACd,OAAO,CAAC;AAEZ,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,eAAO,MAAM,MAAM,wCAA2E,CAAC;AAC/F,eAAO,MAAM,SAAS,aAA0C,CAAC;AACjE,eAAO,MAAM,MAAM,aAAuC,CAAC;AAC3D,eAAO,MAAM,IAAI,aAAsE,CAAC;AACxF,eAAO,MAAM,OAAO,aAA0D,CAAC;AAC/E,eAAO,MAAM,SAAS,+CAAyE,CAAC;AAChG,eAAO,MAAM,SAAS,aAAkE,CAAC;AACzF,eAAO,MAAM,WAAW,UAAoG,CAAC;AAC7H,eAAO,MAAM,eAAe,UAA6C,CAAC;AAC1E,eAAO,MAAM,eAAe,UAAqD,CAAC;AAClF,eAAO,MAAM,kBAAkB,UAAgD,CAAC;AAChF,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,WAAW,aAA8B,CAAC;AAIvD,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAW7C;AAED,wBAAgB,iBAAiB,IAAI,QAAQ,EAAE,CAM9C;AAED,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAO7C;AAID,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAQ1F;AAID,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgB3E"}