update planned wallets function
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
// database.js
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
import sqlite3 from 'sqlite3';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|||||||
@@ -15,90 +15,145 @@ export default class AdminUserHandler {
|
|||||||
|
|
||||||
static async calculateStatistics() {
|
static async calculateStatistics() {
|
||||||
try {
|
try {
|
||||||
|
// Получаем общую статистику по пользователям
|
||||||
const users = await db.allAsync(`
|
const users = await db.allAsync(`
|
||||||
SELECT
|
SELECT
|
||||||
u.*,
|
u.*,
|
||||||
COUNT(DISTINCT p.id) as total_purchases,
|
COUNT(DISTINCT p.id) as total_purchases,
|
||||||
COUNT(DISTINCT cw.id) as total_wallets
|
COUNT(DISTINCT cw.id) as total_wallets
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN purchases p ON u.id = p.user_id
|
LEFT JOIN purchases p ON u.id = p.user_id
|
||||||
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
|
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
|
||||||
LEFT JOIN transactions t ON u.id = t.user_id
|
LEFT JOIN transactions t ON u.id = t.user_id
|
||||||
GROUP BY u.id
|
GROUP BY u.id
|
||||||
ORDER BY u.created_at DESC
|
ORDER BY u.created_at DESC
|
||||||
` );
|
`);
|
||||||
|
|
||||||
// Calculate general statistics
|
// Общие метрики
|
||||||
const totalUsers = users.length;
|
const totalUsers = users.length;
|
||||||
const activeUsers = users.filter(u => u.total_purchases > 0).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 bonusBalance = users.reduce((sum, u) => sum + (u.bonus_balance || 0), 0);
|
||||||
const totalPurchases = users.reduce((sum, u) => sum + (u.total_purchases || 0), 0);
|
const totalPurchases = users.reduce((sum, u) => sum + (u.total_purchases || 0), 0);
|
||||||
|
|
||||||
// Create statistics message
|
// Рассчитываем общий баланс активных и архивных кошельков
|
||||||
|
let totalActiveWalletsBalance = 0;
|
||||||
|
let totalArchivedWalletsBalance = 0;
|
||||||
|
|
||||||
|
for (const user of users) {
|
||||||
|
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
|
||||||
|
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
|
||||||
|
totalActiveWalletsBalance += activeWalletsBalance;
|
||||||
|
totalArchivedWalletsBalance += archivedWalletsBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рассчитываем общий реальный баланс (крипто + бонусы)
|
||||||
|
const totalRealBalance = totalActiveWalletsBalance + totalArchivedWalletsBalance + bonusBalance;
|
||||||
|
|
||||||
|
// Получаем статистику по транзакциям
|
||||||
|
const totalTransactions = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as total_transactions FROM transactions
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Получаем статистику по продуктам
|
||||||
|
const totalProducts = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as total_products FROM products
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Получаем статистику по локациям
|
||||||
|
const totalLocations = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as total_locations FROM locations
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Получаем статистику по категориям
|
||||||
|
const totalCategories = await db.getAsync(`
|
||||||
|
SELECT COUNT(*) as total_categories FROM categories
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Формируем сообщение со статистикой
|
||||||
let message = `📊 System Statistics\n\n`;
|
let message = `📊 System Statistics\n\n`;
|
||||||
message += `👥 Total Users: ${totalUsers}\n`;
|
message += `👥 Total Users: ${totalUsers}\n`;
|
||||||
message += `✅ Active Users: ${activeUsers}\n`;
|
message += `✅ Active Users: ${activeUsers}\n`;
|
||||||
message += `💰 Bonus Balance: $${bonusBalance.toFixed(2)}\n`;
|
message += `💰 Total All Users Balance: $${totalRealBalance.toFixed(2)}\n`;
|
||||||
message += `💰 Total Balance: $${(totalBalance + bonusBalance).toFixed(2)}\n`;
|
message += ` ├ Active Wallets Balance: $${totalActiveWalletsBalance.toFixed(2)}\n`;
|
||||||
message += `🛍 Total Purchases: ${totalPurchases}`;
|
message += ` ├ Archived Wallets Balance: $${totalArchivedWalletsBalance.toFixed(2)}\n`;
|
||||||
|
message += ` └ Bonus Balance: $${bonusBalance.toFixed(2)}\n`;
|
||||||
|
message += `🛍 Total Purchases: ${totalPurchases}\n`;
|
||||||
|
message += `💸 Total Transactions: ${totalTransactions.total_transactions}\n`;
|
||||||
|
message += `📦 Total Products: ${totalProducts.total_products}\n`;
|
||||||
|
message += `📍 Total Locations: ${totalLocations.total_locations}\n`;
|
||||||
|
message += `📂 Total Categories: ${totalCategories.total_categories}\n`;
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null
|
console.error('Error in calculateStatistics:', error);
|
||||||
|
return 'Error loading statistics. Please try again.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async viewUserPage(page) {
|
static async viewUserPage(page) {
|
||||||
const limit = 10;
|
const limit = 10;
|
||||||
const offset = (page || 0) * limit;
|
const offset = (page || 0) * limit;
|
||||||
|
|
||||||
const previousPage = page > 0 ? page - 1 : 0;
|
const previousPage = page > 0 ? page - 1 : 0;
|
||||||
const nextPage = page + 1;
|
const nextPage = page + 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const users = await db.allAsync(`
|
const users = await db.allAsync(`
|
||||||
SELECT
|
SELECT
|
||||||
u.*,
|
u.*,
|
||||||
COUNT(DISTINCT p.id) as total_purchases,
|
COUNT(DISTINCT p.id) as total_purchases,
|
||||||
COUNT(DISTINCT cw.id) as total_wallets
|
COUNT(DISTINCT cw.id) as total_wallets
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN purchases p ON u.id = p.user_id
|
LEFT JOIN purchases p ON u.id = p.user_id
|
||||||
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
|
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
|
||||||
LEFT JOIN transactions t ON u.id = t.user_id
|
LEFT JOIN transactions t ON u.id = t.user_id
|
||||||
GROUP BY u.id
|
GROUP BY u.id
|
||||||
ORDER BY u.created_at DESC
|
ORDER BY u.created_at DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
OFFSET ?
|
OFFSET ?
|
||||||
`, [limit, offset]);
|
`, [limit, offset]);
|
||||||
|
|
||||||
if ((users.length === 0) && (page == 0)) {
|
if ((users.length === 0) && (page == 0)) {
|
||||||
return {text: 'No users registered yet.'};
|
return { text: 'No users registered yet.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((users.length === 0) && (page > 0)) {
|
if ((users.length === 0) && (page > 0)) {
|
||||||
return await this.viewUserPage(page - 1);
|
return await this.viewUserPage(page - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const statistics = await this.calculateStatistics()
|
// Calculate balances for each user
|
||||||
|
const usersWithBalances = await Promise.all(users.map(async (user) => {
|
||||||
|
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
|
||||||
|
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
|
||||||
|
const totalBalance = (user.total_balance || 0) + (user.bonus_balance || 0) + activeWalletsBalance + archivedWalletsBalance;
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
activeWalletsBalance,
|
||||||
|
archivedWalletsBalance,
|
||||||
|
totalBalance
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
const statistics = await this.calculateStatistics();
|
||||||
const message = `${statistics}\n\nSelect a user from the list below:`;
|
const message = `${statistics}\n\nSelect a user from the list below:`;
|
||||||
|
|
||||||
// Create inline keyboard with user list
|
// Create inline keyboard with user list
|
||||||
const keyboard = {
|
const keyboard = {
|
||||||
inline_keyboard: users.map(user => [{
|
inline_keyboard: usersWithBalances.map(user => [{
|
||||||
text: `ID: ${user.telegram_id} | Nickname: ${user.username ? "@" + user.username : "None"} | Balance: $${(user.total_balance || 0) + (user.bonus_balance || 0)}`,
|
text: `ID: ${user.telegram_id} | Nickname: ${user.username ? "@" + user.username : "None"} | Balance: $${user.totalBalance.toFixed(2)}`,
|
||||||
callback_data: `view_user_${user.telegram_id}`
|
callback_data: `view_user_${user.telegram_id}`
|
||||||
}])
|
}])
|
||||||
};
|
};
|
||||||
|
|
||||||
keyboard.inline_keyboard.push([
|
keyboard.inline_keyboard.push([
|
||||||
{text: `«`, callback_data: `list_users_${previousPage}`},
|
{ text: `«`, callback_data: `list_users_${previousPage}` },
|
||||||
{text: `»`, callback_data: `list_users_${nextPage}`},
|
{ text: `»`, callback_data: `list_users_${nextPage}` },
|
||||||
])
|
]);
|
||||||
|
|
||||||
return {text: message, markup: keyboard}
|
return { text: message, markup: keyboard };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in handleUserList:', error);
|
console.error('Error in handleUserList:', error);
|
||||||
return {text: 'Error loading user list. Please try again.'}
|
return { text: 'Error loading user list. Please try again.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,19 +190,19 @@ export default class AdminUserHandler {
|
|||||||
|
|
||||||
static async handleViewUser(callbackQuery) {
|
static async handleViewUser(callbackQuery) {
|
||||||
if (!this.isAdmin(callbackQuery.from.id)) return;
|
if (!this.isAdmin(callbackQuery.from.id)) return;
|
||||||
|
|
||||||
const telegramId = callbackQuery.data.replace('view_user_', '');
|
const telegramId = callbackQuery.data.replace('view_user_', '');
|
||||||
const chatId = callbackQuery.message.chat.id;
|
const chatId = callbackQuery.message.chat.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const detailedUser = await UserService.getDetailedUserByTelegramId(telegramId);
|
const detailedUser = await UserService.getDetailedUserByTelegramId(telegramId);
|
||||||
const user = await UserService.getUserByTelegramId(telegramId);
|
const user = await UserService.getUserByTelegramId(telegramId);
|
||||||
|
|
||||||
if (!detailedUser) {
|
if (!detailedUser) {
|
||||||
await bot.sendMessage(chatId, 'User not found.');
|
await bot.sendMessage(chatId, 'User not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get recent transactions
|
// Get recent transactions
|
||||||
const transactions = await db.allAsync(`
|
const transactions = await db.allAsync(`
|
||||||
SELECT t.amount, t.created_at, t.wallet_type, t.tx_hash
|
SELECT t.amount, t.created_at, t.wallet_type, t.tx_hash
|
||||||
@@ -157,7 +212,7 @@ export default class AdminUserHandler {
|
|||||||
ORDER BY t.created_at DESC
|
ORDER BY t.created_at DESC
|
||||||
LIMIT 5
|
LIMIT 5
|
||||||
`, [telegramId]);
|
`, [telegramId]);
|
||||||
|
|
||||||
// Get recent purchases
|
// Get recent purchases
|
||||||
const purchases = await db.allAsync(`
|
const purchases = await db.allAsync(`
|
||||||
SELECT p.quantity, p.total_price, p.purchase_date,
|
SELECT p.quantity, p.total_price, p.purchase_date,
|
||||||
@@ -169,7 +224,7 @@ export default class AdminUserHandler {
|
|||||||
ORDER BY p.purchase_date DESC
|
ORDER BY p.purchase_date DESC
|
||||||
LIMIT 5
|
LIMIT 5
|
||||||
`, [telegramId]);
|
`, [telegramId]);
|
||||||
|
|
||||||
// Get pending purchases
|
// Get pending purchases
|
||||||
const pendingPurchases = await db.allAsync(`
|
const pendingPurchases = await db.allAsync(`
|
||||||
SELECT p.quantity, p.total_price, p.purchase_date,
|
SELECT p.quantity, p.total_price, p.purchase_date,
|
||||||
@@ -180,37 +235,40 @@ export default class AdminUserHandler {
|
|||||||
WHERE u.telegram_id = ? AND p.status = 'pending'
|
WHERE u.telegram_id = ? AND p.status = 'pending'
|
||||||
ORDER BY p.purchase_date DESC
|
ORDER BY p.purchase_date DESC
|
||||||
`, [telegramId]);
|
`, [telegramId]);
|
||||||
|
|
||||||
// Get wallet balances
|
// Get wallet balances
|
||||||
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
|
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
|
||||||
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
|
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
|
||||||
|
|
||||||
|
// Общий баланс (крипто + бонусы + total_balance)
|
||||||
|
const totalBalance = activeWalletsBalance + archivedWalletsBalance + user.bonus_balance + (user.total_balance || 0);
|
||||||
|
|
||||||
const message = `
|
const message = `
|
||||||
👤 User Profile:
|
👤 User Profile:
|
||||||
|
|
||||||
ID: ${telegramId}
|
ID: ${telegramId}
|
||||||
📍 Location: ${detailedUser.country || 'Not set'}, ${detailedUser.city || 'Not set'}, ${detailedUser.district || 'Not set'}
|
📍 Location: ${detailedUser.country || 'Not set'}, ${detailedUser.city || 'Not set'}, ${detailedUser.district || 'Not set'}
|
||||||
|
|
||||||
📊 Activity:
|
📊 Activity:
|
||||||
- Total Purchases: ${detailedUser.purchase_count}
|
- Total Purchases: ${detailedUser.purchase_count}
|
||||||
- Total Spent: $${detailedUser.total_spent || 0}
|
- Total Spent: $${detailedUser.total_spent || 0}
|
||||||
- Active Wallets: ${detailedUser.crypto_wallet_count} ($${activeWalletsBalance.toFixed(2)})
|
- Active Wallets: ${detailedUser.crypto_wallet_count} ($${activeWalletsBalance.toFixed(2)})
|
||||||
- Archived Wallets: ${detailedUser.archived_wallet_count} ($${archivedWalletsBalance.toFixed(2)})
|
- Archived Wallets: ${detailedUser.archived_wallet_count} ($${archivedWalletsBalance.toFixed(2)})
|
||||||
- Bonus Balance: $${user.bonus_balance || 0}
|
- Bonus Balance: $${user.bonus_balance || 0}
|
||||||
- Total Balance: $${((user.total_balance || 0) + (user.bonus_balance || 0)).toFixed(2)}
|
- Total Balance: $${totalBalance.toFixed(2)}
|
||||||
|
|
||||||
💰 Recent Transactions (Last 5 of ${transactions.length}):
|
💰 Recent Transactions (Last 5 of ${transactions.length}):
|
||||||
${transactions.map(t => ` • $${t.amount} ${t.wallet_type} (${t.tx_hash}) at ${new Date(t.created_at).toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}`).join('\n')}
|
${transactions.map(t => ` • $${t.amount} ${t.wallet_type} (${t.tx_hash}) at ${new Date(t.created_at).toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}`).join('\n')}
|
||||||
|
|
||||||
🛍 Recent Purchases (Last 5 of ${purchases.length}):
|
🛍 Recent Purchases (Last 5 of ${purchases.length}):
|
||||||
${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
|
${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
|
||||||
|
|
||||||
🕒 Pending Purchases:
|
🕒 Pending Purchases:
|
||||||
${pendingPurchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n') || ' • No pending purchases'}
|
${pendingPurchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n') || ' • No pending purchases'}
|
||||||
|
|
||||||
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
|
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const keyboard = {
|
const keyboard = {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
[
|
[
|
||||||
@@ -224,7 +282,7 @@ ${pendingPurchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total
|
|||||||
[{text: '« Back to User List', callback_data: `list_users_0`}]
|
[{text: '« Back to User List', callback_data: `list_users_0`}]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
await bot.editMessageText(message, {
|
await bot.editMessageText(message, {
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
message_id: callbackQuery.message.message_id,
|
message_id: callbackQuery.message.message_id,
|
||||||
|
|||||||
@@ -43,22 +43,22 @@ export default class AdminWalletsHandler {
|
|||||||
const action = callbackQuery.data;
|
const action = callbackQuery.data;
|
||||||
const chatId = callbackQuery.message.chat.id;
|
const chatId = callbackQuery.message.chat.id;
|
||||||
const walletType = action.split('_').pop();
|
const walletType = action.split('_').pop();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Удаляем предыдущее сообщение перед отправкой нового
|
// Удаляем предыдущее сообщение перед отправкой нового
|
||||||
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
|
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
|
||||||
|
|
||||||
// Получаем все кошельки выбранного типа
|
// Получаем все кошельки выбранного типа (активные и архивные)
|
||||||
const wallets = await WalletService.getWalletsByType(walletType);
|
const wallets = await WalletService.getWalletsByType(walletType);
|
||||||
|
|
||||||
if (wallets.length === 0) {
|
if (wallets.length === 0) {
|
||||||
await bot.sendMessage(chatId, `No wallets found for ${walletType}.`);
|
await bot.sendMessage(chatId, `No wallets found for ${walletType}.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вычисляем суммарный баланс
|
// Вычисляем суммарный баланс
|
||||||
const totalBalance = await this.calculateTotalBalance(wallets);
|
const totalBalance = await this.calculateTotalBalance(wallets);
|
||||||
|
|
||||||
// Отображаем первую страницу с пагинацией
|
// Отображаем первую страницу с пагинацией
|
||||||
await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, 0);
|
await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -72,7 +72,7 @@ export default class AdminWalletsHandler {
|
|||||||
const startIndex = page * pageSize;
|
const startIndex = page * pageSize;
|
||||||
const endIndex = startIndex + pageSize;
|
const endIndex = startIndex + pageSize;
|
||||||
const walletsPage = wallets.slice(startIndex, endIndex);
|
const walletsPage = wallets.slice(startIndex, endIndex);
|
||||||
|
|
||||||
// Формируем список кошельков с балансами
|
// Формируем список кошельков с балансами
|
||||||
let walletList = '';
|
let walletList = '';
|
||||||
for (const wallet of walletsPage) {
|
for (const wallet of walletsPage) {
|
||||||
@@ -83,16 +83,17 @@ export default class AdminWalletsHandler {
|
|||||||
null, // userId не нужен для админки
|
null, // userId не нужен для админки
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
);
|
);
|
||||||
|
|
||||||
const balances = await walletUtilsInstance.getAllBalances();
|
const balances = await walletUtilsInstance.getAllBalances();
|
||||||
const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 };
|
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
||||||
|
const balance = balances[baseType] || { amount: 0, usdValue: 0 };
|
||||||
|
|
||||||
walletList += `💰 *${wallet.wallet_type}*\n`;
|
walletList += `💰 *${wallet.wallet_type}*\n`;
|
||||||
walletList += `├ Balance: ${balance.amount.toFixed(8)} ${wallet.wallet_type}\n`;
|
walletList += `├ Balance: ${balance.amount.toFixed(8)} ${wallet.wallet_type}\n`;
|
||||||
walletList += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
|
walletList += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
|
||||||
walletList += `└ Address: \`${wallet.address}\`\n\n`;
|
walletList += `└ Address: \`${wallet.address}\`\n\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем клавиатуру с пагинацией
|
// Создаем клавиатуру с пагинацией
|
||||||
const keyboard = {
|
const keyboard = {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
@@ -106,17 +107,17 @@ export default class AdminWalletsHandler {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Убираем кнопку "Назад", если это первая страница
|
// Убираем кнопку "Назад", если это первая страница
|
||||||
if (page === 0) {
|
if (page === 0) {
|
||||||
keyboard.inline_keyboard[0].shift();
|
keyboard.inline_keyboard[0].shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Убираем кнопку "Далее", если это последняя страница
|
// Убираем кнопку "Далее", если это последняя страница
|
||||||
if (endIndex >= wallets.length) {
|
if (endIndex >= wallets.length) {
|
||||||
keyboard.inline_keyboard[0].pop();
|
keyboard.inline_keyboard[0].pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляем сообщение с суммарным балансом и списком кошельков
|
// Отправляем сообщение с суммарным балансом и списком кошельков
|
||||||
await bot.sendMessage(
|
await bot.sendMessage(
|
||||||
chatId,
|
chatId,
|
||||||
@@ -139,12 +140,13 @@ export default class AdminWalletsHandler {
|
|||||||
null, // userId не нужен для админки
|
null, // userId не нужен для админки
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
);
|
);
|
||||||
|
|
||||||
const balances = await walletUtilsInstance.getAllBalances();
|
const balances = await walletUtilsInstance.getAllBalances();
|
||||||
const balance = balances[wallet.wallet_type] || { usdValue: 0 };
|
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
||||||
|
const balance = balances[baseType] || { usdValue: 0 };
|
||||||
totalBalance += balance.usdValue;
|
totalBalance += balance.usdValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalBalance;
|
return totalBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
// userHandler.js
|
||||||
|
|
||||||
import config from "../../config/config.js";
|
import config from "../../config/config.js";
|
||||||
import bot from "../../context/bot.js";
|
import bot from "../../context/bot.js";
|
||||||
import UserService from "../../services/userService.js";
|
import UserService from "../../services/userService.js";
|
||||||
|
import WalletService from "../../services/walletService.js";
|
||||||
|
|
||||||
export default class UserHandler {
|
export default class UserHandler {
|
||||||
static async canUseBot(msg) {
|
static async canUseBot(msg) {
|
||||||
@@ -30,44 +33,51 @@ export default class UserHandler {
|
|||||||
static async showProfile(msg) {
|
static async showProfile(msg) {
|
||||||
const chatId = msg.chat.id;
|
const chatId = msg.chat.id;
|
||||||
const telegramId = msg.from.id;
|
const telegramId = msg.from.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await UserService.recalculateUserBalanceByTelegramId(telegramId);
|
await UserService.recalculateUserBalanceByTelegramId(telegramId);
|
||||||
const userStats = await UserService.getDetailedUserByTelegramId(telegramId);
|
const userStats = await UserService.getDetailedUserByTelegramId(telegramId);
|
||||||
|
|
||||||
if (!userStats) {
|
if (!userStats) {
|
||||||
await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получаем балансы активных и архивных кошельков
|
||||||
|
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(userStats.id);
|
||||||
|
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(userStats.id);
|
||||||
|
|
||||||
|
// Общий баланс (крипто + бонусы + total_balance)
|
||||||
|
const totalBalance = activeWalletsBalance + archivedWalletsBalance + userStats.bonus_balance + (userStats.total_balance || 0);
|
||||||
|
|
||||||
const locationText = userStats.country && userStats.city && userStats.district
|
const locationText = userStats.country && userStats.city && userStats.district
|
||||||
? `${userStats.country}, ${userStats.city}, ${userStats.district}`
|
? `${userStats.country}, ${userStats.city}, ${userStats.district}`
|
||||||
: 'Not set';
|
: 'Not set';
|
||||||
|
|
||||||
const text = `
|
const text = `
|
||||||
👤 *Your Profile*
|
👤 *Your Profile*
|
||||||
|
|
||||||
📱 Telegram ID: \`${telegramId}\`
|
📱 Telegram ID: \`${telegramId}\`
|
||||||
📍 Location: ${locationText}
|
📍 Location: ${locationText}
|
||||||
|
|
||||||
📊 Statistics:
|
📊 Statistics:
|
||||||
├ Total Purchases: ${userStats.purchase_count || 0}
|
├ Total Purchases: ${userStats.purchase_count || 0}
|
||||||
├ Total Spent: $${userStats.total_spent || 0}
|
├ Total Spent: $${userStats.total_spent || 0}
|
||||||
├ Active Wallets: ${userStats.crypto_wallet_count || 0}
|
├ Active Wallets: ${userStats.crypto_wallet_count || 0} ($${activeWalletsBalance.toFixed(2)})
|
||||||
├ Archived Wallets: ${userStats.archived_wallet_count || 0}
|
├ Archived Wallets: ${userStats.archived_wallet_count || 0} ($${archivedWalletsBalance.toFixed(2)})
|
||||||
├ Bonus Balance: $${userStats.bonus_balance || 0}
|
├ Bonus Balance: $${userStats.bonus_balance || 0}
|
||||||
└ Available Balance: $${(userStats.total_balance || 0) + (userStats.bonus_balance || 0)}
|
└ Available Balance: $${totalBalance.toFixed(2)}
|
||||||
|
|
||||||
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
|
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const keyboard = {
|
const keyboard = {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
[{text: '📍 Set Location', callback_data: 'set_location'}],
|
[{text: '📍 Set Location', callback_data: 'set_location'}],
|
||||||
[{text: '❌ Delete Account', callback_data: 'delete_account'}]
|
[{text: '❌ Delete Account', callback_data: 'delete_account'}]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
await bot.sendMessage(chatId, text, {
|
await bot.sendMessage(chatId, text, {
|
||||||
parse_mode: 'Markdown',
|
parse_mode: 'Markdown',
|
||||||
reply_markup: keyboard
|
reply_markup: keyboard
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// userWalletsHandler.js
|
||||||
|
|
||||||
import db from '../../config/database.js';
|
import db from '../../config/database.js';
|
||||||
import WalletGenerator from '../../utils/walletGenerator.js';
|
import WalletGenerator from '../../utils/walletGenerator.js';
|
||||||
import WalletUtils from '../../utils/walletUtils.js';
|
import WalletUtils from '../../utils/walletUtils.js';
|
||||||
@@ -27,7 +29,7 @@ export default class UserWalletsHandler {
|
|||||||
|
|
||||||
// Получаем активные криптокошельки
|
// Получаем активные криптокошельки
|
||||||
const cryptoWallets = await db.allAsync(`
|
const cryptoWallets = await db.allAsync(`
|
||||||
SELECT wallet_type, address
|
SELECT wallet_type, address, balance
|
||||||
FROM crypto_wallets
|
FROM crypto_wallets
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
ORDER BY wallet_type
|
ORDER BY wallet_type
|
||||||
@@ -42,11 +44,10 @@ export default class UserWalletsHandler {
|
|||||||
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address, // ETH address
|
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address, // ETH address
|
||||||
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address, // USDT address
|
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address, // USDT address
|
||||||
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address, // USDC address
|
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address, // USDC address
|
||||||
updatedUser.id,
|
updatedUser.id
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const balances = await walletUtilsInstance.getAllBalances();
|
const balances = await walletUtilsInstance.getAllBalancesFromDB();
|
||||||
let totalUsdValue = 0;
|
let totalUsdValue = 0;
|
||||||
|
|
||||||
// Отображаем активные кошельки
|
// Отображаем активные кошельки
|
||||||
@@ -68,12 +69,12 @@ export default class UserWalletsHandler {
|
|||||||
// Бонусный баланс
|
// Бонусный баланс
|
||||||
message += `🎁 *Bonus Balance:* $${updatedUser.bonus_balance.toFixed(2)}\n`;
|
message += `🎁 *Bonus Balance:* $${updatedUser.bonus_balance.toFixed(2)}\n`;
|
||||||
|
|
||||||
// Общий баланс (крипто + бонусы)
|
// Общий баланс (крипто + бонусы + total_balance)
|
||||||
const totalBalance = totalUsdValue + updatedUser.bonus_balance;
|
const totalBalance = totalUsdValue + updatedUser.bonus_balance + (updatedUser.total_balance || 0);
|
||||||
message += `💰 *Total Balance:* $${totalBalance.toFixed(2)}\n`;
|
message += `💰 *Total Balance:* $${totalBalance.toFixed(2)}\n`;
|
||||||
|
|
||||||
// Доступный баланс (общий баланс минус расходы на покупки)
|
// Доступный баланс (общий баланс минус расходы на покупки)
|
||||||
const availableBalance = updatedUser.total_balance + updatedUser.bonus_balance;
|
const availableBalance = totalBalance; // Теперь availableBalance включает криптобаланс
|
||||||
message += `💳 *Available Balance:* $${availableBalance.toFixed(2)}\n`;
|
message += `💳 *Available Balance:* $${availableBalance.toFixed(2)}\n`;
|
||||||
} else {
|
} else {
|
||||||
message = 'You don\'t have any active wallets yet.';
|
message = 'You don\'t have any active wallets yet.';
|
||||||
@@ -112,7 +113,7 @@ export default class UserWalletsHandler {
|
|||||||
console.error('Error in showBalance:', error);
|
console.error('Error in showBalance:', error);
|
||||||
await bot.sendMessage(chatId, 'Error loading balance. Please try again.');
|
await bot.sendMessage(chatId, 'Error loading balance. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleTransactionHistory(callbackQuery, page = 0) {
|
static async handleTransactionHistory(callbackQuery, page = 0) {
|
||||||
const chatId = callbackQuery.message.chat.id;
|
const chatId = callbackQuery.message.chat.id;
|
||||||
@@ -259,10 +260,10 @@ export default class UserWalletsHandler {
|
|||||||
case 'ETH':
|
case 'ETH':
|
||||||
address = walletAddresses.eth;
|
address = walletAddresses.eth;
|
||||||
break;
|
break;
|
||||||
case 'USDT ERC-20':
|
case 'USDT':
|
||||||
address = walletAddresses.usdt;
|
address = walletAddresses.usdt;
|
||||||
break;
|
break;
|
||||||
case 'USDC ERC-20':
|
case 'USDC':
|
||||||
address = walletAddresses.usdc;
|
address = walletAddresses.usdc;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -22,58 +22,80 @@ class WalletService {
|
|||||||
static async getActiveWalletsBalance(userId) {
|
static async getActiveWalletsBalance(userId) {
|
||||||
try {
|
try {
|
||||||
const wallets = await db.allAsync(
|
const wallets = await db.allAsync(
|
||||||
`SELECT wallet_type, address
|
`SELECT wallet_type, balance
|
||||||
FROM crypto_wallets
|
FROM crypto_wallets
|
||||||
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'`,
|
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const prices = await WalletUtils.getCryptoPrices();
|
||||||
|
|
||||||
let totalBalance = 0;
|
let totalBalance = 0;
|
||||||
for (const wallet of wallets) {
|
for (const wallet of wallets) {
|
||||||
const walletUtils = new WalletUtils(
|
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
||||||
wallet.wallet_type === 'BTC' ? wallet.address : null,
|
const balance = wallet.balance || 0;
|
||||||
wallet.wallet_type === 'LTC' ? wallet.address : null,
|
|
||||||
wallet.wallet_type === 'ETH' ? wallet.address : null,
|
switch (baseType) {
|
||||||
wallet.wallet_type === 'USDT' ? wallet.address : null,
|
case 'BTC':
|
||||||
wallet.wallet_type === 'USDC' ? wallet.address : null,
|
totalBalance += balance * prices.btc;
|
||||||
userId
|
break;
|
||||||
);
|
case 'LTC':
|
||||||
|
totalBalance += balance * prices.ltc;
|
||||||
const balances = await walletUtils.getAllBalances();
|
break;
|
||||||
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
|
case 'ETH':
|
||||||
|
totalBalance += balance * prices.eth;
|
||||||
|
break;
|
||||||
|
case 'USDT':
|
||||||
|
totalBalance += balance; // USDT is 1:1 with USD
|
||||||
|
break;
|
||||||
|
case 'USDC':
|
||||||
|
totalBalance += balance; // USDC is 1:1 with USD
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalBalance;
|
return totalBalance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching active wallets balance:', error);
|
console.error('Error fetching active wallets balance:', error);
|
||||||
throw new Error('Failed to fetch active wallets balance');
|
throw new Error('Failed to fetch active wallets balance');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getArchivedWalletsBalance(userId) {
|
static async getArchivedWalletsBalance(userId) {
|
||||||
try {
|
try {
|
||||||
const wallets = await db.allAsync(
|
const wallets = await db.allAsync(
|
||||||
`SELECT wallet_type, address
|
`SELECT wallet_type, balance
|
||||||
FROM crypto_wallets
|
FROM crypto_wallets
|
||||||
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
|
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const prices = await WalletUtils.getCryptoPrices();
|
||||||
|
|
||||||
let totalBalance = 0;
|
let totalBalance = 0;
|
||||||
for (const wallet of wallets) {
|
for (const wallet of wallets) {
|
||||||
const walletUtils = new WalletUtils(
|
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
||||||
wallet.wallet_type === 'BTC' ? wallet.address : null,
|
const balance = wallet.balance || 0;
|
||||||
wallet.wallet_type === 'LTC' ? wallet.address : null,
|
|
||||||
wallet.wallet_type === 'ETH' ? wallet.address : null,
|
switch (baseType) {
|
||||||
wallet.wallet_type === 'USDT' ? wallet.address : null,
|
case 'BTC':
|
||||||
wallet.wallet_type === 'USDC' ? wallet.address : null,
|
totalBalance += balance * prices.btc;
|
||||||
userId
|
break;
|
||||||
);
|
case 'LTC':
|
||||||
|
totalBalance += balance * prices.ltc;
|
||||||
const balances = await walletUtils.getAllBalances();
|
break;
|
||||||
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
|
case 'ETH':
|
||||||
|
totalBalance += balance * prices.eth;
|
||||||
|
break;
|
||||||
|
case 'USDT':
|
||||||
|
totalBalance += balance; // USDT is 1:1 with USD
|
||||||
|
break;
|
||||||
|
case 'USDC':
|
||||||
|
totalBalance += balance; // USDC is 1:1 with USD
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalBalance;
|
return totalBalance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching archived wallets balance:', error);
|
console.error('Error fetching archived wallets balance:', error);
|
||||||
@@ -87,10 +109,10 @@ class WalletService {
|
|||||||
const wallets = await db.allAsync(
|
const wallets = await db.allAsync(
|
||||||
`SELECT *
|
`SELECT *
|
||||||
FROM crypto_wallets
|
FROM crypto_wallets
|
||||||
WHERE wallet_type = ?`,
|
WHERE wallet_type = ? OR wallet_type LIKE ?`,
|
||||||
[walletType]
|
[walletType, `${walletType}_%`]
|
||||||
);
|
);
|
||||||
|
|
||||||
return wallets;
|
return wallets;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching wallets by type:', error);
|
console.error('Error fetching wallets by type:', error);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// walletUtils.js
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import db from '../config/database.js'; // Импортируем базу данных
|
import db from '../config/database.js'; // Импортируем базу данных
|
||||||
|
|
||||||
@@ -264,24 +266,24 @@ export default class WalletUtils {
|
|||||||
|
|
||||||
async getAllBalancesFromDB() {
|
async getAllBalancesFromDB() {
|
||||||
const prices = await WalletUtils.getCryptoPrices();
|
const prices = await WalletUtils.getCryptoPrices();
|
||||||
|
|
||||||
const balances = {
|
const balances = {
|
||||||
BTC: { amount: 0, usdValue: 0 },
|
BTC: { amount: 0, usdValue: 0 },
|
||||||
LTC: { amount: 0, usdValue: 0 },
|
LTC: { amount: 0, usdValue: 0 },
|
||||||
ETH: { amount: 0, usdValue: 0 },
|
ETH: { amount: 0, usdValue: 0 },
|
||||||
'USDT ERC-20': { amount: 0, usdValue: 0 },
|
USDT: { amount: 0, usdValue: 0 },
|
||||||
'USDC ERC-20': { amount: 0, usdValue: 0 }
|
USDC: { amount: 0, usdValue: 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Получаем балансы из таблицы crypto_wallets
|
// Получаем балансы из таблицы crypto_wallets
|
||||||
const wallets = await db.allAsync(`
|
const wallets = await db.allAsync(`
|
||||||
SELECT wallet_type, balance FROM crypto_wallets WHERE user_id = ?
|
SELECT wallet_type, balance FROM crypto_wallets WHERE user_id = ?
|
||||||
`, [this.userId]);
|
`, [this.userId]);
|
||||||
|
|
||||||
for (const wallet of wallets) {
|
for (const wallet of wallets) {
|
||||||
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
const [baseType] = wallet.wallet_type.split('_'); // Учитываем только базовый тип
|
||||||
const balance = wallet.balance || 0;
|
const balance = wallet.balance || 0;
|
||||||
|
|
||||||
switch (baseType) {
|
switch (baseType) {
|
||||||
case 'BTC':
|
case 'BTC':
|
||||||
balances.BTC.amount += balance;
|
balances.BTC.amount += balance;
|
||||||
@@ -296,16 +298,16 @@ export default class WalletUtils {
|
|||||||
balances.ETH.usdValue += balance * prices.eth;
|
balances.ETH.usdValue += balance * prices.eth;
|
||||||
break;
|
break;
|
||||||
case 'USDT':
|
case 'USDT':
|
||||||
balances['USDT ERC-20'].amount += balance;
|
balances.USDT.amount += balance;
|
||||||
balances['USDT ERC-20'].usdValue += balance;
|
balances.USDT.usdValue += balance;
|
||||||
break;
|
break;
|
||||||
case 'USDC':
|
case 'USDC':
|
||||||
balances['USDC ERC-20'].amount += balance;
|
balances.USDC.amount += balance;
|
||||||
balances['USDC ERC-20'].usdValue += balance;
|
balances.USDC.usdValue += balance;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return balances;
|
return balances;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +317,7 @@ export default class WalletUtils {
|
|||||||
|
|
||||||
async getAllBalancesExt() {
|
async getAllBalancesExt() {
|
||||||
console.log('[DEBUG] getAllBalancesExt called'); // Логируем вызов метода
|
console.log('[DEBUG] getAllBalancesExt called'); // Логируем вызов метода
|
||||||
|
|
||||||
const [
|
const [
|
||||||
btcBalance,
|
btcBalance,
|
||||||
ltcBalance,
|
ltcBalance,
|
||||||
@@ -331,7 +333,7 @@ export default class WalletUtils {
|
|||||||
this.getUsdcErc20Balance(),
|
this.getUsdcErc20Balance(),
|
||||||
WalletUtils.getCryptoPrices()
|
WalletUtils.getCryptoPrices()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('[DEBUG] Balances fetched:', { // Логируем полученные балансы
|
console.log('[DEBUG] Balances fetched:', { // Логируем полученные балансы
|
||||||
btcBalance,
|
btcBalance,
|
||||||
ltcBalance,
|
ltcBalance,
|
||||||
@@ -340,13 +342,13 @@ export default class WalletUtils {
|
|||||||
usdcErc20Balance,
|
usdcErc20Balance,
|
||||||
prices
|
prices
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
|
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
|
||||||
LTC: { amount: ltcBalance, usdValue: ltcBalance * prices.ltc },
|
LTC: { amount: ltcBalance, usdValue: ltcBalance * prices.ltc },
|
||||||
ETH: { amount: ethBalance, usdValue: ethBalance * prices.eth },
|
ETH: { amount: ethBalance, usdValue: ethBalance * prices.eth },
|
||||||
'USDT ERC-20': { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
|
USDT: { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
|
||||||
'USDC ERC-20': { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
|
USDC: { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user