diff --git a/bot.js b/bot.js index f7bf78d..d8ccb29 100644 --- a/bot.js +++ b/bot.js @@ -1130,16 +1130,16 @@ const formatConversionRate = (conversionRate, fiatCode) => { return `${rate} ${fiatCode} / USDC`; }; -// const createDepositKeyboard = (depositId) => { -// return { -// inline_keyboard: [[ -// { -// text: `๐Ÿ”— View Deposit ${depositId}`, -// url: depositLink(depositId) -// } -// ]] -// }; -// }; +const createDepositKeyboard = (depositId) => { + return { + inline_keyboard: [[ + { + text: `๐Ÿ”— View Deposit ${depositId}`, + url: depositLink(depositId) + } + ]] + }; +}; // Sniper logic async function checkSniperOpportunity(depositId, depositAmount, currencyHash, conversionRate, verifierAddress) { @@ -1461,7 +1461,7 @@ bot.onText(/\/sniper (?!threshold)(.+)/, async (msg, match) => { const platform = parts[1] ? parts[1].toLowerCase() : null; const supportedCurrencies = Object.values(currencyHashToCode); - const supportedPlatforms = ['revolut', 'wise', 'cashapp', 'venmo', 'zelle', 'mercado pago', 'monzo','paypal']; + const supportedPlatforms = ['revolut', 'wise', 'cashapp', 'venmo', 'zelle']; if (!supportedCurrencies.includes(currency)) { bot.sendMessage(chatId, `โŒ Currency '${currency}' not supported.\n\n*Supported currencies:*\n${supportedCurrencies.join(', ')}`, { parse_mode: 'Markdown' }); @@ -1494,1058 +1494,41 @@ bot.onText(/\/unsnipe (.+)/, async (msg, match) => { bot.sendMessage(chatId, `๐ŸŽฏ Stopped sniping ${currency}${platformText}.`, { parse_mode: 'Markdown' }); }); -// Menu creation functions -const createMainMenu = () => { - return { - inline_keyboard: [ - [ - { text: '๐Ÿ“Š Deposit Tracking', callback_data: 'menu_deposits' }, - { text: '๐ŸŽฏ Sniper Setup', callback_data: 'menu_snipers' } - ], - [ - { text: '๐Ÿ“‹ My Status', callback_data: 'action_list' }, - { text: '๐Ÿ”ง Settings', callback_data: 'menu_settings' } - ], - [ - { text: 'โ“ Help', callback_data: 'action_help' }, - { text: '๐Ÿ“ˆ System Status', callback_data: 'action_status' } - ] - ] - }; -}; - -const createDepositMenu = () => { - return { - inline_keyboard: [ - [ - { text: '๐ŸŒ Listen to ALL Deposits', callback_data: 'action_deposit_all' } - ], - [ - { text: '๐Ÿ›‘ Stop Listening to All', callback_data: 'action_deposit_stop' } - ], - [ - { text: 'โž• Track Specific Deposit', callback_data: 'prompt_deposit_add' } - ], - [ - { text: 'โž– Remove Specific Deposit', callback_data: 'prompt_deposit_remove' } - ], - [ - { text: '๐Ÿ  Back to Main Menu', callback_data: 'menu_main' } - ] - ] - }; -}; - -const createSniperMenu = () => { - return { - inline_keyboard: [ - [ - { text: '๐ŸŽฏ Add Currency Sniper', callback_data: 'prompt_sniper_add' } - ], - [ - { text: '๐Ÿ“Š Set Alert Threshold', callback_data: 'prompt_threshold' } - ], - [ - { text: '๐Ÿ“‹ View My Snipers', callback_data: 'action_sniper_list' } - ], - [ - { text: '๐Ÿ—‘๏ธ Remove Sniper', callback_data: 'prompt_sniper_remove' } - ], - [ - { text: '๐Ÿงน Clear All Snipers', callback_data: 'action_sniper_clear' } - ], - [ - { text: '๐Ÿ  Back to Main Menu', callback_data: 'menu_main' } - ] - ] - }; -}; - -const createSettingsMenu = () => { - return { - inline_keyboard: [ - [ - { text: '๐Ÿ—‘๏ธ Clear All Data', callback_data: 'confirm_clearall' } - ], - [ - { text: '๐Ÿ”„ Refresh Status', callback_data: 'action_status' } - ], - [ - { text: '๐Ÿ  Back to Main Menu', callback_data: 'menu_main' } - ] - ] - }; -}; - -const createCurrencyKeyboard = () => { - const currencies = ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CHF', 'ARS', 'NOK', 'DKK', 'PLN', 'CZK', 'NZD', 'RON']; - const keyboard = []; - - // Create rows of 3 currencies each - for (let i = 0; i < currencies.length; i += 3) { - const row = currencies.slice(i, i + 3).map(curr => ({ - text: curr, - callback_data: `select_currency_${curr.toLowerCase()}` - })); - keyboard.push(row); - } - - // Add more currencies and cancel buttons - keyboard.push([ - { text: '๐ŸŒ More Currencies', callback_data: 'show_more_currencies' }, - { text: 'โŒ Cancel', callback_data: 'menu_snipers' } - ]); - - return { inline_keyboard: keyboard }; -}; - -const createPlatformKeyboard = (currency) => { - return { - inline_keyboard: [ - [ - { text: '๐ŸŒ All Platforms', callback_data: `sniper_${currency}_all` } - ], - [ - { text: '๐Ÿ’ณ Revolut', callback_data: `sniper_${currency}_revolut` }, - { text: '๐Ÿฆ Wise', callback_data: `sniper_${currency}_wise` } - ], - [ - { text: '๐Ÿ’ฐ PayPal', callback_data: `sniper_${currency}_paypal` }, - { text: '๐Ÿช Zelle', callback_data: `sniper_${currency}_zelle` } - ], - [ - { text: '๐Ÿ“ฑ CashApp', callback_data: `sniper_${currency}_cashapp` }, - { text: '๐Ÿ’ธ Venmo', callback_data: `sniper_${currency}_venmo` } - ], - [ - { text: '๐Ÿฆ Mercado Pago', callback_data: `sniper_${currency}_mercado pago` }, - { text: '๐Ÿ’ธ Monzo', callback_data: `sniper_${currency}_monzo` } - ], - [ - { text: '๐Ÿ’ธ PayPal', callback_data: `sniper_${currency}_paypal` } - ], - [ - { text: '๐Ÿ”™ Back to Currencies', callback_data: 'prompt_sniper_add' } - ] - ] - }; -}; - -const createConfirmKeyboard = (action) => { - return { - inline_keyboard: [ - [ - { text: 'โœ… Yes, Confirm', callback_data: `confirm_${action}` }, - { text: 'โŒ Cancel', callback_data: 'menu_main' } - ] - ] - }; -}; - -// Store user states for multi-step interactions -const userStates = new Map(); - -// Update the /start command to show the main menu -bot.onText(/\/start/, async (msg) => { - const chatId = msg.chat.id; - await db.initUser(chatId, msg.from.username, msg.from.first_name, msg.from.last_name); - - const welcomeMessage = ` -๐Ÿค– **Welcome to ZKP2P Tracker!** - -Track ZKP2P deposits and get arbitrage alerts in real-time. Use the menu below to get started: - -โ€ข **Deposit Tracking** - Monitor specific deposits or all activity -โ€ข **Sniper Setup** - Get alerts for profitable arbitrage opportunities -โ€ข **My Status** - View your current tracking settings -โ€ข **Settings** - Configure thresholds and manage your data - -Choose an option below to begin: -`.trim(); - - bot.sendMessage(chatId, welcomeMessage, { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); - -// Add /menu command for easy access -bot.onText(/\/menu/, (msg) => { - const chatId = msg.chat.id; - - bot.sendMessage(chatId, '๐Ÿ“‹ **Main Menu**\n\nChoose what you\'d like to do:', { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); - -// Handle callback queries (button presses) -bot.on('callback_query', async (callbackQuery) => { - const chatId = callbackQuery.message.chat.id; - const messageId = callbackQuery.message.message_id; - const data = callbackQuery.data; - - // Initialize user - await db.initUser(chatId, callbackQuery.from.username, callbackQuery.from.first_name, callbackQuery.from.last_name); - - try { - // Handle menu navigation - if (data === 'menu_main') { - await bot.editMessageText('๐Ÿ“‹ **Main Menu**\n\nChoose what you\'d like to do:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } - - else if (data === 'menu_deposits') { - await bot.editMessageText('๐Ÿ“Š **Deposit Tracking**\n\nManage your deposit notifications:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); - } - - else if (data === 'menu_snipers') { - await bot.editMessageText('๐ŸŽฏ **Sniper Setup**\n\nConfigure arbitrage alerts for better exchange rates:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - } - - else if (data === 'menu_settings') { - await bot.editMessageText('๐Ÿ”ง **Settings**\n\nManage your bot configuration:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSettingsMenu() - }); - } - - // Handle deposit actions - else if (data === 'action_deposit_all') { - await db.setUserListenAll(chatId, true); - await bot.editMessageText('๐ŸŒ **Now listening to ALL deposits!**\n\nYou will receive notifications for every event on every deposit.\n\nUse the menu to manage other settings.', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); - } - - else if (data === 'action_deposit_stop') { - await db.setUserListenAll(chatId, false); - await bot.editMessageText('๐Ÿ›‘ **Stopped listening to all deposits.**\n\nYou will now only receive notifications for specifically tracked deposits.', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); - } - - // Handle prompts for user input - else if (data === 'prompt_deposit_add') { - userStates.set(chatId, { action: 'waiting_deposit_add', messageId }); - await bot.editMessageText('โž• **Add Specific Deposit**\n\nPlease send the deposit ID(s) you want to track.\n\nExamples:\nโ€ข `123` - track single deposit\nโ€ข `123,456,789` - track multiple deposits\n\nSend your message now:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: [[ - { text: 'โŒ Cancel', callback_data: 'menu_deposits' } - ]] - } - }); - } - - else if (data === 'prompt_deposit_remove') { - userStates.set(chatId, { action: 'waiting_deposit_remove', messageId }); - await bot.editMessageText('โž– **Remove Specific Deposit**\n\nPlease send the deposit ID(s) you want to stop tracking.\n\nExamples:\nโ€ข `123` - remove single deposit\nโ€ข `123,456,789` - remove multiple deposits\n\nSend your message now:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: [[ - { text: 'โŒ Cancel', callback_data: 'menu_deposits' } - ]] - } - }); - } - - // Handle sniper actions - else if (data === 'prompt_sniper_add') { - await bot.editMessageText('๐ŸŽฏ **Add Currency Sniper**\n\nSelect a currency to snipe for arbitrage opportunities:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createCurrencyKeyboard() - }); - } - - else if (data === 'show_more_currencies') { - const moreCurrencies = ['AED', 'CNY', 'SEK', 'ILS', 'INR', 'KES', 'MXN', 'HKD', 'MYR', 'PHP', 'SAR', 'SGD', 'HUF', 'THB', 'TRY', 'VND', 'IDR', 'ZAR']; - const keyboard = []; - - for (let i = 0; i < moreCurrencies.length; i += 3) { - const row = moreCurrencies.slice(i, i + 3).map(curr => ({ - text: curr, - callback_data: `select_currency_${curr.toLowerCase()}` - })); - keyboard.push(row); - } - - keyboard.push([ - { text: '๐Ÿ”™ Back', callback_data: 'prompt_sniper_add' }, - { text: 'โŒ Cancel', callback_data: 'menu_snipers' } - ]); - - await bot.editMessageText('๐ŸŒ **More Currencies**\n\nSelect a currency:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: { inline_keyboard: keyboard } - }); - } - - // Handle currency selection - else if (data.startsWith('select_currency_')) { - const currency = data.replace('select_currency_', '').toUpperCase(); - await bot.editMessageText(`๐ŸŽฏ **Snipe ${currency}**\n\nChoose which platform(s) to monitor for ${currency} arbitrage opportunities:`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createPlatformKeyboard(currency.toLowerCase()) - }); - } - - // Handle sniper setup - else if (data.startsWith('sniper_')) { - const parts = data.split('_'); - const currency = parts[1].toUpperCase(); - const platform = parts[2] === 'all' ? null : parts[2]; - - await db.setUserSniper(chatId, currency, platform); - - const platformText = platform ? ` on ${platform}` : ' (all platforms)'; - await bot.editMessageText(`๐ŸŽฏ **Sniper activated for ${currency}${platformText}!**\n\nYou'll be alerted when new deposits offer better rates than market.\n\nConfigure more settings below:`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - } - - else if (data === 'prompt_threshold') { - userStates.set(chatId, { action: 'waiting_threshold', messageId }); - const currentThreshold = await db.getUserThreshold(chatId); - await bot.editMessageText(`๐Ÿ“Š **Set Alert Threshold**\n\nCurrent threshold: **${currentThreshold}%**\n\nEnter your new threshold percentage (e.g., 0.5 for 0.5%):\n\n*You'll be alerted when deposits offer rates this much better than market rates.*\n\nSend your message now:`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: [[ - { text: 'โŒ Cancel', callback_data: 'menu_snipers' } - ]] - } - }); - } - - else if (data === 'action_sniper_list') { - const snipers = await db.getUserSnipers(chatId); - if (snipers.length === 0) { - await bot.editMessageText('๐ŸŽฏ **No Sniper Currencies Set**\n\nYou haven\'t configured any currency snipers yet.', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - } else { - let message = `๐ŸŽฏ **Active Snipers:**\n\n`; - snipers.forEach(sniper => { - const platformText = sniper.platform ? ` on ${sniper.platform}` : ' (all platforms)'; - message += `โ€ข ${sniper.currency}${platformText}\n`; - }); - - await bot.editMessageText(message, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - } - } - - else if (data === 'prompt_sniper_remove') { - userStates.set(chatId, { action: 'waiting_sniper_remove', messageId }); - await bot.editMessageText('๐Ÿ—‘๏ธ **Remove Sniper**\n\nEnter the currency (and optionally platform) to remove:\n\nExamples:\nโ€ข `EUR` - remove EUR from all platforms\nโ€ข `EUR revolut` - remove EUR only from Revolut\n\nSend your message now:', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: [[ - { text: 'โŒ Cancel', callback_data: 'menu_snipers' } - ]] - } - }); - } - - else if (data === 'action_sniper_clear') { - await bot.editMessageText('๐Ÿงน **Clear All Snipers**\n\nAre you sure you want to remove ALL sniper configurations?', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createConfirmKeyboard('sniper_clear_confirmed') - }); - } - - // Handle list action - else if (data === 'action_list') { - const userDeposits = await db.getUserDeposits(chatId); - const userStates = await db.getUserDepositStates(chatId); - const listeningAll = await db.getUserListenAll(chatId); - const snipers = await db.getUserSnipers(chatId); - const threshold = await db.getUserThreshold(chatId); - - let message = '๐Ÿ“‹ **Your Current Status:**\n\n'; - - if (listeningAll) { - message += `๐ŸŒ **Listening to ALL deposits**\n\n`; - } - - if (snipers.length > 0) { - message += `๐ŸŽฏ **Active Snipers:** (${threshold}% threshold)\n`; - snipers.forEach(sniper => { - const platformText = sniper.platform ? ` on ${sniper.platform}` : ' (all platforms)'; - message += `โ€ข ${sniper.currency}${platformText}\n`; - }); - message += `\n`; - } - - const idsArray = Array.from(userDeposits).sort((a, b) => a - b); - if (idsArray.length > 0) { - message += `๐Ÿ“Š **Tracking ${idsArray.length} specific deposits:**\n\n`; - idsArray.slice(0, 10).forEach(id => { // Show max 10 - const state = userStates.get(id); - const status = state ? state.status : 'tracking'; - const emoji = status === 'fulfilled' ? 'โœ…' : - status === 'pruned' ? '๐ŸŸก' : '๐Ÿ‘€'; - message += `${emoji} \`${id}\` - ${status}\n`; - }); - - if (idsArray.length > 10) { - message += `\n... and ${idsArray.length - 10} more\n`; - } - } - - if (idsArray.length === 0 && !listeningAll && snipers.length === 0) { - message += `No active tracking or snipers configured.`; - } - - await bot.editMessageText(message, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } - - // Handle help action - else if (data === 'action_help') { - const helpMessage = ` -๐Ÿค– **ZKP2P Tracker Help** - -**๐Ÿ”น Deposit Tracking:** -โ€ข Track specific deposit IDs for targeted notifications -โ€ข Listen to ALL deposits for complete market monitoring -โ€ข Get real-time alerts for order creation, fulfillment, and cancellation - -**๐Ÿ”น Sniper Alerts:** -โ€ข Monitor specific currencies for arbitrage opportunities -โ€ข Set custom profit thresholds (default 0.2%) -โ€ข Choose specific platforms or monitor all -โ€ข Get instant alerts when profitable rates appear - -**๐Ÿ”น Example Scenarios:** -โ€ข Track deposit #123 to see when orders are filled -โ€ข Snipe EUR on Revolut for arbitrage opportunities -โ€ข Listen to all deposits to monitor market activity -โ€ข Set 0.5% threshold to only see highly profitable opportunities - -**๐Ÿ”น Commands Available:** -You can still use text commands if preferred: -โ€ข \`/deposit 123\` - Track specific deposit -โ€ข \`/sniper eur revolut\` - Snipe EUR on Revolut -โ€ข \`/list\` - Show current status -โ€ข \`/menu\` - Show this menu anytime - -Questions? The menu system makes everything easier! ๐Ÿš€ -`.trim(); - - await bot.editMessageText(helpMessage, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } - - // Handle status action - else if (data === 'action_status') { - const wsConnected = resilientProvider?.isConnected || false; - const wsStatus = wsConnected ? '๐ŸŸข Connected' : '๐Ÿ”ด Disconnected'; - - let dbStatus = '๐Ÿ”ด Disconnected'; - try { - const { data, error } = await supabase.from('users').select('chat_id').limit(1); - if (!error) dbStatus = '๐ŸŸข Connected'; - } catch (error) { - console.error('Database test failed:', error); - } - - let botStatus = '๐Ÿ”ด Disconnected'; - try { - await bot.getMe(); - botStatus = '๐ŸŸข Connected'; - } catch (error) { - console.error('Bot test failed:', error); - } - - let message = `๐Ÿ”ง **System Status:**\n\n`; - message += `โ€ข **WebSocket:** ${wsStatus}\n`; - message += `โ€ข **Database:** ${dbStatus}\n`; - message += `โ€ข **Telegram:** ${botStatus}\n\n`; - - if (!wsConnected && resilientProvider) { - message += `โš ๏ธ **WebSocket reconnection attempts:** ${resilientProvider.reconnectAttempts}/${resilientProvider.maxReconnectAttempts}\n\n`; - } - - message += `All systems operational! ๐Ÿš€`; - - await bot.editMessageText(message, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } - - // Handle confirmation actions - else if (data === 'confirm_clearall') { - await bot.editMessageText('๐Ÿ—‘๏ธ **Clear All Data**\n\nโš ๏ธ This will remove:\nโ€ข All tracked deposits\nโ€ข All sniper configurations\nโ€ข All your settings\n\nAre you absolutely sure?', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createConfirmKeyboard('clearall_confirmed') - }); - } - - else if (data === 'confirm_clearall_confirmed') { - await db.clearUserData(chatId); - await bot.editMessageText('๐Ÿ—‘๏ธ **All Data Cleared**\n\nCleared all tracked deposit IDs, stopped listening to all deposits, and cleared all sniper settings.\n\nYou can start fresh now!', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } - - else if (data === 'confirm_sniper_clear_confirmed') { - await db.removeUserSniper(chatId); - await bot.editMessageText('๐Ÿงน **All Snipers Cleared**\n\nRemoved all sniper configurations. You can set up new ones anytime.', { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - } - - // Answer the callback query to remove loading state - await bot.answerCallbackQuery(callbackQuery.id); - - } catch (error) { - console.error('Error handling callback query:', error); - await bot.answerCallbackQuery(callbackQuery.id, { text: 'โŒ An error occurred. Please try again.' }); - } -}); - -// Handle text messages based on user state -bot.on('message', async (msg) => { +// Handle /start command - show help +bot.onText(/\/start/, (msg) => { const chatId = msg.chat.id; - const text = msg.text; - - // Skip if it's a command (already handled) - if (text?.startsWith('/')) return; - - const userState = userStates.get(chatId); - if (!userState) return; - - const { action, messageId } = userState; - - try { - if (action === 'waiting_deposit_add') { - const newIds = text.split(/[,\s]+/).map(id => parseInt(id.trim())).filter(id => !isNaN(id)); - - if (newIds.length === 0) { - bot.sendMessage(chatId, 'โŒ No valid deposit IDs provided. Please try again with numbers only.', { - reply_markup: { - inline_keyboard: [[ - { text: '๐Ÿ”™ Back to Menu', callback_data: 'menu_deposits' } - ]] - } - }); - return; - } - - for (const id of newIds) { - await db.addUserDeposit(chatId, id); - } - - const userDeposits = await db.getUserDeposits(chatId); - const idsArray = Array.from(userDeposits).sort((a, b) => a - b); - - // Update the original menu message - await bot.editMessageText(`โœ… **Successfully Added!**\n\nNow tracking deposit IDs: \`${idsArray.join(', ')}\`\n\nYou'll receive notifications for all events on these deposits.`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); - - // Delete user's input message - try { - await bot.deleteMessage(chatId, msg.message_id); - } catch (e) { - // Ignore if can't delete - } - } - - else if (action === 'waiting_deposit_remove') { - const idsToRemove = text.split(/[,\s]+/).map(id => parseInt(id.trim())).filter(id => !isNaN(id)); - - if (idsToRemove.length === 0) { - bot.sendMessage(chatId, 'โŒ No valid deposit IDs provided. Please try again with numbers only.', { - reply_markup: { - inline_keyboard: [[ - { text: '๐Ÿ”™ Back to Menu', callback_data: 'menu_deposits' } - ]] - } - }); - return; - } - - for (const id of idsToRemove) { - await db.removeUserDeposit(chatId, id); - } - - const userDeposits = await db.getUserDeposits(chatId); - const remainingIds = Array.from(userDeposits).sort((a, b) => a - b); - - let message = 'โœ… **Successfully Removed!**\n\n'; - if (remainingIds.length > 0) { - message += `Still tracking: \`${remainingIds.join(', ')}\``; - } else { - message += `No deposits currently being tracked.`; - } - - await bot.editMessageText(message, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); - - try { - await bot.deleteMessage(chatId, msg.message_id); - } catch (e) { - // Ignore if can't delete - } - } - - else if (action === 'waiting_threshold') { - const threshold = parseFloat(text.trim()); - - if (isNaN(threshold)) { - bot.sendMessage(chatId, 'โŒ Invalid threshold. Please provide a number (e.g., 0.5 for 0.5%)', { - reply_markup: { - inline_keyboard: [[ - { text: '๐Ÿ”™ Back to Menu', callback_data: 'menu_snipers' } - ]] - } - }); - return; - } - - await db.setUserThreshold(chatId, threshold); - - await bot.editMessageText(`๐Ÿ“Š **Threshold Updated!**\n\nSniper threshold set to **${threshold}%**\n\nYou'll now be alerted when deposits offer rates ${threshold}% or better than market rates.`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - - try { - await bot.deleteMessage(chatId, msg.message_id); - } catch (e) { - // Ignore if can't delete - } - } - - else if (action === 'waiting_sniper_remove') { - const parts = text.trim().toLowerCase().split(' '); - const currency = parts[0].toUpperCase(); - const platform = parts[1] ? parts[1].toLowerCase() : null; - - await db.removeUserSniper(chatId, currency, platform); - - const platformText = platform ? ` on ${platform}` : ' (all platforms)'; - - await bot.editMessageText(`โœ… **Sniper Removed!**\n\nStopped sniping ${currency}${platformText}.`, { - chat_id: chatId, - message_id: messageId, - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); - - try { - await bot.deleteMessage(chatId, msg.message_id); - } catch (e) { - // Ignore if can't delete - } - } - - // Clear user state after handling - userStates.delete(chatId); - - } catch (error) { - console.error('Error handling user input:', error); - bot.sendMessage(chatId, 'โŒ An error occurred. Please try again.', { - reply_markup: { - inline_keyboard: [[ - { text: '๐Ÿ”™ Back to Menu', callback_data: 'menu_main' } - ]] - } - }); - userStates.delete(chatId); - } -}); - -// Enhanced notification messages to include menu options -const createDepositKeyboard = (depositId) => { - return { - inline_keyboard: [ - [ - { - text: `๐Ÿ”— View Deposit ${depositId}`, - url: depositLink(depositId) - } - ], - [ - { - text: 'โš™๏ธ Manage Tracking', - callback_data: 'menu_deposits' - } - ] - ] - }; -}; - -// Update the existing help command to redirect to menu -bot.onText(/\/help/, (msg) => { - const chatId = msg.chat.id; - - bot.sendMessage(chatId, 'โ“ **Need Help?**\n\nUse the interactive menu below for easy navigation, or type `/menu` anytime to access it.', { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); - -// Add persistent menu command -bot.onText(/\/quickmenu/, (msg) => { - const chatId = msg.chat.id; - - const quickMenuKeyboard = { - keyboard: [ - ['๐Ÿ“Š Deposits', '๐ŸŽฏ Snipers'], - ['๐Ÿ“‹ Status', 'โš™๏ธ Settings'], - ['๐Ÿ”ง System', 'โ“ Help'] - ], - resize_keyboard: true, - one_time_keyboard: false - }; - - bot.sendMessage(chatId, '๐ŸŽ›๏ธ **Quick Access Menu**\n\nUse these buttons for quick access to main functions:', { - parse_mode: 'Markdown', - reply_markup: quickMenuKeyboard - }); -}); - -// Handle persistent menu button presses -bot.onText(/^๐Ÿ“Š Deposits$/, (msg) => { - const chatId = msg.chat.id; - bot.sendMessage(chatId, '๐Ÿ“Š **Deposit Tracking**\n\nManage your deposit notifications:', { - parse_mode: 'Markdown', - reply_markup: createDepositMenu() - }); -}); - -bot.onText(/^๐ŸŽฏ Snipers$/, (msg) => { - const chatId = msg.chat.id; - bot.sendMessage(chatId, '๐ŸŽฏ **Sniper Setup**\n\nConfigure arbitrage alerts:', { - parse_mode: 'Markdown', - reply_markup: createSniperMenu() - }); -}); - -bot.onText(/^๐Ÿ“‹ Status$/, async (msg) => { - const chatId = msg.chat.id; - - // Reuse the existing /list functionality - const userDeposits = await db.getUserDeposits(chatId); - const userStatesMap = await db.getUserDepositStates(chatId); - const listeningAll = await db.getUserListenAll(chatId); - const snipers = await db.getUserSnipers(chatId); - const threshold = await db.getUserThreshold(chatId); - - let message = '๐Ÿ“‹ **Your Current Status:**\n\n'; - - if (listeningAll) { - message += `๐ŸŒ **Listening to ALL deposits**\n\n`; - } - - if (snipers.length > 0) { - message += `๐ŸŽฏ **Active Snipers:** (${threshold}% threshold)\n`; - snipers.forEach(sniper => { - const platformText = sniper.platform ? ` on ${sniper.platform}` : ' (all platforms)'; - message += `โ€ข ${sniper.currency}${platformText}\n`; - }); - message += `\n`; - } - - const idsArray = Array.from(userDeposits).sort((a, b) => a - b); - if (idsArray.length > 0) { - message += `๐Ÿ“Š **Tracking ${idsArray.length} specific deposits:**\n\n`; - idsArray.slice(0, 10).forEach(id => { - const state = userStatesMap.get(id); - const status = state ? state.status : 'tracking'; - const emoji = status === 'fulfilled' ? 'โœ…' : - status === 'pruned' ? '๐ŸŸก' : '๐Ÿ‘€'; - message += `${emoji} \`${id}\` - ${status}\n`; - }); - - if (idsArray.length > 10) { - message += `\n... and ${idsArray.length - 10} more\n`; - } - } - - if (idsArray.length === 0 && !listeningAll && snipers.length === 0) { - message += `No active tracking or snipers configured.\n\nUse the menu below to get started!`; - } - - bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); - -bot.onText(/^โš™๏ธ Settings$/, (msg) => { - const chatId = msg.chat.id; - bot.sendMessage(chatId, 'โš™๏ธ **Settings**\n\nManage your bot configuration:', { - parse_mode: 'Markdown', - reply_markup: createSettingsMenu() - }); -}); - -bot.onText(/^๐Ÿ”ง System$/, async (msg) => { - const chatId = msg.chat.id; - - // Reuse the existing /status functionality - const wsConnected = resilientProvider?.isConnected || false; - const wsStatus = wsConnected ? '๐ŸŸข Connected' : '๐Ÿ”ด Disconnected'; - - let dbStatus = '๐Ÿ”ด Disconnected'; - try { - const { data, error } = await supabase.from('users').select('chat_id').limit(1); - if (!error) dbStatus = '๐ŸŸข Connected'; - } catch (error) { - console.error('Database test failed:', error); - } - - let botStatus = '๐Ÿ”ด Disconnected'; - try { - await bot.getMe(); - botStatus = '๐ŸŸข Connected'; - } catch (error) { - console.error('Bot test failed:', error); - } - - let message = `๐Ÿ”ง **System Status:**\n\n`; - message += `โ€ข **WebSocket:** ${wsStatus}\n`; - message += `โ€ข **Database:** ${dbStatus}\n`; - message += `โ€ข **Telegram:** ${botStatus}\n\n`; - - if (!wsConnected && resilientProvider) { - message += `โš ๏ธ **WebSocket reconnection attempts:** ${resilientProvider.reconnectAttempts}/${resilientProvider.maxReconnectAttempts}\n\n`; - } - - message += `All systems operational! ๐Ÿš€`; - - bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); - -bot.onText(/^โ“ Help$/, (msg) => { - const chatId = msg.chat.id; - const helpMessage = ` -๐Ÿค– **ZKP2P Tracker Help** - -**๐Ÿ”น Deposit Tracking:** -โ€ข Track specific deposit IDs for targeted notifications -โ€ข Listen to ALL deposits for complete market monitoring -โ€ข Get real-time alerts for order creation, fulfillment, and cancellation - -**๐Ÿ”น Sniper Alerts:** -โ€ข Monitor specific currencies for arbitrage opportunities -โ€ข Set custom profit thresholds (default 0.2%) -โ€ข Choose specific platforms or monitor all -โ€ข Get instant alerts when profitable rates appear - -**๐Ÿ”น Example Scenarios:** -โ€ข Track deposit #123 to see when orders are filled -โ€ข Snipe EUR on Revolut for arbitrage opportunities -โ€ข Listen to all deposits to monitor market activity -โ€ข Set 0.5% threshold to only see highly profitable opportunities - -**๐Ÿ”น Available Commands:** -โ€ข \`/menu\` - Show interactive menu (recommended) -โ€ข \`/quickmenu\` - Show persistent button menu -โ€ข \`/deposit 123\` - Track specific deposit -โ€ข \`/sniper eur revolut\` - Snipe EUR on Revolut -โ€ข \`/list\` - Show current status -โ€ข \`/status\` - Check system status -โ€ข \`/clearall\` - Reset all settings - -**๐Ÿ”น Pro Tips:** -โ€ข Use the menu system for easier navigation -โ€ข Set reasonable thresholds (0.1-1%) for sniper alerts -โ€ข Monitor multiple currencies for more opportunities -โ€ข Track specific deposits for important transactions - -Questions? The interactive menus make everything easier! ๐Ÿš€ -`.trim(); +๐Ÿค– *ZKP2P Tracker Commands:* - bot.sendMessage(chatId, helpMessage, { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); -}); +**Deposit Tracking:** +- \`/deposit all\` - Listen to ALL deposits (every event) +- \`/deposit stop\` - Stop listening to all deposits +- \`/deposit 123\` - Track a specific deposit +- \`/deposit 123,456,789\` - Track multiple deposits +- \`/remove 123\` - Stop tracking specific deposit(s) -// Add command to remove persistent keyboard -bot.onText(/^\/hidemenu$/, (msg) => { - const chatId = msg.chat.id; - bot.sendMessage(chatId, '๐Ÿ‘‹ Persistent menu hidden.\n\nUse `/menu` or `/quickmenu` anytime to access the interactive menus.', { - parse_mode: 'Markdown', - reply_markup: { remove_keyboard: true } - }); -}); +**Sniper (Arbitrage Alerts):** +- \`/sniper eur\` - Snipe EUR on ALL platforms +- \`/sniper eur revolut\` - Snipe EUR only on Revolut +- \`/sniper usd zelle\` - Snipe USD only on Zelle +- \`/sniper threshold 0.5\` - Set your alert threshold to 0.5% +- \`/sniper list\` - Show active sniper settings +- \`/sniper clear\` - Clear all sniper settings +- \`/unsnipe eur\` - Stop sniping EUR (all platforms) +- \`/unsnipe eur wise\` - Stop sniping EUR on Wise only -// Auto-show menu for new users or when they seem lost -bot.on('message', async (msg) => { - const chatId = msg.chat.id; - const text = msg.text?.toLowerCase(); - - // Skip if it's a command or callback response - if (text?.startsWith('/') || userStates.has(chatId)) return; - - // Show menu if user sends common confused messages - const confusedPhrases = ['help', 'menu', 'what', 'how', '?', 'commands', 'start', 'hi', 'hello']; - - if (confusedPhrases.some(phrase => text?.includes(phrase))) { - bot.sendMessage(chatId, '๐Ÿ‘‹ **Need help?**\n\nUse the interactive menu below to easily navigate all features:', { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - } -}); +**General:** +- \`/list\` - Show all tracking status (deposits + snipers) +- \`/clearall\` - Stop all tracking and clear everything +- \`/status\` - Check WebSocket connection and settings +- \`/help\` - Show this help message -// Enhanced welcome message for group/channel usage -bot.on('new_chat_members', async (msg) => { - // Only respond in private chats to avoid spam - if (msg.chat.type !== 'private') return; - - const chatId = msg.chat.id; - const newMembers = msg.new_chat_members; - - // Check if our bot was added - const botInfo = await bot.getMe(); - const botAdded = newMembers.some(member => member.id === botInfo.id); +*Note: Each user has their own settings. Sniper alerts you when deposits offer better exchange rates than market!* +`.trim(); - if (botAdded) { - setTimeout(() => { - bot.sendMessage(chatId, ` -๐ŸŽ‰ **Welcome to ZKP2P Tracker!** - -I'm here to help you track ZKP2P deposits and find arbitrage opportunities. - -**Quick Start:** -โ€ข Use the menu below to get started -โ€ข Track specific deposits for notifications -โ€ข Set up snipers for arbitrage alerts -โ€ข Monitor the entire ZKP2P ecosystem - -Ready to begin? -`.trim(), { - parse_mode: 'Markdown', - reply_markup: createMainMenu() - }); - }, 1000); - } + bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown' }); }); -console.log('โœ… Interactive menu system loaded successfully!'); - -// // Handle /start command - show help -// bot.onText(/\/start/, (msg) => { -// const chatId = msg.chat.id; -// const helpMessage = ` -// ๐Ÿค– *ZKP2P Tracker Commands:* - -// **Deposit Tracking:** -// - \`/deposit all\` - Listen to ALL deposits (every event) -// - \`/deposit stop\` - Stop listening to all deposits -// - \`/deposit 123\` - Track a specific deposit -// - \`/deposit 123,456,789\` - Track multiple deposits -// - \`/remove 123\` - Stop tracking specific deposit(s) - -// **Sniper (Arbitrage Alerts):** -// - \`/sniper eur\` - Snipe EUR on ALL platforms -// - \`/sniper eur revolut\` - Snipe EUR only on Revolut -// - \`/sniper usd zelle\` - Snipe USD only on Zelle -// - \`/sniper threshold 0.5\` - Set your alert threshold to 0.5% -// - \`/sniper list\` - Show active sniper settings -// - \`/sniper clear\` - Clear all sniper settings -// - \`/unsnipe eur\` - Stop sniping EUR (all platforms) -// - \`/unsnipe eur wise\` - Stop sniping EUR on Wise only - -// **General:** -// - \`/list\` - Show all tracking status (deposits + snipers) -// - \`/clearall\` - Stop all tracking and clear everything -// - \`/status\` - Check WebSocket connection and settings -// - \`/help\` - Show this help message - -// *Note: Each user has their own settings. Sniper alerts you when deposits offer better exchange rates than market!* -// `.trim(); - -// bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown' }); -// }); - bot.onText(/\/help/, (msg) => { const chatId = msg.chat.id;