Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block exchanges between user A to B #630

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions bot/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,28 @@ const bannedUserErrorMessage = async (ctx: MainContext, user: UserDocument) => {
}
};

const userOrderIsBlockedByUserTaker = async (ctx: MainContext, user: UserDocument) => {
try {
await ctx.telegram.sendMessage(
user.tg_id,
ctx.i18n.t('user_order_is_blocked_by_user_taker')
);
} catch (error) {
logger.error(error);
}
};

const userTakerIsBlockedByUserOrder = async (ctx: MainContext, user: UserDocument) => {
try {
await ctx.telegram.sendMessage(
user.tg_id,
ctx.i18n.t('user_taker_is_blocked_by_user_order')
);
} catch (error) {
logger.error(error);
}
};

const fiatSentMessages = async (ctx: MainContext, buyer: UserDocument, seller: UserDocument, i18nBuyer: I18nContext, i18nSeller: I18nContext) => {
try {
await ctx.telegram.sendMessage(
Expand Down Expand Up @@ -1734,4 +1756,6 @@ export {
showConfirmationButtons,
counterPartyCancelOrderMessage,
checkInvoiceMessage,
userTakerIsBlockedByUserOrder,
userOrderIsBlockedByUserTaker
};
83 changes: 83 additions & 0 deletions bot/modules/block/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { CustomContext } from './customContext';

const { User, Block, Order } = require('../../../models');
const messages = require('./messages');
const globalMessages = require('../../messages');

const block = async (ctx: CustomContext, username: string): Promise<void> => {
const userToBlock = await User.findOne({ username });
const user = ctx.user;

if (!userToBlock) {
await globalMessages.notFoundUserMessage(ctx);
return;
}

const areExistingOrders = await Order.exists({
$or: [
{ seller_id: user.id, buyer_id: userToBlock.id },
{ seller_id: userToBlock.id, buyer_id: user.id },
],
status: {
$nin: [
'PENDING',
'CLOSED',
'CANCELED_BY_ADMIN',
'EXPIRED',
'COMPLETED_BY_ADMIN',
],
},
});

if (areExistingOrders) {
await messages.ordersInProcess(ctx);
return;
}

const isAlreadyBlocked = await Block.exists({
blocker_tg_id: user.tg_id,
blocked_tg_id: userToBlock.tg_id,
});
if (isAlreadyBlocked) {
await messages.userAlreadyBlocked(ctx);
return;
}

const block = new Block({
blocker_tg_id: user.tg_id,
blocked_tg_id: userToBlock.tg_id,
});
await block.save();
await messages.userBlocked(ctx);
};
Comment on lines +7 to +52
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and improve error handling.

The function needs improvements in several areas:

  1. Input validation for username format and self-blocking
  2. Database operations should use transactions
  3. Error handling for database operations

Apply this diff to improve the function:

 const block = async (ctx: CustomContext, username: string): Promise<void> => {
+  if (!username?.match(/^[a-zA-Z0-9_]{5,32}$/)) {
+    await messages.invalidUsernameMessage(ctx);
+    return;
+  }
+
+  if (username === ctx.user.username) {
+    await messages.cannotBlockSelfMessage(ctx);
+    return;
+  }
+
   const userToBlock = await User.findOne({ username });
   const user = ctx.user;

   if (!userToBlock) {
     await globalMessages.notFoundUserMessage(ctx);
     return;
   }

+  const session = await mongoose.startSession();
+  try {
+    await session.withTransaction(async () => {
       const areExistingOrders = await Order.exists({
         $or: [
           { seller_id: user.id, buyer_id: userToBlock.id },
           { seller_id: userToBlock.id, buyer_id: user.id },
         ],
         status: {
           $nin: [
             'PENDING',
             'CLOSED',
             'CANCELED_BY_ADMIN',
             'EXPIRED',
             'COMPLETED_BY_ADMIN',
           ],
         },
       });

       if (areExistingOrders) {
         await messages.ordersInProcess(ctx);
         return;
       }

       const isAlreadyBlocked = await Block.exists({
         blocker_tg_id: user.tg_id,
         blocked_tg_id: userToBlock.tg_id,
       });
       if (isAlreadyBlocked) {
         await messages.userAlreadyBlocked(ctx);
         return;
       }

       const block = new Block({
         blocker_tg_id: user.tg_id,
         blocked_tg_id: userToBlock.tg_id,
       });
       await block.save({ session });
       await messages.userBlocked(ctx);
+    });
+  } catch (error) {
+    logger.error(error);
+    await messages.databaseErrorMessage(ctx);
+  } finally {
+    session.endSession();
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.


const unblock = async (ctx: CustomContext, username: string): Promise<void> => {
const userToUnblock = await User.findOne({ username });
const user = ctx.user;

const result = await Block.deleteOne({
blocker_tg_id: user.tg_id,
blocked_tg_id: userToUnblock.tg_id,
});

if (result.deletedCount === 1) {
await messages.userUnblocked(ctx);
} else {
await globalMessages.notFoundUserMessage(ctx);
}
};
Comment on lines +54 to +68
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and improve error handling.

The function needs improvements in several areas:

  1. Input validation for username format
  2. Database operations should use transactions
  3. Error handling for database operations
  4. Consistent error messages

Apply this diff to improve the function:

 const unblock = async (ctx: CustomContext, username: string): Promise<void> => {
+  if (!username?.match(/^[a-zA-Z0-9_]{5,32}$/)) {
+    await messages.invalidUsernameMessage(ctx);
+    return;
+  }
+
   const userToUnblock = await User.findOne({ username });
+  if (!userToUnblock) {
+    await globalMessages.notFoundUserMessage(ctx);
+    return;
+  }
+
   const user = ctx.user;

+  const session = await mongoose.startSession();
+  try {
+    await session.withTransaction(async () => {
       const result = await Block.deleteOne({
         blocker_tg_id: user.tg_id,
         blocked_tg_id: userToUnblock.tg_id,
-      });
+      }).session(session);

       if (result.deletedCount === 1) {
         await messages.userUnblocked(ctx);
       } else {
-        await globalMessages.notFoundUserMessage(ctx);
+        await messages.userNotBlockedMessage(ctx);
       }
+    });
+  } catch (error) {
+    logger.error(error);
+    await messages.databaseErrorMessage(ctx);
+  } finally {
+    session.endSession();
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.


const blocklist = async (ctx: CustomContext): Promise<void> => {
const blocks = await Block.find({ blocker_tg_id: ctx.user.tg_id });
const tgIdBlocks = blocks.map(blocked => blocked.blocked_tg_id);

if (!tgIdBlocks.length) {
await messages.blocklistEmptyMessage(ctx);
return;
}

const usersBlocked = await User.find({ tg_id: { $in: tgIdBlocks } });
await messages.blocklistMessage(ctx, usersBlocked);
};
Comment on lines +70 to +81
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add pagination and improve error handling.

The function needs improvements in several areas:

  1. Database operations should use transactions
  2. Add pagination for large lists
  3. Error handling for database operations

Apply this diff to improve the function:

-const blocklist = async (ctx: CustomContext): Promise<void> => {
+const blocklist = async (ctx: CustomContext, page: number = 1): Promise<void> => {
+  const PAGE_SIZE = 50;
+  const skip = (page - 1) * PAGE_SIZE;
+
+  const session = await mongoose.startSession();
+  try {
+    await session.withTransaction(async () => {
+      const totalBlocks = await Block.countDocuments({ blocker_tg_id: ctx.user.tg_id });
+      const totalPages = Math.ceil(totalBlocks / PAGE_SIZE);
+
+      if (totalBlocks === 0) {
+        await messages.blocklistEmptyMessage(ctx);
+        return;
+      }
+
       const blocks = await Block.find({ blocker_tg_id: ctx.user.tg_id })
+        .skip(skip)
+        .limit(PAGE_SIZE)
+        .session(session);
+
       const tgIdBlocks = blocks.map(blocked => blocked.blocked_tg_id);
-
-      if (!tgIdBlocks.length) {
-        await messages.blocklistEmptyMessage(ctx);
-        return;
-      }
-
-      const usersBlocked = await User.find({ tg_id: { $in: tgIdBlocks } });
+      const usersBlocked = await User.find({ tg_id: { $in: tgIdBlocks } })
+        .session(session);
+
       await messages.blocklistMessage(ctx, usersBlocked);
+
+      if (totalPages > 1) {
+        await messages.paginationMessage(ctx, page, totalPages);
+      }
+    });
+  } catch (error) {
+    logger.error(error);
+    await messages.databaseErrorMessage(ctx);
+  } finally {
+    session.endSession();
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.


module.exports = { block, unblock, blocklist };
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update exports to use ES6 syntax.

The module uses TypeScript but still uses CommonJS exports.

Apply this diff to improve the exports:

-module.exports = { block, unblock, blocklist };
+export { block, unblock, blocklist };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
module.exports = { block, unblock, blocklist };
export { block, unblock, blocklist };

9 changes: 9 additions & 0 deletions bot/modules/block/customContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Context } from 'telegraf';

export interface CustomContext extends Context {
user?: {
id: string;
tg_id: string;
},
i18n?: any;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace any type with a more specific type for i18n.

Using any type reduces type safety. Consider defining a proper interface for the i18n object to catch potential type-related issues at compile time.

-  i18n?: any;
+  i18n?: {
+    t: (key: string, params?: Record<string, unknown>) => string;
+  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
i18n?: any;
i18n?: {
t: (key: string, params?: Record<string, unknown>) => string;
};

}
19 changes: 19 additions & 0 deletions bot/modules/block/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Telegraf } from 'telegraf';

const commands = require('./commands');
const { userMiddleware } = require('../../middleware/user');

exports.configure = (bot: Telegraf) => {
bot.command('block', userMiddleware, async (ctx, next) => {
const args = ctx.message.text.split(' ') || [];
if (args.length !== 2) return next();
commands.block(ctx, args[1]);
});
Comment on lines +7 to +11
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and input validation.

The command handler should:

  1. Include try-catch for error handling
  2. Validate username format
  3. Add rate limiting to prevent abuse
-  bot.command('block', userMiddleware, async (ctx, next) => {
+  bot.command('block', userMiddleware, rateLimiter, async (ctx: CustomContext, next) => {
     const args = ctx.message.text.split(' ') || [];
     if (args.length !== 2) return next();
-    commands.block(ctx, args[1]);
+    const username = args[1];
+    if (!username.match(/^@?[\w\d]{5,32}$/)) {
+      await ctx.reply(ctx.i18n?.t('invalid_username_format') ?? 'Invalid username format');
+      return;
+    }
+    try {
+      await commands.block(ctx, username);
+    } catch (error) {
+      console.error('Error in block command:', error);
+      await ctx.reply(ctx.i18n?.t('block_error') ?? 'Failed to block user');
+    }
   });

Committable suggestion skipped: line range outside the PR's diff.


bot.command('unblock', userMiddleware, async (ctx, next) => {
const args = ctx.message.text.split(' ') || [];
if (args.length !== 2) return next();
commands.unblock(ctx, args[1]);
});
Comment on lines +13 to +17
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and input validation to unblock command.

Apply the same improvements as the block command.

-  bot.command('unblock', userMiddleware, async (ctx, next) => {
+  bot.command('unblock', userMiddleware, rateLimiter, async (ctx: CustomContext, next) => {
     const args = ctx.message.text.split(' ') || [];
     if (args.length !== 2) return next();
-    commands.unblock(ctx, args[1]);
+    const username = args[1];
+    if (!username.match(/^@?[\w\d]{5,32}$/)) {
+      await ctx.reply(ctx.i18n?.t('invalid_username_format') ?? 'Invalid username format');
+      return;
+    }
+    try {
+      await commands.unblock(ctx, username);
+    } catch (error) {
+      console.error('Error in unblock command:', error);
+      await ctx.reply(ctx.i18n?.t('unblock_error') ?? 'Failed to unblock user');
+    }
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bot.command('unblock', userMiddleware, async (ctx, next) => {
const args = ctx.message.text.split(' ') || [];
if (args.length !== 2) return next();
commands.unblock(ctx, args[1]);
});
bot.command('unblock', userMiddleware, rateLimiter, async (ctx: CustomContext, next) => {
const args = ctx.message.text.split(' ') || [];
if (args.length !== 2) return next();
const username = args[1];
if (!username.match(/^@?[\w\d]{5,32}$/)) {
await ctx.reply(ctx.i18n?.t('invalid_username_format') ?? 'Invalid username format');
return;
}
try {
await commands.unblock(ctx, username);
} catch (error) {
console.error('Error in unblock command:', error);
await ctx.reply(ctx.i18n?.t('unblock_error') ?? 'Failed to unblock user');
}
});

bot.command('blocklist', userMiddleware, commands.blocklist);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to blocklist command.

Wrap the command execution in try-catch.

-  bot.command('blocklist', userMiddleware, commands.blocklist);
+  bot.command('blocklist', userMiddleware, rateLimiter, async (ctx: CustomContext) => {
+    try {
+      await commands.blocklist(ctx);
+    } catch (error) {
+      console.error('Error in blocklist command:', error);
+      await ctx.reply(ctx.i18n?.t('blocklist_error') ?? 'Failed to fetch block list');
+    }
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bot.command('blocklist', userMiddleware, commands.blocklist);
bot.command('blocklist', userMiddleware, rateLimiter, async (ctx: CustomContext) => {
try {
await commands.blocklist(ctx);
} catch (error) {
console.error('Error in blocklist command:', error);
await ctx.reply(ctx.i18n?.t('blocklist_error') ?? 'Failed to fetch block list');
}
});

};
55 changes: 55 additions & 0 deletions bot/modules/block/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { CustomContext } from './customContext';

const { logger } = require('../../../logger');


const ordersInProcess = async (ctx: CustomContext) => {
try {
ctx.reply(ctx.i18n.t('orders_in_process'));
} catch (error) {
logger.error(error);
}
};

const userAlreadyBlocked = async (ctx: CustomContext) => {
try {
ctx.reply(ctx.i18n.t('user_already_blocked'));
} catch (error) {
logger.error(error);
}
};

const userBlocked = async (ctx: CustomContext) => {
try {
ctx.reply(ctx.i18n.t('user_blocked'));
} catch (error) {
logger.error(error);
}
};

const userUnblocked = async (ctx: CustomContext) => {
try {
ctx.reply(ctx.i18n.t('user_unblocked'));
} catch (error) {
logger.error(error);
}
};

const blocklistMessage = async (ctx: CustomContext, usersBlocked) => {
try {
const userList = usersBlocked.map(block => block.username);
ctx.reply(userList.join('\n'));
} catch (error) {
logger.error(error);
}
};
Comment on lines +38 to +45
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle edge cases in the blocklist message.

The function needs improvements to handle:

  1. Empty or null input array
  2. Long lists that might exceed message length limits
  3. Missing usernames in the blocked users list

Apply this diff to improve edge case handling:

 const blocklistMessage = async (ctx: CustomContext, usersBlocked) => {
   try {
+    if (!usersBlocked?.length) {
+      return await messages.blocklistEmptyMessage(ctx);
+    }
+
     const userList = usersBlocked.map(block => block.username);
+    const validUserList = userList.filter(Boolean);
+
+    if (!validUserList.length) {
+      return await messages.blocklistEmptyMessage(ctx);
+    }
+
+    // Split long lists into chunks of 50 usernames
+    const chunkSize = 50;
+    for (let i = 0; i < validUserList.length; i += chunkSize) {
+      const chunk = validUserList.slice(i, i + chunkSize);
       ctx.reply(userList.join('\n'));
+    }
   } catch (error) {
     logger.error(error);
+    await ctx.reply(ctx.i18n.t('error_sending_message')).catch(() => {});
   }
 };

Committable suggestion skipped: line range outside the PR's diff.


const blocklistEmptyMessage = async (ctx: CustomContext) => {
try {
ctx.reply(ctx.i18n.t('blocklist_empty'));
} catch (error) {
logger.error(error);
}
};

module.exports = { userAlreadyBlocked, userBlocked, userUnblocked, blocklistMessage, blocklistEmptyMessage, ordersInProcess }
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update exports to use ES6 syntax and include missing function.

  1. The module uses TypeScript but still uses CommonJS exports.
  2. The ordersInProcess function is missing from the exports.

Apply this diff to improve the exports:

-module.exports = { userAlreadyBlocked, userBlocked, userUnblocked, blocklistMessage, blocklistEmptyMessage, ordersInProcess }
+export {
+  userAlreadyBlocked,
+  userBlocked,
+  userUnblocked,
+  blocklistMessage,
+  blocklistEmptyMessage,
+  ordersInProcess
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
module.exports = { userAlreadyBlocked, userBlocked, userUnblocked, blocklistMessage, blocklistEmptyMessage, ordersInProcess }
export {
userAlreadyBlocked,
userBlocked,
userUnblocked,
blocklistMessage,
blocklistEmptyMessage,
ordersInProcess
};

25 changes: 24 additions & 1 deletion bot/modules/orders/takeOrder.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @ts-check
const { logger } = require('../../../logger');
const { Order } = require('../../../models');
const { Order, Block, User } = require('../../../models');
const { deleteOrderFromChannel } = require('../../../util');
const messages = require('../../messages');
const {
Expand Down Expand Up @@ -48,6 +48,18 @@ exports.takebuy = async (ctx, bot, orderId) => {
if (!(await validateObjectId(ctx, orderId))) return;
const order = await Order.findOne({ _id: orderId });
if (!order) return;

const userOffer = await User.findOne({_id: order.buyer_id});

const userOfferIsBlocked = await Block.exists({ blocker_tg_id: user.tg_id, blocked_tg_id: userOffer.tg_id });
const takerIsBlocked = await Block.exists({blocker_tg_id: userOffer.tg_id, blocked_tg_id: user.tg_id});

if (userOfferIsBlocked)
return await messages.userOrderIsBlockedByUserTaker(ctx, user);

if (takerIsBlocked)
return await messages.userTakerIsBlockedByUserOrder(ctx, user);

Comment on lines +51 to +62
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extract blocking checks into a reusable function.

The blocking checks are duplicated in both takebuy and takesell functions. Extract them into a reusable function to improve maintainability.

Create a new function to handle blocking checks:

+const checkBlockingStatus = async (ctx, user, otherUser) => {
+  const userIsBlocked = await Block.exists({ blocker_tg_id: user.tg_id, blocked_tg_id: otherUser.tg_id });
+  if (userIsBlocked) {
+    await messages.userOrderIsBlockedByUserTaker(ctx, user);
+    return true;
+  }
+
+  const takerIsBlocked = await Block.exists({ blocker_tg_id: otherUser.tg_id, blocked_tg_id: user.tg_id });
+  if (takerIsBlocked) {
+    await messages.userTakerIsBlockedByUserOrder(ctx, user);
+    return true;
+  }
+
+  return false;
+};

Then update the takebuy function:

     const userOffer = await User.findOne({_id: order.buyer_id});

-    const userOfferIsBlocked = await Block.exists({ blocker_tg_id: user.tg_id, blocked_tg_id: userOffer.tg_id });
-    const takerIsBlocked = await Block.exists({blocker_tg_id: userOffer.tg_id, blocked_tg_id: user.tg_id});
-
-    if (userOfferIsBlocked)
-      return await messages.userOrderIsBlockedByUserTaker(ctx, user);
-
-    if (takerIsBlocked)
-      return await messages.userTakerIsBlockedByUserOrder(ctx, user);
+    if (await checkBlockingStatus(ctx, user, userOffer)) {
+      return;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const userOffer = await User.findOne({_id: order.buyer_id});
const userOfferIsBlocked = await Block.exists({ blocker_tg_id: user.tg_id, blocked_tg_id: userOffer.tg_id });
const takerIsBlocked = await Block.exists({blocker_tg_id: userOffer.tg_id, blocked_tg_id: user.tg_id});
if (userOfferIsBlocked)
return await messages.userOrderIsBlockedByUserTaker(ctx, user);
if (takerIsBlocked)
return await messages.userTakerIsBlockedByUserOrder(ctx, user);
const userOffer = await User.findOne({_id: order.buyer_id});
if (await checkBlockingStatus(ctx, user, userOffer)) {
return;
}

// We verify if the user is not banned on this community
if (await isBannedFromCommunity(user, order.community_id))
return await messages.bannedUserErrorMessage(ctx, user);
Expand All @@ -74,6 +86,17 @@ exports.takesell = async (ctx, bot, orderId) => {
if (!orderId) return;
const order = await Order.findOne({ _id: orderId });
if (!order) return;
const seller = await User.findOne({_id: order.seller_id});

const sellerIsBlocked = await Block.exists({ blocker_tg_id: user.tg_id, blocked_tg_id: seller.tg_id });
const buyerIsBlocked = await Block.exists({blocker_tg_id: seller.tg_id, blocked_tg_id: user.tg_id});

if (sellerIsBlocked)
return await messages.userOrderIsBlockedByUserTaker(ctx, user);

if (buyerIsBlocked)
return await messages.userTakerIsBlockedByUserOrder(ctx, user);

// We verify if the user is not banned on this community
if (await isBannedFromCommunity(user, order.community_id))
return await messages.bannedUserErrorMessage(ctx, user);
Expand Down
2 changes: 2 additions & 0 deletions bot/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const NostrModule = require('./modules/nostr');
const OrdersModule = require('./modules/orders');
const UserModule = require('./modules/user');
const DisputeModule = require('./modules/dispute');
const BlockModule = require('./modules/block');
const {
rateUser,
cancelAddInvoice,
Expand Down Expand Up @@ -257,6 +258,7 @@ const initialize = (botToken: string, options: Partial<Telegraf.Options<MainCont
NostrModule.configure(bot);
CommunityModule.configure(bot);
LanguageModule.configure(bot);
BlockModule.configure(bot);

bot.command('release', userMiddleware, async ctx => {
try {
Expand Down
7 changes: 7 additions & 0 deletions locales/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,10 @@ privacy: |
*2. Wie wir die Informationen verwenden:*
- _Reputationssystem:_ Um das Reputationssystem für jeden Benutzer aufzubauen und zu pflegen.
- _Streitbeilegung:_ Im Falle eines Streits stellen wir dem Mediator (Löser) die folgenden Informationen zur Verfügung: Ihren Benutzernamen, Ihre Telegram-ID, die Anzahl der abgeschlossenen Transaktionen, die Bewertung des Gegenübers, die Anzahl der Tage, an denen Sie den Bot verwendet haben, und die Anzahl der angesammelten Streitfälle.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
Comment on lines +637 to +643
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Translate the new messages to German to maintain consistency.

The new messages for user blocking functionality are in English while this file is for German translations.

Apply this diff to add German translations:

-user_already_blocked: User is already blocked
-user_blocked: User successfully blocked
-user_unblocked: User successfully unblocked
-blocklist_empty: You do not have any blocked user
-orders_in_process: There are orders in process with this user
-user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
-user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
+user_already_blocked: Benutzer ist bereits gesperrt
+user_blocked: Benutzer erfolgreich gesperrt
+user_unblocked: Benutzer erfolgreich entsperrt
+blocklist_empty: Du hast keine gesperrten Benutzer
+orders_in_process: Es gibt laufende Aufträge mit diesem Benutzer
+user_order_is_blocked_by_user_taker: Du kannst diesen Auftrag nicht annehmen, da du seinen Ersteller gesperrt hast
+user_taker_is_blocked_by_user_order: Du kannst diesen Auftrag nicht annehmen, da dich sein Ersteller gesperrt hat
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
user_already_blocked: Benutzer ist bereits gesperrt
user_blocked: Benutzer erfolgreich gesperrt
user_unblocked: Benutzer erfolgreich entsperrt
blocklist_empty: Du hast keine gesperrten Benutzer
orders_in_process: Es gibt laufende Aufträge mit diesem Benutzer
user_order_is_blocked_by_user_taker: Du kannst diesen Auftrag nicht annehmen, da du seinen Ersteller gesperrt hast
user_taker_is_blocked_by_user_order: Du kannst diesen Auftrag nicht annehmen, da dich sein Ersteller gesperrt hat

7 changes: 7 additions & 0 deletions locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -635,3 +635,10 @@ privacy: |
*2. How We Use the Information:*
- _Reputation System:_ To build and maintain the reputation system for each user.
- _Dispute Resolution:_ In case of a dispute, we provide the mediator (solver) with the following information: your username, Telegram ID, number of completed transactions, counterpart's rating, number of days using the bot, and the number of accumulated disputes.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
7 changes: 7 additions & 0 deletions locales/es.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,10 @@ privacy: |
*2. Cómo Utilizamos la Información:*
- _Sistema de Reputación:_ Para construir y mantener el sistema de reputación de cada usuario.
- _Resolución de Disputas:_ En caso de una disputa, proporcionamos al mediador (solver) la siguiente información: tu nombre de usuario, ID de Telegram, número de transacciones concretadas, calificación de la contraparte, cantidad de días usando el bot y el número de disputas acumuladas.
user_already_blocked: El usuario ya está bloqueado
user_blocked: Usuario bloqueado correctamente
user_unblocked: Usuario desbloqueado correctamente
blocklist_empty: No tienes ningun usuario bloqueado
orders_in_process: Hay ordenes en proceso con este usuario
user_order_is_blocked_by_user_taker: No puedes aceptar esta oferta porque has bloqueado a su creador
user_taker_is_blocked_by_user_order: No puedes aceptar esta oferta porque su creador te ha bloqueado
7 changes: 7 additions & 0 deletions locales/fa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -633,3 +633,10 @@ privacy: |
*۲. نحوه استفاده ما از اطلاعات:*
- _سیستم اعتبار:_ برای ایجاد و حفظ سیستم اعتبار برای هر کاربر.
- _حل اختلافات:_ در صورت بروز اختلاف، اطلاعات زیر را در اختیار میانجی (حل‌کننده) قرار می‌دهیم: نام کاربری شما، شناسه تلگرام، تعداد تراکنش‌های انجام شده، امتیاز طرف مقابل، تعداد روزهایی که از ربات استفاده کرده‌اید و تعداد اختلافات جمع شده.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
Comment on lines +636 to +642
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Translate blocking messages to Persian/Farsi.

The new blocking-related messages are currently in English and need to be translated to Persian/Farsi to maintain consistency with the rest of the locale file.

7 changes: 7 additions & 0 deletions locales/fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -633,3 +633,10 @@ privacy: |
*2. Comment nous utilisons les informations:*
- _Système de réputation:_ Pour construire et maintenir le système de réputation de chaque utilisateur.
- _Résolution des litiges:_ En cas de litige, nous fournissons au médiateur (solver) les informations suivantes : votre nom d'utilisateur, votre identifiant Telegram, le nombre de transactions effectuées, la note de la contrepartie, le nombre de jours d'utilisation du bot et le nombre de litiges accumulés.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
Comment on lines +636 to +642
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing French translations for user blocking messages.

The new messages are currently in English and need to be translated to French to maintain consistency with the rest of the file.

Here are the messages that need translation:

-user_already_blocked: User is already blocked
-user_blocked: User successfully blocked
-user_unblocked: User successfully unblocked
-blocklist_empty: You do not have any blocked user
-orders_in_process: There are orders in process with this user
-user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
-user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
+user_already_blocked: L'utilisateur est déjà bloqué
+user_blocked: Utilisateur bloqué avec succès
+user_unblocked: Utilisateur débloqué avec succès
+blocklist_empty: Vous n'avez aucun utilisateur bloqué
+orders_in_process: Il y a des ordres en cours avec cet utilisateur
+user_order_is_blocked_by_user_taker: Vous ne pouvez pas accepter cette offre car vous avez bloqué son créateur
+user_taker_is_blocked_by_user_order: Vous ne pouvez pas accepter cette offre car son créateur vous a bloqué
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
user_already_blocked: L'utilisateur est déjà bloqué
user_blocked: Utilisateur bloqué avec succès
user_unblocked: Utilisateur débloqué avec succès
blocklist_empty: Vous n'avez aucun utilisateur bloqué
orders_in_process: Il y a des ordres en cours avec cet utilisateur
user_order_is_blocked_by_user_taker: Vous ne pouvez pas accepter cette offre car vous avez bloqué son créateur
user_taker_is_blocked_by_user_order: Vous ne pouvez pas accepter cette offre car son créateur vous a bloqué

7 changes: 7 additions & 0 deletions locales/it.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,10 @@ privacy: |
*2. Come utilizziamo le informazioni:*
- _Sistema di reputazione:_ Per costruire e mantenere il sistema di reputazione di ciascun utente.
- _Risoluzione delle controversie:_ In caso di controversia, forniamo al mediatore (solver) le seguenti informazioni: il tuo nome utente, ID Telegram, numero di transazioni completate, valutazione della controparte, numero di giorni di utilizzo del bot e numero di controversie accumulate.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
Comment on lines +634 to +640
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing Italian translations for user blocking messages.

The new messages are currently in English and need to be translated to Italian to maintain consistency with the rest of the file.

Here are the messages that need translation:

-user_already_blocked: User is already blocked
-user_blocked: User successfully blocked
-user_unblocked: User successfully unblocked
-blocklist_empty: You do not have any blocked user
-orders_in_process: There are orders in process with this user
-user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
-user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
+user_already_blocked: L'utente è già bloccato
+user_blocked: Utente bloccato con successo
+user_unblocked: Utente sbloccato con successo
+blocklist_empty: Non hai alcun utente bloccato
+orders_in_process: Ci sono ordini in corso con questo utente
+user_order_is_blocked_by_user_taker: Non puoi accettare questo ordine perché hai bloccato il suo creatore
+user_taker_is_blocked_by_user_order: Non puoi accettare questo ordine perché il suo creatore ti ha bloccato
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
user_already_blocked: L'utente è già bloccato
user_blocked: Utente bloccato con successo
user_unblocked: Utente sbloccato con successo
blocklist_empty: Non hai alcun utente bloccato
orders_in_process: Ci sono ordini in corso con questo utente
user_order_is_blocked_by_user_taker: Non puoi accettare questo ordine perché hai bloccato il suo creatore
user_taker_is_blocked_by_user_order: Non puoi accettare questo ordine perché il suo creatore ti ha bloccato

7 changes: 7 additions & 0 deletions locales/ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,10 @@ privacy: |
*2. 정보 사용 방법:*
- _평판 시스템:_ 각 사용자의 평판 시스템을 구축하고 유지하기 위해 사용됩니다.
- _분쟁 해결:_ 분쟁이 발생할 경우, 중재자(해결자)에게 사용자 이름, Telegram ID, 완료된 거래 수, 상대방의 평가, 봇 사용 일수, 누적된 분쟁 수와 같은 정보를 제공합니다.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
7 changes: 7 additions & 0 deletions locales/pt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,10 @@ privacy: |
*2. Como Usamos as Informações:*
- _Sistema de Reputação:_ Para construir e manter o sistema de reputação de cada usuário.
- _Resolução de Disputas:_ Em caso de uma disputa, fornecemos ao mediador (solver) as seguintes informações: seu nome de usuário, ID do Telegram, número de transações concluídas, classificação da contraparte, número de dias usando o bot e o número de disputas acumuladas.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
Comment on lines +634 to +640
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing Portuguese translations for user blocking messages.

The new messages are currently in English and need to be translated to Portuguese to maintain consistency with the rest of the file.

Here are the messages that need translation:

-user_already_blocked: User is already blocked
-user_blocked: User successfully blocked
-user_unblocked: User successfully unblocked
-blocklist_empty: You do not have any blocked user
-orders_in_process: There are orders in process with this user
-user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
-user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
+user_already_blocked: Usuário já está bloqueado
+user_blocked: Usuário bloqueado com sucesso
+user_unblocked: Usuário desbloqueado com sucesso
+blocklist_empty: Você não tem nenhum usuário bloqueado
+orders_in_process: Existem ordens em andamento com este usuário
+user_order_is_blocked_by_user_taker: Você não pode aceitar esta oferta porque bloqueou seu criador
+user_taker_is_blocked_by_user_order: Você não pode aceitar esta oferta porque seu criador bloqueou você
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
user_already_blocked: Usuário já está bloqueado
user_blocked: Usuário bloqueado com sucesso
user_unblocked: Usuário desbloqueado com sucesso
blocklist_empty: Você não tem nenhum usuário bloqueado
orders_in_process: Existem ordens em andamento com este usuário
user_order_is_blocked_by_user_taker: Você não pode aceitar esta oferta porque bloqueou seu criador
user_taker_is_blocked_by_user_order: Você não pode aceitar esta oferta porque seu criador bloqueou você

7 changes: 7 additions & 0 deletions locales/ru.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,10 @@ privacy: |
*2. Как мы используем информацию:*
- _Система репутации:_ Для создания и поддержания системы репутации каждого пользователя.
- _Разрешение споров:_ В случае спора мы предоставляем медиатору (решателю) следующую информацию: ваше имя пользователя, ID Telegram, количество завершенных транзакций, рейтинг контрагента, количество дней использования бота и количество накопленных споров.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
7 changes: 7 additions & 0 deletions locales/uk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -630,3 +630,10 @@ privacy: |
*2. Як ми використовуємо інформацію:*
- _Система репутації:_ Для створення та підтримки системи репутації для кожного користувача.
- _Розв'язання спорів:_ У разі спору ми надаємо медіатору (розв'язувачу) наступну інформацію: ваше ім’я користувача, ID Telegram, кількість завершених транзакцій, рейтинг контрагента, кількість днів використання бота та кількість накопичених спорів.
user_already_blocked: User is already blocked
user_blocked: User successfully blocked
user_unblocked: User successfully unblocked
blocklist_empty: You do not have any blocked user
orders_in_process: There are orders in process with this user
user_order_is_blocked_by_user_taker: You can't take this order because you blocked its maker
user_taker_is_blocked_by_user_order: You can't take this order because its maker blocked you
15 changes: 15 additions & 0 deletions models/block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import mongoose, { Document, Schema } from 'mongoose';

export interface IBlock extends Document {
blocker_tg_id: string;
blocked_tg_id: string;
created_at: Date;
}

const blockSchema = new Schema<IBlock>({
blocker_tg_id: { type: String },
blocked_tg_id: { type: String },
created_at: { type: Date, default: Date.now },
});
Comment on lines +9 to +13
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add indexes and field validation to the schema.

The current schema could benefit from:

  1. Adding required field validation
  2. Adding indexes for efficient querying
  3. Adding a unique compound index to prevent duplicate blocks
 const blockSchema = new Schema<IBlock>({
-  blocker_tg_id: { type: String },
-  blocked_tg_id: { type: String },
+  blocker_tg_id: { type: String, required: true, index: true },
+  blocked_tg_id: { type: String, required: true, index: true },
   created_at: { type: Date, default: Date.now },
 });
+
+// Prevent duplicate blocks
+blockSchema.index({ blocker_tg_id: 1, blocked_tg_id: 1 }, { unique: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const blockSchema = new Schema<IBlock>({
blocker_tg_id: { type: String },
blocked_tg_id: { type: String },
created_at: { type: Date, default: Date.now },
});
const blockSchema = new Schema<IBlock>({
blocker_tg_id: { type: String, required: true, index: true },
blocked_tg_id: { type: String, required: true, index: true },
created_at: { type: Date, default: Date.now },
});
// Prevent duplicate blocks
blockSchema.index({ blocker_tg_id: 1, blocked_tg_id: 1 }, { unique: true });


export default mongoose.model<IBlock>('Block', blockSchema);
Loading
Loading