755 lines
27 KiB
JavaScript
755 lines
27 KiB
JavaScript
import db from '../config/database.js';
|
||
import User from '../models/User.js';
|
||
import WalletService from "../utils/walletService.js";
|
||
import config from "../config/config.js";
|
||
|
||
export default class UserProductHandler {
|
||
constructor(bot) {
|
||
this.bot = bot;
|
||
this.userStates = new Map();
|
||
}
|
||
|
||
async showProducts(msg) {
|
||
const chatId = msg.chat.id;
|
||
const messageId = msg?.message_id;
|
||
|
||
try {
|
||
const countries = await db.allAsync(
|
||
'SELECT DISTINCT country FROM locations ORDER BY country'
|
||
);
|
||
|
||
if (countries.length === 0) {
|
||
const message = 'No products available at the moment.';
|
||
if (messageId) {
|
||
await this.bot.editMessageText(message, {
|
||
chat_id: chatId,
|
||
message_id: messageId
|
||
});
|
||
} else {
|
||
await this.bot.sendMessage(chatId, message);
|
||
}
|
||
return;
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: countries.map(loc => [{
|
||
text: loc.country,
|
||
callback_data: `shop_country_${loc.country}`
|
||
}])
|
||
};
|
||
|
||
const message = '🌍 Select your country:';
|
||
|
||
try {
|
||
if (messageId) {
|
||
await this.bot.editMessageText(message, {
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
});
|
||
}
|
||
} catch (error) {
|
||
await this.bot.sendMessage(chatId, message, {reply_markup: keyboard});
|
||
}
|
||
} catch (error) {
|
||
console.error('Error in showProducts:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading products. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleCountrySelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const country = callbackQuery.data.replace('shop_country_', '');
|
||
|
||
try {
|
||
const cities = await db.allAsync(
|
||
'SELECT DISTINCT city FROM locations WHERE country = ? ORDER BY city',
|
||
[country]
|
||
);
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...cities.map(loc => [{
|
||
text: loc.city,
|
||
callback_data: `shop_city_${country}_${loc.city}`
|
||
}]),
|
||
[{text: '« Back to Countries', callback_data: 'shop_start'}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
`🏙 Select city in ${country}:`,
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleCountrySelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading cities. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleCitySelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const [country, city] = callbackQuery.data.replace('shop_city_', '').split('_');
|
||
|
||
try {
|
||
const districts = await db.allAsync(
|
||
'SELECT district FROM locations WHERE country = ? AND city = ? ORDER BY district',
|
||
[country, city]
|
||
);
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...districts.map(loc => [{
|
||
text: loc.district,
|
||
callback_data: `shop_district_${country}_${city}_${loc.district}`
|
||
}]),
|
||
[{text: '« Back to Cities', callback_data: `shop_country_${country}`}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
`📍 Select district in ${city}:`,
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleCitySelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading districts. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleDistrictSelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const [country, city, district] = callbackQuery.data.replace('shop_district_', '').split('_');
|
||
|
||
try {
|
||
const location = await db.getAsync(
|
||
'SELECT id FROM locations WHERE country = ? AND city = ? AND district = ?',
|
||
[country, city, district]
|
||
);
|
||
|
||
if (!location) {
|
||
throw new Error('Location not found');
|
||
}
|
||
|
||
const categories = await db.allAsync(
|
||
'SELECT id, name FROM categories WHERE location_id = ? ORDER BY name',
|
||
[location.id]
|
||
);
|
||
|
||
if (categories.length === 0) {
|
||
await this.bot.editMessageText(
|
||
'No products available in this location yet.',
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{text: '« Back to Districts', callback_data: `shop_city_${country}_${city}`}
|
||
]]
|
||
}
|
||
}
|
||
);
|
||
return;
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...categories.map(cat => [{
|
||
text: cat.name,
|
||
callback_data: `shop_category_${location.id}_${cat.id}`
|
||
}]),
|
||
[{text: '« Back to Districts', callback_data: `shop_city_${country}_${city}`}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
'📦 Select category:',
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleDistrictSelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading categories. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleCategorySelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const [locationId, categoryId] = callbackQuery.data.replace('shop_category_', '').split('_');
|
||
|
||
try {
|
||
const subcategories = await db.allAsync(
|
||
'SELECT id, name FROM subcategories WHERE category_id = ? ORDER BY name',
|
||
[categoryId]
|
||
);
|
||
|
||
const location = await db.getAsync(
|
||
'SELECT country, city, district FROM locations WHERE id = ?',
|
||
[locationId]
|
||
);
|
||
|
||
if (subcategories.length === 0) {
|
||
await this.bot.editMessageText(
|
||
'No products available in this category yet.',
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{
|
||
text: '« Back to Categories',
|
||
callback_data: `shop_district_${location.country}_${location.city}_${location.district}`
|
||
}
|
||
]]
|
||
}
|
||
}
|
||
);
|
||
return;
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...subcategories.map(sub => [{
|
||
text: sub.name,
|
||
callback_data: `shop_subcategory_${locationId}_${categoryId}_${sub.id}`
|
||
}]),
|
||
[{
|
||
text: '« Back to Categories',
|
||
callback_data: `shop_district_${location.country}_${location.city}_${location.district}`
|
||
}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
'📦 Select subcategory:',
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleCategorySelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading subcategories. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleSubcategorySelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const [locationId, categoryId, subcategoryId, photoMessageId] = callbackQuery.data.replace('shop_subcategory_', '').split('_');
|
||
|
||
try {
|
||
// Delete the photo message if it exists
|
||
if (photoMessageId) {
|
||
try {
|
||
await this.bot.deleteMessage(chatId, photoMessageId);
|
||
} catch (error) {
|
||
console.error('Error deleting photo message:', error);
|
||
}
|
||
}
|
||
|
||
const products = await db.allAsync(
|
||
`SELECT id, name, price, description, quantity_in_stock, photo_url
|
||
FROM products
|
||
WHERE location_id = ? AND category_id = ? AND subcategory_id = ?
|
||
AND quantity_in_stock > 0
|
||
ORDER BY name`,
|
||
[locationId, categoryId, subcategoryId]
|
||
);
|
||
|
||
const location = await db.getAsync('SELECT * FROM locations WHERE id = ?', [locationId]);
|
||
const category = await db.getAsync('SELECT name FROM categories WHERE id = ?', [categoryId]);
|
||
const subcategory = await db.getAsync('SELECT name FROM subcategories WHERE id = ?', [subcategoryId]);
|
||
|
||
if (products.length === 0) {
|
||
await this.bot.editMessageText(
|
||
'No products available in this subcategory.',
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{
|
||
text: '« Back to Subcategories',
|
||
callback_data: `shop_category_${locationId}_${categoryId}`
|
||
}
|
||
]]
|
||
}
|
||
}
|
||
);
|
||
return;
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...products.map(prod => [{
|
||
text: `${prod.name} - $${prod.price}`,
|
||
callback_data: `shop_product_${prod.id}`
|
||
}]),
|
||
[{text: '« Back to Subcategories', callback_data: `shop_category_${locationId}_${categoryId}`}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
`📦 Products in ${subcategory.name}:`,
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleSubcategorySelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading products. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleProductSelection(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const productId = callbackQuery.data.replace('shop_product_', '');
|
||
|
||
try {
|
||
const product = await db.getAsync(
|
||
`SELECT p.*, c.name as category_name, s.name as subcategory_name
|
||
FROM products p
|
||
JOIN categories c ON p.category_id = c.id
|
||
JOIN subcategories s ON p.subcategory_id = s.id
|
||
WHERE p.id = ?`,
|
||
[productId]
|
||
);
|
||
|
||
if (!product) {
|
||
throw new Error('Product not found');
|
||
}
|
||
|
||
// Delete the previous message
|
||
await this.bot.deleteMessage(chatId, messageId);
|
||
|
||
const message = `
|
||
📦 ${product.name}
|
||
|
||
💰 Price: $${product.price}
|
||
📝 Description: ${product.description}
|
||
📦 Available: ${product.quantity_in_stock} pcs
|
||
|
||
Category: ${product.category_name}
|
||
Subcategory: ${product.subcategory_name}
|
||
`;
|
||
|
||
let photoMessageId = null;
|
||
|
||
// First send the photo if it exists
|
||
let photoMessage;
|
||
if (product.photo_url) {
|
||
try {
|
||
photoMessage = await this.bot.sendPhoto(chatId, product.photo_url, {caption: 'Public photo'});
|
||
} catch (e) {
|
||
photoMessage = await this.bot.sendPhoto(chatId, "./corrupt-photo.jpg", {caption: 'Public photo'})
|
||
}
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
[{text: '🛒 Buy Now', callback_data: `buy_product_${productId}`}],
|
||
[
|
||
{
|
||
text: '➖',
|
||
callback_data: `decrease_quantity_${productId}`,
|
||
callback_game: {} // Initially disabled as quantity starts at 1
|
||
},
|
||
{text: '1', callback_data: 'current_quantity'},
|
||
{
|
||
text: '➕',
|
||
callback_data: `increase_quantity_${productId}`,
|
||
callback_game: product.quantity_in_stock <= 1 ? {} : null // Disabled if stock is 1 or less
|
||
}
|
||
],
|
||
[{
|
||
text: `« Back to ${product.subcategory_name}`,
|
||
callback_data: `shop_subcategory_${product.location_id}_${product.category_id}_${product.subcategory_id}_${photoMessageId}`
|
||
}]
|
||
]
|
||
};
|
||
|
||
// Then send the message with controls
|
||
await this.bot.sendMessage(chatId, message, {
|
||
reply_markup: keyboard,
|
||
parse_mode: 'HTML'
|
||
});
|
||
|
||
// Store the current quantity and photo message ID in user state
|
||
this.userStates.set(chatId, {
|
||
action: 'buying_product',
|
||
productId,
|
||
quantity: 1,
|
||
photoMessageId
|
||
});
|
||
} catch (error) {
|
||
console.error('Error in handleProductSelection:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading product details. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handleIncreaseQuantity(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const productId = callbackQuery.data.replace('increase_quantity_', '');
|
||
const state = this.userStates.get(chatId);
|
||
|
||
try {
|
||
const product = await db.getAsync(
|
||
'SELECT quantity_in_stock FROM products WHERE id = ?',
|
||
[productId]
|
||
);
|
||
|
||
if (!product) {
|
||
throw new Error('Product not found');
|
||
}
|
||
|
||
const currentQuantity = state?.quantity || 1;
|
||
|
||
// If already at max stock, silently ignore
|
||
if (currentQuantity >= product.quantity_in_stock) {
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
return;
|
||
}
|
||
|
||
const newQuantity = Math.min(currentQuantity + 1, product.quantity_in_stock);
|
||
|
||
// Update state
|
||
this.userStates.set(chatId, {
|
||
...state,
|
||
quantity: newQuantity
|
||
});
|
||
|
||
// Update quantity display in keyboard
|
||
const keyboard = callbackQuery.message.reply_markup.inline_keyboard;
|
||
keyboard[1] = [
|
||
{
|
||
text: '➖',
|
||
callback_data: `decrease_quantity_${productId}`,
|
||
callback_game: newQuantity <= 1 ? {} : null
|
||
},
|
||
{text: newQuantity.toString(), callback_data: 'current_quantity'},
|
||
{
|
||
text: '➕',
|
||
callback_data: `increase_quantity_${productId}`,
|
||
callback_game: newQuantity >= product.quantity_in_stock ? {} : null
|
||
}
|
||
];
|
||
|
||
await this.bot.editMessageReplyMarkup(
|
||
{inline_keyboard: keyboard},
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId
|
||
}
|
||
);
|
||
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
} catch (error) {
|
||
console.error('Error in handleIncreaseQuantity:', error);
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
}
|
||
}
|
||
|
||
async handleDecreaseQuantity(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const messageId = callbackQuery.message.message_id;
|
||
const productId = callbackQuery.data.replace('decrease_quantity_', '');
|
||
const state = this.userStates.get(chatId);
|
||
|
||
try {
|
||
const product = await db.getAsync(
|
||
'SELECT quantity_in_stock FROM products WHERE id = ?',
|
||
[productId]
|
||
);
|
||
|
||
if (!product) {
|
||
throw new Error('Product not found');
|
||
}
|
||
|
||
const currentQuantity = state?.quantity || 1;
|
||
|
||
// If already at minimum, silently ignore
|
||
if (currentQuantity <= 1) {
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
return;
|
||
}
|
||
|
||
const newQuantity = Math.max(currentQuantity - 1, 1);
|
||
|
||
// Update state
|
||
this.userStates.set(chatId, {
|
||
...state,
|
||
quantity: newQuantity
|
||
});
|
||
|
||
// Update quantity display in keyboard
|
||
const keyboard = callbackQuery.message.reply_markup.inline_keyboard;
|
||
keyboard[1] = [
|
||
{
|
||
text: '➖',
|
||
callback_data: `decrease_quantity_${productId}`,
|
||
callback_game: newQuantity <= 1 ? {} : null
|
||
},
|
||
{text: newQuantity.toString(), callback_data: 'current_quantity'},
|
||
{
|
||
text: '➕',
|
||
callback_data: `increase_quantity_${productId}`,
|
||
callback_game: newQuantity >= product.quantity_in_stock ? {} : null
|
||
}
|
||
];
|
||
|
||
await this.bot.editMessageReplyMarkup(
|
||
{inline_keyboard: keyboard},
|
||
{
|
||
chat_id: chatId,
|
||
message_id: messageId
|
||
}
|
||
);
|
||
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
} catch (error) {
|
||
console.error('Error in handleDecreaseQuantity:', error);
|
||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||
}
|
||
}
|
||
|
||
async handleBuyProduct(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const userId = callbackQuery.from.id;
|
||
const productId = callbackQuery.data.replace('buy_product_', '');
|
||
const state = this.userStates.get(chatId);
|
||
|
||
try {
|
||
const user = await User.getById(userId);
|
||
if (!user) {
|
||
throw new Error('User not found');
|
||
}
|
||
|
||
const product = await db.getAsync(
|
||
'SELECT * FROM products WHERE id = ?',
|
||
[productId]
|
||
);
|
||
|
||
if (!product) {
|
||
throw new Error('Product not found');
|
||
}
|
||
|
||
const quantity = state?.quantity || 1;
|
||
const totalPrice = product.price * quantity;
|
||
|
||
// Get user's crypto wallets with balances
|
||
const cryptoWallets = await db.allAsync(`
|
||
SELECT wallet_type, address
|
||
FROM crypto_wallets
|
||
WHERE user_id = ?
|
||
ORDER BY wallet_type
|
||
`, [user.id]);
|
||
|
||
if (cryptoWallets.length === 0) {
|
||
await this.bot.sendMessage(
|
||
chatId,
|
||
'You need to add a crypto wallet first to make purchases.',
|
||
{
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{text: '➕ Add Wallet', callback_data: 'add_wallet'}
|
||
]]
|
||
}
|
||
}
|
||
);
|
||
return;
|
||
}
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
...cryptoWallets.map(wallet => [{
|
||
text: `Pay with ${wallet.wallet_type}`,
|
||
callback_data: `pay_with_${wallet.wallet_type}_${productId}_${quantity}`
|
||
}]),
|
||
[{text: '« Cancel', callback_data: `shop_product_${productId}`}]
|
||
]
|
||
};
|
||
|
||
await this.bot.editMessageText(
|
||
`🛒 Purchase Summary:\n\n` +
|
||
`Product: ${product.name}\n` +
|
||
`Quantity: ${quantity}\n` +
|
||
`Total: $${totalPrice}\n\n` +
|
||
`Select payment method:`,
|
||
{
|
||
chat_id: chatId,
|
||
message_id: callbackQuery.message.message_id,
|
||
reply_markup: keyboard
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error('Error in handleBuyProduct:', error);
|
||
await this.bot.sendMessage(chatId, 'Error processing purchase. Please try again.');
|
||
}
|
||
}
|
||
|
||
async handlePay(callbackQuery) {
|
||
const chatId = callbackQuery.message.chat.id;
|
||
const userId = callbackQuery.from.id;
|
||
const [walletType, productId, quantity] = callbackQuery.data.replace('pay_with_', '').split('_');
|
||
const state = this.userStates.get(chatId);
|
||
|
||
try {
|
||
await User.recalculateBalance(userId);
|
||
const user = await User.getById(userId);
|
||
|
||
if (!user) {
|
||
throw new Error('User not found');
|
||
}
|
||
|
||
const product = await db.getAsync(
|
||
'SELECT * FROM products WHERE id = ?',
|
||
[productId]
|
||
);
|
||
|
||
if (!product) {
|
||
throw new Error('Product not found');
|
||
}
|
||
|
||
const totalPrice = product.price * quantity;
|
||
const balance = user.total_balance + user.bonus_balance;
|
||
|
||
if (totalPrice > balance) {
|
||
this.userStates.delete(chatId);
|
||
await this.bot.editMessageText(`Not enough money`, {
|
||
chat_id: chatId,
|
||
message_id: callbackQuery.message.message_id,
|
||
});
|
||
return;
|
||
}
|
||
|
||
await db.runAsync(
|
||
'INSERT INTO purchases (user_id, product_id, wallet_type, tx_hash, quantity, total_price) VALUES (?, ?, ?, ?, ?, ?)',
|
||
[user.id, product.id, walletType, "null", quantity, totalPrice]
|
||
);
|
||
|
||
let hiddenPhotoMessage;
|
||
if (product.hidden_photo_url) {
|
||
try {
|
||
hiddenPhotoMessage = await this.bot.sendPhoto(chatId, product.hidden_photo_url, {caption: 'Hidden photo'});
|
||
} catch (e) {
|
||
hiddenPhotoMessage = await this.bot.sendPhoto(chatId, "./corrupt-photo.jpg", {caption: 'Hidden photo'})
|
||
}
|
||
}
|
||
|
||
const message = `
|
||
📦 Product Details:
|
||
|
||
Name: ${product.name}
|
||
Price: $${product.price}
|
||
Description: ${product.description}
|
||
Stock: ${product.quantity_in_stock}
|
||
Location: ${product.country}, ${product.city}, ${product.district}
|
||
Category: ${product.category_name}
|
||
Subcategory: ${product.subcategory_name}
|
||
|
||
🔒 Private Information:
|
||
${product.private_data}
|
||
Hidden Location: ${product.hidden_description}
|
||
Coordinates: ${product.hidden_coordinates}
|
||
`;
|
||
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
[{text: "I've got it!", callback_data: "Asdasdasd"}],
|
||
[{text: "Contact support", url: config.SUPPORT_LINK}]
|
||
]
|
||
};
|
||
|
||
await this.bot.sendMessage(chatId, message, {reply_markup: keyboard});
|
||
await this.bot.deleteMessage(chatId, callbackQuery.message.message_id);
|
||
} catch (error) {
|
||
console.error('Error in handleBuyProduct:', error);
|
||
await this.bot.sendMessage(chatId, 'Error processing purchase. Please try again.');
|
||
}
|
||
}
|
||
|
||
async showPurchases(msg) {
|
||
const chatId = msg.chat.id;
|
||
const userId = msg.from.id;
|
||
|
||
try {
|
||
const user = await User.getById(userId);
|
||
if (!user) {
|
||
await this.bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
||
return;
|
||
}
|
||
|
||
const purchases = await db.allAsync(`
|
||
SELECT p.*, pr.name as product_name, pr.description,
|
||
l.country, l.city, l.district
|
||
FROM purchases p
|
||
JOIN products pr ON p.product_id = pr.id
|
||
JOIN locations l ON pr.location_id = l.id
|
||
WHERE p.user_id = ?
|
||
ORDER BY p.purchase_date DESC
|
||
LIMIT 10
|
||
`, [user.id]);
|
||
|
||
if (purchases.length === 0) {
|
||
await this.bot.sendMessage(
|
||
chatId,
|
||
'You haven\'t made any purchases yet.',
|
||
{
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{text: '🛍 Browse Products', callback_data: 'shop_start'}
|
||
]]
|
||
}
|
||
}
|
||
);
|
||
return;
|
||
}
|
||
|
||
let message = '🛍 *Your Recent Purchases:*\n\n';
|
||
|
||
for (const purchase of purchases) {
|
||
const date = new Date(purchase.purchase_date).toLocaleString();
|
||
message += `📦 *${purchase.product_name}*\n`;
|
||
message += `├ Quantity: ${purchase.quantity}\n`;
|
||
message += `├ Total: $${purchase.total_price}\n`;
|
||
message += `├ Location: ${purchase.country}, ${purchase.city}\n`;
|
||
message += `├ Payment: ${purchase.wallet_type}\n`;
|
||
message += `├ TX: \`${purchase.tx_hash}\`\n`;
|
||
message += `└ Date: ${date}\n\n`;
|
||
}
|
||
|
||
await this.bot.sendMessage(chatId, message, {
|
||
parse_mode: 'Markdown',
|
||
reply_markup: {
|
||
inline_keyboard: [[
|
||
{text: '🛍 Browse Products', callback_data: 'shop_start'}
|
||
]]
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Error in showPurchases:', error);
|
||
await this.bot.sendMessage(chatId, 'Error loading purchase history. Please try again.');
|
||
}
|
||
}
|
||
} |