diff --git a/config.py b/config.py index dad297b..5e191aa 100644 --- a/config.py +++ b/config.py @@ -21,6 +21,15 @@ PENIS_TOP_LIMIT = 10 # --- Казино --- CASINO_MAX_BET = 1.0 +# --- Magnit API --- +MAGNIT_API_BASE_URL = os.getenv("MAGNIT_API_BASE_URL", "https://mirror.porno4free.ru/magnit") +MAGNIT_STORE_CODE = os.getenv("MAGNIT_STORE_CODE", "618224") +MAGNIT_REQUEST_TIMEOUT = int(os.getenv("MAGNIT_REQUEST_TIMEOUT", "15")) +# Можно указывать просто строку с названием или словарь с query/label/store_code. +MAGNIT_PRODUCTS = [ + "RedBull", +] + # --- Конфигурация макетов --- TEMPLATES = { "1": { @@ -103,6 +112,6 @@ def get_hint_text(templates_str: str) -> str: "• /penis_casino [ставка] — казино на размер.\n" "• /gadanie [тема] — гадание на фене с мемом.\n" "• /ebalnik — включить/выключить автоответы в чате.\n" - "• /prices /fetch — цены.\n" + "• /prices /fetch — цены и остатки Магнита.\n" "Подсказка: в группах пиши /команда@username_бота." ) diff --git a/main.py b/main.py index 6c8936a..80f08a2 100644 --- a/main.py +++ b/main.py @@ -388,6 +388,62 @@ def minutes_until_next_lesson(now: datetime | None = None) -> str: minutes = (total_seconds % 3600) // 60 return f"Твой мозг окончательно сгниет, а очко порвется через: {days} дн {hours} ч {minutes} мин" +def _prepare_magnit_query(value: str) -> str: + value = re.sub(r"(?<=[a-zа-яё])(?=[A-ZА-ЯЁ])", " ", value) + value = re.sub(r"[_-]+", " ", value) + return re.sub(r"\s+", " ", value).strip() + +def _normalize_magnit_product(entry) -> dict[str, str]: + if isinstance(entry, str): + raw_query = entry.strip() + if not raw_query: + raise ValueError("Пустой query в MAGNIT_PRODUCTS.") + return { + "query": _prepare_magnit_query(raw_query), + "label": raw_query, + "store_code": config.MAGNIT_STORE_CODE, + } + + if isinstance(entry, dict): + raw_query = str(entry.get("query", "")).strip() + if not raw_query: + raise ValueError("У товара в MAGNIT_PRODUCTS отсутствует query.") + return { + "query": _prepare_magnit_query(raw_query), + "label": str(entry.get("label") or raw_query).strip() or raw_query, + "store_code": str(entry.get("store_code") or config.MAGNIT_STORE_CODE).strip(), + } + + raise ValueError("MAGNIT_PRODUCTS должен содержать строки или словари.") + +def _pick_magnit_item(data: dict): + best_match = data.get("best_match") + if isinstance(best_match, dict): + return best_match + + items = data.get("items") + if isinstance(items, list) and items: + return items[0] + return None + +def _format_money(value) -> str | None: + if value is None: + return None + amount = f"{float(value):.2f}".rstrip("0").rstrip(".") + return f"{amount} ₽" + +def _format_magnit_price(item: dict) -> str: + new_price = _format_money(item.get("price")) + old_price = _format_money(item.get("old_price")) + if old_price and new_price and item.get("old_price") and item.get("old_price") != item.get("price"): + return f"{old_price} -> {new_price}" + return new_price or "нет цены" + +def _format_magnit_quantity(value) -> str: + if value is None: + return "нет данных" + return f"{value} шт." + # --- Игровая логика (/penis) --- def play_penis(user_id: int, user_name: str | None = None, now: datetime | None = None) -> tuple[bool, str]: @@ -542,61 +598,58 @@ async def handle_zvetok_cmd(message: Message): await message.answer(minutes_until_next_lesson()) async def handle_prices_cmd(message: Message): - def fetch(): - return requests.get( - "https://bebrik.xyz/magnit/products", - headers={"X-Token": "sisi"}, - timeout=10, - ) try: - resp = await asyncio.to_thread(fetch) - if resp.status_code != 200: - await message.answer("пошел нахуй") - return - data = resp.json() - if not data: - await message.answer("иди нахуй") - return - text = "смерть в 25:\n\n" - for item in data: - name = item.get("name") - price = item.get("price") - text += f"{name} — {price} ₽\n" - await message.answer(text[:4000]) - except Exception: - await message.answer("unable to connect x-server гандон") + products = [_normalize_magnit_product(entry) for entry in config.MAGNIT_PRODUCTS] + except ValueError as exc: + await message.answer(f"Проверь MAGNIT_PRODUCTS в config.py: {exc}") + return + + if not products: + await message.answer("Список MAGNIT_PRODUCTS пуст. Добавь товары в config.py.") + return + + async def fetch_product(product_cfg: dict[str, str]) -> str: + def fetch(): + return requests.get( + f"{config.MAGNIT_API_BASE_URL.rstrip('/')}/price", + params={ + "store_code": product_cfg["store_code"], + "query": product_cfg["query"], + }, + timeout=config.MAGNIT_REQUEST_TIMEOUT, + ) + + label = product_cfg["label"] or product_cfg["query"] or "товар" + try: + resp = await asyncio.to_thread(fetch) + if resp.status_code != 200: + logger.warning("Magnit API returned %s for %s", resp.status_code, product_cfg["query"]) + return f"{label}, ошибка API ({resp.status_code}), -" + + data = resp.json() + if not isinstance(data, dict): + return f"{label}, битый ответ API, -" + + item = _pick_magnit_item(data) + if not isinstance(item, dict): + return f"{label}, товар не найден, -" + + name = str(item.get("name") or label).strip() or label + price = _format_magnit_price(item) + quantity = _format_magnit_quantity(item.get("quantity")) + return f"{name}, {price}, {quantity}" + except requests.RequestException: + logger.exception("Failed to fetch Magnit product: %s", product_cfg["query"]) + return f"{label}, ошибка запроса, -" + except ValueError: + logger.exception("Magnit API returned invalid JSON for %s", product_cfg["query"]) + return f"{label}, битый JSON, -" + + lines = await asyncio.gather(*(fetch_product(product) for product in products)) + await message.answer("\n".join(lines)[:4000]) async def handle_fetch_cmd(message: Message): - def fetch(): - return requests.get( - "https://mirror.porno4free.ru/magnit/magnit/prices/all", - headers={"x-token": "secret_token"}, - timeout=10, - ) - try: - resp = await asyncio.to_thread(fetch) - if resp.status_code != 200: - await message.reply("пошел нахуй") - return - data = resp.json() - if not isinstance(data, dict) or not data: - await message.reply("иди нахуй") - return - lines = ["смерть в 25:"] - for group in sorted(data.keys()): - items = data.get(group) - lines.append(f"\n{group}:") - if not isinstance(items, list): - continue - for item in items: - name = item.get("name") - price = item.get("price") - if not name or price is None: - continue - lines.append(f"{name} — {price} ₽") - await message.answer("\n".join(lines)[:4000]) - except Exception: - await message.answer("unable to connect x-server гандон") + await handle_prices_cmd(message) async def handle_svodka_cmd(message: Message): data = await asyncio.to_thread(get_military_data, "creamy_caprice", 5) @@ -790,8 +843,8 @@ def main(): BotCommand(command="size", description="хуй"), BotCommand(command="penis", description="миниигра 1 раз в 24ч"), BotCommand(command="top_penis", description="топ по длине"), - BotCommand(command="prices", description="цены x-server"), - BotCommand(command="fetch", description="цены (новый источник)"), + BotCommand(command="prices", description="цены и остатки Магнита"), + BotCommand(command="fetch", description="alias для /prices"), BotCommand(command="zvetok", description="Цветянский бля"), BotCommand(command="talk", description="Побазарить"), BotCommand(command="ebalnik", description="вкл/выкл автответов"),