Commit e9104fb87389dfc147fbc5763cd6bfc5acf29341

Authored by Adhidarma Hadiwinoto
1 parent 517105a6b1
Exists in master

non reload sn

Showing 3 changed files with 28 additions and 3 deletions Inline Diff

1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 2
3 import logging 3 import logging
4 from logging.handlers import TimedRotatingFileHandler 4 from logging.handlers import TimedRotatingFileHandler
5 import ConfigParser 5 import ConfigParser
6 import json 6 import json
7 from os import getpid 7 from os import getpid
8 import re 8 import re
9 import signal 9 import signal
10 import sys 10 import sys
11 11
12 config = ConfigParser.RawConfigParser() 12 config = ConfigParser.RawConfigParser()
13 config.read('config.ini') 13 config.read('config.ini')
14 14
15 PORT = config.get('globals','PORT') 15 PORT = config.get('globals','PORT')
16 BAUDRATE = config.getint('globals', 'BAUDRATE') 16 BAUDRATE = config.getint('globals', 'BAUDRATE')
17 PIN = None # SIM card PIN (if any) 17 PIN = None # SIM card PIN (if any)
18 PIN_TRX = config.get('globals', 'PIN_TRX') 18 PIN_TRX = config.get('globals', 'PIN_TRX')
19 19
20 BASE_CHIPINFO = config.get('globals', 'BASE_CHIPINFO') 20 BASE_CHIPINFO = config.get('globals', 'BASE_CHIPINFO')
21 CHIPINFO = BASE_CHIPINFO 21 CHIPINFO = BASE_CHIPINFO
22 22
23 AAA_HOST = config.get('globals', 'AAA_HOST') 23 AAA_HOST = config.get('globals', 'AAA_HOST')
24 CITY = config.get('globals', 'CITY') 24 CITY = config.get('globals', 'CITY')
25 PRODUCTS = config.get('globals', 'PRODUCTS') 25 PRODUCTS = config.get('globals', 'PRODUCTS')
26 26
27 REDIS_HOST = config.get('globals', 'REDIS_HOST') 27 REDIS_HOST = config.get('globals', 'REDIS_HOST')
28 REDIS_PORT = config.getint('globals', 'REDIS_PORT') 28 REDIS_PORT = config.getint('globals', 'REDIS_PORT')
29 REDIS_TTL = config.getint('globals', 'REDIS_TTL') 29 REDIS_TTL = config.getint('globals', 'REDIS_TTL')
30 REDIS_DISABLE_PULL_TTL = config.getint('globals', 'REDIS_DISABLE_PULL_TTL') 30 REDIS_DISABLE_PULL_TTL = config.getint('globals', 'REDIS_DISABLE_PULL_TTL')
31 31
32 PULL_INTERVAL = config.getint('globals', 'PULL_INTERVAL') 32 PULL_INTERVAL = config.getint('globals', 'PULL_INTERVAL')
33 SLEEP_AFTER_TOPUP = config.getint('globals', 'SLEEP_AFTER_TOPUP') 33 SLEEP_AFTER_TOPUP = config.getint('globals', 'SLEEP_AFTER_TOPUP')
34 SLEEP_BETWEEN_BALANCE_N_TOPUP = config.getint('globals', 'SLEEP_BETWEEN_BALANCE_N_TOPUP') 34 SLEEP_BETWEEN_BALANCE_N_TOPUP = config.getint('globals', 'SLEEP_BETWEEN_BALANCE_N_TOPUP')
35 TOPUP_USSD_TIMEOUT = config.getint('globals', 'TOPUP_USSD_TIMEOUT') 35 TOPUP_USSD_TIMEOUT = config.getint('globals', 'TOPUP_USSD_TIMEOUT')
36 SLEEP_AFTER_USSD_ERROR = 180 36 SLEEP_AFTER_USSD_ERROR = 180
37 37
38 MIN_SIGNAL_STRENGTH = 0 38 MIN_SIGNAL_STRENGTH = 0
39 try: 39 try:
40 MIN_SIGNAL_STRENGTH = config.getint('globals', 'MIN_SIGNAL_STRENGTH') 40 MIN_SIGNAL_STRENGTH = config.getint('globals', 'MIN_SIGNAL_STRENGTH')
41 except: 41 except:
42 pass 42 pass
43 if not MIN_SIGNAL_STRENGTH: 43 if not MIN_SIGNAL_STRENGTH:
44 MIN_SIGNAL_STRENGTH = 13 44 MIN_SIGNAL_STRENGTH = 13
45 45
46 MIN_BALANCE = config.getint('globals', 'MIN_BALANCE') 46 MIN_BALANCE = config.getint('globals', 'MIN_BALANCE')
47 47
48 PULL_COUNT = 0 48 PULL_COUNT = 0
49 MSISDN = '' 49 MSISDN = ''
50 BALANCE = 0 50 BALANCE = 0
51 51
52 DISABLE_SEM=0 52 DISABLE_SEM=0
53 DISABLE_SEM_ON_TRX=60 53 DISABLE_SEM_ON_TRX=60
54 54
55 TERMINATING=False 55 TERMINATING=False
56 56
57 LAST_REQUEST_ID = None 57 LAST_REQUEST_ID = None
58 LAST_SN = None 58 LAST_SN = None
59 LAST_PRODUCT = '' 59 LAST_PRODUCT = ''
60 60
61 PULSA = 0 61 PULSA = 0
62 MASA_AKTIF = "" 62 MASA_AKTIF = ""
63 63
64 logger = None 64 logger = None
65 65
66 from gsmmodem.modem import GsmModem 66 from gsmmodem.modem import GsmModem
67 from gsmmodem.exceptions import TimeoutException 67 from gsmmodem.exceptions import TimeoutException
68 68
69 from time import sleep 69 from time import sleep
70 from time import strftime 70 from time import strftime
71 71
72 import redis 72 import redis
73 import requests 73 import requests
74 74
75 import sate24 75 import sate24
76 import xltunai 76 import xltunai
77 77
78 redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=0) 78 redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=0)
79 79
80 def signalHandler(signum, frame): 80 def signalHandler(signum, frame):
81 global TERMINATING 81 global TERMINATING
82 82
83 if signum == signal.SIGINT or signum == signal.SIGTERM: 83 if signum == signal.SIGINT or signum == signal.SIGTERM:
84 logger.info('Signal ({0}) received, waiting for next loop to terminate. Terminating...'.format(signum)) 84 logger.info('Signal ({0}) received, waiting for next loop to terminate. Terminating...'.format(signum))
85 TERMINATING = True 85 TERMINATING = True
86 86
87 def handleSms(sms): 87 def handleSms(sms):
88 global CHIPINFO 88 global CHIPINFO
89 global DISABLE_SEM 89 global DISABLE_SEM
90 global PRODUCTS 90 global PRODUCTS
91 global LAST_PRODUCT 91 global LAST_PRODUCT
92 global LAST_REQUEST_ID 92 global LAST_REQUEST_ID
93 global LAST_SN
93 94
94 logger.info(u'Incoming SMS from: {0}; Time: {1}; Message: {2}'.format(sms.number, sms.time, sms.text)) 95 logger.info(u'Incoming SMS from: {0}; Time: {1}; Message: {2}'.format(sms.number, sms.time, sms.text))
95 96
96 try: 97 try:
97 epic_key = 'epic.msgin.gw:' + BASE_CHIPINFO 98 epic_key = 'epic.msgin.gw:' + BASE_CHIPINFO
98 redis_client.publish(epic_key + '.message', u'{0}: {1} ({2})'.format(sms.number, sms.text, CHIPINFO)) 99 redis_client.publish(epic_key + '.message', u'{0}: {1} ({2})'.format(sms.number, sms.text, CHIPINFO))
99 except: 100 except:
100 logger.warning('Fail to publish epic message to redis') 101 logger.warning('Fail to publish epic message to redis')
101 102
102 if not xltunai.isValidSender(sms.number): 103 if not xltunai.isValidSender(sms.number):
103 logger.info('Ignoring incoming SMS from invalid sender') 104 logger.info('Ignoring incoming SMS from invalid sender')
104 return 105 return
105 106
106 if sms.text.find('Terimakasih, transaksi CASH IN ke akun') >= 0: 107 if sms.text.find('Terimakasih, transaksi CASH IN ke akun') >= 0:
107 logger.info('handleSms: CASH IN, aktivasi pull jika non aktif') 108 logger.info('handleSms: CASH IN, aktivasi pull jika non aktif')
109 LAST_SN = xltunai.getSNFromCashInMessage(sms.text)
108 enablePull() 110 enablePull()
109 return 111 return
110 112
111 if sms.text.find('Maaf, transaksi gagal') >= 0: 113 if sms.text.find('Maaf, transaksi gagal') >= 0:
112 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text) 114 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text)
113 return 115 return
114 116
115 elif sms.text.find('PIN yang Anda masukkan salah. Silahkan ulangi kembali') >= 0: 117 elif sms.text.find('PIN yang Anda masukkan salah. Silahkan ulangi kembali') >= 0:
116 pushTopupStatus(LAST_REQUEST_ID, '91', sms.text) 118 pushTopupStatus(LAST_REQUEST_ID, '91', sms.text)
117 return 119 return
118 120
119 elif sms.text.find('Maaf, saldo XL-Tunai anda tidak cukup') >= 0: 121 elif sms.text.find('Maaf, saldo XL-Tunai anda tidak cukup') >= 0:
120 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text) 122 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text)
121 return 123 return
122 124
123 elif sms.text.find('Maaf, layanan ini hanya untuk nomor tujuan prabayar XL') >= 0: 125 elif sms.text.find('Maaf, layanan ini hanya untuk nomor tujuan prabayar XL') >= 0:
124 pushTopupStatus(LAST_REQUEST_ID, '14', sms.text) 126 pushTopupStatus(LAST_REQUEST_ID, '14', sms.text)
125 return 127 return
126 128
127 elif sms.text.find('Mohon maaf, nomor yang Anda masukkan tidak valid') >= 0: 129 elif sms.text.find('Mohon maaf, nomor yang Anda masukkan tidak valid') >= 0:
128 pushTopupStatus(LAST_REQUEST_ID, '14', sms.text) 130 pushTopupStatus(LAST_REQUEST_ID, '14', sms.text)
129 return 131 return
130 132
131 elif sms.text.find('Mohon maaf, transaksi Anda melebihi limit nominal bulanan') >= 0: 133 elif sms.text.find('Mohon maaf, transaksi Anda melebihi limit nominal bulanan') >= 0:
132 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text) 134 pushTopupStatus(LAST_REQUEST_ID, '40', sms.text)
133 135
134 logger.info('Monthly limit for "{0}" detected, removing from product list'.format(LAST_PRODUCT)) 136 logger.info('Monthly limit for "{0}" detected, removing from product list'.format(LAST_PRODUCT))
135 PRODUCTS = sate24.removeProduct(PRODUCTS, LAST_PRODUCT) 137 PRODUCTS = sate24.removeProduct(PRODUCTS, LAST_PRODUCT)
136 logger.warning('Monthly limit for "{0}" exceeded. New active products: "{1}"'.format(LAST_PRODUCT, PRODUCTS)) 138 logger.warning('Monthly limit for "{0}" exceeded. New active products: "{1}"'.format(LAST_PRODUCT, PRODUCTS))
137 return 139 return
138 140
139 elif sms.text.find('Maaf, transaksi Anda masih dalam proses') >= 0: 141 elif sms.text.find('Maaf, transaksi Anda masih dalam proses') >= 0:
140 pushTopupStatus(LAST_REQUEST_ID, '68', sms.text) 142 pushTopupStatus(LAST_REQUEST_ID, '68', sms.text)
141 return 143 return
142 144
145 elif sms.text.find('Anda terima uang XLTunai') >= 0:
146 LAST_SN = xltunai.getSNFromReceiveTransferMessage(sms.text)
147 return
148
143 destination = xltunai.getDestinationFromMessage(sms.text) 149 destination = xltunai.getDestinationFromMessage(sms.text)
144 if destination == '': 150 if destination == '':
145 logger.warning('handleSms: gagal parsing nomor tujuan') 151 logger.warning('handleSms: gagal parsing nomor tujuan')
146 return 152 return
147 153
148 nominal = xltunai.getNominalFromMessage(sms.text) 154 nominal = xltunai.getNominalFromMessage(sms.text)
149 if nominal == '': 155 if nominal == '':
150 logger.warning('handleSms: gagal parsing nominal') 156 logger.warning('handleSms: gagal parsing nominal')
151 return 157 return
152 158
153 sn = xltunai.getSNFromMessage(sms.text) 159 sn = xltunai.getSNFromMessage(sms.text)
154 if sn == '': 160 if sn == '':
155 logger.warning('handleSms: gagal parsing SN') 161 logger.warning('handleSms: gagal parsing SN')
156 162
157 163
158 DISABLE_SEM = 0 164 DISABLE_SEM = 0
159 165
160 response_code = xltunai.getResponseCodeByMessage(sms.text) 166 response_code = xltunai.getResponseCodeByMessage(sms.text)
161 167
162 request_id = getRequestIdByNominalDestination(nominal, destination) 168 request_id = getRequestIdByNominalDestination(nominal, destination)
163 if not request_id: 169 if not request_id:
164 logger.info('Unknown request id for nominal:{0} destination:{1}'.format(nominal, destination)) 170 logger.info('Unknown request id for nominal:{0} destination:{1}'.format(nominal, destination))
165 return 171 return
166 172
167 pushTopupStatus(request_id, response_code, sms.text) 173 pushTopupStatus(request_id, response_code, sms.text)
168 try: 174 try:
169 deleteMultipleStoredSms(2) 175 deleteMultipleStoredSms(2)
170 except: 176 except:
171 #logger.warning('Failed on delete SMS') 177 #logger.warning('Failed on delete SMS')
172 pass 178 pass
173 179
174 180
175 def getRequestIdByNominalDestination(nominal, destination): 181 def getRequestIdByNominalDestination(nominal, destination):
176 global CHIPINFO 182 global CHIPINFO
177 183
178 redis_key = sate24.keyByNominalDestination(CHIPINFO, nominal, destination) 184 redis_key = sate24.keyByNominalDestination(CHIPINFO, nominal, destination)
179 #return redis_client.spop(redis_key) 185 #return redis_client.spop(redis_key)
180 return redis_client.rpop(redis_key) 186 return redis_client.rpop(redis_key)
181 187
182 def pushTopupStatus(request_id, response_code, _message, dontparsesn = False): 188 def pushTopupStatus(request_id, response_code, _message, dontparsesn = False):
183 global BALANCE 189 global BALANCE
184 global CHIPINFO 190 global CHIPINFO
185 global LAST_SN 191 global LAST_SN
186 192
187 redis_key = sate24.keyByRequestId(CHIPINFO, request_id) + '.response_code' 193 redis_key = sate24.keyByRequestId(CHIPINFO, request_id) + '.response_code'
188 if response_code == '00': 194 if response_code == '00':
189 redis_client.set(redis_key, response_code) 195 redis_client.set(redis_key, response_code)
190 else: 196 else:
191 redis_response_code = redis_client.get(redis_key) 197 redis_response_code = redis_client.get(redis_key)
192 if redis_response_code == '00': 198 if redis_response_code == '00':
193 logger.info('Ignoring message from success trx ') 199 logger.info('Ignoring message from success trx ')
194 return 200 return
195 201
196 message = "{0} -- Prev balance: {1}".format(_message, BALANCE) 202 message = "{0} -- Prev balance: {1}".format(_message, BALANCE)
197 203
198 timestamp = strftime('%Y%m%d%H%M%S') 204 timestamp = strftime('%Y%m%d%H%M%S')
199 205
200 if response_code == '00' and not dontparsesn: 206 if response_code == '00' and not dontparsesn:
201 sn = xltunai.getSNFromMessage(message) 207 sn = xltunai.getSNFromMessage(message)
202 LAST_SN = sn 208 LAST_SN = sn
203 message = 'SN={0};{1}'.format(sn, message) 209 message = 'SN={0};{1}'.format(sn, message)
204 210
205 push_message = CHIPINFO + '$' + message 211 push_message = CHIPINFO + '$' + message
206 212
207 payload = { 213 payload = {
208 'trans_id': request_id, 214 'trans_id': request_id,
209 'trans_date': timestamp, 215 'trans_date': timestamp,
210 'resp_code': response_code, 216 'resp_code': response_code,
211 'ussd_msg': push_message 217 'ussd_msg': push_message
212 } 218 }
213 219
214 url = AAA_HOST + '/topup' 220 url = AAA_HOST + '/topup'
215 221
216 try: 222 try:
217 logger.info('Sending topup status to AAA') 223 logger.info('Sending topup status to AAA')
218 logger.info(payload) 224 logger.info(payload)
219 r = requests.get(url, params=payload) 225 r = requests.get(url, params=payload)
220 except: 226 except:
221 logger.warning('Error sending topup status to AAA') 227 logger.warning('Error sending topup status to AAA')
222 228
223 try: 229 try:
224 # publish echi topup report ke redis 230 # publish echi topup report ke redis
225 echi_key = 'echi.topup_report.gw:' + BASE_CHIPINFO 231 echi_key = 'echi.topup_report.gw:' + BASE_CHIPINFO
226 232
227 redis_client.publish(echi_key + '.json', json.dumps(payload)) 233 redis_client.publish(echi_key + '.json', json.dumps(payload))
228 234
229 echi_message = timestamp + ' - ' + response_code + ' - ' + message 235 echi_message = timestamp + ' - ' + response_code + ' - ' + message
230 redis_client.publish(echi_key + '.message', echi_message) 236 redis_client.publish(echi_key + '.message', echi_message)
231 except: 237 except:
232 logger.warning('Error publishing topup report to redis') 238 logger.warning('Error publishing topup report to redis')
233 239
234 def getIsDisableRedisKey(): 240 def getIsDisableRedisKey():
235 return CHIPINFO + '.pulldisable' 241 return CHIPINFO + '.pulldisable'
236 242
237 def isPullEnable(): 243 def isPullEnable():
238 redis_key = getIsDisableRedisKey() 244 redis_key = getIsDisableRedisKey()
239 result = 'FALSE' 245 result = 'FALSE'
240 246
241 try: 247 try:
242 result = redis_client.get(redis_key) 248 result = redis_client.get(redis_key)
243 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL) 249 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL)
244 except: 250 except:
245 return False 251 return False
246 252
247 if not result: 253 if not result:
248 return True 254 return True
249 255
250 return result == 'FALSE' 256 return result == 'FALSE'
251 257
252 def enablePull(): 258 def enablePull():
253 logger.info('Enabling Pull on products {0}'.format(PRODUCTS)) 259 logger.info('Enabling Pull on products {0}'.format(PRODUCTS))
254 redis_key = getIsDisableRedisKey() 260 redis_key = getIsDisableRedisKey()
255 try: 261 try:
256 redis_client.set(redis_key, 'FALSE') 262 redis_client.set(redis_key, 'FALSE')
257 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL) 263 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL)
258 except: 264 except:
259 return 265 return
260 266
261 def disablePull(): 267 def disablePull():
262 global DISABLE_SEM 268 global DISABLE_SEM
263 global DISABLE_SEM_ON_TRX 269 global DISABLE_SEM_ON_TRX
264 270
265 logger.info('Disabling Pull') 271 logger.info('Disabling Pull')
266 redis_key = getIsDisableRedisKey() 272 redis_key = getIsDisableRedisKey()
267 try: 273 try:
268 redis_client.set(redis_key, 'TRUE') 274 redis_client.set(redis_key, 'TRUE')
269 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL) 275 redis_client.expire(redis_key, REDIS_DISABLE_PULL_TTL)
270 except: 276 except:
271 return 277 return
272 278
273 def adviceLastSN(requestId, modem): 279 def adviceLastSN(requestId, modem):
274 global DISABLE_SEM 280 global DISABLE_SEM
275 global LAST_SN 281 global LAST_SN
276 global TOPUP_USSD_TIMEOUT 282 global TOPUP_USSD_TIMEOUT
277 283
278 if not LAST_SN: 284 if not LAST_SN:
279 message = 'Tidak ada trx sebelumnya untuk perbandingan SN. Silahkan bandingkan prev balance dengan trx berikutnya di chip yang sama' 285 message = 'Tidak ada trx sebelumnya untuk perbandingan SN. Silahkan bandingkan prev balance dengan trx berikutnya di chip yang sama'
280 pushTopupStatus(requestId, '68', message) 286 pushTopupStatus(requestId, '68', message)
281 DISABLE_SEM = 0 287 DISABLE_SEM = 0
282 return 288 return
283 289
284 ussd_command = xltunai.getHistoryUSSDCommand() 290 ussd_command = xltunai.getHistoryUSSDCommand()
285 291
286 logger.info(u'Executing advice {0}'.format(ussd_command)) 292 logger.info(u'Executing advice {0}'.format(ussd_command))
287 try: 293 try:
288 response = modem.sendUssd(ussd_command, TOPUP_USSD_TIMEOUT) 294 response = modem.sendUssd(ussd_command, TOPUP_USSD_TIMEOUT)
289 295
290 message = response.message.strip() 296 message = response.message.strip()
291 logger.info(u'USSD response: {0}'.format(message)) 297 logger.info(u'USSD response: {0}'.format(message))
292 298
293 lastSNFromHistory = xltunai.getLastSNFromHistoryMessage(message) 299 lastSNFromHistory = xltunai.getLastSNFromHistoryMessage(message)
294 300
295 if response.sessionActive: 301 if response.sessionActive:
296 response.cancel() 302 response.cancel()
297 303
298 if not lastSNFromHistory: 304 if not lastSNFromHistory:
299 if DISABLE_SEM: 305 if DISABLE_SEM:
300 logger.info('Failed to get last sn from history, retrying in 15 secs') 306 logger.info('Failed to get last sn from history, retrying in 15 secs')
301 sleep(15) 307 sleep(15)
302 adviceLastSN(requestid, modem) 308 adviceLastSN(requestid, modem)
303 309
304 elif lastSNFromHistory == 'P2P TRANSFER': 310 elif lastSNFromHistory == 'NON RELOAD':
305 topupMessage = "Topup gagal berdasarkan advice. Trx terakhir adalah P2P Transfer." 311 topupMessage = "Topup gagal berdasarkan advice. Trx terakhir adalah P2P Transfer."
306 pushTopupStatus(requestId, '40', topupMessage) 312 pushTopupStatus(requestId, '40', topupMessage)
307 DISABLE_SEM = 0 313 DISABLE_SEM = 0
308 314
309 elif lastSNFromHistory == LAST_SN: 315 elif lastSNFromHistory == LAST_SN:
310 topupMessage = "Topup gagal berdasarkan advice. {0} = {1}. {2}".format(lastSNFromHistory, LAST_SN, message) 316 topupMessage = "Topup gagal berdasarkan advice. {0} = {1}. {2}".format(lastSNFromHistory, LAST_SN, message)
311 pushTopupStatus(requestId, '40', topupMessage) 317 pushTopupStatus(requestId, '40', topupMessage)
312 DISABLE_SEM = 0 318 DISABLE_SEM = 0
313 319
314 320
315 else: 321 else:
316 topupMessage = "SN={0}; Topup berhasil berdasarkan advice. {1}".format(lastSNFromHistory, message) 322 topupMessage = "SN={0}; Topup berhasil berdasarkan advice. {1}".format(lastSNFromHistory, message)
317 LAST_SN = lastSNFromHistory 323 LAST_SN = lastSNFromHistory
318 pushTopupStatus(requestId, '00', topupMessage, True) 324 pushTopupStatus(requestId, '00', topupMessage, True)
319 DISABLE_SEM = 0 325 DISABLE_SEM = 0
320 326
321 except: 327 except:
322 message = "USSD Error: Something wrong when executing USSD advice. Signal strength: {0}".format(modem.signalStrength) 328 message = "USSD Error: Something wrong when executing USSD advice. Signal strength: {0}".format(modem.signalStrength)
323 logger.warning(message) 329 logger.warning(message)
324 pushTopupStatus(requestId, '68', message) 330 pushTopupStatus(requestId, '68', message)
325 331
326 sleep(60) 332 sleep(60)
327 adviceLastSN(requestId, modem) 333 adviceLastSN(requestId, modem)
328 #waitForResultSmsAfterUssdError(modem, task['requestId'], message) 334 #waitForResultSmsAfterUssdError(modem, task['requestId'], message)
329 return 335 return
330 336
331 337
332 338
333 def topupTask(task, modem): 339 def topupTask(task, modem):
334 global LAST_REQUEST_ID 340 global LAST_REQUEST_ID
335 global LAST_PRODUCT 341 global LAST_PRODUCT
336 342
337 if not task: 343 if not task:
338 return 344 return
339 345
340 if task['status'] != 'OK': 346 if task['status'] != 'OK':
341 return 347 return
342 348
343 LAST_REQUEST_ID = task['requestId'] 349 LAST_REQUEST_ID = task['requestId']
344 LAST_PRODUCT = task['product'].upper() 350 LAST_PRODUCT = task['product'].upper()
345 351
346 redis_key = sate24.keyByRequestId(CHIPINFO, task['requestId']) 352 redis_key = sate24.keyByRequestId(CHIPINFO, task['requestId'])
347 redis_client.set(redis_key, task) 353 redis_client.set(redis_key, task)
348 redis_client.expire(redis_key, REDIS_TTL) 354 redis_client.expire(redis_key, REDIS_TTL)
349 355
350 nominal = xltunai.getNominalFromProduct(task['product']) 356 nominal = xltunai.getNominalFromProduct(task['product'])
351 intl_destination = xltunai.toInternationalNumber(task['destination']) 357 intl_destination = xltunai.toInternationalNumber(task['destination'])
352 358
353 redis_key = sate24.keyByNominalDestination(CHIPINFO, nominal, intl_destination) 359 redis_key = sate24.keyByNominalDestination(CHIPINFO, nominal, intl_destination)
354 #redis_client.sadd(redis_key, task['requestId']) 360 #redis_client.sadd(redis_key, task['requestId'])
355 try: 361 try:
356 redis_client.rpush(redis_key, task['requestId']) 362 redis_client.rpush(redis_key, task['requestId'])
357 except: 363 except:
358 redis_client.delete(redis_key) 364 redis_client.delete(redis_key)
359 redis_client.rpush(redis_key, task['requestId']) 365 redis_client.rpush(redis_key, task['requestId'])
360 finally: 366 finally:
361 redis_client.expire(redis_key, REDIS_TTL) 367 redis_client.expire(redis_key, REDIS_TTL)
362 368
363 #pushTopupStatus(task['requestId'], '68', 'Siap mengirimkan trx ke operator') 369 #pushTopupStatus(task['requestId'], '68', 'Siap mengirimkan trx ke operator')
364 370
365 message = 'Executing USSD' 371 message = 'Executing USSD'
366 response_code = '68' 372 response_code = '68'
367 373
368 ussd_command = xltunai.getTopupUSSDCommand(task['destination'], task['product'], PIN_TRX) 374 ussd_command = xltunai.getTopupUSSDCommand(task['destination'], task['product'], PIN_TRX)
369 375
370 logger.info(u'Executing {0}'.format(ussd_command)) 376 logger.info(u'Executing {0}'.format(ussd_command))
371 try: 377 try:
372 response = modem.sendUssd(ussd_command, TOPUP_USSD_TIMEOUT) 378 response = modem.sendUssd(ussd_command, TOPUP_USSD_TIMEOUT)
373 379
374 message = response.message.strip() 380 message = response.message.strip()
375 logger.info(u'USSD response: {0}'.format(message)) 381 logger.info(u'USSD response: {0}'.format(message))
376 382
377 response_code = xltunai.getResponseCodeByUSSDResponse(message) 383 response_code = xltunai.getResponseCodeByUSSDResponse(message)
378 384
379 if response.sessionActive: 385 if response.sessionActive:
380 response.cancel() 386 response.cancel()
381 387
382 except TimeoutException: 388 except TimeoutException:
383 message = "USSD Error: Timeout when executing USSD topup. Signal strength: {0}".format(modem.signalStrength) 389 message = "USSD Error: Timeout when executing USSD topup. Signal strength: {0}".format(modem.signalStrength)
384 logger.warning(message) 390 logger.warning(message)
385 pushTopupStatus(task['requestId'], '68', message) 391 pushTopupStatus(task['requestId'], '68', message)
386 #waitForResultSmsAfterUssdError(modem, task['requestId'], message) 392 #waitForResultSmsAfterUssdError(modem, task['requestId'], message)
387 sleep(15) 393 sleep(15)
388 adviceLastSN(task['requestId'], modem) 394 adviceLastSN(task['requestId'], modem)
389 return 395 return
390 except: 396 except:
391 message = "USSD Error: Something wrong when executing USSD topup. Signal strength: {0}".format(modem.signalStrength) 397 message = "USSD Error: Something wrong when executing USSD topup. Signal strength: {0}".format(modem.signalStrength)
392 logger.warning(message) 398 logger.warning(message)
393 pushTopupStatus(task['requestId'], '68', message) 399 pushTopupStatus(task['requestId'], '68', message)
394 #waitForResultSmsAfterUssdError(modem, task['requestId'], message) 400 #waitForResultSmsAfterUssdError(modem, task['requestId'], message)
395 sleep(15) 401 sleep(15)
396 adviceLastSN(task['requestId'], modem) 402 adviceLastSN(task['requestId'], modem)
397 return 403 return
398 404
399 DISABLE_SEM = DISABLE_SEM_ON_TRX 405 DISABLE_SEM = DISABLE_SEM_ON_TRX
400 pushTopupStatus(task['requestId'], response_code, message) 406 pushTopupStatus(task['requestId'], response_code, message)
401 407
402 def waitForResultSmsAfterUssdError(modem, request_id, last_error_message = ''): 408 def waitForResultSmsAfterUssdError(modem, request_id, last_error_message = ''):
403 logger.info('Sleeping for {0} seconds after an USSD error'.format(SLEEP_AFTER_USSD_ERROR)) 409 logger.info('Sleeping for {0} seconds after an USSD error'.format(SLEEP_AFTER_USSD_ERROR))
404 sleep(SLEEP_AFTER_USSD_ERROR) 410 sleep(SLEEP_AFTER_USSD_ERROR)
405 411
406 redis_key = sate24.keyByRequestId(CHIPINFO, request_id) + '.response_code' 412 redis_key = sate24.keyByRequestId(CHIPINFO, request_id) + '.response_code'
407 response_code = redis_client.get(redis_key) 413 response_code = redis_client.get(redis_key)
408 414
409 if response_code == '68' or response_code == None: 415 if response_code == '68' or response_code == None:
410 416
411 prev_balance = BALANCE 417 prev_balance = BALANCE
412 current_balance = checkBalance(modem, do_not_update = True, return_result = True) 418 current_balance = checkBalance(modem, do_not_update = True, return_result = True)
413 419
414 if current_balance == 0: 420 if current_balance == 0:
415 return 421 return
416 422
417 if current_balance < prev_balance: 423 if current_balance < prev_balance:
418 delta = prev_balance - current_balance 424 delta = prev_balance - current_balance
419 pushTopupStatus( 425 pushTopupStatus(
420 request_id, 426 request_id,
421 '00', 427 '00',
422 'Transaksi dianggap sukses karena saldo berkurang {0}. Last error message: {1}'.format( 428 'Transaksi dianggap sukses karena saldo berkurang {0}. Last error message: {1}'.format(
423 delta, 429 delta,
424 last_error_message 430 last_error_message
425 ) 431 )
426 ) 432 )
427 else: 433 else:
428 pushTopupStatus( 434 pushTopupStatus(
429 request_id, 435 request_id,
430 '40', 436 '40',
431 'Transaksi dianggap gagal karena saldo tidak berkurang. Last error message: {0}'.format(last_error_message) 437 'Transaksi dianggap gagal karena saldo tidak berkurang. Last error message: {0}'.format(last_error_message)
432 ) 438 )
433 439
434 def _saveBalanceToRedis(balance_key, balance_value): 440 def _saveBalanceToRedis(balance_key, balance_value):
435 try: 441 try:
436 redis_client.set(balance_key, balance_value) 442 redis_client.set(balance_key, balance_value)
437 redis_client.expire(balance_key, 3600*24*60) 443 redis_client.expire(balance_key, 3600*24*60)
438 except: 444 except:
439 logger.warning('Failed to save balance to redis') 445 logger.warning('Failed to save balance to redis')
440 446
441 def saveBalanceToRedis(balance): 447 def saveBalanceToRedis(balance):
442 global BASE_CHIPINFO 448 global BASE_CHIPINFO
443 global CHIPINFO 449 global CHIPINFO
444 global MSISDN 450 global MSISDN
445 451
446 try: 452 try:
447 balance = int(balance) 453 balance = int(balance)
448 except ValueError: 454 except ValueError:
449 return 455 return
450 456
451 457
452 data = { 458 data = {
453 'balance.gw:' + BASE_CHIPINFO: balance, 459 'balance.gw:' + BASE_CHIPINFO: balance,
454 'balance.gw:' + CHIPINFO: balance, 460 'balance.gw:' + CHIPINFO: balance,
455 'balance.gw:' + MSISDN: balance 461 'balance.gw:' + MSISDN: balance
456 } 462 }
457 463
458 redis_pipe = redis_client.pipeline() 464 redis_pipe = redis_client.pipeline()
459 465
460 try: 466 try:
461 redis_pipe.mset(data) 467 redis_pipe.mset(data)
462 468
463 for k in data: 469 for k in data:
464 redis_pipe.expire(k, 3600 * 24 * 60) 470 redis_pipe.expire(k, 3600 * 24 * 60)
465 471
466 except: 472 except:
467 logger.warning('Failed to save balance to redis') 473 logger.warning('Failed to save balance to redis')
468 finally: 474 finally:
469 redis_pipe.execute() 475 redis_pipe.execute()
470 476
471 def saveSignalStrengthToRedis(value): 477 def saveSignalStrengthToRedis(value):
472 global BASE_CHIPINFO 478 global BASE_CHIPINFO
473 global CHIPINFO 479 global CHIPINFO
474 global MSISDN 480 global MSISDN
475 481
476 try: 482 try:
477 redis_pipe = redis_client.pipeline() 483 redis_pipe = redis_client.pipeline()
478 redis_client.set('balance.gw:' + BASE_CHIPINFO + '.signal', value) 484 redis_client.set('balance.gw:' + BASE_CHIPINFO + '.signal', value)
479 redis_client.expire('balance.gw:' + BASE_CHIPINFO + '.signal', 15 * 60) 485 redis_client.expire('balance.gw:' + BASE_CHIPINFO + '.signal', 15 * 60)
480 except: 486 except:
481 logger.warning('Failed to save signal strength to redis') 487 logger.warning('Failed to save signal strength to redis')
482 488
483 489
484 def pull(modem): 490 def pull(modem):
485 global PRODUCTS 491 global PRODUCTS
486 global PULL_COUNT 492 global PULL_COUNT
487 global BALANCE 493 global BALANCE
488 global DISABLE_SEM 494 global DISABLE_SEM
489 global MIN_SIGNAL_STRENGTH 495 global MIN_SIGNAL_STRENGTH
490 496
491 if not PRODUCTS: 497 if not PRODUCTS:
492 return 498 return
493 499
494 signalStrength = modem.signalStrength 500 signalStrength = modem.signalStrength
495 if signalStrength < MIN_SIGNAL_STRENGTH: 501 if signalStrength < MIN_SIGNAL_STRENGTH:
496 #logger.warn('Signal strength {0} < {1}, skipping pull'.format(signalStrength, MIN_SIGNAL_STRENGTH)) 502 #logger.warn('Signal strength {0} < {1}, skipping pull'.format(signalStrength, MIN_SIGNAL_STRENGTH))
497 return 503 return
498 504
499 pull_per_minute = 60 / PULL_INTERVAL 505 pull_per_minute = 60 / PULL_INTERVAL
500 506
501 PULL_COUNT += 1 507 PULL_COUNT += 1
502 if PULL_COUNT % (5 * pull_per_minute) == 0: 508 if PULL_COUNT % (5 * pull_per_minute) == 0:
503 checkBalance(modem) 509 checkBalance(modem)
504 sleep(15) 510 sleep(15)
505 PULL_COUNT = 0 511 PULL_COUNT = 0
506 512
507 if DISABLE_SEM > 0: 513 if DISABLE_SEM > 0:
508 logger.info('SEMAPHORE is still on, delaying pull') 514 logger.info('SEMAPHORE is still on, delaying pull')
509 DISABLE_SEM -= 1 515 DISABLE_SEM -= 1
510 return 516 return
511 517
512 if not isPullEnable(): 518 if not isPullEnable():
513 return 519 return
514 520
515 r = None 521 r = None
516 url = AAA_HOST + '/pull?city=' + CITY + '&nom=' + PRODUCTS 522 url = AAA_HOST + '/pull?city=' + CITY + '&nom=' + PRODUCTS
517 try: 523 try:
518 r = requests.get(url) 524 r = requests.get(url)
519 except: 525 except:
520 logger.warning("Error requesting to AAA") 526 logger.warning("Error requesting to AAA")
521 return 527 return
522 528
523 if not r: 529 if not r:
524 return 530 return
525 531
526 task = sate24.parsePullMessage(r.text) 532 task = sate24.parsePullMessage(r.text)
527 533
528 if task['status'] == 'OK': 534 if task['status'] == 'OK':
529 535
530 logger.info('PULL ' + url + ' => ' + r.text) 536 logger.info('PULL ' + url + ' => ' + r.text)
531 publishAAATaskToRedis(task) 537 publishAAATaskToRedis(task)
532 538
533 if not checkBalance(modem) or BALANCE == 0: 539 if not checkBalance(modem) or BALANCE == 0:
534 pushTopupStatus(task['requestId'], '91', 'Transaksi dibatalkan karena gagal check balance') 540 pushTopupStatus(task['requestId'], '91', 'Transaksi dibatalkan karena gagal check balance')
535 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP) 541 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP)
536 return 542 return
537 543
538 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP) 544 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP)
539 545
540 topupTask(task, modem) 546 topupTask(task, modem)
541 sleep(SLEEP_AFTER_TOPUP) 547 sleep(SLEEP_AFTER_TOPUP)
542 548
543 def publishAAATaskToRedis(task): 549 def publishAAATaskToRedis(task):
544 try: 550 try:
545 key = 'kimochi.aaa_pull.gw:' + BASE_CHIPINFO 551 key = 'kimochi.aaa_pull.gw:' + BASE_CHIPINFO
546 plain_text = task['timestamp'] + ' - ' + task['member'] + ' isi ' + task['product'] + ' ke ' + task['destination']; 552 plain_text = task['timestamp'] + ' - ' + task['member'] + ' isi ' + task['product'] + ' ke ' + task['destination'];
547 553
548 redis_client.publish(key + '.json', json.dumps(task)) 554 redis_client.publish(key + '.json', json.dumps(task))
549 redis_client.publish(key + '.text', plain_text); 555 redis_client.publish(key + '.text', plain_text);
550 except: 556 except:
551 logger.warning('Error publishing kimochi') 557 logger.warning('Error publishing kimochi')
552 558
553 def publishMessageToRedis(): 559 def publishMessageToRedis():
554 pass 560 pass
555 561
556 def pullLoop(modem): 562 def pullLoop(modem):
557 global TERMINATING 563 global TERMINATING
558 564
559 while True: 565 while True:
560 signalStrength = modem.signalStrength 566 signalStrength = modem.signalStrength
561 saveSignalStrengthToRedis(signalStrength) 567 saveSignalStrengthToRedis(signalStrength)
562 568
563 if TERMINATING: 569 if TERMINATING:
564 logger.info('Terminated by request signal') 570 logger.info('Terminated by request signal')
565 sys.exit(0) 571 sys.exit(0)
566 572
567 pull(modem) 573 pull(modem)
568 574
569 sleep(PULL_INTERVAL) 575 sleep(PULL_INTERVAL)
570 576
571 def checkSignal(modem): 577 def checkSignal(modem):
572 logger.info('Signal: {0}'.format(modem.signalStrength)) 578 logger.info('Signal: {0}'.format(modem.signalStrength))
573 try: 579 try:
574 redis_client.set(CHIPINFO + '.signal', modem.signalStrength) 580 redis_client.set(CHIPINFO + '.signal', modem.signalStrength)
575 redis_client.expire(CHIPINFO + '.signal', 3600*24) 581 redis_client.expire(CHIPINFO + '.signal', 3600*24)
576 except: 582 except:
577 logger.warning("Can not save signal strength to redis") 583 logger.warning("Can not save signal strength to redis")
578 584
579 def checkAccount(modem): 585 def checkAccount(modem):
580 try: 586 try:
581 ussd_string = '*123*120*8*3#' 587 ussd_string = '*123*120*8*3#'
582 response = modem.sendUssd(ussd_string, 30) 588 response = modem.sendUssd(ussd_string, 30)
583 logger.info('Account Info: {0}'.format(response.message)) 589 logger.info('Account Info: {0}'.format(response.message))
584 590
585 if response.sessionActive: 591 if response.sessionActive:
586 response.cancel() 592 response.cancel()
587 except: 593 except:
588 logger.warning('Error when requesting account info') 594 logger.warning('Error when requesting account info')
589 return False 595 return False
590 596
591 def checkBalance(modem, do_not_update = False, return_result = False): 597 def checkBalance(modem, do_not_update = False, return_result = False):
592 global BALANCE 598 global BALANCE
593 599
594 _BALANCE = 0 600 _BALANCE = 0
595 601
596 try: 602 try:
597 603
598 ussd_string = '*123*120#' 604 ussd_string = '*123*120#'
599 response = modem.sendUssd(ussd_string, 30) 605 response = modem.sendUssd(ussd_string, 30)
600 _BALANCE = xltunai.getBalanceFromUSSDResponse(response.message) 606 _BALANCE = xltunai.getBalanceFromUSSDResponse(response.message)
601 607
602 if not do_not_update: 608 if not do_not_update:
603 BALANCE = _BALANCE 609 BALANCE = _BALANCE
604 saveBalanceToRedis(BALANCE) 610 saveBalanceToRedis(BALANCE)
605 611
606 logger.info('Balance: {0}'.format(_BALANCE)) 612 logger.info('Balance: {0}'.format(_BALANCE))
607 if response.sessionActive: 613 if response.sessionActive:
608 response.cancel() 614 response.cancel()
609 615
610 if _BALANCE != 0 and _BALANCE < MIN_BALANCE: 616 if _BALANCE != 0 and _BALANCE < MIN_BALANCE:
611 logger.info('Disabling pull, balance {0} < {1}'.format(_BALANCE, MIN_BALANCE)) 617 logger.info('Disabling pull, balance {0} < {1}'.format(_BALANCE, MIN_BALANCE))
612 disablePull() 618 disablePull()
613 619
614 except: 620 except:
615 logger.warning('Error when requesting BALANCE by USSD') 621 logger.warning('Error when requesting BALANCE by USSD')
616 if return_result: 622 if return_result:
617 return 0 623 return 0
618 else: 624 else:
619 return False 625 return False
620 626
621 try: 627 try:
622 redis_client.set(CHIPINFO + '.balance', BALANCE) 628 redis_client.set(CHIPINFO + '.balance', BALANCE)
623 except: 629 except:
624 logger.warning('Failed to save balance to redis') 630 logger.warning('Failed to save balance to redis')
625 631
626 if return_result: 632 if return_result:
627 return _BALANCE 633 return _BALANCE
628 else: 634 else:
629 return True 635 return True
630 636
631 def getPulsaInfo(modem): 637 def getPulsaInfo(modem):
632 global PULSA 638 global PULSA
633 global MASA_AKTIF 639 global MASA_AKTIF
634 640
635 try: 641 try:
636 ussd_string = "*123#" 642 ussd_string = "*123#"
637 response = modem.sendUssd(ussd_string) 643 response = modem.sendUssd(ussd_string)
638 644
639 message = response.message.strip() 645 message = response.message.strip()
640 logger.info('PULSA: {0}'.format(message)) 646 logger.info('PULSA: {0}'.format(message))
641 if response.sessionActive: 647 if response.sessionActive:
642 response.cancel() 648 response.cancel()
643 649
644 PULSA = xltunai.getPulsaFromUssdResponseMessage(message) 650 PULSA = xltunai.getPulsaFromUssdResponseMessage(message)
645 MASA_AKTIF =xltunai.getMasaAktifFromUssdResponseMessage(message) 651 MASA_AKTIF =xltunai.getMasaAktifFromUssdResponseMessage(message)
646 logger.info('PULSA: {0} -- MASA AKTIF: {1}'.format(PULSA, MASA_AKTIF)) 652 logger.info('PULSA: {0} -- MASA AKTIF: {1}'.format(PULSA, MASA_AKTIF))
647 653
648 return message 654 return message
649 655
650 except: 656 except:
651 logger.warning('Error when requesting pulsa info by USSD') 657 logger.warning('Error when requesting pulsa info by USSD')
652 return '' 658 return ''
653 659
654 def getSIMCardInfo(modem): 660 def getSIMCardInfo(modem):
655 try: 661 try:
656 ussd_string = xltunai.getSIMCardInfoUSSDCommand() 662 ussd_string = xltunai.getSIMCardInfoUSSDCommand()
657 response = modem.sendUssd(ussd_string) 663 response = modem.sendUssd(ussd_string)
658 664
659 message = response.message.strip() 665 message = response.message.strip()
660 logger.info('SIM INFO: {0}'.format(message)) 666 logger.info('SIM INFO: {0}'.format(message))
661 if response.sessionActive: 667 if response.sessionActive:
662 response.cancel() 668 response.cancel()
663 669
664 return message 670 return message
665 671
666 except: 672 except:
667 logger.warning('Error when requesting SIM card info by USSD') 673 logger.warning('Error when requesting SIM card info by USSD')
668 return '' 674 return ''
669 675
670 def updateChipInfo(msisdn): 676 def updateChipInfo(msisdn):
671 global BASE_CHIPINFO 677 global BASE_CHIPINFO
672 global CHIPINFO 678 global CHIPINFO
673 global MSISDN 679 global MSISDN
674 680
675 MSISDN = msisdn 681 MSISDN = msisdn
676 if CHIPINFO.find(msisdn) == -1: 682 if CHIPINFO.find(msisdn) == -1:
677 CHIPINFO = BASE_CHIPINFO + '-' + msisdn 683 CHIPINFO = BASE_CHIPINFO + '-' + msisdn
678 684
679 logger.info('CHIPINFO: {0}'.format(CHIPINFO)) 685 logger.info('CHIPINFO: {0}'.format(CHIPINFO))
680 686
681 def main(): 687 def main():
682 global logger 688 global logger
683 689
684 log_format = '%(asctime)s %(levelname)s: %(message)s' 690 log_format = '%(asctime)s %(levelname)s: %(message)s'
685 691
686 logging.basicConfig(format=log_format, level=logging.INFO) 692 logging.basicConfig(format=log_format, level=logging.INFO)
687 logger = logging.getLogger(__name__) 693 logger = logging.getLogger(__name__)
688 694
689 logger_formatter = logging.Formatter(log_format) 695 logger_formatter = logging.Formatter(log_format)
690 logger_handler = TimedRotatingFileHandler('logs/log', when='midnight') 696 logger_handler = TimedRotatingFileHandler('logs/log', when='midnight')
691 logger_handler.setFormatter(logger_formatter) 697 logger_handler.setFormatter(logger_formatter)
692 logger.addHandler(logger_handler) 698 logger.addHandler(logger_handler)
693 699
694 requests_logger = logging.getLogger('requests') 700 requests_logger = logging.getLogger('requests')
695 requests_logger.setLevel(logging.WARNING) 701 requests_logger.setLevel(logging.WARNING)
696 702
697 logger.info('Initializing modem...') 703 logger.info('Initializing modem...')
698 704
699 modem = GsmModem(PORT, BAUDRATE, smsReceivedCallbackFunc=handleSms) 705 modem = GsmModem(PORT, BAUDRATE, smsReceivedCallbackFunc=handleSms)
700 modem.smsTextMode = True 706 modem.smsTextMode = True
701 modem.connect(PIN) 707 modem.connect(PIN)
702 708
703 logger.info('Waiting for network coverage...') 709 logger.info('Waiting for network coverage...')
704 modem.waitForNetworkCoverage(10) 710 modem.waitForNetworkCoverage(10)
705 711
706 logger.info('Modem ready') 712 logger.info('Modem ready')
707 713
708 getPulsaInfo(modem) 714 getPulsaInfo(modem)
709 sleep(2) 715 sleep(2)
710 716
711 simcard_info = getSIMCardInfo(modem) 717 simcard_info = getSIMCardInfo(modem)
712 msisdn = xltunai.getMSISDNFromSIMCardInfo(simcard_info) 718 msisdn = xltunai.getMSISDNFromSIMCardInfo(simcard_info)
713 719
714 if not msisdn: 720 if not msisdn:
715 logger.warning('Gagal mendapatkan msisdn, terminating') 721 logger.warning('Gagal mendapatkan msisdn, terminating')
716 sleep(5) 722 sleep(5)
717 sys.exit(2) 723 sys.exit(2)
718 724
719 imsi = modem.imsi 725 imsi = modem.imsi
720 logger.info('MSISDN: {0} -- IMSI: {1}'.format(msisdn, imsi)) 726 logger.info('MSISDN: {0} -- IMSI: {1}'.format(msisdn, imsi))
721 727
722 updateChipInfo(msisdn) 728 updateChipInfo(msisdn)
723 saveSimCardInfo(imsi, msisdn) 729 saveSimCardInfo(imsi, msisdn)
724 730
725 sleep(2) 731 sleep(2)
726 732
727 saveSignalStrengthToRedis(modem.signalStrength) 733 saveSignalStrengthToRedis(modem.signalStrength)
728 734
729 logger.info('Process stored SMS') 735 logger.info('Process stored SMS')
730 try: 736 try:
731 modem.processStoredSms() 737 modem.processStoredSms()
732 except: 738 except:
733 logger.warning('Failed on Process stored SMS') 739 logger.warning('Failed on Process stored SMS')
734 740
735 logger.info('Delete stored SMS') 741 logger.info('Delete stored SMS')
736 try: 742 try:
737 modem.deleteMultipleStoredSms() 743 modem.deleteMultipleStoredSms()
738 except: 744 except:
739 #logger.warning('Failed on delete SMS') 745 #logger.warning('Failed on delete SMS')
740 pass 746 pass
741 747
742 sleep(2) 748 sleep(2)
743 749
744 checkAccount(modem) 750 checkAccount(modem)
745 sleep(5) 751 sleep(5)
746 752
747 enablePull() 753 enablePull()
748 754
749 checkBalance(modem) 755 checkBalance(modem)
750 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP) 756 sleep(SLEEP_BETWEEN_BALANCE_N_TOPUP)
751 757
752 pullLoop(modem) 758 pullLoop(modem)
753 logger.info('Waiting for SMS message...') 759 logger.info('Waiting for SMS message...')
754 try: 760 try:
755 modem.rxThread.join(2**31) # Specify a (huge) timeout so that it essentially blocks indefinitely, but still receives CTRL+C interrupt signal 761 modem.rxThread.join(2**31) # Specify a (huge) timeout so that it essentially blocks indefinitely, but still receives CTRL+C interrupt signal
756 finally: 762 finally:
757 modem.close(); 763 modem.close();
758 764
759 def saveSimCardInfo(imsi, msisdn): 765 def saveSimCardInfo(imsi, msisdn):
760 logger.info('Save sim card info to redis') 766 logger.info('Save sim card info to redis')
761 767
762 data = { 768 data = {
763 'gw': BASE_CHIPINFO, 769 'gw': BASE_CHIPINFO,
764 'imsi': imsi, 770 'imsi': imsi,
765 'msisdn': msisdn, 771 'msisdn': msisdn,
766 'pulsa': PULSA, 772 'pulsa': PULSA,
767 'masa_aktif': MASA_AKTIF 773 'masa_aktif': MASA_AKTIF
768 } 774 }
769 775
770 json_data = json.dumps(data) 776 json_data = json.dumps(data)
771 777
772 with open('simcardinfo.txt', 'w') as f: 778 with open('simcardinfo.txt', 'w') as f:
773 f.write(json_data) 779 f.write(json_data)
774 f.closed 780 f.closed
775 781
776 782
777 map_data = { 783 map_data = {
778 BASE_CHIPINFO + '.simcardinfo': json_data, 784 BASE_CHIPINFO + '.simcardinfo': json_data,
779 'simcardinfo.gw:' + BASE_CHIPINFO: json_data, 785 'simcardinfo.gw:' + BASE_CHIPINFO: json_data,
780 'simcardinfo.imsi:' + imsi: json_data, 786 'simcardinfo.imsi:' + imsi: json_data,
781 'simcardinfo.msisdn:' + msisdn: json_data 787 'simcardinfo.msisdn:' + msisdn: json_data
782 } 788 }
783 789
784 logger.info(map_data) 790 logger.info(map_data)
785 791
786 redis_pipe = redis_client.pipeline() 792 redis_pipe = redis_client.pipeline()
787 793
788 try: 794 try:
789 redis_pipe.mset(map_data) 795 redis_pipe.mset(map_data)
790 796
791 for k in data: 797 for k in data:
792 redis_pipe.expire(k, 3600 * 24 * 60) 798 redis_pipe.expire(k, 3600 * 24 * 60)
793 799
794 except: 800 except:
795 logger.warning('Failed to save simcardinfo to redis') 801 logger.warning('Failed to save simcardinfo to redis')
796 finally: 802 finally:
797 redis_pipe.execute() 803 redis_pipe.execute()
798 804
799 if __name__ == '__main__': 805 if __name__ == '__main__':
800 pidfile = open('pid.txt', 'w') 806 pidfile = open('pid.txt', 'w')
801 pidfile.write(str(getpid())) 807 pidfile.write(str(getpid()))
802 pidfile.close() 808 pidfile.close()
803 809
804 # trap CTRL-C 810 # trap CTRL-C
805 signal.signal(signal.SIGINT, signalHandler) 811 signal.signal(signal.SIGINT, signalHandler)
806 # trap supervisor stop 812 # trap supervisor stop
807 signal.signal(signal.SIGTERM, signalHandler) 813 signal.signal(signal.SIGTERM, signalHandler)
808 814
809 main() 815 main()
810 816
1 import xltunai 1 import xltunai
2 2
3 def test_getOperatorFromProduct(): 3 def test_getOperatorFromProduct():
4 assert xltunai.getOperatorFromProduct('XL5') == 'XL' 4 assert xltunai.getOperatorFromProduct('XL5') == 'XL'
5 assert xltunai.getOperatorFromProduct('XL10') == 'XL' 5 assert xltunai.getOperatorFromProduct('XL10') == 'XL'
6 assert xltunai.getOperatorFromProduct('XL25') == 'XL' 6 assert xltunai.getOperatorFromProduct('XL25') == 'XL'
7 assert xltunai.getOperatorFromProduct('XL50') == 'XL' 7 assert xltunai.getOperatorFromProduct('XL50') == 'XL'
8 assert xltunai.getOperatorFromProduct('XL100') == 'XL' 8 assert xltunai.getOperatorFromProduct('XL100') == 'XL'
9 9
10 assert xltunai.getOperatorFromProduct('XA5') == 'XA' 10 assert xltunai.getOperatorFromProduct('XA5') == 'XA'
11 assert xltunai.getOperatorFromProduct('XA10') == 'XA' 11 assert xltunai.getOperatorFromProduct('XA10') == 'XA'
12 assert xltunai.getOperatorFromProduct('XA25') == 'XA' 12 assert xltunai.getOperatorFromProduct('XA25') == 'XA'
13 assert xltunai.getOperatorFromProduct('XA50') == 'XA' 13 assert xltunai.getOperatorFromProduct('XA50') == 'XA'
14 assert xltunai.getOperatorFromProduct('XA100') == 'XA' 14 assert xltunai.getOperatorFromProduct('XA100') == 'XA'
15 15
16 assert xltunai.getOperatorFromProduct('XL100H') == 'XL' 16 assert xltunai.getOperatorFromProduct('XL100H') == 'XL'
17 17
18 def test_getNominalFromProduct(): 18 def test_getNominalFromProduct():
19 assert xltunai.getNominalFromProduct('XL5') == 5000 19 assert xltunai.getNominalFromProduct('XL5') == 5000
20 assert xltunai.getNominalFromProduct('XL10') == 10000 20 assert xltunai.getNominalFromProduct('XL10') == 10000
21 assert xltunai.getNominalFromProduct('XL25') == 25000 21 assert xltunai.getNominalFromProduct('XL25') == 25000
22 assert xltunai.getNominalFromProduct('XL50') == 50000 22 assert xltunai.getNominalFromProduct('XL50') == 50000
23 assert xltunai.getNominalFromProduct('XL100') == 100000 23 assert xltunai.getNominalFromProduct('XL100') == 100000
24 24
25 assert xltunai.getNominalFromProduct('XA5') == 5000 25 assert xltunai.getNominalFromProduct('XA5') == 5000
26 assert xltunai.getNominalFromProduct('XA10') == 10000 26 assert xltunai.getNominalFromProduct('XA10') == 10000
27 assert xltunai.getNominalFromProduct('XA25') == 25000 27 assert xltunai.getNominalFromProduct('XA25') == 25000
28 assert xltunai.getNominalFromProduct('XA50') == 50000 28 assert xltunai.getNominalFromProduct('XA50') == 50000
29 assert xltunai.getNominalFromProduct('XA100') == 100000 29 assert xltunai.getNominalFromProduct('XA100') == 100000
30 30
31 assert xltunai.getNominalFromProduct('XL100H') == 100000 31 assert xltunai.getNominalFromProduct('XL100H') == 100000
32 32
33 def test_getDenomCodeFromProduct(): 33 def test_getDenomCodeFromProduct():
34 assert xltunai.getDenomCodeFromProduct('XL5') == 1 34 assert xltunai.getDenomCodeFromProduct('XL5') == 1
35 assert xltunai.getDenomCodeFromProduct('XL10') == 2 35 assert xltunai.getDenomCodeFromProduct('XL10') == 2
36 assert xltunai.getDenomCodeFromProduct('XL25') == 3 36 assert xltunai.getDenomCodeFromProduct('XL25') == 3
37 assert xltunai.getDenomCodeFromProduct('XL50') == 4 37 assert xltunai.getDenomCodeFromProduct('XL50') == 4
38 assert xltunai.getDenomCodeFromProduct('XL100') == 5 38 assert xltunai.getDenomCodeFromProduct('XL100') == 5
39 assert xltunai.getDenomCodeFromProduct('XL200') == 6 39 assert xltunai.getDenomCodeFromProduct('XL200') == 6
40 40
41 assert xltunai.getDenomCodeFromProduct('XL300') == None 41 assert xltunai.getDenomCodeFromProduct('XL300') == None
42 42
43 def test_getCostFromProduct(): 43 def test_getCostFromProduct():
44 assert xltunai.getCostFromProduct('XL5') == 5500 44 assert xltunai.getCostFromProduct('XL5') == 5500
45 assert xltunai.getCostFromProduct('XL10') == 10500 45 assert xltunai.getCostFromProduct('XL10') == 10500
46 assert xltunai.getCostFromProduct('XL25') == 25000 46 assert xltunai.getCostFromProduct('XL25') == 25000
47 assert xltunai.getCostFromProduct('XL50') == 50000 47 assert xltunai.getCostFromProduct('XL50') == 50000
48 assert xltunai.getCostFromProduct('XL100') == 100000 48 assert xltunai.getCostFromProduct('XL100') == 100000
49 assert xltunai.getCostFromProduct('XL200') == 200000 49 assert xltunai.getCostFromProduct('XL200') == 200000
50 50
51 assert xltunai.getCostFromProduct('XL300') == 0 51 assert xltunai.getCostFromProduct('XL300') == 0
52 52
53 def test_getSNFromMessage(): 53 def test_getSNFromMessage():
54 assert xltunai.getSNFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '75614092695337' 54 assert xltunai.getSNFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '75614092695337'
55 assert xltunai.getSNFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. SN ID :75614092695337') == '' 55 assert xltunai.getSNFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. SN ID :75614092695337') == ''
56 assert xltunai.getSNFromMessage('Nomor 6287889517766 telah berhasil diisi pulsa sebesar Rp 10000.Sisa Saldo Rp 3004500. Ref No 43280489064186') == '43280489064186' 56 assert xltunai.getSNFromMessage('Nomor 6287889517766 telah berhasil diisi pulsa sebesar Rp 10000.Sisa Saldo Rp 3004500. Ref No 43280489064186') == '43280489064186'
57 57
58 def test_toInternationalNumber(): 58 def test_toInternationalNumber():
59 assert xltunai.toInternationalNumber('081808180818') == '6281808180818' 59 assert xltunai.toInternationalNumber('081808180818') == '6281808180818'
60 assert xltunai.toInternationalNumber('6281808180818') == '6281808180818' 60 assert xltunai.toInternationalNumber('6281808180818') == '6281808180818'
61 assert xltunai.toInternationalNumber('4114') == '4114' 61 assert xltunai.toInternationalNumber('4114') == '4114'
62 62
63 def test_getDestinationFromMessage(): 63 def test_getDestinationFromMessage():
64 assert xltunai.getDestinationFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '6287884153131' 64 assert xltunai.getDestinationFromMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '6287884153131'
65 65
66 def test_getNominalFromMessage(): 66 def test_getNominalFromMessage():
67 assert xltunai.getNominalFromMessage('Nomor 6287770722609 telah berhasil diisi pulsa sebesar 5000. Sisa Saldo Rp 1949500. Ref No 41040432037489') == '5000' 67 assert xltunai.getNominalFromMessage('Nomor 6287770722609 telah berhasil diisi pulsa sebesar 5000. Sisa Saldo Rp 1949500. Ref No 41040432037489') == '5000'
68 assert xltunai.getNominalFromMessage('Bla bla') == '' 68 assert xltunai.getNominalFromMessage('Bla bla') == ''
69 69
70 def test_getBalanceFromMessage(): 70 def test_getBalanceFromMessage():
71 assert xltunai.getBalanceFromMessage('Nomor 6287889517766 telah berhasil diisi pulsa sebesar Rp 10000.Sisa Saldo Rp 3004500. Ref No 43280489064186') == 3004500 71 assert xltunai.getBalanceFromMessage('Nomor 6287889517766 telah berhasil diisi pulsa sebesar Rp 10000.Sisa Saldo Rp 3004500. Ref No 43280489064186') == 3004500
72 72
73 def test_getBalanceFromUSSDResponse(): 73 def test_getBalanceFromUSSDResponse():
74 message = """USSD response message: Saldo Rp134500 74 message = """USSD response message: Saldo Rp134500
75 Isi Pulsa 5000, Pilih menu no.2 75 Isi Pulsa 5000, Pilih menu no.2
76 1 Belanja di toko 76 1 Belanja di toko
77 2 Isi Pulsa 77 2 Isi Pulsa
78 3 Pembayaran 78 3 Pembayaran
79 4 Belanja Online 79 4 Belanja Online
80 5 Kirim Uang 80 5 Kirim Uang
81 6 Tarik Uang 81 6 Tarik Uang
82 7 Pengaturan 82 7 Pengaturan
83 8 Info""" 83 8 Info"""
84 assert xltunai.getBalanceFromUSSDResponse(message) == 134500 84 assert xltunai.getBalanceFromUSSDResponse(message) == 134500
85 85
86 def test_getTopupUSSDCommand(): 86 def test_getTopupUSSDCommand():
87 assert xltunai.getTopupUSSDCommand('081808180818', 'XL2', '1234') == None 87 assert xltunai.getTopupUSSDCommand('081808180818', 'XL2', '1234') == None
88 assert xltunai.getTopupUSSDCommand('081808180818', 'XL5', '1234') == '*123*120*2*2*081808180818*1*1234#' 88 assert xltunai.getTopupUSSDCommand('081808180818', 'XL5', '1234') == '*123*120*2*2*081808180818*1*1234#'
89 assert xltunai.getTopupUSSDCommand('6281808180818', 'XL5', '1234') == '*123*120*2*2*6281808180818*1*1234#' 89 assert xltunai.getTopupUSSDCommand('6281808180818', 'XL5', '1234') == '*123*120*2*2*6281808180818*1*1234#'
90 assert xltunai.getTopupUSSDCommand('081808180818', 'XL50', '1234') == '*123*120*2*2*081808180818*4*1234#' 90 assert xltunai.getTopupUSSDCommand('081808180818', 'XL50', '1234') == '*123*120*2*2*081808180818*4*1234#'
91 91
92 def test_valid_sender(): 92 def test_valid_sender():
93 assert xltunai.isValidSender('120') == True 93 assert xltunai.isValidSender('120') == True
94 assert xltunai.isValidSender('121') == False 94 assert xltunai.isValidSender('121') == False
95 95
96 def test_getResponseCodeByMessage(): 96 def test_getResponseCodeByMessage():
97 assert xltunai.getResponseCodeByMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '00' 97 assert xltunai.getResponseCodeByMessage('Nomor 6287884153131 telah berhasil diisi pulsa sebesar Rp.5000. Nikmati transaksi XL Tunai lainnya. Ref ID :75614092695337') == '00'
98 assert xltunai.getResponseCodeByMessage('Bla bla bla') == '68' 98 assert xltunai.getResponseCodeByMessage('Bla bla bla') == '68'
99 99
100 def test_getResponseCodeByUSSDResponse(): 100 def test_getResponseCodeByUSSDResponse():
101 assert xltunai.getResponseCodeByUSSDResponse('Mohon maaf, nomor yang Anda masukkan tidak valid') == '14' 101 assert xltunai.getResponseCodeByUSSDResponse('Mohon maaf, nomor yang Anda masukkan tidak valid') == '14'
102 assert xltunai.getResponseCodeByUSSDResponse('Bla bla bla') == '68' 102 assert xltunai.getResponseCodeByUSSDResponse('Bla bla bla') == '68'
103 103
104 def test_getMSISDNFromSIMCardInfo(): 104 def test_getMSISDNFromSIMCardInfo():
105 assert xltunai.getMSISDNFromSIMCardInfo('Nomor 6287880852347 adalah nomor dengan POC JK0 dengan ICCID 8962116820756544479') == '6287880852347' 105 assert xltunai.getMSISDNFromSIMCardInfo('Nomor 6287880852347 adalah nomor dengan POC JK0 dengan ICCID 8962116820756544479') == '6287880852347'
106 assert xltunai.getMSISDNFromSIMCardInfo('bla bla bla') == '' 106 assert xltunai.getMSISDNFromSIMCardInfo('bla bla bla') == ''
107 107
108 def test_getIMSIFromSIMCardInfo(): 108 def test_getIMSIFromSIMCardInfo():
109 assert xltunai.getIMSIFromSIMCardInfo('Nomor 6287880852347 adalah nomor dengan POC JK0 dengan ICCID 8962116820756544479') == '8962116820756544479' 109 assert xltunai.getIMSIFromSIMCardInfo('Nomor 6287880852347 adalah nomor dengan POC JK0 dengan ICCID 8962116820756544479') == '8962116820756544479'
110 assert xltunai.getIMSIFromSIMCardInfo('bla bla bla') == '' 110 assert xltunai.getIMSIFromSIMCardInfo('bla bla bla') == ''
111 111
112 def test_getPulsaFromUssdResponseMessage(): 112 def test_getPulsaFromUssdResponseMessage():
113 assert xltunai.getPulsaFromUssdResponseMessage('Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama!') == 41875 113 assert xltunai.getPulsaFromUssdResponseMessage('Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama!') == 41875
114 assert xltunai.getPulsaFromUssdResponseMessage('''Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama! 114 assert xltunai.getPulsaFromUssdResponseMessage('''Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama!
115 1 Mau 115 1 Mau
116 2 Info 4G 116 2 Info 4G
117 3 Internet 117 3 Internet
118 4 Pkt Nelp&SMS 118 4 Pkt Nelp&SMS
119 5 Pkt Seru 119 5 Pkt Seru
120 6 mPulsa 120 6 mPulsa
121 7 60Mazda2''') == 41875 121 7 60Mazda2''') == 41875
122 assert xltunai.getPulsaFromUssdResponseMessage('') == None 122 assert xltunai.getPulsaFromUssdResponseMessage('') == None
123 assert xltunai.getPulsaFromUssdResponseMessage('Pulsa habis s/d') == None 123 assert xltunai.getPulsaFromUssdResponseMessage('Pulsa habis s/d') == None
124 124
125 def test_getPulsaFromUssdResponseMessage(): 125 def test_getPulsaFromUssdResponseMessage():
126 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama!') == '2015-09-16' 126 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 41875 s/d 16Sep15. Mendadak DISKON 1 JUTA pelanggan pertama!') == '2015-09-16'
127 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 53000 s.d 10Sep15. Pkt TalkMania-XTRA 250Mnt, Rp3375!') == '2015-09-10' 127 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 53000 s.d 10Sep15. Pkt TalkMania-XTRA 250Mnt, Rp3375!') == '2015-09-10'
128 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 41875 s/d. Mendadak DISKON 1 JUTA pelanggan pertama!') == None 128 assert xltunai.getMasaAktifFromUssdResponseMessage('Pulsa 41875 s/d. Mendadak DISKON 1 JUTA pelanggan pertama!') == None
129 129
130 130
131 def test_getLastSNFromMessage(): 131 def test_getLastSNFromMessage():
132 assert xltunai.getLastSNFromHistoryMessage('Transaksi Terakhir : 20063778248257/5500/12-02-2016 14:37:08/M201312021528418273770285/P201511211752675316198716/RELOAD XL 5000') == '20063778248257' 132 assert xltunai.getLastSNFromHistoryMessage('Transaksi Terakhir : 20063778248257/5500/12-02-2016 14:37:08/M201312021528418273770285/P201511211752675316198716/RELOAD XL 5000') == '20063778248257'
133 assert xltunai.getLastSNFromHistoryMessage('Transaksi Terakhir :\n20063778248257/5500/12-02-2016 14:37:08/M201312021528418273770285/P201511211752675316198716/RELOAD XL 5000') == '20063778248257' 133 assert xltunai.getLastSNFromHistoryMessage('Transaksi Terakhir :\n20063778248257/5500/12-02-2016 14:37:08/M201312021528418273770285/P201511211752675316198716/RELOAD XL 5000') == '20063778248257'
134
135 def test_getSNFromReceiveTransferMessage():
136 assert xltunai.getSNFromReceiveTransferMessage('Anda terima uang XLTunai Rp2500000 dr 6287886957792 DIDIT ARIYANTO pada 19/02/2016 16:14:32.Cek saldo di *123*120#.RefID 49498359729433. Info hub 817') == '49498359729433'
137
138 def test_getSNFromCashInMessage():
139 assert xltunai.getSNFromCashInMessage('Terimakasih, transaksi CASH IN ke akun 6287886957792 berhasil. Jumlah transaksi Rp 5000000, biaya 0. Saldo anda saat ini 5000000 . Ref ID: 96346333194106') == '96346333194106'
134 140
1 import re 1 import re
2 import time 2 import time
3 3
4 nominalCodes = { 4 nominalCodes = {
5 5000: 1, 5 5000: 1,
6 10000: 2, 6 10000: 2,
7 25000: 3, 7 25000: 3,
8 50000: 4, 8 50000: 4,
9 100000: 5, 9 100000: 5,
10 200000: 6 10 200000: 6
11 } 11 }
12 12
13 nominalCosts = { 13 nominalCosts = {
14 5000: 5500, 14 5000: 5500,
15 10000: 10500, 15 10000: 10500,
16 25000: 25000, 16 25000: 25000,
17 50000: 50000, 17 50000: 50000,
18 100000: 100000, 18 100000: 100000,
19 200000: 200000 19 200000: 200000
20 } 20 }
21 21
22 valid_senders = ['120',] 22 valid_senders = ['120',]
23 message_codes = { 23 message_codes = {
24 'telah berhasil diisi pulsa sebesar': '00', 24 'telah berhasil diisi pulsa sebesar': '00',
25 'Maaf, transaksi Anda masih dalam proses': '40' 25 'Maaf, transaksi Anda masih dalam proses': '40'
26 } 26 }
27 27
28 ussd_response_codes = { 28 ussd_response_codes = {
29 'nomor yang Anda masukkan tidak valid': '14', 29 'nomor yang Anda masukkan tidak valid': '14',
30 'Maaf, transaksi Anda masih dalam proses': '40', 30 'Maaf, transaksi Anda masih dalam proses': '40',
31 'Mohon maaf, transaksi Anda melebihi limit': '40', 31 'Mohon maaf, transaksi Anda melebihi limit': '40',
32 'http url not found': '40', 32 'http url not found': '40',
33 'operation timeout': '40' 33 'operation timeout': '40'
34 } 34 }
35 35
36 def getOperatorFromProduct(product): 36 def getOperatorFromProduct(product):
37 return re.sub(r'\d+.*', '', product) 37 return re.sub(r'\d+.*', '', product)
38 38
39 def getNominalFromProduct(product): 39 def getNominalFromProduct(product):
40 return int("".join(re.findall(r'\d+', product))) * 1000 40 return int("".join(re.findall(r'\d+', product))) * 1000
41 41
42 def getNominalFromMessage(message): 42 def getNominalFromMessage(message):
43 nominal = "".join(re.findall(r'pulsa sebesar \d+\.', message)) 43 nominal = "".join(re.findall(r'pulsa sebesar \d+\.', message))
44 nominal = "".join(re.findall(r'\d+', nominal)) 44 nominal = "".join(re.findall(r'\d+', nominal))
45 return nominal 45 return nominal
46 46
47 def getDenomCodeFromProduct(product): 47 def getDenomCodeFromProduct(product):
48 nominal = getNominalFromProduct(product) 48 nominal = getNominalFromProduct(product)
49 try: 49 try:
50 result = nominalCodes[nominal] 50 result = nominalCodes[nominal]
51 except: 51 except:
52 result = None 52 result = None
53 53
54 return result 54 return result
55 55
56 def getCostFromProduct(product): 56 def getCostFromProduct(product):
57 nominal = getNominalFromProduct(product) 57 nominal = getNominalFromProduct(product)
58 try: 58 try:
59 result = nominalCosts[nominal] 59 result = nominalCosts[nominal]
60 except: 60 except:
61 result = 0 61 result = 0
62 62
63 return int(result) 63 return int(result)
64 64
65 def getDestinationFromMessage(message): 65 def getDestinationFromMessage(message):
66 result = "".join(re.findall(r'^Nomor \d+ telah berhasil', message)) 66 result = "".join(re.findall(r'^Nomor \d+ telah berhasil', message))
67 result = "".join(re.findall(r'\d+', result)) 67 result = "".join(re.findall(r'\d+', result))
68 return result 68 return result
69 69
70 def getSNFromMessage(message): 70 def getSNFromMessage(message):
71 try: 71 try:
72 sn = "".join(re.findall(r'Ref (?:ID :|No )(\d+)', message)) 72 sn = "".join(re.findall(r'Ref (?:ID :|No )(\d+)', message))
73 sn = "".join(re.findall(r'\d+', sn)) 73 sn = "".join(re.findall(r'\d+', sn))
74 except: 74 except:
75 sn = "" 75 sn = ""
76 76
77 return sn 77 return sn
78 78
79 def getBalanceFromMessage(message): 79 def getBalanceFromMessage(message):
80 try: 80 try:
81 sn = "".join(re.findall(r'Saldo Rp (\d+)', message)) 81 sn = "".join(re.findall(r'Saldo Rp (\d+)', message))
82 return int(sn) 82 return int(sn)
83 except: 83 except:
84 return 0 84 return 0
85
86 def getSNFromReceiveTransferMessage(message):
87 try:
88 sn = "".join(re.findall(r'RefID (\d+)', message))
89 return sn
90 except:
91 return
85 92
93 def getSNFromCashInMessage(message):
94 try:
95 sn = "".join(re.findall(r'Ref ID: (\d+)$', message))
96 return sn
97 except:
98 return
86 99
87 def toInternationalNumber(number): 100 def toInternationalNumber(number):
88 return re.sub(r'^0', '62', number) 101 return re.sub(r'^0', '62', number)
89 102
90 def getSIMCardInfoUSSDCommand(): 103 def getSIMCardInfoUSSDCommand():
91 return '*123*7*3*1*1#' 104 return '*123*7*3*1*1#'
92 105
93 def getHistoryUSSDCommand(): 106 def getHistoryUSSDCommand():
94 return '*123*120*8*3#' 107 return '*123*120*8*3#'
95 108
96 def getMSISDNFromSIMCardInfo(message): 109 def getMSISDNFromSIMCardInfo(message):
97 msisdn = "".join(re.findall(r'Nomor \d+ adalah nomor', message)) 110 msisdn = "".join(re.findall(r'Nomor \d+ adalah nomor', message))
98 msisdn = "".join(re.findall(r'\d+', msisdn)) 111 msisdn = "".join(re.findall(r'\d+', msisdn))
99 return msisdn 112 return msisdn
100 113
101 def getIMSIFromSIMCardInfo(message): 114 def getIMSIFromSIMCardInfo(message):
102 imsi = "".join(re.findall(r'dengan ICCID \d+', message)) 115 imsi = "".join(re.findall(r'dengan ICCID \d+', message))
103 imsi = "".join(re.findall(r'\d+', imsi)) 116 imsi = "".join(re.findall(r'\d+', imsi))
104 return imsi 117 return imsi
105 118
106 def getBalanceFromUSSDResponse(message): 119 def getBalanceFromUSSDResponse(message):
107 balance = "".join(re.findall(r'Saldo Rp\d+', message)) 120 balance = "".join(re.findall(r'Saldo Rp\d+', message))
108 balance = re.sub('Saldo Rp', '', balance) 121 balance = re.sub('Saldo Rp', '', balance)
109 try: 122 try:
110 return int(balance) 123 return int(balance)
111 except: 124 except:
112 return 0 125 return 0
113 126
114 def getTopupUSSDCommand(destination, product, pin_trx): 127 def getTopupUSSDCommand(destination, product, pin_trx):
115 denom_code = getDenomCodeFromProduct(product) 128 denom_code = getDenomCodeFromProduct(product)
116 if not denom_code: 129 if not denom_code:
117 return None 130 return None
118 131
119 ussdCommand = u'*123*120*2*2*{0}*{1}*{2}#'.format(destination, denom_code, pin_trx); 132 ussdCommand = u'*123*120*2*2*{0}*{1}*{2}#'.format(destination, denom_code, pin_trx);
120 return ussdCommand 133 return ussdCommand
121 134
122 def isValidSender(sender): 135 def isValidSender(sender):
123 return str(sender) in valid_senders 136 return str(sender) in valid_senders
124 137
125 def getResponseCodeByMessage(message): 138 def getResponseCodeByMessage(message):
126 for pattern in message_codes: 139 for pattern in message_codes:
127 if message.find(pattern) >= 0: 140 if message.find(pattern) >= 0:
128 return message_codes[pattern] 141 return message_codes[pattern]
129 142
130 return '68' 143 return '68'
131 144
132 def getResponseCodeByUSSDResponse(response_message): 145 def getResponseCodeByUSSDResponse(response_message):
133 for pattern in ussd_response_codes: 146 for pattern in ussd_response_codes:
134 if response_message.find(pattern) >= 0: 147 if response_message.find(pattern) >= 0:
135 return ussd_response_codes[pattern] 148 return ussd_response_codes[pattern]
136 149
137 return '68' 150 return '68'
138 151
139 def getPulsaFromUssdResponseMessage(message): 152 def getPulsaFromUssdResponseMessage(message):
140 pulsa = re.findall(r"Pulsa (\d+) s", message) 153 pulsa = re.findall(r"Pulsa (\d+) s", message)
141 if pulsa: 154 if pulsa:
142 return int(pulsa[0]) 155 return int(pulsa[0])
143 156
144 def getMasaAktifFromUssdResponseMessage(message): 157 def getMasaAktifFromUssdResponseMessage(message):
145 masa_aktif = re.findall(' s[\./]d (\w+)\.', message) 158 masa_aktif = re.findall(' s[\./]d (\w+)\.', message)
146 if not masa_aktif: 159 if not masa_aktif:
147 return 160 return
148 161
149 masa_aktif = time.strptime(masa_aktif[0], '%d%b%y') 162 masa_aktif = time.strptime(masa_aktif[0], '%d%b%y')
150 return time.strftime("%Y-%m-%d", masa_aktif) 163 return time.strftime("%Y-%m-%d", masa_aktif)
151 164
152 def getLastSNFromHistoryMessage(message): 165 def getLastSNFromHistoryMessage(message):
153 if re.findall(r"P2P TRANSFER", message): 166 if not re.findall(r"RELOAD", message):
154 return 'P2P TRANSFER' 167 return 'NON RELOAD'
155 168
156 sn = re.findall(r"Transaksi Terakhir\s:\s(\d+)", message) 169 sn = re.findall(r"Transaksi Terakhir\s:\s(\d+)", message)
157 if sn: 170 if sn:
158 return sn[0] 171 return sn[0]
159 172