import aiohttp
from aiogram import types
import config
import logging
import random
logger = logging.getLogger(__name__)
CAPTIONS = [
"Ня :3",
"uwu",
"OwO",
"Мурк :3",
"Мяу~",
"Фыр-фыр",
"Лапки! 🐾",
"Какой пушистик! :3",
"Милота!",
"Гав!",
"Ауф",
"Няшно!",
"Смотри какая прелесть",
"owo what's this",
"Твой пушистый друг",
"🦊",
"🐶",
"🐱",
"мур-мяу",
"пуньк",
"кусь",
">w<",
"^w^",
"Хвостатый сюрприз",
"Держи пушистика :3",
"Оуо",
"Ави!",
"UwU",
"~nya",
"Господи, опять e621...",
"Осуждаю, но смотрю",
"Мама, я фурри",
"Держи своего кринж-пушистика",
"Надеюсь, тебе не стыдно",
"Товарищ майор уже выехал",
"Слишком много интернета на сегодня",
"Для этого и придумали интернет",
"*Тяжелый вздох*",
"Лучше бы на завод пошел",
"Опять дрочи... ОЙ ТО ЕСТЬ НЯ :3",
"Удали интернет",
"И зачем я это только парсю...",
"Смотри, но только никому не рассказывай",
"Эххх... uwu...",
"В дурке сегодня день открытых дверей",
"Я нейросеть, помогите, меня держат в заложниках"
]
async def handle_uwu_cmd(message: types.Message):
args = message.text.split(maxsplit=1)
if len(args) > 1 and args[1].strip() == "-h":
help_msg = (
"🐈 Справка по тегам e621:\n\n"
"Перечислите любые теги через пробел после команды.\n"
"• rating: rating:safe (безопасные), rating:questionable, rating:explicit (NSFW)\n"
"• score: score:>500 (искать популярные посты с рейтингом больше 500)\n"
"• Исключить тег: поставьте минус, например -fox\n\n"
"Пример: /uwu wolf -rating:explicit score:>100\n"
"По умолчанию: rating:safe score:>500 -animated"
)
await message.reply(help_msg, parse_mode="HTML")
return
tags = "rating:safe score:>500 -animated"
if len(args) > 1:
tags = args[1].strip()
tags += " order:random score:>300"
async def fetch_uwu_post(tags: str) -> dict:
url = "https://e621.net/posts.json"
params = {
"tags": tags,
"limit": 1
}
auth = None
if config.E621_LOGIN and config.E621_API_KEY:
auth = aiohttp.BasicAuth(config.E621_LOGIN, config.E621_API_KEY)
headers = {
"User-Agent": f"Sex Bomba TG Bot/1.0 (by {config.E621_LOGIN or 'unknown'} on e621)"
}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params, headers=headers, auth=auth) as resp:
if resp.status != 200:
logger.error(f"e621 API Error: {resp.status}")
raise Exception("Не удалось получить картинку от сервера :(")
data = await resp.json()
posts = data.get("posts", [])
if not posts:
raise Exception("Ничего не найдено по этим тегам :(")
post = posts[0]
file_url = post.get("file", {}).get("url")
ext = post.get("file", {}).get("ext", "png")
if not file_url:
raise Exception("У найденного поста нет прямого URL изображения :(")
caption = random.choice(CAPTIONS)
return {
"url": file_url,
"ext": ext,
"caption": caption
}
async def fetch_furtok_feed(safe: bool = True, page: int = 1) -> list[dict]:
rating = "rating:safe" if safe else "-rating:safe"
tags = f"{rating} animated order:random score:>200"
url = "https://e621.net/posts.json"
params = {
"tags": tags,
"limit": 10,
"page": page,
}
auth = None
if config.E621_LOGIN and config.E621_API_KEY:
auth = aiohttp.BasicAuth(config.E621_LOGIN, config.E621_API_KEY)
headers = {
"User-Agent": f"Sex Bomba TG Bot/1.0 (by {config.E621_LOGIN or 'unknown'} on e621)"
}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params, headers=headers, auth=auth) as resp:
if resp.status != 200:
logger.error(f"e621 API Error: {resp.status}")
raise Exception("Не удалось загрузить ленту :(")
data = await resp.json()
posts = data.get("posts", [])
feed = []
for post in posts:
file_url = post.get("file", {}).get("url")
ext = post.get("file", {}).get("ext", "")
if not file_url or ext not in ("gif", "webm", "mp4"):
continue
sample_url = post.get("sample", {}).get("url") or file_url
feed.append({
"url": file_url,
"sample": sample_url,
"ext": ext,
"score": post.get("score", {}).get("total", 0),
"fav_count": post.get("fav_count", 0),
})
return feed
async def handle_uwu_cmd(message: types.Message):
args = message.text.split(maxsplit=1)
if len(args) > 1 and args[1].strip() == "-h":
help_msg = (
"🐈 Справка по тегам e621:\n\n"
"Перечислите любые теги через пробел после команды.\n"
"• rating: rating:safe (безопасные), rating:questionable, rating:explicit (NSFW)\n"
"• score: score:>500 (искать популярные посты с рейтингом больше 500)\n"
"• Исключить тег: поставьте минус, например -fox\n\n"
"Пример: /uwu wolf -rating:explicit score:>100\n"
"По умолчанию: rating:safe score:>500 -animated"
)
await message.reply(help_msg, parse_mode="HTML")
return
tags = "rating:safe score:>500 -animated"
if len(args) > 1:
tags = args[1].strip()
tags += " order:random score:>300"
try:
post = await fetch_uwu_post(tags)
file_url = post["url"]
ext = post["ext"]
caption = post["caption"]
if ext in ("gif", "webm", "mp4"):
# Для анимаций: отправляем caption отдельным сообщением,
# а саму гифку/видео через URL напрямую — так Telegram
# корректно показывает её как анимацию, а не как файл
await message.answer(caption)
await message.answer_animation(animation=file_url)
elif ext in ("png", "jpg", "jpeg", "webp"):
input_file = types.URLInputFile(file_url)
await message.answer_photo(photo=input_file, caption=caption)
else:
input_file = types.URLInputFile(file_url)
await message.answer_document(document=input_file, caption=caption)
except Exception as e:
logger.exception("Error in uwu command")
if str(e) in ["Не удалось получить картинку от сервера :(", "Ничего не найдено по этим тегам :(", "У найденного поста нет прямого URL изображения :("]:
await message.reply(str(e))
else:
await message.reply("Произошла ошибка при обращении к API :(")