Skip to content

feat: Telegram provider (direct Bot API)#16

Closed
VanditKumar (KumarVandit) wants to merge 83 commits into
photon-hq:mainfrom
KumarVandit:telegram-support
Closed

feat: Telegram provider (direct Bot API)#16
VanditKumar (KumarVandit) wants to merge 83 commits into
photon-hq:mainfrom
KumarVandit:telegram-support

Conversation

@KumarVandit
Copy link
Copy Markdown
Member

@KumarVandit VanditKumar (KumarVandit) commented Apr 18, 2026

Summary

Adds a Telegram provider for Spectrum, written directly against the public Bot API.

Architecture

  • Schema-first. bot-api-spec/schema/telegram.json defines every Bot API method and type the provider touches. The generator at bot-api-spec/generators/typescript.ts emits providers/telegram/generated/{types,methods}.ts.
  • Thin runtime. providers/telegram/runtime/ wraps fetch with typed invoke<M>(), multipart handling, retry, and long polling.
  • Surface is scoped to Spectrum's universal Platform contract; expanding the API surface goes through schema edit → regenerate.

Regenerate with bun run gen:telegram (wired into the root package.json).

Universal Spectrum APIs implemented

API Status
space.send(content) Supported
message.reply(content) Supported
sent.edit(newContent) Text + richlink
space.startTyping() Supported
space.stopTyping() No-op (Telegram auto-expires typing)
message.react(emoji) Supported

Content types

Inbound (events.messages) and outbound (send) cover:

  • text
  • attachment (photo / document / audio / video, mime-routed)
  • voice
  • contact (with vCard round-trip)
  • richlink (link preview)
  • poll (with per-vote diff stream on poll_answer)
  • reaction (newly-added emoji only)
  • group (Telegram albums; coalesced when coalesceAlbums: true)
  • reply, edit, typing — control content

custom, poll_option (inbound-only), and effect (iMessage-only) throw UnsupportedError.

Runtime features

  • Long-polling with allowed_updates, abort propagation, optional dropPendingUpdates on startup
  • 429 handling with retry_after; 5xx + network backoff with jitter; at-most-once policy for mutating methods
  • migrate_to_chat_id auto-follow on group → supergroup migrations
  • Per-request timeout (requestTimeoutMs, default 60s) covers retries + backoff
  • Album coalescing with debounce + ceiling-flush + concurrent-capacity eviction
  • Poll vote diff stream — caches prior vote vectors and emits per-option selected: true / false events
  • Reaction extraction from MessageReactionUpdated diffs (add-only, emoji-only)
  • Bounded LRU caches (via quick-lru) for messages, polls, and prior votes
  • Lazy attachment download — getFile URL resolved on each .read() / .stream() so messages stay readable beyond the file-URL TTL
  • Graceful shutdown via AbortController; in-flight albums drain on teardown

File layout

packages/spectrum-ts/src/providers/telegram/
  index.ts            # definePlatform wiring
  types.ts            # zod config schemas, TelegramRuntime
  identity.ts         # user/chat → spectrum sender/space mapping
  messages.ts         # send/reply/edit/react/typing dispatch
  events/
    index.ts          # message stream, album buffer wiring
    inbound.ts        # Message → Content mapping
    polls.ts          # poll_answer diff → poll_option events
    reactions.ts      # MessageReactionUpdated diff → reaction events
  generated/          # generated from schema, do not edit
    types.ts
    methods.ts
  runtime/
    client.ts         # fetch wrapper, multipart, retries, migrations
    errors.ts         # TelegramApiError / TelegramNetworkError
    retry.ts          # RetryPolicy + withRetry
    polling.ts        # pollUpdates async iterable
    cache.ts          # LRU + album buffer + poll store

bot-api-spec/
  README.md
  schema/telegram.json
  generators/typescript.ts

Test plan

  • Typecheck: bun x tsc --noEmit -p packages/spectrum-ts/tsconfig.json
  • Lint: bun run check
  • Build: bun run build (emits dist/providers/telegram/index.js)
  • Live smoke: text, photo, voice, contact, richlink against a real bot
  • Reply, edit, react
  • Typing indicator
  • Poll send + vote diff stream
  • Album receive (coalesced + uncoalesced)
  • dropPendingUpdates: true on restart
  • Supergroup migration retry

Example bots will land separately once the provider is merged.

Summary by CodeRabbit

  • New Features

    • Added a Telegram platform: send/receive text, rich links, attachments, voice, contacts, reactions, polls; edit/reply/typing; long-poll streaming, reliable retry, file upload/download, per-message lookup, poll vote diffs, and optional album coalescing.
  • Documentation

    • Added Telegram docs entry and a Bot API spec README with usage and generator guidance.
  • Chores

    • Updated ignore rules, added generator/build scripts, and added a small runtime cache dependency.

Review Change Stack

Implements a full Telegram provider for spectrum-ts covering 155 of ~180
grammy Bot API methods, including:

- Core messaging (send, edit, delete, forward, copy, pin, reply, react)
- Rich content (polls, locations, contacts, venues, dice, stickers, media groups, animations, voice, video notes)
- Inline keyboards, callback queries, chat actions
- Payments (invoices, shipping, pre-checkout, paid media, Telegram Stars)
- Business mode (connections, messages, account management, checklists, stories)
- Chat management (ban, restrict, promote, permissions, titles, photos, invite links)
- Forum topics (create, edit, close, reopen, delete, general forum management)
- Sticker management (full CRUD for sticker sets, upload, position, emoji, keywords, masks)
- Bot profile (get/set name, description, commands, menu button, profile photo, admin rights)
- Webhook management (set, delete, get info)
- Gifts & monetization (available gifts, user/chat gifts, gift premium, transfer, upgrade, convert)
- Event streams (messages, edits, channel posts, callbacks, reactions, join requests, member updates, boosts, poll answers, payments, business events)
- Error handling with custom TelegramError, structured logging, and retry with exponential backoff
- Extends PlatformDef with forwardMessage, copyMessage, pinMessage, unpinMessage actions

New files:
  packages/spectrum-ts/src/providers/telegram/{index,messages,types,errors,webhook}.ts

Modified core files to support new platform actions:
  platform/types.ts, platform/define.ts, spectrum.ts, types/message.ts, types/space.ts
Grammy's bot.on() handlers fire from both polling and webhook-delivered
updates. When webhook is configured, the HTTP server feeds updates into
grammy via webhookCallback(), so bot.start() (long-polling) must not run.
Also fixes destroyClient to only call bot.stop() in polling mode.
Long-polling via bot.start() is the only supported mode for now.
Webhook mode (setWebhook, deleteWebhook, getWebhookInfo, HTTP server)
is listed under "Upcoming / Future Integration Areas" and will be
implemented properly when the PlatformDef events system supports it.
Supports both long-polling (default) and webhook mode. When
config.webhook is set, an HTTP server is started and Telegram
is told to POST updates to the specified URL. grammy's bot.on()
handlers fire identically in both modes — the only difference
is the transport.

- webhook.ts: HTTP server with webhookCallback, secret token
  verification, and automatic setWebhook/deleteWebhook lifecycle
- messages.ts: useWebhook flag skips bot.start()/bot.stop() when
  webhook server handles update delivery
- index.ts: createClient starts webhook server when configured,
  destroyClient closes server or stops polling accordingly
- types.ts: WebhookConfig schema, WebhookInfo interface
- Static methods: setWebhook, deleteWebhook, getWebhookInfo for
  manual webhook control outside the automatic lifecycle
Inline Mode:
- inlineQueries event stream for incoming inline queries
- chosenInlineResults event stream for chosen result feedback
- answerInlineQuery to respond with results
- savePreparedInlineMessage for Mini App pre-prepared messages
- Inline edit variants: editMessageTextInline,
  editMessageCaptionInline, editMessageReplyMarkupInline,
  editMessageLiveLocationInline, stopMessageLiveLocationInline

HTML5 Games:
- sendGame to send a game message
- setGameScore / setGameScoreInline to update scores
- getGameHighScores / getGameHighScoresInline for leaderboards

All implementations use grammy's typed API directly with no
workarounds. Both features removed from "Upcoming" comment.
Strip out the built-in webhook HTTP server — webhook support will be
added at the Spectrum framework level across all providers. Keeps
setWebhook/deleteWebhook/getWebhookInfo as static API utilities.

Refactor all event streams to use an eager EventSink pattern: bot.on()
handlers register in createClient before bot.start(), sinks buffer
events until streams are iterated. Fixes inline mode and other streams
that previously failed due to grammy middleware freezing.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a complete Telegram provider: Bot API schema & generator, Zod types, TelegramClient with retry/abort, long-polling, caches (messages/polls/albums), inbound/outbound conversion, event mapping (reactions/polls), platform wiring, and build/docs updates.

Changes

Telegram Provider Platform

Layer / File(s) Summary
Bot API Schema & Generator
packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json, packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts, packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
Telegram Bot API v9.6 schema, TypeScript generator, and spec README documenting the DSL and regeneration workflow.
Type Definitions
packages/spectrum-ts/src/providers/telegram/types.ts
Zod schemas for TelegramConfig, user/space/message shapes, cache knobs, and TelegramRuntime types.
Runtime Client, Errors & Retry
packages/spectrum-ts/src/providers/telegram/runtime/client.ts, packages/spectrum-ts/src/providers/telegram/runtime/errors.ts, packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
TelegramClient with multipart vs JSON body selection, download helpers, TelegramApiError/NetworkError, abort-aware retry/backoff, and per-method retry policy.
Long-Polling
packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
pollUpdates async generator with timeout normalization, allowed_updates defaults, and optional pending-update discard.
Runtime Cache
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
AlbumBuffer, QuickLRU message cache, PollStore for polls + per-voter vote vectors, defaults and factory.
Messages Stream & Orchestration
packages/spectrum-ts/src/providers/telegram/events/index.ts
Managed messages stream wiring pollUpdates → conversion → caching and album buffering; stream lifecycle and draining logic.
Inbound Conversion
packages/spectrum-ts/src/providers/telegram/events/inbound.ts
toTelegramMessage conversion, lazy file downloads for attachments/voice, richlink/contact/poll detection, and album coalescing.
Event Mapping
packages/spectrum-ts/src/providers/telegram/events/polls.ts, packages/spectrum-ts/src/providers/telegram/events/reactions.ts
poll_answer diffing into per-option events; MessageReactionUpdated → per-emoji reaction events with cache fallback stubs.
Outbound Send
packages/spectrum-ts/src/providers/telegram/messages.ts
Dispatcher routes Spectrum Content to Bot API methods (text, richlink, attachment, voice, contact, poll, group, edit, reaction, typing, reply), handles uploads, vCard limits, poll state caching, and outbound caching.
Identity & Platform Wiring
packages/spectrum-ts/src/providers/telegram/identity.ts, packages/spectrum-ts/src/providers/telegram/index.ts
user/chat normalization and definePlatform("Telegram") wiring: lifecycle.createClient/destroyClient, user/space.resolve, messages handler, send action, and actions.getMessage backed by cache.
Build & Docs
packages/spectrum-ts/tsup.config.ts, package.json, packages/spectrum-ts/package.json, README.md, .gitignore
tsup entry for provider, gen:telegram scripts, quick-lru dependency, README Platforms update, and .gitignore additions (test-results/, telegram-full-backup/).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • underthestars-zhy

Poem

🐰 I hopped through schema, cache, and stream,
stitched retries, downloads, and a generator dream,
From JSON spec to send() shining bright,
albums coalesced and reactions take flight,
Telegram now chats with Spectrum tonight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Telegram provider (direct Bot API)' directly and clearly describes the main change: adding a Telegram provider implementation using the direct Telegram Bot API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/spectrum-ts/package.json`:
- Around line 42-45: The package manifest is missing an explicit dependency on
"@grammyjs/types" while the code imports it directly (import from
"@grammyjs/types" in the Telegram provider files); add "@grammyjs/types" to the
package.json dependencies with a compatible version (matching the installed
grammy version) so pnpm/PnP resolve it, or alternatively remove direct imports
of "@grammyjs/types" and use types exposed by "grammy" instead.

In `@packages/spectrum-ts/src/providers/telegram/errors.ts`:
- Around line 99-114: The rate-limit and server-error branches (checking wrapped
instanceof TelegramError && wrapped.retryAfter and wrapped.errorCode >= 500)
must not sleep/continue when attempt === maxRetries; update those branches to
first check attempt < maxRetries before calling sleep and continue, and if
attempt >= maxRetries rethrow or throw wrapped immediately so the caller does
not incur a final wait. Use the existing symbols (wrapped, TelegramError,
retryAfter, errorCode, attempt, maxRetries, sleep, logger.warn) to locate and
modify the logic.

In `@packages/spectrum-ts/src/providers/telegram/index.ts`:
- Around line 400-2053: The exported Telegram API methods (e.g., sendInvoice,
sendMessage-like wrappers such as sendInvoice, sendGift, getChatInfo, etc.)
currently expect a raw TelegramClient but are mounted onto the telegram function
via definePlatform (def.static) where callers will pass
SpectrumLike/Space/Message; update these wrappers to accept the public types
(SpectrumLike | Space | Message or a union) or resolve a TelegramClient from
those inputs inside each method (e.g., add a helper
getTelegramClientFrom(spectrum) and call it at the start of sendInvoice,
getChatInfo, sendGift, answerCallbackQuery, etc.), or alternatively change the
mounting so def.static is applied to the client instance instead of the
function; ensure all methods that reference TelegramClient (unique symbol
TelegramClient and functions like sendInvoice, createInvoiceLink, getChatInfo,
answerCallbackQuery) are adjusted to obtain the internal client before calling
the underlying bot helpers.
- Around line 264-295: The EventSink's unbounded buffer can OOM; modify
createSink and sinkToStream to enforce a fixed max buffer size (e.g.
MAX_BUFFER_SIZE) and a clear policy: in createSink's push(value) check
sink.buffer.length and either drop oldest entries (shift) or drop the incoming
value when full, and optionally track a dropped counter; in sinkToStream only
emit up to MAX_BUFFER_SIZE items when draining sink.buffer and then clear the
buffer; ensure sink.buffer never grows beyond MAX_BUFFER_SIZE and keep the
symbols EventSink, createSink, sinkToStream and buffer to locate where to add
the size constant and the drop logic.
- Around line 2390-2420: The current code calls bot.start() without awaiting it
and swallows errors with .catch(), so createClient may return while polling
fails; change this by removing the .catch() on bot.start(), await the
bot.start() promise (so errors propagate and fail initialization), and rely on
the existing bot.catch(...) runtime error handler for ongoing errors; locate the
bot.start invocation in createClient (symbol: bot.start) and replace the
non-awaited call with an awaited call that is not caught locally so startup
errors propagate.

In `@packages/spectrum-ts/src/providers/telegram/types.ts`:
- Around line 202-217: The ChatPermissions interface is out of date: rename the
property canSendOther to canSendOtherMessages and add the new canEditTag
boolean; update the exported interface ChatPermissions to replace the old key
and include canEditTag so the shape matches the Telegram Bot API (v9.5) while
keeping all other existing optional boolean fields unchanged.
- Around line 268-279: The SendPollParams interface is out of date with Telegram
Bot API 9.6: replace the singular correctOptionId with correctOptionIds
(number[]), and add the new boolean flags allowsRevoting, shuffleOptions,
allowAddingOptions, and hideResultsUntilCloses as optional properties on the
SendPollParams interface; remove correctOptionId and update any code that
builds/serializes poll payloads (e.g., callers of SendPollParams and any
JSON/snake_case conversion logic) to emit correct_option_ids (array) and the new
fields when present.

In `@packages/spectrum-ts/src/spectrum.ts`:
- Around line 181-196: The editMessage implementation currently accepts a rest
array and calls resolveContents which can return multiple items but then
re-edits the same messageId in a loop (so only the last content wins); change
editMessage to enforce a single content payload: either 1) narrow the public API
to accept a single ContentInput (remove rest params and resolveContents usage)
or 2) keep the rest API but validate after resolveContents and throw/reject if
resolved.length !== 1, then call definition.actions.editMessage exactly once
with that single item; update the corresponding type/handler in define.ts (the
platform define for editMessage) so the signature and validation mirror this
single-content contract (referencing editMessage, resolveContents, and
definition.actions.editMessage).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e37418ab-9e00-4d17-a9ca-d641b3c6d39b

📥 Commits

Reviewing files that changed from the base of the PR and between 431b731 and d6e8e57.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .gitignore
  • examples/basic/index.ts
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/platform/define.ts
  • packages/spectrum-ts/src/platform/types.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/spectrum.ts
  • packages/spectrum-ts/src/types/message.ts
  • packages/spectrum-ts/src/types/space.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsup.config.ts
  • examples/basic/index.ts
  • packages/spectrum-ts/src/platform/define.ts
  • packages/spectrum-ts/src/types/space.ts
  • packages/spectrum-ts/src/types/message.ts
  • packages/spectrum-ts/src/spectrum.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/platform/types.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsup.config.ts
  • examples/basic/index.ts
  • packages/spectrum-ts/src/platform/define.ts
  • packages/spectrum-ts/src/types/space.ts
  • packages/spectrum-ts/src/types/message.ts
  • packages/spectrum-ts/src/spectrum.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/platform/types.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
🧠 Learnings (2)
📚 Learning: 2026-04-13T23:00:20.897Z
Learnt from: CR
Repo: photon-hq/spectrum-ts PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-13T23:00:20.897Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : Don't use `.only` or `.skip` in committed code in test files

Applied to files:

  • .gitignore
📚 Learning: 2026-04-13T23:00:20.897Z
Learnt from: CR
Repo: photon-hq/spectrum-ts PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-13T23:00:20.897Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Avoid barrel files (index files that re-export everything) in JavaScript/TypeScript

Applied to files:

  • packages/spectrum-ts/tsup.config.ts
🔇 Additional comments (1)
.gitignore (1)

39-40: The rule change is appropriate. Verification shows no tracked source files currently exist under test/, so ignoring it does not hide any tracked sources. The comment indicates the intent is to ignore test results/artifacts, which is a valid use of this pattern.

			> Likely an incorrect or invalid review comment.

Comment thread packages/spectrum-ts/package.json Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/errors.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/index.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/index.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/index.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/types.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/types.ts Outdated
Comment thread packages/spectrum-ts/src/spectrum.ts Outdated
- Add @grammyjs/types as explicit dependency for strict package managers
- Fix withRetry to skip sleep on final attempt before throwing
- Cap EventSink buffer at 1000 events to prevent unbounded memory growth
- Rename ChatPermissions.canSendOther to canSendOtherMessages, add canEditTag
- Update SendPollParams with Bot API 7.6+ fields (correctOptionIds,
  allowsRevoting, shuffleOptions, allowAddingOptions, hideResultsUntilCloses)
- Fix edit() to only apply the last content item instead of looping
@coderabbitai coderabbitai Bot added the release Just as it is label Apr 18, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/spectrum-ts/src/providers/telegram/index.ts (1)

2395-2425: ⚠️ Potential issue | 🟠 Major

Surface polling startup failures before returning the client.

createClient returns immediately after scheduling bot.start(), and the chained .catch() only logs failures. In grammY, bot.start() itself never resolves until the bot is stopped, while onStart is the readiness hook that runs after setup and right before the first updates are fetched. That means a startup failure in long polling can leave callers with a client that was “created” successfully but never starts receiving events. (grammy.dev)

Possible fix
-      bot
-        .start({
-          allowed_updates: [
-            "message",
-            "edited_message",
-            "channel_post",
-            "edited_channel_post",
-            "inline_query",
-            "chosen_inline_result",
-            "callback_query",
-            "shipping_query",
-            "pre_checkout_query",
-            "poll",
-            "poll_answer",
-            "my_chat_member",
-            "chat_member",
-            "chat_join_request",
-            "chat_boost",
-            "removed_chat_boost",
-            "message_reaction",
-            "message_reaction_count",
-            "business_connection",
-            "business_message",
-            "edited_business_message",
-            "deleted_business_messages",
-            "purchased_paid_media",
-          ],
-        })
-        .catch((err) => {
-          logger.error("Bot polling failed", err);
-        });
+      await new Promise<void>((resolve, reject) => {
+        void bot
+          .start({
+            allowed_updates: [
+              "message",
+              "edited_message",
+              "channel_post",
+              "edited_channel_post",
+              "inline_query",
+              "chosen_inline_result",
+              "callback_query",
+              "shipping_query",
+              "pre_checkout_query",
+              "poll",
+              "poll_answer",
+              "my_chat_member",
+              "chat_member",
+              "chat_join_request",
+              "chat_boost",
+              "removed_chat_boost",
+              "message_reaction",
+              "message_reaction_count",
+              "business_connection",
+              "business_message",
+              "edited_business_message",
+              "deleted_business_messages",
+              "purchased_paid_media",
+            ],
+            onStart: () => resolve(),
+          })
+          .catch(reject);
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/spectrum-ts/src/providers/telegram/index.ts` around lines 2395 -
2425, createClient currently calls bot.start(...) and returns immediately, so
start-up failures can be missed; change createClient to await bot.start by
wrapping it in a Promise that resolves when the bot's onStart handler runs and
rejects if bot.start rejects. Concretely: inside createClient, register an
onStart callback (bot.on("start"/bot.on("message"? use grammY's onStart hook) —
use the existing onStart registration in this module) to call resolve(), call
bot.start(...).catch(reject) for the Promise, await that Promise before
returning the client, and rethrow the error (or let it bubble) instead of only
logging in the .catch; ensure you reference the existing createClient function
and the bot.start and onStart hooks so the client is only returned after
successful startup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/errors.ts`:
- Around line 65-79: fromUnknown currently wraps HttpError into a TelegramError
with errorCode 0, which prevents transport/network failures from being treated
as retryable; change fromUnknown to return the original HttpError instance
(instead of new TelegramError(0, ...)) so network-level errors are preserved,
and update the retry handling logic (the branch that inspects TelegramError
errorCode/retryAfter) to also detect HttpError instances as transient transport
failures and apply the same exponential-backoff/retry behavior; locate
fromUnknown, TelegramError.fromGrammyError, and the retry decision code to
implement these changes.

In `@packages/spectrum-ts/src/providers/telegram/index.ts`:
- Around line 2301-2319: The handlers bot.on("my_chat_member") and
bot.on("chat_member") incorrectly set userId from u.from.id (the actor) instead
of the affected member; change them to use the member ID from
u.new_chat_member.user (or u.old_chat_member.user if appropriate) when
populating sinks.myChatMemberUpdates and sinks.chatMemberUpdates, and if the
actor is required preserve u.from.id in a separate field like actorId.
- Around line 2147-2158: Replace the grammY filter "callback_query:data" handler
with an unfiltered "callback_query" handler in index.ts (change the event
registration around bot.on("callback_query:data", ...) to
bot.on("callback_query", ...)) and update the mapping inside that handler
(sinks.callbackQueries push) to handle both q.data and q.game_short_name
(populate the same field(s) your events expect, e.g., set data to q.data ??
q.game_short_name or add a gameShortName property) so game-launch callbacks are
captured; also update the callbackQueries function in messages.ts to accept and
normalize the extended CallbackQuery shape, and extend the CallbackQuery type in
types.ts (or adjust its interface) to include game_short_name so type-checking
matches the new handler.

---

Duplicate comments:
In `@packages/spectrum-ts/src/providers/telegram/index.ts`:
- Around line 2395-2425: createClient currently calls bot.start(...) and returns
immediately, so start-up failures can be missed; change createClient to await
bot.start by wrapping it in a Promise that resolves when the bot's onStart
handler runs and rejects if bot.start rejects. Concretely: inside createClient,
register an onStart callback (bot.on("start"/bot.on("message"? use grammY's
onStart hook) — use the existing onStart registration in this module) to call
resolve(), call bot.start(...).catch(reject) for the Promise, await that Promise
before returning the client, and rethrow the error (or let it bubble) instead of
only logging in the .catch; ensure you reference the existing createClient
function and the bot.start and onStart hooks so the client is only returned
after successful startup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 089eb758-b57d-4bed-bba4-764e70300ffc

📥 Commits

Reviewing files that changed from the base of the PR and between d6e8e57 and 9c6f2bf.

📒 Files selected for processing (7)
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/platform/define.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/spectrum.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/spectrum-ts/src/platform/define.ts
  • packages/spectrum-ts/src/spectrum.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/spectrum-ts/package.json
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts

Comment thread packages/spectrum-ts/src/providers/telegram/errors.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/index.ts Outdated
Comment thread packages/spectrum-ts/src/providers/telegram/index.ts Outdated
- Return HttpError directly from fromUnknown() instead of wrapping as
  TelegramError(0), add retry branch with exponential backoff for
  network failures
- Use new_chat_member.user.id instead of from.id in myChatMember and
  chatMember handlers — from.id is the actor, not the affected user
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/spectrum-ts/src/providers/telegram/index.ts (1)

2117-2134: Consider avoiding as never type assertions.

Lines 2129, 2142, 2232, and 2250 use mapContent(bot, msg as never), which suppresses all type checking. This can mask genuine type mismatches and makes future refactoring error-prone. If there's a type incompatibility with mapContent's expected parameter, consider adjusting the function signature or creating a properly-typed adapter instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/spectrum-ts/src/providers/telegram/index.ts` around lines 2117 -
2134, The code is using a dangerous `as never` cast when calling mapContent(bot,
msg as never) which suppresses type checking; update mapContent's type signature
to accept the actual Telegram message type (or add an overload) or create a
small adapter function that converts ctx.editedMessage into the exact type
mapContent expects, then replace mapContent(bot, msg as never) with a properly
typed call (locations: the edited_message handler where sinks.editedMessages is
pushed and any other spots using mapContent with as never); avoid using
any/never casts so the compiler can catch real mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/spectrum-ts/src/providers/telegram/index.ts`:
- Around line 2117-2134: The code is using a dangerous `as never` cast when
calling mapContent(bot, msg as never) which suppresses type checking; update
mapContent's type signature to accept the actual Telegram message type (or add
an overload) or create a small adapter function that converts ctx.editedMessage
into the exact type mapContent expects, then replace mapContent(bot, msg as
never) with a properly typed call (locations: the edited_message handler where
sinks.editedMessages is pushed and any other spots using mapContent with as
never); avoid using any/never casts so the compiler can catch real mismatches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be638aad-1595-479c-a7a7-a708a5312201

📥 Commits

Reviewing files that changed from the base of the PR and between 9c6f2bf and 0a5c4b4.

📒 Files selected for processing (2)
  • packages/spectrum-ts/src/providers/telegram/errors.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/errors.ts
🔇 Additional comments (10)
packages/spectrum-ts/src/providers/telegram/index.ts (7)

2147-2158: Still filtering out game callback queries.

The callback_query:data filter excludes callbacks with game_short_name (from game launch buttons). Telegram sends exactly one of data or game_short_name on a callback query. If game callbacks should be captured in events.callbackQueries, switch to the unfiltered callback_query filter and extend the CallbackQuery type accordingly.


2395-2425: bot.start() is not awaited—startup failures are silently swallowed.

Currently, createClient returns successfully even if long polling fails to start (e.g., invalid token, network error). Per grammy best practices, startup errors should propagate to fail initialization and trigger container restarts.


264-300: LGTM!

The EventSink implementation now properly caps the buffer at 1000 events, dropping oldest events when full. The sinkToStream correctly drains buffered events before attaching the live listener.


336-401: LGTM!

The static utilities for keyboard building and deep-link URL helpers are well-structured and provide a clean API for common Telegram patterns.


2060-2084: LGTM!

The space resolution correctly enforces Telegram Bot API constraints—requiring exactly one user for private conversations initiated by the user.


2301-2319: LGTM!

The my_chat_member and chat_member handlers now correctly use u.new_chat_member.user.id to identify the affected member rather than the actor who triggered the update.


2517-2585: LGTM!

The action implementations correctly delegate to the underlying message functions and align with the PlatformDef.actions contract. The type assertion client as TelegramClient is safe here since the provider definition guarantees the client type.

packages/spectrum-ts/src/providers/telegram/errors.ts (3)

1-42: LGTM!

The logger implementation is clean and well-structured. The threshold-based gating pattern using noop functions for disabled levels avoids runtime condition checks on every log call.


44-80: LGTM!

The TelegramError class properly wraps grammy errors, correctly extracts retry_after only for HTTP 429 responses, and fromUnknown now preserves HttpError instances for proper retry handling in the caller.


85-139: LGTM!

The retry logic is correct with proper guards (attempt < maxRetries) on all retry branches, ensuring no unnecessary sleep on the final attempt. The exponential backoff with a 30-second cap is appropriate.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Line 19: The README is using a non-null assertion on
process.env.TELEGRAM_BOT_TOKEN (telegram.config({ token:
process.env.TELEGRAM_BOT_TOKEN! })), which encourages unsafe runtime crashes;
change the example to first read the env var into a const (e.g., const token =
process.env.TELEGRAM_BOT_TOKEN) and add a short guard that throws or logs a
clear error and exits if token is falsy, then pass that token into
telegram.config; apply the same change for the other occurrence at lines
referenced (lines ~476-477).
- Around line 547-553: Add a clear note after the example that the Telegram
provider registers its event sink eagerly and will buffer incoming events
(rather than creating the stream lazily like other providers); explicitly
mention app.events.telegram.callbackQueries as an eagerly registered/ buffered
stream so readers understand events may be queued before the for-await loop is
started and to differentiate this behavior from the generic “lazy stream”
wording elsewhere in the docs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: add23866-dced-40fa-8e5e-37fcfe047a72

📥 Commits

Reviewing files that changed from the base of the PR and between 0a5c4b4 and 2557f09.

📒 Files selected for processing (1)
  • README.md
📜 Review details
🔇 Additional comments (1)
README.md (1)

165-170: Docs and public API surface are aligned here.

These Message/Space methods and optional provider actions match the declared TypeScript interfaces and platform action hooks.

Also applies to: 261-266, 663-668

Comment thread README.md Outdated
Comment thread README.md Outdated
VanditKumar (KumarVandit) and others added 14 commits April 19, 2026 04:12
- Replace non-null assertion with nullish coalescing in README examples
- Add note about Telegram's eager event buffering behavior
- Replace `as never` casts with properly typed TgMessage assertions
- Widen callback_query:data filter to callback_query so game callbacks
  (game_short_name) are not silently dropped
- Add gameShortName and chatInstance to CallbackQuery type
- Document why static methods accept TelegramClient (opt-in via
  platform accessor, keeps internal types off public interface)
- Document why bot.start() is not awaited (promise never resolves
  while polling is active)
Integrate PR photon-hq#17 (contact content type with vCard support) into
telegram-support branch. Resolved conflicts in bun.lock and
examples/basic/index.ts.
- Inbound: map msg.contact to asContact() with structured name, phone,
  and raw vCard string instead of asCustom()
- Outbound: handle case "contact" in send() and replyToMessage() via
  bot.api.sendContact with name, phone, and vcard fields
- Add vcard parameter to sendContact static method
…astra)

Two Telegram example apps demonstrating Spectrum's provider integration
with popular AI frameworks:

- telegram-ai-sdk: Group moderation bot using AI classification to detect
  spam, scam, toxic, and off-topic messages with configurable strictness,
  warning system, callback-based mod review, and new member welcome flow.

- telegram-mastra: Inline knowledge agent using Mastra tools (Wikipedia
  search, math evaluation) to answer queries in any chat via @bot inline
  mode, with DM conversational fallback and analytics tracking.
Integrates PR photon-hq#18 which adds voice as a first-class content type with
iMessage audio-bubble rendering and WhatsApp audio support, including
optional ffmpeg transcoding for non-m4a inputs.
Maps inbound msg.voice to asVoice() for proper voice content typing,
and adds case "voice" to both send() and replyToMessage() switches
using bot.api.sendVoice with InputFile buffer and duration metadata.
PR photon-hq#17 (contact) and photon-hq#18 (voice) are now merged upstream; resolve
duplicate-addition conflicts by accepting main's versions for voice.ts,
audio.ts, and tsup.config.ts.
Moving telegram-ai-sdk and telegram-mastra examples to a separate PR
to keep this one focused on the Telegram provider implementation.
# Conflicts:
#	bun.lock
#	packages/spectrum-ts/src/platform/define.ts
#	packages/spectrum-ts/src/platform/types.ts
#	packages/spectrum-ts/src/spectrum.ts
#	packages/spectrum-ts/src/types/space.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/spectrum-ts/src/providers/telegram/messages.ts (2)

366-442: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add explicit minimum size validation before sending any children.

The pre-validation loop (lines 374-411) validates content types but doesn't check group.items.length. If asGroup requires at least 2 items, a single-item group will send one child message and then fail when building the wrapper at line 433, leaving a partial send in Telegram.

🛠️ Proposed fix
 const sendGroupContent = async (
   runtime: TelegramRuntime,
   spaceId: string,
   group: Group,
   opts: SendOpts
 ): Promise<TelegramMessage> => {
+  if (group.items.length < 2) {
+    throw new Error("Telegram group send requires at least 2 items");
+  }
+
   // Pre-validate every child — Telegram has no atomic undo for partial
   // album sends.
   for (const item of group.items) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts` around lines 366 -
442, The sendGroupContent function currently validates child types but not group
size; before any dispatchSend calls (at the start of sendGroupContent), add an
explicit check on group.items.length and throw an UnsupportedError or Error if
the group is too small (e.g., length < 2) so you never send a single child and
then fail when constructing the wrapper via asGroup; update the validation near
the top of sendGroupContent (before the "Pre-validate every child" loop) to
enforce the minimum item count and include a clear error message referencing
group.items and asGroup.

237-253: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve original vCard when available instead of regenerating.

The buildVCardField function always regenerates the vCard via toVCard(contact), but inbound contacts store the original Telegram vCard in contact.raw.vcard. This loses fidelity for contacts that don't round-trip cleanly through the normalized shape. A past review flagged this, but the fix doesn't appear to have been applied.

💡 Proposed fix
+const rawVCard = (contact: Contact): string | undefined => {
+  const raw = contact.raw;
+  if (typeof raw === "string" && raw.startsWith("BEGIN:VCARD")) {
+    return raw;
+  }
+  if (
+    raw &&
+    typeof raw === "object" &&
+    "vcard" in raw &&
+    typeof (raw as { vcard?: unknown }).vcard === "string"
+  ) {
+    return (raw as { vcard: string }).vcard;
+  }
+  return undefined;
+};
+
 const buildVCardField = async (
   contact: Contact
 ): Promise<string | undefined> => {
+  const original = rawVCard(contact);
+  if (original && Buffer.byteLength(original, "utf8") <= VCARD_MAX_BYTES) {
+    return original;
+  }
   if (!hasExtraContactData(contact)) {
     return;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts` around lines 237 -
253, buildVCardField currently always regenerates the vCard via toVCard(contact)
which loses fidelity for inbound contacts that already include
contact.raw.vcard; change buildVCardField to first check for contact.raw?.vcard
and if present validate its size with Buffer.byteLength against VCARD_MAX_BYTES
and return it unchanged, otherwise fall back to generating with toVCard(contact)
inside the existing try/catch and size check; keep the
hasExtraContactData(contact) guard and return undefined on errors or oversize
values.
🧹 Nitpick comments (3)
packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

83-92: 💤 Low value

Consider defining a minimal ReactionTarget type to avoid double casting.

The target as unknown as SpectrumMessage cast at line 87 bypasses type safety. While functionally correct (since asReaction only needs { id, content }), the cast obscures the actual contract. A shared minimal type would make the code more self-documenting.

This is a minor type hygiene issue—the runtime behavior is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/events/reactions.ts` around lines
83 - 92, Define a minimal ReactionTarget interface (e.g., with the fields
asReaction requires such as id and content) and use it for the local variable
instead of double-casting; replace the `target as unknown as SpectrumMessage` in
the newlyAddedEmojis mapping with a properly typed `target: ReactionTarget` (or
narrow the existing target to ReactionTarget) and update any related typings for
asReaction if necessary so asReaction(newTarget) compiles without unsafe casts;
this keeps the call sites (newlyAddedEmojis, asReaction, target) type-safe and
self-documenting.
packages/spectrum-ts/src/providers/telegram/events/polls.ts (1)

70-72: 💤 Low value

Document the synthetic timestamp rationale.

Line 72 uses new Date() for the event timestamp because poll_answer updates don't include a timestamp from Telegram. This is a reasonable fallback, but a brief comment would help future maintainers understand this isn't a bug.

📝 Suggested documentation
-  // `poll_answer` carries no chat or timestamp.
+  // `poll_answer` carries no chat or timestamp; use current time as best-effort.
   const space = cached.chat;
   const timestamp = new Date();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/events/polls.ts` around lines 70
- 72, Add a brief inline comment above the assignment to timestamp (where
timestamp = new Date() is set in the poll_answer handling) explaining that
Telegram's poll_answer updates do not include a timestamp, so a synthetic
timestamp is used to represent the event receipt time; reference the poll_answer
update type and the cached.chat/space variable to clarify this is intentional
and not a bug.
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (1)

38-43: 💤 Low value

Consider handling the edge case where inFlight is empty but size check triggers.

The overflow check at line 38 relies on Map.keys().next().value to get the oldest entry. While JavaScript Maps maintain insertion order, the check this.inFlight.size >= this.options.concurrentCapacity should ensure there's always at least one entry. However, if concurrentCapacity is configured as 0, this would cause an infinite loop of flushing undefined keys.

Consider adding validation in the constructor or a guard:

💡 Suggested validation
 constructor(options: AlbumBufferOptions) {
+  if (options.concurrentCapacity < 1) {
+    throw new RangeError("AlbumBuffer concurrentCapacity must be >= 1");
+  }
   this.options = options;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts` around lines 38
- 43, Validate and guard against a non-positive concurrentCapacity to prevent
edge cases where capacity is 0 (which can cause unintended behavior when
checking this.inFlight.size >= this.options.concurrentCapacity). In the class
constructor for the cache (where this.inFlight and this.options are initialized)
enforce that options.concurrentCapacity is an integer >= 1 (either throw a clear
error or coerce to a safe default like 1), and additionally add a defensive
check before the overflow logic that returns early if
this.options.concurrentCapacity <= 0; keep references to this.inFlight,
this.options.concurrentCapacity, and the flush(oldestKey) call when making these
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/index.ts`:
- Around line 22-23: Replace the unsafe type assertion in asRuntime with a
proper type guard that narrows unknown to TelegramRuntime: implement a function
isTelegramRuntime(client: unknown): client is TelegramRuntime that checks the
presence and types of all TelegramRuntime properties (including token, bot, api,
and the missing me property) and use that guard inside asRuntime to return the
typed value only when the check passes (throw or handle error otherwise).

---

Duplicate comments:
In `@packages/spectrum-ts/src/providers/telegram/messages.ts`:
- Around line 366-442: The sendGroupContent function currently validates child
types but not group size; before any dispatchSend calls (at the start of
sendGroupContent), add an explicit check on group.items.length and throw an
UnsupportedError or Error if the group is too small (e.g., length < 2) so you
never send a single child and then fail when constructing the wrapper via
asGroup; update the validation near the top of sendGroupContent (before the
"Pre-validate every child" loop) to enforce the minimum item count and include a
clear error message referencing group.items and asGroup.
- Around line 237-253: buildVCardField currently always regenerates the vCard
via toVCard(contact) which loses fidelity for inbound contacts that already
include contact.raw.vcard; change buildVCardField to first check for
contact.raw?.vcard and if present validate its size with Buffer.byteLength
against VCARD_MAX_BYTES and return it unchanged, otherwise fall back to
generating with toVCard(contact) inside the existing try/catch and size check;
keep the hasExtraContactData(contact) guard and return undefined on errors or
oversize values.

---

Nitpick comments:
In `@packages/spectrum-ts/src/providers/telegram/events/polls.ts`:
- Around line 70-72: Add a brief inline comment above the assignment to
timestamp (where timestamp = new Date() is set in the poll_answer handling)
explaining that Telegram's poll_answer updates do not include a timestamp, so a
synthetic timestamp is used to represent the event receipt time; reference the
poll_answer update type and the cached.chat/space variable to clarify this is
intentional and not a bug.

In `@packages/spectrum-ts/src/providers/telegram/events/reactions.ts`:
- Around line 83-92: Define a minimal ReactionTarget interface (e.g., with the
fields asReaction requires such as id and content) and use it for the local
variable instead of double-casting; replace the `target as unknown as
SpectrumMessage` in the newlyAddedEmojis mapping with a properly typed `target:
ReactionTarget` (or narrow the existing target to ReactionTarget) and update any
related typings for asReaction if necessary so asReaction(newTarget) compiles
without unsafe casts; this keeps the call sites (newlyAddedEmojis, asReaction,
target) type-safe and self-documenting.

In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts`:
- Around line 38-43: Validate and guard against a non-positive
concurrentCapacity to prevent edge cases where capacity is 0 (which can cause
unintended behavior when checking this.inFlight.size >=
this.options.concurrentCapacity). In the class constructor for the cache (where
this.inFlight and this.options are initialized) enforce that
options.concurrentCapacity is an integer >= 1 (either throw a clear error or
coerce to a safe default like 1), and additionally add a defensive check before
the overflow logic that returns early if this.options.concurrentCapacity <= 0;
keep references to this.inFlight, this.options.concurrentCapacity, and the
flush(oldestKey) call when making these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca2e4883-e584-4338-a925-7a5040b0d03c

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 39200c1.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (21)
.gitignore (1)

47-48: Good improvement!

The test-results/ pattern properly addresses the previous concern about the overly broad test/ ignore rule. This more specific pattern correctly targets test artifacts while allowing test source files to remain tracked.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json (1)

1-592: LGTM! Schema accurately models Telegram Bot API 9.6.

All past review comments have been properly addressed:

  • Version updated to 9.6
  • Contact type includes the vcard field
  • Poll uses correct_option_ids (array) with proper 9.6 semantics
  • sendPoll includes all 9.6 parameters (allows_revoting, shuffle_options, allow_adding_options, hide_results_until_closes)
  • Descriptions reference the correct field names

The schema comprehensively defines the Bot API surface used by the generator and runtime.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts (1)

1-240: LGTM! Well-structured generator with proper error handling.

The generator correctly:

  • Validates the schema structure before processing (fail-fast with clear error on malformed input)
  • Handles the documented TypeRef DSL including the bare InputFile primitive (past review comment addressed)
  • Generates clean TypeScript interfaces with JSDoc comments
  • Emits a type-safe Methods map consumed by the runtime client
  • Uses descriptive error messages for debugging

The console.log statements are appropriate for a CLI tool that communicates output paths to the user.

packages/spectrum-ts/package.json (2)

47-48: LGTM! Generator script correctly wired.

The gen:telegram script properly invokes the Bun-executable generator, aligning with the PR's documented regeneration workflow.


58-58: LGTM! LRU cache dependency added.

quick-lru is appropriately added for the Telegram runtime cache implementation (messages/polls/albums as noted in AI summary).

package.json (1)

14-15: LGTM! Root-level generator script added.

The gen:telegram script provides a convenient entry point for regenerating Telegram Bot API types from the monorepo root.

packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

1-62: LGTM! Well-designed error hierarchy with proper TypeScript features.

The error classes effectively model Telegram-specific failure modes:

  • TelegramApiError classifies API responses with helper getters for retry logic
  • TelegramNetworkError wraps transport failures and preserves the original cause
  • Proper use of override keyword for the cause property
  • All error details preserved for downstream retry and migration handling

The structure aligns well with the retry strategy in retry.ts and the client error handling in client.ts.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

1-114: LGTM! Robust retry implementation with all past issues resolved.

The retry logic correctly handles:

  • Abort race fixed: Listener registered before the re-check (lines 40-46)
  • Full jitter: Backoff uses [50%, 100%] range, preventing 0ms retries (line 58)
  • Rate limit respect: retry_after honored without capping (lines 73-79 explain why clamping to maxDelayMs would burn through attempts)
  • Proper error classification: Distinguishes rate limits, server errors, and network errors

The inline comment at lines 73-79 clearly documents the reasoning for not capping Telegram's rate limit window—excellent clarity for future maintainers.

packages/spectrum-ts/src/providers/telegram/types.ts (1)

1-73: LGTM! Comprehensive schemas with proper validation.

All past review comments have been addressed:

  • String validation: trim() applied to token, apiBaseUrl, and chatId (string branch)
  • Integer validation: chatId numeric branch uses .int(), cache limits use .int().positive()
  • User schema: Properly defined with optional fields and clear comment explaining why (lines 29-30)
  • URL validation: apiBaseUrl uses .url() for proper format checking

The schemas leverage Zod 4 features correctly and provide strong runtime validation for the Telegram provider configuration and message shapes.

packages/spectrum-ts/src/providers/telegram/identity.ts (1)

1-68: LGTM! Clean identity conversion helpers.

The conversion functions correctly map Telegram Bot API shapes to Spectrum's message and space structures:

  • Proper ID normalization: Numeric IDs stringified consistently
  • Optional field handling: Conditional assignment only when fields are present
  • Reasonable fallbacks: chatToSender provides a sensible fallback for firstName when title/username are missing
  • Clear documentation: Comment explains sender_chat usage (lines 26-27) and why type is used instead of interface (lines 43-45)

The helpers integrate cleanly with the rest of the provider, particularly the space resolver in index.ts.

packages/spectrum-ts/src/providers/telegram/runtime/client.ts (1)

1-339: LGTM!

The client implementation is well-structured with proper timeout handling, abort signal management, and retry logic. Previous review concerns have been addressed:

  • Timeout signal is built once per invoke() and shared across retries and migrations
  • Constructor validates requestTimeoutMs for non-negative finite values
  • Caller aborts are properly detected and rethrown in body-read paths
  • combineSignals correctly uses AbortSignal.any() for combining signals
packages/spectrum-ts/src/providers/telegram/runtime/polling.ts (1)

1-102: LGTM!

The polling implementation correctly handles:

  • Timeout sanitization with proper clamping to Telegram's 50-second limit
  • Default allowed updates with opt-in for reaction and poll events
  • dropPendingUpdates via the offset: -1 technique
  • Graceful shutdown via abort signal checks at appropriate points
packages/spectrum-ts/src/providers/telegram/events/inbound.ts (1)

1-344: LGTM!

The inbound conversion logic is well-implemented:

  • Lazy file fetching via getFile + downloadFile without capturing polling signals
  • Proper content type priority (richlink → voice → photo → document/audio/video → contact → poll → caption)
  • Contact conversion preserves the original Telegram contact object in raw
  • Album coalescing anchors the wrapper id to the first member's real message_id
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (1)

1-226: LGTM overall!

The cache implementation is well-structured:

  • AlbumBuffer properly handles debounce/ceiling timers and async flushing
  • PollStore uses separate LRU caches for polls and per-voter vote vectors
  • Composite cache keys (${spaceId}:${messageId}) prevent cross-chat collisions
  • The comment about QuickLRU's 2× maxSize behavior is helpful documentation
packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

1-94: LGTM overall!

The reaction handling correctly:

  • Filters to add-only, plain-unicode emoji reactions
  • Handles both user and actor_chat (anonymous channel admin) senders
  • Uses composite cache keys for message lookup
  • Generates deterministic event IDs
packages/spectrum-ts/src/providers/telegram/events/polls.ts (1)

1-99: LGTM overall!

The poll answer handling correctly:

  • Computes per-option add/remove diffs against cached prior votes
  • Supports both regular users and anonymous channel admin voters
  • Handles missing polls gracefully by returning empty array
  • Generates deterministic event IDs with clear add/remove distinction
packages/spectrum-ts/src/providers/telegram/events/index.ts (1)

1-155: LGTM!

The message stream implementation is well-coordinated:

  • Album coalescing properly caches the grouped wrapper before emitting
  • Drain happens inside the pump on both normal completion and abort, ensuring proper ordering
  • The disposer awaits the pump to guarantee all cleanup completes before returning
  • Cache updates happen for both individual messages and coalesced albums
packages/spectrum-ts/src/providers/telegram/index.ts (1)

25-39: Cache option normalization looks solid.

Good use of nullish fallbacks to preserve defaults while allowing per-knob overrides.

README.md (1)

56-56: Platform table update is correct and clear.

packages/spectrum-ts/tsup.config.ts (1)

8-8: Build entry wiring for Telegram provider is correctly added.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md (1)

16-40: Scope and mapping notes are well-scoped and actionable.

Comment on lines +22 to +23
const asRuntime = (client: unknown): TelegramRuntime =>
client as TelegramRuntime;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether definePlatform typing can preserve TelegramRuntime and avoid `unknown` casts.
rg -n "export function definePlatform|interface|type .*Platform" packages/spectrum-ts/src/platform/define.ts -C4
rg -n "asRuntime|as TelegramRuntime" packages/spectrum-ts/src/providers/telegram/index.ts -C3

Repository: photon-hq/spectrum-ts

Length of output: 1793


🏁 Script executed:

rg -n "type TelegramRuntime|interface TelegramRuntime" packages/spectrum-ts/src/providers/telegram/ -A10

Repository: photon-hq/spectrum-ts

Length of output: 1393


🏁 Script executed:

rg -n "abort|cache.*destroy|client" packages/spectrum-ts/src/providers/telegram/types.ts -B2 -A2

Repository: photon-hq/spectrum-ts

Length of output: 418


🏁 Script executed:

# Check imports at the top of index.ts to understand the type origin
head -30 packages/spectrum-ts/src/providers/telegram/index.ts

Repository: photon-hq/spectrum-ts

Length of output: 937


Replace the type assertion with proper runtime type narrowing, including all properties of TelegramRuntime.

The cast bypasses type safety at the platform boundary. Per guidelines, leverage type narrowing instead of assertions.

The suggested type guard is incomplete—it omits the me property. Update it to validate all four properties:

♻️ Suggested refactor
-const asRuntime = (client: unknown): TelegramRuntime =>
-  client as TelegramRuntime;
+const isTelegramRuntime = (client: unknown): client is TelegramRuntime => {
+  if (!client || typeof client !== "object") return false;
+  const maybe = client as Partial<TelegramRuntime>;
+  return (
+    typeof maybe.abort === "object" &&
+    typeof maybe.cache === "object" &&
+    typeof maybe.client === "object" &&
+    maybe.me !== undefined
+  );
+};
+
+const asRuntime = (client: unknown): TelegramRuntime => {
+  if (!isTelegramRuntime(client)) {
+    throw new Error("Invalid Telegram runtime");
+  }
+  return client;
+};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/index.ts` around lines 22 - 23,
Replace the unsafe type assertion in asRuntime with a proper type guard that
narrows unknown to TelegramRuntime: implement a function
isTelegramRuntime(client: unknown): client is TelegramRuntime that checks the
presence and types of all TelegramRuntime properties (including token, bot, api,
and the missing me property) and use that guard inside asRuntime to return the
typed value only when the check passes (throw or handle error otherwise).

@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/events/inbound.ts`:
- Around line 155-160: The current early return of linkPreview.url causes
messages with surrounding prose to be collapsed into a single richlink; modify
the logic in the inbound link-preview handling (the block that inspects
linkPreview and msg) so you only return linkPreview.url when the message body is
effectively just the URL (for example msg.text trimmed equals the preview URL or
the preview metadata indicates the text is the URL), otherwise fall through and
preserve the original text content so messageToContent() emits both text and
richlink; update the conditional around linkPreview?.url to check msg.text
(and/or linkPreview text fields) before returning.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts`:
- Around line 33-39: The toMessageId function currently uses Number(...) which
accepts coercive forms like "1e3", "0x10" or padded strings; change it to
strictly validate the input against a base-10 digits-only pattern (e.g. /^\d+$/)
before converting, then use parseInt(value, 10) and verify the result is a safe
integer (and positive if required by Telegram semantics); if validation fails,
throw the same error message. Update references to toMessageId accordingly.

In `@packages/spectrum-ts/src/providers/telegram/runtime/client.ts`:
- Around line 189-193: The constructor currently assigns baseUrl directly which
allows a trailing slash (e.g. "https://api.telegram.org/") and later produces
double-slashes in generated URLs; update the constructor (the baseUrl assignment
in the TelegramClient constructor) to normalize the value once by trimming any
trailing slashes and collapsing repeated slashes so baseUrl never ends with a
slash (use opts.baseUrl ?? BASE_URL, sanitize it, and assign the sanitized
string to this.baseUrl); ensure the normalization handles undefined/empty inputs
and preserves the scheme/host.
- Around line 252-261: The error handler in invokeOnce() and downloadFile() is
only checking isCallerAbort(err, callerSignal) but the fetch uses the combined
abort signal (combined) that includes the internal timeout; update those checks
to use isCallerAbort(err, combined) (or otherwise check both callerSignal and
combined) so that when the internal timeout aborts during body read the abort is
detected and re-thrown instead of being wrapped in TelegramNetworkError;
specifically change the abort-checks in invokeOnce() (where payload parsing is
caught) and in downloadFile() (the places referenced around the JSON/body read
error handling) to reference combined, and leave TelegramNetworkError wrapping
for other non-abort errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be803b8f-d42d-466c-b7ea-4ad8983024d7

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 2f098ea.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (9)
.gitignore (1)

47-51: Scoped ignore rules look good.

test-results/ is specific to artifacts, and telegram-full-backup/ is appropriately kept local-only.

packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

83-88: ⚡ Quick win

Replace the double type assertion on reaction targets with a narrowed target contract.

Line 87 (target as unknown as SpectrumMessage) bypasses static checks and can mask shape mismatches between cached/stub targets and SpectrumMessage. Please prefer a dedicated narrowed target type (or a real SpectrumMessage construction path) before passing into asReaction.

Use this read-only verification to confirm the actual contract and downstream expectations:

#!/bin/bash
set -euo pipefail

echo "== asReaction target contract =="
rg -n -C3 --type ts 'export const asReaction|target:' \
  packages/spectrum-ts/src/content/reaction.ts \
  packages/spectrum-ts/src/types/message.ts

echo
echo "== current cast site =="
rg -n -C3 --type ts 'target:\s*target as unknown as SpectrumMessage' \
  packages/spectrum-ts/src/providers/telegram/events/reactions.ts

echo
echo "== downstream usages of reaction.target =="
rg -n -C2 --type ts 'reaction\.target\.' packages/spectrum-ts/src

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

packages/spectrum-ts/src/providers/telegram/index.ts (1)

22-23: ⚡ Quick win

Replace unchecked asRuntime cast with a runtime type guard.

This boundary currently trusts unknown and can fail later at arbitrary property access points. A narrow-and-throw guard gives earlier, clearer failures.

♻️ Minimal guard-based refactor
-const asRuntime = (client: unknown): TelegramRuntime =>
-  client as TelegramRuntime;
+const isTelegramRuntime = (client: unknown): client is TelegramRuntime => {
+  if (!client || typeof client !== "object") return false;
+  const value = client as Partial<TelegramRuntime>;
+  return (
+    typeof value.client === "object" &&
+    typeof value.abort === "object" &&
+    typeof value.cache === "object" &&
+    value.me !== undefined
+  );
+};
+
+const asRuntime = (client: unknown): TelegramRuntime => {
+  if (!isTelegramRuntime(client)) {
+    throw new Error("Invalid Telegram runtime client");
+  }
+  return client;
+};

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions."

packages/spectrum-ts/src/providers/telegram/events/polls.ts (1)

44-98: Poll-answer diffing flow is clean and safe.

Good use of prior-vote caching + add/remove diff emission, with graceful early exits for missing voter/poll context and invalid option indexes.

packages/spectrum-ts/src/providers/telegram/runtime/polling.ts (1)

15-27: Timeout sanitization and abort-aware polling loop look solid.

The clamping/default behavior and abort/non-abort error split make this path resilient for production polling.

Also applies to: 78-101

packages/spectrum-ts/src/providers/telegram/identity.ts (1)

7-67: Identity/space normalization is consistent and readable.

The explicit id mapping and optional-field guards are well structured.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

19-113: Retry primitives are well implemented.

The abort-safe sleep, capped jittered backoff, and Telegram-aware delay selection are all wired cleanly.

packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

10-61: Error taxonomy is clear and practical.

The class split and derived getters (isRateLimit, retryAfter, migrateToChatId) make downstream retry/routing decisions straightforward.

packages/spectrum-ts/src/providers/telegram/events/index.ts (1)

81-153: Stream lifecycle and album coalescing flow look robust.

Good coordination between buffering, cache updates, drain-on-shutdown, and final stream termination.

Comment thread packages/spectrum-ts/src/providers/telegram/events/inbound.ts
Comment thread packages/spectrum-ts/src/providers/telegram/messages.ts
Comment thread packages/spectrum-ts/src/providers/telegram/runtime/client.ts
Comment thread packages/spectrum-ts/src/providers/telegram/runtime/client.ts
extractRichlinkUrl only emitted a richlink whenever
link_preview_options.url was set, which silently dropped the surrounding
text. Tighten the rule to only collapse the message to a richlink when
the trimmed body equals the preview URL; mixed prose + preview now
falls through to plain text so callers don't lose the original content.
toMessageId previously used Number(...) which accepts coercive forms
(1e3, 0x10, leading/trailing whitespace, signed inputs) and would
silently turn them into integers. Telegram message_ids are decimal,
so validate against /^\d+$/ and require a safe integer before
returning.
Two small TelegramClient hardenings:

- Trim trailing slashes off baseUrl on construction so callers passing
  "https://api.telegram.org/" don't produce "//bot<token>/..." URLs.

- AbortSignal.timeout rejects with a DOMException named TimeoutError
  on Node/undici. The previous isCallerAbort guard only matched
  AbortError plus the caller's signal, so a timeout firing during the
  fetch or JSON read got wrapped in TelegramNetworkError. Widen the
  helper to recognise both names and key the abort checks off the
  combined signal so caller cancels and internal timeouts propagate
  uniformly instead of being misclassified as network failures.
@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/events/index.ts`:
- Around line 85-93: The flush handler currently caches only the lead album
member under messageCacheKey(grouped.space.id, grouped.id) after coalescing,
leaving other member ids pointing to stale child records; modify the flush logic
in the flush function (the block using coalesceAlbumGroup(members),
runtime.cache.messages.set(...), and emit(grouped)) to iterate all original
members and set runtime.cache.messages.set(messageCacheKey(grouped.space.id,
member.id), grouped) (or equivalent) for each member.id so every album member
cache entry is replaced with the grouped wrapper before calling emit(grouped);
apply the same change to the analogous block around the 117-130 region.

In `@packages/spectrum-ts/src/providers/telegram/identity.ts`:
- Around line 54-65: chatToSpace currently only copies chat.title, so private
(direct-message) chats lose the readable name Telegram exposes via
chat.first_name and chat.last_name; update chatToSpace (the function creating
TelegramSpaceShape) to populate space.title for private chats when chat.title is
undefined by concatenating chat.first_name and chat.last_name (trim resulting
string) and falling back to username or an empty string if needed, ensuring
TelegramSpaceShape.title is set for DM spaces.

In `@packages/spectrum-ts/src/providers/telegram/types.ts`:
- Around line 9-12: The schema currently requires strictly positive integers for
the cache capacity fields (messages, polls, pollVotes, albumConcurrent) which
rejects 0; update each field's Zod validator (the properties messages, polls,
pollVotes, albumConcurrent in the Telegram provider types/schema) to allow zero
by replacing the .positive() constraint with a non-negative constraint (e.g.,
use .nonnegative() or .min(0) after .int()) so that 0 is accepted to represent a
disabled cache slot while preserving integer and optional behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 020e9464-4ecc-4d53-a1d7-c52d6f95a8fb

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 67b0b90.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (10)
.gitignore (1)

47-51: Good ignore scoping for local artifacts.

Using test-results/ (Line 48) instead of a broad test/ pattern is the right fix, and telegram-full-backup/ (Line 51) is appropriately scoped as local-only scratch data.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json (1)

464-523: Poll method shape is aligned with the updated Bot API surface.

The sendPoll params now correctly include the 9.6 additions while keeping your regular-poll scope explicit.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts (1)

69-104: toTs() coverage looks good for the schema DSL variants in this PR.

Nice improvement including bare InputFile handling and clear fail-fast behavior on unknown expressions.

packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

16-47: Error classification surface is clean and practical.

The derived flags (isRateLimit, isServerError, retryAfter, migrateToChatId) should make retry/migration handling much simpler downstream.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

19-51: sleep() cancellation handling is robust now.

Good fix for the abort timing window; listener registration + post-registration aborted check closes the race.

packages/spectrum-ts/src/providers/telegram/index.ts (1)

22-23: asRuntime still uses an unchecked cast at the platform boundary.

A guard-based narrowing here would fail fast on invalid client shapes instead of trusting unknown.

#!/bin/bash
# Verify current assertion-based runtime cast and absence/presence of a guard.
rg -n "const asRuntime|as TelegramRuntime|isTelegramRuntime" packages/spectrum-ts/src/providers/telegram/index.ts -C2

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

packages/spectrum-ts/src/providers/telegram/messages.ts (2)

35-44: Strict message_id parsing is robust.

Digits-only validation plus safe-integer checks removes coercive numeric forms and keeps Bot API calls deterministic.


396-479: Group send flow is safely structured.

Pre-validation before dispatching children and caching the synthetic wrapper under the returned key avoids partial-send surprises and lookup mismatches.

README.md (1)

56-56: Telegram platform docs entry looks correct.

The package path is consistent with the new provider export/build wiring.

packages/spectrum-ts/tsup.config.ts (1)

8-8: Build entry addition is correct.

Including providers/telegram/index in tsup entry points aligns packaging with the new provider module.

Comment thread packages/spectrum-ts/src/providers/telegram/events/index.ts
Comment thread packages/spectrum-ts/src/providers/telegram/identity.ts
Comment on lines +9 to +12
messages: z.number().int().positive().optional(),
polls: z.number().int().positive().optional(),
pollVotes: z.number().int().positive().optional(),
albumConcurrent: z.number().int().positive().optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow 0 for cache capacities so the documented “disable slot” behavior actually works.

Line 9–12 currently require strictly positive numbers, which rejects 0 despite your runtime/docs contract saying 0 disables that cache slot.

Suggested fix
 export const cacheConfigSchema = z
   .object({
-    messages: z.number().int().positive().optional(),
-    polls: z.number().int().positive().optional(),
-    pollVotes: z.number().int().positive().optional(),
-    albumConcurrent: z.number().int().positive().optional(),
+    messages: z.number().int().nonnegative().optional(),
+    polls: z.number().int().nonnegative().optional(),
+    pollVotes: z.number().int().nonnegative().optional(),
+    albumConcurrent: z.number().int().nonnegative().optional(),
     albumDebounceMs: z.number().int().nonnegative().optional(),
     albumCeilingMs: z.number().int().nonnegative().optional(),
     coalesceAlbums: z.boolean().optional(),
   })
   .optional();

As per coding guidelines, "Validate and sanitize user input in JavaScript/TypeScript".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
messages: z.number().int().positive().optional(),
polls: z.number().int().positive().optional(),
pollVotes: z.number().int().positive().optional(),
albumConcurrent: z.number().int().positive().optional(),
messages: z.number().int().nonnegative().optional(),
polls: z.number().int().nonnegative().optional(),
pollVotes: z.number().int().nonnegative().optional(),
albumConcurrent: z.number().int().nonnegative().optional(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/types.ts` around lines 9 - 12,
The schema currently requires strictly positive integers for the cache capacity
fields (messages, polls, pollVotes, albumConcurrent) which rejects 0; update
each field's Zod validator (the properties messages, polls, pollVotes,
albumConcurrent in the Telegram provider types/schema) to allow zero by
replacing the .positive() constraint with a non-negative constraint (e.g., use
.nonnegative() or .min(0) after .int()) so that 0 is accepted to represent a
disabled cache slot while preserving integer and optional behavior.

Each album member is cached individually as it streams in, but the
flush handler only overwrote the lead member's slot with the coalesced
wrapper. Subsequent reactions or edits keyed off any non-lead member id
were resolving to the stale standalone child record. Overwrite every
member slot with the grouped wrapper so the cache stays consistent
across all ids in the album.
Private chats arrive without a chat.title, so DM spaces previously
landed with no name at all. Extend the Chat schema with first_name
and last_name (already exposed by the Bot API for private chats),
regenerate the typed shape, and synthesize space.title in chatToSpace
from first_name + last_name (falling back to username) when the chat
is private.
@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/messages.ts`:
- Around line 410-449: The upfront group pre-validation loop (the for (const
item of group.items) { ... } block) only rejects obvious unsupported types but
misses deterministic, non-I/O child validation that happens later and can cause
partial sends; update this pass to also run the same local validators used later
(e.g., contact phone-number checks, poll option title length checks, and any
other deterministic validators currently applied around the other child-handling
blocks referenced in the comment ranges) so that any child that would
deterministically fail is rejected before dispatchSend() is called; reuse or
extract the existing local validation logic into a helper and call it from the
group.items loop (keeping UnsupportedError.content for type-based errors and
ensuring the helper throws the same error types/messages).
- Around line 35-43: toMessageId currently accepts "0" and negative values as
valid because it only checks the decimal-digit pattern and safe integer; update
to reject non-positive IDs by adding a check after parsing to ensure parsed > 0
and throw an Error with a clear message if not. Locate function toMessageId and
add the parsed > 0 validation (and corresponding error) so only positive
integers are returned.

In `@packages/spectrum-ts/src/providers/telegram/runtime/client.ts`:
- Around line 191-193: The constructor of TelegramClient currently assigns
opts.token directly; validate and sanitize it immediately by checking that
opts.token is a non-empty string after trimming (e.g., const token =
String(opts.token ?? "").trim()), throw a clear Error like "TelegramClient:
token is required and cannot be empty or whitespace" if validation fails, then
assign the trimmed token to this.token; keep existing baseUrl handling
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3119ab6a-d8c6-4e72-8aa8-4d07a9387e9f

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 9ffd439.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (12)
.gitignore (2)

47-48: Scoped test artifact ignore looks good.

Using test-results/ keeps generated outputs out of git without hiding actual test source directories.


50-51: Backup directory ignore is appropriately specific.

telegram-full-backup/ is a clear, localized ignore rule for local-only artifacts.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json (1)

187-195: Schema updates for Contact + Poll 9.6 fields look consistent.

vcard, correct_option_ids, and the new sendPoll options are modeled coherently and align with the stated provider scope.

Also applies to: 281-291, 506-528

packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts (1)

69-104: toTs() coverage looks solid after the InputFile handling update.

The DSL mapping paths are clear and fail fast on unknown expressions, which is great for generator reliability.

package.json (1)

14-15: gen:telegram script wiring looks good.

This provides a clear regeneration path from the repo root and matches the documented workflow.

packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

27-47: Error classification helpers are well-structured.

isRateLimit/isServerError/retryAfter/migrateToChatId expose exactly what retry and migration code needs.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

19-51: Retry/abort behavior looks robust.

The sleep cancellation path and Telegram-specific retry_after handling are implemented cleanly.

Also applies to: 69-90

packages/spectrum-ts/src/providers/telegram/runtime/polling.ts (1)

15-27: Polling loop sanitization and abort flow look good.

The timeout normalization plus graceful abort exits keep long-poll behavior predictable.

Also applies to: 58-101

README.md (1)

56-56: Platform table update is accurate.

Adding Telegram here keeps README discoverability aligned with the new provider.

packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

87-87: Avoid the as unknown as SpectrumMessage cast on reaction targets.

Line 87 still bypasses type safety for target, which can hide contract drift between cached/stub targets and SpectrumMessage.

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

packages/spectrum-ts/src/providers/telegram/index.ts (1)

22-23: Replace asRuntime assertion with a runtime type guard.

Lines 22-23 currently trust any unknown client shape, which weakens the provider boundary and defers failures to deeper runtime paths.

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

packages/spectrum-ts/src/providers/telegram/messages.ts (1)

391-392: 🏗️ Heavy lift

Group items are properly inflated with platform helpers downstream in platform/build.ts.

The inline comment at lines 389–390 correctly documents the pattern: platform inflation adds react()/reply() methods downstream in the core layer. When group content flows through wrapContent() in platform/build.ts:266–276, raw provider records in content.items are recursively passed to wrapProviderMessage(), which binds the platform helpers. The runtime safety concern does not apply here.

That said, the double cast as unknown as SpectrumMessage[] violates the TypeScript guideline to "leverage type narrowing instead of type assertions." Consider introducing a lighter type (e.g., GroupItem with only id and content) or restructuring to avoid the cast entirely.

			> Likely an incorrect or invalid review comment.

Comment thread packages/spectrum-ts/src/providers/telegram/messages.ts
Comment on lines +410 to +449
// Pre-validate every child — Telegram has no atomic undo for partial
// album sends.
for (const item of group.items) {
const child = item.content;
if (child.type === "group" || child.type === "reaction") {
throw UnsupportedError.content(
child.type,
PLATFORM_NAME,
`nested ${child.type} inside group is not allowed`
);
}
if (
child.type === "reply" ||
child.type === "edit" ||
child.type === "typing"
) {
throw UnsupportedError.content(
child.type,
PLATFORM_NAME,
`${child.type} cannot be an album member`
);
}
if (child.type === "custom") {
throw UnsupportedError.content("custom", PLATFORM_NAME);
}
if (child.type === "poll_option") {
throw UnsupportedError.content(
"poll_option",
PLATFORM_NAME,
"poll_option is an inbound-only content type"
);
}
if (child.type === "effect") {
throw UnsupportedError.content(
"effect",
PLATFORM_NAME,
"effect is an iMessage-only content type"
);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pre-validation still misses deterministic child failures.

This scan rejects unsupported member types, but later children can still fail on local validation already implemented elsewhere in this file. For example, a group with a valid first item and a contact without a phone number, or a poll with an overlong option title, will still partial-send before the later child throws. Pull those non-I/O validators into the upfront pass so the whole group rejects before the first dispatchSend().

Also applies to: 289-294, 350-356

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts` around lines 410 -
449, The upfront group pre-validation loop (the for (const item of group.items)
{ ... } block) only rejects obvious unsupported types but misses deterministic,
non-I/O child validation that happens later and can cause partial sends; update
this pass to also run the same local validators used later (e.g., contact
phone-number checks, poll option title length checks, and any other
deterministic validators currently applied around the other child-handling
blocks referenced in the comment ranges) so that any child that would
deterministically fail is rejected before dispatchSend() is called; reuse or
extract the existing local validation logic into a helper and call it from the
group.items loop (keeping UnsupportedError.content for type-based errors and
ensuring the helper throws the same error types/messages).

Comment thread packages/spectrum-ts/src/providers/telegram/runtime/client.ts
toMessageId previously accepted "0" since the digits-only regex doesn't
distinguish positive from non-positive values. Telegram message_ids
start at 1, so add an explicit `> 0` check after parsing alongside the
safe-integer guard so bogus ids fail at the boundary instead of being
sent to the API.
Trim opts.token and throw at construction when it ends up empty (or
isn't a string at all) so misconfigured callers fail with a clear
message instead of producing requests to /bot/getMe with no token,
which the Bot API rejects with an opaque 404.
@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

77-88: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid the double-cast on reaction targets.

At Line 87, target as unknown as SpectrumMessage bypasses type guarantees and can pass a non-SpectrumMessage shape into asReaction. Please make the reaction target contract explicit (minimal shared target type) instead of forcing this cast.

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/events/reactions.ts` around lines
77 - 88, The double-cast bypasses type safety for the reaction target; update
the types so asReaction receives a properly-typed target instead of using
`target as unknown as SpectrumMessage`: introduce a minimal shared interface
(e.g., ReactionTarget) that contains the fields asReaction needs, change or
overload resolveReactionTarget to return ReactionTarget (or a union) and/or add
a type guard function (e.g., isSpectrumMessage(target): target is
SpectrumMessage) to narrow the value before calling asReaction; alternatively
broaden asReaction's parameter to accept ReactionTarget—then replace `target as
unknown as SpectrumMessage` with either a narrowed value from the type guard or
the correctly typed ReactionTarget.
packages/spectrum-ts/src/providers/telegram/types.ts (1)

7-17: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow 0 for cache capacities to support disabling cache slots.

Lines 9–12 use .positive() which rejects 0, but 0 is a valid sentinel to disable caching for that slot. The schema should accept non-negative integers.

🛠️ Suggested fix
 export const cacheConfigSchema = z
   .object({
-    messages: z.number().int().positive().optional(),
-    polls: z.number().int().positive().optional(),
-    pollVotes: z.number().int().positive().optional(),
-    albumConcurrent: z.number().int().positive().optional(),
+    messages: z.number().int().nonnegative().optional(),
+    polls: z.number().int().nonnegative().optional(),
+    pollVotes: z.number().int().nonnegative().optional(),
+    albumConcurrent: z.number().int().nonnegative().optional(),
     albumDebounceMs: z.number().int().nonnegative().optional(),
     albumCeilingMs: z.number().int().nonnegative().optional(),
     coalesceAlbums: z.boolean().optional(),
   })
   .optional();

As per coding guidelines, "Validate and sanitize user input in JavaScript/TypeScript".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/types.ts` around lines 7 - 17,
The schema cacheConfigSchema currently uses .positive() for integer capacity
fields (messages, polls, pollVotes, albumConcurrent) which rejects 0; update
those fields to use .nonnegative() so zero is accepted as a valid sentinel to
disable a cache slot while keeping albumDebounceMs and albumCeilingMs as
nonnegative already; locate and change the validators for messages, polls,
pollVotes, and albumConcurrent inside cacheConfigSchema accordingly.
🧹 Nitpick comments (1)
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (1)

83-84: 💤 Low value

Consider removing console.error in favor of structured error handling.

The console.error in flush() (line 83) logs to stdout, which may not align with production observability patterns. Consider emitting via an error callback or structured logger if the project has one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts` around lines 83
- 84, The console.error call inside the flush() function should be replaced with
structured error handling: remove console.error("Telegram album flush failed:",
err) and instead invoke the module's error handler or logger (e.g.
this.options.onError?.(err) or this.logger?.error("Telegram album flush failed",
err)); if no handler exists add an optional onError callback to the
class/options and call that, falling back to rethrowing the error so callers can
decide how to log/handle it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/spectrum-ts/src/providers/telegram/runtime/retry.ts`:
- Line 98: The current spread merge "const policy: RetryPolicy = {
...DEFAULT_RETRY_POLICY, ...opts.policy }" can overwrite defaults with explicit
undefined values; fix by merging while ignoring undefined fields from
opts.policy (e.g., build a filteredPolicy from Object.entries(opts.policy)
keeping only entries with value !== undefined, or assign each key using nullish
coalescing like maxAttempts: opts.policy?.maxAttempts ??
DEFAULT_RETRY_POLICY.maxAttempts), then spread DEFAULT_RETRY_POLICY and the
filteredPolicy into policy; ensure you update the policy variable (and any
downstream math using policy.maxAttempts/backoff) to use this sanitized merge.

---

Duplicate comments:
In `@packages/spectrum-ts/src/providers/telegram/events/reactions.ts`:
- Around line 77-88: The double-cast bypasses type safety for the reaction
target; update the types so asReaction receives a properly-typed target instead
of using `target as unknown as SpectrumMessage`: introduce a minimal shared
interface (e.g., ReactionTarget) that contains the fields asReaction needs,
change or overload resolveReactionTarget to return ReactionTarget (or a union)
and/or add a type guard function (e.g., isSpectrumMessage(target): target is
SpectrumMessage) to narrow the value before calling asReaction; alternatively
broaden asReaction's parameter to accept ReactionTarget—then replace `target as
unknown as SpectrumMessage` with either a narrowed value from the type guard or
the correctly typed ReactionTarget.

In `@packages/spectrum-ts/src/providers/telegram/types.ts`:
- Around line 7-17: The schema cacheConfigSchema currently uses .positive() for
integer capacity fields (messages, polls, pollVotes, albumConcurrent) which
rejects 0; update those fields to use .nonnegative() so zero is accepted as a
valid sentinel to disable a cache slot while keeping albumDebounceMs and
albumCeilingMs as nonnegative already; locate and change the validators for
messages, polls, pollVotes, and albumConcurrent inside cacheConfigSchema
accordingly.

---

Nitpick comments:
In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts`:
- Around line 83-84: The console.error call inside the flush() function should
be replaced with structured error handling: remove console.error("Telegram album
flush failed:", err) and instead invoke the module's error handler or logger
(e.g. this.options.onError?.(err) or this.logger?.error("Telegram album flush
failed", err)); if no handler exists add an optional onError callback to the
class/options and call that, falling back to rethrowing the error so callers can
decide how to log/handle it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b25fb059-2e86-44ae-9b50-db487a2e8a46

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and a1b998b.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (31)
packages/spectrum-ts/src/providers/telegram/index.ts (1)

22-23: ⚡ Quick win

Replace asRuntime assertion with a runtime type guard.

At Line 23, client as TelegramRuntime skips narrowing and can mask invalid caller state. Add a small isTelegramRuntime(...) guard and throw a descriptive Error when the shape is invalid.

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions".

packages/spectrum-ts/src/providers/telegram/messages.ts (10)

412-451: ⚖️ Poor tradeoff

Pre-validation loop still allows deterministic child failures that cause partial sends.

The upfront type-based validation (lines 414–450) correctly rejects unsupported content types before any child is sent. However, children that pass this check can still fail deterministically on local validation that happens later:

  • A contact without a phone number throws at line 292–296 in sendContactContent
  • A poll with an overlong option title throws at line 340–348 in validatePollOptionTitles

If the first group item is valid but a later child triggers one of these, you'll partial-send. Consider running these deterministic validators in the pre-validation pass.

♻️ Extract and reuse validators
+const validateGroupChild = (child: Content): void => {
+  if (child.type === "contact") {
+    const phone = child.phones?.[0]?.value;
+    if (!phone) {
+      throw new Error(
+        "Telegram sendContact requires at least one phone number on the contact"
+      );
+    }
+  }
+  if (child.type === "poll") {
+    validatePollOptionTitles(child);
+  }
+};

 const sendGroupContent = async (
   runtime: TelegramRuntime,
   spaceId: string,
   group: Group,
   opts: SendOpts
 ): Promise<TelegramMessage> => {
   // ... existing min-items check ...

   // Pre-validate every child
   for (const item of group.items) {
     const child = item.content;
     // ... existing type checks ...
+    validateGroupChild(child);
   }

1-19: LGTM!

Import statements are well-organized and specific, avoiding namespace imports per coding guidelines.


25-31: LGTM!

toChatId correctly handles both numeric and string chat IDs, with proper integer validation to ensure numeric IDs round-trip cleanly.


33-46: LGTM!

toMessageId properly validates strict decimal digits and rejects zero/negative values, addressing previous review feedback. The validation is robust.


48-79: LGTM!

recordOutbound correctly handles sender fallback chain (message.frommessage.sender_chatruntime.me) and uses composite cache keys via messageCacheKey, addressing prior review feedback.


92-110: LGTM!

The PHOTO_MIME_TYPES whitelist correctly routes only JPEG/PNG/WEBP to sendPhoto, with all other formats falling through to sendDocument. This addresses the prior review about unsupported image formats.


163-211: LGTM!

The exhaustive switch with never guard ensures all attachment routes are handled, and the pattern is clean.


350-384: LGTM!

sendPollContent correctly sets is_anonymous: false (line 363) to ensure poll_answer updates are delivered, addressing prior review feedback.


396-482: LGTM!

Group sending correctly validates minimum item count, pre-validates child types, caches the wrapper under the lead child's key, and handles the reply-only-first-child semantics. Prior review concerns have been addressed.


540-588: LGTM!

The dispatchSend switch is exhaustive with the content satisfies never guard, and all content types are properly routed. Unsupported types throw descriptive UnsupportedErrors.

packages/spectrum-ts/src/providers/telegram/events/inbound.ts (6)

28-47: LGTM!

fetchFileBytes correctly uses client.invoke("getFile") and client.downloadFile() for transport consistency, addressing prior review about bypassing the client transport.


49-93: LGTM!

attachmentFromFile and voiceFromFile correctly create fresh fetch calls per read()/stream() invocation, so they don't bind to any external polling signal. Prior review concerns have been addressed.


124-146: LGTM!

contactToContent preserves the full Telegram contact object in raw: contact, addressing the prior review about losing fields when vcard is present.


148-181: LGTM!

extractRichlinkUrl correctly checks text.trim() === linkPreview.url before collapsing to richlink, preserving mixed prose + preview messages as text. Prior review concerns addressed.


215-278: LGTM!

messageToContent follows correct priority order and handles the caption-only fallback properly. Media content takes precedence over caption text.


313-345: LGTM!

coalesceAlbumGroup correctly uses head.id (a real Telegram message_id) for the wrapper, handles single-member edge case by returning the member directly, and promotes any caption to the wrapper.

packages/spectrum-ts/src/providers/telegram/types.ts (4)

19-25: LGTM!

configSchema correctly trims token and apiBaseUrl, addressing prior review feedback about whitespace-only values.


29-38: LGTM!

userSchema correctly marks all profile fields as optional with an explanatory comment, addressing the prior review about the resolver's inability to populate required fields.


48-50: LGTM!

spaceParamsSchema correctly uses z.string().trim().min(1) and z.number().int(), addressing prior review concerns about whitespace and non-integer chat IDs.


59-72: LGTM!

TelegramMessage type composition and TelegramRuntime interface are well-structured with explicit types.

packages/spectrum-ts/src/providers/telegram/runtime/client.ts (6)

17-63: LGTM!

The mutating-method classification and policy-downgrade logic (retryNetworkErrors: false, retryServerErrors: false) correctly implements at-most-once semantics for non-idempotent Telegram methods.


65-92: LGTM!

isAbortLike correctly handles both AbortError and TimeoutError (from AbortSignal.timeout), and combineSignals properly uses AbortSignal.any() for signal composition.


108-182: LGTM!

buildBody correctly detects nested Blobs and fails fast with a clear error message, addressing the prior review about silently corrupted payloads.


191-215: LGTM!

Constructor validation is thorough: token is trimmed and checked for non-empty, baseUrl is normalized (trailing slashes removed), and requestTimeoutMs is validated as non-negative finite or null.


217-300: LGTM!

The timeout signal is correctly created once in invoke() and passed through to invokeOnce() and migration retries via the combined parameter. Abort detection uses the combined signal throughout. Prior review concerns about fresh timeout windows on migration have been addressed.


306-346: LGTM!

downloadFile correctly creates its own timeout signal, combines it with the caller signal, and uses isAbortLike(err, combined) for abort detection throughout. The error snippet reading also propagates aborts correctly.

packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (4)

22-86: LGTM!

AlbumBuffer correctly implements async flushAll() that awaits all in-flight flushes, addressing prior review concerns about teardown ordering. Timer callbacks properly handle rejected promises.


104-150: LGTM!

PollStore provides a clean interface for poll and vote caching with proper key namespacing. The refreshPollOptions method handles dynamic option additions correctly.


183-188: LGTM!

messageCacheKey correctly namespaces by spaceId to handle Telegram's per-chat message_id uniqueness, addressing prior review about cache key collisions.


200-225: LGTM!

createTelegramCache properly wires up all cache components and the destroy() method correctly clears all underlying caches.

Comment thread packages/spectrum-ts/src/providers/telegram/runtime/retry.ts Outdated
Spreading a partial `Partial<RetryPolicy>` into the defaults let an
explicit `undefined` field on the override wipe out the default (e.g.,
`{ maxAttempts: undefined }` would leave the merged policy with no
attempt cap). Add a small `mergeRetryPolicy` helper that skips
undefined entries and route both `withRetry` and `TelegramClient`
through it so sparse overrides are honoured safely.
@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/spectrum-ts/src/providers/telegram/types.ts (1)

9-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Cache capacity validators still reject 0 despite past review.

Lines 9-12 use .positive() which rejects 0, but a previous review (marked as addressed) noted that the documented behavior allows 0 to disable cache slots. The fix to .nonnegative() appears not to have been applied or was reverted.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/types.ts` around lines 9 - 12,
The validators for cache capacities (messages, polls, pollVotes, albumConcurrent
in types.ts) currently use z.number().int().positive() which rejects 0; change
each to z.number().int().nonnegative().optional() so 0 is accepted to represent
"disabled" cache slots, and ensure no other validator in the same file still
uses .positive() for these fields.
🧹 Nitpick comments (1)
packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

52-60: ⚡ Quick win

Prefer native ErrorOptions.cause for network error chaining.

Using super(message, { cause }) preserves standardized cause propagation across runtimes and loggers better than manual assignment. The project's ESNext target fully supports this pattern.

Suggested patch
 export class TelegramNetworkError extends Error {
   readonly method: string;
   override readonly cause: unknown;

   constructor(method: string, cause: unknown) {
     const reason = cause instanceof Error ? cause.message : String(cause);
-    super(`Telegram ${method} network error: ${reason}`);
+    super(`Telegram ${method} network error: ${reason}`, { cause });
     this.name = "TelegramNetworkError";
     this.method = method;
-    this.cause = cause;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/runtime/errors.ts` around lines
52 - 60, The TelegramNetworkError constructor should pass the original cause to
the Error base via ErrorOptions instead of manually assigning it: in the
TelegramNetworkError constructor call super(`Telegram ${method} network error:
${reason}`, { cause }) and remove the manual this.cause = cause assignment (you
can keep a readonly cause declaration only if you want an explicit type,
otherwise rely on the inherited cause), while still setting this.name and
this.method as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/spectrum-ts/src/providers/telegram/types.ts`:
- Around line 9-12: The validators for cache capacities (messages, polls,
pollVotes, albumConcurrent in types.ts) currently use
z.number().int().positive() which rejects 0; change each to
z.number().int().nonnegative().optional() so 0 is accepted to represent
"disabled" cache slots, and ensure no other validator in the same file still
uses .positive() for these fields.

---

Nitpick comments:
In `@packages/spectrum-ts/src/providers/telegram/runtime/errors.ts`:
- Around line 52-60: The TelegramNetworkError constructor should pass the
original cause to the Error base via ErrorOptions instead of manually assigning
it: in the TelegramNetworkError constructor call super(`Telegram ${method}
network error: ${reason}`, { cause }) and remove the manual this.cause = cause
assignment (you can keep a readonly cause declaration only if you want an
explicit type, otherwise rely on the inherited cause), while still setting
this.name and this.method as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bfa09c97-dc26-46e9-93c2-1ad0f82dd900

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 761285a.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (22)
.gitignore (1)

47-51: Ignore rules are now properly scoped and clear.

Good update: Line 48 (test-results/) is specific (instead of broadly ignoring tests), and Line 51 (telegram-full-backup/) is a reasonable local-artifact ignore with clear inline context.

README.md (1)

56-56: Telegram platform docs entry looks correct.

Nice addition to the platform matrix; package path matches the provider layout introduced in this PR.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json (1)

1-604: Schema update is coherent and generation-ready.

The Bot API subset, method signatures, and 9.6 poll/contact modeling are consistent with the provider runtime/generator contract.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts (1)

48-239: Generator implementation looks solid.

Schema loading, DSL translation, and output emission are cleanly structured and fit the schema-first workflow well.

packages/spectrum-ts/package.json (1)

47-59: Package wiring changes are aligned with the feature.

gen:telegram and quick-lru fit the introduced generator/runtime cache flow.

package.json (1)

14-15: Root script addition is good.

gen:telegram at workspace root improves discoverability for regeneration.

packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md (1)

16-99: Spec README is clear and operationally useful.

Scope boundaries, cache behavior, and regeneration instructions are well captured for maintainers.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

22-135: Retry path looks robust after the recent fixes.

The undefined-safe policy merge, abort-aware sleep, and Telegram-specific delay handling are all in good shape.

packages/spectrum-ts/src/providers/telegram/identity.ts (1)

1-84: LGTM!

Identity mapping utilities correctly normalize Telegram User/Chat data into Spectrum sender/space shapes. The DM title synthesis (lines 56-67) and type guard (line 61) follow best practices.

packages/spectrum-ts/src/providers/telegram/runtime/client.ts (1)

1-377: LGTM!

The client implementation correctly handles:

  • Abort signal composition with timeout (lines 224-228, 311-315)
  • Nested Blob validation (lines 122-135, 161-165, 173-177)
  • Request timeout enforcement across retries and migrations (lines 217-230)
  • Token and timeout validation in constructor (lines 192-214)
packages/spectrum-ts/src/providers/telegram/runtime/polling.ts (1)

1-102: LGTM!

Polling implementation correctly:

  • Sanitizes timeout to Telegram's [0, 50] range (lines 15-27)
  • Distinguishes between omitted vs empty allowedUpdates (line 64)
  • Handles dropPendingUpdates with offset calculation (lines 67-76)
  • Propagates abort signals throughout (lines 71-72, 87-89, 94-95)
packages/spectrum-ts/src/providers/telegram/events/inbound.ts (1)

1-346: LGTM!

Message conversion logic correctly:

  • Implements lazy file fetching via client methods (lines 30-47, 60-70, 79-91)
  • Guards vCard/richlink/poll parsing with try-catch (lines 114-122, 195-199, 203-210)
  • Preserves raw contact data (line 132)
  • Only emits richlink when text is effectively bare (lines 152-181)
  • Anchors coalesced album wrapper to a real message id (line 332)
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (1)

1-226: LGTM!

Cache implementation correctly:

  • Makes album flush awaitable during teardown (lines 52-54, 72-85)
  • Scopes message cache keys by space id (lines 184-187)
  • Documents QuickLRU's 2× maxSize behavior (lines 169-170)
  • Provides poll option refresh for dynamic polls (lines 125-137)
packages/spectrum-ts/src/providers/telegram/events/polls.ts (1)

1-99: LGTM!

Poll answer event generation correctly:

  • Computes per-option vote diffs using sets (lines 9-18)
  • Handles both regular voters and anonymous channel admins (lines 53-61)
  • Guards against missing cached polls and out-of-range option indexes (lines 62-65, 31-33)
  • Generates deterministic event ids (lines 77, 88)
packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

1-94: LGTM!

Reaction event generation correctly:

  • Extracts only plain emoji reactions (lines 13-18, 20-32)
  • Uses properly scoped message cache keys (line 54)
  • Handles both regular users and anonymous channel admins (lines 68-74)
  • Falls back to a stub for uncached reaction targets (lines 36-46, 54-58)
packages/spectrum-ts/src/providers/telegram/messages.ts (3)

414-451: Still unresolved: pre-validation should include deterministic child validation before first send.

Line 414 currently pre-validates only type-level unsupported content. Deterministic local failures (for example, contact without phone or poll option length checks) can still throw later after earlier children are already posted, causing partial group sends.

Also applies to: 456-465


35-46: Strict message_id parsing is solid.

Good hardening here: decimal-only, safe-integer check, and positive constraint prevents coercive/invalid IDs from leaking into API calls.


350-365: Good poll send defaults for vote-stream compatibility.

Pinning is_anonymous: false together with local option-title validation is the right setup for reliable poll_answer handling.

packages/spectrum-ts/src/providers/telegram/index.ts (2)

22-23: asRuntime still bypasses runtime validation at the platform boundary.

This remains a direct assertion from unknown, so malformed client values fail late and opaquely.


76-115: Lifecycle + message/send wiring looks clean.

createClient/destroyClient, stream hookup, and cache-backed getMessage action are composed clearly and consistently.

packages/spectrum-ts/tsup.config.ts (1)

8-8: Build entry wiring looks correct.

Adding the Telegram entrypoint here aligns with the new provider surface and keeps build outputs consistent.

packages/spectrum-ts/src/providers/telegram/events/index.ts (1)

85-99: Album coalescing + teardown coordination are in good shape.

Rewriting each member cache entry to the grouped wrapper and draining albums in pump terminal paths improves consistency and shutdown reliability.

Also applies to: 104-159

… error

Pass the original cause to the Error base via `super(msg, { cause })`
instead of manually assigning it. ESNext targets pick up the standard
inherited `cause` slot, which loggers and devtools render natively.
@KumarVandit
Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
packages/spectrum-ts/src/providers/telegram/types.ts (1)

9-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow 0 for cache capacities to support the documented "disable slot" behavior.

The schema uses .positive() which rejects 0, but the runtime/docs contract indicates 0 disables that cache slot. Users attempting to disable a cache will get a validation error instead of the expected behavior.

🛠️ Suggested fix
 export const cacheConfigSchema = z
   .object({
-    messages: z.number().int().positive().optional(),
-    polls: z.number().int().positive().optional(),
-    pollVotes: z.number().int().positive().optional(),
-    albumConcurrent: z.number().int().positive().optional(),
+    messages: z.number().int().nonnegative().optional(),
+    polls: z.number().int().nonnegative().optional(),
+    pollVotes: z.number().int().nonnegative().optional(),
+    albumConcurrent: z.number().int().nonnegative().optional(),
     albumDebounceMs: z.number().int().nonnegative().optional(),
     albumCeilingMs: z.number().int().nonnegative().optional(),
     coalesceAlbums: z.boolean().optional(),
   })
   .optional();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/types.ts` around lines 9 - 12,
The schema in packages/spectrum-ts/src/providers/telegram/types.ts currently
uses .positive() for the cache capacity fields (messages, polls, pollVotes,
albumConcurrent) which rejects 0; change those validators to allow zero (e.g.
use .nonnegative() or .int().min(0)) so a value of 0 (to disable the slot)
passes validation while preserving integer and optional constraints for each
field.
packages/spectrum-ts/src/providers/telegram/messages.ts (1)

412-451: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pre-validation still misses deterministic child failures.

The upfront validation rejects unsupported types but does not run local validators that would fail later. A group containing a valid first item followed by a contact without a phone number (line 292 check) or a poll with overlong option titles (line 340 check) will partial-send before the later child throws.

💡 Suggested approach

Extract the deterministic validators and run them in the pre-validation loop:

+const validateChildContent = (child: Content): void => {
+  if (child.type === "contact") {
+    const phone = child.phones?.[0]?.value;
+    if (!phone) {
+      throw new Error(
+        "Telegram sendContact requires at least one phone number on the contact"
+      );
+    }
+  }
+  if (child.type === "poll") {
+    validatePollOptionTitles(child);
+  }
+};
+
 const sendGroupContent = async (
   // ...
 ): Promise<TelegramMessage> => {
   // ...
   for (const item of group.items) {
     const child = item.content;
     // existing type checks...
+    validateChildContent(child);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts` around lines 412 -
451, The pre-validation loop over group.items currently only rejects unsupported
types via UnsupportedError.content but doesn’t run per-type deterministic
validators, which allows later children (e.g., contact without phone, poll with
long options) to fail after a partial send; update the loop in messages.ts that
iterates group.items to call the existing local validators for each child's
content (e.g., invoke the contact validator, poll/options validator, and any
other deterministic validateXContent functions you have) and surface/throw the
same validation errors immediately (instead of waiting until send-time),
ensuring the pre-validation rejects any child that would deterministically fail.
🧹 Nitpick comments (3)
packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (1)

80-85: 💤 Low value

Consider structured logging over console.error.

The error logging uses console.error which may not integrate well with production logging infrastructure. Consider using a logger interface or at minimum including more context (e.g., mediaGroupId).

💡 Suggestion
     } catch (err) {
-      console.error("Telegram album flush failed:", err);
+      console.error("Telegram album flush failed:", { mediaGroupId, err });
     }

As per coding guidelines, "Remove console.log, debugger, and alert statements from production code" - though console.error for error logging is often acceptable, structured logging would be preferable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts` around lines 80
- 85, Replace the console.error call in the Telegram cache flush catch block
with structured logging via the existing logger interface (e.g.,
this.logger.error or this.options.logger.error) and include contextual fields
like mediaGroupId and entry.members; update the catch in the method that calls
this.options.flush to call the structured logger (fallback to a logger on this
or this.options) and pass a descriptive message plus the err object and context
so production logging systems can index it.
packages/spectrum-ts/src/providers/telegram/runtime/client.ts (1)

45-52: 💤 Low value

Consider using some() for cleaner prefix matching.

The loop can be simplified using array method for better readability.

💡 Alternative implementation
 const isMutatingMethod = (method: string): boolean => {
-  for (const prefix of MUTATING_METHOD_PREFIXES) {
-    if (method.startsWith(prefix)) {
-      return true;
-    }
-  }
-  return false;
+  return MUTATING_METHOD_PREFIXES.some((prefix) => method.startsWith(prefix));
 };

As per coding guidelines, "Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript" - though some() is even more idiomatic for this pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/runtime/client.ts` around lines
45 - 52, Replace the manual loop in isMutatingMethod with an idiomatic
array.some() check against MUTATING_METHOD_PREFIXES: locate the isMutatingMethod
function and change the implementation to return
MUTATING_METHOD_PREFIXES.some(prefix => method.startsWith(prefix)) so it
directly returns whether any prefix matches, removing the explicit for...of loop
and the manual true/false return logic.
packages/spectrum-ts/src/providers/telegram/messages.ts (1)

25-31: 💤 Low value

toChatId accepts invalid string inputs without validation.

The function accepts any non-numeric string as a valid chat_id, including empty strings after the numeric check fails. Telegram usernames must start with @ and be at least 5 characters. Invalid values will fail at the API boundary with confusing errors.

💡 Suggested validation
 const toChatId = (spaceId: string): number | string => {
+  if (!spaceId) {
+    throw new Error("Telegram chat_id cannot be empty");
+  }
   const asNumber = Number(spaceId);
   if (Number.isInteger(asNumber) && String(asNumber) === spaceId) {
     return asNumber;
   }
   return spaceId;
 };

As per coding guidelines, "Validate and sanitize user input in JavaScript/TypeScript".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/spectrum-ts/src/providers/telegram/messages.ts` around lines 25 -
31, The toChatId function currently accepts any non-numeric string (including
empty strings) which causes confusing API errors; update toChatId to trim the
input, parse numeric as before, and for non-numeric values validate that the
string starts with '@' and is at least 5 characters (or match a Telegram
username regex) and otherwise throw a clear ValidationError (or return a
rejected result) so invalid chat_id values are rejected early; adjust callers of
toChatId if they expect a thrown error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/spectrum-ts/src/providers/telegram/messages.ts`:
- Around line 412-451: The pre-validation loop over group.items currently only
rejects unsupported types via UnsupportedError.content but doesn’t run per-type
deterministic validators, which allows later children (e.g., contact without
phone, poll with long options) to fail after a partial send; update the loop in
messages.ts that iterates group.items to call the existing local validators for
each child's content (e.g., invoke the contact validator, poll/options
validator, and any other deterministic validateXContent functions you have) and
surface/throw the same validation errors immediately (instead of waiting until
send-time), ensuring the pre-validation rejects any child that would
deterministically fail.

In `@packages/spectrum-ts/src/providers/telegram/types.ts`:
- Around line 9-12: The schema in
packages/spectrum-ts/src/providers/telegram/types.ts currently uses .positive()
for the cache capacity fields (messages, polls, pollVotes, albumConcurrent)
which rejects 0; change those validators to allow zero (e.g. use .nonnegative()
or .int().min(0)) so a value of 0 (to disable the slot) passes validation while
preserving integer and optional constraints for each field.

---

Nitpick comments:
In `@packages/spectrum-ts/src/providers/telegram/messages.ts`:
- Around line 25-31: The toChatId function currently accepts any non-numeric
string (including empty strings) which causes confusing API errors; update
toChatId to trim the input, parse numeric as before, and for non-numeric values
validate that the string starts with '@' and is at least 5 characters (or match
a Telegram username regex) and otherwise throw a clear ValidationError (or
return a rejected result) so invalid chat_id values are rejected early; adjust
callers of toChatId if they expect a thrown error.

In `@packages/spectrum-ts/src/providers/telegram/runtime/cache.ts`:
- Around line 80-85: Replace the console.error call in the Telegram cache flush
catch block with structured logging via the existing logger interface (e.g.,
this.logger.error or this.options.logger.error) and include contextual fields
like mediaGroupId and entry.members; update the catch in the method that calls
this.options.flush to call the structured logger (fallback to a logger on this
or this.options) and pass a descriptive message plus the err object and context
so production logging systems can index it.

In `@packages/spectrum-ts/src/providers/telegram/runtime/client.ts`:
- Around line 45-52: Replace the manual loop in isMutatingMethod with an
idiomatic array.some() check against MUTATING_METHOD_PREFIXES: locate the
isMutatingMethod function and change the implementation to return
MUTATING_METHOD_PREFIXES.some(prefix => method.startsWith(prefix)) so it
directly returns whether any prefix matches, removing the explicit for...of loop
and the manual true/false return logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e11fc308-cdd7-46fc-85f0-f6f578a0c97b

📥 Commits

Reviewing files that changed from the base of the PR and between 000e1ed and 8f69c12.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/spectrum-ts/src/providers/telegram/generated/methods.ts is excluded by !**/generated/**
  • packages/spectrum-ts/src/providers/telegram/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (21)
  • .gitignore
  • README.md
  • package.json
  • packages/spectrum-ts/package.json
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/tsup.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity in TypeScript
Prefer unknown over any when the type is genuinely unknown in TypeScript
Use const assertions (as const) for immutable values and literal types in TypeScript
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions in JavaScript/TypeScript
Prefer for...of loops over .forEach() and indexed for loops in JavaScript/TypeScript
Use optional chaining (?.) and nullish coalescing (??) for safer property access in JavaScript/TypeScript
Prefer template literals over string concatenation in JavaScript/TypeScript
Use destructuring for object and array assignments in JavaScript/TypeScript
Use const by default, let only when reassignment is needed, never var in JavaScript/TypeScript
Always await promises in async functions - don't forget to use the return value in JavaScript/TypeScript
Use async/await syntax instead of promise chains for better readability in JavaScript/TypeScript
Handle errors appropriately in async code with try-catch blocks in JavaScript/TypeScript
Don't use async functions as Promise executors in JavaScript/TypeScript
Remove console.log, debugger, and alert statements from production code in JavaScript/TypeScript
Throw Error objects with descriptive messages, not strings or other values in JavaScript/TypeScript
Use try-catch blocks meaningfully - don't catch errors just to rethrow them in JavaScript/TypeScript
Prefer early returns over nested conditionals for error cases in JavaScript/TypeScript
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables in JavaScript/TypeScript
Use early returns to reduce nesting in JavaScript/TypeScript
Prefer simple conditionals over nested ternary operators in JavaScript/TypeScript
Group related code together and separate concerns in JavaScript/TypeScript
Don't use eval() or assign directly to document.cookie in JavaScript/TypeScript
Validate and sanitize user input in JavaScript/TypeScript
Avoid spread syntax in accumulators within loops in JavaScript/Ty...

Files:

  • packages/spectrum-ts/src/providers/telegram/events/polls.ts
  • packages/spectrum-ts/tsup.config.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/client.ts
  • packages/spectrum-ts/src/providers/telegram/types.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/errors.ts
  • packages/spectrum-ts/src/providers/telegram/events/reactions.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/polling.ts
  • packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/retry.ts
  • packages/spectrum-ts/src/providers/telegram/identity.ts
  • packages/spectrum-ts/src/providers/telegram/events/index.ts
  • packages/spectrum-ts/src/providers/telegram/index.ts
  • packages/spectrum-ts/src/providers/telegram/events/inbound.ts
  • packages/spectrum-ts/src/providers/telegram/runtime/cache.ts
  • packages/spectrum-ts/src/providers/telegram/messages.ts
🪛 LanguageTool
packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md

[grammar] ~38-~38: Ensure spelling is correct
Context: ...00 vote vectors, 100 concurrent albums, 500ms debounce, 2s ceiling. Set any capacity ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (26)
.gitignore (1)

47-51: Scoped ignore rules look correct.

Nice update replacing broad test ignores with test-results/ and adding a clearly documented local backup artifact ignore. This keeps source tracking safer and intent explicit.

packages/spectrum-ts/src/providers/telegram/events/reactions.ts (1)

83-88: 🏗️ Heavy lift

Remove the double type assertion on the reaction target (Line 87).

target as unknown as SpectrumMessage suppresses type guarantees and can hide shape mismatches between cached/stubbed targets and the reaction contract. Prefer a shared minimal target type (or align asReaction’s input type) so this path compiles without assertions.

#!/bin/bash
set -euo pipefail

# Verify target type expectations and current cast path.
rg -n -C3 'export const asReaction|target:' packages/spectrum-ts/src/content/reaction.ts
rg -n -C5 'export type Message|interface Message' packages/spectrum-ts/src/types/message.ts
rg -n -C5 'type ProviderMessageRecord|interface ProviderMessageRecord' packages/spectrum-ts/src/platform
rg -n -C5 'resolveReactionTarget|reactionTargetStub|as unknown as SpectrumMessage' packages/spectrum-ts/src/providers/telegram/events/reactions.ts

As per coding guidelines, "Leverage TypeScript's type narrowing instead of type assertions."

packages/spectrum-ts/package.json (1)

47-48: Looks good — script wiring and dependency addition are consistent.

The gen:telegram script and quick-lru dependency fit the Telegram provider changes cleanly.

Also applies to: 58-58

packages/spectrum-ts/src/providers/telegram/runtime/errors.ts (1)

3-59: Solid error primitives for runtime classification.

The shape and getters are well-structured for downstream retry and migration handling.

packages/spectrum-ts/src/providers/telegram/runtime/retry.ts (1)

22-39: Retry flow looks robust and well-contained.

Good handling of undefined override values, abort-safe waiting, and Telegram-specific retry semantics.

Also applies to: 41-74, 75-114, 116-135

README.md (1)

56-56: Docs update is accurate.

Platform table now correctly includes Telegram provider availability.

packages/spectrum-ts/tsup.config.ts (1)

8-8: Build entry update is correct.

Including providers/telegram/index in tsup entries is the right wiring for distributable output.

package.json (1)

14-15: Root script addition looks good.

The workspace-level generator command is consistent with the new Telegram codegen flow.

packages/spectrum-ts/src/providers/telegram/events/polls.ts (1)

9-18: Poll diff emission logic is well implemented.

The prior-vs-next vector comparison and add/remove event generation are clear and deterministic.

Also applies to: 44-98

packages/spectrum-ts/src/providers/telegram/bot-api-spec/generators/typescript.ts (1)

45-61: Solid generator hardening and TypeRef coverage.

Nice work adding clear validation/error paths and keeping the generated surface deterministic and readable.

Also applies to: 69-104, 149-157, 220-239

packages/spectrum-ts/src/providers/telegram/runtime/polling.ts (1)

15-27: Polling controls look good.

Timeout sanitization, offset progression, and abort/error handling are clean and production-friendly.

Also applies to: 58-101

packages/spectrum-ts/src/providers/telegram/bot-api-spec/README.md (1)

16-40: Documentation alignment is strong.

The scope/mapping notes and regeneration flow are clear and consistent with the implementation.

Also applies to: 81-99

packages/spectrum-ts/src/providers/telegram/identity.ts (1)

54-67: Private-chat title fallback is a good fix.

This improves DM space identity quality without changing the external shape.

Also applies to: 69-83

packages/spectrum-ts/src/providers/telegram/index.ts (1)

25-39: Platform wiring looks coherent.

Lifecycle setup/teardown, polling passthrough, and cache-backed getMessage integration are well connected.

Also applies to: 76-104, 109-114

packages/spectrum-ts/src/providers/telegram/bot-api-spec/schema/telegram.json (1)

2-3: Schema updates are in good shape.

The 9.6-aligned poll/contact coverage and method definitions match the provider’s intended scope.

Also applies to: 187-195, 256-291, 476-535

packages/spectrum-ts/src/providers/telegram/events/index.ts (1)

81-113: Event stream teardown/coalescing logic looks robust.

Good sequencing around album draining, abort handling, and final stream termination.

Also applies to: 115-159

packages/spectrum-ts/src/providers/telegram/events/inbound.ts (1)

30-47: Inbound mapping improvements look solid.

The lazy-download flow, richlink guardrails, and coalesced album identity handling are all well implemented.

Also applies to: 148-181, 280-311, 315-345

packages/spectrum-ts/src/providers/telegram/messages.ts (1)

540-588: LGTM!

The dispatchSend function has proper exhaustive type checking with the satisfies never pattern and handles all content types appropriately.

packages/spectrum-ts/src/providers/telegram/types.ts (2)

19-27: LGTM!

The configuration schema properly validates and trims string inputs, with appropriate constraints on pollingTimeout (positive, max 50).


67-72: LGTM!

The TelegramRuntime interface is clean and well-typed, providing a clear contract for the runtime dependencies.

packages/spectrum-ts/src/providers/telegram/runtime/client.ts (3)

191-215: LGTM!

The constructor properly validates the token (non-empty after trim), normalizes baseUrl to remove trailing slashes, and validates requestTimeoutMs as either null or a non-negative finite number. This ensures fail-fast behavior for configuration errors.


217-230: LGTM!

The timeout signal is correctly built once per invoke() call so the deadline covers retries, backoff, and migration follow-ups. The combined signal is passed to invokeOnce() ensuring consistent timeout behavior.


306-346: LGTM!

The downloadFile method properly combines caller and timeout signals, propagates abort errors, and includes error snippets in API errors for better debugging.

packages/spectrum-ts/src/providers/telegram/runtime/cache.ts (3)

22-50: LGTM!

The AlbumBuffer class properly handles concurrent album buffering with debounce and ceiling timers, enforces capacity limits by flushing the oldest album on overflow, and the push method correctly rearms the debounce timer on each new member.


104-150: LGTM!

The PollStore class correctly manages poll and vote caches using QuickLRU. The refreshPollOptions method appropriately uses peek() to check existence without affecting LRU order before updating.


200-225: LGTM!

The createTelegramCache factory properly initializes all cache components and provides a clean destroy() method for cleanup.

@KumarVandit
Copy link
Copy Markdown
Member Author

Reopened as #59 with the same branch, now hosted directly on photon-hq/spectrum-ts:telegram-support instead of a fork. All commits (and review history is preserved here for reference).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Just as it is

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants