telegram-shop/src/handlers/adminHandlers/adminUserHandler.js
Artyom Ashirov 64e6397570 bug fixes
2024-11-25 16:22:28 +03:00

416 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import config from '../../config/config.js';
import db from '../../config/database.js';
import bot from "../../context/bot.js";
import UserService from "../../services/userService.js";
import userStates from "../../context/userStates.js";
export default class AdminUserHandler {
static isAdmin(userId) {
return config.ADMIN_IDS.includes(userId.toString());
}
static async calculateStatistics() {
try {
const users = await db.allAsync(`
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
` );
// Calculate general statistics
const totalUsers = users.length;
const activeUsers = users.filter(u => u.total_purchases > 0).length;
const totalBalance = users.reduce((sum, u) => sum + (u.total_balance || 0), 0);
const bonusBalance = users.reduce((sum, u) => sum + (u.bonus_balance || 0), 0);
const totalPurchases = users.reduce((sum, u) => sum + (u.total_purchases || 0), 0);
// Create statistics message
let message = `📊 System Statistics\n\n`;
message += `👥 Total Users: ${totalUsers}\n`;
message += `✅ Active Users: ${activeUsers}\n`;
message += `💰 Bonus Balance: $${bonusBalance.toFixed(2)}\n`;
message += `💰 Total Balance: $${(totalBalance + bonusBalance).toFixed(2)}\n`;
message += `🛍 Total Purchases: ${totalPurchases}`;
return message;
} catch (error) {
return null
}
}
static async viewUserPage(page) {
const limit = 10;
const offset = (page || 0) * limit;
const previousPage = page > 0 ? page - 1 : 0;
const nextPage = page + 1;
try {
const users = await db.allAsync(`
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
LIMIT ?
OFFSET ?
`, [limit, offset]);
if ((users.length === 0) && (page == 0)) {
return {text: 'No users registered yet.'};
}
if ((users.length === 0) && (page > 0)) {
return await this.viewUserPage(page - 1);
}
const statistics = await this.calculateStatistics()
const message = `${statistics}\n\nSelect a user from the list below:`;
// Create inline keyboard with user list
const keyboard = {
inline_keyboard: users.map(user => [{
text: `ID: ${user.telegram_id} | Nickname: ${user.username ? "@" + user.username : "None"} | Balance: $${(user.total_balance || 0) + (user.bonus_balance || 0)}`,
callback_data: `view_user_${user.telegram_id}`
}])
};
keyboard.inline_keyboard.push([
{text: `«`, callback_data: `list_users_${previousPage}`},
{text: `»`, callback_data: `list_users_${nextPage}`},
])
return {text: message, markup: keyboard}
} catch (error) {
console.error('Error in handleUserList:', error);
return {text: 'Error loading user list. Please try again.'}
}
}
static async handleUserList(msg) {
if (!this.isAdmin(msg.from.id)) {
await bot.sendMessage(msg.chat.id, 'Unauthorized access.');
return;
}
const {text, markup} = await this.viewUserPage(0);
await bot.sendMessage(msg.chat.id, text, {reply_markup: markup})
}
static async handleUserListPage(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const chatId = callbackQuery.message.chat.id;
const page = parseInt(callbackQuery.data.replace('list_users_', ''));
try {
const {text, markup} = await this.viewUserPage(page);
await bot.editMessageText(text, {
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: markup,
parse_mode: 'HTML'
});
} catch (e) {
return;
}
}
static async handleViewUser(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) return;
const telegramId = callbackQuery.data.replace('view_user_', '');
const chatId = callbackQuery.message.chat.id;
try {
const detailedUser = await UserService.getDetailedUserByTelegramId(telegramId);
const user = await UserService.getUserByTelegramId(telegramId);
if (!detailedUser) {
await bot.sendMessage(chatId, 'User not found.');
return;
}
// Get recent transactions
const transactions = await db.allAsync(`
SELECT t.amount, t.created_at, t.wallet_type, t.tx_hash
FROM transactions t
JOIN users u ON t.user_id = u.id
WHERE u.telegram_id = ?
ORDER BY t.created_at DESC
LIMIT 5
`, [telegramId]);
// Get recent purchases
const purchases = await db.allAsync(`
SELECT p.quantity, p.total_price, p.purchase_date,
pr.name as product_name
FROM purchases p
JOIN products pr ON p.product_id = pr.id
JOIN users u ON p.user_id = u.id
WHERE u.telegram_id = ?
ORDER BY p.purchase_date DESC
LIMIT 5
`, [telegramId]);
const message = `
👤 User Profile:
ID: ${telegramId}
📍 Location: ${detailedUser.country || 'Not set'}, ${detailedUser.city || 'Not set'}, ${detailedUser.district || 'Not set'}
📊 Activity:
- Total Purchases: ${detailedUser.purchase_count}
- Total Spent: $${detailedUser.total_spent || 0}
- Active Wallets: ${detailedUser.crypto_wallet_count}
- Bonus Balance: $${user.bonus_balance || 0}
- Total Balance: $${(user.total_balance || 0) + (user.bonus_balance || 0)}
💰 Recent Transactions:
${transactions.map(t => `${t.amount} ${t.wallet_type} (${t.tx_hash})`).join('\n')}
🛍 Recent Purchases:
${purchases.map(p => `${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
`;
const keyboard = {
inline_keyboard: [
[
{text: '💰 Edit Balance', callback_data: `edit_user_balance_${telegramId}`},
{text: '📍 Edit Location', callback_data: `edit_user_location_${telegramId}`}
],
[
{text: '🚫 Block User', callback_data: `block_user_${telegramId}`},
{text: '❌ Delete User', callback_data: `delete_user_${telegramId}`}
],
[{text: '« Back to User List', callback_data: `list_users_0`}]
]
};
await bot.editMessageText(message, {
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: keyboard,
parse_mode: 'HTML'
});
} catch (error) {
console.error('Error in handleViewUser:', error);
await bot.sendMessage(chatId, 'Error loading user details. Please try again.');
}
}
static async handleDeleteUser(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const telegramId = callbackQuery.data.replace('delete_user_', '');
const chatId = callbackQuery.message.chat.id;
try {
const keyboard = {
inline_keyboard: [
[
{text: '✅ Confirm Delete', callback_data: `confirm_delete_user_${telegramId}`},
{text: '❌ Cancel', callback_data: `view_user_${telegramId}`}
]
]
};
await bot.editMessageText(
`⚠️ Are you sure you want to delete user ${telegramId}?\n\nThis action will:\n- Delete all user data\n- Remove all wallets\n- Erase purchase history\n\nThis action cannot be undone!`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: keyboard,
parse_mode: 'HTML'
}
);
} catch (error) {
console.error('Error in handleDeleteUser:', error);
await bot.sendMessage(chatId, 'Error processing delete request. Please try again.');
}
}
static async handleConfirmDelete(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const telegramId = callbackQuery.data.replace('confirm_delete_user_', '');
const chatId = callbackQuery.message.chat.id;
try {
await UserService.updateUserStatus(telegramId, 1);
const keyboard = {
inline_keyboard: [
[{text: '« Back to User List', callback_data: 'admin_users'}]
]
};
try {
await bot.sendMessage(telegramId, '⚠Your account has been deleted by administrator');
} catch (e) {
// ignore if we can't notify user
}
await bot.editMessageText(
`✅ User ${telegramId} has been successfully deleted.`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: keyboard
}
);
} catch (error) {
console.error('Error in handleConfirmDelete:', error);
await bot.sendMessage(chatId, 'Error deleting user. Please try again.');
}
}
static async handleBlockUser(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const telegramId = callbackQuery.data.replace('block_user_', '');
const chatId = callbackQuery.message.chat.id;
try {
const keyboard = {
inline_keyboard: [
[
{text: '✅ Confirm Block', callback_data: `confirm_block_user_${telegramId}`},
{text: '❌ Cancel', callback_data: `view_user_${telegramId}`}
]
]
};
await bot.editMessageText(
`⚠️ Are you sure you want to block user ${telegramId}?`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: keyboard,
parse_mode: 'HTML'
}
);
} catch (error) {
console.error('Error in handleBlockUser:', error);
await bot.sendMessage(chatId, 'Error processing block request. Please try again.');
}
}
static async handleConfirmBlock(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const telegramId = callbackQuery.data.replace('confirm_block_user_', '');
const chatId = callbackQuery.message.chat.id;
try {
await UserService.updateUserStatus(telegramId, 2);
const keyboard = {
inline_keyboard: [
[{text: '« Back to User List', callback_data: 'admin_users'}]
]
};
try {
await bot.sendMessage(telegramId, '⚠Your account has been blocked by administrator');
} catch (e) {
// ignore if we can't notify user
}
await bot.editMessageText(
`✅ User ${telegramId} has been successfully blocked.`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: keyboard
}
);
} catch (error) {
console.error('Error in handleConfirmBlock:', error);
await bot.sendMessage(chatId, 'Error blocking user. Please try again.');
}
}
static async handleEditUserBalance(callbackQuery) {
if (!this.isAdmin(callbackQuery.from.id)) {
return;
}
const telegramId = callbackQuery.data.replace('edit_user_balance_', '');
const chatId = callbackQuery.message.chat.id;
try {
const user = await UserService.getUserByTelegramId(telegramId);
if (!user) {
await bot.sendMessage(chatId, 'User not found.');
return;
}
await bot.editMessageText(
`Enter new value for bonus balance. \n\n👥 User: ${telegramId}\n💰 Bonus Balance Now: $${user.bonus_balance.toFixed(2)}`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
}
);
userStates.set(chatId, { action: "edit_bonus_balance", telegram_id: telegramId });
} catch (error) {
console.error('Error in handleEditUserBalance:', error);
await bot.sendMessage(chatId, 'Error loading user wallets. Please try again.');
}
}
static async handleBonusBalanceInput(msg) {
if (!this.isAdmin(msg.from.id)) {
return;
}
const chatId = msg.chat.id;
const state = userStates.get(chatId);
if (!state || state.action !== 'edit_bonus_balance') {
return false;
}
const newValue = parseFloat(msg.text);
if (isNaN(newValue)) {
await bot.sendMessage(chatId, 'Invalid value. Try again');
return;
}
try {
await db.runAsync(`UPDATE users SET bonus_balance = ? WHERE telegram_id = ?`, [newValue, state.telegram_id])
await bot.sendMessage(chatId, '✅ Done')
} catch (e) {
await bot.sendMessage(chatId, 'Something went wrong');
}
userStates.delete(chatId);
}
}