def monitor(self): log.info("[CallMonitor] Started monitor") BrowserWrapper.b.open_menu("Status") waiting_from = None while True: if pbot.has_request(): skip_processing = False request = pbot.request log.info("[CallMonitor] Has personal bot request: %s" % request) # reboot and reset/restore are still possible while in the call # as we have a confirmation message before running each of them if self.call_or_dialing_started(): if isinstance(request, BalanceRequest)\ or isinstance(request, SendSmsRequest)\ or isinstance(request, SendUssdRequest): log.info( "[CallMonitor] Could not process the request while in a call. Waiting..." ) skip_processing = True if isinstance(request, RebootRequest) or isinstance( request, ResetRestoreRequest): waiting_from = None if not skip_processing: log.info("[CallMonitor] Processing personal bot request") pbot.process_request(self.goip) log.info("[CallMonitor] Processed personal bot request") BrowserWrapper.b.open_menu("Status") # send daily status every day once at 23:XX when no-one is using GoIP caller if current_time().hour == 23 and not self.call_or_dialing_started() and\ (not self.daily_status_sent_at or self.daily_status_sent_at.date() != current_date().date()): self.daily_status_sent_at = current_time( ) # using in-memory var to decrease amt of calls to DB vs.set_daily_status_sent(self.daily_status_sent_at) bot.send(daily_status()) # this should be checked every time, so no var defined above sleep_for_sec = 2 if self.call_or_dialing_started( ) else LAST_CALL_SLEEP_SECONDS # if not authorised for < 5 minutes - just wait for this issue to get fixed (with reset/restore?) if not BrowserWrapper.b.is_authorized( ) and not passed_more_that_sec(waiting_from, 5 * 60): log.warning( "[CallMonitor] Browser is not ok - session is not authorised" ) if waiting_from is None: waiting_from = current_time() sleep(sleep_for_sec) continue waiting_from = None # this is the way to get up-to-day info from the page BrowserWrapper.b.driver.refresh() if self.goip.goip_monitor(): # if all is fine with GoIP self.call_monitor() # run call monitor logic else: log.info("[CallMonitor] GoIP monitor is not ok") sleep(sleep_for_sec, print_log=False)
def goip_monitor(self): # we couldn't afford sleep(600) because we are working with browser in the single thread # instead - just skip method' body till the sleep time is elapsed if self.goip_slept_at and not passed_more_that_sec( self.goip_slept_at, GOIP_MONITOR_SLEEP_SECONDS): return True cdr_started = BrowserWrapper.b.by_id("l1_cdrt").text.strip() if cdr_started.startswith("1970-01"): log.error( "[GoipMonitor] Have internal GoIP issue (1970 year at clock).") if vs.last_date_cdr_restart() == current_time().date( ): # if we had the same problem today bot.send( "Переналаштовую дзвонилку бо вона ґеґнула (1970 рік надворі)!" ) self.reset_and_restore() else: vs.set_last_date_cdr_restart(current_time().date( )) # if this is the first problem occurrence bot.send( "Перезавантажую дзвонилку бо вона знову ні-гугу (1970 рік надворі)!" ) self.reboot() # open the 'Status' tab again to support the logic in rest of the cycle BrowserWrapper.b.open_menu("Status") # just waiting for the fix to be applied. Nothing could be done now return False # reason for the status check is doing fix only if GoIP problem persists for >1 cycle goip_is_working = self.statuses_ok() if not goip_is_working and vs.last_reg_status( ) == self.voip_connection_status: self.reset_and_restore() # open the 'Status' tab again to support the logic in rest of the cycle BrowserWrapper.b.open_menu("Status") self.voip_connection_status = vs.last_reg_status() self.goip_slept_at = current_time( ) # we have this in-memory var to decrease amt of calls to the DB vs.set_monitor_slept_at( self.goip_slept_at ) # we are using this value as heartbeat for the whole GoIP monitor log.info("[GoipMonitor] Sleeping for %d sec..." % GOIP_MONITOR_SLEEP_SECONDS) if not goip_is_working: # just waiting for the fix to be applied. Nothing could be done now log.error( "[GoipMonitor] Patient is not ok. Will check again soon.") return False return True
def monthly_status(): has_status, minutes_left, valid_till = parse_ussd(USSD_MONTHLY_STATUS, MONTHLY_STATUS_REGEX, [False, None, None]) valid_days = 0 if has_status: log.info( "[Monthly status] Found information: minutes left '%s', valid till '%s'" % (minutes_left, valid_till)) valid_till_date = datetime.strptime(valid_till, "%d.%m.%y") valid_days = (valid_till_date - current_time()).days return has_status, minutes_left, valid_days
def start_call(self): # this could happen when previous call ended and new started in-between check cycles if self.call_started and self.call_number != self.last_called_number: self.finish_call() # if we missed dialing statuses because of slow cycles if not self.dialing_started: self.start_dialing() # start actual call if not self.call_started: log.info("[Process call] Started call to %s" % self.call_number) self.last_msg = self.bot_message("Говоримо з %s" % self.call_number) self.call_started = current_time() self.last_called_number = self.call_number
def start_dialing(self): self.dialing_started = current_time() try: log_msg, self.msg_call_status = self.STATUS_LOG_MSG.get( self.status) except TypeError as e: # if call already started log.warning( "[Start call] Exception getting message from call status: %s" % e) log_msg, self.msg_call_status = self.STATUS_LOG_MSG.get( self.DIALING) log.info("[Start call] %s" % log_msg) if self.status_changed: self.last_msg = self.bot_message(self.msg_call_status)
def statuses_ok(self): log.info("[Statuses ok] Checking") b = BrowserWrapper.b sim = b.by_id("l1_gsm_sim").text.strip() gsm = b.by_id("l1_gsm_status").text.strip() voip = b.by_id("l1_status_line").text.strip() vs.set_last_reg_status(None) # reset value if voip == "401": log.error( "[Statuses ok] Incorrect SIP username/password specified.") # try to fix fix-able issue only once per day as more attempts are often useless if vs.last_date_error_notified() and vs.last_date_error_notified( ).date() == current_time().date(): return True # do not fix vs.set_last_date_error_notified(current_time()) vs.set_last_reg_status( "Помилка реєстрації VoIP. Невірний логін/пароль (код %s)" % voip) return False # try to fix if voip == "403": log.error("[Statuses ok] Error 403. No easy remedy for this") vs.set_last_reg_status("Невідома помилка (код %s)" % voip) if voip != "Y": log.error( "[Statuses ok] VoIP registration failed (status = '%s')." % voip) vs.set_last_reg_status("Помилка реєстрації VoIP (код %s)" % voip) return False # try to fix if sim != "Y": log.error("[Statuses ok] SIM not found. No easy remedy for this") vs.set_last_reg_status("SIM не знайдено") if gsm != "Y": log.error("[Statuses ok] No GSM network. No easy remedy for this") vs.set_last_reg_status("Помилка реєстрації GSM") vs.set_last_date_error_notified(None) return True # do not try to fix
def finish_call(self): number = self.any_call_number() log.info("[Finish call] Processing call end for '%s'" % number) started_when = self.call_or_dialing_started() log.info("[Finish call] Call started at %s" % started_when) seconds = (current_time() - started_when).seconds log.info("[Finish call] Overall call duration is %s seconds" % seconds) duration_str = seconds_to_time_str(seconds, no_seconds=(seconds > 3600)) log.info("[Finish call] Call to %s ended (%s)" % (number, duration_str)) if self.call_started: text = "Дзвоник до %s - %s" % (number, duration_str) vs.increase_daily_call_duration(seconds) vs.increase_daily_ok_calls_amount(1) else: if self.msg_call_status: text = self.msg_call_status + random_list_item( self.ERROR_PHRASES) else: text = "Ймовірно невдалий дзвоник до {number}" vs.increase_daily_failed_calls_amount(1) self.bot_message(text) self.dialing_started = self.call_started = self.last_called_number = self.msg_call_status = self.last_msg = None
def daily_status(scheduled_run=True): has_balance_info, money, tariff, number_valid_till = balance() has_monthly_info, monthly_minutes_left, monthly_valid_days = monthly_status( ) has_yearly_info, yearly_valid_till = yearly_status() string = "" ok_calls_amt = vs.daily_ok_calls_amount() failed_calls_amt = vs.daily_failed_calls_amount() all_calls_amt = ok_calls_amt + failed_calls_amt calls_duration = vs.daily_calls_duration() fixed_times = vs.daily_fixed_times() today_is_sunday = current_date().strftime("%w") == "0" # daily calls status if all_calls_amt > 0: calls_stats = " (%d%%)" % (100 * ok_calls_amt / all_calls_amt) else: calls_stats = "" if calls_duration: duration_str = seconds_to_time_str( calls_duration, no_seconds=True) # omit the seconds part duration_str = "%s%s" % (duration_str, calls_stats) if scheduled_run: vs.increase_weekly_call_duration(calls_duration) else: duration_str = "не було" string += "Розмов %s\n" % duration_str # weekly calls status weekly_calls_duration = vs.weekly_calls_duration() if not scheduled_run: # as we do not increment weekly calls duration if it's a not scheduled_run weekly_calls_duration += calls_duration if today_is_sunday or not scheduled_run: # if it's Sunday or on-demand info request duration_str = seconds_to_time_str( weekly_calls_duration, no_seconds=True) # omit the seconds part string += "За тиждень %s\n" % duration_str if has_balance_info: string += "На рахунку %s грн" % money has_money_diff, money_diff = daily_balance_diff(money=money) if has_money_diff: string += " (%s грн)" % money_diff string += "\n" if has_monthly_info: days_add = "на %s дні(в)" % monthly_valid_days if monthly_valid_days > 0 else "до сьогодні" string += "%s хв %s\n" % (monthly_minutes_left, days_add) if fixed_times: string += "<b>Полагоджено %s раз(и)</b>\n" % fixed_times if tariff: string += "Тариф %s\n" % tariff if number_valid_till or yearly_valid_till: if yearly_valid_till and yearly_valid_till < number_valid_till: valid_till = yearly_valid_till else: valid_till = number_valid_till or yearly_valid_till # as 1 might be None days_left = (valid_till - current_time()).days if days_left < 10: string += "<b>Поповни! Лишилось %s дні(в)</b>\n" % days_left string += "Рік/номер до %s\n" % valid_till.strftime(DATE_FORMAT) if scheduled_run: reset_daily_values(money=money) if today_is_sunday: vs.set_weekly_calls_duration(0) return string