@overpod/mcp-telegram 1.34.0 → 1.35.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 CHANGED
@@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.35.0] — 2026-04-25
9
+
10
+ ### Changed
11
+
12
+ **Rate limiter — structured stderr events for retries.**
13
+
14
+ The internal `RateLimiter` previously logged `FLOOD_WAIT` and network retries as
15
+ human-readable strings via `console.error`, and the temporary-server-error
16
+ branch (5xx, etc.) was silent. All three retry branches now emit a single
17
+ structured stderr line per event:
18
+
19
+ ```
20
+ [rate-limiter] event {"event":"flood_wait","context":"list-chats","seconds":30,"attempt":1,"maxRetries":3}
21
+ ```
22
+
23
+ Event types: `flood_wait`, `network_retry`, `temporary_retry`. Each event
24
+ carries:
25
+
26
+ - `event` — event class (string literal)
27
+ - `context` — caller-supplied operation name (never user input / PII)
28
+ - `attempt` / `maxRetries` — retry counters
29
+ - `seconds` (flood_wait only) or `delayMs` (network/temporary)
30
+ - `error` (network/temporary only) — the upstream Telegram error message
31
+
32
+ This is a logging contract change, not a behaviour change: retry timing,
33
+ backoff, and error-throwing semantics are identical to 1.34.0.
34
+
35
+ ### Why this matters
36
+
37
+ Downstream log collectors can now aggregate retry rates by event class and
38
+ caller without parsing free-form English. `mcp-telegram-cloud` v1.12.0+ wires
39
+ this into SigNoz directly. Self-hosters can `grep '\[rate-limiter\] event'` or
40
+ pipe into any structured log pipeline.
41
+
42
+ ### Compatibility
43
+
44
+ No breaking changes. No new dependencies. No new environment variables.
45
+ Tests: 490/490 pass.
46
+
8
47
  ## [1.34.0] — 2026-04-24
9
48
 
10
49
  ### Added
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Rate limiter and retry logic for Telegram API calls.
3
3
  * Handles FLOOD_WAIT errors and implements exponential backoff.
4
+ *
5
+ * Emits structured events on stderr so downstream log collectors (e.g. cloud
6
+ * SigNoz) can aggregate by `event` and `context`. Format:
7
+ * [rate-limiter] event {"event":"flood_wait","context":"X","seconds":N,...}
4
8
  */
5
9
  export interface RateLimiterOptions {
6
10
  /** Maximum number of requests per second (default: 20) */
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Rate limiter and retry logic for Telegram API calls.
3
3
  * Handles FLOOD_WAIT errors and implements exponential backoff.
4
+ *
5
+ * Emits structured events on stderr so downstream log collectors (e.g. cloud
6
+ * SigNoz) can aggregate by `event` and `context`. Format:
7
+ * [rate-limiter] event {"event":"flood_wait","context":"X","seconds":N,...}
4
8
  */
5
9
  export class RateLimiter {
6
10
  minInterval;
@@ -41,7 +45,13 @@ export class RateLimiter {
41
45
  if (attempt >= this.maxRetries) {
42
46
  throw new Error(`Rate limit exceeded after ${this.maxRetries} retries. Telegram requires ${waitSeconds}s wait. Try again later.`);
43
47
  }
44
- console.error(`[rate-limiter] FLOOD_WAIT for ${context}. Waiting ${waitSeconds}s (attempt ${attempt + 1}/${this.maxRetries})`);
48
+ logEvent({
49
+ event: "flood_wait",
50
+ context,
51
+ seconds: waitSeconds,
52
+ attempt: attempt + 1,
53
+ maxRetries: this.maxRetries,
54
+ });
45
55
  await sleep(waitSeconds * 1000);
46
56
  return this.executeWithRetry(fn, context, attempt + 1, options);
47
57
  }
@@ -51,7 +61,14 @@ export class RateLimiter {
51
61
  throw new Error(`Network error after ${this.maxRetries} retries: ${errorMessage}. Check your connection.`);
52
62
  }
53
63
  const delay = Math.min(this.initialRetryDelay * 2 ** attempt, this.maxRetryDelay);
54
- console.error(`[rate-limiter] Network error for ${context}. Retrying in ${delay}ms (attempt ${attempt + 1}/${this.maxRetries})`);
64
+ logEvent({
65
+ event: "network_retry",
66
+ context,
67
+ delayMs: delay,
68
+ attempt: attempt + 1,
69
+ maxRetries: this.maxRetries,
70
+ error: errorMessage,
71
+ });
55
72
  await sleep(delay);
56
73
  return this.executeWithRetry(fn, context, attempt + 1, options);
57
74
  }
@@ -61,6 +78,14 @@ export class RateLimiter {
61
78
  throw new Error(`Temporary error after ${this.maxRetries} retries: ${errorMessage}`);
62
79
  }
63
80
  const delay = Math.min(this.initialRetryDelay * 2 ** attempt, this.maxRetryDelay);
81
+ logEvent({
82
+ event: "temporary_retry",
83
+ context,
84
+ delayMs: delay,
85
+ attempt: attempt + 1,
86
+ maxRetries: this.maxRetries,
87
+ error: errorMessage,
88
+ });
64
89
  await sleep(delay);
65
90
  return this.executeWithRetry(fn, context, attempt + 1, options);
66
91
  }
@@ -85,3 +110,6 @@ function isTemporaryError(msg) {
85
110
  function sleep(ms) {
86
111
  return new Promise((resolve) => setTimeout(resolve, ms));
87
112
  }
113
+ function logEvent(payload) {
114
+ console.error(`[rate-limiter] event ${JSON.stringify(payload)}`);
115
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.34.0",
3
+ "version": "1.35.0",
4
4
  "description": "MCP server for Telegram userbot — messages, media, reactions, polls & more. Built on GramJS/MTProto.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -63,7 +63,7 @@
63
63
  "zod": "^4.3.6"
64
64
  },
65
65
  "devDependencies": {
66
- "@biomejs/biome": "^2.4.12",
66
+ "@biomejs/biome": "^2.4.13",
67
67
  "@types/node": "^25.6.0",
68
68
  "@types/qrcode": "^1.5.6",
69
69
  "c8": "^11.0.0",