From d5770ad71733f63384c334d7aff48957f440eedd Mon Sep 17 00:00:00 2001 From: Tori Date: Mon, 15 Jun 2026 23:54:49 -0500 Subject: [PATCH] feat: upgrade mongoose from 6.13.6 to 9.6.2 Closes #655. Migrates mongoose 6 -> 9 (staged 6->7->8->9), with explicit Types.ObjectId for _id and .toString() at the call sites that used ObjectId as a string. Adds skipLibCheck for a mongoose 9 internal types issue. Squashed from 12 incremental commits (CI iterations + CodeRabbit fixes) into one clean upgrade commit. --- bot/commands.ts | 15 +++-- bot/messages.ts | 8 +-- bot/modules/block/index.ts | 6 +- bot/modules/community/actions.ts | 2 +- bot/modules/community/commands.ts | 12 ++-- bot/modules/community/messages.ts | 2 +- bot/modules/community/scenes.ts | 12 ++-- bot/modules/dispute/actions.ts | 10 +++- bot/modules/dispute/commands.ts | 18 ++++-- bot/modules/orders/commands.ts | 2 +- bot/modules/orders/takeOrder.ts | 4 +- bot/ordersActions.ts | 12 +++- bot/scenes.ts | 4 +- bot/start.ts | 48 ++++++++-------- bot/validations.ts | 38 ++++++------- jobs/pending_payments.ts | 8 ++- models/community.ts | 2 + models/order.ts | 2 + models/user.ts | 2 + package-lock.json | 95 +++++++++++++++---------------- package.json | 4 +- tests/bot/validation.spec.ts | 4 +- tsconfig.json | 1 + tsconfig.test.json | 12 ++++ util/communityHelper.ts | 2 +- util/index.ts | 8 +-- 26 files changed, 189 insertions(+), 144 deletions(-) diff --git a/bot/commands.ts b/bot/commands.ts index 8d792240..c93d7f9b 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -732,7 +732,7 @@ const cancelOrder = async ( if (order.hash) await cancelHoldInvoice({ hash: order.hash }); order.status = 'CANCELED'; - order.canceled_by = user._id; + order.canceled_by = user._id.toString(); await order.save(); OrderEvents.orderUpdated(order); // we sent a private message to the user @@ -789,7 +789,10 @@ const cancelOrder = async ( if (counterPartyUser == null) throw new Error('counterPartyUser was not found'); - const updateOrder = await setCooperativeCancelFlag(order._id, initiator); + const updateOrder = await setCooperativeCancelFlag( + order._id.toString(), + initiator, + ); // If the call returns null, the flag was already set (or order is missing), // so we treat it as a duplicate request. @@ -811,12 +814,12 @@ const cancelOrder = async ( if (updateOrder.hash) await cancelHoldInvoice({ hash: updateOrder.hash }); updateOrder.status = 'CANCELED'; - updateOrder.canceled_by = String(user._id); + updateOrder.canceled_by = user._id.toString(); await updateOrder.save(); let seller = initiatorUser; let i18nCtxSeller = ctx.i18n; - if (order.seller_id == counterPartyUser._id) { + if (order.seller_id === counterPartyUser._id.toString()) { seller = counterPartyUser; i18nCtxSeller = i18nCtxCP; } @@ -940,7 +943,9 @@ const release = async ( // actually completed. Otherwise a failed settle would leave a stale // RELEASED dispute that makes solvers believe the seller already // released (see bot/modules/dispute/actions.ts). - const dispute = await Dispute.findOne({ order_id: currentOrder._id }); + const dispute = await Dispute.findOne({ + order_id: currentOrder._id.toString(), + }); if (dispute) { dispute.status = 'RELEASED'; await dispute.save(); diff --git a/bot/messages.ts b/bot/messages.ts index 0aacf627..9b8a0b9d 100644 --- a/bot/messages.ts +++ b/bot/messages.ts @@ -101,7 +101,7 @@ const invoicePaymentRequestMessage = async ( parse_mode: 'Markdown', }); - await ctx.telegram.sendMessage(user.tg_id, order._id, { + await ctx.telegram.sendMessage(user.tg_id, order._id.toString(), { reply_markup: { inline_keyboard: [ [ @@ -443,7 +443,7 @@ const beginTakeBuyMessage = async ( }, ]); - await bot.telegram.sendMessage(seller.tg_id, order._id, { + await bot.telegram.sendMessage(seller.tg_id, order._id.toString(), { reply_markup: { inline_keyboard: [ [ @@ -543,7 +543,7 @@ const onGoingTakeBuyMessage = async ( days: ageInDays, }), ); - await bot.telegram.sendMessage(buyer.tg_id, order._id, { + await bot.telegram.sendMessage(buyer.tg_id, order._id.toString(), { reply_markup: { inline_keyboard: [ [{ text: i18nBuyer.t('continue'), callback_data: 'addInvoiceBtn' }], @@ -575,7 +575,7 @@ const beginTakeSellMessage = async ( ctx.i18n.t('you_took_someone_order', { expirationTime }), { parse_mode: 'MarkdownV2' }, ); - await bot.telegram.sendMessage(buyer.tg_id, order._id, { + await bot.telegram.sendMessage(buyer.tg_id, order._id.toString(), { reply_markup: { inline_keyboard: [ [ diff --git a/bot/modules/block/index.ts b/bot/modules/block/index.ts index 5d7699ab..6e69a8ab 100644 --- a/bot/modules/block/index.ts +++ b/bot/modules/block/index.ts @@ -2,9 +2,9 @@ import { Telegraf } from 'telegraf'; import { CommunityContext } from '../community/communityContext'; import { logger } from '../../../logger'; -const commands = require('./commands'); -const messages = require('./messages'); -const { userMiddleware } = require('../../middleware/user'); +import * as commands from './commands'; +import * as messages from './messages'; +import { userMiddleware } from '../../middleware/user'; export const configure = (bot: Telegraf) => { bot.command('block', userMiddleware, async ctx => { diff --git a/bot/modules/community/actions.ts b/bot/modules/community/actions.ts index fb04b658..011c1677 100644 --- a/bot/modules/community/actions.ts +++ b/bot/modules/community/actions.ts @@ -134,7 +134,7 @@ export const withdrawEarnings = async (ctx: CommunityContext) => { // changeVisibility, updateCommunity). const community = await Community.findOne({ _id: ctx.match?.[1], - creator_id: ctx.user._id, + creator_id: ctx.user._id.toString(), enabled: { $ne: false }, }); if (!community) return ctx.reply(ctx.i18n.t('community_not_found')); diff --git a/bot/modules/community/commands.ts b/bot/modules/community/commands.ts index eb83b986..f57bbb9d 100644 --- a/bot/modules/community/commands.ts +++ b/bot/modules/community/commands.ts @@ -8,7 +8,7 @@ import { CommunityContext } from './communityContext'; import { Telegraf } from 'telegraf'; import { getUserI18nContext } from '../../../util'; -async function getOrderCountByCommunity(): Promise { +async function getOrderCountByCommunity(): Promise> { const data = await Order.aggregate([ { $group: { _id: '$community_id', total: { $count: {} } } }, ]); @@ -65,7 +65,7 @@ export const setComm = async (ctx: MainContext) => { return await ctx.reply(ctx.i18n.t('community_not_found')); } - user.default_community_id = community._id; + user.default_community_id = community._id.toString(); await user.save(); await ctx.reply(ctx.i18n.t('operation_successful')); @@ -102,7 +102,7 @@ export const myComms = async (ctx: MainContext) => { const { user } = ctx; const communities = await Community.find({ - creator_id: user._id, + creator_id: user._id.toString(), enabled: { $ne: false }, }); @@ -161,7 +161,7 @@ export const updateCommunity = async ( if (!(await validateObjectId(ctx, id))) return; const community = await Community.findOne({ _id: id, - creator_id: user._id, + creator_id: user._id.toString(), enabled: { $ne: false }, }); @@ -243,7 +243,7 @@ export const deleteCommunity = async (ctx: CommunityContext) => { if (!(await validateObjectId(ctx, id))) return; const community = await Community.findOne({ _id: id, - creator_id: ctx.user._id, + creator_id: ctx.user._id.toString(), enabled: { $ne: false }, }); @@ -366,7 +366,7 @@ export const changeVisibility = async (ctx: CommunityContext) => { if (!(await validateObjectId(ctx, id))) return; const community = await Community.findOne({ _id: id, - creator_id: ctx.user._id, + creator_id: ctx.user._id.toString(), enabled: { $ne: false }, }); diff --git a/bot/modules/community/messages.ts b/bot/modules/community/messages.ts index 90ce18da..4c3c8861 100644 --- a/bot/modules/community/messages.ts +++ b/bot/modules/community/messages.ts @@ -168,7 +168,7 @@ export const earningsMessage = async (ctx: MainContext) => { // We check if there is a payment scheduled for this community const isScheduled = await PendingPayment.findOne({ community_id: communityId, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + attempts: { $lt: Number(process.env.PAYMENT_ATTEMPTS) }, paid: false, }); if (isScheduled) diff --git a/bot/modules/community/scenes.ts b/bot/modules/community/scenes.ts index 6ce77484..36a8b53e 100644 --- a/bot/modules/community/scenes.ts +++ b/bot/modules/community/scenes.ts @@ -394,9 +394,9 @@ const createCommunitySteps = { const user = await User.findOne({ username }); if (user) { solvers.push({ - id: user._id, + id: user._id.toString(), username: user.username, - } as IUsernameId); + } as unknown as IUsernameId); } } } else { @@ -745,9 +745,9 @@ export const updateSolversCommunityWizard = new Scenes.WizardScene( if (user == null) throw new Error('user not found'); if (user) { solvers.push({ - id: user._id, + id: user._id.toString(), username: user.username, - } as IUsernameId); + } as unknown as IUsernameId); botUsers.push(username); } else { notBotUsers.push(username); @@ -1010,8 +1010,8 @@ export const addEarningsInvoiceWizard = new Scenes.WizardScene( return await ctx.reply(ctx.i18n.t('invoice_with_incorrect_amount')); const isScheduled = await PendingPayment.findOne({ - community_id: community._id, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + community_id: community._id.toString(), + attempts: { $lt: Number(process.env.PAYMENT_ATTEMPTS) }, paid: false, is_invoice_expired: false, }); diff --git a/bot/modules/dispute/actions.ts b/bot/modules/dispute/actions.ts index 7738fac3..5a1b2988 100644 --- a/bot/modules/dispute/actions.ts +++ b/bot/modules/dispute/actions.ts @@ -35,10 +35,16 @@ export const takeDispute = async (ctx: MainContext): Promise => { if (seller === null) throw new Error('seller not found'); const initiator = order.buyer_dispute ? 'buyer' : 'seller'; const buyerDisputes = await Dispute.countDocuments({ - $or: [{ buyer_id: buyer._id }, { seller_id: buyer._id }], + $or: [ + { buyer_id: buyer._id.toString() }, + { seller_id: buyer._id.toString() }, + ], }); const sellerDisputes = await Dispute.countDocuments({ - $or: [{ buyer_id: seller._id }, { seller_id: seller._id }], + $or: [ + { buyer_id: seller._id.toString() }, + { seller_id: seller._id.toString() }, + ], }); dispute.solver_id = solver.id; diff --git a/bot/modules/dispute/commands.ts b/bot/modules/dispute/commands.ts index 708419ee..5b2d0855 100644 --- a/bot/modules/dispute/commands.ts +++ b/bot/modules/dispute/commands.ts @@ -35,7 +35,7 @@ export const handleDispute = async (ctx: MainContext, orderId: string) => { const seller = await User.findOne({ _id: order.seller_id }); if (seller === null) throw new Error('seller was not found'); let initiator: 'seller' | 'buyer' = 'seller'; - if (user._id == order.buyer_id) initiator = 'buyer'; + if (user._id.toString() == order.buyer_id) initiator = 'buyer'; order.previous_dispute_status = order.status; if (initiator === 'seller') order.seller_dispute = true; @@ -53,11 +53,17 @@ export const handleDispute = async (ctx: MainContext, orderId: string) => { // If a user disputes is equal to MAX_DISPUTES, we ban the user const buyerDisputes = (await Dispute.countDocuments({ - $or: [{ buyer_id: buyer._id }, { seller_id: buyer._id }], + $or: [ + { buyer_id: buyer._id.toString() }, + { seller_id: buyer._id.toString() }, + ], })) + 1; const sellerDisputes = (await Dispute.countDocuments({ - $or: [{ buyer_id: seller._id }, { seller_id: seller._id }], + $or: [ + { buyer_id: seller._id.toString() }, + { seller_id: seller._id.toString() }, + ], })) + 1; const maxDisputes = Number(process.env.MAX_DISPUTES); // if MAX_DISPUTES is not specified or can't be parsed as number, following @@ -164,13 +170,13 @@ const deleteDispute = async (ctx: MainContext) => { // We check if this dispute is from a community we validate that // the solver is running this command - if (dispute && dispute.solver_id != admin._id) { + if (dispute && dispute.solver_id != admin._id.toString()) { return await globalMessages.notAuthorized(ctx); } } - if (user._id == dispute.buyer_id) dispute.buyer_id = null; - if (user._id == dispute.seller_id) dispute.seller_id = null; + if (user._id.toString() == dispute.buyer_id) dispute.buyer_id = null; + if (user._id.toString() == dispute.seller_id) dispute.seller_id = null; await dispute.save(); await ctx.reply(ctx.i18n.t('operation_successful')); diff --git a/bot/modules/orders/commands.ts b/bot/modules/orders/commands.ts index 74ba87d9..4d0b682f 100644 --- a/bot/modules/orders/commands.ts +++ b/bot/modules/orders/commands.ts @@ -217,7 +217,7 @@ async function enterWizard( const isMaxPending = async (user: UserDocument) => { const pendingOrders = await Order.countDocuments({ - creator_id: user._id, + creator_id: user._id.toString(), status: 'PENDING', }); const maxPendingOrders = process.env.MAX_PENDING_ORDERS; diff --git a/bot/modules/orders/takeOrder.ts b/bot/modules/orders/takeOrder.ts index b1b57140..c6941c6e 100644 --- a/bot/modules/orders/takeOrder.ts +++ b/bot/modules/orders/takeOrder.ts @@ -88,7 +88,7 @@ export const takebuy = async ( const { randomImage } = generateRandomImage(user._id.toString()); order.status = 'WAITING_PAYMENT'; - order.seller_id = user._id; + order.seller_id = user._id.toString(); order.taken_at = new Date(Date.now()); order.random_image = randomImage; @@ -160,7 +160,7 @@ export const takesell = async ( if (!(await meetsCounterpartyRequirements(ctx, user, seller))) return; order.status = 'WAITING_BUYER_INVOICE'; - order.buyer_id = user._id; + order.buyer_id = user._id.toString(); order.taken_at = new Date(Date.now()); // Reserve a take slot (enforces the cap and increments the counter under a diff --git a/bot/ordersActions.ts b/bot/ordersActions.ts index 6a1ec206..87676e54 100644 --- a/bot/ordersActions.ts +++ b/bot/ordersActions.ts @@ -328,7 +328,10 @@ const getOrder = async ( const where = { _id: orderId, - $or: [{ seller_id: user._id }, { buyer_id: user._id }], + $or: [ + { seller_id: user._id.toString() }, + { buyer_id: user._id.toString() }, + ], }; const order = await Order.findOne(where).exec(); @@ -349,7 +352,10 @@ const getOrders = async (user: UserDocument, status?: string) => { const where: any = { $and: [ { - $or: [{ buyer_id: user._id }, { seller_id: user._id }], + $or: [ + { buyer_id: user._id.toString() }, + { seller_id: user._id.toString() }, + ], }, ], }; @@ -396,7 +402,7 @@ const getNewRangeOrderPayload = async (order: IOrder) => { paymentMethod: order.payment_method, status: 'PENDING', priceMargin: order.price_margin, - range_parent_id: order._id, + range_parent_id: order._id.toString(), tgChatId: order.tg_chat_id, tgOrderMessage: order.tg_order_message, community_id: order.community_id, diff --git a/bot/scenes.ts b/bot/scenes.ts index 3c206de4..3d41a87e 100644 --- a/bot/scenes.ts +++ b/bot/scenes.ts @@ -201,8 +201,8 @@ const addInvoicePHIWizard = new Scenes.WizardScene( } const isScheduled = await PendingPayment.findOne({ - order_id: order._id, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + order_id: order._id.toString(), + attempts: { $lt: Number(process.env.PAYMENT_ATTEMPTS) }, is_invoice_expired: false, }); // Block update if payment is already in-flight (confirmed is handled above) diff --git a/bot/start.ts b/bot/start.ts index 7623a1ce..0394c0cb 100644 --- a/bot/start.ts +++ b/bot/start.ts @@ -3,7 +3,8 @@ import { Telegraf, session, Context, Telegram } from 'telegraf'; import { I18n, I18nContext } from '@grammyjs/i18n'; import { Message } from 'typegram'; import { UserDocument } from '../models/user'; -import { FilterQuery } from 'mongoose'; +import { IOrder } from '../models/order'; +import type { QueryFilter as FilterQuery } from 'mongoose'; import * as OrderEvents from './modules/events/orders'; import { limit } from '@grammyjs/ratelimiter'; import schedule from 'node-schedule'; @@ -86,12 +87,6 @@ export interface MainContext extends Context { admin: UserDocument; } -export interface OrderQuery { - status?: string; - buyer_id?: string; - seller_id?: string; -} - export interface HasTelegram { telegram: Telegram; } @@ -100,9 +95,14 @@ const askForConfirmation = async (user: UserDocument, command: string) => { try { let orders: any[] = []; if (command === '/cancel') { - const where: FilterQuery = { + const where: FilterQuery = { $and: [ - { $or: [{ buyer_id: user._id }, { seller_id: user._id }] }, + { + $or: [ + { buyer_id: user._id.toString() }, + { seller_id: user._id.toString() }, + ], + }, { $or: [ { status: 'ACTIVE' }, @@ -115,14 +115,14 @@ const askForConfirmation = async (user: UserDocument, command: string) => { }; orders = await Order.find(where); } else if (command === '/fiatsent') { - const where: FilterQuery = { - $and: [{ buyer_id: user._id }, { status: 'ACTIVE' }], + const where: FilterQuery = { + $and: [{ buyer_id: user._id.toString() }, { status: 'ACTIVE' }], }; orders = await Order.find(where); } else if (command === '/release') { - const where: FilterQuery = { + const where: FilterQuery = { $and: [ - { seller_id: user._id }, + { seller_id: user._id.toString() }, { $or: [ { status: 'ACTIVE' }, @@ -134,8 +134,8 @@ const askForConfirmation = async (user: UserDocument, command: string) => { }; orders = await Order.find(where); } else if (command === '/setinvoice') { - const where: FilterQuery = { - buyer_id: user._id, + const where: FilterQuery = { + buyer_id: user._id.toString(), status: { $in: ['PAID_HOLD_INVOICE', 'WAITING_BUYER_INVOICE'] }, }; @@ -351,7 +351,7 @@ const initialize = ( } // We look for a dispute for this order - const dispute = await Dispute.findOne({ order_id: order._id }); + const dispute = await Dispute.findOne({ order_id: order._id.toString() }); // We check if this is a solver, the order must be from the same community if (!ctx.admin.admin) { @@ -389,7 +389,7 @@ const initialize = ( order.is_frozen = true; order.status = 'FROZEN'; - order.action_by = ctx.admin._id; + order.action_by = ctx.admin._id.toString(); await order.save(); await ctx.reply(ctx.i18n.t('order_frozen')); @@ -415,7 +415,7 @@ const initialize = ( if (order === null) return; // We look for a dispute for this order - const dispute = await Dispute.findOne({ order_id: order._id }); + const dispute = await Dispute.findOne({ order_id: order._id.toString() }); // We check if this is a solver, the order must be from the same community if (!ctx.admin.admin) { @@ -435,7 +435,7 @@ const initialize = ( // We check if this dispute is from a community we validate that // the solver is running this command - if (dispute && dispute.solver_id != ctx.admin._id) { + if (dispute && dispute.solver_id != ctx.admin._id.toString()) { logger.debug( `cancelorder ${order._id}: @${ctx.admin.username} is not the solver of this dispute`, ); @@ -453,7 +453,7 @@ const initialize = ( logger.info(`order ${order._id}: cancelled by admin`); order.status = 'CANCELED_BY_ADMIN'; - order.canceled_by = ctx.admin._id; + order.canceled_by = ctx.admin._id.toString(); await order.save(); order.status = 'CANCELED'; OrderEvents.orderUpdated(order); @@ -563,7 +563,7 @@ const initialize = ( } // We look for a dispute for this order - const dispute = await Dispute.findOne({ order_id: order._id }); + const dispute = await Dispute.findOne({ order_id: order._id.toString() }); // We check if this is a solver, the order must be from the same community if (!ctx.admin.admin) { @@ -1052,7 +1052,7 @@ const initialize = ( } // We look for a dispute for this order - const dispute = await Dispute.findOne({ order_id: order._id }); + const dispute = await Dispute.findOne({ order_id: order._id.toString() }); // We check if this is a solver, the order must be from the same community if (!ctx.admin.admin) { @@ -1087,8 +1087,8 @@ const initialize = ( // We make sure the buyers invoice is not being paid const isPending = await PendingPayment.findOne({ - order_id: order._id, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + order_id: order._id.toString(), + attempts: { $lt: Number(process.env.PAYMENT_ATTEMPTS) }, }); if (isPending) return; diff --git a/bot/validations.ts b/bot/validations.ts index 045eb3f4..a149e85c 100644 --- a/bot/validations.ts +++ b/bot/validations.ts @@ -1,11 +1,6 @@ -import { - HasTelegram, - MainContext, - OrderQuery, - ctxUpdateAssertMsg, -} from './start'; +import { HasTelegram, MainContext, ctxUpdateAssertMsg } from './start'; import { IUsernameId } from '../models/community'; -import { FilterQuery } from 'mongoose'; +import type { QueryFilter as FilterQuery } from 'mongoose'; import { UserDocument } from '../models/user'; import { IOrder } from '../models/order'; // @ts-ignore @@ -44,7 +39,7 @@ const validateUser = async (ctx: MainContext, start: boolean) => { return false; } - let user = await User.findOne({ tg_id: tgUser.id }); + let user = await User.findOne({ tg_id: String(tgUser.id) }); if (!user && start) { user = new User({ @@ -522,8 +517,8 @@ const validateReleaseOrder = async ( orderId: string, ) => { try { - let where: FilterQuery = { - seller_id: user._id, + let where: FilterQuery = { + seller_id: user._id.toString(), status: 'WAITING_BUYER_INVOICE', _id: orderId, }; @@ -535,7 +530,7 @@ const validateReleaseOrder = async ( where = { $and: [ - { seller_id: user._id }, + { seller_id: user._id.toString() }, { $or: [ { status: 'ACTIVE' }, @@ -573,7 +568,12 @@ const validateDisputeOrder = async ( $and: [ { _id: orderId }, { $or: [{ status: 'ACTIVE' }, { status: 'FIAT_SENT' }] }, - { $or: [{ seller_id: user._id }, { buyer_id: user._id }] }, + { + $or: [ + { seller_id: user._id.toString() }, + { buyer_id: user._id.toString() }, + ], + }, ], }; @@ -597,9 +597,9 @@ const validateFiatSentOrder = async ( orderId: string, ) => { try { - const where: FilterQuery = { + const where: FilterQuery = { $and: [ - { buyer_id: user._id }, + { buyer_id: user._id.toString() }, { $or: [{ status: 'ACTIVE' }, { status: 'PAID_HOLD_INVOICE' }] }, ], }; @@ -634,7 +634,7 @@ const validateFiatSentOrder = async ( const validateSeller = async (ctx: MainContext, user: UserDocument) => { try { const where = { - seller_id: user._id, + seller_id: user._id.toString(), status: 'FIAT_SENT', }; @@ -700,8 +700,8 @@ const validateUserWaitingOrder = async ( ) => { try { // If is a seller - let where: FilterQuery = { - seller_id: user._id, + let where: FilterQuery = { + seller_id: user._id.toString(), status: 'WAITING_PAYMENT', }; let orders = await Order.find(where); @@ -711,7 +711,7 @@ const validateUserWaitingOrder = async ( } // If is a buyer where = { - buyer_id: user._id, + buyer_id: user._id.toString(), status: 'WAITING_BUYER_INVOICE', }; orders = await Order.find(where); @@ -736,7 +736,7 @@ const isBannedFromCommunity = async ( const community = await Community.findOne({ _id: communityId }); if (!community) return false; return community.banned_users.some( - (buser: IUsernameId) => buser.id == user._id, + (buser: IUsernameId) => buser.id === user._id.toString(), ); } catch (error) { logger.error(error); diff --git a/jobs/pending_payments.ts b/jobs/pending_payments.ts index 5a5b1117..ec43c429 100644 --- a/jobs/pending_payments.ts +++ b/jobs/pending_payments.ts @@ -25,12 +25,16 @@ const advanceNextRetry = (pending: IPendingPayment): void => { ); }; +const maxPaymentAttempts = process.env.PAYMENT_ATTEMPTS + ? parseInt(process.env.PAYMENT_ATTEMPTS, 10) + : 3; + export const attemptPendingPayments = async ( bot: Telegraf, ): Promise => { const pendingPayments = await PendingPayment.find({ paid: false, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + attempts: { $lt: maxPaymentAttempts }, is_invoice_expired: false, community_id: null, next_retry: { $lte: new Date() }, @@ -339,7 +343,7 @@ export const attemptCommunitiesPendingPayments = async ( ): Promise => { const pendingPayments = await PendingPayment.find({ paid: false, - attempts: { $lt: process.env.PAYMENT_ATTEMPTS }, + attempts: { $lt: maxPaymentAttempts }, is_invoice_expired: false, community_id: { $ne: null }, next_retry: { $lte: new Date() }, diff --git a/models/community.ts b/models/community.ts index e2fad26c..13218adc 100644 --- a/models/community.ts +++ b/models/community.ts @@ -43,6 +43,8 @@ const usernameIdSchema = new Schema({ export interface ICommunity extends Document { _id: string; + // mongoose 9 no longer exposes the `id` virtual on the Document type + id: string; name: string; creator_id: string; group: string; diff --git a/models/order.ts b/models/order.ts index b4cacdd8..eb586b6b 100644 --- a/models/order.ts +++ b/models/order.ts @@ -2,6 +2,8 @@ import mongoose, { Document, Schema } from 'mongoose'; export interface IOrder extends Document { _id: string; + // mongoose 9 no longer exposes the `id` virtual on the Document type + id: string; description?: string; amount: number; max_amount: number; diff --git a/models/user.ts b/models/user.ts index 36154f67..ece9cb96 100644 --- a/models/user.ts +++ b/models/user.ts @@ -7,6 +7,8 @@ interface UserReview { export interface UserDocument extends Document { _id: string; + // mongoose 9 no longer exposes the `id` virtual on the Document type + id: string; tg_id: string; username?: string; lang: string; diff --git a/package-lock.json b/package-lock.json index 3b399435..aad0b286 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "dotenv": "^10.0.0", "invoices": "2.0.6", "lightning": "11.1.0", - "mongoose": "^8.24.0", + "mongoose": "^9.6.2", "node-schedule": "^2.0.0", "nostr-tools": "^2.5.2", "qrcode": "^1.5.0", @@ -53,7 +53,7 @@ "typescript": "5.1.6" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.19.0" } }, "node_modules/@alexbosworth/blockchain": { @@ -358,9 +358,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", - "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.13.tgz", + "integrity": "sha512-E3Sv4eCYAlKYUTx8S3ioQcDUscOif+8zZ5OnW1IzJ+Tt+EO+ke8mn+Y3FX6N1H79picwbdOavVOb1jPi2EOyrg==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -711,9 +711,9 @@ "license": "MIT" }, "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", "license": "MIT", "dependencies": { "@types/webidl-conversions": "*" @@ -1469,12 +1469,12 @@ } }, "node_modules/bson": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", - "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.1.tgz", + "integrity": "sha512-h/C0qe6857pQhcSJHLfsR1uYGj98Ge3wKAD3Ed9KqH3wcVh+BM4Jq4xISD7vs9OPuT07n+q3QQVjslJ286j6ag==", "license": "Apache-2.0", "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" } }, "node_modules/buffer": { @@ -4096,12 +4096,12 @@ "dev": true }, "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.3.0.tgz", + "integrity": "sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==", "license": "Apache-2.0", "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/keyv": { @@ -4658,26 +4658,26 @@ "dev": true }, "node_modules/mongodb": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", - "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz", + "integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.3.0", - "bson": "^6.10.4", - "mongodb-connection-string-url": "^3.0.2" + "bson": "^7.2.0", + "mongodb-connection-string-url": "^7.0.0" }, "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" }, "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", "snappy": "^7.3.2", - "socks": "^2.7.1" + "socks": "^2.8.6" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -4704,31 +4704,33 @@ } }, "node_modules/mongodb-connection-string-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.2.tgz", + "integrity": "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==", "license": "Apache-2.0", "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^14.1.0 || ^13.0.0" + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" } }, "node_modules/mongoose": { - "version": "8.24.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.1.tgz", - "integrity": "sha512-UpHBA0l5kHyKJQFjmBaFYQFo5sgz1DK0TRqDkOyBLYbqiIbKKhIvBpHWBXqeo0rgW4kGI1UhhAw+kTQZoj1BdA==", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.2.tgz", + "integrity": "sha512-7m8HntjkoRnwEmuPC0kdlwcZXJOQf4twumFj+PNzg/anqqZE2Er7hQslqyzy07mP3JcFjoTSgH5765PyqOXsxw==", "license": "MIT", "dependencies": { - "bson": "^6.10.4", - "kareem": "2.6.3", - "mongodb": "~6.20.0", + "kareem": "3.3.0", + "mongodb": "~7.2", "mpath": "0.9.0", - "mquery": "5.0.0", + "mquery": "6.0.0", "ms": "2.1.3", "sift": "17.1.3" }, "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" }, "funding": { "type": "opencollective", @@ -4744,15 +4746,12 @@ } }, "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", "license": "MIT", - "dependencies": { - "debug": "4.x" - }, "engines": { - "node": ">=14.0.0" + "node": ">=20.19.0" } }, "node_modules/ms": { diff --git a/package.json b/package.json index 2721d1bd..1e3640f1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "lnp2pbot", "version": "0.15.2", "engines": { - "node": ">=20.0.0" + "node": ">=20.19.0" }, "author": "Francisco Calderón ", "description": "P2P lightning network telegram bot", @@ -28,7 +28,7 @@ "dotenv": "^10.0.0", "invoices": "2.0.6", "lightning": "11.1.0", - "mongoose": "^8.24.0", + "mongoose": "^9.6.2", "node-schedule": "^2.0.0", "nostr-tools": "^2.5.2", "qrcode": "^1.5.0", diff --git a/tests/bot/validation.spec.ts b/tests/bot/validation.spec.ts index 63a5f3a6..55622d49 100644 --- a/tests/bot/validation.spec.ts +++ b/tests/bot/validation.spec.ts @@ -67,9 +67,9 @@ describe('Validations', () => { sandbox = sinon.createSandbox(); // Mock process.env within the sandbox sandbox.stub(process, 'env').value({ - MIN_PAYMENT_AMT: 100, + MIN_PAYMENT_AMT: '100', NODE_ENV: 'production', - INVOICE_EXPIRATION_WINDOW: 3600000, + INVOICE_EXPIRATION_WINDOW: '3600000', }); replyStub = sinon.stub(ctx, 'reply'); diff --git a/tsconfig.json b/tsconfig.json index c37ea80d..0488ecf0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ }, "include": [ "app.ts", + "monitoring.ts", "bot/**/*", "jobs/**/*", "ln/**/*", diff --git a/tsconfig.test.json b/tsconfig.test.json index a657e5b4..cbec45b7 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,6 +1,18 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["mocha"] + }, "include": [ + "app.ts", + "monitoring.ts", + "bot/**/*", + "jobs/**/*", + "ln/**/*", + "lnurl/**/*", + "models/**/*", + "util/**/*", + "scripts/**/*", "tests/**/*" ], "exclude": [ diff --git a/util/communityHelper.ts b/util/communityHelper.ts index 60926e79..55725289 100644 --- a/util/communityHelper.ts +++ b/util/communityHelper.ts @@ -32,7 +32,7 @@ export const getCommunityInfo = async ( enabled: { $ne: false }, }); if (community) { - communityId = community._id; + communityId = community._id.toString(); } } else if (user.default_community_id) { // Private chat with default community diff --git a/util/index.ts b/util/index.ts index dc6ab966..40788e09 100644 --- a/util/index.ts +++ b/util/index.ts @@ -38,7 +38,7 @@ const isIso4217 = (code: string): boolean => { const isOrderCreator = (user: UserDocument, order: IOrder) => { try { - return user._id == order.creator_id; + return user._id.toString() == order.creator_id; } catch (error) { logger.error(error); return false; @@ -88,8 +88,8 @@ const handleReputationItems = async ( const yesterday = new Date(Date.now() - 86400000).toISOString(); const orders = await Order.find({ status: 'SUCCESS', - seller_id: buyer._id, - buyer_id: seller._id, + seller_id: buyer._id.toString(), + buyer_id: seller._id.toString(), taken_at: { $gte: yesterday }, }); if (orders.length > 0) { @@ -495,7 +495,7 @@ const isDisputeSolver = (community: ICommunity | null, user: UserDocument) => { return false; } - return community.solvers.some(solver => solver.id == user._id); + return community.solvers.some(solver => solver.id == user._id.toString()); }; // Return the fee the bot will charge to the seller