diff --git a/.gitignore b/.gitignore index a0c7666..ff5d2a5 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ storage/message.txt /addons/todo/todo.db /addons/rule34/urls.db /addons/x_days_to/days_to_new_year.db +/addons/hello/photo_2025-11-17_20-57-54.jpg +/addons/poll/img.png +/addons/hello/мемы/ diff --git a/addons/dowloadmp4_to_youtube/__init__.py b/addons/dowloadmp4_to_youtube/__init__.py index e0acb2f..94558db 100644 --- a/addons/dowloadmp4_to_youtube/__init__.py +++ b/addons/dowloadmp4_to_youtube/__init__.py @@ -1,6 +1,5 @@ def register(dp, state, bot): from . import handlers - handlers.register_handlers(dp, state, bot) diff --git a/addons/download_mp3_to_youtube/dowloadmp3_to_youtube.py b/addons/download_mp3_to_youtube/dowloadmp3_to_youtube.py index 99e834f..df3f5ac 100644 --- a/addons/download_mp3_to_youtube/dowloadmp3_to_youtube.py +++ b/addons/download_mp3_to_youtube/dowloadmp3_to_youtube.py @@ -13,7 +13,7 @@ from mutagen.id3 import ID3, APIC, error logger = logging.getLogger(__name__) -async def get_video_info(url: str) -> dict: +async def get_video_info(url: str) -> Optional[dict]: """Получает информацию о видео через yt-dlp""" try: process = await asyncio.create_subprocess_exec( @@ -63,8 +63,8 @@ def apply_metadata(mp3_path: str, metadata: dict): # Сначала удаляем старые теги ID3 если есть try: ID3(mp3_path).delete() - except: - pass + except Exception as e: + logger.warning(f"Не удалось удалить старые ID3 теги: {e}") # Добавляем текстовые теги try: diff --git a/addons/draw/handlers.py b/addons/draw/handlers.py index ee0872e..2747e0a 100644 --- a/addons/draw/handlers.py +++ b/addons/draw/handlers.py @@ -3,7 +3,7 @@ import base64 from io import BytesIO import asyncio import aiohttp - +from PIL import Image from aiogram import Dispatcher, Bot from aiogram.types import Message, BufferedInputFile from aiogram.filters import Command @@ -43,6 +43,11 @@ async def generate_img2img(prompt: str, init_image: BytesIO) -> BytesIO | None: :return: BytesIO с результатом или None при ошибке """ try: + # Определяем размеры оригинала + init_image.seek(0) + with Image.open(init_image) as img: + width, height = img.size + # кодируем входное изображение в base64 init_image_base64 = base64.b64encode(init_image.getvalue()).decode("utf-8") @@ -50,15 +55,15 @@ async def generate_img2img(prompt: str, init_image: BytesIO) -> BytesIO | None: "init_images": [init_image_base64], "prompt": prompt, "negative_prompt": "blurry, low quality, bad anatomy, watermark, text, cropped", - "steps": 20, # можно 15–20 - "width": 1024, # лучше подставлять размеры исходного фото - "height": 1024, - "sampler_name": "Euler a", # мягкий и стабильный для img2img - "Schedule_type": "Karras", - "cfg_scale": 6, # чуть ниже, чем для txt2img + "steps": 25, + "width": width, # берём ширину оригинала + "height": height, # берём высоту оригинала + "sampler_name": "Euler a", + "scheduler": "Karras", # исправлен ключ + "cfg_scale": 10, "seed": -1, - "denoising_strength": 0.8, # 0.3–0.5 для «сохранить стиль», 0.6–0.8 для «перерисовать» - "restore_faces": True, # если работаешь с людьми + "denoising_strength": 0.45, + "restore_faces": True, "override_settings": { "sd_model_checkpoint": "waiNSFWIllustrious_v150.safetensors" }, @@ -101,7 +106,7 @@ async def generate_image(prompt: str) -> BytesIO | None: "hr_scale": 2, # во сколько раз увеличить при highres fix "hr_upscaler": "Latent", # апскейлер для highres fix "override_settings": { - "sd_model_checkpoint": "waiNSFWIllustrious_v150.safetensors" # выбор модели + "sd_model_checkpoint": "sd_xl_base_1.safetensors" # выбор модели }, } diff --git a/addons/hello/handlers.py b/addons/hello/handlers.py index 39f8cb2..af93119 100644 --- a/addons/hello/handlers.py +++ b/addons/hello/handlers.py @@ -1,32 +1,66 @@ from aiogram import Dispatcher, Bot -from aiogram.types import Message +from aiogram.types import Message, FSInputFile from aiogram.filters import Command from models.state import BotState from config import Config import logging from utils.antispam import admin_required from storage.message_storage import save_message # импортируем функцию +import os +import asyncio +from random import choice, seed +from time import time logger = logging.getLogger(__name__) + +async def list_files(): + # Запускаем синхронный os.listdir в отдельном потоке + return await asyncio.to_thread(os.listdir, "/Users/mac/myfirstprogramm/addons/hello/мемы") + def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): + i = 0 @dp.message(Command("hello")) @admin_required(1) async def hello(message: Message): # сохраняем саму команду пользователя + nonlocal i save_message(message.chat.id, message.message_id) for admin_id in Config.ADMINS: try: - name = Config.Names.get(admin_id, "Админ") - msg = await bot.send_message( - chat_id=admin_id, text=f"🤖 Я готов к работе, господин {name}!" - ) - # сохраняем сообщение, отправленное админу - save_message(msg.chat.id, msg.message_id) + if 1345058877 == admin_id: + name = Config.Names.get(admin_id, "Админ") + photo = FSInputFile("/Users/mac/myfirstprogramm/addons/hello/photo_2025-11-17_20-57-54.jpg") + msg = await bot.send_photo( + chat_id=admin_id, photo=photo, caption=f"🤖 Я готов к работе, господин {name}!" + ) - logger.info(f"Сообщение отправлено админу {admin_id} ({name})") + save_message(msg.chat.id, msg.message_id) + logger.info(f"Фото отправлено админу {admin_id} ({name})") + elif 6394047531 == admin_id: + png = choice(await list_files()) + i += 1 + seed(time() + i) + name = Config.Names.get(admin_id, "Админ") + photo = FSInputFile(f"/Users/mac/myfirstprogramm/addons/hello/мемы/{png}") + msg = await bot.send_photo( + chat_id=admin_id, photo=photo, caption=f"🤖 Я готов к работе, господин {name}!" + ) + + save_message(msg.chat.id, msg.message_id) + logger.info(f"Фото {f"/Users/mac/myfirstprogramm/addons/hello/мемы/{png}"} отправлено админу {admin_id} ({name})") + + else: + name = Config.Names.get(admin_id, "Админ") + msg = await bot.send_message( + chat_id=admin_id, text=f"🤖 Я готов к работе, господин {name}!" + ) + # сохраняем сообщение, отправленное админу + save_message(msg.chat.id, msg.message_id) + + logger.info(f"Сообщение отправлено админу {admin_id} ({name})") except Exception as e: logger.error(f"Ошибка при отправке админу {admin_id}: {e}") diff --git a/addons/id/handlers.py b/addons/id/handlers.py index ed45d9f..db364f1 100644 --- a/addons/id/handlers.py +++ b/addons/id/handlers.py @@ -3,7 +3,6 @@ from aiogram import Dispatcher, Bot from aiogram.types import Message from aiogram.filters import Command from models.state import BotState -from utils.antispam import saving API_URL = "http://127.0.0.1:7700/speak" @@ -12,7 +11,27 @@ logger = getLogger(__name__) def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): @dp.message(Command("id")) - @saving - async def id(message: Message): - id = message.from_user.id - msg = await message.reply(str(id)) + async def id_handler(message: Message): + # Разбираем аргументы команды + args = message.text.split() + if len(args) > 1: + try: + user_id = int(args[1]) # берём ID из аргумента + except ValueError: + await message.reply("ID должен быть числом") + return + else: + # если аргумента нет — берём ID самого пользователя + user_id = message.from_user.id + + # Получаем фото профиля + photos = await bot.get_user_profile_photos(user_id=user_id) + + if photos.total_count > 0: + for i, photo_sizes in enumerate(photos.photos): + file_id = photo_sizes[-1].file_id # самое большое разрешение + await message.answer_photo(file_id, caption=f"Аватар #{i + 1}") + await message.reply(f"ID пользователя: {user_id}") + else: + await message.reply(f"У пользователя {user_id} нет аватара") + diff --git a/addons/poll/handlers.py b/addons/poll/handlers.py index 8b3a86d..d48bc68 100644 --- a/addons/poll/handlers.py +++ b/addons/poll/handlers.py @@ -1,7 +1,7 @@ from config import Config from utils.antispam import admin_required from aiogram import Dispatcher, Bot -from aiogram.types import Message +from aiogram.types import Message, FSInputFile from models.state import BotState from aiogram.filters import Command from logging import getLogger @@ -20,7 +20,7 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): poll_msg = await bot.send_poll( chat_id=chat_id, question="Кто опоздает?", - options=["Я", "Я очень сильно опоздаю", "Я пиздец как опоздаю", "Наверное", "Не опоздаю"], + options=["Я", "Я очень сильно опоздаю", "Я пиздец как опоздаю", "Наверное", "я ДОЛБОЯЩЕР и я к 2 паре", "Не опоздаю"], is_anonymous=False, allows_multiple_answers=False, ) @@ -44,39 +44,49 @@ def register_handlers(dp: Dispatcher, state: BotState, bot: Bot): # всегда пишем в первый чат из Config.CHAT_IDS # 6394047531 + #850906163 + STARAST = 6394047531 - if option_ids and option_ids[0] == 0: + if not option_ids: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} опоздает" + chat_id=STARAST, text=f"{username} Отменил свой голос" + ) + save_message(msg.chat.id, msg.message_id) + elif option_ids and option_ids[0] == 0: + msg = await bot.send_message( + chat_id=STARAST, text=f"{username} опоздает" ) save_message(msg.chat.id, msg.message_id) elif option_ids and option_ids[0] == 1: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} сильно опоздает" + chat_id=STARAST, text=f"{username} сильно опоздает" ) save_message(msg.chat.id, msg.message_id) elif option_ids and option_ids[0] == 2: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} Пиздец опоздает" + chat_id=STARAST, text=f"{username} Пиздец опоздает" ) save_message(msg.chat.id, msg.message_id) elif option_ids[0] == 3: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} возможно опоздает" + chat_id=STARAST, text=f"{username} возможно опоздает" ) save_message(msg.chat.id, msg.message_id) + elif option_ids[0] == 4: - msg = await bot.send_message( - chat_id=6394047531, text=f"{username} возможно опоздает" + photo = FSInputFile("/Users/mac/myfirstprogramm/addons/poll/img.png") + msg = await bot.send_photo( + chat_id=STARAST, photo=photo, caption=f"{username} ДОЛБОЯЩЕР" ) save_message(msg.chat.id, msg.message_id) - elif not option_ids: + + elif option_ids[0] == 5: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} Отменил свой голос" + chat_id=STARAST, text=f"{username} не опоздает" ) save_message(msg.chat.id, msg.message_id) else: msg = await bot.send_message( - chat_id=6394047531, text=f"{username} выбрал вариант {option_ids}" + chat_id=STARAST, text=f"{username} выбрал вариант {option_ids}" ) save_message(msg.chat.id, msg.message_id) diff --git a/addons/rule34/get_post.py b/addons/rule34/get_post.py index 8ac5e86..c0cb628 100644 --- a/addons/rule34/get_post.py +++ b/addons/rule34/get_post.py @@ -1,11 +1,8 @@ -import requests +import asyncio from random import randint, choice -from bs4 import BeautifulSoup +from playwright.async_api import async_playwright BASE_URL = "https://rule34.xxx" -HEADERS = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -} URL = f"{BASE_URL}/index.php?page=post&s=list" MAXIMUM = 999 @@ -25,49 +22,68 @@ def get_tags_str() -> str: def get_tags() -> str: return "+".join(TAGS) if TAGS else "" -def get_url(): - while True: - try: - tags = get_tags() - pid = randint(1, MAXIMUM) - # Формируем корректный URL с query‑параметрами - if tags: - url_page = f"{URL}&tags={tags}&pid={pid}" - else: - url_page = f"{URL}&pid={pid}" +async def get_url(): + async with async_playwright() as p: + browser = await p.firefox.launch(headless=True) + page = await browser.new_page(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)") - r = requests.get(url_page, headers=HEADERS, timeout=5) - soup = BeautifulSoup(r.text, 'lxml') - block = soup.find(class_="image-list") - if not block: - continue - block = block.find_all("span") - if not block: + while True: + try: + tags = get_tags() + pid = randint(1, MAXIMUM) + + # Формируем корректный URL + if tags: + url_page = f"{URL}&tags={tags}&pid={pid}" + else: + url_page = f"{URL}&pid={pid}" + + await page.goto(url_page, timeout=5000) + + # Ищем блок с картинками + block = await page.query_selector(".image-list") + if not block: + continue + + spans = await block.query_selector_all("span") + if not spans: + continue + + link_el = await choice(spans).query_selector("a") + if not link_el: + continue + + href = await link_el.get_attribute("href") + if not href: + continue + + await page.goto(f"{BASE_URL}{href}", timeout=30000) + + flexi = await page.query_selector(".flexi") + if not flexi: + continue + + img_el = await flexi.query_selector("img") + if not img_el: + continue + + url = await img_el.get_attribute("src") + if not url: + continue + + await browser.close() + return url + + except Exception as e: + print(f"[get_url ERROR] {e}") continue - link = choice(block).find("a") - if not link: - continue - r2 = requests.get(f"{BASE_URL}{link.get('href')}", headers=HEADERS, timeout=10) - soup_two = BeautifulSoup(r2.text, 'lxml') - - flexi = soup_two.find(class_="flexi") - if not flexi: - continue - - img = flexi.find("img") - if not img: - continue - - url = img.get("src") - if not url: - continue - - return url - - except Exception as e: - print(f"[get_url ERROR] {e}") - continue +# Пример использования +async def main(): + result = await get_url() + print("Result URL:", result) +if __name__ == "__main__": + asyncio.run(main()) diff --git a/addons/rule34/handlers.py b/addons/rule34/handlers.py index 38d441e..9ee7d0e 100644 --- a/addons/rule34/handlers.py +++ b/addons/rule34/handlers.py @@ -26,7 +26,7 @@ def register_handlers(dp: Dispatcher, state, bot: Bot): @dp.message(Command("rule34")) async def rule34(message: Message): msg = await message.answer_photo( - photo=get_url(), + photo= await get_url(), caption="Вот фото 📷", reply_markup=get_keyboard() ) @@ -39,7 +39,7 @@ def register_handlers(dp: Dispatcher, state, bot: Bot): for attempt in range(3): # максимум 3 попытки try: media = InputMediaPhoto( - media=get_url(), + media=await get_url(), caption=f"Новое фото 🌄 (попытка {attempt + 1})" ) await callback.message.edit_media( diff --git a/addons/send_message/handlers.py b/addons/send_message/handlers.py index c1252ec..feaa148 100644 --- a/addons/send_message/handlers.py +++ b/addons/send_message/handlers.py @@ -8,6 +8,7 @@ from aiogram.types import Message from models.state import BotState from aiogram.filters import Command from logging import getLogger +import aiohttp logger = getLogger(__name__) diff --git a/bot/core.py b/bot/core.py index b687aea..60fb609 100644 --- a/bot/core.py +++ b/bot/core.py @@ -28,7 +28,7 @@ class TelegramBot: self.addons.load("poll") self.addons.load("hello") # self.addons.load("draw") - # self.addons.load("gpt") + self.addons.load("gpt") # self.addons.load("rule34") # self.addons.load("todo") self.addons.load("miniapps") diff --git a/config.py b/config.py index 98ced34..66b95ac 100644 --- a/config.py +++ b/config.py @@ -22,7 +22,7 @@ class Config: # Admins (user_id: уровень) ADMINS: Dict[int, int] = {850906163: 0, 6394047531: 4, 1345058877: 3} - Names: Dict[int, str] = {850906163: "Ляпич", 6394047531: "Прокопович"} + Names: Dict[int, str] = {850906163: "Ляпич", 6394047531: "Прокопович", 1345058877: "Сом"} # Chats CHAT_IDS = [-1003038389942] @@ -32,7 +32,7 @@ class Config: } # Settings - ANTISPAM_DELAY = 600 + ANTISPAM_DELAY = 20 WATCHER_BASE_DELAY = 600 # Пути diff --git a/handlers/schedule.py b/handlers/schedule.py index a075892..339949a 100644 --- a/handlers/schedule.py +++ b/handlers/schedule.py @@ -41,7 +41,7 @@ def register_handlers(dp: Dispatcher, state: BotState): @dp.message(Command("prasp")) @saving - async def send_schedule(message: types.Message): + async def send_pschedule(message: types.Message): """Отправка расписания""" if is_chat_spam(message.chat.id, state): await message.answer("НЕ СПАМЬТЕ!!!") diff --git a/services/schedule_service.py b/services/schedule_service.py index 847880c..f386c64 100644 --- a/services/schedule_service.py +++ b/services/schedule_service.py @@ -92,8 +92,8 @@ class ScheduleService: return result, url, day, month - - def exact_group_regex(self, group: str) -> re.Pattern: + @staticmethod + def exact_group_regex(group: str) -> re.Pattern: # ищем как отдельный токен: граница слева/справа или начало/конец pattern = rf"(^|{BOUNDARY}){re.escape(group)}({BOUNDARY}|$)" return re.compile(pattern) diff --git a/utils/analytics.py b/utils/analytics.py index fc57e15..1223385 100644 --- a/utils/analytics.py +++ b/utils/analytics.py @@ -157,16 +157,15 @@ def analyze_bot_logs(log_file_path="bot.log"): return stats -def calculate_duration_hours(start_str, end_str): +def calculate_duration_hours(start_str: str, end_str: str) -> float: """Вычисляет продолжительность в часах""" + fmt = "%Y-%m-%d %H:%M:%S" try: - fmt = "%Y-%m-%d %H:%M:%S" start = datetime.strptime(start_str, fmt) end = datetime.strptime(end_str, fmt) return round((end - start).total_seconds() / 3600, 2) - except: - return 0 - + except (ValueError, TypeError): + return 0.0 def calculate_success_rate(stats): """Рассчитывает процент успешных операций"""