Conversation
WalkthroughAdded S3-backed persistence for Telegram media in the Wasabi bot, generating public file URLs from runtime config. Media handlers (photo/video/document) now download via Telegram API using bot token, store bytes in S3, and persist telegramFileId/fileUrl/text. Database schema updated to add telegram_file_id and file_url columns. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as Telegram User
participant TG as Telegram
participant WB as Wasabi Bot (server)
participant TAPI as Telegram API
participant S3 as S3 Storage
participant DB as Database
U->>TG: Send photo/video/document (+optional caption)
TG->>WB: Webhook update (message with file_id)
WB->>TAPI: getFile(file_id) using bot token
alt downloadUrl available
TAPI-->>WB: file download URL
WB->>TAPI: HTTP GET file bytes
TAPI-->>WB: Binary media
WB->>WB: Derive extension, build fileUri
WB->>S3: PUT /telegram/files/{fileId}.{ext} (bytes)
S3-->>WB: 200 OK
WB->>DB: Insert ticket_message { text(caption), telegramFileId, fileUrl }
DB-->>WB: Inserted
else missing URL
WB->>DB: Insert ticket_message { text(caption or empty) }
DB-->>WB: Inserted
end
WB-->>TG: Acknowledge handling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
packages/database/src/tables.ts (1)
706-707: Consider text() for fileUrl for very long CDN URLs.If URLs can exceed varchar limits in some drivers, prefer text('file_url') for portability. Optional.
- telegramFileId: varchar('telegram_file_id'), - fileUrl: varchar('file_url'), + telegramFileId: varchar('telegram_file_id'), + fileUrl: text('file_url'),apps/web-app/server/services/telegram/wasabi-bot.ts (6)
143-156: Harden photo upload: robust extension, URL join, and no leading slash in key.
- Derive extension from URL pathname (no query), default to 'jpg' if missing.
- Use URL() to join mediaUrl + key to avoid double slashes.
- Avoid leading slash in S3 key (driver/bucket portability).
- const downloadUrl = await getFileDownloadUrl({ ctx, fileId, botToken }) + const downloadUrl = await getFileDownloadUrl({ ctx, fileId, botToken }) if (!downloadUrl) { return } - const extension = downloadUrl.split('.').pop() - const buffer = await fetch(downloadUrl).then((res) => res.arrayBuffer()) + const { pathname } = new URL(downloadUrl) + const dot = pathname.lastIndexOf('.') + const extension = dot > -1 ? pathname.slice(dot + 1) : 'jpg' + const buffer = await fetch(downloadUrl).then((res) => res.arrayBuffer()) - const fileUri = `/${S3_TELEGRAM_DIRECTORY}/${fileId}.${extension}` - const fileUrl = `${mediaUrl}${fileUri}` + const key = `${S3_TELEGRAM_DIRECTORY}/${fileId}.${extension}` + const fileUrl = new URL(key, mediaUrl).toString() - const storage = useStorage('s3') - await storage.setItemRaw(fileUri, buffer) + const storage = useStorage('s3') + await storage.setItemRaw(key, buffer)
138-146: Return type nit: usereturninstead ofreturn null.These handlers are effectively Promise. Returning null widens the type.
- if (!botToken) { - return null - } + if (!botToken) { + return + }Apply the same pattern in video and file handlers.
179-185: Video path:downloadUrlis computed but unused; either upload to S3 or drop the call.Right now you fetch the URL only to log it (and we’re removing that log). Avoid extra Telegram API calls or complete the S3 flow for parity.
- const downloadUrl = await getFileDownloadUrl({ ctx, fileId: ctx.message.video.file_id, botToken }) + // TODO: If you want S3 parity with photos, fetch and store like in handlePhoto. + // Otherwise, remove this call to avoid an extra API hit.Also applies to: 189-194
207-213: Document path: same as video — unuseddownloadUrland no S3 upload.Mirror photo handling to persist
fileUrlor remove the call.- const downloadUrl = await getFileDownloadUrl({ ctx, fileId: ctx.message.document.file_id, botToken }) + // TODO: If needed, compute and upload to S3 like in handlePhoto.Also applies to: 217-223
7-8: Config join robustness and naming nit.
- Consider deriving mediaBaseUrl once (without trailing slash) to prevent accidental
//joins elsewhere.-const S3_TELEGRAM_DIRECTORY = 'telegram/files' -const { telegram, public: { mediaUrl } } = useRuntimeConfig() +const S3_TELEGRAM_DIRECTORY = 'telegram/files' +const { telegram, public: { mediaUrl } } = useRuntimeConfig() +const mediaBaseUrl = mediaUrl.endsWith('/') ? mediaUrl.slice(0, -1) : mediaUrlAnd then use
new URL(key, mediaBaseUrl).toString()(see photo diff).Also applies to: 10-10
151-156: Optional: set Content-Type via transaction headers when calling setItemRawUnstorage's S3/HTTP driver maps transaction option headers to the underlying request — pass Content-Type as an options header: await storage.setItemRaw(fileUri, buffer, { headers: { 'Content-Type': mimeType } }). Derive mimeType from extension (e.g., 'image/jpeg' for jpg).
File: apps/web-app/server/services/telegram/wasabi-bot.ts Lines: 151–156
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web-app/server/services/telegram/wasabi-bot.ts(4 hunks)packages/database/src/tables.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (2)
packages/database/src/tables.ts (1)
706-707: LGTM on new columns; confirm migrations and repository types are updated.telegramFileId and fileUrl additions look consistent with usage in wasabi-bot. Please ensure a Drizzle migration is generated/applied and repository.ticket.createMessage accepts these fields in its insert type.
apps/web-app/server/services/telegram/wasabi-bot.ts (1)
160-163: Repository type alignment for new fields.Ensure repository.ticket.createMessage accepts telegramFileId and fileUrl; otherwise TS will complain or fields will be dropped.
|
|
||
| // Save photo? | ||
| logger.log('photo', data.user.id, ctx.message.from.id, ctx.message.text, ctx.message.photo, downloadUrl) | ||
| logger.log('photo', data.user.id, ctx.message.from.id, ctx.message.caption, ctx.message.photo, downloadUrl) |
There was a problem hiding this comment.
Stop logging downloadUrl — it embeds the Telegram bot token (secret leak).
downloadUrl contains bot<token> and will leak credentials into logs.
Apply this diff to remove the secret from logs:
- logger.log('photo', data.user.id, ctx.message.from.id, ctx.message.caption, ctx.message.photo, downloadUrl)
+ logger.log('photo', data.user.id, ctx.message.from.id, ctx.message.caption, ctx.message.photo)- logger.log('video', data.user.id, ctx.message.from.id, ctx.message.text, ctx.message.caption, ctx.message.video, downloadUrl)
+ logger.log('video', data.user.id, ctx.message.from.id, ctx.message.text, ctx.message.caption, ctx.message.video)- logger.log('file', data.user.id, ctx.message.from.id, ctx.message.text, ctx.message.caption, ctx.message.document, downloadUrl)
+ logger.log('file', data.user.id, ctx.message.from.id, ctx.message.text, ctx.message.caption, ctx.message.document)Also applies to: 193-193, 221-221
🤖 Prompt for AI Agents
In apps/web-app/server/services/telegram/wasabi-bot.ts around lines 165, 193 and
221, the logger.log calls include downloadUrl which contains the bot token and
leaks secrets; remove downloadUrl from the arguments passed to logger.log (log
the other fields like 'photo', user ids, caption and photo metadata only) or
replace it with a non-sensitive placeholder (e.g., '[REDACTED]' or true/false)
before logging to avoid embedding the bot token in logs.



Summary by CodeRabbit