def __init__(self, phone_number: str, session_pickle_filename: str, load_session: bool = False, debug: bool = False) -> None: logger.log("info", "Instantiating Notifier...") self.phone_number = phone_number self.session_pickle_filename = session_pickle_filename self.debug = debug if load_session: self.session = self.load_session() else: self.session = requests.Session() self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/80.0.3987.163 Safari/537.36', 'X-Channel': 'BB-WEB', 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Caller': 'DVAR-SVC', 'Sec-Fetch-Dest': 'empty', 'Referer': self.BASE_URL }
def visit_cart_page_and_get_address_id(self) -> str: logger.log("info", "Visiting cart page...") resp = self.session.get(self.BASE_URL + self.CART_ENDPOINT, headers=self.headers) addr_id = self._find_address_id(resp.text) logger.log("info", "Address Id found") return addr_id
def visit_main_page(self) -> Response: logger.log("info", "Visiting main website...") resp = self.session.get(self.BASE_URL, headers=self.headers) if not resp.ok: logger.log("error", "Main page not reachable...") return resp self.headers['X-CSRFToken'] = resp.cookies.get('csrftoken') return resp
def wrapper(*args, **kwargs): try: return job_func(*args, **kwargs) except: import traceback logger.log("critical", traceback.format_exc()) if cancel_on_failure: logger.log("warning", "Job Cancelled due to an error.") return schedule.CancelJob
def visit_extra_delivery_slot_check(self) -> bool: logger.log("info", "Visiting extra delivery slot check...") resp = self.session.get(self.BASE_URL + self.EXTRA_CHECK_ENDPOINT, headers=self.headers) if not resp.ok: return False data = resp.json() if 'details' in data and 'checkout_slot_failure_message' in data['details'] and "unfortunately" in data['details']['checkout_slot_failure_message'].lower(): return False return True
def send_otp(self) -> Response: logger.log("info", "Sending OTP to {}...".format(self.phone_number)) resp = self.session.post(self.BASE_URL + self.OTP_ENDPOINT, headers=self.headers, data=json.dumps( {"identifier": self.phone_number})) if not resp.ok: logger.log("error", "Failed to send OTP.") return resp
def login(self, otp: str) -> Response: logger.log("info", "Logging with OTP: {}...".format(otp)) resp = self.session.post(self.BASE_URL + self.LOGIN_ENDPOINT, headers=self.headers, data=json.dumps( { "mobile_no": self.phone_number, "mobile_no_otp": otp } )) if not resp.ok: logger.log("error", "Login failed.") return resp
def wrapper(*args, **kwargs): try: return job_func(*args, **kwargs) except IndexError: logger.log( "critical", "Need to run login.py script since session is either outdated or never created." ) except ConnectionError: logger.log("critical", "BigBasket didn't respond well.") except: logger.log("critical", traceback.format_exc()) logger.log( "critical", "Please report the developer at " "https://github.com/wrap-away/bigbasket-notifier/issues " "to inform him about this error.") if cancel_on_failure: logger.log("warning", "Job Cancelled due to an error.") return schedule.CancelJob
def job(notifier: Notifier, delay: int, system_notifier, telegram_notifier): """ Job to check if a delivery slot gets available for the default selected address in your bigbasket website. @param notifier: Notifier - Notifier class - To monitor bigbasket website. @param system_notifier: None/notification class - To notify users (cross-platform) via balloon tiles. @param delay: int - Just a preventive measure to not make too many requests at the same time. @param telegram_notifier: None/Bot class - Telegram integration to notify via bot. """ notifier.visit_main_page() time.sleep(delay) addr_id = notifier.visit_cart_page_and_get_address_id() time.sleep(delay) initial_status, resp = notifier.check_if_delivery_slot_available(addr_id) if not initial_status: return None logger.log("warning", "Maybe a delivery slot is found.") time.sleep(delay) status = notifier.visit_extra_delivery_slot_check() if not status: logger.log("warning", "No delivery slot was found.") return None success_message = "A free delivery slot is found for your address!" logger.log("critical", success_message) if system_notifier: system_notifier.notify(title='BigBasket Notifier', message=success_message, app_name='bigbasket-notifier') if telegram_notifier: telegram_notifier.notify( config.get_configuration('chat_id', "TELEGRAM"), success_message) return None
def check_if_delivery_slot_available(self, addr_id: str) -> (bool, Response): logger.log("info", "Checking delivery slot for addr_id: {}".format(addr_id)) payload = 'addr_id=' + addr_id separate_headers = { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Sec-Fetch-Dest': 'empty', 'X-CSRFToken': self.session.cookies.get('csrftoken'), 'X-Requested-With': 'XMLHttpRequest', 'Referer': self.BASE_URL, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/80.0.3987.163 Safari/537.36', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } resp = self.session.post(self.BASE_URL + self.AVAILABILITY_ENDPOINT, data=payload, headers=self.headers) if not resp: logger.log("error", "Could not check for delivery slot.") return False, resp data = resp.json() if data['status'] == "failure": logger.log("info", "No delivery slot is found.") return False, resp else: return True, resp
def load_session(self) -> Session: logger.log("info", "Loading previous session...") with open(self.session_pickle_filename, 'rb') as session_pickle: return pickle.load(session_pickle)
def save_session(self) -> bool: logger.log("info", "Saving current session...") with open(self.session_pickle_filename, 'wb') as session_pickle: pickle.dump(self.session, session_pickle) return True
def log_error(ws_err: WorkspaceResponseError, level: int = logging.ERROR): """Log a workspace response error""" err_str = ws_err.resp_data['error']['message'] logger.log(level, f'Workspace response error: {err_str}')