import logging import random from pathlib import PurePosixPath from urllib.parse import urlparse import aiohttp from aiogram import types logger = logging.getLogger(__name__) CAPTIONS = [ "Шо ты блять псюнчик решил свой подергать?", "Решил подрочить сука?", "гнойный писюн у тебя кнч, да", ] LESAINT_POSTS_API = "https://lesaintdesseins.fr/wp-json/wp/v2/posts" LESAINT_REFERER = "https://lesaintdesseins.fr/" LATEST_POSTS_LIMIT = 30 REQUEST_HEADERS = { "User-Agent": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/124.0.0.0 Safari/537.36" ), "Accept": "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.9", "Referer": LESAINT_REFERER, } def _extract_photo_candidates(posts: list[dict]) -> list[dict]: candidates = [] for post in posts: embedded = post.get("_embedded") or {} media_items = embedded.get("wp:featuredmedia") or [] for media in media_items: if media.get("media_type") != "image": continue media_details = media.get("media_details") or {} sizes = media_details.get("sizes") or {} image_url = ( (sizes.get("full") or {}).get("source_url") or media.get("source_url") ) if not image_url: continue candidates.append( { "image_url": image_url, "post_url": post.get("link", LESAINT_REFERER), "title": (post.get("title") or {}).get("rendered", "").strip(), } ) break return candidates def _guess_filename(image_url: str) -> str: parsed = urlparse(image_url) filename = PurePosixPath(parsed.path).name or "nude.jpg" if "." not in filename: filename = f"{filename}.jpg" return filename async def _fetch_latest_photo_candidates(session: aiohttp.ClientSession) -> list[dict]: params = { "per_page": str(LATEST_POSTS_LIMIT), "orderby": "date", "order": "desc", "_embed": "wp:featuredmedia", } async with session.get(LESAINT_POSTS_API, params=params, headers=REQUEST_HEADERS) as resp: if resp.status != 200: body = await resp.text() raise RuntimeError(f"API returned {resp.status}: {body[:300]}") posts = await resp.json(content_type=None) if not isinstance(posts, list): raise RuntimeError("Unexpected API response format") return _extract_photo_candidates(posts) async def _download_photo( session: aiohttp.ClientSession, image_url: str ) -> tuple[bytes | None, str]: async with session.get(image_url, headers=REQUEST_HEADERS) as resp: if resp.status != 200: logger.warning("Image download failed for %s: %s", image_url, resp.status) return None, _guess_filename(image_url) content_type = resp.headers.get("Content-Type", "") if "image" not in content_type.lower(): logger.warning( "Unexpected content type for %s: %s", image_url, content_type ) return None, _guess_filename(image_url) return await resp.read(), _guess_filename(image_url) async def handle_nude_cmd(message: types.Message): args = message.text.split(maxsplit=1) if len(args) > 1 and args[1].strip() == "-h": help_msg = ( "🔥 Справка по команде /nude:\n\n" "Берет случайную фотку из последних постов сайта le saint des seins.\n" "• Источник: https://lesaintdesseins.fr/\n" "• Достает последние посты через WordPress REST API\n" "• Выбирает случайную картинку и отправляет ее прямо в Telegram\n\n" "Пример: /nude" ) await message.reply(help_msg, parse_mode="HTML") return timeout = aiohttp.ClientTimeout(total=30) try: async with aiohttp.ClientSession(timeout=timeout) as session: candidates = await _fetch_latest_photo_candidates(session) if not candidates: await message.reply("Не удалось найти свежие фотки :(") return selected = random.choice(candidates) caption = random.choice(CAPTIONS) image_bytes, filename = await _download_photo(session, selected["image_url"]) if image_bytes: photo = types.BufferedInputFile(image_bytes, filename=filename) await message.answer_photo(photo=photo, caption=caption) return try: photo = types.URLInputFile(selected["image_url"]) await message.answer_photo(photo=photo, caption=caption) except Exception: logger.exception("Failed to send photo by URL") await message.answer( f"{caption}\n{selected['image_url']}\n{selected['post_url']}" ) except Exception: logger.exception("Error in nude command") await message.reply("Произошла ошибка при получении фото :(")