Commit ceb8e91da04ce15ae3e9ba09b04171f475ee265e

Authored by Adhidarma Hadiwinoto
1 parent 02f7bea1fa
Exists in master

Deteksi msisdn

Showing 2 changed files with 6 additions and 6 deletions Inline Diff

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