Skip to content

feat: add order-take rate limit to prevent spam#828

Open
Matobi98 wants to merge 4 commits into
lnp2pBot:mainfrom
Matobi98:feat/order-take-rate-limit
Open

feat: add order-take rate limit to prevent spam#828
Matobi98 wants to merge 4 commits into
lnp2pBot:mainfrom
Matobi98:feat/order-take-rate-limit

Conversation

@Matobi98

@Matobi98 Matobi98 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Add a rate limit on order taking to prevent spam: users who exceed MAX_ORDERS_TAKE consecutive takes without completing an order are temporarily blocked until the cooldown expires or they complete a trade.

Summary

  • Add MAX_ORDERS_TAKE and ORDER_TAKE_COOLDOWN_HOURS env vars to control the rate limit
  • Track take_order_count and take_order_cooldown_until on the User model
  • Block users from taking new orders once the counter hits the limit, notifying them with the remaining cooldown time
  • Reset the counter and cancel the cooldown when the user completes any order (as maker or taker)

Changes

  • models/user.ts — new fields: take_order_count, take_order_cooldown_until
  • bot/modules/orders/takeOrder.ts — rate limit check in takebuy and takesell
  • util/index.ts — reset counter on order completion (SUCCESS)
  • bot/messages.ts — new orderTakeRateLimitMessage with remaining hours
  • locales/order_take_rate_limit key added in all 10 languages
  • .env-sample — documented new env vars with defaults (MAX_ORDERS_TAKE=10, ORDER_TAKE_COOLDOWN_HOURS=24)

Summary by CodeRabbit

  • New Features

    • Order-taking is rate-limited: configurable max accepted orders and cooldown duration.
    • Users receive localized notifications showing remaining cooldown hours when the limit is reached.
    • Counters and cooldowns reset automatically when cooldown expires or upon completing active orders.
  • Chores

    • Added rate-limit entries to environment sample.
    • Added/updated localized messages across supported languages.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 02e57d18-a56b-4e52-8ad7-37c160226bb4

📥 Commits

Reviewing files that changed from the base of the PR and between bcb433d and a2c0d91.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • bot/modules/orders/takeOrder.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • bot/modules/orders/takeOrder.ts

Walkthrough

This PR introduces per-user order-take rate limiting by tracking take counts and enforcing configurable cooldowns. It adds database fields for state tracking, environment variables for thresholds, enforcement logic in order handlers, localized user messaging, and state reset on order completion across ten languages.

Changes

Order-take rate limiting

Layer / File(s) Summary
User model and rate-limit configuration
models/user.ts, .env-sample
UserDocument interface and Mongoose schema extend to persist take_order_count (min 0, default 0) and take_order_cooldown_until (nullable). Environment variables MAX_ORDERS_TAKE and ORDER_TAKE_COOLDOWN_HOURS define the rate-limit thresholds.
Rate limit enforcement in order handlers and user notification
bot/modules/orders/takeOrder.ts, bot/messages.ts
checkTakeRateLimit helper verifies the user is not in an active cooldown period before allowing order takes in both takebuy and takesell handlers; incrementTakeOrderCount increments the take counter and sets a cooldown timestamp when the configured maximum is reached; orderTakeRateLimitMessage sends a localized Telegram notification with remaining cooldown hours when enforcement triggers.
Localized rate-limit messages
locales/en.yaml, locales/de.yaml, locales/es.yaml, locales/fa.yaml, locales/fr.yaml, locales/it.yaml, locales/ko.yaml, locales/pt.yaml, locales/ru.yaml, locales/uk.yaml
The order_take_rate_limit translation key with ${hours} placeholder is added across all ten supported languages to notify users of the rate limit.
Rate-limit state reset on order completion
util/index.ts
When handleReputationItems finds no matching recent circular orders, both buyer and seller take_order_count and take_order_cooldown_until are reset to clear the rate-limit state.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

  • lnp2pBot/bot#824: Concurrent PR that wraps takebuy/takesell with PerOrderIdMutex to serialize per-order execution, while this PR adds per-user cooldown/rate-limiting orthogonally.

Suggested reviewers

  • Luquitasjeffrey
  • mostronatorcoder

Poem

🐰 A flurry of takes, a counter does grow,
When limits are hit, a cooldown's in tow,
Hours tick away, locales sing the chime,
Now polite bot reminders save everyone time.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: adding a rate limit mechanism for order-taking to prevent spam, which is the core objective across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
bot/modules/orders/takeOrder.ts (2)

86-97: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Persisting the user counter before order save can over-penalize users.

At Line 86/147, incrementTakeOrderCount(user) is saved before Line 96/153 order.save(). If order persistence fails, the user still gets count/cooldown updates for a take that never completed. This should be one atomic unit (or at least update the counter only after order save succeeds).

Also applies to: 147-153, 194-195

🤖 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 `@bot/modules/orders/takeOrder.ts` around lines 86 - 97, The
incrementTakeOrderCount(user) call is executed before persisting the order,
which will penalize the user if order.save() fails; move the
incrementTakeOrderCount(user) to after a successful await order.save() (or wrap
both operations in a DB transaction if supported) so the counter/cooldown is
only updated once order.status, order.seller_id, taken_at and order.random_image
have been persisted; adjust the same pattern wherever incrementTakeOrderCount
appears (lines referencing incrementTakeOrderCount, order.save,
generateRandomImage, and order.status) to ensure counter updates occur post-save
or inside the same atomic transaction.

62-63: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Per-order mutex does not protect per-user counter updates.

At Line 62 and Line 114, locking is keyed by orderId, so the same user can take different orders concurrently and race on take_order_count via Line 194 user.save(). Use an atomic DB update ($inc + conditional cooldown update) or add a per-user lock to avoid lost updates.

Also applies to: 114-115, 184-195

🤖 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 `@bot/modules/orders/takeOrder.ts` around lines 62 - 63, The per-order mutex
(PerOrderIdMutex.instance.runExclusive keyed by orderId) does not prevent
concurrent updates to the same user's take_order_count because different orders
for the same user can run concurrently; replace or augment this by performing an
atomic database update (use a single update with $inc on take_order_count plus
conditional update of the cooldown fields via a conditional operator/aggregation
pipeline) or acquire a per-user lock (e.g., PerUserIdMutex.instance.runExclusive
keyed by user.id) around the critical section that reads/modifies user and calls
user.save(); locate uses of PerOrderIdMutex.instance.runExclusive, the
user.save() call and the take_order_count update and change them to either an
atomic DB operation or wrap the user mutation in a per-user mutex to prevent
lost updates.
🤖 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 `@bot/modules/orders/takeOrder.ts`:
- Around line 170-182: The cooldown check is leaving stale state in
user.take_order_count when user.take_order_cooldown_until is in the past; update
the logic in the function in takeOrder.ts that checks
user.take_order_cooldown_until to first detect an expired timestamp and
reset/clear user.take_order_count and user.take_order_cooldown_until before
proceeding, then persist/update the user as needed; continue to call
messages.orderTakeRateLimitMessage(ctx, user, user.take_order_cooldown_until)
only when the cooldown is still in the future. Also apply the same
expired-timestamp reset to the similar block referenced around the 188–193
region.
- Around line 185-186: The env parsing for rate-limit config (maxOrdersTake and
cooldownHours in takeOrder.ts) can yield NaN or non-positive values and disable
cooldown logic; after parsing parseInt(process.env.MAX_ORDERS_TAKE || '10') and
parseInt(process.env.ORDER_TAKE_COOLDOWN_HOURS || '24'), validate each parsed
value (maxOrdersTake and cooldownHours) with Number.isFinite/Number.isInteger
and ensure they are > 0, otherwise reset to safe defaults (e.g., 10 for
maxOrdersTake and 24 for cooldownHours) and optionally clamp to sensible bounds;
update any usages that rely on these variables (e.g., rate-limit checks like
count >= cooldownHours) to use the validated values.

---

Outside diff comments:
In `@bot/modules/orders/takeOrder.ts`:
- Around line 86-97: The incrementTakeOrderCount(user) call is executed before
persisting the order, which will penalize the user if order.save() fails; move
the incrementTakeOrderCount(user) to after a successful await order.save() (or
wrap both operations in a DB transaction if supported) so the counter/cooldown
is only updated once order.status, order.seller_id, taken_at and
order.random_image have been persisted; adjust the same pattern wherever
incrementTakeOrderCount appears (lines referencing incrementTakeOrderCount,
order.save, generateRandomImage, and order.status) to ensure counter updates
occur post-save or inside the same atomic transaction.
- Around line 62-63: The per-order mutex (PerOrderIdMutex.instance.runExclusive
keyed by orderId) does not prevent concurrent updates to the same user's
take_order_count because different orders for the same user can run
concurrently; replace or augment this by performing an atomic database update
(use a single update with $inc on take_order_count plus conditional update of
the cooldown fields via a conditional operator/aggregation pipeline) or acquire
a per-user lock (e.g., PerUserIdMutex.instance.runExclusive keyed by user.id)
around the critical section that reads/modifies user and calls user.save();
locate uses of PerOrderIdMutex.instance.runExclusive, the user.save() call and
the take_order_count update and change them to either an atomic DB operation or
wrap the user mutation in a per-user mutex to prevent lost updates.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 870f3837-70a7-4381-a84d-d62b7257d716

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2beea and bcb433d.

📒 Files selected for processing (15)
  • .env-sample
  • bot/messages.ts
  • bot/modules/orders/takeOrder.ts
  • locales/de.yaml
  • locales/en.yaml
  • locales/es.yaml
  • locales/fa.yaml
  • locales/fr.yaml
  • locales/it.yaml
  • locales/ko.yaml
  • locales/pt.yaml
  • locales/ru.yaml
  • locales/uk.yaml
  • models/user.ts
  • util/index.ts

Comment thread bot/modules/orders/takeOrder.ts Outdated
Comment thread bot/modules/orders/takeOrder.ts Outdated
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.

1 participant