advanced-ussd-builder
v2.4.3
Published
Advanced USSD Menu Builder with persistent state and navigation
Maintainers
Readme
Advanced USSD Builder
A powerful TypeScript library for building interactive USSD menus with persistent state management
Installation • Quick Start • Documentation • Examples • API
📋 Table of Contents
- Features
- Installation
- Quick Start
- Provider Support
- Core Concepts
- Usage Examples
- API Reference
- Provider Integration
- Advanced Features
- License
✨ Features
- 🚀 Multi-Provider Support - Works with MTN, Nalo, Telecel, Airtel-Tigo, Cross-Switch, and Custom providers
- 🔐 Built-in Authentication - PIN-based authentication with customizable handlers
- 🔌 Middleware Support - Request/response interceptors for logging, validation, and transformation
- 🎨 Custom Provider Setup - Easy integration with any USSD gateway through flexible configuration
- 🔀 Advanced Proxy Support - Forward requests to external USSD services with automatic network detection, flexible request formats (telco-specific or normalized), and custom transformations
- 💾 Persistent State Management - Redis-powered session management for stateful interactions
- 📱 Dynamic Menu Generation - Create menus programmatically with render and handler functions
- 🔄 Navigation History - Track and navigate through menu paths seamlessly
- 🎯 TypeScript Support - Full type definitions for better development experience
- ⚡ High Performance - Optimized for speed with minified production builds
- 🔧 Flexible Configuration - Customizable session prefixes, navigation keys, and more
📦 Installation
npm install advanced-ussd-builderor using yarn:
yarn add advanced-ussd-builderPrerequisites
- Node.js >= 14.x
- Redis server (for session management)
- TypeScript (for development)
🚀 Quick Start
import { UssdBuilder, UssdMenu, iUssdMenu, iUssdMenuOptions, iMTNUSSDArgs } from 'advanced-ussd-builder';
// Define menu configuration
const menuOptions: iUssdMenuOptions = {
provider: 'mtn',
require_pin: false,
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
redis_connection_url: 'redis://localhost:6379',
state: {
user_id: '',
session_data: {}
}
};
// Create handler menus with path-based handlers
const registrationMenu: iUssdMenu = {
title: 'Register',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'root_menu',
handler: path.join(__dirname, '/handlers/registration.js')
};
// Create handler menus with function handlers
const balanceMenu: iUssdMenu = {
title: 'Check Balance',
type: 'handler',
handler_type: 'function',
handler: async (args) => {
const balance = await getUserBalance(args.msisdn);
return `Your balance is $${balance}`;
}
};
// Create menu array
const menus = [registrationMenu, balanceMenu];
// Initialize USSD Builder with root menu
const ussdBuilder = new UssdBuilder(
menuOptions,
new UssdMenu('Welcome to MyApp', menus)
);
// Process USSD request (Express.js example)
app.post('/ussd/mtn', async (req, res) => {
const ussdArgs = req.body as iMTNUSSDArgs;
const sendResponse = (response) => {
return res.json(response);
};
await ussdBuilder.pipe<'mtn'>(ussdArgs, sendResponse);
});🌐 Provider Support
Advanced USSD Builder supports multiple telecom providers out of the box:
| Provider | Status | Request Interface | Response Interface |
|----------|--------|------------------|-------------------|
| MTN | ✅ Supported | iMTNUSSDArgs | iMTNUssdCallback |
| Nalo | ✅ Supported | iNaloUSSDArgs | iNaloUssdCallback |
| Telecel | ✅ Supported | iTelecelUssdArgs | iTelecelUssdCallBack |
| Airtel-Tigo | ✅ Supported | iAirtelTigoUssdArgs | iAirtelTigoUssdCallBack |
| Cross-Switch | ✅ Supported | iCrossSwitchUssdArgs | iCrossSwitchUssdCallBack |
| Custom | ✅ Supported | Custom Interface | Custom Response |
📚 Core Concepts
UssdMenu
The UssdMenu class represents a menu container that can hold multiple menu items. Each menu item follows the iUssdMenu interface structure.
// Create a root menu with multiple options
const menuItems: iUssdMenu[] = [
{
title: 'Option 1',
type: 'handler',
handler_type: 'function',
handler: async (args) => {
return 'Response for option 1';
}
},
{
title: 'Option 2',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'default',
handler: './handlers/option2.js'
}
];
const rootMenu = new UssdMenu('Select an option:', menuItems);UssdBuilder
The main orchestrator that manages menus, sessions, and request processing. It requires both configuration options and a root menu.
const options: iUssdMenuOptions = {
provider: 'mtn', // or 'nalo', 'telecel', 'airtel-tigo', 'cross-switch', 'custom'
require_pin: false,
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
redis_connection_url: 'redis://localhost:6379',
state: {}, // Custom state object
middleware: async (args) => {
// Optional middleware
},
authentication_handler: async (args) => {
// Optional authentication
return true;
}
};
const builder = new UssdBuilder(options, rootMenu);Session Management
Sessions are automatically managed using Redis with the following structure:
- Session Key:
{prefix}:{session_id} - Session Data: Stores navigation path, current menu, and custom data
- TTL: Configurable time-to-live for session expiration
💡 Usage Examples
Render Menu (Display Options)
// Render menu displays a list of options to the user
const renderMenus: iUssdMenu[] = [
{
title: 'Account Services',
type: 'render', // This will display as an option
render: {
success_message: 'Select an option:',
error_message: 'Invalid selection'
}
},
{
title: 'Transfer Money',
type: 'render',
render: {
success_message: 'Choose transfer type:'
}
},
{
title: 'Buy Airtime',
type: 'render',
render: {
success_message: 'Select amount:'
}
}
];
const ussdBuilder = new UssdBuilder(
options,
new UssdMenu('Main Menu\n1. Account Services\n2. Transfer Money\n3. Buy Airtime', renderMenus)
);Render Menu with Function Handler
// When type is 'render' with a handler, the handler controls the response
const dynamicRenderMenu: iUssdMenu = {
title: 'Account List',
type: 'render',
handler_type: 'function',
handler: async (args) => {
// Dynamically generate menu options
const accounts = await getUserAccounts(args.msisdn);
const menuOptions = accounts.map((acc, idx) =>
`${idx + 1}. ${acc.name} - ${acc.balance}`
).join('\n');
// Handler returns the complete response
return {
message: `Your Accounts:\n${menuOptions}\n\nSelect account:`,
continue_session: true
};
}
// No render object needed when handler is provided
};Basic Menu
const bankingMenus: iUssdMenu[] = [
{
title: 'Check Balance',
type: 'handler',
handler_type: 'function',
handler: async (args) => {
const balance = await getBalance(args.msisdn);
return `Your balance: $${balance}`;
}
},
{
title: 'Transfer Funds',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'root_menu',
handler: './handlers/transfer.js'
},
{
title: 'Mini Statement',
type: 'handler',
handler_type: 'function',
handler: async (args) => {
const statement = await getStatement(args.msisdn);
return statement;
}
}
];
const ussdBuilder = new UssdBuilder(
options,
new UssdMenu('Welcome to Banking Services', bankingMenus)
);Dynamic Menus
// Dynamically create menus based on user data
const createDynamicMenu = async (userPhone: string) => {
const user = await getUserData(userPhone);
const menuItems: iUssdMenu[] = [];
// Add different menus based on user status
if (user.isRegistered) {
menuItems.push({
title: 'My Account',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'root_menu',
handler: './handlers/account.js'
});
} else {
menuItems.push({
title: 'Register',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'root_menu',
handler: './handlers/registration.js'
});
}
return new UssdMenu('Welcome', menuItems);
};
// Use in request handler
app.post('/ussd', async (req, res) => {
const rootMenu = await createDynamicMenu(req.body.msisdn);
const builder = new UssdBuilder(options, rootMenu);
await builder.pipe<'mtn'>(req.body, (response) => res.json(response));
});Proxy Menu (External Service Integration)
New in v2.4+: Enhanced proxy support with automatic network detection and flexible request formats.
Basic Proxy Configuration
// Simple proxy to external service
const basicProxy: iUssdMenu = {
title: 'Loan Services',
type: 'proxy',
proxy_config: {
target_url: 'https://loan-service.example.com/ussd',
timeout: 25000,
headers: {
'API-Key': process.env.LOAN_API_KEY
}
}
};Telco-Specific Endpoints (Default Behavior)
// Forward requests in original telco format to telco-specific endpoints
const telcoSpecificProxy: iUssdMenu = {
title: 'Payment Service',
type: 'proxy',
proxy_config: {
target_url: 'https://payment.example.com/v1/ussd/mtn', // MTN-specific endpoint
// forward_original_format: true (default)
// Forwards MTN request format directly to the endpoint
}
};Unified Endpoint with Network Detection
// Use single endpoint for all networks with automatic network detection
const unifiedProxy: iUssdMenu = {
title: 'Unified Service',
type: 'proxy',
proxy_config: {
target_url: 'https://api.example.com/ussd', // Single endpoint for all networks
forward_original_format: false, // Use normalized format
// Network is automatically detected from phone number
}
};
// The proxy sends normalized request with network field:
// {
// msisdn: '0241234567',
// session_id: 'session-123',
// user_input: '1',
// service_code: '*123#',
// is_new_request: false,
// provider: 'mtn',
// network: 'MTN', // Auto-detected from phone number prefix
// session_data: {...},
// original_request: {...}
// }Advanced Proxy with Transformations
const advancedProxy: iUssdMenu = {
title: 'Insurance Portal',
type: 'proxy',
proxy_config: {
target_url: 'https://insurance.example.com/ussd',
forward_original_format: false,
timeout: 20000,
retry_attempts: 2,
retry_delay: 1500,
session_bridge: true, // Share session state
headers: {
'Authorization': `Bearer ${process.env.TOKEN}`,
'X-Partner-ID': 'partner-123'
},
// Custom request transformation
transform_request: (data) => ({
phone: data.msisdn,
network: data.network, // Includes auto-detected network
session: data.session_id,
input: data.user_input,
metadata: {
provider: data.provider,
timestamp: Date.now()
}
}),
// Custom response transformation
transform_response: (response) => ({
message: response.text,
require_feedback: response.continue
})
}
};Custom Proxy Handler
// Use custom function instead of HTTP request
const customProxy: iUssdMenu = {
title: 'Custom Service',
type: 'proxy',
proxy_config: {
proxy_handler: async (builder, user_input) => {
// Custom logic here
const result = await customServiceCall(builder.session, user_input);
return {
message: result.display,
require_feedback: result.needsInput
};
}
}
};Network Detection
The library automatically detects the network from Ghana phone numbers:
- MTN: 024, 054, 055, 059, 025
- Telecel: 020, 050
- AirtelTigo: 026, 056, 027, 057, 028
// Network detection works with various formats
getNetworkFromPhoneNumber('0241234567'); // Returns: 'MTN'
getNetworkFromPhoneNumber('233241234567'); // Returns: 'MTN'
getNetworkFromPhoneNumber('+233241234567'); // Returns: 'MTN'
getNetworkFromPhoneNumber('0201234567'); // Returns: 'TELECEL'
getNetworkFromPhoneNumber('0561234567'); // Returns: 'AIRTEL-TIGO'// Combine local and proxy menus const mainMenus: iUssdMenu[] = [ { title: 'My Account', type: 'handler', handler_type: 'path', handler: './handlers/account.js' }, ...proxyMenus // External services appear as regular menu items ];
const ussdBuilder = new UssdBuilder( options, new UssdMenu('Select Service:', mainMenus) );
### Handler Menus
```typescript
// Simple handler
const balanceHandler = new UssdMenu('check_balance', async (args) => {
const balance = await getBalance(args.msisdn);
return `Your balance: $${balance}\n\nPress 0 to go back`;
});
// Handler with module path
const complexHandler = new UssdMenu('process_transfer', './handlers/transfer.js');
builder.register_menu(balanceHandler);
builder.register_menu(complexHandler);Paginated Menus
Pagination is automatic when menu items exceed the configured limit:
const builder = new UssdBuilder({
redis_url: 'redis://localhost:6379',
max_menu_items: 5 // Show 5 items per page
});
const largeMenu = new UssdMenu('products', 'Select Product');
// Adding more than 5 items will trigger pagination
for (let i = 1; i <= 20; i++) {
largeMenu.add_child(`product_${i}`, `Product ${i}`);
}📖 API Reference
UssdBuilder
Constructor Options
interface UssdBuilderOptions {
redis_url: string; // Redis connection URL
session_prefix?: string; // Session key prefix for multi-instance isolation (default: 'ussd')
session_ttl?: number; // Session TTL in seconds (default: 120 seconds / 2 minutes)
max_menu_items?: number; // Max items per page (default: 5)
end_session_text?: string; // Custom end session message
}Methods
| Method | Description | Parameters | Returns |
|--------|-------------|------------|---------|
| pipe<T>(args, callback) | Process USSD request | args: provider args, callback: response handler | Promise |
| getSession() | Get current session | - | iUssdSession |
| saveSession() | Save session to Redis | - | Promise |
| endSession() | End current session | - | Promise |
UssdMenu
Constructor
new UssdMenu(
id: string,
title: string | Function | undefined,
opts?: {
custom_input?: boolean;
module_path?: string;
}
)Menu Item Types
| Property | Description | Type |
|----------|-------------|------|
| title | Menu item display text | string |
| type | Menu type | 'render' | 'handler' | 'proxy' |
| handler_type | Handler type (for handler menus) | 'path' | 'function' |
| handler | Handler implementation | string | Function |
| handler_name_in_destination | Exported function name in module | string |
| proxy_config | Proxy configuration (for proxy menus) | ProxyConfig object |
🔌 Provider Integration
MTN
app.post('/ussd/mtn', async (req, res) => {
const ussdArgs = req.body as iMTNUSSDArgs;
// Response callback
const sendResponse = <iMTNUssdCallback>(response: iMTNUssdCallback) => {
return res.json(response);
};
// Configure and build menu
const options: iUssdMenuOptions = {
provider: 'mtn',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL,
state: { /* custom state */ }
};
const menus: iUssdMenu[] = [/* your menus */];
const ussdBuilder = new UssdBuilder(
options,
new UssdMenu('Welcome', menus)
);
await ussdBuilder.pipe<'mtn'>(ussdArgs, sendResponse);
});Nalo
app.post('/ussd/nalo', async (req, res) => {
const ussdArgs = req.body as iNaloUSSDArgs;
const sendResponse = <iNaloUssdCallback>(response: iNaloUssdCallback) => {
return res.json(response);
};
const options: iUssdMenuOptions = {
provider: 'nalo',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL
};
const ussdBuilder = new UssdBuilder(options, rootMenu);
await ussdBuilder.pipe<'nalo'>(ussdArgs, sendResponse);
});Telecel
app.post('/ussd/telecel', async (req, res) => {
const ussdArgs = req.body as iTelecelUssdArgs;
const sendResponse = <iTelecelUssdCallBack>(response: iTelecelUssdCallBack) => {
return res.json(response);
};
const options: iUssdMenuOptions = {
provider: 'telecel',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL
};
const ussdBuilder = new UssdBuilder(options, rootMenu);
await ussdBuilder.pipe<'telecel'>(ussdArgs, sendResponse);
});Airtel-Tigo
app.post('/ussd/at', async (req, res) => {
const ussdArgs = req.body as iAirtelTigoUssdArgs;
const sendResponse = <iAirtelTigoUssdCallBack>(response: iAirtelTigoUssdCallBack) => {
return res.json(response);
};
const options: iUssdMenuOptions = {
provider: 'airtel-tigo',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL
};
const ussdBuilder = new UssdBuilder(options, rootMenu);
await ussdBuilder.pipe<'airtel-tigo'>(ussdArgs, sendResponse);
});Cross-Switch
app.post('/ussd/cross-switch', async (req, res) => {
const ussdArgs = req.body as iCrossSwitchUssdArgs;
const sendResponse = <iCrossSwitchUssdCallBack>(response: iCrossSwitchUssdCallBack) => {
return res.json(response);
};
const options: iUssdMenuOptions = {
provider: 'cross-switch',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL
};
const ussdBuilder = new UssdBuilder(options, rootMenu);
await ussdBuilder.pipe<'cross-switch'>(ussdArgs, sendResponse);
});Custom Provider
The library supports custom providers for any USSD gateway not listed above:
// Custom provider implementation
app.post('/ussd/custom', async (req, res) => {
const customArgs = {
msisdn: req.body.phoneNumber,
session_id: req.body.sessionId,
msg: req.body.userInput,
// Map your custom fields
};
const sendResponse = (response) => {
// Transform response to your custom format
const customResponse = {
text: response.message || response.msg,
continueSession: response.msgtype !== false,
sessionId: response.session_id
};
return res.json(customResponse);
};
const options: iUssdMenuOptions = {
provider: 'custom',
service_code: req.body.serviceCode,
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL,
make_provider_response: false, // Handle response formatting manually
state: {}
};
const menus: iUssdMenu[] = [/* your menus */];
const ussdBuilder = new UssdBuilder(
options,
new UssdMenu('Welcome', menus)
);
await ussdBuilder.pipe<'custom'>(customArgs, sendResponse);
});🚀 Advanced Features
Session State Management
// Pass state through options that persists across menus
const options: iUssdMenuOptions = {
provider: 'mtn',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL,
state: {
user_id: '12345',
user_name: 'John Doe',
account_type: 'premium',
// Any custom data you need
}
};
// Access state in handlers
const menuHandler: iUssdMenu = {
title: 'Account Info',
type: 'handler',
handler_type: 'function',
handler: async (args) => {
// Access state passed through options
const userId = args.state?.user_id;
const userName = args.state?.user_name;
return `Welcome ${userName}, your ID is ${userId}`;
}
};Middleware Support
const options: iUssdMenuOptions = {
provider: 'mtn',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: false,
redis_connection_url: REDIS_URL,
middleware: async (args) => {
// Log every request
console.log('USSD Request:', args);
// Validate user
const user = await validateUser(args.msisdn);
if (!user) {
throw new Error('Unauthorized');
}
// Add user to args
args.user = user;
}
};Authentication Handler
const options: iUssdMenuOptions = {
provider: 'mtn',
service_code: '*123#',
next_page_key: '#',
back_page_key: '0',
require_pin: true, // Enable PIN requirement
redis_connection_url: REDIS_URL,
authentication_handler: async (args) => {
// Custom authentication logic
const isValid = await validateUserPin(args.msisdn, args.input);
return isValid;
}
};Proxy Service Integration
Proxy Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| target_url | string | - | URL of the target USSD service (required unless using proxy_handler) |
| forward_original_format | boolean | true | Forward telco-specific format or use normalized format |
| timeout | number | 25000 | Request timeout in milliseconds |
| retry_attempts | number | 1 | Number of retry attempts for failed requests |
| retry_delay | number | 1000 | Delay between retries in milliseconds |
| session_bridge | boolean | true | Share session state with target service |
| headers | object | {} | Additional HTTP headers to send |
| transform_request | function | - | Custom request transformation function |
| transform_response | function | - | Custom response transformation function |
| proxy_handler | function | - | Custom handler function instead of HTTP request |
Request Formats
1. Original Telco Format (default)
// Forwards the exact format from the telco
const telcoProxy: iUssdMenu = {
title: 'Service',
type: 'proxy',
proxy_config: {
target_url: 'https://api.example.com/v1/ussd/mtn',
forward_original_format: true // default
}
};
// MTN format example:
// {
// sessionId: 'session-123',
// messageType: '1',
// msisdn: '233241234567',
// ussdString: '1',
// serviceCode: '*123#',
// cellId: 'cell-id',
// imsi: 'imsi-id',
// ...
// }2. Normalized Format with Network Detection
// Sends normalized format with auto-detected network
const normalizedProxy: iUssdMenu = {
title: 'Service',
type: 'proxy',
proxy_config: {
target_url: 'https://api.example.com/ussd', // Single endpoint
forward_original_format: false
}
};
// Normalized format:
// {
// msisdn: '0241234567',
// session_id: 'session-123',
// user_input: '1',
// service_code: '*123#',
// is_new_request: false,
// provider: 'mtn',
// network: 'MTN', // Auto-detected from phone number
// session_data: {...},
// original_request: {...} // Original telco request
// }Backend Implementation Examples
Single Endpoint with Network Routing
// Express.js example
app.post('/ussd', (req, res) => {
const { network, msisdn, user_input, session_id } = req.body;
// Route based on detected network
switch(network) {
case 'MTN':
return handleMTN(req, res);
case 'TELECEL':
return handleTelecel(req, res);
case 'AIRTEL-TIGO':
return handleAirtelTigo(req, res);
default:
return handleUnknown(req, res);
}
});Separate Telco Endpoints
// MTN endpoint - receives MTN format
app.post('/v1/ussd/mtn', (req, res) => {
const { sessionId, msisdn, ussdString, messageType } = req.body;
// Handle MTN-specific format
});
// Telecel endpoint - receives Telecel format
app.post('/v1/ussd/telecel', (req, res) => {
const { sessionid, msisdn, msg, type } = req.body;
// Handle Telecel-specific format
});XML Payload Support
The proxy handler automatically preserves XML payloads when forwarding requests to target endpoints. This is particularly important for Telecel and Airtel-Tigo providers which use XML formats.
XML Preservation Features
- Automatic Detection: Content-type headers are automatically detected and preserved
- Raw Body Forwarding: XML bodies are forwarded as-is without conversion to JSON
- Provider Support: Full support for Telecel and Airtel-Tigo XML formats
- Response Parsing: XML responses are properly parsed and converted to the expected format
Example: XML Provider Proxy
// Telecel XML format proxy
const telecelProxy: iUssdMenu = {
title: 'Telecel Service',
type: 'proxy',
proxy_config: {
target_url: 'https://api.example.com/v1/ussd/telecel',
forward_original_format: true // Preserves XML format
}
};
// When receiving Telecel XML:
// <request>
// <msisdn>233241234567</msisdn>
// <sessionid>session-123</sessionid>
// <type>1</type>
// <msg>1</msg>
// </request>
// The XML is forwarded as-is to the target endpointExample: Airtel-Tigo XML Support
// Airtel-Tigo XML format proxy
const airtelTigoProxy: iUssdMenu = {
title: 'Airtel-Tigo Service',
type: 'proxy',
proxy_config: {
target_url: 'https://api.example.com/v1/ussd/airtel-tigo',
forward_original_format: true // Preserves XML format
}
};
// Airtel-Tigo XML with nested structure:
// <dataSet>
// <param>
// <name>sessionId</name>
// <value>session-123</value>
// </param>
// <param>
// <name>msisdn</name>
// <value>233241234567</value>
// </param>
// </dataSet>Backend Handling for XML
// Express.js with XML body parser
const xmlParser = require('express-xml-bodyparser');
app.use(xmlParser());
app.post('/v1/ussd/telecel', (req, res) => {
// req.body contains parsed XML
const { msisdn, sessionid, msg, type } = req.body.request;
// Process and return XML response
res.set('Content-Type', 'text/xml');
res.send(`
<response>
<message>Response message</message>
<type>1</type>
</response>
`);
});Use Cases
- Third-party Integration: Connect to external USSD services (loans, insurance, payments)
- Microservices Architecture: Route to different backend services
- Partner Applications: Seamlessly integrate partner USSD applications
- Load Balancing: Distribute requests across multiple backends
- Network Portability: Handle users who port numbers between networks
- Testing: Easy testing with different network configurations
- XML Service Integration: Seamlessly integrate with XML-based USSD services
// Complete example with all features
const advancedProxyMenu: iUssdMenu = {
title: 'Payment Gateway',
type: 'proxy',
proxy_config: {
target_url: process.env.PAYMENT_API_URL,
forward_original_format: false, // Use normalized format
timeout: 25000,
retry_attempts: 2,
retry_delay: 1000,
session_bridge: true,
headers: {
'Authorization': `Bearer ${process.env.AUTH_TOKEN}`,
'X-Partner-ID': 'partner-123'
},
transform_request: (data) => ({
phoneNumber: data.msisdn,
network: data.network, // Includes auto-detected network
sessionId: data.session_id,
userInput: data.user_input,
metadata: {
provider: data.provider,
detectedNetwork: data.network,
timestamp: Date.now()
}
}),
transform_response: (response) => ({
message: response.display_text,
require_feedback: response.await_input === true,
session_data: response.session_state
})
}
};Path-based Handler Modules
// menu-handler.js - External handler module
export const root_menu = async (args) => {
// Handler logic
const userData = await fetchUserData(args.msisdn);
return `Welcome ${userData.name}`;
};
export const process_payment = async (args) => {
// Another handler in same module
return 'Payment processed';
};
// Usage in menu definition
const menus: iUssdMenu[] = [
{
title: 'User Info',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'root_menu', // Function to call
handler: path.join(__dirname, '/handlers/menu-handler.js')
},
{
title: 'Process Payment',
type: 'handler',
handler_type: 'path',
handler_name_in_destination: 'process_payment', // Different function
handler: path.join(__dirname, '/handlers/menu-handler.js')
}
];📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
Mohammed Bashiru
- GitHub: @iammohammedb
🙏 Acknowledgments
- Redis for reliable session management
- TypeScript for type safety
- Jest for testing framework
- All contributors and users of this library
📞 Support
- 🌐 Website: www.bashtech.solutions
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
Made with ❤️ by the BashTech Solutions Team
