diff --git a/src/handlers/userHandlers/userDeletionHandler.js b/src/handlers/userHandlers/userDeletionHandler.js index cf9117a..fcfd3fc 100644 --- a/src/handlers/userHandlers/userDeletionHandler.js +++ b/src/handlers/userHandlers/userDeletionHandler.js @@ -4,10 +4,10 @@ import bot from "../../context/bot.js"; import UserService from "../../services/userService.js"; import userStates from "../../context/userStates.js"; import logger from '../../utils/logger.js'; +import { editOrSendCallback } from '../../utils/messageUtils.js'; export default class UserDeletionHandler { static async handleDeleteAccount(callbackQuery) { - const telegramId = callbackQuery.from.id; const chatId = callbackQuery.message.chat.id; try { @@ -31,7 +31,7 @@ export default class UserDeletionHandler { ); } catch (error) { logger.error({ err: error }, 'Error in handleDeleteUser'); - await bot.sendMessage(chatId, 'Error processing delete request. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error processing delete request. Please try again.'); } } @@ -48,7 +48,7 @@ export default class UserDeletionHandler { ); } catch (error) { logger.error({ err: error }, 'Error in handleConfirmDelete'); - await bot.sendMessage(chatId, 'Error deleting user. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error deleting user. Please try again.'); } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/userLocationHandler.js b/src/handlers/userHandlers/userLocationHandler.js index c8edab3..abdf270 100644 --- a/src/handlers/userHandlers/userLocationHandler.js +++ b/src/handlers/userHandlers/userLocationHandler.js @@ -3,6 +3,7 @@ import LocationService from "../../services/locationService.js"; import bot from "../../context/bot.js"; import UserService from "../../services/userService.js"; import logger from '../../utils/logger.js'; +import { editOrSendCallback } from '../../utils/messageUtils.js'; export default class UserLocationHandler { static async handleSetLocation(callbackQuery) { @@ -48,7 +49,7 @@ export default class UserLocationHandler { ); } catch (error) { logger.error({ err: error }, 'Error in handleSetLocation'); - await bot.sendMessage(chatId, 'Error loading countries. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading countries. Please try again.'); } } @@ -80,7 +81,7 @@ export default class UserLocationHandler { ); } catch (error) { logger.error({ err: error }, 'Error in handleSetCountry'); - await bot.sendMessage(chatId, 'Error loading cities. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading cities. Please try again.'); } } @@ -112,7 +113,7 @@ export default class UserLocationHandler { ); } catch (error) { logger.error({ err: error }, 'Error in handleSetCity'); - await bot.sendMessage(chatId, 'Error loading districts. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading districts. Please try again.'); } } @@ -142,7 +143,7 @@ export default class UserLocationHandler { } catch (error) { await db.runAsync('ROLLBACK'); logger.error({ err: error }, 'Error in handleSetDistrict'); - await bot.sendMessage(chatId, 'Error updating location. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error updating location. Please try again.'); } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/userProductHandler.js b/src/handlers/userHandlers/userProductHandler.js index 2a1184f..ac3f227 100644 --- a/src/handlers/userHandlers/userProductHandler.js +++ b/src/handlers/userHandlers/userProductHandler.js @@ -8,6 +8,7 @@ import CategoryService from "../../services/categoryService.js"; import UserService from "../../services/userService.js"; import PurchaseService from '../../services/purchaseService.js'; import Validators from '../../utils/validators.js'; +import { editOrSendCallback, deleteAndSend } from '../../utils/messageUtils.js'; import fs from 'fs'; import path from 'path'; @@ -572,8 +573,7 @@ export default class UserProductHandler { // Проверка баланса пользователя if (userBalance <= 0) { - await bot.sendMessage( - chatId, + await editOrSendCallback(callbackQuery, `❌ Insufficient balance. Your current balance is $${userBalance}. You need $${totalPrice} to complete this purchase.`, { reply_markup: { @@ -587,8 +587,7 @@ export default class UserProductHandler { } if (userBalance < totalPrice) { - await bot.sendMessage( - chatId, + await editOrSendCallback(callbackQuery, `❌ Insufficient balance. Your current balance is $${userBalance}. You need $${totalPrice} to complete this purchase.`, { reply_markup: { @@ -610,8 +609,7 @@ export default class UserProductHandler { `, [user.id]); if (cryptoWallets.length === 0) { - await bot.sendMessage( - chatId, + await editOrSendCallback(callbackQuery, 'You need to add a crypto wallet first to make purchases.', { reply_markup: { @@ -652,7 +650,7 @@ export default class UserProductHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handleBuyProduct'); - await bot.sendMessage(chatId, 'Error processing purchase. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error processing purchase. Please try again.'); } } @@ -663,16 +661,16 @@ export default class UserProductHandler { const state = await userStates.get(chatId); if (!Validators.isValidWalletType(walletType)) { - await bot.sendMessage(chatId, 'Invalid wallet type.'); + await editOrSendCallback(callbackQuery, 'Invalid wallet type.'); return; } if (!Validators.isValidNumericId(Number(productId))) { - await bot.sendMessage(chatId, 'Invalid product.'); + await editOrSendCallback(callbackQuery, 'Invalid product.'); return; } const qty = Number(quantity); if (!Number.isFinite(qty) || qty <= 0) { - await bot.sendMessage(chatId, 'Invalid quantity.'); + await editOrSendCallback(callbackQuery, 'Invalid quantity.'); return; } @@ -703,7 +701,7 @@ export default class UserProductHandler { // Проверка наличия товара if (product.quantity_in_stock < quantity) { - await bot.sendMessage(chatId, `❌ Not enough items in stock. Only ${product.quantity_in_stock} available.`); + await editOrSendCallback(callbackQuery, `❌ Not enough items in stock. Only ${product.quantity_in_stock} available.`); return; } @@ -764,7 +762,7 @@ export default class UserProductHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handlePay'); - await bot.sendMessage(chatId, 'Error processing purchase. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error processing purchase. Please try again.'); } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/userPurchaseHandler.js b/src/handlers/userHandlers/userPurchaseHandler.js index f63a0f5..7a6aa88 100644 --- a/src/handlers/userHandlers/userPurchaseHandler.js +++ b/src/handlers/userHandlers/userPurchaseHandler.js @@ -15,6 +15,7 @@ import CategoryService from "../../services/categoryService.js"; import WalletService from "../../services/walletService.js"; import userStates from "../../context/userStates.js"; import Validators from '../../utils/validators.js'; +import { editOrSendCallback, deleteAndSend } from '../../utils/messageUtils.js'; const UPLOADS_DIR = path.join(process.cwd(), 'uploads'); const FALLBACK_PHOTO = path.join(process.cwd(), 'corrupt-photo.jpg'); @@ -144,7 +145,7 @@ export default class UserPurchaseHandler { await userStates.delete(chatId); } catch (e) { logger.error({ err: e }, 'Error in handlePurchaseListPage'); - await bot.sendMessage(chatId, 'Error loading purchase history. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading purchase history. Please try again.'); } } @@ -177,14 +178,13 @@ export default class UserPurchaseHandler { // Получаем данные покупки const purchase = await PurchaseService.getPurchaseById(purchaseId); if (!purchase) { - await bot.sendMessage(chatId, "No such purchase"); + await editOrSendCallback(callbackQuery, "No such purchase"); return; } - // Получаем данные товара по product_id const product = await ProductService.getProductById(purchase.product_id); if (!product) { - await bot.sendMessage(chatId, "No such product"); + await editOrSendCallback(callbackQuery, "No such product"); return; } @@ -249,7 +249,7 @@ export default class UserPurchaseHandler { }); } catch (error) { logger.error({ err: error }, 'Error in viewPurchase'); - await bot.sendMessage(chatId, 'Error loading purchase details. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading purchase details. Please try again.'); } } @@ -262,14 +262,14 @@ export default class UserPurchaseHandler { // Получаем данные покупки const purchase = await PurchaseService.getPurchaseById(purchaseId); if (!purchase) { - await bot.sendMessage(chatId, "Purchase not found."); + await editOrSendCallback(callbackQuery, "Purchase not found."); return; } // Получаем данные пользователя по user_id из покупки const user = await UserService.getUserByUserId(purchase.user_id); if (!user) { - await bot.sendMessage(chatId, "User not found for this purchase."); + await editOrSendCallback(callbackQuery, 'User not found.'); return; } @@ -318,7 +318,7 @@ export default class UserPurchaseHandler { await this.showPurchases({ chat: { id: chatId }, from: { id: callbackQuery.from.id } }); } catch (error) { logger.error({ err: error }, 'Error in handleConfirmReceived'); - await bot.sendMessage(chatId, 'Error confirming receipt. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error confirming receipt. Please try again.'); } } } diff --git a/src/handlers/userHandlers/wallet/archiveHandler.js b/src/handlers/userHandlers/wallet/archiveHandler.js index 57824ab..e876329 100644 --- a/src/handlers/userHandlers/wallet/archiveHandler.js +++ b/src/handlers/userHandlers/wallet/archiveHandler.js @@ -3,6 +3,7 @@ import WalletUtils from '../../../utils/walletUtils.js'; import UserService from '../../../services/userService.js'; import bot from '../../../context/bot.js'; import logger from '../../../utils/logger.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class ArchiveHandler { static async handleViewArchivedWallets(callbackQuery) { @@ -82,7 +83,7 @@ export default class ArchiveHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handleViewArchivedWallets'); - await bot.sendMessage(chatId, 'Error loading archived wallets. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading archived wallets. Please try again.'); } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/wallet/balanceHandler.js b/src/handlers/userHandlers/wallet/balanceHandler.js index 8c80746..f2a8824 100644 --- a/src/handlers/userHandlers/wallet/balanceHandler.js +++ b/src/handlers/userHandlers/wallet/balanceHandler.js @@ -4,6 +4,7 @@ import UserService from '../../../services/userService.js'; import WalletService from '../../../services/walletService.js'; import bot from '../../../context/bot.js'; import logger from '../../../utils/logger.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class BalanceHandler { static async showBalance(msg) { diff --git a/src/handlers/userHandlers/wallet/createHandler.js b/src/handlers/userHandlers/wallet/createHandler.js index 6625f6e..106b308 100644 --- a/src/handlers/userHandlers/wallet/createHandler.js +++ b/src/handlers/userHandlers/wallet/createHandler.js @@ -5,6 +5,7 @@ import bot from '../../../context/bot.js'; import UserService from '../../../services/userService.js'; import logger from '../../../utils/logger.js'; import WalletHelpers from './helpers.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class CreateHandler { static async handleAddWallet(callbackQuery) { @@ -35,7 +36,7 @@ export default class CreateHandler { const walletType = callbackQuery.data.replace('generate_wallet_', '').replace('_', ' '); if (!Validators.isValidWalletType(walletType)) { - await bot.sendMessage(chatId, 'Invalid wallet type.'); + await editOrSendCallback(callbackQuery, 'Invalid wallet type.'); return; } diff --git a/src/handlers/userHandlers/wallet/historyHandler.js b/src/handlers/userHandlers/wallet/historyHandler.js index 1bd4721..24c6bed 100644 --- a/src/handlers/userHandlers/wallet/historyHandler.js +++ b/src/handlers/userHandlers/wallet/historyHandler.js @@ -2,6 +2,7 @@ import db from '../../../config/database.js'; import UserService from '../../../services/userService.js'; import bot from '../../../context/bot.js'; import logger from '../../../utils/logger.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class HistoryHandler { static async handleTransactionHistory(callbackQuery, page = 0) { @@ -11,7 +12,7 @@ export default class HistoryHandler { try { const user = await UserService.getUserByTelegramId(telegramId.toString()); if (!user) { - await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.'); + await editOrSendCallback(callbackQuery, 'Profile not found. Please use /start to create one.'); return; } @@ -63,7 +64,7 @@ export default class HistoryHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handleTransactionHistory'); - await bot.sendMessage(chatId, 'Error loading transaction history. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading transaction history. Please try again.'); } } @@ -103,7 +104,7 @@ export default class HistoryHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handleWalletHistory'); - await bot.sendMessage(chatId, 'Error loading transaction history. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading transaction history. Please try again.'); } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/wallet/refreshHandler.js b/src/handlers/userHandlers/wallet/refreshHandler.js index 8b57973..78f976a 100644 --- a/src/handlers/userHandlers/wallet/refreshHandler.js +++ b/src/handlers/userHandlers/wallet/refreshHandler.js @@ -3,6 +3,7 @@ import WalletUtils from '../../../utils/walletUtils.js'; import UserService from '../../../services/userService.js'; import bot from '../../../context/bot.js'; import logger from '../../../utils/logger.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class RefreshHandler { static async handleRefreshBalance(callbackQuery) { @@ -14,7 +15,7 @@ export default class RefreshHandler { const user = await UserService.getUserByTelegramId(callbackQuery.from.id.toString()); if (!user) { - await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.'); + await editOrSendCallback(callbackQuery, 'Profile not found. Please use /start to create one.'); return; } @@ -69,7 +70,7 @@ export default class RefreshHandler { } catch (error) { logger.error({ err: error }, 'Error in handleRefreshBalance'); await bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Error refreshing balances.' }); - await bot.sendMessage(chatId, '❌ Error refreshing balances. Please try again.', { + await editOrSendCallback(callbackQuery, '❌ Error refreshing balances. Please try again.', { reply_markup: { inline_keyboard: [[{ text: '« Back', callback_data: 'back_to_balance' }]] } }); } diff --git a/src/handlers/userHandlers/wallet/topUpHandler.js b/src/handlers/userHandlers/wallet/topUpHandler.js index f0934cc..0e38ae5 100644 --- a/src/handlers/userHandlers/wallet/topUpHandler.js +++ b/src/handlers/userHandlers/wallet/topUpHandler.js @@ -3,6 +3,7 @@ import WalletUtils from '../../../utils/walletUtils.js'; import UserService from '../../../services/userService.js'; import bot from '../../../context/bot.js'; import logger from '../../../utils/logger.js'; +import { editOrSendCallback } from '../../../utils/messageUtils.js'; export default class TopUpHandler { static async handleTopUpWallet(callbackQuery) { @@ -56,7 +57,7 @@ export default class TopUpHandler { }); } catch (error) { logger.error({ err: error }, 'Error in handleTopUpWallet'); - await bot.sendMessage(chatId, 'Error loading wallets. Please try again.'); + await editOrSendCallback(callbackQuery, 'Error loading wallets. Please try again.'); } } } \ No newline at end of file diff --git a/src/utils/messageUtils.js b/src/utils/messageUtils.js new file mode 100644 index 0000000..e842130 --- /dev/null +++ b/src/utils/messageUtils.js @@ -0,0 +1,31 @@ +import bot from '../context/bot.js'; + +export async function editOrSend(chatId, messageId, text, options = {}) { + if (messageId) { + try { + const result = await bot.editMessageText(text, { + chat_id: chatId, + message_id: messageId, + ...options, + }); + return result; + } catch (e) { + // message too old or already edited — delete and send new + try { await bot.deleteMessage(chatId, messageId); } catch (_) {} + } + } + return bot.sendMessage(chatId, text, options); +} + +export async function editOrSendCallback(callbackQuery, text, options = {}) { + const chatId = callbackQuery.message.chat.id; + const messageId = callbackQuery.message.message_id; + return editOrSend(chatId, messageId, text, options); +} + +export async function deleteAndSend(chatId, messageId, text, options = {}) { + if (messageId) { + try { await bot.deleteMessage(chatId, messageId); } catch (_) {} + } + return bot.sendMessage(chatId, text, options); +} \ No newline at end of file