diff --git a/.gitignore b/.gitignore index e449eeb8..cb945275 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules .env admin.macaroon tls.cert -dist/ \ No newline at end of file +dist/ +.history/ \ No newline at end of file diff --git a/bot/commands.js b/bot/commands.js index ae768f2a..c0a26a4f 100644 --- a/bot/commands.js +++ b/bot/commands.js @@ -405,7 +405,8 @@ const showHoldInvoice = async (ctx, bot, order) => { request, amount, order.fiat_code, - order.fiat_amount + order.fiat_amount, + order.random_image ); } catch (error) { logger.error(`Error in showHoldInvoice: ${error}`); diff --git a/bot/messages.ts b/bot/messages.ts index 076ca17d..4a65ac7f 100644 --- a/bot/messages.ts +++ b/bot/messages.ts @@ -24,6 +24,7 @@ import { IConfig } from '../models/config'; import { IPendingPayment } from '../models/pending_payment'; import { PayViaPaymentRequestResult } from 'lightning'; import { IFiat } from '../util/fiatModel'; +import { generateQRWithImage } from '../util'; const startMessage = async (ctx: MainContext) => { try { @@ -86,9 +87,11 @@ const invoicePaymentRequestMessage = async ( rate, days: ageInDays, }); + await ctx.telegram.sendMessage(user.tg_id, message); + // Create QR code - const qrBytes = await QR.toBuffer(request); + const qrBytes = await generateQRWithImage(request, order.random_image); // Send payment request in QR and text await ctx.telegram.sendMediaGroup(user.tg_id, [ { @@ -107,13 +110,17 @@ const pendingSellMessage = async (ctx: Telegraf, user: UserDocument try { const orderExpirationWindow = Number(process.env.ORDER_PUBLISHED_EXPIRATION_WINDOW) / 60 / 60; - await ctx.telegram.sendMessage( - user.tg_id, - i18n.t('pending_sell', { + + await ctx.telegram.sendMediaGroup(user.tg_id, [{ + type: 'photo', + media: { source: Buffer.from(order.random_image, 'base64') }, + caption: `${i18n.t('pending_sell', { channel, orderExpirationWindow: Math.round(orderExpirationWindow), - }) + })}`, + }] ); + await ctx.telegram.sendMessage( user.tg_id, i18n.t('cancel_order_cmd', { orderId: order._id }), @@ -318,10 +325,14 @@ const beginTakeBuyMessage = async (ctx: MainContext, bot: Telegraf, try { const expirationTime = Number(process.env.HOLD_INVOICE_EXPIRATION_WINDOW) / 60; - await bot.telegram.sendMessage( - seller.tg_id, - ctx.i18n.t('begin_take_buy', { expirationTime }) + + await bot.telegram.sendMediaGroup(seller.tg_id, [{ + type: 'photo', + media: { source: Buffer.from(order.random_image, 'base64') }, + caption: `${ctx.i18n.t('begin_take_buy', { expirationTime })}`, + }] ); + await bot.telegram.sendMessage(seller.tg_id, order._id, { reply_markup: { inline_keyboard: [ @@ -348,7 +359,8 @@ const showHoldInvoiceMessage = async ( request: string, amount: number, fiatCode: IOrder["fiat_code"], - fiatAmount: IOrder["fiat_amount"] + fiatAmount: IOrder["fiat_amount"], + randomImage: IOrder["random_image"] ) => { try { let currency = getCurrency(fiatCode); @@ -356,6 +368,7 @@ const showHoldInvoiceMessage = async ( !!currency && !!currency.symbol_native ? currency.symbol_native : fiatCode; + await ctx.reply( ctx.i18n.t('pay_invoice', { amount: numberFormat(fiatCode, amount), @@ -363,8 +376,10 @@ const showHoldInvoiceMessage = async ( currency, }) ); + // Create QR code - const qrBytes = await QR.toBuffer(request); + const qrBytes = await generateQRWithImage(request, randomImage); + // Send payment request in QR and text await ctx.replyWithMediaGroup([ { @@ -1597,9 +1612,8 @@ const showConfirmationButtons = async (ctx: MainContext, orders: Array, }; }) .map(ord => ({ - text: `${ord._id.slice(0, 2)}..${ord._id.slice(-2)} - ${ord.type} - ${ - ord.fiat - } ${ord.amount}`, + text: `${ord._id.slice(0, 2)}..${ord._id.slice(-2)} - ${ord.type} - ${ord.fiat + } ${ord.amount}`, callback_data: `${commandString}_${ord._id}`, })); inlineKeyboard.push(lineBtn); diff --git a/bot/ordersActions.js b/bot/ordersActions.js index ba25bb39..98a48913 100644 --- a/bot/ordersActions.js +++ b/bot/ordersActions.js @@ -8,6 +8,7 @@ const { getFee, getUserAge, getStars, + generateRandomImage, } = require('../util'); const { logger } = require('../logger'); @@ -99,6 +100,10 @@ const createOrder = async ( } await order.save(); + const randomImage = await generateRandomImage(order._id); + order.random_image = randomImage; + await order.save(); + if (order.status !== 'PENDING') { OrderEvents.orderUpdated(order); } diff --git a/images/Ant.png b/images/Ant.png new file mode 100644 index 00000000..ab97319b Binary files /dev/null and b/images/Ant.png differ diff --git a/images/Aquarium.png b/images/Aquarium.png new file mode 100644 index 00000000..f7705ee5 Binary files /dev/null and b/images/Aquarium.png differ diff --git a/images/Badger.png b/images/Badger.png new file mode 100644 index 00000000..8dbcd295 Binary files /dev/null and b/images/Badger.png differ diff --git a/images/Bat Face.png b/images/Bat Face.png new file mode 100644 index 00000000..c8e211c3 Binary files /dev/null and b/images/Bat Face.png differ diff --git a/images/Bear.png b/images/Bear.png new file mode 100644 index 00000000..1daf0973 Binary files /dev/null and b/images/Bear.png differ diff --git a/images/Beaver.png b/images/Beaver.png new file mode 100644 index 00000000..f4deeb05 Binary files /dev/null and b/images/Beaver.png differ diff --git a/images/Bee.png b/images/Bee.png new file mode 100644 index 00000000..bd63577c Binary files /dev/null and b/images/Bee.png differ diff --git a/images/Bird.png b/images/Bird.png new file mode 100644 index 00000000..637ca588 Binary files /dev/null and b/images/Bird.png differ diff --git a/images/Bug.png b/images/Bug.png new file mode 100644 index 00000000..64a4a5b1 Binary files /dev/null and b/images/Bug.png differ diff --git a/images/Bull.png b/images/Bull.png new file mode 100644 index 00000000..74725f3d Binary files /dev/null and b/images/Bull.png differ diff --git a/images/Bumblebee.png b/images/Bumblebee.png new file mode 100644 index 00000000..04f0cb53 Binary files /dev/null and b/images/Bumblebee.png differ diff --git a/images/Butterfly.png b/images/Butterfly.png new file mode 100644 index 00000000..9467cb2f Binary files /dev/null and b/images/Butterfly.png differ diff --git a/images/Cat Footprint.png b/images/Cat Footprint.png new file mode 100644 index 00000000..47fef249 Binary files /dev/null and b/images/Cat Footprint.png differ diff --git a/images/Cat.png b/images/Cat.png new file mode 100644 index 00000000..941e492a Binary files /dev/null and b/images/Cat.png differ diff --git a/images/Caterpillar.png b/images/Caterpillar.png new file mode 100644 index 00000000..df11e512 Binary files /dev/null and b/images/Caterpillar.png differ diff --git a/images/Chicken.png b/images/Chicken.png new file mode 100644 index 00000000..b5ddad60 Binary files /dev/null and b/images/Chicken.png differ diff --git a/images/Clown Fish.png b/images/Clown Fish.png new file mode 100644 index 00000000..d5eacd78 Binary files /dev/null and b/images/Clown Fish.png differ diff --git a/images/Corgi.png b/images/Corgi.png new file mode 100644 index 00000000..c47c52d8 Binary files /dev/null and b/images/Corgi.png differ diff --git a/images/Cow.png b/images/Cow.png new file mode 100644 index 00000000..6c1fed37 Binary files /dev/null and b/images/Cow.png differ diff --git a/images/Crab.png b/images/Crab.png new file mode 100644 index 00000000..756ae40d Binary files /dev/null and b/images/Crab.png differ diff --git a/images/Deer.png b/images/Deer.png new file mode 100644 index 00000000..7ab4d1d8 Binary files /dev/null and b/images/Deer.png differ diff --git a/images/Dinosaur.png b/images/Dinosaur.png new file mode 100644 index 00000000..6fda96ac Binary files /dev/null and b/images/Dinosaur.png differ diff --git a/images/Dog Park.png b/images/Dog Park.png new file mode 100644 index 00000000..3ce1384f Binary files /dev/null and b/images/Dog Park.png differ diff --git a/images/Dog.png b/images/Dog.png new file mode 100644 index 00000000..57341688 Binary files /dev/null and b/images/Dog.png differ diff --git a/images/Dolphin.png b/images/Dolphin.png new file mode 100644 index 00000000..f009e1a9 Binary files /dev/null and b/images/Dolphin.png differ diff --git a/images/Dragonfly.png b/images/Dragonfly.png new file mode 100644 index 00000000..8bcf8064 Binary files /dev/null and b/images/Dragonfly.png differ diff --git a/images/Duck.png b/images/Duck.png new file mode 100644 index 00000000..c8660192 Binary files /dev/null and b/images/Duck.png differ diff --git a/images/Elephant.png b/images/Elephant.png new file mode 100644 index 00000000..be17ee63 Binary files /dev/null and b/images/Elephant.png differ diff --git a/images/Falcon.png b/images/Falcon.png new file mode 100644 index 00000000..041f5c91 Binary files /dev/null and b/images/Falcon.png differ diff --git a/images/Fish Food.png b/images/Fish Food.png new file mode 100644 index 00000000..9363afa8 Binary files /dev/null and b/images/Fish Food.png differ diff --git a/images/Fish.png b/images/Fish.png new file mode 100644 index 00000000..f4ed23a4 Binary files /dev/null and b/images/Fish.png differ diff --git a/images/Fly.png b/images/Fly.png new file mode 100644 index 00000000..ca818ce4 Binary files /dev/null and b/images/Fly.png differ diff --git a/images/Frog.png b/images/Frog.png new file mode 100644 index 00000000..2fffe0ec Binary files /dev/null and b/images/Frog.png differ diff --git a/images/Giraffe.png b/images/Giraffe.png new file mode 100644 index 00000000..b8c8327f Binary files /dev/null and b/images/Giraffe.png differ diff --git a/images/Gorilla.png b/images/Gorilla.png new file mode 100644 index 00000000..bd6924b2 Binary files /dev/null and b/images/Gorilla.png differ diff --git a/images/Grasshopper.png b/images/Grasshopper.png new file mode 100644 index 00000000..81aa181f Binary files /dev/null and b/images/Grasshopper.png differ diff --git a/images/Hornet Hive.png b/images/Hornet Hive.png new file mode 100644 index 00000000..49f9f7a6 Binary files /dev/null and b/images/Hornet Hive.png differ diff --git a/images/Hornet.png b/images/Hornet.png new file mode 100644 index 00000000..99f733da Binary files /dev/null and b/images/Hornet.png differ diff --git a/images/Horse.png b/images/Horse.png new file mode 100644 index 00000000..a9563a09 Binary files /dev/null and b/images/Horse.png differ diff --git a/images/Hummingbird.png b/images/Hummingbird.png new file mode 100644 index 00000000..dc30998a Binary files /dev/null and b/images/Hummingbird.png differ diff --git a/images/Icons8 Logo.png b/images/Icons8 Logo.png new file mode 100644 index 00000000..577ec4e1 Binary files /dev/null and b/images/Icons8 Logo.png differ diff --git a/images/Insect.png b/images/Insect.png new file mode 100644 index 00000000..76555fa1 Binary files /dev/null and b/images/Insect.png differ diff --git a/images/Kangaroo.png b/images/Kangaroo.png new file mode 100644 index 00000000..ec781894 Binary files /dev/null and b/images/Kangaroo.png differ diff --git a/images/Kiwi Bird.png b/images/Kiwi Bird.png new file mode 100644 index 00000000..6585060a Binary files /dev/null and b/images/Kiwi Bird.png differ diff --git a/images/Ladybird.png b/images/Ladybird.png new file mode 100644 index 00000000..3e5ccc43 Binary files /dev/null and b/images/Ladybird.png differ diff --git a/images/Leopard.png b/images/Leopard.png new file mode 100644 index 00000000..4c14fd7c Binary files /dev/null and b/images/Leopard.png differ diff --git a/images/Lion.png b/images/Lion.png new file mode 100644 index 00000000..a79cc25a Binary files /dev/null and b/images/Lion.png differ diff --git a/images/Llama.png b/images/Llama.png new file mode 100644 index 00000000..ade6a6e1 Binary files /dev/null and b/images/Llama.png differ diff --git a/images/Mite.png b/images/Mite.png new file mode 100644 index 00000000..e09455fe Binary files /dev/null and b/images/Mite.png differ diff --git a/images/Mosquito.png b/images/Mosquito.png new file mode 100644 index 00000000..a5d0aed5 Binary files /dev/null and b/images/Mosquito.png differ diff --git a/images/Octopus.png b/images/Octopus.png new file mode 100644 index 00000000..41c2e829 Binary files /dev/null and b/images/Octopus.png differ diff --git a/images/Panda.png b/images/Panda.png new file mode 100644 index 00000000..bbe93903 Binary files /dev/null and b/images/Panda.png differ diff --git a/images/Pig With Lipstick.png b/images/Pig With Lipstick.png new file mode 100644 index 00000000..c341572d Binary files /dev/null and b/images/Pig With Lipstick.png differ diff --git a/images/Pig.png b/images/Pig.png new file mode 100644 index 00000000..7afaa0e4 Binary files /dev/null and b/images/Pig.png differ diff --git a/images/Prawn.png b/images/Prawn.png new file mode 100644 index 00000000..2244f983 Binary files /dev/null and b/images/Prawn.png differ diff --git a/images/Puffin Bird.png b/images/Puffin Bird.png new file mode 100644 index 00000000..3b4d7648 Binary files /dev/null and b/images/Puffin Bird.png differ diff --git a/images/Rabbit.png b/images/Rabbit.png new file mode 100644 index 00000000..ced6a4cf Binary files /dev/null and b/images/Rabbit.png differ diff --git a/images/Rhinoceros.png b/images/Rhinoceros.png new file mode 100644 index 00000000..243ff7f8 Binary files /dev/null and b/images/Rhinoceros.png differ diff --git a/images/Seahorse.png b/images/Seahorse.png new file mode 100644 index 00000000..a67c1cef Binary files /dev/null and b/images/Seahorse.png differ diff --git a/images/Shark.png b/images/Shark.png new file mode 100644 index 00000000..28c28d92 Binary files /dev/null and b/images/Shark.png differ diff --git a/images/Sheep.png b/images/Sheep.png new file mode 100644 index 00000000..8473aca3 Binary files /dev/null and b/images/Sheep.png differ diff --git a/images/Snail.png b/images/Snail.png new file mode 100644 index 00000000..90ccf488 Binary files /dev/null and b/images/Snail.png differ diff --git a/images/Spider.png b/images/Spider.png new file mode 100644 index 00000000..224a9f5a Binary files /dev/null and b/images/Spider.png differ diff --git a/images/Starfish.png b/images/Starfish.png new file mode 100644 index 00000000..ba6532dc Binary files /dev/null and b/images/Starfish.png differ diff --git a/images/Stork.png b/images/Stork.png new file mode 100644 index 00000000..1fe3c2cf Binary files /dev/null and b/images/Stork.png differ diff --git a/images/Tentacles.png b/images/Tentacles.png new file mode 100644 index 00000000..62c8625a Binary files /dev/null and b/images/Tentacles.png differ diff --git a/images/Turtle.png b/images/Turtle.png new file mode 100644 index 00000000..7b536159 Binary files /dev/null and b/images/Turtle.png differ diff --git a/images/Unicorn.png b/images/Unicorn.png new file mode 100644 index 00000000..fb33e71a Binary files /dev/null and b/images/Unicorn.png differ diff --git a/images/Wasp.png b/images/Wasp.png new file mode 100644 index 00000000..9c64088e Binary files /dev/null and b/images/Wasp.png differ diff --git a/images/Whale.png b/images/Whale.png new file mode 100644 index 00000000..58d8a88f Binary files /dev/null and b/images/Whale.png differ diff --git a/images/Wolf.png b/images/Wolf.png new file mode 100644 index 00000000..2da6e66f Binary files /dev/null and b/images/Wolf.png differ diff --git a/locales/de.yaml b/locales/de.yaml index d3182561..6da2d499 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -38,12 +38,16 @@ invoice_payment_request: | Buyer Reputation: ${rate}, Tage mit dem Bot: ${days} + Hinweis: Vergewissern Sie sich vor der Zahlung der Rechnung, dass das beigefügte Bild mit dem bei der Bestellung gesendeten Bild übereinstimmt + Bitte bezahle diese LN-Rechnung, um deinen Verkaufsprozess zu starten. Diese LN-Rechnung läuft in ${expirationTime} Minuten ab pending_sell: | 📝 Dein Angebot wurde im Kanal ${channel} veröffentlicht Du musst warten, bis ein anderer Nutzer deinen Auftrag auswählt. Sie wird für ${orderExpirationWindow} Stunden im Kanal verfügbar sein + Hinweis: Merken Sie sich dieses Bild, da Sie es später in der Zahlungsrechnung wiedersehen werden + Bevor ein anderer Benutzer dein Angebot annimmt, kannst du diesen mit dem folgenden Befehl stornieren 👇 cancel_order_cmd: | /cancel ${orderId} @@ -100,9 +104,14 @@ order_already_settled: Diese Bestellung wurde bereits abgewickelt. invalid_data: Du hast ungültige Daten gesendet, versuche es erneut. begin_take_buy: | 🤖 Drücke auf Weiter, um das Angebot anzunehmen. Wenn du auf Abbrechen drückst, wird der Auftrag freigegeben und neu veröffentlicht. Du hast ${expirationTime} Minuten, bevor dieser Auftrag abläuft. + + Hinweis: Merken Sie sich dieses Bild, da Sie es später in der Zahlungsrechnung wiedersehen werden continue: Weiter cancel: Abbrechen -pay_invoice: Bitte bezahle diese LN-Rechnung von ${amount} sats für ${currency} ${fiatAmount}, um den Vorgang zu starten. +pay_invoice: | + Hinweis: Vergewissern Sie sich vor der Zahlung der Rechnung, dass das beigefügte Bild mit dem bei der Bestellung gesendeten Bild übereinstimmt + + Bitte bezahle diese LN-Rechnung von ${amount} sats für ${currency} ${fiatAmount}, um den Vorgang zu starten. payment_received: | 🤑 Zahlung erhalten! diff --git a/locales/en.yaml b/locales/en.yaml index a9d73982..44bc717e 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -40,12 +40,16 @@ invoice_payment_request: | Buyer Reputation: ${rate}, days using the bot: ${days} + Note: Confirm that the attached image matches the one sent during order creation before paying the invoice + Please pay this invoice to start up your selling process, it will expire in ${expirationTime} minutes pending_sell: | 📝 Your offer has been published in the ${channel} channel You have to wait until another user picks your order, it will be available for ${orderExpirationWindow} hours in the channel + Note: Remember this image because you will see it again inside the invoice to pay + You can cancel this order before another user picks it up by executing the command 👇 cancel_order_cmd: | /cancel ${orderId} @@ -101,10 +105,17 @@ order_already_taken: This order has already been taken by another user. order_already_settled: This order has already been settled. invalid_data: You have sent invalid data, try again. begin_take_buy: | - 🤖 Press Continue to take the offer, if you press Cancel, you will be released from the order and it will be republished. You have ${expirationTime} minutes before this order expires. 👇 + 🤖 Press Continue to take the offer, if you press Cancel, you will be released from the order and it will be republished. + + Note: Remember this image because you will see it again inside the invoice to pay + + You have ${expirationTime} minutes before this order expires. 👇 continue: Continue cancel: Cancel -pay_invoice: Please pay this invoice of ${amount} sats for ${currency} ${fiatAmount} to start the operation. +pay_invoice: | + Note: Confirm that the attached image matches the one sent during order creation before paying the invoice + + Please pay this invoice of ${amount} sats for ${currency} ${fiatAmount} to start the operation payment_received: | 🤑 Payment received! diff --git a/locales/es.yaml b/locales/es.yaml index 26cbc22a..38c667f7 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -38,12 +38,16 @@ invoice_payment_request: | Reputación del comprador: ${rate}, días utilizando el bot: ${days} + Nota: Confirme que la imagen adjunta coincide con la enviada durante la creación del pedido antes de pagar la factura + Si deseas continuar por favor paga esta factura, esta factura expira en ${expirationTime} minutos pending_sell: | 📝 Publicada la oferta en el canal ${channel} Espera que alguien tome tu venta, si la orden no es tomada en ${orderExpirationWindow} horas será borrada del canal. + Nota: Recuerde esta imagen porque la verá nuevamente dentro de la factura a pagar + Puedes cancelar esta orden antes de que alguien la tome ejecutando 👇 cancel_order_cmd: | /cancel ${orderId} @@ -100,9 +104,14 @@ order_already_settled: Esta orden ya ha sido liquidada. invalid_data: Has enviado datos incorrectos, inténtalo nuevamente. begin_take_buy: | 🤖 Presiona Continuar para tomar la oferta, si presionas Cancelar te desvincularé de la orden y será publicada nuevamente, tienes ${expirationTime} minutos o la orden expirará 👇 + + Nota: Recuerde esta imagen porque la verá nuevamente dentro de la factura a pagar continue: Continuar cancel: Cancelar -pay_invoice: Por favor paga esta factura de ${amount} sats equivalente a ${currency} ${fiatAmount} para comenzar la operación. +pay_invoice: | + Por favor paga esta factura de ${amount} sats equivalente a ${currency} ${fiatAmount} para comenzar la operación. + + Nota: Confirme que la imagen adjunta coincide con la enviada durante la creación del pedido antes de pagar la factura payment_received: | 🤑 ¡Pago recibido! diff --git a/locales/fa.yaml b/locales/fa.yaml index 64fa703a..a3c9d80a 100644 --- a/locales/fa.yaml +++ b/locales/fa.yaml @@ -40,12 +40,16 @@ invoice_payment_request: | Buyer Reputation: ${rate}, days using the bot: ${days} + توجه: قبل از پرداخت فاکتور، تأیید کنید که تصویر پیوست شده با تصویر ارسال شده در هنگام ایجاد سفارش مطابقت دارد + لطفاً برای شروع فرآیند فروش خود، این فاکتور را بپردازید، این درخواست در ${expirationTime} دقیقه منقضی می شود pending_sell: | 📝 سفارش فروش sat شما در کانال ${channel} منتشر شده است باید منتظر بمانید تا کاربر دیگری سفارش شما را انتخاب کند، این سفارش برای ${orderExpirationWindow} ساعت در کانال در دسترس خواهد بود. + توجه: این تصویر را به خاطر بسپارید زیرا آن را دوباره در فاکتور پرداخت خواهید دید + شما می توانید این سفارش را قبل از اینکه کاربر دیگری آن را انتخاب کند با اجرای دستور زیر لغو کنید 👇 cancel_order_cmd: | /cancel ${orderId} @@ -102,9 +106,14 @@ order_already_settled: این سفارش قبلا تسویه شده است. invalid_data: داده های ارسال شده نامعتبر اند، دوباره امتحان کنید. begin_take_buy: | 🤖 برای دریافت سفارش، ادامه را فشار دهید، اگر لغو را فشار دهید، از معامله خارج می شوید و سفارش مجدداً منتشر می شود. شما ${expirationTime} دقیقه قبل از انقضای این سفارش فرصت دارید. 👇 + + توجه: این تصویر را به خاطر بسپارید زیرا آن را دوباره در فاکتور پرداخت خواهید دید continue: ادامه cancel: لغو -pay_invoice: لطفاً برای شروع عملیات، فاکتور ${amount} sat را برای ${currency} {fiatAmount} بپردازید. +pay_invoice: | + توجه: قبل از پرداخت فاکتور، تأیید کنید که تصویر پیوست شده با تصویر ارسال شده در هنگام ایجاد سفارش مطابقت دارد + + لطفاً برای شروع عملیات، فاکتور ${amount} sat را برای ${currency} {fiatAmount} بپردازید. payment_received: | 🤑 پرداخت شد! diff --git a/locales/fr.yaml b/locales/fr.yaml index 7fe68d21..7d897cce 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -40,12 +40,16 @@ invoice_payment_request: | Buyer Reputation: ${rate}, jours d'utilisation du bot: ${days} + Note : Vérifiez que l'image jointe correspond à celle envoyée lors de la création de la commande avant de payer la facture + Merci de régler cette facture pour démarrer le processus de vente, elle expirera dans ${expirationTime} minutes pending_sell: | 📝 Ton offre a été publiée dans le canal ${channel} Tu dois attendre jusqu'à ce que quelqu'un récupère ton offre, elle sera visible pendant ${orderExpirationWindow} heures dans le canal + Note : Mémorisez cette image car vous la reverrez dans la facture à payer + Tu peux annuler cette offre avant qu'un autre utilisateur ne la prenne en exécutant la commande 👇 cancel_order_cmd: | /cancel ${orderId} @@ -102,9 +106,14 @@ order_already_settled: Cette commande a déjà été réglée. invalid_data: Tu as envoyé des données invalides, merci de réessayer. begin_take_buy: | 🤖 Appuie sur Continuer pour prendre l'offre, si tu clique sur Annuler, tu seras libéré.e de l'offre et elle sera publiée de nouveau. Tu as ${expirationTime} minutes avant que cette offre n'expire. 👇 + + Note : Mémorisez cette image car vous la reverrez dans la facture à payer continue: Continuer cancel: Annuler -pay_invoice: Merci de payer cette facture de ${amount} sats à ${fiatAmount} ${currency} pour démarrer la transaction. +pay_invoice: | + Note : Vérifiez que l'image jointe correspond à celle envoyée lors de la création de la commande avant de payer la facture + + Merci de payer cette facture de ${amount} sats à ${fiatAmount} ${currency} pour démarrer la transaction. payment_received: | 🤑 Paiement reçu! diff --git a/locales/it.yaml b/locales/it.yaml index b46301fe..96c11993 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -38,12 +38,16 @@ invoice_payment_request: | Reputazione del compratore: ${rate}, giorni di utilizzo del bot: ${days} + Nota: Conferma che l'immagine allegata corrisponda a quella inviata durante la creazione dell'ordine prima di pagare la fattura + Si prega di procedere al pagamento di questa invoice per avviare il processo di vendita, la invoice scadrà tra ${expirationTime} minuti pending_sell: | 📝 La tua offerta è stata pubblicata nel canale ${channel}. È necessario attendere che un altro utente prenda in carico l'ordine, che scadrà tra ${orderExpirationWindow} ore. + Nota: Ricorda questa immagine perché la rivedrai nella fattura da pagare + È possibile annullare l'ordine prima che un altro utente lo prenda eseguendo il comando 👇 cancel_order_cmd: | /cancel ${orderId} @@ -99,10 +103,15 @@ order_already_taken: Quest'ordine è già stato preso da un altro utente. order_already_settled: Questo ordine è già stato regolato. invalid_data: I dati inviati non sono validi, riprovare. begin_take_buy: | + Nota: Ricorda questa immagine perché la rivedrai nella fattura da pagare + 🤖 Premere Continua per accettare l'ordine, se premi Cancella verrai svincolato dall'ordine e sarà ripubblicato. Hai ${expirationTime} minuti prima che l'ordine scada. 👇 continue: Continua cancel: Cancella -pay_invoice: Si prega di pagare questa invoice di ${amount} sats per ${currency} ${fiatAmount} per iniziare lo scambio. +pay_invoice: | + Nota: Conferma che l'immagine allegata corrisponda a quella inviata durante la creazione dell'ordine prima di pagare la fattura + + Si prega di pagare questa invoice di ${amount} sats per ${currency} ${fiatAmount} per iniziare lo scambio. payment_received: | 🤑 Pagamento ricevuto! diff --git a/locales/ko.yaml b/locales/ko.yaml index cd019dab..70db4066 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -38,12 +38,16 @@ invoice_payment_request: | 누군가 당신에게 ${order.amount} sats를 ${currency} ${order.fiat_amount}로 구매하기를 희망합니다. Buyer Reputation: ${rate}, 봇 사용일수: ${days} + + 참고: 청구서 결제 전에 첨부된 이미지가 주문 생성 시 전송된 이미지와 일치하는지 확인하세요 판매 프로세스를 시작하려면 이 인보이스에 결제해주세요. 이 인보이스는 ${expirationTime} 분 후에 만료됩니다. pending_sell: | 📝 당신의 주문이 ${channel} 채널에 등록되었습니다. 다른 사용자가 당신의 주문을 수락할 때까지 기다려야 합니다. 주문은 ${orderExpirationWindow} 시간 동안 채널에서 보여집니다. + + 참고: 결제 청구서에서 다시 보게 될 이미지이므로 기억해 두세요 당신은 다른 사용자가 이 주문을 수락하기 전에 아래 명령어를 입력하여 취소할 수 있습니다.👇 cancel_order_cmd: | @@ -101,9 +105,14 @@ order_already_settled: 이 주문은 이미 정산되었습니다. invalid_data: 유효하지 않은 데이터를 보냈습니다. 다시 시도해보세요. begin_take_buy: | 🤖 주문을 수락하려면 `계속` 버튼을 눌러 주세요. `취소` 버튼을 누르면, 당신은 해당 주문에서 나오게 되고, 주문은 마켓 채널에 재등록됩니다. 주문이 만료되기까지 ${expirationTime}분 남았습니다. 👇 + + 참고: 결제 청구서에서 다시 보게 될 이미지이므로 기억해 두세요 continue: 계속 cancel: 취소 -pay_invoice: 거래를 시작하기 위해 ${currency} ${fiatAmount}에 해당되는 ${amount} 사토시를 이 라이트닝 인보이스에 지불해 주세요. +pay_invoice: | + 참고: 청구서 결제 전에 첨부된 이미지가 주문 생성 시 전송된 이미지와 일치하는지 확인하세요 + + 거래를 시작하기 위해 ${currency} ${fiatAmount}에 해당되는 ${amount} 사토시를 이 라이트닝 인보이스에 지불해 주세요. payment_received: | 🤑 결제가 확인되었습니다. diff --git a/locales/pt.yaml b/locales/pt.yaml index 21c518e0..63216089 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -37,11 +37,15 @@ invoice_payment_request: | Reputação do comprador: ${rate}, dias usando o bot: ${days} + Nota: Confirme que a imagem anexada corresponde à enviada durante a criação do pedido antes de pagar a fatura + Por favor, pague esta fatura para iniciar seu processo de venda, a fatura expirará em ${expirationTime} minutos pending_sell: | 📝 Oferta publicada no canal ${channel} Você tem que esperar até que outro usuário aceite sua oferta, ela estará disponível por ${orderExpirationWindow} horas no canal + + Nota: Lembre-se desta imagem porque você a verá novamente dentro da fatura a pagar Você pode cancelar a oferta antes que outro usuário a aceite executando o comando 👇 cancel_order_cmd: | @@ -99,9 +103,14 @@ order_already_settled: Esta ordem já foi liquidada. invalid_data: Você enviou dados inválidos, tente novamente. begin_take_buy: | 🤖 Pressione Continue para aceitar a oferta, se você pressionar Cancel, desvincularei você do pedido e ele será publicado novamente. Você tem ${expirationTime} minutos ou o pedido expirará. 👇 + + Nota: Lembre-se desta imagem porque você a verá novamente dentro da fatura a pagar continue: Continuar cancel: Cancelar -pay_invoice: Por favor, pague esta fatura de ${amount} sats por ${currency} ${fiatAmount} para iniciar a operação. +pay_invoice: | + Nota: Confirme que a imagem anexada corresponde à enviada durante a criação do pedido antes de pagar a fatura + + Por favor, pague esta fatura de ${amount} sats por ${currency} ${fiatAmount} para iniciar a operação. payment_received: | 🤑 Pagamento recebido! diff --git a/locales/ru.yaml b/locales/ru.yaml index 32c5c2d2..293e0397 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -37,12 +37,16 @@ invoice_payment_request: | Репутация продавца: ${rate}, дней использования бота: ${days} + Примечание: Перед оплатой счета подтвердите, что прикрепленное изображение совпадает с изображением, отправленным при создании заказа + Пожалуйста, оплатите этот счет, чтобы начать продажу. Срок действия этого счета истекает через ${expirationTime} минут pending_sell: | 📝 Разместил заявку в канале ${channel} Подождите, пока кто-нибудь примет вашу заявку на продажу. Если заявка не будет принята в течение ${orderExpirationWindow} часов, она будет удалена из канала автоматически. + Примечание: Запомните это изображение, так как вы увидите его снова в счете на оплату + Вы можете отменить эту заявку до того, как ее примет кто-то другой, командой 👇 cancel_order_cmd: /cancel ${orderId} pending_buy: | @@ -99,9 +103,14 @@ order_already_settled: Этот заказ уже урегулирован. invalid_data: Вы отправили некорректные данные. Попробуйте заново. begin_take_buy: | 🤖 Нажмите «Продолжить», чтобы принять предложение. Если вы нажмете «Отмена», заявка будет снова опубликована для всех. У вас есть ${expirationTime} минут для исполнения заявки. 👇 + + Примечание: Запомните это изображение, так как вы увидите его снова в счете на оплату continue: Продолжить cancel: Отменить -pay_invoice: Пожалуйста, оплатите этот счет на ${amount} сат за ${currency} ${fiatAmount} для начала сделки. +pay_invoice: | + Примечание: Перед оплатой счета подтвердите, что прикрепленное изображение совпадает с изображением, отправленным при создании заказа + + Пожалуйста, оплатите этот счет на ${amount} сат за ${currency} ${fiatAmount} для начала сделки. payment_received: | 🤑 Платеж получен! diff --git a/locales/uk.yaml b/locales/uk.yaml index 60ef8e2c..c72695c5 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -36,6 +36,8 @@ invoice_payment_request: | Хтось хоче купити Ваші ${order.amount} Сатоші за ${currency} ${order.fiat_amount}. Репутація продавця: ${rate}, днів використання бота: ${days} + + Примітка: Перед оплатою рахунку підтвердьте, що прикріплене зображення збігається із зображенням, надісланим під час створення замовлення Будь ласка, сплатіть цей рахунок, щоб почати продаж. Термін дії цього рахунку закінчується через ${expirationTime} хвилин pending_sell: | @@ -43,6 +45,8 @@ pending_sell: | Зачекайте, поки хтось прийме Вашу заявку на продаж. Якщо заявка не буде прийнята протягом ${orderExpirationWindow} годин, вона буде видалена з каналу автоматично. + Примітка: Запам'ятайте це зображення, оскільки ви побачите його знову в рахунку на оплату + Ви можете скасувати цю заявку до того, як її прийме хтось інший командою 👇 cancel_order_cmd: | /cancel ${orderId} @@ -99,9 +103,14 @@ order_already_settled: Це замовлення вже врегульовано invalid_data: Ви надіслали неправильні дані. Спробуйте ще раз. begin_take_buy: | 🤖 Натисніть "Продовжити", щоб прийняти пропозицію. Якщо Ви натиснете "Скасувати", заявка буде знову опублікована для всіх. Ви маєте ${expirationTime} хвилин для виконання заявки. 👇 + + Примітка: Запам'ятайте це зображення, оскільки ви побачите його знову в рахунку на оплату continue: Продовжити cancel: Відмінити -pay_invoice: Будь ласка, сплатіть цей рахунок з ${amount} сатоші у вибраній ${currency} ${fiatAmount} для початку операції. +pay_invoice: | + Примітка: Перед оплатою рахунку підтвердьте, що прикріплене зображення збігається із зображенням, надісланим під час створення замовлення + + Будь ласка, сплатіть цей рахунок з ${amount} сатоші у вибраній ${currency} ${fiatAmount} для початку операції. payment_received: | 🤑 Платіж отриманий! diff --git a/models/order.ts b/models/order.ts index 8441aa9d..71fc7ec5 100644 --- a/models/order.ts +++ b/models/order.ts @@ -44,6 +44,7 @@ export interface IOrder extends Document { community_id: string; is_frozen: boolean; is_public: boolean; + random_image: string; } const orderSchema = new Schema({ @@ -138,6 +139,7 @@ const orderSchema = new Schema({ community_id: { type: String }, is_public: { type: Boolean, default: true }, is_frozen: { type: Boolean, default: false }, + random_image: { type: String }, }); export default mongoose.model('Order', orderSchema); diff --git a/package-lock.json b/package-lock.json index a43054ab..e32f4ec7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@grammyjs/i18n": "^0.5.1", "@grammyjs/ratelimiter": "^1.1.5", "axios": "^1.7.4", + "canvas": "^3.0.0", "crypto": "^1.0.1", "dotenv": "^10.0.0", "invoices": "2.0.6", @@ -2282,6 +2283,17 @@ "node": ">=8.0.0" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", @@ -2524,6 +2536,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/canvas": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.0.0.tgz", + "integrity": "sha512-NtcIBY88FjymQy+g2g5qnuP5IslrbWCQ3A6rSr1PeuYxVRapRZ3BZCrDyAakvI6CuDYidgZaf55ygulFVwROdg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "simple-get": "^3.0.3" + }, + "engines": { + "node": "^18.12.0 || >= 20.9.0" + } + }, "node_modules/cbor": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.0.tgz", @@ -2605,6 +2632,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2874,6 +2907,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -2890,7 +2935,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, "engines": { "node": ">=4.0.0" } @@ -2959,6 +3003,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -3029,6 +3082,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-abstract": { "version": "1.23.4", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.4.tgz", @@ -3679,6 +3741,15 @@ "node": ">=6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -4006,6 +4077,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4099,6 +4176,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -4399,6 +4482,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -5175,6 +5264,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5195,6 +5296,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/mocha": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", @@ -5438,6 +5545,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5503,6 +5616,36 @@ "node": ">=4" } }, + "node_modules/node-abi": { + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -5781,7 +5924,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -5952,6 +6094,84 @@ "node": ">= 0.4" } }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6097,6 +6317,16 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6318,6 +6548,30 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6717,6 +6971,37 @@ "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -6967,6 +7252,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/telegraf": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.8.0.tgz", @@ -7381,6 +7694,18 @@ "resolved": "https://registry.npmjs.org/tstl/-/tstl-2.5.16.tgz", "integrity": "sha512-+O2ybLVLKcBwKm4HymCEwZIT0PpwS3gCYnxfSDEjJEKADvIFruaQjd3m7CAKNU1c7N3X3WjVz87re7TA2A5FUw==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", @@ -7830,8 +8155,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index 89a4f469..95f53124 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@grammyjs/i18n": "^0.5.1", "@grammyjs/ratelimiter": "^1.1.5", "axios": "^1.7.4", + "canvas": "^3.0.0", "crypto": "^1.0.1", "dotenv": "^10.0.0", "invoices": "2.0.6", diff --git a/util/index.ts b/util/index.ts index 7ca1bef5..25ca9d97 100644 --- a/util/index.ts +++ b/util/index.ts @@ -10,7 +10,12 @@ import fiatJson from './fiat.json'; import languagesJson from './languages.json'; import { Order, Community } from "../models"; import { logger } from "../logger"; +const fs = require('fs').promises; +const path = require('path'); const { I18n } = require('@grammyjs/i18n'); + +const QRCode = require('qrcode'); +const { Image, createCanvas } = require('canvas'); // ISO 639-1 language codes const languages: ILanguages = languagesJson; @@ -225,7 +230,7 @@ const decimalRound = (value: number, exp: number): number => { const extractId = (text: string): (string | null) => { const matches = text.match(/:([a-f0-9]{24}):$/); - if (matches !== null){ + if (matches !== null) { return matches?.[1]; } return null; @@ -380,8 +385,8 @@ const getDetailedOrder = (i18n: I18nContext, order: IOrder, buyer: UserDocument, const fee = order.fee ? sanitizeMD(Number(order.fee)) : ''; const creator = order.creator_id === buyerId ? buyerUsername : sellerUsername; - const buyerAge = buyer? getUserAge(buyer) : ''; - const sellerAge = seller? getUserAge(seller) : ''; + const buyerAge = buyer ? getUserAge(buyer) : ''; + const sellerAge = seller ? getUserAge(seller) : ''; const buyerTrades = buyer ? buyer.trades_completed : 0; const sellerTrades = seller ? seller.trades_completed : 0; const message = i18n.t('order_detail', { @@ -525,6 +530,44 @@ export const removeLightningPrefix = (invoice: string) => { return invoice; }; +const generateRandomImage = async (nonce: string) => { + let randomImage = ''; + try { + const files = await fs.readdir('images'); + const imageFiles = files.filter(file => + ['.png'].includes(path.extname(file).toLowerCase()) + ); + + const randomFile = imageFiles[Math.floor(Math.random() * imageFiles.length)]; + const fallbackImage = await fs.readFile(`images/${randomFile}`); + + randomImage = Buffer.from(fallbackImage, 'binary').toString('base64'); + } catch (fallbackError) { + logger.error(fallbackError); + } + + return randomImage; +} + +const generateQRWithImage = async (request, randomImage) => { + const canvas = createCanvas(400, 400); + await QRCode.toCanvas(canvas, request, { + margin: 2, + width: 400, + }); + + const ctx = canvas.getContext('2d'); + const centerImage = new Image(); + centerImage.src = `data:image/png;base64,${randomImage}`; + + const imageSize = canvas.width * 0.3; + const imagePos = (canvas.width - imageSize) / 2; + + ctx.drawImage(centerImage, imagePos, imagePos, imageSize, imageSize); + + return canvas.toBuffer(); +}; + export { isIso4217, plural, @@ -555,5 +598,7 @@ export { getUserAge, getTimeToExpirationOrder, toKebabCase, - isOrderCreator + isOrderCreator, + generateRandomImage, + generateQRWithImage, };