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