feat: add /sendbatch command for batch sends#61
Conversation
Adds a new /sendbatch Telegram command that allows sending sats to multiple recipients in a single operation with confirmation. Supports two formats: - Per-line memo: /sendbatch\n<amount> @user <memo>\n... - Shared memo: /sendbatch <memo>\n<amount> @user\n... Includes balance pre-check and re-check at execution, sequential transfer execution with stop-on-failure, idempotency via Bunt storage, and per-recipient notifications.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
actor User as User (Sender)
participant Bot as "Telegram Bot"
participant Parser as "Input Parser"
participant Resolver as "Username Resolver"
participant Storage as "Batch Storage"
participant UI as "Inline Confirmation UI"
participant Executor as "Confirm/Cancel Handlers"
participant Ledger as "Ledger/Transactions"
participant Recipient as "Recipient Notification"
User->>Bot: /sendbatch with memo + recipient lines
Bot->>Parser: Parse lines -> amounts, usernames, memos
Parser-->>Bot: Parsed recipients
Bot->>Resolver: Resolve `@usernames` -> internal users
Resolver-->>Bot: Resolved recipients
Bot->>Bot: Validate sender wallet & compute total
Bot->>Storage: Persist SendBatchData -> BatchID
Storage-->>Bot: BatchID
Bot->>UI: Send confirmation (Confirm/Cancel) with BatchID
alt User clicks Confirm
UI->>Executor: confirmSendBatchHandler(BatchID)
Executor->>Storage: Load SendBatchData
Executor->>Executor: Verify ownership & re-check balance
Executor->>Ledger: Execute transfers sequentially
Ledger-->>Executor: Transfer results
Executor->>Recipient: Send arrival notifications
Executor->>Storage: Mark batch inactive
Executor->>User: Send summary and total sent
Executor->>UI: Delete confirmation message
else User clicks Cancel
UI->>Executor: cancelSendBatchHandler(BatchID)
Executor->>Storage: Verify owner & inactivate batch
Executor->>UI: Delete confirmation message
Executor->>User: Send cancellation confirmation
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/telegram/sendbatch.go`:
- Around line 284-287: The transaction currently overwrites per-line memos with
a synthetic batch memo; change the assignment so NewTransaction(...) sets t.Memo
to entry.Memo when entry.Memo is non-empty, and only use the generated
transactionMemo as a fallback (i.e., if entry.Memo == "" then t.Memo =
transactionMemo). Update the same pattern where transactions are created around
the other occurrence (the block around the code at the second location
referencing NewTransaction and t.Memo) so invoices/transaction history preserve
shared/per-line memos instead of always using the batch label.
- Around line 237-241: The batch is only marked inactive after sends, which
risks partial double-payments; before any calls to t.Send() in
confirmSendBatchHandler, persist a durable "processing"/consumed state by
updating batchData via batchData.Set(...) (or a new SetState/markProcessing
helper) using bot.Bunt so the batch is stored as consumed/processing, then
proceed with the send loop; ensure any failure to persist aborts before sending
and update the final state after all sends complete (references: batchData,
batchData.Set, t.Send, bot.Bunt).
- Around line 34-41: Add an expiry timestamp to SendBatchData (e.g., ExpiresAt
int64 or ExpiresAt time.Time) and set it when the batch is created; update the
persistence so the expiry is stored with the object. In confirmSendBatchHandler
check the stored ExpiresAt against the current time and reject (return an
error/HTTP 4xx and mark inactive) if the batch is stale; also apply the same
expiry check where batches are loaded/executed (the other handlers referenced
around lines 194-205 and 237-240) so unconfirmed batches cannot be executed
after expiry. Ensure any JSON (un)marshalling and Base.Set/Get logic handles the
new field.
- Around line 21-25: The package-global ReplyMarkup (sendBatchConfirmationMenu)
and its buttons (btnCancelSendBatch, btnConfirmSendBatch) are being mutated per
request causing races; change the code to create a fresh tb.ReplyMarkup and
per-request buttons inside the handler that needs them (do not call
.Data()/.Inline() on the package-global), and apply the same fix for the
analogous sendConfirmationMenu in send.go so each request gets its own locally
constructed menu and callback data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 412fa896-728e-4f84-8463-387929cf68b6
📒 Files selected for processing (2)
internal/telegram/handler.gointernal/telegram/sendbatch.go
translations/en.toml
Outdated
| */tip* 🏅 Reply to a message to tip: `/tip <amount> [<memo>]` | ||
| */balance* 👑 Check your balance: `/balance` | ||
| */send* 💸 Send funds to a user: `/send <amount> @user or user@ln.tips [<memo>]` | ||
| */sendbatch* 💸 Send to multiple users: `/sendbatch [memo]` |
There was a problem hiding this comment.
This doesn't properly explain the command.
There was a problem hiding this comment.
I've updated the /sendbatch help within the /help command output. It now explains the multi-line structure clearly without cluttering the main text too much.
translations/en.toml
Outdated
| */sendbatch* 💸 Send to multiple users: | ||
| `/sendbatch [memo]` | ||
| `<amount> @user1 [memo]` | ||
| `<amount> @user2 [memo]` |
There was a problem hiding this comment.
Still doesn't explain the fact that lkr values can be used here.
There was a problem hiding this comment.
I've updated the /sendbatch help in en.toml to explain that LKR values are converted to sats
Use entry.Memo (shared or per-line) as the transaction memo when present, falling back to the synthetic batch label only when empty. This ensures memos appear in transaction history and invoice metadata. Addresses CodeRabbit review comment #4.
|
@helloscoopa Ready for re-review. CodeRabbit comments addressed (3 dismissed with reasoning, 1 fixed: memo now preserved on transaction records). |
Summary
/sendbatchcommand for sending sats to multiple recipients in one operationTransactional Safety
Usage
or with shared memo:
Test plan
/sendbatchon first line only🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Localization
Chores