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