Commit 7f2cc9f7e58f864a312e879e87372e7f6f9c465b

Authored by Adhidarma Hadiwinoto
1 parent 06ff0a91c0
Exists in master

IMSI dan MSISDN pada report ke core

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

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