Files
kwork/app/config.py
2026-07-17 12:21:53 +07:00

83 lines
2.7 KiB
Python

from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
from dotenv import load_dotenv
@dataclass(frozen=True)
class Settings:
telegram_bot_token: str
telegram_channel_id: str
telegram_proxy_url: str | None
scheduler_interval_minutes: int
kwork_category_ids: list[int]
fl_rss_url: str | None
keywords_include: list[str]
keywords_exclude: list[str]
min_price_amount: int | None
requests_timeout_seconds: int
state_file: Path
published_offers_file: Path
openai_api_base_url: str
openai_api_key: str
openai_model: str
openai_system_prompt_file: Path
def load_settings() -> Settings:
load_dotenv()
telegram_bot_token = _require_env("TELEGRAM_BOT_TOKEN")
telegram_channel_id = _require_env("TELEGRAM_CHANNEL_ID")
return Settings(
telegram_bot_token=telegram_bot_token,
telegram_channel_id=telegram_channel_id,
telegram_proxy_url=_parse_optional_text(os.getenv("TELEGRAM_PROXY_URL", "")),
scheduler_interval_minutes=int(os.getenv("SCHEDULER_INTERVAL_MINUTES", "5")),
kwork_category_ids=_parse_int_list(os.getenv("KWORK_CATEGORY_IDS", "41")),
fl_rss_url=_parse_optional_text(os.getenv("FL_RSS_URL", "https://www.fl.ru/rss/?category=5")),
keywords_include=_parse_text_list(os.getenv("KEYWORDS_INCLUDE", "")),
keywords_exclude=_parse_text_list(os.getenv("KEYWORDS_EXCLUDE", "")),
min_price_amount=_parse_optional_int(os.getenv("MIN_PRICE_AMOUNT", "")),
requests_timeout_seconds=int(os.getenv("REQUESTS_TIMEOUT_SECONDS", "20")),
state_file=Path(os.getenv("STATE_FILE", "data/sent_offers.json")),
published_offers_file=Path(os.getenv("PUBLISHED_OFFERS_FILE", "data/published_offers.json")),
openai_api_base_url=_require_env("OPENAI_API_BASE_URL"),
openai_api_key=_require_env("OPENAI_API_KEY"),
openai_model=_require_env("OPENAI_MODEL"),
openai_system_prompt_file=Path(_require_env("OPENAI_SYSTEM_PROMPT_FILE")),
)
def _require_env(name: str) -> str:
value = os.getenv(name)
if not value:
raise ValueError(f"Environment variable {name} is required")
return value
def _parse_text_list(raw_value: str) -> list[str]:
return [item.strip().lower() for item in raw_value.split(",") if item.strip()]
def _parse_int_list(raw_value: str) -> list[int]:
return [int(item.strip()) for item in raw_value.split(",") if item.strip()]
def _parse_optional_int(raw_value: str) -> int | None:
value = raw_value.strip()
if not value:
return None
return int(value)
def _parse_optional_text(raw_value: str) -> str | None:
value = raw_value.strip()
if not value:
return None
return value