feat(telegram-intel): add custom channel watchlists#2262
feat(telegram-intel): add custom channel watchlists#2262lspassos1 wants to merge 2 commits intokoala73:mainfrom
Conversation
|
@lspassos1 is attempting to deploy a commit to the Elie Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR adds a user-defined Telegram watchlist to the Telegram Intel panel, allowing users to resolve any public channel by Key findings:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant TelegramIntelPanel
participant telegram-intel service
participant EdgeFn as api/telegram-resolve<br/>api/telegram-channel
participant Relay as ais-relay.cjs
participant TG as Telegram API
User->>TelegramIntelPanel: types @username in input
TelegramIntelPanel->>TelegramIntelPanel: debounce 800ms
TelegramIntelPanel->>telegram-intel service: fetchTelegramChannelPreview(username)
telegram-intel service->>EdgeFn: GET /api/telegram-resolve?username=
EdgeFn->>Relay: GET /telegram/resolve?username=
Relay->>TG: getEntity(username)
TG-->>Relay: entity
Relay->>TG: GetFullChannel (member count)
TG-->>Relay: ChannelFull
Relay-->>EdgeFn: { username, title, memberCount, url }
EdgeFn-->>telegram-intel service: preview (cached 24h CDN)
telegram-intel service-->>TelegramIntelPanel: TelegramChannelPreview
TelegramIntelPanel->>TelegramIntelPanel: renderPreview() – show Add button
User->>TelegramIntelPanel: clicks Add / presses Enter
TelegramIntelPanel->>telegram-watchlist: addTelegramWatchlistEntry()
telegram-watchlist->>telegram-watchlist: persist to localStorage
telegram-watchlist->>TelegramIntelPanel: dispatchEvent(wm-telegram-watchlist-changed)
TelegramIntelPanel->>TelegramIntelPanel: renderWatchlistPills()
TelegramIntelPanel->>telegram-intel service: fetchTelegramChannelFeed(username)
telegram-intel service->>EdgeFn: GET /api/telegram-channel?username=&limit=
EdgeFn->>Relay: GET /telegram/channel?username=&limit=
Relay->>TG: getMessages(entity, limit)
TG-->>Relay: messages[]
Relay-->>EdgeFn: TelegramFeedResponse (cached 60s)
EdgeFn-->>telegram-intel service: feed items
telegram-intel service-->>TelegramIntelPanel: items with watchlist=true
TelegramIntelPanel->>TelegramIntelPanel: mergeTelegramItems(watchlist, base) → renderItems()
Reviews (1): Last reviewed commit: "feat(telegram-intel): add custom channel..." | Re-trigger Greptile |
src/components/TelegramIntelPanel.ts
Outdated
| @@ -68,13 +156,193 @@ export class TelegramIntelPanel extends Panel { | |||
| return; | |||
| } | |||
There was a problem hiding this comment.
Base-feed errors are silently swallowed
The original guard was if (!this.relayEnabled || response.error), which surfaced the error message to the user whenever the relay returned an error (even while enabled is still true). The new guard only checks !this.relayEnabled, so any transient fetch error on the curated feed (e.g., relay timeout, 5xx, partial response) now falls through silently: baseItems is set to [], syncWatchlistFeed runs, and the user sees an empty panel with no indication that the main feed failed.
Consider restoring the response.error branch:
| if (!this.relayEnabled || response.error) { | |
| this.watchlistItems = []; | |
| this.setCount(0); | |
| replaceChildren(this.content, | |
| h('div', { className: 'empty-state error' }, | |
| response.error || t('components.telegramIntel.disabled') | |
| ), | |
| ); | |
| return; | |
| } |
scripts/ais-relay.cjs
Outdated
| if (!(full?.fullChat instanceof telegramState.api.ChatFull)) { | ||
| memberCount = full?.fullChat?.participantsCount ?? null; | ||
| } |
There was a problem hiding this comment.
Semantically inverted
ChatFull instanceof check
GetFullChannel always returns a ChannelFull, never a ChatFull. The condition !(... instanceof ChatFull) is therefore always true, so participantsCount is unconditionally extracted — the guard does nothing useful. If a future call ever yields an unexpected response type, the ?? null fallback rescues the value silently.
A clearer and more defensive form would be:
| if (!(full?.fullChat instanceof telegramState.api.ChatFull)) { | |
| memberCount = full?.fullChat?.participantsCount ?? null; | |
| } | |
| if (full?.fullChat instanceof telegramState.api.ChannelFull) { | |
| memberCount = full.fullChat.participantsCount ?? null; | |
| } |
This way the intent is explicit, type-safe, and won't accidentally pick up participantsCount from an unrelated payload type.
|
Addressed the latest review feedback in
Validation rerun:
|
Summary
Adds a user-defined Telegram watchlist to Telegram Intel so any public channel can be added by
@username, persisted locally, previewed before save, and merged into the existing curated feed.Root cause
Telegram Intel only exposed the product-managed relay feed. There was no way for a user to resolve a public channel, persist it in personal settings, or fetch that channel's recent posts alongside the curated OSINT stream.
Changes
telegram:watchlist:v1with normalization and change eventstelegram-resolveandtelegram-channeledge handlers plus matching relay endpointsgetEntity()and fetch member counts throughGetFullChannelValidation
npx tsx --test tests/telegram-watchlist.test.mts tests/telegram-intel-service.test.mts tests/telegram-edge-handlers.test.mjs tests/edge-functions.test.mjsnpm run typecheckRisk
Low to moderate. The change stays on the existing Telegram relay path, but it introduces new on-demand relay routes that depend on the configured Telegram session remaining valid.
Closes #1994