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