Skip to content

Comments

Add interactive wizard for /dispute (button-based order selection)#731

Open
Matobi98 wants to merge 10 commits intolnp2pBot:mainfrom
Matobi98:issue612
Open

Add interactive wizard for /dispute (button-based order selection)#731
Matobi98 wants to merge 10 commits intolnp2pBot:mainfrom
Matobi98:issue612

Conversation

@Matobi98
Copy link
Contributor

@Matobi98 Matobi98 commented Jan 30, 2026

Adds an interactive wizard for /dispute so users can pick their order via buttons instead of typing /dispute , reducing errors and friction.

Summary by CodeRabbit

  • New Features
    • Users can initiate disputes directly from order actions or by providing an order identifier; if none is provided an inline picker lists eligible orders to start the dispute flow.
  • Localization
    • Added an order-selection prompt across multiple locales (DE, EN, ES, FA, FR, IT, KO, PT, RU, UK). Italian and Ukrainian include expanded order-detail text for richer order views.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉


Walkthrough

Extracts dispute initiation logic into a new handler handleDispute, adds an action entrypoint initiateDispute and bot.action route, provides an order-listing UI helper listOrdersForDispute, and adds choose_order_for_dispute translations across multiple locale files.

Changes

Cohort / File(s) Summary
Dispute actions
bot/modules/dispute/actions.ts
Added initiateDispute(ctx) which reads orderId from the action payload, deletes the trigger message, and delegates to handleDispute.
Dispute commands
bot/modules/dispute/commands.ts
Added and exported handleDispute(ctx, orderId) that runs the dispute initiation flow; refactored dispute to dispatch between listing orders and initiating a dispute.
Action registration
bot/modules/dispute/index.ts
Registered new bot.action initiateDispute_([0-9a-f]{24}) mapped to actions.initiateDispute with existing user middleware.
Order listing UI
bot/modules/dispute/messages.ts
Added listOrdersForDispute(ctx, orders) to build an inline keyboard and send the localized choose_order_for_dispute prompt.
Localizations
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
Added choose_order_for_dispute key in each locale. en.yaml updated a guidance sentence; it.yaml added additional keys (sell_sats, buy_sats, order_detail). Some locale files contain duplicate insertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • crate a wizard to disputes #612 — introduces a UI-driven dispute entrypoint and order-selection flow, which aligns with the new order-listing and initiateDispute action added here.

Possibly related PRs

Suggested reviewers

  • grunch
  • Luquitasjeffrey
  • Catrya

Poem

🐰 I hop through handlers, nibble a thread,

Buttons of orders lined up ahead.
Tap to begin, a tiny dispute song,
Locales chirp, the keyboard clicks along.
🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an interactive wizard with button-based order selection for the /dispute command.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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
Contributor

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

🤖 Fix all issues with AI agents
In `@bot/modules/dispute/commands.ts`:
- Around line 116-122: In the conditional in the command handler, remove the
redundant "return" statements after the awaited calls—specifically eliminate the
trailing returns after messages.listOrdersForDispute(ctx, orders) and after
ctx.reply(ctx.i18n.t('you_have_no_orders'))—so the branch ends with the awaited
calls only; keep the if/else logic intact and ensure no other control flow needs
those returns.

In `@bot/modules/dispute/messages.ts`:
- Line 4: The file imports the symbol User but never uses it; remove User from
the import statement (the import line that currently reads "import { User } from
...") so the file no longer imports the unused symbol and run/verify linting to
ensure no remaining unused-import warnings.
🧹 Nitpick comments (3)
locales/it.yaml (1)

188-198: Consider translating remaining English strings in order_detail.

Several labels within order_detail remain in English:

  • Line 188: Days in operation \\(buyer\\)
  • Line 190: Successful trades \\(buyer\\)
  • Line 196: Days in operation \\(seller\\)
  • Line 198: Successful trades \\(seller\\)

For consistency with the rest of the Italian locale, these should be translated.

bot/modules/dispute/actions.ts (1)

58-64: LGTM with minor suggestion.

The implementation is clean and properly delegates to handleDispute. For consistency with takeDispute above (which has explicit Promise<void> return type), consider adding the same type annotation.

Suggested improvement
-export const initiateDispute = async (ctx: MainContext) => {
+export const initiateDispute = async (ctx: MainContext): Promise<void> => {
bot/modules/dispute/messages.ts (1)

8-32: Consider simplifying the button mapping.

The function works correctly, but Promise.all with async callback is unnecessary here since there are no async operations inside the map. This can be simplified:

Suggested refactor
 export const listOrdersForDispute = async (
   ctx: MainContext,
   orders: IOrder[],
 ) => {
   try {
-    const buttons = await Promise.all(
-      orders.map(async order => {
-        return [
-          {
-            text: `${order._id.toString().substring(0, 2)}..${order._id.toString().slice(-2)} - ${order.type} - ${order.fiat_code} ${order.fiat_amount}`,
-            callback_data: `initiateDispute_${order._id}`,
-          },
-        ];
-      }),
-    );
+    const buttons = orders.map(order => [
+      {
+        text: `${order._id.toString().substring(0, 2)}..${order._id.toString().slice(-2)} - ${order.type} - ${order.fiat_code} ${order.fiat_amount}`,
+        callback_data: `initiateDispute_${order._id}`,
+      },
+    ]);

     await ctx.reply(ctx.i18n.t('choose_order_for_dispute'), {

Copy link
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
bot/modules/dispute/commands.ts (1)

23-31: ⚠️ Potential issue | 🟠 Major

Validate DISPUTE_START_WINDOW to avoid bypassing the time gate.

parseInt can yield NaN (or a negative value), which makes the comparison against order.taken_at fail open and allows immediate disputes. Validate the parsed value before using it.

🔧 Suggested fix
-    const disputStartWindow = process.env.DISPUTE_START_WINDOW;
-    if (disputStartWindow === undefined)
+    const disputeStartWindow = process.env.DISPUTE_START_WINDOW;
+    if (disputeStartWindow === undefined)
       throw new Error('DISPUTE_START_WINDOW environment variable not defined');
-    const secsUntilDispute = parseInt(disputStartWindow);
+    const secsUntilDispute = Number.parseInt(disputeStartWindow, 10);
+    if (!Number.isFinite(secsUntilDispute) || secsUntilDispute < 0) {
+      throw new Error('DISPUTE_START_WINDOW must be a non-negative integer');
+    }
     const time = new Date();
     time.setSeconds(time.getSeconds() - secsUntilDispute);

Copy link
Contributor

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

🤖 Fix all issues with AI agents
In `@bot/modules/dispute/commands.ts`:
- Around line 97-100: The conditional in the dispute command uses
ctx.state.command.args.length and then checks ctx.state.command.args[0] !==
'undefined' which tests the literal string "undefined" (redundant since length>0
proves an element exists); either remove the "'undefined'" comparison and rely
on the length check, or if you actually intend to treat the user typing the word
"undefined" as invalid, add a clarifying comment above this condition explaining
that special-case; update the condition in the handler that reads
ctx.state.command.args (the if block containing args.length and args[0] !==
'undefined') accordingly.
🧹 Nitpick comments (4)
bot/modules/dispute/commands.ts (4)

54-61: Use countDocuments() instead of deprecated count().

Mongoose 6.x deprecated Model.count() in favor of countDocuments(). Since this project uses mongoose 6.13.6, update these calls to avoid deprecation warnings.

Proposed fix
       const buyerDisputes =
-        (await Dispute.count({
+        (await Dispute.countDocuments({
           $or: [{ buyer_id: buyer._id }, { seller_id: buyer._id }],
         })) + 1;
       const sellerDisputes =
-        (await Dispute.count({
+        (await Dispute.countDocuments({
           $or: [{ buyer_id: seller._id }, { seller_id: seller._id }],
         })) + 1;

38-38: Use strict equality or .equals() for ObjectId comparison.

Comparing Mongoose ObjectIds with == may produce unexpected results. Prefer === with .toString() or use the .equals() method for reliable comparison.

Proposed fix
-    if (user._id == order.buyer_id) initiator = 'buyer';
+    if (user._id.equals(order.buyer_id)) initiator = 'buyer';

23-23: Minor typo in variable name.

disputStartWindow is missing an 'e' — should be disputeStartWindow for clarity.

Proposed fix
-    const disputStartWindow = process.env.DISPUTE_START_WINDOW;
-    if (disputStartWindow === undefined)
+    const disputeStartWindow = process.env.DISPUTE_START_WINDOW;
+    if (disputeStartWindow === undefined)
       throw new Error('DISPUTE_START_WINDOW environment variable not defined');
-    const secsUntilDispute = parseInt(disputStartWindow);
+    const secsUntilDispute = parseInt(disputeStartWindow);

108-114: Inconsistent ObjectId query pattern — consider removing .toString().

The query uses user._id.toString() for comparisons, but throughout the codebase (bot/validations.ts, bot/start.ts, bot/ordersActions.ts, and bot/modules/orders/takeOrder.ts) all other seller_id and buyer_id queries use user._id directly without conversion. For consistency, remove the explicit string conversion.

Suggested change
     const orders = await Order.find({
       $or: [
-        { seller_id: user._id.toString() },
-        { buyer_id: user._id.toString() },
+        { seller_id: user._id },
+        { buyer_id: user._id },
       ],
       status: { $in: ['ACTIVE', 'FIAT_SENT'] },
     });

Copy link
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
bot/modules/dispute/commands.ts (1)

54-61: ⚠️ Potential issue | 🟡 Minor

Replace deprecated Model.count() with countDocuments().

Mongoose 6.x deprecated Model.count() in favor of countDocuments(). Since both usages here include filters, use countDocuments() to avoid deprecation warnings.

Proposed fix
       const buyerDisputes =
-        (await Dispute.count({
+        (await Dispute.countDocuments({
           $or: [{ buyer_id: buyer._id }, { seller_id: buyer._id }],
         })) + 1;
       const sellerDisputes =
-        (await Dispute.count({
+        (await Dispute.countDocuments({
           $or: [{ buyer_id: seller._id }, { seller_id: seller._id }],
         })) + 1;
🧹 Nitpick comments (1)
bot/modules/dispute/commands.ts (1)

23-26: Typo in variable name: disputStartWindowdisputeStartWindow.

Minor typo - missing 'e' in the variable name.

Proposed fix
-    const disputStartWindow = process.env.DISPUTE_START_WINDOW;
-    if (disputStartWindow === undefined)
+    const disputeStartWindow = process.env.DISPUTE_START_WINDOW;
+    if (disputeStartWindow === undefined)
       throw new Error('DISPUTE_START_WINDOW environment variable not defined');
-    const secsUntilDispute = parseInt(disputStartWindow);
+    const secsUntilDispute = parseInt(disputeStartWindow);

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.

3 participants