Spaces:
Paused
Paused
| import { Markup } from "telegraf"; | |
| import { countryData } from "./country"; | |
| import { formatPrice } from "./priceUtils"; | |
| import { BotContext } from "../types/botTypes"; | |
| import { messageManager } from "./messageManager"; | |
| import fiveSimProducts from "./5sim_products.json"; | |
| import { VirtualNumberService } from "../services/VirtualNumberService"; | |
| import { createLogger } from "../../utils/logger"; | |
| const logger = createLogger('KeyboardUtils'); | |
| export const getMainMenuKeyboard = () => { | |
| return Markup.inlineKeyboard([ | |
| // [Markup.button.callback(messageManager.getMessage('btn_login'), 'login')], | |
| // [Markup.button.callback(messageManager.getMessage('btn_terms'), 'terms')], | |
| // [Markup.button.callback(messageManager.getMessage('btn_webook_events'), 'webook_events'), | |
| // // Markup.button.callback('🎟️ Book Tickets by Event URL', 'webook_book_by_url') | |
| // ], | |
| [Markup.button.callback(messageManager.getMessage('btn_webook_book_by_url'), 'webook_book_by_url')], | |
| // [Markup.button.callback(messageManager.getMessage('btn_new_members'), 'new_members')], | |
| // [Markup.button.callback(messageManager.getMessage('btn_stats'), 'stats')], | |
| [Markup.button.callback(messageManager.getMessage('btn_change_language'), 'change_language')], | |
| ]); | |
| }; | |
| export const getLoggedInMenuKeyboard = () => { | |
| return Markup.inlineKeyboard([ | |
| // [Markup.button.callback('🔍 Browse Services', 'browse_services')], | |
| [Markup.button.callback(messageManager.getMessage('btn_webook_events'), 'webook_events')], | |
| [Markup.button.callback(messageManager.getMessage('btn_webook_book_by_url'), 'webook_book_by_url')], | |
| [ | |
| // Markup.button.callback(messageManager.getMessage('btn_profile'), 'profile'), | |
| Markup.button.callback(messageManager.getMessage('btn_change_language'), 'change_language') | |
| ], | |
| // [ | |
| // Markup.button.callback(messageManager.getMessage('btn_top_up'), 'top_up_balance'), | |
| // Markup.button.callback(messageManager.getMessage('btn_history'), 'history') | |
| // ], | |
| // [Markup.button.callback('💰 Buy with Balance', 'buy_with_balance')], | |
| [ | |
| Markup.button.callback(messageManager.getMessage('btn_back'), 'main_menu') | |
| ], | |
| ]); | |
| }; | |
| export const getServicesKeyboard = (page: number = 0, sortBy?: 'az' | 'za') => { | |
| const buttons = []; | |
| const services = Object.entries(fiveSimProducts); | |
| const rowSize = 2; // 2 buttons per row | |
| const servicesPerPage = 20; | |
| // Apply sorting for all services first | |
| let sortedServices = [...services]; | |
| if (sortBy === 'az') { | |
| sortedServices.sort((a, b) => a[1].label_en.localeCompare(b[1].label_en)); | |
| } else if (sortBy === 'za') { | |
| sortedServices.sort((a, b) => b[1].label_en.localeCompare(a[1].label_en)); | |
| } | |
| const totalPages = Math.ceil(sortedServices.length / servicesPerPage); | |
| // Get services for current page from the sorted array | |
| const startIndex = page * servicesPerPage; | |
| const endIndex = Math.min(startIndex + servicesPerPage, sortedServices.length); | |
| const pageServices = sortedServices.slice(startIndex, endIndex); | |
| // Add sort button | |
| const sortButtonText = sortBy === 'az' ? 'Sort Z-A ⬇️' : 'Sort A-Z ⬆️'; | |
| const sortButtonCallback = sortBy === 'az' ? `sort_services_za_${page}` : `sort_services_az_${page}`; | |
| buttons.push([Markup.button.callback(sortButtonText, sortButtonCallback)]); | |
| // Generate service buttons in pairs | |
| for (let i = 0; i < pageServices.length; i += rowSize) { | |
| const row = []; | |
| for (let j = 0; j < rowSize && i + j < pageServices.length; j++) { | |
| const [serviceId, serviceData] = pageServices[i + j]; | |
| row.push( | |
| Markup.button.callback( | |
| `${serviceData.icon} ${serviceData.label_en}`, | |
| `service_${serviceId}` | |
| ) | |
| ); | |
| } | |
| buttons.push(row); | |
| } | |
| // Add pagination buttons | |
| const paginationRow = []; | |
| if (page > 0) { | |
| paginationRow.push( | |
| Markup.button.callback( | |
| '⬅️ Previous', | |
| `services_page_${page - 1}${sortBy ? `_${sortBy}` : ''}` | |
| ) | |
| ); | |
| } | |
| paginationRow.push( | |
| Markup.button.callback( | |
| `📄 ${page + 1}/${totalPages}`, | |
| 'noop' | |
| ) | |
| ); | |
| if (page < totalPages - 1) { | |
| paginationRow.push( | |
| Markup.button.callback( | |
| 'Next ➡️', | |
| `services_page_${page + 1}${sortBy ? `_${sortBy}` : ''}` | |
| ) | |
| ); | |
| } | |
| if (paginationRow.length > 0) { | |
| buttons.push(paginationRow); | |
| } | |
| // Add search button | |
| buttons.push([Markup.button.callback('🔍 Search Product', 'search_product')]); | |
| // Add back button to return to main menu | |
| buttons.push([ | |
| Markup.button.callback('🔙 Back to Menu', 'logged_in_menu') | |
| ]); | |
| return Markup.inlineKeyboard(buttons); | |
| }; | |
| export const getServicesPaginationInfo = (page: number = 0) => { | |
| const services = Object.entries(fiveSimProducts); | |
| const servicesPerPage = 10; | |
| const totalPages = Math.ceil(services.length / servicesPerPage); | |
| const startIndex = page * servicesPerPage; | |
| const endIndex = Math.min(startIndex + servicesPerPage, services.length); | |
| return `<b>Services List</b>\n` + | |
| `📋 Showing services ${startIndex + 1}-${endIndex} of ${services.length}\n` + | |
| `📄 Page ${page + 1} of ${totalPages}`; | |
| }; | |
| export const getBackToMainMenuButton = () => { | |
| return Markup.inlineKeyboard([ | |
| [Markup.button.callback(messageManager.getMessage('btn_back'), 'main_menu')] | |
| ]); | |
| }; | |
| export const mapApiCountriesToButtons = (apiCountries: any[], service: string) => { | |
| const buttons = []; | |
| const rowSize = 2; // 2 buttons per row | |
| // Sort countries by name for better UX | |
| const sortedCountries = apiCountries.sort((a, b) => a.name.localeCompare(b.name)); | |
| for (let i = 0; i < sortedCountries.length; i += rowSize) { | |
| const row = []; | |
| for (let j = 0; j < rowSize && i + j < sortedCountries.length; j++) { | |
| const country = sortedCountries[i + j]; | |
| const countryInfo = countryData[country.id.toLowerCase()]; | |
| if (countryInfo) { | |
| row.push( | |
| Markup.button.callback( | |
| `${countryInfo.label} ${countryInfo.flag} ${countryInfo.code}`, | |
| `country_${service}_${country.id}` | |
| ) | |
| ); | |
| } | |
| } | |
| if (row.length > 0) { | |
| buttons.push(row); | |
| } | |
| } | |
| return buttons; | |
| }; | |
| export const getCountriesKeyboard = async (service: string, page: number = 0) => { | |
| const buttons = []; | |
| const countriesPerPage = 15; | |
| try { | |
| // Get countries from API | |
| const virtualNumberService = VirtualNumberService.getInstance(); | |
| const apiCountries = await virtualNumberService.getAvailableCountries(service); | |
| const mappedButtons = mapApiCountriesToButtons(apiCountries, service); | |
| const totalPages = Math.ceil(mappedButtons.length / countriesPerPage); | |
| const startIndex = page * countriesPerPage; | |
| const endIndex = Math.min(startIndex + countriesPerPage, mappedButtons.length); | |
| const pageButtons = mappedButtons.slice(startIndex, endIndex); | |
| buttons.push(...pageButtons); | |
| const paginationRow = []; | |
| if (page > 0) { | |
| paginationRow.push( | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_previous'), | |
| `page_${service}_${page - 1}` | |
| ) | |
| ); | |
| } | |
| paginationRow.push( | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_page_info') | |
| .replace('{current}', (page + 1).toString()) | |
| .replace('{total}', totalPages.toString()), | |
| 'noop' | |
| ) | |
| ); | |
| if (page < totalPages - 1) { | |
| paginationRow.push( | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_next'), | |
| `page_${service}_${page + 1}` | |
| ) | |
| ); | |
| } | |
| if (paginationRow.length > 0) { | |
| buttons.push(paginationRow); | |
| } | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_main_menu'), | |
| 'main_menu' | |
| ) | |
| ]); | |
| } catch (error: any) { | |
| logger.error(`Error fetching countries: ${error.message}`); | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_error_loading_countries'), | |
| 'main_menu' | |
| ) | |
| ]); | |
| } | |
| return Markup.inlineKeyboard(buttons); | |
| }; | |
| export const getServicePricesKeyboard = ( | |
| prices: any, | |
| service: string, | |
| country: string, | |
| ctx: BotContext | |
| ) => { | |
| const buttons = []; | |
| const hasValidPrices = prices && Object.values(prices).some( | |
| (priceInfo: any) => priceInfo && priceInfo.count > 0 | |
| ); | |
| if (!hasValidPrices) { | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_no_prices'), | |
| 'noop' | |
| ), | |
| ]); | |
| } else { | |
| Object.entries(prices).forEach(([operator, priceInfo]: [string, any]) => { | |
| if (priceInfo.count > 0) { | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_buy_format') | |
| .replace('{price}', priceInfo.cost.toFixed(2)) | |
| .replace('{count}', priceInfo.count.toString()), | |
| `buy_${service}_${country}_${operator}` | |
| ), | |
| ]); | |
| } | |
| }); | |
| } | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_back_to_services'), | |
| `service_${service}` | |
| ), | |
| ]); | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_main_menu'), | |
| 'main_menu' | |
| ) | |
| ]); | |
| return Markup.inlineKeyboard(buttons); | |
| }; | |
| export const getHistoryKeyboard = () => { | |
| return Markup.inlineKeyboard([ | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_numbers_history'), | |
| 'numbers_history' | |
| )], | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_purchases_history'), | |
| 'purchases_history' | |
| )], | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_back_to_main'), | |
| 'main_menu' | |
| )] | |
| ]); | |
| }; | |
| export const getLanguageSelectionKeyboard = (isLoggedIn: boolean = false) => { | |
| const buttons = [ | |
| [ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_lang_english'), | |
| 'set_language_en' | |
| ), | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_lang_arabic'), | |
| 'set_language_ar' | |
| ) | |
| ] | |
| ]; | |
| buttons.push([ | |
| Markup.button.callback( | |
| messageManager.getMessage('btn_back'), | |
| isLoggedIn ? 'logged_in_menu' : 'main_menu' | |
| ) | |
| ]); | |
| return Markup.inlineKeyboard(buttons); | |
| }; | |
| export const getProfileKeyboard = () => { | |
| return Markup.inlineKeyboard([ | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_change_email'), | |
| 'change_email' | |
| )], | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_change_password'), | |
| 'change_password' | |
| )], | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_account_info'), | |
| 'account_info' | |
| )], | |
| // [Markup.button.callback( | |
| // messageManager.getMessage('btn_gift_balance'), | |
| // 'gift_balance' | |
| // )], | |
| [Markup.button.callback( | |
| messageManager.getMessage('btn_back_to_logged_in'), | |
| 'logged_in_menu' | |
| )] | |
| ]); | |
| }; |