Commit 384d84098c6f6dadcf5020e61edd18e6fbc91514

Authored by Adhidarma Hadiwinoto
1 parent 59e6da8245
Exists in master

debug

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