From fc1adf46cee929b9a5d5fe3d86099ec5aedda854 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Wed, 7 Aug 2024 10:37:44 +0200 Subject: [PATCH 1/2] bot/modules/dispute: convert to TS Convert bot/modules/disputemodule to TypeScript. --- .../dispute/{actions.js => actions.ts} | 23 +++++-- .../dispute/{commands.js => commands.ts} | 61 +++++++++++-------- bot/modules/dispute/{index.js => index.ts} | 8 ++- .../dispute/{messages.js => messages.ts} | 47 ++++++++------ models/dispute.ts | 4 +- 5 files changed, 86 insertions(+), 57 deletions(-) rename bot/modules/dispute/{actions.js => actions.ts} (67%) rename bot/modules/dispute/{commands.js => commands.ts} (69%) rename bot/modules/dispute/{index.js => index.ts} (58%) rename bot/modules/dispute/{messages.js => messages.ts} (78%) diff --git a/bot/modules/dispute/actions.js b/bot/modules/dispute/actions.ts similarity index 67% rename from bot/modules/dispute/actions.js rename to bot/modules/dispute/actions.ts index d672afbc..48e8d57b 100644 --- a/bot/modules/dispute/actions.js +++ b/bot/modules/dispute/actions.ts @@ -1,16 +1,21 @@ -const { User, Order, Dispute } = require('../../../models'); -const messages = require('./messages'); -const { validateAdmin } = require('../../validations'); +import { User, Order, Dispute } from '../../../models'; +import { MainContext } from '../../start'; +import * as messages from './messages'; +import { validateAdmin } from '../../validations'; const globalMessages = require('../../messages'); -exports.takeDispute = async ctx => { - const tgId = ctx.update.callback_query.from.id; +export const takeDispute = async (ctx: MainContext) : Promise => { + const tgId: string = (ctx.update as any).callback_query.from.id; const admin = await validateAdmin(ctx, tgId); if (!admin) return; - const orderId = ctx.match[1]; + const orderId = ctx.match?.[1]; // We check if this is a solver, the order must be from the same community const order = await Order.findOne({ _id: orderId }); + if(order === null) + throw new Error("order not found"); const dispute = await Dispute.findOne({ order_id: orderId }); + if(dispute === null) + throw new Error("dispute not found"); if (!admin.admin) { if (!order.community_id) return await globalMessages.notAuthorized(ctx, tgId); @@ -20,11 +25,17 @@ exports.takeDispute = async ctx => { } ctx.deleteMessage(); const solver = await User.findOne({ tg_id: tgId }); + if(solver === null) + throw new Error("solver not found"); if (dispute.status === 'RELEASED') return await messages.sellerReleased(ctx, solver); const buyer = await User.findOne({ _id: order.buyer_id }); + if(buyer === null) + throw new Error("buyer not found"); const seller = await User.findOne({ _id: order.seller_id }); + if(seller === null) + throw new Error("seller not found"); const initiator = order.buyer_dispute ? 'buyer' : 'seller'; const buyerDisputes = await Dispute.count({ $or: [{ buyer_id: buyer._id }, { seller_id: buyer._id }], diff --git a/bot/modules/dispute/commands.js b/bot/modules/dispute/commands.ts similarity index 69% rename from bot/modules/dispute/commands.js rename to bot/modules/dispute/commands.ts index 7f48be90..11c819d5 100644 --- a/bot/modules/dispute/commands.js +++ b/bot/modules/dispute/commands.ts @@ -1,27 +1,27 @@ -const { User, Dispute, Order } = require('../../../models'); -const { - validateParams, - validateObjectId, - validateDisputeOrder, -} = require('../../validations'); -const messages = require('./messages'); +import { MainContext } from "../../start"; + +import { User, Dispute, Order } from '../../../models'; +import { validateParams, validateObjectId, validateDisputeOrder } from '../../validations'; +import * as messages from './messages'; const globalMessages = require('../../messages'); -const { logger } = require('../../../logger'); -const { removeAtSymbol } = require('../../../util'); +import { logger } from '../../../logger'; +import { removeAtSymbol } from '../../../util'; -const dispute = async ctx => { +const dispute = async (ctx: MainContext) => { try { const { user } = ctx; - const [orderId] = await validateParams(ctx, 2, '\\<_order id_\\>'); - - if (!orderId) return; + const [orderId] = (await validateParams(ctx, 2, '\\<_order id_\\>'))!; + if (!(await validateObjectId(ctx, orderId))) return; const order = await validateDisputeOrder(ctx, user, orderId); - if (!order) return; + if (order === false) return; // Users can't initiate a dispute before this time - const secsUntilDispute = parseInt(process.env.DISPUTE_START_WINDOW); + const disputStartWindow = process.env.DISPUTE_START_WINDOW; + if(disputStartWindow === undefined) + throw new Error("DISPUTE_START_WINDOW environment variable not defined"); + const secsUntilDispute = parseInt(disputStartWindow); const time = new Date(); time.setSeconds(time.getSeconds() - secsUntilDispute); if (order.taken_at > time) { @@ -29,17 +29,23 @@ const dispute = async ctx => { } const buyer = await User.findOne({ _id: order.buyer_id }); + if(buyer === null) + throw new Error("buyer was not found"); const seller = await User.findOne({ _id: order.seller_id }); - let initiator = 'seller'; + if(seller === null) + throw new Error("seller was not found"); + let initiator: ('seller' | 'buyer') = 'seller'; if (user._id == order.buyer_id) initiator = 'buyer'; - order[`${initiator}_dispute`] = true; - order.previous_dispute_status = order.status; + if(initiator === 'seller') + order.seller_dispute = true; + else + order.buyer_dispute = true; order.status = 'DISPUTE'; const sellerToken = Math.floor(Math.random() * 899 + 100); const buyerToken = Math.floor(Math.random() * 899 + 100); - order.buyer_dispute_token = buyerToken; - order.seller_dispute_token = sellerToken; + order.buyer_dispute_token = String(buyerToken); + order.seller_dispute_token = String(sellerToken); await order.save(); // If this is a non community order, we may ban the user globally @@ -54,11 +60,14 @@ const dispute = async ctx => { (await Dispute.count({ $or: [{ buyer_id: seller._id }, { seller_id: seller._id }], })) + 1; - if (buyerDisputes >= process.env.MAX_DISPUTES) { + const maxDisputes = Number(process.env.MAX_DISPUTES); + // if MAX_DISPUTES is not specified or can't be parsed as number, following + // maxDisputes will be NaN and following conditions will be false + if (buyerDisputes >= maxDisputes) { buyer.banned = true; await buyer.save(); } - if (sellerDisputes >= process.env.MAX_DISPUTES) { + if (sellerDisputes >= maxDisputes) { seller.banned = true; await seller.save(); } @@ -83,15 +92,15 @@ const dispute = async ctx => { } }; -const deleteDispute = async ctx => { +const deleteDispute = async (ctx: MainContext) => { try { const { admin } = ctx; - let [username, orderId] = await validateParams( + let [username, orderId] = (await validateParams( ctx, 3, '\\<_username_\\> \\<_order id_\\>' - ); + ))!; if (!username) return; if (!orderId) return; @@ -140,4 +149,4 @@ const deleteDispute = async ctx => { } }; -module.exports = { dispute, deleteDispute }; +export { dispute, deleteDispute }; diff --git a/bot/modules/dispute/index.js b/bot/modules/dispute/index.ts similarity index 58% rename from bot/modules/dispute/index.js rename to bot/modules/dispute/index.ts index a484a53d..f1a1ea1b 100644 --- a/bot/modules/dispute/index.js +++ b/bot/modules/dispute/index.ts @@ -1,8 +1,10 @@ -const commands = require('./commands'); -const actions = require('./actions'); +import * as commands from './commands'; +import * as actions from './actions'; +import { Telegraf } from 'telegraf'; +import { MainContext } from '../../start'; const { userMiddleware, adminMiddleware } = require('../../middleware/user'); -exports.configure = bot => { +export const configure = (bot: Telegraf) => { bot.command('dispute', userMiddleware, commands.dispute); bot.command('deldispute', adminMiddleware, commands.deleteDispute); bot.action( diff --git a/bot/modules/dispute/messages.js b/bot/modules/dispute/messages.ts similarity index 78% rename from bot/modules/dispute/messages.js rename to bot/modules/dispute/messages.ts index dbc7754c..8882666b 100644 --- a/bot/modules/dispute/messages.js +++ b/bot/modules/dispute/messages.ts @@ -1,11 +1,16 @@ -const { - getDisputeChannel, - getDetailedOrder, - sanitizeMD, -} = require('../../../util'); -const { logger } = require('../../../logger'); +import { getDisputeChannel, getDetailedOrder, sanitizeMD } from '../../../util'; +import { logger } from '../../../logger'; +import { MainContext } from '../../start'; +import { IOrder } from '../../../models/order'; +import { UserDocument } from '../../../models/user'; -exports.beginDispute = async (ctx, initiator, order, buyer, seller) => { +export const beginDispute = async ( + ctx: MainContext, + initiator: ('seller' | 'buyer'), + order: IOrder, + buyer: UserDocument, + seller: UserDocument +) => { try { let initiatorUser = buyer; let counterPartyUser = seller; @@ -50,9 +55,11 @@ exports.beginDispute = async (ctx, initiator, order, buyer, seller) => { } }; -exports.takeDisputeButton = async (ctx, order) => { +export const takeDisputeButton = async (ctx: MainContext, order: IOrder) => { try { const disputeChannel = await getDisputeChannel(order); + if(disputeChannel === undefined) + throw new Error("disputeChannel is undefined") await ctx.telegram.sendMessage(disputeChannel, ctx.i18n.t('new_dispute'), { reply_markup: { inline_keyboard: [ @@ -70,15 +77,15 @@ exports.takeDisputeButton = async (ctx, order) => { } }; -exports.disputeData = async ( - ctx, - buyer, - seller, - order, - initiator, - solver, - buyerDisputes, - sellerDisputes +export const disputeData = async ( + ctx: MainContext, + buyer: UserDocument, + seller: UserDocument, + order: IOrder, + initiator: ('seller' | 'buyer'), + solver: UserDocument, + buyerDisputes: any, + sellerDisputes: any ) => { try { const type = @@ -135,7 +142,7 @@ exports.disputeData = async ( } }; -exports.notFoundDisputeMessage = async ctx => { +export const notFoundDisputeMessage = async (ctx: MainContext) => { try { await ctx.reply(ctx.i18n.t('not_found_dispute')); } catch (error) { @@ -143,7 +150,7 @@ exports.notFoundDisputeMessage = async ctx => { } }; -exports.sellerReleased = async (ctx, solver) => { +export const sellerReleased = async (ctx: MainContext, solver: UserDocument) => { try { await ctx.telegram.sendMessage( solver.tg_id, @@ -154,7 +161,7 @@ exports.sellerReleased = async (ctx, solver) => { } }; -exports.disputeTooSoonMessage = async ctx => { +export const disputeTooSoonMessage = async (ctx: MainContext) => { try { await ctx.reply(ctx.i18n.t('dispute_too_soon')); } catch (error) { diff --git a/models/dispute.ts b/models/dispute.ts index 68f751c4..b682107c 100644 --- a/models/dispute.ts +++ b/models/dispute.ts @@ -2,8 +2,8 @@ import mongoose, { Document, Schema } from 'mongoose'; export interface IDispute extends Document { initiator: string; - seller_id: string; - buyer_id: string; + seller_id: string | null; + buyer_id: string | null; status: string; community_id: string; order_id: string; From 382c42885cb1673c09ad850f54c7415b2bc0f035 Mon Sep 17 00:00:00 2001 From: webwarrior Date: Thu, 23 Jan 2025 11:49:38 +0100 Subject: [PATCH 2/2] bot/modules/dispute: fix bug in commands.ts Accidentally introduced when converting to TS (`order.previous_dispute_status` not set in `dispute` function). --- bot/modules/dispute/commands.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/modules/dispute/commands.ts b/bot/modules/dispute/commands.ts index 11c819d5..a3965141 100644 --- a/bot/modules/dispute/commands.ts +++ b/bot/modules/dispute/commands.ts @@ -37,6 +37,7 @@ const dispute = async (ctx: MainContext) => { let initiator: ('seller' | 'buyer') = 'seller'; if (user._id == order.buyer_id) initiator = 'buyer'; + order.previous_dispute_status = order.status; if(initiator === 'seller') order.seller_dispute = true; else