From c9bcb0922180e696181e99a8e91901395afb52ff Mon Sep 17 00:00:00 2001 From: NW Date: Tue, 24 Dec 2024 09:19:14 +0000 Subject: [PATCH] udpdate wallet function --- src/config/database.js | 2 +- .../adminHandlers/adminWalletsHandler.js | 166 ++++++++++++++---- .../userHandlers/userWalletsHandler.js | 5 +- src/index.js | 4 +- src/services/walletService.js | 24 +-- src/utils/walletGenerator.js | 23 +-- 6 files changed, 155 insertions(+), 69 deletions(-) diff --git a/src/config/database.js b/src/config/database.js index 3979b3b..6bd1071 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -109,7 +109,7 @@ const initDb = async () => { wallet_type TEXT NOT NULL, address TEXT NOT NULL, derivation_path TEXT NOT NULL, - encrypted_mnemonic TEXT NOT NULL, + mnemonic TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE(user_id, wallet_type) diff --git a/src/handlers/adminHandlers/adminWalletsHandler.js b/src/handlers/adminHandlers/adminWalletsHandler.js index bd223f4..4fefc77 100644 --- a/src/handlers/adminHandlers/adminWalletsHandler.js +++ b/src/handlers/adminHandlers/adminWalletsHandler.js @@ -6,6 +6,11 @@ import fs from 'fs'; import csvWriter from 'csv-writer'; export default class AdminWalletsHandler { + // Метод для проверки, является ли пользователь администратором + static isAdmin(userId) { + return config.ADMIN_IDS.includes(userId.toString()); + } + static async handleWalletManagement(msg) { const chatId = msg.chat.id; @@ -42,6 +47,9 @@ export default class AdminWalletsHandler { const walletType = action.split('_').pop(); try { + // Удаляем предыдущее сообщение перед отправкой нового + await bot.deleteMessage(chatId, callbackQuery.message.message_id); + // Получаем все кошельки выбранного типа const wallets = await WalletService.getWalletsByType(walletType); @@ -62,14 +70,32 @@ export default class AdminWalletsHandler { } static async displayWalletsPage(chatId, wallets, walletType, totalBalance, page) { - const pageSize = 50; + const pageSize = 5; const startIndex = page * pageSize; const endIndex = startIndex + pageSize; const walletsPage = wallets.slice(startIndex, endIndex); - + // Формируем список кошельков с балансами - const walletList = walletsPage.map(wallet => `${wallet.address} - ${wallet.balance}`).join('\n'); - + let walletList = ''; + for (const wallet of walletsPage) { + const walletUtilsInstance = new WalletUtils( + wallet.wallet_type.startsWith('BTC') ? wallet.address : null, + wallet.wallet_type.startsWith('LTC') ? wallet.address : null, + wallet.wallet_type.startsWith('TRON') ? wallet.address : null, + wallet.wallet_type.startsWith('ETH') ? wallet.address : null, + null, // userId не нужен для админки + Date.now() - 30 * 24 * 60 * 60 * 1000 + ); + + const balances = await walletUtilsInstance.getAllBalances(); + const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 }; + + walletList += `💰 *${wallet.wallet_type}*\n`; + walletList += `├ Balance: ${balance.amount.toFixed(8)} ${wallet.wallet_type.split(' ')[0]}\n`; + walletList += `├ Value: $${balance.usdValue.toFixed(2)}\n`; + walletList += `└ Address: \`${wallet.address}\`\n\n`; + } + // Создаем клавиатуру с пагинацией const keyboard = { inline_keyboard: [ @@ -83,23 +109,22 @@ export default class AdminWalletsHandler { ] ] }; - + // Убираем кнопку "Назад", если это первая страница if (page === 0) { keyboard.inline_keyboard[0].shift(); } - + // Убираем кнопку "Далее", если это последняя страница if (endIndex >= wallets.length) { keyboard.inline_keyboard[0].pop(); } - + // Отправляем сообщение с суммарным балансом и списком кошельков - await bot.editMessageText( + await bot.sendMessage( + chatId, `Total Balance for ${walletType}: $${totalBalance.toFixed(2)}\n\nWallets (${startIndex + 1}-${endIndex} of ${wallets.length}):\n${walletList}`, { - chat_id: chatId, - message_id: callbackQuery.message.message_id, parse_mode: 'Markdown', reply_markup: keyboard } @@ -130,46 +155,97 @@ export default class AdminWalletsHandler { static async handlePagination(callbackQuery) { const action = callbackQuery.data; const chatId = callbackQuery.message.chat.id; - const [actionType, walletType, page] = action.split('_'); - - // Получаем все кошельки выбранного типа - const wallets = await WalletService.getWalletsByType(walletType); - - // Вычисляем суммарный баланс - const totalBalance = await this.calculateTotalBalance(wallets); - - // Отображаем страницу с учетом пагинации - await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, parseInt(page)); + + // Используем регулярное выражение для извлечения номера страницы + const match = action.match(/next_page_(.+)_(\d+)/) || action.match(/prev_page_(.+)_(\d+)/); + if (!match) { + console.error('Invalid pagination action:', action); + await bot.sendMessage(chatId, 'Invalid pagination action. Please try again.'); + return; + } + + const walletType = match[1]; // Тип кошелька (например, BTC) + const pageNumber = parseInt(match[2]); // Номер страницы + + try { + // Удаляем предыдущее сообщение перед отправкой нового + await bot.deleteMessage(chatId, callbackQuery.message.message_id); + + // Получаем все кошельки выбранного типа + const wallets = await WalletService.getWalletsByType(walletType); + + // Вычисляем суммарный баланс + const totalBalance = await this.calculateTotalBalance(wallets); + + // Отображаем страницу с учетом пагинации + await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, pageNumber); + } catch (error) { + console.error('Error fetching wallets:', error); + await bot.sendMessage(chatId, 'Failed to fetch wallets. Please try again later.'); + } } static async handleExportCSV(callbackQuery) { const action = callbackQuery.data; const chatId = callbackQuery.message.chat.id; const walletType = action.split('_').pop(); - + try { + // Удаляем предыдущее сообщение перед отправкой нового + await bot.deleteMessage(chatId, callbackQuery.message.message_id); + // Получаем все кошельки выбранного типа const wallets = await WalletService.getWalletsByType(walletType); - + if (wallets.length === 0) { await bot.sendMessage(chatId, `No wallets found for ${walletType}.`); return; } - + + // Расшифровываем мнемоническую фразу и получаем балансы + const walletsWithData = await Promise.all(wallets.map(async (wallet) => { + if (!wallet.mnemonic) { + console.error(`Mnemonic is missing for wallet with ID ${wallet.id}`); + return null; // Пропускаем кошелек, если mnemonic отсутствует + } + + const walletUtilsInstance = new WalletUtils( + wallet.wallet_type.startsWith('BTC') ? wallet.address : null, + wallet.wallet_type.startsWith('LTC') ? wallet.address : null, + wallet.wallet_type.startsWith('TRON') ? wallet.address : null, + wallet.wallet_type.startsWith('ETH') ? wallet.address : null, + null, // userId не нужен для админки + Date.now() - 30 * 24 * 60 * 60 * 1000 + ); + + const balances = await walletUtilsInstance.getAllBalances(); + const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 }; + + return { + address: wallet.address, + balance: balance.amount.toFixed(8), + mnemonic: wallet.mnemonic + }; + })); + + // Убираем null значения из массива + const filteredWallets = walletsWithData.filter(wallet => wallet !== null); + // Создаем CSV-файл const csv = csvWriter.createObjectCsvWriter({ path: `wallets_${walletType}.csv`, header: [ { id: 'address', title: 'Address' }, - { id: 'balance', title: 'Balance' } + { id: 'balance', title: 'Balance' }, + { id: 'mnemonic', title: 'Mnemonic' } ] }); - - await csv.writeRecords(wallets); - + + await csv.writeRecords(filteredWallets); + // Отправляем файл пользователю await bot.sendDocument(chatId, fs.createReadStream(`wallets_${walletType}.csv`)); - + // Удаляем временный файл fs.unlinkSync(`wallets_${walletType}.csv`); } catch (error) { @@ -177,8 +253,38 @@ export default class AdminWalletsHandler { await bot.sendMessage(chatId, 'Failed to export wallets to CSV. Please try again later.'); } } + + // Метод для возврата к списку типов кошельков + static async handleBackToWalletTypes(callbackQuery) { + const chatId = callbackQuery.message.chat.id; - static isAdmin(userId) { - return config.ADMIN_IDS.includes(userId.toString()); + try { + // Удаляем предыдущее сообщение перед отправкой нового + await bot.deleteMessage(chatId, callbackQuery.message.message_id); + + const keyboard = { + reply_markup: { + inline_keyboard: [ + [ + { text: 'Bitcoin (BTC)', callback_data: 'wallet_type_BTC' }, + { text: 'Litecoin (LTC)', callback_data: 'wallet_type_LTC' } + ], + [ + { text: 'Tether (USDT)', callback_data: 'wallet_type_USDT' }, + { text: 'Ethereum (ETH)', callback_data: 'wallet_type_ETH' } + ], + [ + { text: 'TRON (TRX)', callback_data: 'wallet_type_TRON' } + ] + ] + } + }; + + // Отправляем новое сообщение с клавиатурой + await bot.sendMessage(chatId, 'Select wallet type:', keyboard); + } catch (error) { + console.error('Error in handleBackToWalletTypes:', error); + await bot.sendMessage(chatId, 'An error occurred. Please try again later.'); + } } } \ No newline at end of file diff --git a/src/handlers/userHandlers/userWalletsHandler.js b/src/handlers/userHandlers/userWalletsHandler.js index 21c0fc4..3115014 100644 --- a/src/handlers/userHandlers/userWalletsHandler.js +++ b/src/handlers/userHandlers/userWalletsHandler.js @@ -275,7 +275,6 @@ export default class UserWalletsHandler { // Generate new wallets const mnemonic = await WalletGenerator.generateMnemonic(); const wallets = await WalletGenerator.generateWallets(mnemonic); - const encryptedMnemonic = await WalletGenerator.encryptMnemonic(mnemonic, telegramId); // Get the base wallet type (ETH for ERC-20, TRON for TRC-20) const baseType = this.getBaseWalletType(walletType); @@ -298,14 +297,14 @@ export default class UserWalletsHandler { // Store the new wallet await db.runAsync( `INSERT INTO crypto_wallets ( - user_id, wallet_type, address, derivation_path, encrypted_mnemonic + user_id, wallet_type, address, derivation_path, mnemonic ) VALUES (?, ?, ?, ?, ?)`, [ user.id, baseType, wallets[baseType].address, wallets[baseType].path, - encryptedMnemonic + mnemonic ] ); diff --git a/src/index.js b/src/index.js index 5a91b35..6019b0f 100644 --- a/src/index.js +++ b/src/index.js @@ -350,8 +350,8 @@ bot.on('callback_query', async (callbackQuery) => { logDebug(action, 'handleExportCSV'); await adminWalletsHandler.handleExportCSV(callbackQuery); } else if (action === 'back_to_wallet_types') { - logDebug(action, 'handleWalletManagement'); - await adminWalletsHandler.handleWalletManagement(msg); + logDebug(action, 'handleBackToWalletTypes'); + await adminWalletsHandler.handleBackToWalletTypes(callbackQuery); } // Dump manage else if (action === "export_database") { diff --git a/src/services/walletService.js b/src/services/walletService.js index 127afb1..e3c0185 100644 --- a/src/services/walletService.js +++ b/src/services/walletService.js @@ -20,18 +20,20 @@ class WalletService { } // Добавляем метод для получения кошельков по типу static async getWalletsByType(walletType) { - try { - const wallets = await db.allAsync( - `SELECT * FROM crypto_wallets WHERE wallet_type = ?`, - [walletType] - ); - - return wallets; - } catch (error) { - console.error('Error fetching wallets by type:', error); - throw new Error('Failed to fetch wallets by type'); + try { + const wallets = await db.allAsync( + `SELECT * + FROM crypto_wallets + WHERE wallet_type = ?`, + [walletType] + ); + + return wallets; + } catch (error) { + console.error('Error fetching wallets by type:', error); + throw new Error('Failed to fetch wallets by type'); + } } } -} export default WalletService; \ No newline at end of file diff --git a/src/utils/walletGenerator.js b/src/utils/walletGenerator.js index 4563c16..9261dbd 100644 --- a/src/utils/walletGenerator.js +++ b/src/utils/walletGenerator.js @@ -11,34 +11,13 @@ const ECPair = ECPairFactory(ecc); export default class WalletGenerator { static async generateMnemonic() { try { - return bip39.generateMnemonic(256); // 24 words for maximum security + return bip39.generateMnemonic(128); // 12 words for maximum security } catch (error) { console.error('Error generating mnemonic:', error); throw new Error('Failed to generate mnemonic'); } } - static async encryptMnemonic(mnemonic, userId) { - try { - const key = process.env.ENCRYPTION_KEY || 'default-key-12345'; - return CryptoJS.AES.encrypt(mnemonic, key + userId.toString()).toString(); - } catch (error) { - console.error('Error encrypting mnemonic:', error); - throw new Error('Failed to encrypt mnemonic'); - } - } - - static async decryptMnemonic(encryptedMnemonic, userId) { - try { - const key = process.env.ENCRYPTION_KEY || 'default-key-12345'; - const bytes = CryptoJS.AES.decrypt(encryptedMnemonic, key + userId.toString()); - return bytes.toString(CryptoJS.enc.Utf8); - } catch (error) { - console.error('Error decrypting mnemonic:', error); - throw new Error('Failed to decrypt mnemonic'); - } - } - static async generateWallets(mnemonic) { try { const seed = await bip39.mnemonicToSeed(mnemonic);