diff --git a/bot/validations.js b/bot/validations.ts similarity index 76% rename from bot/validations.js rename to bot/validations.ts index a3288bd2..0e9ce593 100644 --- a/bot/validations.js +++ b/bot/validations.ts @@ -1,18 +1,32 @@ +import { MainContext, OrderQuery, ctxUpdateAssertMsg } from "./start"; +import { ICommunity } from "../models/community"; +import { FilterQuery } from "mongoose"; +import { UserDocument } from "../models/user"; +import { IOrder } from "../models/order"; +import { Telegraf } from "telegraf"; + const { parsePaymentRequest } = require('invoices'); const { ObjectId } = require('mongoose').Types; -const messages = require('./messages'); -const { Order, User, Community } = require('../models'); +import * as messages from './messages'; +import { Order, User, Community } from '../models'; const { isIso4217, isDisputeSolver } = require('../util'); const { existLightningAddress } = require('../lnurl/lnurl-pay'); const logger = require('../logger'); // We look in database if the telegram user exists, // if not, it creates a new user -const validateUser = async (ctx, start) => { +const validateUser = async (ctx: MainContext, start: boolean) => { try { - const tgUser = ctx.update.callback_query - ? ctx.update.callback_query.from - : ctx.update.message.from; + let tgUser = null; + if (("callback_query" in ctx.update) && ctx.update.callback_query) { + tgUser = ctx.update.callback_query.from; + } + else if (("message" in ctx.update) && ctx.update.message) { + tgUser = ctx.update.message.from; + } + else { + throw new Error(ctxUpdateAssertMsg); + } // We need to make sure the user has a username if (!tgUser.username) { return await messages.nonHandleErrorMessage(ctx); @@ -45,15 +59,18 @@ const validateUser = async (ctx, start) => { } }; -const validateSuperAdmin = async (ctx, id) => { +const validateSuperAdmin = async (ctx: MainContext, id?: string) => { try { + if (!('message' in ctx.update) || !('text' in ctx.update.message)) { + throw new Error(ctxUpdateAssertMsg); + } const tgUserId = id || ctx.update.message.from.id; const user = await User.findOne({ tg_id: tgUserId }); // If the user never started the bot we can't send messages // to that user, so we do nothing if (!user) return; - if (!user.admin) return await messages.notAuthorized(ctx, tgUserId); + if (!user.admin) return await messages.notAuthorized(ctx, tgUserId.toString()); return user; } catch (error) { @@ -62,8 +79,11 @@ const validateSuperAdmin = async (ctx, id) => { } }; -const validateAdmin = async (ctx, id) => { +const validateAdmin = async (ctx: MainContext, id?: string) => { try { + if (!('message' in ctx.update) || !('text' in ctx.update.message)) { + throw new Error(ctxUpdateAssertMsg); + } const tgUserId = id || ctx.update.message.from.id; const user = await User.findOne({ tg_id: tgUserId }); // If the user never started the bot we can't send messages @@ -74,10 +94,11 @@ const validateAdmin = async (ctx, id) => { if (user.default_community_id) community = await Community.findOne({ _id: user.default_community_id }); + if (community === null) throw Error("Community was not found in DB"); const isSolver = isDisputeSolver(community, user); if (!user.admin && !isSolver) - return await messages.notAuthorized(ctx, tgUserId); + return await messages.notAuthorized(ctx, tgUserId.toString()); return user; } catch (error) { @@ -86,7 +107,7 @@ const validateAdmin = async (ctx, id) => { } }; -const validateSellOrder = async ctx => { +const validateSellOrder = async (ctx: MainContext) => { try { const args = ctx.state.command.args; if (args.length < 4) { @@ -130,11 +151,11 @@ const validateSellOrder = async ctx => { return false; } - if (amount !== 0 && amount < process.env.MIN_PAYMENT_AMT) { + if (amount !== 0 && amount < Number(process.env.MIN_PAYMENT_AMT)) { await messages.mustBeGreatherEqThan( ctx, 'monto_en_sats', - process.env.MIN_PAYMENT_AMT + Number(process.env.MIN_PAYMENT_AMT) ); return false; } @@ -149,7 +170,7 @@ const validateSellOrder = async ctx => { return false; } - if (fiatAmount.some(x => x < 1)) { + if (fiatAmount.some((x: number) => x < 1)) { await messages.mustBeGreatherEqThan(ctx, 'monto_en_fiat', 1); return false; } @@ -174,7 +195,7 @@ const validateSellOrder = async ctx => { } }; -const validateBuyOrder = async ctx => { +const validateBuyOrder = async (ctx: MainContext) => { try { const args = ctx.state.command.args; if (args.length < 4) { @@ -216,11 +237,11 @@ const validateBuyOrder = async ctx => { return false; } - if (amount !== 0 && amount < process.env.MIN_PAYMENT_AMT) { + if (amount !== 0 && amount < Number(process.env.MIN_PAYMENT_AMT)) { await messages.mustBeGreatherEqThan( ctx, 'monto_en_sats', - process.env.MIN_PAYMENT_AMT + Number(process.env.MIN_PAYMENT_AMT) ); return false; } @@ -235,7 +256,7 @@ const validateBuyOrder = async ctx => { return false; } - if (fiatAmount.some(x => x < 1)) { + if (fiatAmount.some((x: number) => x < 1)) { await messages.mustBeGreatherEqThan(ctx, 'monto_en_fiat', 1); return false; } @@ -259,7 +280,7 @@ const validateBuyOrder = async ctx => { return false; } }; -const validateLightningAddress = async lightningAddress => { +const validateLightningAddress = async (lightningAddress: string) => { const pattern = /^[\w-.]+@([\w-]+.)+[\w-]{2,4}$/g; return ( @@ -267,18 +288,19 @@ const validateLightningAddress = async lightningAddress => { ); }; -const validateInvoice = async (ctx, lnInvoice) => { +const validateInvoice = async (ctx: MainContext, lnInvoice: string) => { try { const invoice = parsePaymentRequest({ request: lnInvoice }); const latestDate = new Date( - Date.now() + parseInt(process.env.INVOICE_EXPIRATION_WINDOW) + Date.now() + Number(process.env.INVOICE_EXPIRATION_WINDOW) ).toISOString(); - if (!!invoice.tokens && invoice.tokens < process.env.MIN_PAYMENT_AMT) { + if (!("MAIN_PAYMENT_AMT" in process.env)) throw Error("MIN_PAYMENT_AMT not found, please check .env file"); + if (!!invoice.tokens && invoice.tokens < Number(process.env.MIN_PAYMENT_AMT)) { await messages.minimunAmountInvoiceMessage(ctx); return false; } - if (new Date(invoice.expires_at) < latestDate) { + if (new Date(invoice.expires_at).toISOString() < latestDate) { await messages.minimunExpirationTimeInvoiceMessage(ctx); return false; } @@ -306,20 +328,20 @@ const validateInvoice = async (ctx, lnInvoice) => { } }; -const isValidInvoice = async (ctx, lnInvoice) => { +const isValidInvoice = async (ctx: MainContext, lnInvoice: string) => { try { const invoice = parsePaymentRequest({ request: lnInvoice }); const latestDate = new Date( - Date.now() + parseInt(process.env.INVOICE_EXPIRATION_WINDOW) + Date.now() + Number(process.env.INVOICE_EXPIRATION_WINDOW) ).toISOString(); - if (!!invoice.tokens && invoice.tokens < process.env.MIN_PAYMENT_AMT) { + if (!!invoice.tokens && invoice.tokens < Number(process.env.MIN_PAYMENT_AMT)) { await messages.invoiceMustBeLargerMessage(ctx); return { success: false, }; } - if (new Date(invoice.expires_at) < latestDate) { + if (new Date(invoice.expires_at).toISOString() < latestDate) { await messages.invoiceExpiryTooShortMessage(ctx); return { success: false, @@ -359,7 +381,7 @@ const isValidInvoice = async (ctx, lnInvoice) => { } }; -const isOrderCreator = (user, order) => { +const isOrderCreator = (user: UserDocument, order: IOrder) => { try { return user._id == order.creator_id; } catch (error) { @@ -368,7 +390,7 @@ const isOrderCreator = (user, order) => { } }; -const validateTakeSellOrder = async (ctx, bot, user, order) => { +const validateTakeSellOrder = async (ctx: MainContext, bot: Telegraf, user: UserDocument, order: IOrder) => { try { if (!order) { await messages.invalidOrderMessage(ctx, bot, user); @@ -397,10 +419,10 @@ const validateTakeSellOrder = async (ctx, bot, user, order) => { } }; -const validateTakeBuyOrder = async (ctx, bot, user, order) => { +const validateTakeBuyOrder = async (ctx: MainContext, bot: Telegraf, user: UserDocument, order: IOrder) => { try { if (!order) { - await messages.invalidOrderMessage(bot, user); + await messages.invalidOrderMessage(ctx, bot, user); return false; } if (isOrderCreator(user, order) && process.env.NODE_ENV === 'production') { @@ -422,9 +444,9 @@ const validateTakeBuyOrder = async (ctx, bot, user, order) => { } }; -const validateReleaseOrder = async (ctx, user, orderId) => { +const validateReleaseOrder = async (ctx: MainContext, user: UserDocument, orderId: string) => { try { - let where = { + let where: FilterQuery = { seller_id: user._id, status: 'WAITING_BUYER_INVOICE', _id: orderId, @@ -465,7 +487,7 @@ const validateReleaseOrder = async (ctx, user, orderId) => { } }; -const validateDisputeOrder = async (ctx, user, orderId) => { +const validateDisputeOrder = async (ctx: MainContext, user: UserDocument, orderId: string) => { try { const where = { $and: [ @@ -489,9 +511,9 @@ const validateDisputeOrder = async (ctx, user, orderId) => { } }; -const validateFiatSentOrder = async (ctx, user, orderId) => { +const validateFiatSentOrder = async (ctx: MainContext, user: UserDocument, orderId: string) => { try { - const where = { + const where: FilterQuery = { $and: [ { buyer_id: user._id }, { $or: [{ status: 'ACTIVE' }, { status: 'PAID_HOLD_INVOICE' }] }, @@ -525,7 +547,7 @@ const validateFiatSentOrder = async (ctx, user, orderId) => { }; // If a seller have an order with status FIAT_SENT, return false -const validateSeller = async (ctx, user) => { +const validateSeller = async (ctx: MainContext, user: UserDocument) => { try { const where = { seller_id: user._id, @@ -546,10 +568,13 @@ const validateSeller = async (ctx, user) => { } }; -const validateParams = async (ctx, paramNumber, errOutputString) => { +const validateParams = async (ctx: MainContext, paramNumber: number, errOutputString: string): Promise> => { try { + if (!('message' in ctx.update) || !('text' in ctx.update.message)) { + throw new Error(ctxUpdateAssertMsg); + } const paramsArray = ctx.update.message.text.split(' '); - const params = paramsArray.filter(el => el !== ''); + const params = paramsArray.filter((el: string) => el !== ''); if (params.length !== paramNumber) { await messages.customMessage( ctx, @@ -562,11 +587,11 @@ const validateParams = async (ctx, paramNumber, errOutputString) => { return params.slice(1); } catch (error) { logger.error(error); - return false; + return null; } }; -const validateObjectId = async (ctx, id) => { +const validateObjectId = async (ctx: MainContext, id: string) => { try { if (!ObjectId.isValid(id)) { await messages.notValidIdMessage(ctx); @@ -580,10 +605,10 @@ const validateObjectId = async (ctx, id) => { } }; -const validateUserWaitingOrder = async (ctx, bot, user) => { +const validateUserWaitingOrder = async (ctx: MainContext, bot: Telegraf, user: UserDocument) => { try { // If is a seller - let where = { + let where: FilterQuery = { seller_id: user._id, status: 'WAITING_PAYMENT', }; @@ -610,12 +635,12 @@ const validateUserWaitingOrder = async (ctx, bot, user) => { }; // We check if the user is banned from the community in the order -const isBannedFromCommunity = async (user, communityId) => { +const isBannedFromCommunity = async (user: UserDocument, communityId: string) => { try { if (!communityId) return false; const community = await Community.findOne({ _id: communityId }); if (!community) return false; - return community.banned_users.some(buser => buser.id == user._id); + return community.banned_users.toObject().some((buser: ICommunity) => buser.id == user._id); } catch (error) { logger.error(error); return false;