bot-me / src /bots /utils /keyboardUtils.ts
Mohammed Foud
all
d4f30f7
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_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_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'
)]
]);
};