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