From 7afd8ca21644fc36de8de6f641c25e12b5fa17d7 Mon Sep 17 00:00:00 2001 From: q Date: Fri, 10 Apr 2026 11:17:45 +0300 Subject: [PATCH] some fix for nude --- games/nude.py | 227 +++++++++++++++++++++++++++++++------------------- 1 file changed, 139 insertions(+), 88 deletions(-) diff --git a/games/nude.py b/games/nude.py index 280bbf4..71ac438 100644 --- a/games/nude.py +++ b/games/nude.py @@ -1,108 +1,159 @@ -import aiohttp -from aiogram import types -import config import logging import random -import re +from pathlib import PurePosixPath +from urllib.parse import urlparse + +import aiohttp +from aiogram import types + logger = logging.getLogger(__name__) CAPTIONS = [ "Hot stuff πŸ”₯", - "Sexy content 😏" + "Sexy content 😏", ] +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" - "ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ случайныС изобраТСния взрослых ΠΆΠ΅Π½Ρ‰ΠΈΠ½ ΠΈΠ· ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… источников.\n" - "β€’ Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ прямоС сканированиС сайтов для поиска ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π°\n" - "β€’ По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΠΈΡ‰Π΅Ρ‚ популярныС изобраТСния\n" - "β€’ ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅Ρ‚ изобраТСния Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ\n\n" - "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: /nude\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 - # Бписок сайтов для сканирования - sites = [ - "https://www.imagefap.com/gallery.php?pg=random", - "https://xhamster.com/photos?sort=random", - "https://spankbang.com/random" - ] - - random_site = random.choice(sites) - - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - "Accept-Encoding": "gzip, deflate", - "Connection": "keep-alive", - "Upgrade-Insecure-Requests": "1" - } - + timeout = aiohttp.ClientTimeout(total=30) + try: - async with aiohttp.ClientSession() as session: - async with session.get(random_site, headers=headers) as resp: - if resp.status != 200: - logger.error(f"Site access error: {resp.status}") - await message.reply("НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ доступ ΠΊ сайту :(") - return - - html = await resp.text() - - # Π˜Ρ‰Π΅ΠΌ URL ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π² HTML - img_patterns = [ - r']+src="([^"]+)"', - r']+href="([^"]+\.(jpg|jpeg|png|gif|webp))"', - r'data-src="([^"]+)"' - ] - - img_urls = [] - for pattern in img_patterns: - matches = re.findall(pattern, html, re.IGNORECASE) - img_urls.extend(matches) - - if not img_urls: - await message.reply("Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΡ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹ Π½Π° страницС :(") - return - - # Π€ΠΈΠ»ΡŒΡ‚Ρ€ΡƒΠ΅ΠΌ ΠΈ Π²Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ случайноС ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ - valid_imgs = [] - for img_url in img_urls: - if img_url.startswith('//'): - img_url = 'https:' + img_url - elif img_url.startswith('/'): - if 'imagefap.com' in random_site: - img_url = 'https://www.imagefap.com' + img_url - elif 'xhamster.com' in random_site: - img_url = 'https://xhamster.com' + img_url - elif 'spankbang.com' in random_site: - img_url = 'https://spankbang.com' + img_url - - if any(ext in img_url.lower() for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']): - valid_imgs.append(img_url) - - if not valid_imgs: - await message.reply("ΠŸΠΎΠ΄Ρ…ΠΎΠ΄ΡΡ‰ΠΈΠ΅ изобраТСния Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹ :(") - return - - final_img_url = random.choice(valid_imgs) - caption = random.choice(CAPTIONS) - - # ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ - try: - input_file = types.URLInputFile(final_img_url) - await message.answer_photo(photo=input_file, caption=caption) - except Exception as e: - logger.error(f"Error sending photo: {e}") - await message.answer(f"{caption}\n{final_img_url}") - - except Exception as e: + 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("ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ запроса :(") + await message.reply("ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ Ρ„ΠΎΡ‚ΠΎ :(")