|
|
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_new_members'), 'new_members')], |
|
|
|
|
|
[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_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(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; |
|
|
const servicesPerPage = 20; |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
const startIndex = page * servicesPerPage; |
|
|
const endIndex = Math.min(startIndex + servicesPerPage, sortedServices.length); |
|
|
const pageServices = sortedServices.slice(startIndex, endIndex); |
|
|
|
|
|
|
|
|
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)]); |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
buttons.push([Markup.button.callback('🔍 Search Product', 'search_product')]); |
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
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_back_to_logged_in'), |
|
|
'logged_in_menu' |
|
|
)] |
|
|
]); |
|
|
}; |