Commit a3472a346deae2225ea34276c99d1e677e114e24

Authored by Adhidarma Hadiwinoto
1 parent d46698ccca
Exists in master

modem msisdn on matrix

Showing 1 changed file with 3 additions and 3 deletions Inline Diff

lib/partner-mkios.js
1 "use strict"; 1 "use strict";
2 2
3 const fs = require('fs'); 3 const fs = require('fs');
4 const moment = require('moment'); 4 const moment = require('moment');
5 5
6 const Modem = require('./modem'); 6 const Modem = require('./modem');
7 7
8 const pullgw = require('komodo-sdk/gateway/pull'); 8 const pullgw = require('komodo-sdk/gateway/pull');
9 9
10 const config = require('komodo-sdk/config'); 10 const config = require('komodo-sdk/config');
11 const logger = require('komodo-sdk/logger'); 11 const logger = require('komodo-sdk/logger');
12 const matrix = require('komodo-sdk/matrix'); 12 const matrix = require('komodo-sdk/matrix');
13 13
14 const modemDashboard = require('./modem-dashboard'); 14 const modemDashboard = require('./modem-dashboard');
15 15
16 const chipsJsonFile = process.cwd() + '/chips.json'; 16 const chipsJsonFile = process.cwd() + '/chips.json';
17 const chips = fs.existsSync(chipsJsonFile) ? require(chipsJsonFile) : {}; 17 const chips = fs.existsSync(chipsJsonFile) ? require(chipsJsonFile) : {};
18 18
19 if (config && config.debug_modem) { 19 if (config && config.debug_modem) {
20 process.env.KOMODO_DEBUG_MODEM=1; 20 process.env.KOMODO_DEBUG_MODEM=1;
21 } 21 }
22 22
23 if (!config || !config.partner || !config.partner.pin) { 23 if (!config || !config.partner || !config.partner.pin) {
24 logger.warn('Undefined PIN'); 24 logger.warn('Undefined PIN');
25 process.exit(1); 25 process.exit(1);
26 } 26 }
27 27
28 matrix.modem = {}; 28 matrix.modem = {};
29 29
30 matrix.not_ready = true; 30 matrix.not_ready = true;
31 matrix.not_ready_ts = null; 31 matrix.not_ready_ts = null;
32 matrix.not_ready_ts_readable = null; 32 matrix.not_ready_ts_readable = null;
33 matrix.not_ready_max_age_secs = null; 33 matrix.not_ready_max_age_secs = null;
34 34
35 matrix.stock = {}; 35 matrix.stock = {};
36 36
37 const db = require('./local-db').getConnection(); 37 const db = require('./local-db').getConnection();
38 const pendingArchive = require('./pending-archive'); 38 const pendingArchive = require('./pending-archive');
39 const patternMatcher = require('./pattern-rule-matcher'); 39 const patternMatcher = require('./pattern-rule-matcher');
40 const smsHandler = require('./sms-handler'); 40 const smsHandler = require('./sms-handler');
41 41
42 const modem = new Modem(config.partner.modem.dev, {baudRate: 115200}); 42 const modem = new Modem(config.partner.modem.dev, {baudRate: 115200});
43 43
44 const resumeHandlers = {}; 44 const resumeHandlers = {};
45 45
46 let last_trx_id = null; 46 let last_trx_id = null;
47 47
48 modem.on('open', function() { 48 modem.on('open', function() {
49 logger.info('Modem opened'); 49 logger.info('Modem opened');
50 50
51 const ussd_command = '*776*' + config.partner.pin + '#'; 51 const ussd_command = '*776*' + config.partner.pin + '#';
52 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 52 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
53 if (err) { 53 if (err) {
54 logger.warn('Error inserting ussd command (stock check) to local database', {err: err}); 54 logger.warn('Error inserting ussd command (stock check) to local database', {err: err});
55 } 55 }
56 }); 56 });
57 57
58 modem.sendUSSD(ussd_command, function() {}); 58 modem.sendUSSD(ussd_command, function() {});
59 }) 59 })
60 60
61 modem.on('imsi', function(imsi) { 61 modem.on('imsi', function(imsi) {
62 logger.verbose('IMSI: ' + imsi); 62 logger.verbose('IMSI: ' + imsi);
63 matrix.modem.imsi = imsi; 63 matrix.modem.imsi = imsi;
64 matrix.modem.msisdn = chips && chips.by_imsi && chips.by_imsi[imsi] && chips.by_imsi[imsi].msisdn ? chips.by_imsi[imsi].msisdn : config.partner.msisdn;
64 }) 65 })
65 66
66 67
67 function onIncomingSMS(sms) { 68 function onIncomingSMS(sms) {
68 logger.info('Incoming SMS', {sms: sms}); 69 logger.info('Incoming SMS', {sms: sms});
69 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) { 70 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) {
70 if (err) { 71 if (err) {
71 logger.warn('Error inserting sms to local database', {err: err}); 72 logger.warn('Error inserting sms to local database', {err: err});
72 } 73 }
73 }); 74 });
74 75
75 if (!smsHandler.isAllowedSender(sms.sender)) { 76 if (!smsHandler.isAllowedSender(sms.sender)) {
76 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender}); 77 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender});
77 return; 78 return;
78 } 79 }
79 80
80 const stocks = smsHandler.getMultiStockBalance(sms.msg); 81 const stocks = smsHandler.getMultiStockBalance(sms.msg);
81 if (stocks && Array.isArray(stocks) && stocks.length) { 82 if (stocks && Array.isArray(stocks) && stocks.length) {
82 stocks.forEach(function(stock) { 83 stocks.forEach(function(stock) {
83 const vals = stock.split('='); 84 const vals = stock.split('=');
84 updateStock(vals[0], vals[1]); 85 updateStock(vals[0], vals[1]);
85 }) 86 })
86 } 87 }
87 else { 88 else {
88 const stock = smsHandler.getStockBalance(sms.msg); 89 const stock = smsHandler.getStockBalance(sms.msg);
89 if (stock.name && stock.balance) { 90 if (stock.name && stock.balance) {
90 updateStock(stock.name, stock.balance); 91 updateStock(stock.name, stock.balance);
91 } 92 }
92 } 93 }
93 94
94 const destination = smsHandler.getDestination(sms.msg); 95 const destination = smsHandler.getDestination(sms.msg);
95 if (!destination) { 96 if (!destination) {
96 logger.verbose('Ignoring SMS with unknown trx destination'); 97 logger.verbose('Ignoring SMS with unknown trx destination');
97 return; 98 return;
98 } 99 }
99 100
100 const product = smsHandler.getProduct(sms.msg); 101 const product = smsHandler.getProduct(sms.msg);
101 if (!product) { 102 if (!product) {
102 logger.verbose('Ignoring SMS with unknown trx product'); 103 logger.verbose('Ignoring SMS with unknown trx product');
103 return; 104 return;
104 } 105 }
105 106
106 const trx_date = smsHandler.getTrxDate(sms.msg); 107 const trx_date = smsHandler.getTrxDate(sms.msg);
107 if (!trx_date) { 108 if (!trx_date) {
108 logger.verbose('Ignoring SMS with unknown trx date'); 109 logger.verbose('Ignoring SMS with unknown trx date');
109 return; 110 return;
110 } 111 }
111 112
112 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date}); 113 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date});
113 pendingArchive.get(destination, product, trx_date, function(err, trx_id) { 114 pendingArchive.get(destination, product, trx_date, function(err, trx_id) {
114 if (!trx_id) { 115 if (!trx_id) {
115 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date}); 116 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date});
116 return; 117 return;
117 } 118 }
118 119
119 deleteResumeHandler(trx_id); 120 deleteResumeHandler(trx_id);
120 pendingArchive.remove(trx_id); 121 pendingArchive.remove(trx_id);
121 122
122 report({ 123 report({
123 trx_id: trx_id, 124 trx_id: trx_id,
124 rc: smsHandler.getRc(sms.msg) || '68', 125 rc: smsHandler.getRc(sms.msg) || '68',
125 sn: smsHandler.getSn(sms.msg), 126 sn: smsHandler.getSn(sms.msg),
126 message: 'SMS: ' + sms.msg 127 message: 'SMS: ' + sms.msg
127 }) 128 })
128 }) 129 })
129 130
130 } 131 }
131 modem.on('incoming sms', onIncomingSMS); 132 modem.on('incoming sms', onIncomingSMS);
132 133
133 modem.on('signal strength', function(signal_strength) { 134 modem.on('signal strength', function(signal_strength) {
134 matrix.modem.signal_strength = signal_strength; 135 matrix.modem.signal_strength = signal_strength;
135 logger.verbose('Signal strength: ' + signal_strength); 136 logger.verbose('Signal strength: ' + signal_strength);
136 }) 137 })
137 138
138 function onUSSDResponse(data) { 139 function onUSSDResponse(data) {
139 logger.verbose('Got USSD response', {response: data}); 140 logger.verbose('Got USSD response', {response: data});
140 141
141 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) { 142 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) {
142 if (err) { 143 if (err) {
143 logger.warn('Error inserting ussd response to local database', {err: err}); 144 logger.warn('Error inserting ussd response to local database', {err: err});
144 } 145 }
145 }); 146 });
146 147
147 148
148 if (!last_trx_id) return; 149 if (!last_trx_id) return;
149 150
150 151
151 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68'; 152 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68';
152 if (rc !== '68') { 153 if (rc !== '68') {
153 onTrxFinish(last_trx_id); 154 onTrxFinish(last_trx_id);
154 } 155 }
155 156
156 deleteResumeHandler(last_trx_id);; 157 deleteResumeHandler(last_trx_id);;
157 unsuspendPull(); 158 unsuspendPull();
158 159
159 report({ 160 report({
160 trx_id: last_trx_id, 161 trx_id: last_trx_id,
161 rc: rc, 162 rc: rc,
162 sn: getSnFromMessage(data), 163 sn: getSnFromMessage(data),
163 message: data 164 message: data
164 }); 165 });
165 166
166 const stock_name = getStockProductFromMessage(data); 167 const stock_name = getStockProductFromMessage(data);
167 const stock_balance = getStockBalanceFromMesssage(data); 168 const stock_balance = getStockBalanceFromMesssage(data);
168 169
169 updateStock(stock_name, stock_balance); 170 updateStock(stock_name, stock_balance);
170 last_trx_id = null; 171 last_trx_id = null;
171 } 172 }
172 173
173 modem.on('ussd response', onUSSDResponse); 174 modem.on('ussd response', onUSSDResponse);
174 175
175 176
176 logger.info('Opening MODEM'); 177 logger.info('Opening MODEM');
177 modem.open(function(err) { 178 modem.open(function(err) {
178 if (err) { 179 if (err) {
179 logger.warn('Error opening modem port', {err: err}); 180 logger.warn('Error opening modem port', {err: err});
180 process.exit(1); 181 process.exit(1);
181 } 182 }
182 183
183 logger.info('Modem open successfully, going to ready in 30 secs'); 184 logger.info('Modem open successfully, going to ready in 30 secs');
184 setTimeout( unsuspendPull, 30 * 1000 ) 185 setTimeout( unsuspendPull, 30 * 1000 )
185 186
186 }) 187 })
187 188
188 function updateStock(stock_name, stock_balance) { 189 function updateStock(stock_name, stock_balance) {
189 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) { 190 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) {
190 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance}); 191 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance});
191 192
192 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name; 193 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name;
193 194
194 matrix.stock[new_stock_name] = Number(stock_balance); 195 matrix.stock[new_stock_name] = Number(stock_balance);
195 196
196 logger.verbose('Stock balance updated', {stock: matrix.stock}); 197 logger.verbose('Stock balance updated', {stock: matrix.stock});
197 } 198 }
198 } 199 }
199 200
200 function getRcFromMessage(msg, rules) { 201 function getRcFromMessage(msg, rules) {
201 if (!rules || !Array.isArray(rules)) { 202 if (!rules || !Array.isArray(rules)) {
202 return '68'; 203 return '68';
203 } 204 }
204 205
205 for (let rule of rules) { 206 for (let rule of rules) {
206 if (!rule.pattern) return '68'; 207 if (!rule.pattern) return '68';
207 208
208 const re = new RegExp(rule.pattern); 209 const re = new RegExp(rule.pattern);
209 if (msg.search(re) >= 0) { 210 if (msg.search(re) >= 0) {
210 return rule.rc ? rule.rc : '68'; 211 return rule.rc ? rule.rc : '68';
211 } 212 }
212 } 213 }
213 214
214 return '68'; 215 return '68';
215 } 216 }
216 217
217 function getPatternMatchFromMessage(msg, rules) { 218 function getPatternMatchFromMessage(msg, rules) {
218 if (!rules || !Array.isArray(rules)) { 219 if (!rules || !Array.isArray(rules)) {
219 return; 220 return;
220 } 221 }
221 222
222 for (let rule of rules) { 223 for (let rule of rules) {
223 if (!rule.pattern) return; 224 if (!rule.pattern) return;
224 225
225 const re = new RegExp(rule.pattern); 226 const re = new RegExp(rule.pattern);
226 const matches = msg.match(re); 227 const matches = msg.match(re);
227 228
228 if (matches && matches.length >= 2) { 229 if (matches && matches.length >= 2) {
229 return matches[1]; 230 return matches[1];
230 } 231 }
231 } 232 }
232 } 233 }
233 234
234 function getSnFromMessage(msg, rules) { 235 function getSnFromMessage(msg, rules) {
235 return patternMatcher(msg, config.ussd_parser.sn); 236 return patternMatcher(msg, config.ussd_parser.sn);
236 } 237 }
237 238
238 function getStockProductFromMessage(msg, rules) { 239 function getStockProductFromMessage(msg, rules) {
239 return patternMatcher(msg, config.ussd_parser.stock.product); 240 return patternMatcher(msg, config.ussd_parser.stock.product);
240 } 241 }
241 242
242 function getStockBalanceFromMesssage(msg, rules) { 243 function getStockBalanceFromMesssage(msg, rules) {
243 return patternMatcher(msg, config.ussd_parser.stock.balance); 244 return patternMatcher(msg, config.ussd_parser.stock.balance);
244 } 245 }
245 246
246 function suspendPull(trx_id) { 247 function suspendPull(trx_id) {
247 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume'); 248 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume');
248 matrix.not_ready = true; 249 matrix.not_ready = true;
249 matrix.not_ready_ts = new Date(); 250 matrix.not_ready_ts = new Date();
250 matrix.not_ready_ts_readable = moment(matrix.not_ready_ts).format('YYYY-MM-DD HH:mm:ss'); 251 matrix.not_ready_ts_readable = moment(matrix.not_ready_ts).format('YYYY-MM-DD HH:mm:ss');
251 252
252 resumeHandlers['trx' + trx_id] = setTimeout(function() { 253 resumeHandlers['trx' + trx_id] = setTimeout(function() {
253 logger.verbose('Resuming supsended modem', {trx_id: trx_id}); 254 logger.verbose('Resuming supsended modem', {trx_id: trx_id});
254 unsuspendPull(); 255 unsuspendPull();
255 report({ 256 report({
256 trx_id: trx_id, 257 trx_id: trx_id,
257 rc: '68', 258 rc: '68',
258 message: 'USSD timeout' 259 message: 'USSD timeout'
259 }) 260 })
260 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 ); 261 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 );
261 } 262 }
262 263
263 function unsuspendPull() { 264 function unsuspendPull() {
264 matrix.not_ready = false; 265 matrix.not_ready = false;
265 266
266 if (matrix.not_ready_ts) { 267 if (matrix.not_ready_ts) {
267 matrix.not_ready_max_age_secs = Math.max( (new Date() - matrix.not_ready_ts) / 1000, matrix.not_ready_max_age_secs ); 268 matrix.not_ready_max_age_secs = Math.max( (new Date() - matrix.not_ready_ts) / 1000, matrix.not_ready_max_age_secs );
268 } 269 }
269 270
270 logger.verbose('Modem is ready'); 271 logger.verbose('Modem is ready');
271 } 272 }
272 273
273 function deleteResumeHandler(trx_id) { 274 function deleteResumeHandler(trx_id) {
274 if (resumeHandlers['trx' + trx_id]) { 275 if (resumeHandlers['trx' + trx_id]) {
275 logger.verbose('Unregistering delayed resume of trx id ' + trx_id); 276 logger.verbose('Unregistering delayed resume of trx id ' + trx_id);
276 clearTimeout(resumeHandlers['trx' + trx_id]); 277 clearTimeout(resumeHandlers['trx' + trx_id]);
277 delete resumeHandlers['trx' + trx_id]; 278 delete resumeHandlers['trx' + trx_id];
278 } 279 }
279 } 280 }
280 281
281 function onTrxFinish(trx_id) { 282 function onTrxFinish(trx_id) {
282 deleteResumeHandler(trx_id); 283 deleteResumeHandler(trx_id);
283 pendingArchive.remove(trx_id); 284 pendingArchive.remove(trx_id);
284 unsuspendPull(); 285 unsuspendPull();
285 } 286 }
286 287
287 function buy(task) { 288 function buy(task) {
288 if (task.product === task.remote_product) { 289 if (task.product === task.remote_product) {
289 report({ 290 report({
290 trx_id: task.trx_id, 291 trx_id: task.trx_id,
291 rc: '40', 292 rc: '40',
292 message: 'INTERNAL: Gagal melakukan transaksi. Kode USSD belum terdefinisi.' 293 message: 'INTERNAL: Gagal melakukan transaksi. Kode USSD belum terdefinisi.'
293 }); 294 });
294 return; 295 return;
295 } 296 }
296 297
297 suspendPull(task.trx_id); 298 suspendPull(task.trx_id);
298 last_trx_id = task.trx_id; 299 last_trx_id = task.trx_id;
299 300
300 pendingArchive.put(task, function(err) { 301 pendingArchive.put(task, function(err) {
301 if (err) { 302 if (err) {
302 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err}); 303 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err});
303 onTrxFinish(task.trx_id); 304 onTrxFinish(task.trx_id);
304 report({ 305 report({
305 trx_id: task.trx_id, 306 trx_id: task.trx_id,
306 rc: '40', 307 rc: '40',
307 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.' 308 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.'
308 }); 309 });
309 310
310 return; 311 return;
311 } 312 }
312 313
313 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin); 314 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin);
314 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command}); 315 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command});
315 316
316 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 317 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
317 if (err) { 318 if (err) {
318 logger.warn('Error inserting ussd command to local database', {err: err}); 319 logger.warn('Error inserting ussd command to local database', {err: err});
319 } 320 }
320 }); 321 });
321 modem.sendUSSD(ussd_command, function() {}); 322 modem.sendUSSD(ussd_command, function() {});
322 }) 323 })
323 } 324 }
324 325
325 function report(data) { 326 function report(data) {
326 if (data.message) { 327 if (data.message) {
327 if (matrix.modem.imsi) { 328 if (matrix.modem.imsi) {
328 data.message = 'CHIP-IMSI: ' + matrix.modem.imsi + '; ' + data.message; 329 data.message = 'CHIP-IMSI: ' + matrix.modem.imsi + '; ' + data.message;
329 } 330 }
330 331
331 const msisdn = chips && chips.by_imsi && chips.by_imsi[imsi] && chips.by_imsi[imsi].msisdn ? chips.by_imsi[imsi].msisdn : config.partner.msisdn; 332 if (matrix.modem.msisdn) {
332 if (msisdn) { 333 data.message = 'CHIP-MSISDN: ' + matrix.modem.msisdn + '; ' + data.message;
333 data.message = 'CHIP-MSISDN: ' + msisdn + '; ' + data.message;
334 } 334 }
335 } 335 }
336 pullgw.report(data); 336 pullgw.report(data);
337 } 337 }
338 338
339 exports.buy = buy; 339 exports.buy = buy;