Item purchase
This commit is contained in:
@@ -95,6 +95,8 @@ const initDb = async () => {
|
|||||||
city TEXT,
|
city TEXT,
|
||||||
district TEXT,
|
district TEXT,
|
||||||
status INTEGER DEFAULT 0,
|
status INTEGER DEFAULT 0,
|
||||||
|
total_balance REAL DEFAULT 0,
|
||||||
|
bonus_balance REAL DEFAULT 0,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -370,6 +370,10 @@ export default class AdminProductHandler {
|
|||||||
text: 'No products for this location',
|
text: 'No products for this location',
|
||||||
markup: {
|
markup: {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
|
[{
|
||||||
|
text: '📥 Import Products',
|
||||||
|
callback_data: `add_product_${locationId}_${categoryId}_${subcategoryId}`
|
||||||
|
}],
|
||||||
[{text: '« Back', callback_data: `prod_category_${locationId}_${categoryId}`}]
|
[{text: '« Back', callback_data: `prod_category_${locationId}_${categoryId}`}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import db from '../config/database.js';
|
import db from '../config/database.js';
|
||||||
import User from '../models/User.js';
|
import User from '../models/User.js';
|
||||||
|
import WalletService from "../utils/walletService.js";
|
||||||
|
import config from "../config/config.js";
|
||||||
|
|
||||||
export default class UserProductHandler {
|
export default class UserProductHandler {
|
||||||
constructor(bot) {
|
constructor(bot) {
|
||||||
@@ -209,7 +211,10 @@ export default class UserProductHandler {
|
|||||||
message_id: messageId,
|
message_id: messageId,
|
||||||
reply_markup: {
|
reply_markup: {
|
||||||
inline_keyboard: [[
|
inline_keyboard: [[
|
||||||
{ text: '« Back to Categories', callback_data: `shop_district_${location.country}_${location.city}_${location.district}` }
|
{
|
||||||
|
text: '« Back to Categories',
|
||||||
|
callback_data: `shop_district_${location.country}_${location.city}_${location.district}`
|
||||||
|
}
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +228,10 @@ export default class UserProductHandler {
|
|||||||
text: sub.name,
|
text: sub.name,
|
||||||
callback_data: `shop_subcategory_${locationId}_${categoryId}_${sub.id}`
|
callback_data: `shop_subcategory_${locationId}_${categoryId}_${sub.id}`
|
||||||
}]),
|
}]),
|
||||||
[{ text: '« Back to Categories', callback_data: `shop_district_${location.country}_${location.city}_${location.district}` }]
|
[{
|
||||||
|
text: '« Back to Categories',
|
||||||
|
callback_data: `shop_district_${location.country}_${location.city}_${location.district}`
|
||||||
|
}]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,7 +285,10 @@ export default class UserProductHandler {
|
|||||||
message_id: messageId,
|
message_id: messageId,
|
||||||
reply_markup: {
|
reply_markup: {
|
||||||
inline_keyboard: [[
|
inline_keyboard: [[
|
||||||
{ text: '« Back to Subcategories', callback_data: `shop_category_${locationId}_${categoryId}` }
|
{
|
||||||
|
text: '« Back to Subcategories',
|
||||||
|
callback_data: `shop_category_${locationId}_${categoryId}`
|
||||||
|
}
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,9 +356,13 @@ Subcategory: ${product.subcategory_name}
|
|||||||
let photoMessageId = null;
|
let photoMessageId = null;
|
||||||
|
|
||||||
// First send the photo if it exists
|
// First send the photo if it exists
|
||||||
|
let photoMessage;
|
||||||
if (product.photo_url) {
|
if (product.photo_url) {
|
||||||
const photoMessage = await this.bot.sendPhoto(chatId, product.photo_url);
|
try {
|
||||||
photoMessageId = photoMessage.message_id;
|
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 = {
|
const keyboard = {
|
||||||
@@ -366,7 +381,10 @@ Subcategory: ${product.subcategory_name}
|
|||||||
callback_game: product.quantity_in_stock <= 1 ? {} : null // Disabled if stock is 1 or less
|
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}` }]
|
[{
|
||||||
|
text: `« Back to ${product.subcategory_name}`,
|
||||||
|
callback_data: `shop_subcategory_${product.location_id}_${product.category_id}_${product.subcategory_id}_${photoMessageId}`
|
||||||
|
}]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -590,6 +608,87 @@ Subcategory: ${product.subcategory_name}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
async showPurchases(msg) {
|
||||||
const chatId = msg.chat.id;
|
const chatId = msg.chat.id;
|
||||||
const userId = msg.from.id;
|
const userId = msg.from.id;
|
||||||
|
|||||||
11
src/index.js
11
src/index.js
@@ -239,6 +239,9 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
} else if (action.startsWith('buy_product_')) {
|
} else if (action.startsWith('buy_product_')) {
|
||||||
logDebug(action, 'handleBuyProduct');
|
logDebug(action, 'handleBuyProduct');
|
||||||
await userProductHandler.handleBuyProduct(callbackQuery);
|
await userProductHandler.handleBuyProduct(callbackQuery);
|
||||||
|
} else if (action.startsWith('pay_with_')) {
|
||||||
|
logDebug(action, 'handlePay');
|
||||||
|
await userProductHandler.handlePay(callbackQuery);
|
||||||
}
|
}
|
||||||
// Admin location management
|
// Admin location management
|
||||||
else if (action === 'add_location') {
|
else if (action === 'add_location') {
|
||||||
@@ -287,7 +290,7 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleSubcategorySelection');
|
logDebug(action, 'handleSubcategorySelection');
|
||||||
await adminProductHandler.handleSubcategorySelection(callbackQuery);
|
await adminProductHandler.handleSubcategorySelection(callbackQuery);
|
||||||
} else if (action.startsWith('list_products_')) {
|
} else if (action.startsWith('list_products_')) {
|
||||||
logDebug(action, 'handleSubcategorySelection');
|
logDebug(action, 'handleProductListPage');
|
||||||
await adminProductHandler.handleProductListPage(callbackQuery);
|
await adminProductHandler.handleProductListPage(callbackQuery);
|
||||||
} else if (action.startsWith('add_product_')) {
|
} else if (action.startsWith('add_product_')) {
|
||||||
logDebug(action, 'handleAddProduct');
|
logDebug(action, 'handleAddProduct');
|
||||||
@@ -299,7 +302,7 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleProductEdit');
|
logDebug(action, 'handleProductEdit');
|
||||||
await adminProductHandler.handleProductEdit(callbackQuery)
|
await adminProductHandler.handleProductEdit(callbackQuery)
|
||||||
} else if (action.startsWith('delete_product_')) {
|
} else if (action.startsWith('delete_product_')) {
|
||||||
logDebug(action, 'handleViewProduct');
|
logDebug(action, 'handleProductDelete');
|
||||||
await adminProductHandler.handleProductDelete(callbackQuery);
|
await adminProductHandler.handleProductDelete(callbackQuery);
|
||||||
} else if (action.startsWith('confirm_delete_product_')) {
|
} else if (action.startsWith('confirm_delete_product_')) {
|
||||||
logDebug(action, 'handleConfirmDelete');
|
logDebug(action, 'handleConfirmDelete');
|
||||||
@@ -310,13 +313,13 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleViewUser');
|
logDebug(action, 'handleViewUser');
|
||||||
await adminUserHandler.handleViewUser(callbackQuery);
|
await adminUserHandler.handleViewUser(callbackQuery);
|
||||||
} else if (action.startsWith('list_users_')) {
|
} else if (action.startsWith('list_users_')) {
|
||||||
logDebug(action, 'handleViewUser');
|
logDebug(action, 'handleUserListPage');
|
||||||
await adminUserHandler.handleUserListPage(callbackQuery);
|
await adminUserHandler.handleUserListPage(callbackQuery);
|
||||||
} else if (action.startsWith('delete_user_')) {
|
} else if (action.startsWith('delete_user_')) {
|
||||||
logDebug(action, 'handleDeleteUser');
|
logDebug(action, 'handleDeleteUser');
|
||||||
await adminUserHandler.handleDeleteUser(callbackQuery);
|
await adminUserHandler.handleDeleteUser(callbackQuery);
|
||||||
} else if (action.startsWith('block_user_')) {
|
} else if (action.startsWith('block_user_')) {
|
||||||
logDebug(action, 'handleDeleteUser');
|
logDebug(action, 'handleBlockUser');
|
||||||
await adminUserHandler.handleBlockUser(callbackQuery);
|
await adminUserHandler.handleBlockUser(callbackQuery);
|
||||||
} else if (action.startsWith('confirm_delete_user_')) {
|
} else if (action.startsWith('confirm_delete_user_')) {
|
||||||
logDebug(action, 'handleConfirmDelete');
|
logDebug(action, 'handleConfirmDelete');
|
||||||
|
|||||||
@@ -103,4 +103,8 @@ export default class User {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async recalculateBalance(telegramId) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user