-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathindex.js
More file actions
204 lines (175 loc) · 5.98 KB
/
index.js
File metadata and controls
204 lines (175 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import { NativeModules, NativeEventEmitter } from 'react-native';
import { UssdValidator } from './src/validator';
import { UssdParser } from './src/parser';
import { UssdCodes } from './src/ussdCodes';
import { UssdErrorCode, DEFAULT_CONFIG, EVENTS } from './src/constants';
const { Ussd: NativeUssd } = NativeModules;
// Create event emitter
export const ussdEventEmitter = new NativeEventEmitter(NativeUssd);
// Response middleware storage
const responseMiddleware = [];
// Enhanced Ussd module with JavaScript wrappers
const Ussd = {
// Native methods (pass-through)
getSimInfo: () => NativeUssd.getSimInfo(),
sendResponse: (response, subscriptionId) => NativeUssd.sendResponse(response, subscriptionId),
cancelSession: (subscriptionId) => NativeUssd.cancelSession(subscriptionId),
getSessionState: () => NativeUssd.getSessionState(),
setTimeout: (milliseconds) => NativeUssd.setTimeout(milliseconds),
isNetworkAvailable: () => NativeUssd.isNetworkAvailable(),
getNetworkStatus: () => NativeUssd.getNetworkStatus(),
getCarrierInfo: (subscriptionId) => NativeUssd.getCarrierInfo(subscriptionId),
checkPermissions: () => NativeUssd.checkPermissions(),
requestPermissions: () => NativeUssd.requestPermissions(),
getDefaultSimForCalls: () => NativeUssd.getDefaultSimForCalls(),
getDefaultSimForData: () => NativeUssd.getDefaultSimForData(),
getHistory: (limit) => NativeUssd.getHistory(limit),
clearHistory: () => NativeUssd.clearHistory(),
setSecureMode: (enabled) => NativeUssd.setSecureMode(enabled),
getPendingResponses: () => NativeUssd.getPendingResponses(),
getMetrics: () => NativeUssd.getMetrics(),
/**
* Dial a USSD code with options
* @param {string} ussdCode - The USSD code to dial
* @param {Object} options - Options for dialing
* @returns {Promise<void>}
*/
async dial(ussdCode, options = {}) {
// Validate code
const validation = UssdValidator.validateCode(ussdCode);
if (!validation.isValid) {
throw new Error(validation.error);
}
const formattedCode = validation.formattedCode;
// Call native dial with options
return NativeUssd.dial(formattedCode, options);
},
/**
* Dial with automatic retry on failure
* @param {string} code - USSD code to dial
* @param {Object} options - Dial options
* @param {Object} retryConfig - Retry configuration
* @returns {Promise<void>}
*/
async dialWithRetry(code, options = {}, retryConfig = {}) {
const {
maxAttempts = DEFAULT_CONFIG.DEFAULT_RETRY_ATTEMPTS,
delayMs = DEFAULT_CONFIG.DEFAULT_RETRY_DELAY_MS,
exponentialBackoff = true,
retryOnErrors = ['NETWORK_ERROR', 'TIMEOUT', 'SESSION_BUSY'],
} = retryConfig;
let lastError;
let currentDelay = delayMs;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
await this.dial(code, options);
return; // Success
} catch (error) {
lastError = error;
// Check if we should retry this error
const shouldRetry =
attempt < maxAttempts &&
(retryOnErrors.includes(error.code) || retryOnErrors.includes(error.message));
if (!shouldRetry) {
throw error;
}
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, currentDelay));
// Increase delay for next attempt if exponential backoff
if (exponentialBackoff) {
currentDelay *= 2;
}
console.log(`USSD retry attempt ${attempt}/${maxAttempts} for code: ${code}`);
}
}
throw lastError;
},
/**
* Execute multiple USSD requests in sequence
* @param {Array} requests - Array of {code, options} objects
* @param {number} delayBetweenMs - Delay between requests in milliseconds
* @returns {Promise<Array>} Array of results
*/
async dialBatch(requests, delayBetweenMs = DEFAULT_CONFIG.BATCH_DELAY_MS) {
const results = [];
for (let i = 0; i < requests.length; i++) {
const { code, options } = requests[i];
try {
await this.dial(code, options);
results.push({
request: requests[i],
success: true,
response: null, // Response comes via events
});
} catch (error) {
results.push({
request: requests[i],
success: false,
error: error.message || error.toString(),
});
}
// Wait before next request (except for last one)
if (i < requests.length - 1) {
await new Promise((resolve) => setTimeout(resolve, delayBetweenMs));
}
}
return results;
},
/**
* Add response middleware
* @param {Function} middleware - Middleware function
*/
addResponseMiddleware(middleware) {
if (typeof middleware === 'function') {
responseMiddleware.push(middleware);
} else {
throw new Error('Middleware must be a function');
}
},
/**
* Remove response middleware
* @param {Function} middleware - Middleware function to remove
*/
removeResponseMiddleware(middleware) {
const index = responseMiddleware.indexOf(middleware);
if (index > -1) {
responseMiddleware.splice(index, 1);
}
},
/**
* Clear all response middleware
*/
clearResponseMiddleware() {
responseMiddleware.length = 0;
},
};
// Apply middleware to USSD responses
ussdEventEmitter.addListener(EVENTS.USSD_EVENT, async (event) => {
if (responseMiddleware.length > 0) {
let processedResponse = event.ussdReply;
for (const middleware of responseMiddleware) {
try {
processedResponse = await middleware(
processedResponse,
event.code,
event.subscriptionId
);
} catch (error) {
console.error('Error in USSD response middleware:', error);
}
}
// Emit processed response if changed
if (processedResponse !== event.ussdReply) {
event.ussdReply = processedResponse;
}
}
});
// Export everything
export {
UssdValidator,
UssdParser,
UssdCodes,
UssdErrorCode,
EVENTS,
};
export default Ussd;