Skip to content

feat: add /sendbatch command for batch sends#61

Merged
helloscoopa merged 7 commits intomainfrom
feat/sendbatch-command
Mar 25, 2026
Merged

feat: add /sendbatch command for batch sends#61
helloscoopa merged 7 commits intomainfrom
feat/sendbatch-command

Conversation

@xbuddhi
Copy link
Copy Markdown

@xbuddhi xbuddhi commented Mar 25, 2026

Summary

  • Adds /sendbatch command for sending sats to multiple recipients in one operation
  • Supports two input formats: per-line memos and shared memo on command line
  • Includes confirmation step with full summary before execution

Transactional Safety

  • Balance checked before confirmation AND re-checked at execution time
  • Sender mutex locked during entire batch (blocks concurrent /send, /tip, /pay)
  • Sequential execution with stop-on-first-failure (no silent partial sends)
  • Idempotent: Bunt storage marks batch inactive after execution, preventing double-confirm
  • Each transfer logged as individual Transaction record in DB

Usage

/sendbatch
10 @xbuddhi SALARY 2026
10 @cmnisal SALARY 2026 MK
10 @rasmr SALARY 2026

or with shared memo:

/sendbatch MARCH SALARY 2026
10 @xbuddhi
10 @cmnisal
10 @rasmr

Test plan

  • Test Format 1: per-line memos with /sendbatch on first line only
  • Test Format 2: shared memo on command line, no per-line memos
  • Test mixed: shared memo + per-line memo override
  • Test confirmation flow: verify summary shows correct totals
  • Test cancel button dismisses batch
  • Test insufficient balance rejection (both at confirmation and execution)
  • Test invalid username rejection
  • Test self-send prevention
  • Test partial failure: verify succeeded/failed/skipped reporting
  • Test concurrent send blocked while batch executes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added /sendbatch to tip multiple users with shared or per-line memos; includes inline confirmation UI with Confirm and Cancel, per-recipient notifications, and a final summary.
  • Localization

    • English help text updated to document /sendbatch usage and examples.
  • Chores

    • Bot command list updated for BotFather; .gitignore extended with additional ignore rules.

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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 2026

Warning

Rate limit exceeded

@xbuddhi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93b1f1c5-32bf-4090-bd71-38b5e6763d13

📥 Commits

Reviewing files that changed from the base of the PR and between 9ac024f and 04a0d1f.

📒 Files selected for processing (2)
  • internal/telegram/sendbatch.go
  • translations/en.toml
📝 Walkthrough

Walkthrough

Adds a new /sendbatch Telegram command and two inline callback handlers to create, confirm, or cancel multi-recipient tip batches: parses input, persists batch data, shows a confirmation UI, and executes or cancels sequential transfers with ownership checks, locking, and balance re-validation.

Changes

Cohort / File(s) Summary
Handler registration
internal/telegram/handler.go
Registered /sendbatch command handler and two inline-button callback handlers (btnConfirmSendBatch, btnCancelSendBatch) with appropriate interceptor chains (localization, requireUser, callback answering where applicable, locking/unlocking).
Batch-send implementation
internal/telegram/sendbatch.go
New end-to-end batch-send feature: input parsing (sats or lkr→sats), per-recipient/shared memo support, username resolution, sender wallet/balance checks, persisted SendBatchData keyed by BatchID, confirmation UI with Confirm/Cancel buttons, confirm handler executes transfers sequentially (stops on first failure) and notifies recipients, cancel handler inactivates batch.
Bot commands & translations
botfather-setcommands.txt, translations/en.toml
Added /sendbatch BotFather entry and help/usage text; minor whitespace/formatting fixes in translations and commands file.
Ignore rules
.gitignore
Added ignore entries for analytics-related files.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Dev #19: Adds similar Telegram handler registrations and callback wiring patterns in internal/telegram/handler.go.

Suggested labels

Review effort 4/5, Possible security concern

Poem

🐰 I found a list of tips and a plan to dispatch,
Lines of sats and memos all waiting to match.
I hopped to confirm, then nudged every send —
Hops, pings, and carrots until batch reached its end.
Hooray, tiny tails wag for every dispatch!

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add /sendbatch command for batch sends' clearly and concisely summarizes the main change—adding a new /sendbatch command for batch sending functionality, which aligns with all the modifications made across handler registration, command implementation, help text, and configuration files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sendbatch-command

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.

❤️ Share

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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 17ce82d and 52f64a4.

📒 Files selected for processing (2)
  • internal/telegram/handler.go
  • internal/telegram/sendbatch.go

*/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]`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This doesn't properly explain the command.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment on lines +75 to +78
*/sendbatch* 💸 Send to multiple users:
`/sendbatch [memo]`
`<amount> @user1 [memo]`
`<amount> @user2 [memo]`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Still doesn't explain the fact that lkr values can be used here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've updated the /sendbatch help in en.toml to explain that LKR values are converted to sats

xbuddhi added 2 commits March 25, 2026 23:59
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.
@xbuddhi
Copy link
Copy Markdown
Author

xbuddhi commented Mar 25, 2026

@helloscoopa Ready for re-review. CodeRabbit comments addressed (3 dismissed with reasoning, 1 fixed: memo now preserved on transaction records).

@xbuddhi xbuddhi requested a review from helloscoopa March 25, 2026 18:43
Copy link
Copy Markdown

@helloscoopa helloscoopa left a comment

Choose a reason for hiding this comment

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

LGTM

@helloscoopa helloscoopa merged commit 426ec11 into main Mar 25, 2026
4 checks passed
@helloscoopa helloscoopa deleted the feat/sendbatch-command branch March 25, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants