diff --git a/app/core/bot.py b/app/core/bot.py index 9a0d0b9..0af69a5 100644 --- a/app/core/bot.py +++ b/app/core/bot.py @@ -4,7 +4,7 @@ from aiogram import Bot, types from aiogram.contrib.middlewares.logging import LoggingMiddleware from aiogram.dispatcher import Dispatcher from aiogram.utils.callback_data import CallbackData -from app.core.parse_web import get_driver, get_ttl_hash, parse_site +from app.core.parse_web import get_driver, parse_site from app.settings import TELEGRAM_API_TOKEN bot = Bot(token=TELEGRAM_API_TOKEN) @@ -36,7 +36,7 @@ def get_keyboard() -> types.InlineKeyboardMarkup: async def home_office( query: types.CallbackQuery, callback_data: dict[str, str] ) -> types.Message: - driver = get_driver(ttl_hash=get_ttl_hash(seconds=10)) + driver = get_driver() text = parse_site( driver=driver, url='https://yandex.ru/maps/213/moscow/stops/stop__9640740/' @@ -53,7 +53,7 @@ async def home_office( async def office_home( query: types.CallbackQuery, callback_data: dict[str, str] ) -> types.Message: - driver = get_driver(ttl_hash=get_ttl_hash()) + driver = get_driver() text = parse_site( driver=driver, url='https://yandex.ru/maps/213/moscow/stops/stop__9640288/?' @@ -79,7 +79,7 @@ async def echo(message: types.Message) -> types.Message: async def morning_bus_mailing(chat_ids: list[int]) -> None: - driver = get_driver(ttl_hash=get_ttl_hash()) + driver = get_driver() text = parse_site( driver=driver, url='https://yandex.ru/maps/213/moscow/stops/stop__9640740/' diff --git a/app/core/logger.py b/app/core/logger.py deleted file mode 100644 index 0e741d2..0000000 --- a/app/core/logger.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys - -from loguru import logger - -logger.remove() -logger.add( - sink=sys.stdout, - colorize=True, - level='DEBUG', - format="{time:DD.MM.YYYY HH:mm:ss} | {level} | {message}", -) diff --git a/app/core/parse_web.py b/app/core/parse_web.py index c65c337..4969582 100644 --- a/app/core/parse_web.py +++ b/app/core/parse_web.py @@ -1,12 +1,11 @@ import os import tarfile import time -from functools import lru_cache from pathlib import Path import wget -from app.core.logger import logger -from app.settings import BASE_DIR, GECKO_DRIVER_VERSION +from app.core.utils import logger, timed_cache +from app.settings import BASE_DIR, DRIVER_SESSION_TTL, GECKO_DRIVER_VERSION from selenium import webdriver from selenium.common.exceptions import ( NoSuchElementException, @@ -93,14 +92,8 @@ def parse_site(url: str, message: str, driver: RemoteWebDriver | None = None) -> return answer -def get_ttl_hash(seconds: int = 28) -> int: - """Return the same value withing `seconds` time period""" - return round(time.time() / seconds) - - -@lru_cache -def get_driver(ttl_hash: int | None = None) -> RemoteWebDriver: - del ttl_hash +@timed_cache(seconds=DRIVER_SESSION_TTL) +def get_driver() -> RemoteWebDriver: opt = options.Options() opt.headless = True driver = RemoteWebDriver( diff --git a/app/core/utils.py b/app/core/utils.py new file mode 100644 index 0000000..6f23475 --- /dev/null +++ b/app/core/utils.py @@ -0,0 +1,35 @@ +import sys +from datetime import datetime, timedelta +from functools import lru_cache, wraps +from typing import Any + +from loguru import logger + +logger.remove() +logger.add( + sink=sys.stdout, + colorize=True, + level='DEBUG', + format="{time:DD.MM.YYYY HH:mm:ss} | {level} | {message}", +) + + +def timed_cache(**timedelta_kwargs: Any) -> Any: + def _wrapper(func: Any) -> Any: + update_delta = timedelta(**timedelta_kwargs) + next_update = datetime.utcnow() + update_delta + # Apply @lru_cache to f with no cache size limit + cached_func = lru_cache(None)(func) + + @wraps(func) + def _wrapped(*args: Any, **kwargs: Any) -> Any: + nonlocal next_update + now = datetime.utcnow() + if now >= next_update: + cached_func.cache_clear() + next_update = now + update_delta + return cached_func(*args, **kwargs) + + return _wrapped + + return _wrapper diff --git a/app/main.py b/app/main.py index 3d29cd0..5b3568f 100644 --- a/app/main.py +++ b/app/main.py @@ -11,8 +11,8 @@ from aiohttp import web sys.path.append(str(Path(__file__).parent.parent)) from app.core.bot import bot, dispatcher -from app.core.logger import logger from app.core.scheduler import asyncio_schedule +from app.core.utils import logger from app.settings import ( START_WITH_WEBHOOK, TELEGRAM_API_TOKEN, @@ -88,7 +88,7 @@ async def get_updates_from_queue() -> None: async def create_app() -> web.Application: application = web.Application() - application.router.add_get('/', health_check) + application.router.add_get(f'{WEBHOOK_PATH}/', health_check) application.router.add_post( f'{WEBHOOK_PATH}/{TELEGRAM_API_TOKEN}', put_updates_on_queue ) diff --git a/app/settings.py b/app/settings.py index a7d1314..ac17604 100644 --- a/app/settings.py +++ b/app/settings.py @@ -29,3 +29,5 @@ WEBAPP_HOST = config('WEBAPP_HOST', default='127.0.0.1') # or ip WEBAPP_PORT = config('WEBAPP_PORT', cast=int, default=8084) START_WITH_WEBHOOK = config('START_WITH_WEBHOOK', cast=bool, default=False) + +DRIVER_SESSION_TTL = 28 # driver cache ttl session in seconds diff --git a/deploy/Caddyfile b/deploy/Caddyfile index d275063..faaa223 100644 --- a/deploy/Caddyfile +++ b/deploy/Caddyfile @@ -4,7 +4,7 @@ # Removing some headers for improved security: header -Server - route /transport/{$TELEGRAM_API_TOKEN} { + route /transport/* { reverse_proxy transport_bot:8084 } } diff --git a/docker-compose.yml b/docker-compose.yml index 1d009ee..0422567 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,13 +24,16 @@ services: args: USER: web restart: unless-stopped + environment: + - SESSION_TIMED_OUT=30s networks: transport_bot_network: ipv4_address: 200.20.0.10 volumes: - ./deploy/browsers.json:/etc/selenoid/browsers.json:ro - /var/run/docker.sock:/var/run/docker.sock - command: ["-conf", "/etc/selenoid/browsers.json", "-limit", "10", "-container-network", "transport_bot_network"] + command: ["-conf", "/etc/selenoid/browsers.json", "-limit", "10", + "-container-network", "transport_bot_network", "-timeout", "30s"] expose: - "4444"