Commit c4315f5e2b984420816d92ddcb1c548738b8df31

Authored by Adhidarma Hadiwinoto
1 parent 5621a6057f
Exists in master

resume begitu ada ussd response

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