from __future__ import annotations import logging from app.filters import get_offer_skip_reason from app.state import PublishedOffersStore, SentOffersStore from app.telegram import TelegramPublisher from app.sources.base import OfferSource logger = logging.getLogger(__name__) class OfferProcessingService: def __init__( self, sources: list[OfferSource], publisher: TelegramPublisher, sent_offers_store: SentOffersStore, published_offers_store: PublishedOffersStore, include_keywords: list[str], exclude_keywords: list[str], min_price_amount: int | None, ) -> None: self._sources = sources self._publisher = publisher self._sent_offers_store = sent_offers_store self._published_offers_store = published_offers_store self._include_keywords = include_keywords self._exclude_keywords = exclude_keywords self._min_price_amount = min_price_amount async def run_once(self) -> None: for source in self._sources: try: logger.info("Fetching offers from source=%s", source.source_name) offers = source.fetch_offers() logger.info("Fetched %s offers from source=%s", len(offers), source.source_name) except Exception: logger.exception("Failed to fetch offers from source %s", source.source_name) continue skipped_duplicates = 0 skipped_exclude_keyword = 0 skipped_include_keyword = 0 skipped_min_price_amount = 0 published_count = 0 for offer in offers: if self._sent_offers_store.has(offer.dedup_key): skipped_duplicates += 1 continue skip_reason = get_offer_skip_reason( offer, self._include_keywords, self._exclude_keywords, self._min_price_amount, ) if skip_reason == "exclude_keyword": skipped_exclude_keyword += 1 continue if skip_reason == "include_keyword": skipped_include_keyword += 1 continue if skip_reason == "min_price_amount": skipped_min_price_amount += 1 continue try: message = await self._publisher.publish_offer(offer) except Exception: logger.exception("Failed to publish offer %s", offer.dedup_key) continue self._sent_offers_store.add(offer.dedup_key) self._published_offers_store.add(message.chat.id, message.message_id, offer) published_count += 1 logger.info("Published offer %s", offer.dedup_key) logger.info( ( "Source %s summary: fetched=%s published=%s skipped_duplicates=%s " "skipped_exclude_keyword=%s skipped_include_keyword=%s skipped_min_price_amount=%s" ), source.source_name, len(offers), published_count, skipped_duplicates, skipped_exclude_keyword, skipped_include_keyword, skipped_min_price_amount, )