Commit defd7a97651a723c4999cf39a34eeb0bb8a51e95

Authored by Adhidarma Hadiwinoto
1 parent b4dfd3b151
Exists in master

perbaikan penanganan jika last trx non reload

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