forked from zovos/bot_tg
some fix for nude
This commit is contained in:
parent
06d97cbff0
commit
7afd8ca216
1 changed files with 139 additions and 88 deletions
227
games/nude.py
227
games/nude.py
|
|
@ -1,108 +1,159 @@
|
||||||
import aiohttp
|
|
||||||
from aiogram import types
|
|
||||||
import config
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import re
|
from pathlib import PurePosixPath
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from aiogram import types
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CAPTIONS = [
|
CAPTIONS = [
|
||||||
"Hot stuff 🔥",
|
"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):
|
async def handle_nude_cmd(message: types.Message):
|
||||||
args = message.text.split(maxsplit=1)
|
args = message.text.split(maxsplit=1)
|
||||||
|
|
||||||
if len(args) > 1 and args[1].strip() == "-h":
|
if len(args) > 1 and args[1].strip() == "-h":
|
||||||
help_msg = (
|
help_msg = (
|
||||||
"🔥 <b>Справка по команде /nude:</b>\n\n"
|
"🔥 <b>Справка по команде /nude:</b>\n\n"
|
||||||
"Получает случайные изображения взрослых женщин из открытых источников.\n"
|
"Берет случайную фотку из последних постов сайта le saint des seins.\n"
|
||||||
"• Использует прямое сканирование сайтов для поиска контента\n"
|
"• Источник: <code>https://lesaintdesseins.fr/</code>\n"
|
||||||
"• По умолчанию ищет популярные изображения\n"
|
"• Достает последние посты через WordPress REST API\n"
|
||||||
"• Отправляет изображения напрямую\n\n"
|
"• Выбирает случайную картинку и отправляет ее прямо в Telegram\n\n"
|
||||||
"<i>Пример:</i> <code>/nude</code>\n"
|
"<i>Пример:</i> <code>/nude</code>"
|
||||||
"<i>По умолчанию:</i> Случайные изображения с популярных сайтов"
|
|
||||||
)
|
)
|
||||||
await message.reply(help_msg, parse_mode="HTML")
|
await message.reply(help_msg, parse_mode="HTML")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Список сайтов для сканирования
|
timeout = aiohttp.ClientTimeout(total=30)
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
async with session.get(random_site, headers=headers) as resp:
|
candidates = await _fetch_latest_photo_candidates(session)
|
||||||
if resp.status != 200:
|
|
||||||
logger.error(f"Site access error: {resp.status}")
|
if not candidates:
|
||||||
await message.reply("Не удалось получить доступ к сайту :(")
|
await message.reply("Не удалось найти свежие фотки :(")
|
||||||
return
|
return
|
||||||
|
|
||||||
html = await resp.text()
|
selected = random.choice(candidates)
|
||||||
|
caption = random.choice(CAPTIONS)
|
||||||
# Ищем URL изображений в HTML
|
image_bytes, filename = await _download_photo(session, selected["image_url"])
|
||||||
img_patterns = [
|
|
||||||
r'<img[^>]+src="([^"]+)"',
|
if image_bytes:
|
||||||
r'<a[^>]+href="([^"]+\.(jpg|jpeg|png|gif|webp))"',
|
photo = types.BufferedInputFile(image_bytes, filename=filename)
|
||||||
r'data-src="([^"]+)"'
|
await message.answer_photo(photo=photo, caption=caption)
|
||||||
]
|
return
|
||||||
|
|
||||||
img_urls = []
|
try:
|
||||||
for pattern in img_patterns:
|
photo = types.URLInputFile(selected["image_url"])
|
||||||
matches = re.findall(pattern, html, re.IGNORECASE)
|
await message.answer_photo(photo=photo, caption=caption)
|
||||||
img_urls.extend(matches)
|
except Exception:
|
||||||
|
logger.exception("Failed to send photo by URL")
|
||||||
if not img_urls:
|
await message.answer(
|
||||||
await message.reply("Изображения не найдены на странице :(")
|
f"{caption}\n{selected['image_url']}\n{selected['post_url']}"
|
||||||
return
|
)
|
||||||
|
|
||||||
# Фильтруем и выбираем случайное изображение
|
except Exception:
|
||||||
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:
|
|
||||||
logger.exception("Error in nude command")
|
logger.exception("Error in nude command")
|
||||||
await message.reply("Произошла ошибка при обработке запроса :(")
|
await message.reply("Произошла ошибка при получении фото :(")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue