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