diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index cf0e62f1..a7dc2737 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -13,16 +13,18 @@ jobs: ci_to_main: runs-on: ubuntu-latest container: - image: 'ubuntu:24.04' + image: 'ubuntu:26.04' steps: - name: Update apt and install required packages run: | apt update --yes - apt install --yes sudo + apt install --yes sudo curl git nodejs npm - sudo apt install --yes --no-install-recommends npm + # Dependencies required to compile the canvas package from source + sudo apt install --yes --no-install-recommends \ + build-essential pkg-config libcairo2-dev libpango1.0-dev \ + libjpeg-dev libgif-dev librsvg2-dev - sudo apt install --yes git # one time fix to avoid permission problems later on git config --global --add safe.directory "$GITHUB_WORKSPACE" @@ -34,13 +36,13 @@ jobs: sudo rm -f /etc/ssl/certs/ca-bundle.crt sudo apt reinstall --yes ca-certificates sudo update-ca-certificates - curl -fsSL https://pgp.mongodb.com/server-6.0.asc | \ - sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg \ + curl -fsSL https://pgp.mongodb.com/server-8.0.asc | \ + sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \ --dearmor - name: Install MongoDB run: | - echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list sudo apt-get update sudo apt-get install -y mongodb-org - name: Check Mongo version diff --git a/bot/commands.ts b/bot/commands.ts index 5d871c52..87fe0a43 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -682,7 +682,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 @@ -739,7 +739,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. @@ -761,12 +764,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; } @@ -861,7 +864,7 @@ const release = async ( const order = await validateReleaseOrder(ctx, user, orderId); if (!order) 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() }); if (dispute) { dispute.status = 'RELEASED'; diff --git a/bot/messages.ts b/bot/messages.ts index 17354be0..94352837 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/commands.ts b/bot/modules/community/commands.ts index 78e60525..b6f131e2 100644 --- a/bot/modules/community/commands.ts +++ b/bot/modules/community/commands.ts @@ -7,7 +7,7 @@ import { MainContext } from '../../start'; import { CommunityContext } from './communityContext'; import { Telegraf } from 'telegraf'; -async function getOrderCountByCommunity(): Promise { +async function getOrderCountByCommunity(): Promise> { const data = await Order.aggregate([ { $group: { _id: '$community_id', total: { $count: {} } } }, ]); @@ -57,7 +57,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')); @@ -89,7 +89,9 @@ export const myComms = async (ctx: MainContext) => { try { const { user } = ctx; - const communities = await Community.find({ creator_id: user._id }); + const communities = await Community.find({ + creator_id: user._id.toString(), + }); if (!communities.length) return await ctx.reply(ctx.i18n.t('you_dont_have_communities')); @@ -146,7 +148,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(), }); if (!community) { @@ -227,7 +229,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(), }); if (!community) { @@ -250,7 +252,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(), }); if (!community) { diff --git a/bot/modules/community/messages.ts b/bot/modules/community/messages.ts index 94b2c8d6..41ec3d58 100644 --- a/bot/modules/community/messages.ts +++ b/bot/modules/community/messages.ts @@ -164,7 +164,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 79d9ed3c..0d6ff3ce 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); @@ -1008,8 +1008,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 75a9d385..b2e69d6b 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 ad574b32..884b950c 100644 --- a/bot/modules/orders/takeOrder.ts +++ b/bot/modules/orders/takeOrder.ts @@ -78,7 +78,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; @@ -129,7 +129,7 @@ export const takesell = async ( return await messages.bannedUserErrorMessage(ctx, user); if (!(await validateTakeSellOrder(ctx, bot, user, order))) return; order.status = 'WAITING_BUYER_INVOICE'; - order.buyer_id = user._id; + order.buyer_id = user._id.toString(); order.taken_at = new Date(Date.now()); await order.save(); diff --git a/bot/ordersActions.ts b/bot/ordersActions.ts index 66dcbdac..a8323c96 100644 --- a/bot/ordersActions.ts +++ b/bot/ordersActions.ts @@ -297,7 +297,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(); @@ -318,7 +321,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() }, + ], }, ], }; @@ -365,7 +371,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 6db6e7b2..80814fc2 100644 --- a/bot/scenes.ts +++ b/bot/scenes.ts @@ -145,8 +145,8 @@ const addInvoicePHIWizard = new Scenes.WizardScene( return await messages.incorrectAmountInvoiceMessage(ctx); 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, }); // We check if the payment is on flight diff --git a/bot/start.ts b/bot/start.ts index 53e2b8f9..ec6e862a 100644 --- a/bot/start.ts +++ b/bot/start.ts @@ -3,7 +3,7 @@ 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 type { QueryFilter as FilterQuery } from 'mongoose'; import * as OrderEvents from './modules/events/orders'; import { limit } from '@grammyjs/ratelimiter'; import schedule from 'node-schedule'; @@ -101,7 +101,12 @@ const askForConfirmation = async (user: UserDocument, command: string) => { if (command === '/cancel') { 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,13 +120,13 @@ 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' }], + $and: [{ buyer_id: user._id.toString() }, { status: 'ACTIVE' }], }; orders = await Order.find(where); } else if (command === '/release') { const where: FilterQuery = { $and: [ - { seller_id: user._id }, + { seller_id: user._id.toString() }, { $or: [ { status: 'ACTIVE' }, @@ -134,7 +139,7 @@ const askForConfirmation = async (user: UserDocument, command: string) => { orders = await Order.find(where); } else if (command === '/setinvoice') { const where: FilterQuery = { - buyer_id: user._id, + buyer_id: user._id.toString(), status: { $in: ['PAID_HOLD_INVOICE', 'WAITING_BUYER_INVOICE'] }, }; @@ -352,7 +357,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(); if (order.secret) await settleHoldInvoice({ secret: order.secret }); @@ -380,7 +385,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) { @@ -400,7 +405,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`, ); @@ -418,7 +423,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); @@ -530,7 +535,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) { @@ -1015,8 +1020,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 7ee42bbb..6bab6a6b 100644 --- a/bot/validations.ts +++ b/bot/validations.ts @@ -5,7 +5,7 @@ import { 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 +44,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({ @@ -520,7 +520,7 @@ const validateReleaseOrder = async ( ) => { try { let where: FilterQuery = { - seller_id: user._id, + seller_id: user._id.toString(), status: 'WAITING_BUYER_INVOICE', _id: orderId, }; @@ -532,7 +532,7 @@ const validateReleaseOrder = async ( where = { $and: [ - { seller_id: user._id }, + { seller_id: user._id.toString() }, { $or: [ { status: 'ACTIVE' }, @@ -570,7 +570,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() }, + ], + }, ], }; @@ -596,7 +601,7 @@ const validateFiatSentOrder = async ( try { const where: FilterQuery = { $and: [ - { buyer_id: user._id }, + { buyer_id: user._id.toString() }, { $or: [{ status: 'ACTIVE' }, { status: 'PAID_HOLD_INVOICE' }] }, ], }; @@ -631,7 +636,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', }; @@ -698,7 +703,7 @@ const validateUserWaitingOrder = async ( try { // If is a seller let where: FilterQuery = { - seller_id: user._id, + seller_id: user._id.toString(), status: 'WAITING_PAYMENT', }; let orders = await Order.find(where); @@ -708,7 +713,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); @@ -733,7 +738,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 6a35dd18..c9219bcf 100644 --- a/jobs/pending_payments.ts +++ b/jobs/pending_payments.ts @@ -8,12 +8,16 @@ import { getUserI18nContext } from '../util'; import { CommunityContext } from '../bot/modules/community/communityContext'; import { orderUpdated } from '../bot/modules/events/orders'; +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() }, @@ -162,7 +166,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 0434174e..fca1bffe 100644 --- a/models/community.ts +++ b/models/community.ts @@ -35,7 +35,8 @@ const usernameIdSchema = new Schema({ }); export interface ICommunity extends Document { - _id: string; + _id: Types.ObjectId; + id: string; name: string; creator_id: string; group: string; diff --git a/models/order.ts b/models/order.ts index 4cad7b51..264ea599 100644 --- a/models/order.ts +++ b/models/order.ts @@ -1,7 +1,8 @@ -import mongoose, { Document, Schema } from 'mongoose'; +import mongoose, { Document, Schema, Types } from 'mongoose'; export interface IOrder extends Document { - _id: string; + _id: Types.ObjectId; + id: string; description?: string; amount: number; max_amount: number; diff --git a/models/user.ts b/models/user.ts index 699091c8..89f15d0c 100644 --- a/models/user.ts +++ b/models/user.ts @@ -1,4 +1,4 @@ -import mongoose, { Document, Schema } from 'mongoose'; +import mongoose, { Document, Schema, Types } from 'mongoose'; interface UserReview { rating: number; @@ -6,7 +6,8 @@ interface UserReview { } export interface UserDocument extends Document { - _id: string; + _id: Types.ObjectId; + id: string; tg_id: string; username?: string; lang: string; diff --git a/package-lock.json b/package-lock.json index 13072753..2bb1dc43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "dotenv": "^10.0.0", "invoices": "2.0.6", "lightning": "10.25.0", - "mongoose": "^8.17.1", + "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": ">=18.0.0" + "node": ">=20.19.0" } }, "node_modules/@colors/colors": { @@ -331,9 +331,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", - "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", + "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -684,9 +684,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": "*" @@ -1441,12 +1441,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.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", + "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", "license": "Apache-2.0", "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" } }, "node_modules/buffer": { @@ -3695,13 +3695,10 @@ } }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", "engines": { "node": ">= 12" } @@ -4080,11 +4077,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4122,12 +4114,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": { @@ -4730,26 +4722,26 @@ "dev": true }, "node_modules/mongodb": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", - "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "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.1.9", - "bson": "^6.10.4", - "mongodb-connection-string-url": "^3.0.0" + "@mongodb-js/saslprep": "^1.3.0", + "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", - "snappy": "^7.2.2", - "socks": "^2.7.1" + "@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.8.6" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -4776,31 +4768,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.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", + "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", "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.17.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.1.tgz", - "integrity": "sha512-aodS4cacux5caoxB5ErEwRmrafIUsVRJxHnvP7URnSUnTenr32j1qBVV+KjYxryyLSisQkxglAFF69TNLeZTLg==", + "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.18.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", @@ -4816,15 +4810,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": { @@ -6424,11 +6415,12 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -6463,11 +6455,6 @@ "memory-pager": "^1.0.2" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", diff --git a/package.json b/package.json index 4683c48c..76c04285 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "lnp2pbot", "version": "0.15.0", "engines": { - "node": ">=18.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": "10.25.0", - "mongoose": "^8.17.1", + "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 f1492da5..0488ecf0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "strict": true, + "skipLibCheck": true, "esModuleInterop": true, "resolveJsonModule": true, "downlevelIteration": true, @@ -14,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 76a18e4c..5379bd6c 100644 --- a/util/communityHelper.ts +++ b/util/communityHelper.ts @@ -29,7 +29,7 @@ export const getCommunityInfo = async ( const regex = new RegExp(`^@${chat.username}$`, 'i'); community = await Community.findOne({ group: regex }); 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 d484dec6..c484c24b 100644 --- a/util/index.ts +++ b/util/index.ts @@ -37,7 +37,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) { @@ -454,7 +454,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