Example #1
0
class Scanner():
    def __init__(self, notifiers: bool = True):
        self.config = Config(config_file) if path.isfile(
            config_file) else Config()
        if self.config.debug:
            # pylint: disable=E1103
            loggers = [
                logging.getLogger(name)
                for name in logging.root.manager.loggerDict
            ]
            # pylint: enable=E1103
            for logger in loggers:
                logger.setLevel(logging.DEBUG)
            log.info("Debugging mode enabled")
        self.metrics = Metrics()
        self.item_ids = self.config.item_ids
        self.amounts = {}
        try:
            self.tgtg_client = TgtgClient(
                email=self.config.tgtg["username"],
                timeout=self.config.tgtg["timeout"],
                access_token_lifetime=self.config.
                tgtg["access_token_lifetime"],
                max_polling_tries=self.config.tgtg["max_polling_tries"],
                polling_wait_time=self.config.tgtg["polling_wait_time"],
                access_token=self.config.tgtg["access_token"],
                refresh_token=self.config.tgtg["refresh_token"],
                user_id=self.config.tgtg["user_id"])
            self.tgtg_client.login()
            self.config.save_tokens(self.tgtg_client.access_token,
                                    self.tgtg_client.refresh_token,
                                    self.tgtg_client.user_id)
        except TgtgAPIError as err:
            raise err
        except Error as err:
            log.error(err)
            raise TGTGConfigurationError() from err
        if notifiers:
            if self.config.metrics:
                self.metrics.enable_metrics()
            self.notifiers = Notifiers(self.config)
            if not self.config.disable_tests:
                log.info("Sending test Notifications ...")
                self.notifiers.send(self._test_item)

    @property
    def _test_item(self) -> Item:
        """
        Returns an item for test notifications
        """
        items = sorted(self._get_favorites(),
                       key=lambda x: x.items_available,
                       reverse=True)
        if items:
            return items[0]
        items = sorted([
            Item(item)
            for item in self.tgtg_client.get_items(favorites_only=False,
                                                   latitude=53.5511,
                                                   longitude=9.9937,
                                                   radius=50)
        ],
                       key=lambda x: x.items_available,
                       reverse=True)
        return items[0]

    def _job(self) -> None:
        """
        Job iterates over all monitored items
        """
        for item_id in self.item_ids:
            try:
                if item_id != "":
                    item = Item(self.tgtg_client.get_item(item_id))
                    self._check_item(item)
            except Exception:
                log.error("itemID %s Error! - %s", item_id, sys.exc_info())
        for item in self._get_favorites():
            try:
                self._check_item(item)
            except Exception:
                log.error("check item error! - %s", sys.exc_info())
        log.debug("new State: %s", self.amounts)
        if len(self.amounts) == 0:
            log.warning("No items in observation! Did you add any favorites?")
        self.config.save_tokens(self.tgtg_client.access_token,
                                self.tgtg_client.refresh_token,
                                self.tgtg_client.user_id)

    def _get_favorites(self) -> list[Item]:
        """
        Get favorites as list of Items
        """
        items = []
        page = 1
        page_size = 100
        error_count = 0
        while error_count < 5:
            try:
                new_items = self.tgtg_client.get_items(favorites_only=True,
                                                       page_size=page_size,
                                                       page=page)
                items += new_items
                if len(new_items) < page_size:
                    break
                page += 1
            except Exception:
                log.error("get item error! - %s", sys.exc_info())
                error_count += 1
                self.metrics.get_favorites_errors.inc()
        return [Item(item) for item in items]

    def _check_item(self, item: Item) -> None:
        """
        Checks if the available item amount raised from zero to something and triggers notifications.
        """
        try:
            if self.amounts[
                    item.item_id] == 0 and item.items_available > self.amounts[
                        item.item_id]:
                self._send_messages(item)
                self.metrics.send_notifications.labels(
                    item.item_id, item.display_name).inc()
            self.metrics.item_count.labels(
                item.item_id, item.display_name).set(item.items_available)
        except Exception:
            self.amounts[item.item_id] = item.items_available
        finally:
            if self.amounts[item.item_id] != item.items_available:
                log.info("%s - new amount: %s", item.display_name,
                         item.items_available)
                self.amounts[item.item_id] = item.items_available

    def _send_messages(self, item: Item) -> None:
        """
        Send notifications for Item
        """
        log.info("Sending notifications for %s - %s bags available",
                 item.display_name, item.items_available)
        self.notifiers.send(item)

    def run(self) -> NoReturn:
        """
        Main Loop of the Scanner
        """
        log.info("Scanner started ...")
        while True:
            try:
                self._job()
                if self.tgtg_client.captcha_error_count > 10:
                    log.warning("Too many 403 Errors. Sleeping for 1 hour.")
                    sleep(60 * 60)
                    log.info("Continuing scanning.")
                    self.tgtg_client.captcha_error_count = 0
            except Exception:
                log.error("Job Error! - %s", sys.exc_info())
            finally:
                sleep(self.config.sleep_time * (0.9 + 0.2 * random()))

    def __del__(self) -> None:
        """
        Cleanup on shutdown
        """
        try:
            if hasattr(self, 'notifiers') and self.notifiers.telegram.updater:
                self.notifiers.telegram.updater.stop()
        except Exception as exc:
            log.warning(exc)
Example #2
0
class Scanner():
    def __init__(self, notifiers=True):
        self.config = Config(config_file) if path.isfile(
            config_file) else Config()
        if self.config.debug:
            # pylint: disable=E1103
            loggers = [
                logging.getLogger(name)
                for name in logging.root.manager.loggerDict
            ]
            # pylint: enable=E1103
            for logger in loggers:
                logger.setLevel(logging.DEBUG)
            log.info("Debugging mode enabled")
        self.metrics = Metrics()
        if self.config.metrics:
            self.metrics.enable_metrics()
        self.item_ids = self.config.item_ids
        self.amounts = {}
        try:
            self.tgtg_client = TgtgClient(
                email=self.config.tgtg["username"],
                timeout=self.config.tgtg["timeout"],
                access_token_lifetime=self.config.
                tgtg["access_token_lifetime"],
                max_polling_tries=self.config.tgtg["max_polling_tries"],
                polling_wait_time=self.config.tgtg["polling_wait_time"],
                access_token=self.config.tgtg["access_token"],
                refresh_token=self.config.tgtg["refresh_token"],
                user_id=self.config.tgtg["user_id"])
            self.tgtg_client.login()
        except TgtgAPIError as err:
            raise
        except Error as err:
            log.error(err)
            raise TGTGConfigurationError() from err
        if notifiers:
            self.notifiers = Notifiers(self.config)

    def _job(self):
        for item_id in self.item_ids:
            try:
                if item_id != "":
                    data = self.tgtg_client.get_item(item_id)
                    self._check_item(Item(data))
            except Exception:
                log.error("itemID %s Error! - %s", item_id, sys.exc_info())
        for data in self._get_favorites():
            try:
                self._check_item(Item(data))
            except Exception:
                log.error("check item error! - %s", sys.exc_info())
        log.debug("new State: %s", self.amounts)
        self.config.save_tokens(self.tgtg_client.access_token,
                                self.tgtg_client.refresh_token,
                                self.tgtg_client.user_id)

    def _get_favorites(self):
        items = []
        page = 1
        page_size = 100
        error_count = 0
        while True and error_count < 5:
            try:
                new_items = self.tgtg_client.get_items(favorites_only=True,
                                                       page_size=page_size,
                                                       page=page)
                items += new_items
                if len(new_items) < page_size:
                    break
                page += 1
            except Exception:
                log.error("get item error! - %s", sys.exc_info())
                error_count += 1
                self.metrics.get_favorites_errors.inc()
        return items

    def _check_item(self, item: Item):
        try:
            if self.amounts[
                    item.item_id] == 0 and item.items_available > self.amounts[
                        item.item_id]:
                self._send_messages(item)
                self.metrics.send_notifications.labels(
                    item.item_id, item.display_name).inc()
            self.metrics.item_count.labels(
                item.item_id, item.display_name).set(item.items_available)
        except Exception:
            self.amounts[item.item_id] = item.items_available
        finally:
            if self.amounts[item.item_id] != item.items_available:
                log.info("%s - new amount: %s", item.display_name,
                         item.items_available)
                self.amounts[item.item_id] = item.items_available

    def _send_messages(self, item: Item):
        log.info("Sending notifications for %s - %s bags available",
                 item.display_name, item.items_available)
        self.notifiers.send(item)

    def run(self):
        log.info("Scanner started ...")
        while True:
            try:
                self._job()
            except Exception:
                log.error("Job Error! - %s", sys.exc_info())
            finally:
                sleep(self.config.sleep_time * (0.9 + 0.2 * random()))

    def __del__(self):
        try:
            if self.notifiers.telegram.updater:
                self.notifiers.telegram.updater.stop()
        except:
            pass