Appearance
question:(.venv) PS C:UsersАскарPycharmProjectsuust> pip install imaplib email re ERROR: Could not find a version that satisfies the requirement imaplib (from versions: none) ERROR: No matching distribution found for imaplib
answer:Ошибки, которые вы видите, происходят из-за того, что модули `imaplib` и `email`, а также `re` являются частью стандартной библиотеки Python, и они поставляются вместе с интерпретатором Python. Поэтому их не нужно устанавливать с помощью `pip`. Эти модули можно использовать в любом скрипте Python, просто импортировав их, как показано ниже: ```python import imaplib import email import re ``` Убедитесь, что в вашем коде где-то в начале файла (обычно после линии shebang и перед основным кодом) вы добавили эти импорты. Никаких дальнейших действий для установки не требуется. Соответственно, вам необходимо удалить попытки установки этих пакетов через `pip`, так как это вызовет ошибки, подобные тем, которые вы сообщили. Продолжайте использовать эти модули в вашем проекте, просто убедитесь, что они правильно импортированы в ваших скриптах.
question:Traceback (most recent call last): File "C:UsersАскарPycharmProjectsuustmain.py", line 357, in <module> @dp.message_handler(state=EmailStates.waiting_for_email_data) ^^^^^^^^^^^ NameError: name 'EmailStates' is not defined Вот код моего итогового бота: import logging from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageDraw import random from io import BytesIO import aiohttp from aiogram import Bot, Dispatcher, executor, types from aiogram.types import ReplyKeyboardMarkup, KeyboardButton from aiogram.contrib.middlewares.logging import LoggingMiddleware from aiogram.dispatcher import FSMContext from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.dispatcher.filters import CommandStart from aiogram.contrib.fsm_storage.memory import MemoryStorage import aiosqlite import asyncio import time import datetime import math import os import re import imaplib import email from email.parser import BytesParser from email.policy import default API_TOKEN = '6796504222:AAFpaCTHBnBwtnLcSMW371gGNmPiWDVPp6U' ADMIN_ID = 989037374 # Замените на ваш телеграм ID админа logging.basicConfig(level=logging.INFO) bot = Bot(token=API_TOKEN) dp = Dispatcher(bot, storage=MemoryStorage()) dp.middleware.setup(LoggingMiddleware()) # Состояния для загрузки фото class UploadState(StatesGroup): waiting_for_photo = State() class PhotoState(StatesGroup): waiting_for_user_photo = State() # Подключаемся к БД async def create_db_tables(): conn = await get_db_connection() await conn.execute("""CREATE TABLE IF NOT EXISTS whitelist ( user_id INTEGER PRIMARY KEY, subscription_end INTEGER, backgrounds_changed INTEGER DEFAULT 0 ); """) await conn.execute("""CREATE TABLE IF NOT EXISTS photos ( id INTEGER PRIMARY KEY AUTOINCREMENT, photo_id TEXT NOT NULL ); """) await conn.execute(""" CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); """) await conn.execute(""" CREATE TABLE IF NOT EXISTS templates ( user_id INTEGER NOT NULL, photo_ids TEXT, UNIQUE(user_id) ); """) await conn.execute("""CREATE TABLE IF NOT EXISTS emails ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL, password TEXT NOT NULL );""") await conn.commit() await conn.close() async def add_email_to_db(email, password): conn = await get_db_connection() await conn.execute("INSERT INTO emails (email, password) VALUES (?, ?)", (email, password,)) await conn.commit() await conn.close() async def get_db_connection(): conn = await aiosqlite.connect('bot.db') await conn.execute('PRAGMA busy_timeout = 15000') return conn # Инициализируем БД @dp.message_handler(commands=['init'], state="*") async def init_db(message: types.Message): if message.from_user.id == ADMIN_ID: conn = await get_db_connection() await conn.execute("CREATE TABLE IF NOT EXISTS whitelist (user_id INTEGER PRIMARY KEY)") await conn.execute( "CREATE TABLE IF NOT EXISTS photos (id INTEGER PRIMARY KEY AUTOINCREMENT, photo_id TEXT NOT NULL)") await conn.commit() await conn.close() await message.answer("БД инициализирована.") else: await message.answer("Недостаточно прав.") @dp.message_handler(commands=['count'], user_id=ADMIN_ID) async def count_backgrounds(message: types.Message): conn = await get_db_connection() cur = await conn.execute("SELECT COUNT(*) FROM photos") count = await cur.fetchone() await conn.close() if count is not None: await message.answer(f"Количество доступных фонов в БД: **{count[0]}**",parse_mode='MarkdownV2') else: await message.answer("Не удалось получить количество фонов.") # Добавляем пользователя в whitelist @dp.message_handler(commands=['add'], state="*") async def add_to_whitelist(message: types.Message): if message.from_user.id == ADMIN_ID: args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() subscription_end = int(time.time()) + 30 * 86400 # 30 days in seconds await conn.execute( "INSERT OR IGNORE INTO whitelist (user_id, subscription_end) VALUES (?, ?)", (user_id, subscription_end) ) await conn.commit() await conn.close() await message.answer(f"Пользователь {user_id} добавлен в whitelist на 30 дней.") else: await message.answer("Неверный формат команды. Используйте /add user_id.") else: await message.answer("Недостаточно прав.") @dp.message_handler(commands=['info'], user_id=ADMIN_ID) async def info_subscription(message: types.Message): args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() cur = await conn.execute( "SELECT subscription_end, backgrounds_changed FROM whitelist WHERE user_id = ?", (user_id,) ) user_info = await cur.fetchone() if user_info: subscription_end = datetime.datetime.fromtimestamp(user_info[0]) await message.answer( f"Пользователь {user_id} имеет подписку до {subscription_end} и сменил фон {user_info[1]} раз." ) else: await message.answer("Пользователь не найден в whitelist.") await conn.close() else: await message.answer("Неверный аргумент команды. Используйте /info user_id.") @dp.message_handler(commands=['plus'], user_id=ADMIN_ID) async def add_subscription_days(message: types.Message): args = message.get_args().split() if len(args) == 2 and args[0].isdigit() and args[1].isdigit(): user_id = int(args[0]) days_to_add = int(args[1]) conn = await get_db_connection() # Добавляем дни к существующей подписке await conn.execute(""" UPDATE whitelist SET subscription_end = subscription_end + (? * 86400) WHERE user_id = ? """, (days_to_add, user_id)) await conn.commit() await conn.close() await message.answer(f"Пользователю {user_id} продлена подписка на {days_to_add} дней.") else: await message.answer("Неверный формат команды. Используйте /plus user_id days.") @dp.message_handler(commands=['minus'], user_id=ADMIN_ID) async def subtract_subscription_days(message: types.Message): args = message.get_args().split() if len(args) == 2 and args[0].isdigit() and args[1].isdigit(): user_id = int(args[0]) days_to_subtract = int(args[1]) conn = await get_db_connection() # Отнимаем дни от существующей подписки await conn.execute(""" UPDATE whitelist SET subscription_end = subscription_end - (? * 86400) WHERE user_id = ? AND subscription_end - (? * 86400) >= strftime('%s', 'now') """, (days_to_subtract, user_id, days_to_subtract)) affected_rows = conn.total_changes await conn.commit() await conn.close() if affected_rows: await message.answer(f"У пользователя {user_id} убрано {days_to_subtract} дней подписки.") else: await message.answer(f"Не удалось убрать дни подписки у пользователя {user_id}. " "Проверьте базу данных или аргументы команды.") else: await message.answer("Неверный формат команды. Используйте /minus user_id days.") @dp.message_handler(commands=['delete'], user_id=ADMIN_ID) async def delete_from_whitelist(message: types.Message): args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() # Удаляем пользователя из whitelist await conn.execute("DELETE FROM whitelist WHERE user_id = ?", (user_id,)) await conn.commit() await conn.close() await message.answer(f"Пользователь {user_id} удален из whitelist.") else: await message.answer("Неверный формат команды. Используйте /delete user_id.") @dp.message_handler(commands=['list'], user_id=ADMIN_ID) async def list_whitelisted_users(message: types.Message): conn = await get_db_connection() # Получаем список всех пользователей с активной подпиской cur = await conn.execute(""" SELECT user_id, subscription_end FROM whitelist WHERE subscription_end > strftime('%s', 'now') """) users = await cur.fetchall() await conn.close() # Формируем и отправляем ответ if users: user_list = [] for user_id, subscription_end in users: subscription_end_date = datetime.datetime.fromtimestamp(int(subscription_end)).strftime('%Y-%m-%d %H:%M:%S') user_list.append(f"ID: {user_id}, подписка до: {subscription_end_date}") users_info = 'n'.join(user_list) await message.answer(f"Пользователи с активной подпиской:n{users_info}") else: await message.answer("Нет пользователей с активной подпиской.") @dp.message_handler(commands=['post'], user_id=ADMIN_ID) async def post_to_all_users(message: types.Message): args = message.get_args() if not args: await message.answer("Пожалуйста, укажите текст сообщения после команды /post.") return conn = await get_db_connection() # Предполагаем, что в таблице users есть столбец user_id cur = await conn.execute("SELECT user_id FROM users") users = await cur.fetchall() await conn.close() for user_id_tuple in users: try: # Отправляем сообщение каждому пользователю await bot.send_message(user_id_tuple[0], args) except Exception as e: # Возможно, следует обработать исключения, например, если пользователь заблокировал бота logging.exception(e) await message.answer("Сообщения отправлены.") # Старт загрузки фото @dp.message_handler(commands=['upload'], state="*") async def start_upload(message: types.Message): if message.from_user.id == ADMIN_ID: await UploadState.waiting_for_photo.set() await message.answer("Загрузите фото одним сообщением. Когда закончите, введите /ustop") else: await message.answer("Недостаточно прав.") # Обработка загружаемых фото @dp.message_handler(content_types=['photo'], state=UploadState.waiting_for_photo) async def handle_photo(message: types.Message, state: FSMContext): if message.from_user.id == ADMIN_ID: photo_id = message.photo[-1].file_id conn = await get_db_connection() await conn.execute("INSERT INTO photos (photo_id) VALUES (?)", (photo_id,)) await conn.commit() await conn.close() # Остановка загрузки фото @dp.message_handler(commands=['ustop'], state=UploadState.waiting_for_photo) async def stop_upload(message: types.Message, state: FSMContext): if message.from_user.id == ADMIN_ID: await state.finish() await message.answer("Загрузка фотографий завершена.") else: await message.answer("Недостаточно прав.") # Очистка базы данных фотографий @dp.message_handler(commands=['clear'], state="*") async def clear_photos(message: types.Message): if message.from_user.id == ADMIN_ID: conn = await get_db_connection() await conn.execute("DELETE FROM photos") await conn.commit() await conn.close() await message.answer("Все фотографии удалены.") else: await message.answer("Недостаточно прав.") keyboard = ReplyKeyboardMarkup(resize_keyboard=True) keyboard.add(KeyboardButton("🖼 Поменять фон")) keyboard.add(KeyboardButton("🧑💻 Профиль")) keyboard.add(KeyboardButton("📤 Добавить почты")) keyboard.add(KeyboardButton("📭 Получить письма")) # Отправляем клавиатуру пользователю @dp.message_handler(CommandStart(), state="*") async def send_welcome(message: types.Message): user_id = message.from_user.id chat_id = '-1001904990371' conn = await get_db_connection() # Добавляем пользователя в таблицу users, если его там еще нет await conn.execute(""" INSERT OR IGNORE INTO users (user_id) VALUES (?) """, (user_id,)) # Проверяем, есть ли пользователь в whitelist cur = await conn.execute(""" SELECT user_id FROM whitelist WHERE user_id = ? """, (user_id,)) user_info = await cur.fetchone() if user_info: await message.reply("👋 **Привет!** Нажми на кнопку ниже для смены фона.", reply_markup=keyboard, parse_mode='MarkdownV2') else: await message.reply("😭 **Вы не находитесь в whitelist.** Для приобретения доступа напишите @ih82seeucry",parse_mode='MarkdownV2') await conn.commit() await conn.close() # Обработчик добавления почты @dp.message_handler(lambda message: message.text == "📤 Добавить почты", state="*") async def request_email_data(message: types.Message): await EmailStates.waiting_for_email_data.set() await message.answer("Введите адреса электронной почты и пароли в формате mail:password, каждый с новой строки.") # Обработка сообщений с данными почты от пользователя @dp.message_handler(state=EmailStates.waiting_for_email_data) async def add_email(message: types.Message, state: FSMContext): # Обрабатываем входящее сообщение с почтами lines = message.text.strip().split('n') for line in lines: try: email, password = line.split(':') # Сохраняем в БД await add_email_to_db(email, password) await message.answer(f"Почтовый ящик {email} добавлен.") except ValueError: await message.answer("Некорректный формат. Используйте формат 'mail:password'.") # Завершаем состояние приема данных о почте await state.finish() async def extract_code_from_email(email_body): match = re.search(r'bd{6}b', email_body) return match.group(0) if match else None # Функция для попытки получить код async def try_to_get_code(message, state, user_email): timeout = 120 # Таймаут в секундах end_time = time.time() + timeout code_found = False while time.time() < end_time: emails_messages = await get_user_emails(user_email) if isinstance(emails_messages, str): await asyncio.sleep(10) # Ошибка IMAP - повторяем через 10 секунд continue if emails_messages: # Получаем последнее письмо last_email_body = emails_messages[-1].get_body(preferencelist=('plain', 'html')).get_content() code = await extract_code_from_email(last_email_body) if code: await message.answer(f"Ваш код: {code}") await state.finish() return else: # Код не найден, отправляем содержимое письма await message.answer("Код не найден.Вотсодержимое последнего письма:") await send_long_message(message, last_email_body) break # Завершаем цикл, если письмо отправлено await asyncio.sleep(10) # Перерыв на 10 секунд if not code_found: # Если код так и не был найден await message.answer("Не удалось получить код в течение заданного времени.") await state.finish() async def send_long_message(message: types.Message, long_text: str, part_length: int = 4096): for part in [long_text[i:i + part_length] for i in range(0, len(long_text), part_length)]: await message.answer(part) # Метод для получения писем пользователя async def get_user_emails(email): async def get_user_emails(email): conn = await get_db_connection() async with db.execute("SELECT email, password FROM emails WHERE email = ?", (email,)) as cursor: user_email = await cursor.fetchone() if user_email: mail_server = imaplib.IMAP4_SSL('imap.rambler.ru', 993) try: mail_server.login(user_email[0], user_email[1]) mail_server.select('inbox') result, data = mail_server.uid('search', None, "ALL") if result == 'OK': emails = data[0].split()[-5:] return await fetch_emails_from_uids(mail_server, emails) except imaplib.IMAP4.error as e: return f"Ошибка IMAP: {str(e)}" finally: try: # Добавьте обработку ошибок для закрытия соединения mail_server.logout() except imaplib.IMAP4.abort: # Или используйте общий класс исключений если необходимо pass else: return "Не удалось найти данные для этой почты в БД." await conn.close() # Получение писем по их UID async def fetch_emails_from_uids(mail_server, email_uids): email_messages = [] parser = BytesParser(policy=default) for email_uid in email_uids: result, data = mail_server.uid('fetch', email_uid, '(RFC822)') if result == 'OK': email_data = data[0][1] msg = parser.parsebytes(email_data) email_messages.append(msg) return email_messages class EmailStates(StatesGroup): waiting_for_email_data = State() waiting_for_email_to_fetch = State() # Обработчик команды "Получить письма", который запрашивает данные @dp.message_handler(lambda message: message.text == "📭 Получить письма", state="*") async def request_to_fetch_emails(message: types.Message): await EmailStates.waiting_for_email_to_fetch.set() await message.answer("Введите адрес электронной почты, чтобы получить последние письма.") # Обработчик получения адреса электронной почты для получения писем @dp.message_handler(state=EmailStates.waiting_for_email_to_fetch) async def fetch_emails_handler(message: types.Message, state: FSMContext): user_email = message.text.strip() # Отправляем сообщение пользователю о начале поиска кода await message.answer("Ищем код в вашей почте, это может занять до двух минут…") asyncio.create_task(try_to_get_code(message, state, user_email)) # Функция для изменения размера фотографии пользователя def resize_user_photo(user_photo, base_width, base_height, padding=70): new_width = base_width - padding * 2 new_height = base_height - padding * 2 user_photo.thumbnail((new_width, new_height), Image.Resampling.LANCZOS) return user_photo async def download_photo(file_id): async with aiohttp.ClientSession() as session: file_path = await bot.get_file(file_id) photo_url = f"https://api.telegram.org/file/bot{API_TOKEN}/{file_path.file_path}" async with session.get(photo_url) as response: if response.status == 200: return await response.read() response.raise_for_status() class TemplateState(StatesGroup): waiting_for_template_photos = State() @dp.message_handler(lambda message: message.text == "🧑💻 Профиль", state="*") async def user_profile(message: types.Message): user_id = message.from_user.id conn = await get_db_connection() # Получаем информацию о подписке пользователя cur = await conn.execute(""" SELECT subscription_end FROM whitelist WHERE user_id = ? """, (user_id,)) subscription_info = await cur.fetchone() await conn.close() if subscription_info: # Пользователь в whitelist, преобразуем timestamp в читаемый формат subscription_end = datetime.datetime.fromtimestamp( int(subscription_info[0]) ).strftime('%Y-%m-%d %H:%M:%S') escaped_end = subscription_end.replace('-', '-').replace('.', '.') await message.answer(f"**🧑💻 Ваш профиль**nn**Ваш user id:** `{user_id}`n**Время окончания подписки:** {escaped_end}", parse_mode='MarkdownV2') else: # Пользователь не в whitelist await message.answer(f"Ваш user id: `{user_id}`n**У вас нет активной подписки**. Для приобретения напишите @ih82seeucry", parse_mode='MarkdownV2') @dp.message_handler(commands=['admin'], user_id=ADMIN_ID) async def admin_help(message: types.Message): admin_commands = ( "/init - Инициализировать базу данных бота", "/count - Узнать количество фонов в базе данных", "/add [user_id] - Добавить пользователя в whitelist на 30 дней", "/info [user_id] - Получить информацию о подписке пользователя", "/plus [user_id] [days] - Добавить дополнительные дни к подписке пользователя", "/minus [user_id] [days] - Убавить дни подписки пользователя", "/delete [user_id] - Удалить пользователя из whitelist", "/list - Показать список всех пользователей с их подписками", "/post - Отправить сообщение всем пользователям", "/clear - Удалить все фотографии из базы данных", "/upload - Начать загрузку фотографий для фона", "/ustop - Закончить загрузку фотографий для фона", ) await message.answer("n".join(admin_commands)) # Начало процесса смены фона @dp.message_handler(lambda message: message.text == "🖼 Поменять фон", state="*") async def change_background(message: types.Message, state: FSMContext): await PhotoState.waiting_for_user_photo.set() # Отправляем сообщение и ждем пользовательское фото await message.reply("**Загрузите ваши фото одним сообщением.** Если бот не ответил, пропишите /start", reply_markup=types.ReplyKeyboardRemove(),parse_mode='MarkdownV2') # Запоминаем, что пользователь начал процесс смены фона await state.update_data(changing_background=True) # Обновленная функция накладывания фотографий @dp.message_handler(content_types=['photo'], state=PhotoState.waiting_for_user_photo) async def overlay_photo_on_background(message: types.Message, state: FSMContext): await message.reply("**Обрабатываю фотографии.** Подождите, пожалуйста.", parse_mode='MarkdownV2') user_photo_id = message.photo[-1].file_id conn = await get_db_connection() # Получаем случайное фото фона из БД cur = await conn.execute("SELECT id, photo_id FROM photos ORDER BY RANDOM() LIMIT 1") photo_info = await cur.fetchone() if photo_info is None: await message.answer("Нет доступных фоновых изображений.") await state.finish() return background_photo_id = photo_info[1] # Скачиваем фото фона и пользовательское фото background_data = await download_photo(background_photo_id) user_photo_data = await download_photo(user_photo_id) with Image.open(BytesIO(background_data)) as background, Image.open(BytesIO(user_photo_data)) as user_photo: # Пропорции и размеры для пользовательского фото с учетом отступов padding = 70 new_user_width = user_photo.width + padding * 2 new_user_height = user_photo.height + padding * 2 user_photo.thumbnail((new_user_width, new_user_height), Image.Resampling.LANCZOS) background = background.convert("RGBA") # Рассчитываем новые пропорции для фона new_background_width = max(background.width, new_user_width) new_background_height = max(background.height, new_user_height) # Растягиваем фон до новых размеров background = background.resize((new_background_width, new_background_height), Image.Resampling.LANCZOS) # Применяем размытие Гаусса на 20% к фону background = background.filter(ImageFilter.GaussianBlur(radius=background.size[0] * 0.006)) # Если пользовательское изображение не в формате RGBA, конвертируем его if user_photo.mode != 'RGBA': user_photo = user_photo.convert("RGBA") # Создаем маску прозрачности для альфа-канала mask = user_photo.split()[3] # Позиционируем пользовательское изображение по центру растянутого и размытого фона position = ((background.width - user_photo.width) // 2, (background.height - user_photo.height) // 2) background.paste(user_photo, position, user_photo) enhancer = ImageEnhance.Brightness(background) factor = random.uniform(0.7, 1.25) background = enhancer.enhance(factor) # Создание серых полос draw = ImageDraw.Draw(background) num_lines = random.randint(10, 20) # Случайное число полос от 10 до 20 for _ in range(num_lines): # Используем num_lines для случайного количества полос # Определяем длину полосы length = random.randint(150, 300) # Определяем угол наклона в радианах angle = random.uniform(0, 2 * math.pi) # Определяем произвольное начальное положение для полосы start_x = random.randint(0, background.width) start_y = random.randint(0, background.height) # Вычисляем конечное положение для полосы end_x = start_x + int(length * math.cos(angle)) end_y = start_y + int(length * math.sin(angle)) # Рисуем линию с полупрозрачностью draw.line((start_x, start_y, end_x, end_y), fill=(128, 128, 128, 60), width=1) del draw draw = ImageDraw.Draw(background) num_color_lines = random.randint(30, 45) # Случайное число разноцветных полос for _ in range(num_color_lines): # Итерации для разноцветных полос # Генерируем случайные RGB-значения для цвета r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) # Определяем длину полосы length = random.randint(5, 7) # Определяем угол наклона в радианах angle = random.uniform(0, 2 * math.pi) # Определяем произвольное начальное положение для полосы starty_x = random.randint(0, background.width) starty_y = random.randint(0, background.height) # Вычисляем конечное положение для полосы endy_x = starty_x + int(length * math.cos(angle)) endy_y = starty_y + int(length * math.sin(angle)) # Рисуем разноцветную линию с полупрозрачностью draw.line((starty_x, starty_y, endy_x, endy_y), fill=(r, g, b, 165), width=4) # Удаляем объект рисования, так как он больше не нужен del draw # Создание белого шума noise_image = Image.new('RGB', background.size) noise_pixels = [ (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) for _ in range(background.width * background.height) ] noise_image.putdata(noise_pixels) # Конвертируем изображение шума в формат 'RGBA' и изменяем альфа-канал noise_image = noise_image.convert('RGBA') noise_alpha = Image.new('L', noise_image.size, color=55) # Альфа-канал с 10% непрозрачности (25 из 255) noise_image.putalpha(noise_alpha) # Накладываем белый шум на изображение background = Image.alpha_composite(background, noise_image) # Сохраняем результат в объект BytesIO result_image_io = BytesIO() background.save(result_image_io, format='PNG') result_image_io.seek(0) # Отправляем пользователю новое изображение await bot.send_photo(message.chat.id, photo=result_image_io) # Удаляем использованный фон из БД await conn.execute("DELETE FROM photos WHERE id = ?", (photo_info[0],)) await conn.commit() await conn.close() await state.finish() await message.answer("Фото обработано.", reply_markup=keyboard) async def subscription_check(): while True: # Бесконечный цикл try: conn = await get_db_connection() # Удаляем подписки, которые истекли await conn.execute(""" DELETE FROM whitelist WHERE subscription_end < strftime('%s', 'now') """) await conn.commit() await conn.close() except Exception as e: # Логгируем возможные ошибки при работе с базой данных logging.exception("Ошибка при проверке подписок: ", e) finally: # Задержка перед следующей проверкой - один час (3600 секунд) await asyncio.sleep(3600) async def main(): await create_db_tables() asyncio.create_task(subscription_check()) await dp.start_polling() if __name__ == '__main__': asyncio.run(main())
answer:Ошибка `NameError: name 'EmailStates' is not defined` указывает на то, что в вашем скрипте отсутствует определение класса `EmailStates` в момент, когда обработчики сообщений пытаются к нему обратиться. Для устранения ошибки необходимо убедиться, что класс `EmailStates` определён в коде до того, как используются его значения (`waiting_for_email_data`, `waiting_for_email_to_fetch` и т.д.). Похоже, что в вашем скрипте класс `EmailStates` определен в конце. Чтобы исправить ошибку, переместите определение класса `EmailStates` до первого использования его состояний. Например, определите его сразу после классов `UploadState` и `PhotoState`: ```python # Состояния для работы с электронной почтой class EmailStates(StatesGroup): waiting_for_email_data = State() waiting_for_email_to_fetch = State() # Ранее определённые состояния для загрузки фото class UploadState(StatesGroup): waiting_for_photo = State() class PhotoState(StatesGroup): waiting_for_user_photo = State() # Оставшийся код... ``` После этого убедитесь, что каждое состояние из `EmailStates` используется в соответствующих обработчиках на протяжении всего кода. Это изменение позволит обработчикам находить необходимый класс и его состояния во время выполнения и должно устранить ошибку. Затем запустите вашего бота снова и убедитесь, что данная ошибка была решена и бот работает правильно.
question:Бот не позволяет выйти из "Получить письма", не реагирует на другие кнопки в случае случайного нажатия и не проверяет, корректный ли это email адрес и, судя по всему. не проверяет, есть ли он в БД. Вот код бота: import logging from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageDraw import random from io import BytesIO import aiohttp from aiogram import Bot, Dispatcher, executor, types from aiogram.types import ReplyKeyboardMarkup, KeyboardButton from aiogram.contrib.middlewares.logging import LoggingMiddleware from aiogram.dispatcher import FSMContext from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.dispatcher.filters import CommandStart from aiogram.contrib.fsm_storage.memory import MemoryStorage import aiosqlite import asyncio import time import datetime import math import os import re import imaplib import email from email.parser import BytesParser from email.policy import default API_TOKEN = '6796504222:AAFpaCTHBnBwtnLcSMW371gGNmPiWDVPp6U' ADMIN_ID = 989037374 # Замените на ваш телеграм ID админа logging.basicConfig(level=logging.INFO) bot = Bot(token=API_TOKEN) dp = Dispatcher(bot, storage=MemoryStorage()) dp.middleware.setup(LoggingMiddleware()) # Состояния для загрузки фото class UploadState(StatesGroup): waiting_for_photo = State() class PhotoState(StatesGroup): waiting_for_user_photo = State() class EmailStates(StatesGroup): waiting_for_email_data = State() # Подключаемся к БД async def create_db_tables(): conn = await get_db_connection() await conn.execute("""CREATE TABLE IF NOT EXISTS whitelist ( user_id INTEGER PRIMARY KEY, subscription_end INTEGER, backgrounds_changed INTEGER DEFAULT 0 ); """) await conn.execute("""CREATE TABLE IF NOT EXISTS photos ( id INTEGER PRIMARY KEY AUTOINCREMENT, photo_id TEXT NOT NULL ); """) await conn.execute(""" CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); """) await conn.execute(""" CREATE TABLE IF NOT EXISTS templates ( user_id INTEGER NOT NULL, photo_ids TEXT, UNIQUE(user_id) ); """) await conn.execute("""CREATE TABLE IF NOT EXISTS emails ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL, password TEXT NOT NULL );""") await conn.commit() await conn.close() async def add_email_to_db(email, password): conn = await get_db_connection() await conn.execute("INSERT INTO emails (email, password) VALUES (?, ?)", (email, password,)) await conn.commit() await conn.close() async def get_db_connection(): conn = await aiosqlite.connect('bot.db') await conn.execute('PRAGMA busy_timeout = 15000') return conn # Инициализируем БД @dp.message_handler(commands=['init'], state="*") async def init_db(message: types.Message): if message.from_user.id == ADMIN_ID: conn = await get_db_connection() await conn.execute("CREATE TABLE IF NOT EXISTS whitelist (user_id INTEGER PRIMARY KEY)") await conn.execute( "CREATE TABLE IF NOT EXISTS photos (id INTEGER PRIMARY KEY AUTOINCREMENT, photo_id TEXT NOT NULL)") await conn.commit() await conn.close() await message.answer("БД инициализирована.") else: await message.answer("Недостаточно прав.") @dp.message_handler(commands=['count'], user_id=ADMIN_ID) async def count_backgrounds(message: types.Message): conn = await get_db_connection() cur = await conn.execute("SELECT COUNT(*) FROM photos") count = await cur.fetchone() await conn.close() if count is not None: await message.answer(f"Количество доступных фонов в БД: **{count[0]}**",parse_mode='MarkdownV2') else: await message.answer("Не удалось получить количество фонов.") # Добавляем пользователя в whitelist @dp.message_handler(commands=['add'], state="*") async def add_to_whitelist(message: types.Message): if message.from_user.id == ADMIN_ID: args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() subscription_end = int(time.time()) + 30 * 86400 # 30 days in seconds await conn.execute( "INSERT OR IGNORE INTO whitelist (user_id, subscription_end) VALUES (?, ?)", (user_id, subscription_end) ) await conn.commit() await conn.close() await message.answer(f"Пользователь {user_id} добавлен в whitelist на 30 дней.") else: await message.answer("Неверный формат команды. Используйте /add user_id.") else: await message.answer("Недостаточно прав.") @dp.message_handler(commands=['info'], user_id=ADMIN_ID) async def info_subscription(message: types.Message): args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() cur = await conn.execute( "SELECT subscription_end, backgrounds_changed FROM whitelist WHERE user_id = ?", (user_id,) ) user_info = await cur.fetchone() if user_info: subscription_end = datetime.datetime.fromtimestamp(user_info[0]) await message.answer( f"Пользователь {user_id} имеет подписку до {subscription_end} и сменил фон {user_info[1]} раз." ) else: await message.answer("Пользователь не найден в whitelist.") await conn.close() else: await message.answer("Неверный аргумент команды. Используйте /info user_id.") @dp.message_handler(commands=['plus'], user_id=ADMIN_ID) async def add_subscription_days(message: types.Message): args = message.get_args().split() if len(args) == 2 and args[0].isdigit() and args[1].isdigit(): user_id = int(args[0]) days_to_add = int(args[1]) conn = await get_db_connection() # Добавляем дни к существующей подписке await conn.execute(""" UPDATE whitelist SET subscription_end = subscription_end + (? * 86400) WHERE user_id = ? """, (days_to_add, user_id)) await conn.commit() await conn.close() await message.answer(f"Пользователю {user_id} продлена подписка на {days_to_add} дней.") else: await message.answer("Неверный формат команды. Используйте /plus user_id days.") @dp.message_handler(commands=['minus'], user_id=ADMIN_ID) async def subtract_subscription_days(message: types.Message): args = message.get_args().split() if len(args) == 2 and args[0].isdigit() and args[1].isdigit(): user_id = int(args[0]) days_to_subtract = int(args[1]) conn = await get_db_connection() # Отнимаем дни от существующей подписки await conn.execute(""" UPDATE whitelist SET subscription_end = subscription_end - (? * 86400) WHERE user_id = ? AND subscription_end - (? * 86400) >= strftime('%s', 'now') """, (days_to_subtract, user_id, days_to_subtract)) affected_rows = conn.total_changes await conn.commit() await conn.close() if affected_rows: await message.answer(f"У пользователя {user_id} убрано {days_to_subtract} дней подписки.") else: await message.answer(f"Не удалось убрать дни подписки у пользователя {user_id}. " "Проверьте базу данных или аргументы команды.") else: await message.answer("Неверный формат команды. Используйте /minus user_id days.") @dp.message_handler(commands=['delete'], user_id=ADMIN_ID) async def delete_from_whitelist(message: types.Message): args = message.get_args().split() if len(args) == 1 and args[0].isdigit(): user_id = int(args[0]) conn = await get_db_connection() # Удаляем пользователя из whitelist await conn.execute("DELETE FROM whitelist WHERE user_id = ?", (user_id,)) await conn.commit() await conn.close() await message.answer(f"Пользователь {user_id} удален из whitelist.") else: await message.answer("Неверный формат команды. Используйте /delete user_id.") @dp.message_handler(commands=['list'], user_id=ADMIN_ID) async def list_whitelisted_users(message: types.Message): conn = await get_db_connection() # Получаем список всех пользователей с активной подпиской cur = await conn.execute(""" SELECT user_id, subscription_end FROM whitelist WHERE subscription_end > strftime('%s', 'now') """) users = await cur.fetchall() await conn.close() # Формируем и отправляем ответ if users: user_list = [] for user_id, subscription_end in users: subscription_end_date = datetime.datetime.fromtimestamp(int(subscription_end)).strftime('%Y-%m-%d %H:%M:%S') user_list.append(f"ID: {user_id}, подписка до: {subscription_end_date}") users_info = 'n'.join(user_list) await message.answer(f"Пользователи с активной подпиской:n{users_info}") else: await message.answer("Нет пользователей с активной подпиской.") @dp.message_handler(commands=['post'], user_id=ADMIN_ID) async def post_to_all_users(message: types.Message): args = message.get_args() if not args: await message.answer("Пожалуйста, укажите текст сообщения после команды /post.") return conn = await get_db_connection() # Предполагаем, что в таблице users есть столбец user_id cur = await conn.execute("SELECT user_id FROM users") users = await cur.fetchall() await conn.close() for user_id_tuple in users: try: # Отправляем сообщение каждому пользователю await bot.send_message(user_id_tuple[0], args) except Exception as e: # Возможно, следует обработать исключения, например, если пользователь заблокировал бота logging.exception(e) await message.answer("Сообщения отправлены.") # Старт загрузки фото @dp.message_handler(commands=['upload'], state="*") async def start_upload(message: types.Message): if message.from_user.id == ADMIN_ID: await UploadState.waiting_for_photo.set() await message.answer("Загрузите фото одним сообщением. Когда закончите, введите /ustop") else: await message.answer("Недостаточно прав.") # Обработка загружаемых фото @dp.message_handler(content_types=['photo'], state=UploadState.waiting_for_photo) async def handle_photo(message: types.Message, state: FSMContext): if message.from_user.id == ADMIN_ID: photo_id = message.photo[-1].file_id conn = await get_db_connection() await conn.execute("INSERT INTO photos (photo_id) VALUES (?)", (photo_id,)) await conn.commit() await conn.close() # Остановка загрузки фото @dp.message_handler(commands=['ustop'], state=UploadState.waiting_for_photo) async def stop_upload(message: types.Message, state: FSMContext): if message.from_user.id == ADMIN_ID: await state.finish() await message.answer("Загрузка фотографий завершена.") else: await message.answer("Недостаточно прав.") # Очистка базы данных фотографий @dp.message_handler(commands=['clear'], state="*") async def clear_photos(message: types.Message): if message.from_user.id == ADMIN_ID: conn = await get_db_connection() await conn.execute("DELETE FROM photos") await conn.commit() await conn.close() await message.answer("Все фотографии удалены.") else: await message.answer("Недостаточно прав.") keyboard = ReplyKeyboardMarkup(resize_keyboard=True) keyboard.add(KeyboardButton("🖼 Поменять фон")) keyboard.add(KeyboardButton("🧑💻 Профиль")) keyboard.add(KeyboardButton("📤 Добавить почты")) keyboard.add(KeyboardButton("📭 Получить письма")) # Отправляем клавиатуру пользователю @dp.message_handler(CommandStart(), state="*") async def send_welcome(message: types.Message): user_id = message.from_user.id chat_id = '-1001904990371' conn = await get_db_connection() # Добавляем пользователя в таблицу users, если его там еще нет await conn.execute(""" INSERT OR IGNORE INTO users (user_id) VALUES (?) """, (user_id,)) # Проверяем, есть ли пользователь в whitelist cur = await conn.execute(""" SELECT user_id FROM whitelist WHERE user_id = ? """, (user_id,)) user_info = await cur.fetchone() if user_info: await message.reply("👋 **Привет!** Нажми на кнопку ниже для смены фона.", reply_markup=keyboard, parse_mode='MarkdownV2') else: await message.reply("😭 **Вы не находитесь в whitelist.** Для приобретения доступа напишите @ih82seeucry",parse_mode='MarkdownV2') await conn.commit() await conn.close() # Обработчик добавления почты @dp.message_handler(lambda message: message.text == "📤 Добавить почты", state="*") async def request_email_data(message: types.Message): await EmailStates.waiting_for_email_data.set() await message.answer("Введите адреса электронной почты и пароли в формате mail:password, каждый с новой строки.") # Обработка сообщений с данными почты от пользователя @dp.message_handler(state=EmailStates.waiting_for_email_data) async def add_email(message: types.Message, state: FSMContext): # Обрабатываем входящее сообщение с почтами lines = message.text.strip().split('n') for line in lines: try: email, password = line.split(':') # Сохраняем в БД await add_email_to_db(email, password) await message.answer(f"Почтовый ящик {email} добавлен.") except ValueError: await message.answer("Некорректный формат. Используйте формат 'mail:password'.") # Завершаем состояние приема данных о почте await state.finish() async def extract_code_from_email(email_body): match = re.search(r'bd{6}b', email_body) return match.group(0) if match else None # Функция для попытки получить код async def try_to_get_code(message, state, user_email): timeout = 120 # Таймаут в секундах end_time = time.time() + timeout code_found = False while time.time() < end_time: emails_messages = await get_user_emails(user_email) if isinstance(emails_messages, str): await asyncio.sleep(10) # Ошибка IMAP - повторяем через 10 секунд continue if emails_messages: # Получаем последнее письмо last_email_body = emails_messages[-1].get_body(preferencelist=('plain', 'html')).get_content() code = await extract_code_from_email(last_email_body) if code: await message.answer(f"Ваш код: {code}") await state.finish() return else: # Код не найден, отправляем содержимое письма await message.answer("Код не найден.Вотсодержимое последнего письма:") await send_long_message(message, last_email_body) break # Завершаем цикл, если письмо отправлено await asyncio.sleep(10) # Перерыв на 10 секунд if not code_found: # Если код так и не был найден await message.answer("Не удалось получить код в течение заданного времени.") await state.finish() async def send_long_message(message: types.Message, long_text: str, part_length: int = 4096): for part in [long_text[i:i + part_length] for i in range(0, len(long_text), part_length)]: await message.answer(part) # Метод для получения писем пользователя async def get_user_emails(email): async def get_user_emails(email): conn = await get_db_connection() async with db.execute("SELECT email, password FROM emails WHERE email = ?", (email,)) as cursor: user_email = await cursor.fetchone() if user_email: mail_server = imaplib.IMAP4_SSL('imap.rambler.ru', 993) try: mail_server.login(user_email[0], user_email[1]) mail_server.select('inbox') result, data = mail_server.uid('search', None, "ALL") if result == 'OK': emails = data[0].split()[-5:] return await fetch_emails_from_uids(mail_server, emails) except imaplib.IMAP4.error as e: return f"Ошибка IMAP: {str(e)}" finally: try: # Добавьте обработку ошибок для закрытия соединения mail_server.logout() except imaplib.IMAP4.abort: # Или используйте общий класс исключений если необходимо pass else: return "Не удалось найти данные для этой почты в БД." await conn.close() # Получение писем по их UID async def fetch_emails_from_uids(mail_server, email_uids): email_messages = [] parser = BytesParser(policy=default) for email_uid in email_uids: result, data = mail_server.uid('fetch', email_uid, '(RFC822)') if result == 'OK': email_data = data[0][1] msg = parser.parsebytes(email_data) email_messages.append(msg) return email_messages class EmailStates(StatesGroup): waiting_for_email_data = State() waiting_for_email_to_fetch = State() # Обработчик команды "Получить письма", который запрашивает данные @dp.message_handler(lambda message: message.text == "📭 Получить письма", state="*") async def request_to_fetch_emails(message: types.Message): await EmailStates.waiting_for_email_to_fetch.set() await message.answer("Введите адрес электронной почты, чтобы получить последние письма.") # Обработчик получения адреса электронной почты для получения писем @dp.message_handler(state=EmailStates.waiting_for_email_to_fetch) async def fetch_emails_handler(message: types.Message, state: FSMContext): user_email = message.text.strip() # Отправляем сообщение пользователю о начале поиска кода await message.answer("Ищем код в вашей почте, это может занять до двух минут…") asyncio.create_task(try_to_get_code(message, state, user_email)) # Функция для изменения размера фотографии пользователя def resize_user_photo(user_photo, base_width, base_height, padding=70): new_width = base_width - padding * 2 new_height = base_height - padding * 2 user_photo.thumbnail((new_width, new_height), Image.Resampling.LANCZOS) return user_photo async def download_photo(file_id): async with aiohttp.ClientSession() as session: file_path = await bot.get_file(file_id) photo_url = f"https://api.telegram.org/file/bot{API_TOKEN}/{file_path.file_path}" async with session.get(photo_url) as response: if response.status == 200: return await response.read() response.raise_for_status() class TemplateState(StatesGroup): waiting_for_template_photos = State() @dp.message_handler(lambda message: message.text == "🧑💻 Профиль", state="*") async def user_profile(message: types.Message): user_id = message.from_user.id conn = await get_db_connection() # Получаем информацию о подписке пользователя cur = await conn.execute(""" SELECT subscription_end FROM whitelist WHERE user_id = ? """, (user_id,)) subscription_info = await cur.fetchone() await conn.close() if subscription_info: # Пользователь в whitelist, преобразуем timestamp в читаемый формат subscription_end = datetime.datetime.fromtimestamp( int(subscription_info[0]) ).strftime('%Y-%m-%d %H:%M:%S') escaped_end = subscription_end.replace('-', '-').replace('.', '.') await message.answer(f"**🧑💻 Ваш профиль**nn**Ваш user id:** `{user_id}`n**Время окончания подписки:** {escaped_end}", parse_mode='MarkdownV2') else: # Пользователь не в whitelist await message.answer(f"Ваш user id: `{user_id}`n**У вас нет активной подписки**. Для приобретения напишите @ih82seeucry", parse_mode='MarkdownV2') @dp.message_handler(commands=['admin'], user_id=ADMIN_ID) async def admin_help(message: types.Message): admin_commands = ( "/init - Инициализировать базу данных бота", "/count - Узнать количество фонов в базе данных", "/add [user_id] - Добавить пользователя в whitelist на 30 дней", "/info [user_id] - Получить информацию о подписке пользователя", "/plus [user_id] [days] - Добавить дополнительные дни к подписке пользователя", "/minus [user_id] [days] - Убавить дни подписки пользователя", "/delete [user_id] - Удалить пользователя из whitelist", "/list - Показать список всех пользователей с их подписками", "/post - Отправить сообщение всем пользователям", "/clear - Удалить все фотографии из базы данных", "/upload - Начать загрузку фотографий для фона", "/ustop - Закончить загрузку фотографий для фона", ) await message.answer("n".join(admin_commands)) # Начало процесса смены фона @dp.message_handler(lambda message: message.text == "🖼 Поменять фон", state="*") async def change_background(message: types.Message, state: FSMContext): await PhotoState.waiting_for_user_photo.set() # Отправляем сообщение и ждем пользовательское фото await message.reply("**Загрузите ваши фото одним сообщением.** Если бот не ответил, пропишите /start", reply_markup=types.ReplyKeyboardRemove(),parse_mode='MarkdownV2') # Запоминаем, что пользователь начал процесс смены фона await state.update_data(changing_background=True) # Обновленная функция накладывания фотографий @dp.message_handler(content_types=['photo'], state=PhotoState.waiting_for_user_photo) async def overlay_photo_on_background(message: types.Message, state: FSMContext): await message.reply("**Обрабатываю фотографии.** Подождите, пожалуйста.", parse_mode='MarkdownV2') user_photo_id = message.photo[-1].file_id conn = await get_db_connection() # Получаем случайное фото фона из БД cur = await conn.execute("SELECT id, photo_id FROM photos ORDER BY RANDOM() LIMIT 1") photo_info = await cur.fetchone() if photo_info is None: await message.answer("Нет доступных фоновых изображений.") await state.finish() return background_photo_id = photo_info[1] # Скачиваем фото фона и пользовательское фото background_data = await download_photo(background_photo_id) user_photo_data = await download_photo(user_photo_id) with Image.open(BytesIO(background_data)) as background, Image.open(BytesIO(user_photo_data)) as user_photo: # Пропорции и размеры для пользовательского фото с учетом отступов padding = 70 new_user_width = user_photo.width + padding * 2 new_user_height = user_photo.height + padding * 2 user_photo.thumbnail((new_user_width, new_user_height), Image.Resampling.LANCZOS) background = background.convert("RGBA") # Рассчитываем новые пропорции для фона new_background_width = max(background.width, new_user_width) new_background_height = max(background.height, new_user_height) # Растягиваем фон до новых размеров background = background.resize((new_background_width, new_background_height), Image.Resampling.LANCZOS) # Применяем размытие Гаусса на 20% к фону background = background.filter(ImageFilter.GaussianBlur(radius=background.size[0] * 0.006)) # Если пользовательское изображение не в формате RGBA, конвертируем его if user_photo.mode != 'RGBA': user_photo = user_photo.convert("RGBA") # Создаем маску прозрачности для альфа-канала mask = user_photo.split()[3] # Позиционируем пользовательское изображение по центру растянутого и размытого фона position = ((background.width - user_photo.width) // 2, (background.height - user_photo.height) // 2) background.paste(user_photo, position, user_photo) enhancer = ImageEnhance.Brightness(background) factor = random.uniform(0.7, 1.25) background = enhancer.enhance(factor) # Создание серых полос draw = ImageDraw.Draw(background) num_lines = random.randint(10, 20) # Случайное число полос от 10 до 20 for _ in range(num_lines): # Используем num_lines для случайного количества полос # Определяем длину полосы length = random.randint(150, 300) # Определяем угол наклона в радианах angle = random.uniform(0, 2 * math.pi) # Определяем произвольное начальное положение для полосы start_x = random.randint(0, background.width) start_y = random.randint(0, background.height) # Вычисляем конечное положение для полосы end_x = start_x + int(length * math.cos(angle)) end_y = start_y + int(length * math.sin(angle)) # Рисуем линию с полупрозрачностью draw.line((start_x, start_y, end_x, end_y), fill=(128, 128, 128, 60), width=1) del draw draw = ImageDraw.Draw(background) num_color_lines = random.randint(30, 45) # Случайное число разноцветных полос for _ in range(num_color_lines): # Итерации для разноцветных полос # Генерируем случайные RGB-значения для цвета r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) # Определяем длину полосы length = random.randint(5, 7) # Определяем угол наклона в радианах angle = random.uniform(0, 2 * math.pi) # Определяем произвольное начальное положение для полосы starty_x = random.randint(0, background.width) starty_y = random.randint(0, background.height) # Вычисляем конечное положение для полосы endy_x = starty_x + int(length * math.cos(angle)) endy_y = starty_y + int(length * math.sin(angle)) # Рисуем разноцветную линию с полупрозрачностью draw.line((starty_x, starty_y, endy_x, endy_y), fill=(r, g, b, 165), width=4) # Удаляем объект рисования, так как он больше не нужен del draw # Создание белого шума noise_image = Image.new('RGB', background.size) noise_pixels = [ (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) for _ in range(background.width * background.height) ] noise_image.putdata(noise_pixels) # Конвертируем изображение шума в формат 'RGBA' и изменяем альфа-канал noise_image = noise_image.convert('RGBA') noise_alpha = Image.new('L', noise_image.size, color=55) # Альфа-канал с 10% непрозрачности (25 из 255) noise_image.putalpha(noise_alpha) # Накладываем белый шум на изображение background = Image.alpha_composite(background, noise_image) # Сохраняем результат в объект BytesIO result_image_io = BytesIO() background.save(result_image_io, format='PNG') result_image_io.seek(0) # Отправляем пользователю новое изображение await bot.send_photo(message.chat.id, photo=result_image_io) # Удаляем использованный фон из БД await conn.execute("DELETE FROM photos WHERE id = ?", (photo_info[0],)) await conn.commit() await conn.close() await state.finish() await message.answer("Фото обработано.", reply_markup=keyboard) async def subscription_check(): while True: # Бесконечный цикл try: conn = await get_db_connection() # Удаляем подписки, которые истекли await conn.execute(""" DELETE FROM whitelist WHERE subscription_end < strftime('%s', 'now') """) await conn.commit() await conn.close() except Exception as e: # Логгируем возможные ошибки при работе с базой данных logging.exception("Ошибка при проверке подписок: ", e) finally: # Задержка перед следующей проверкой - один час (3600 секунд) await asyncio.sleep(3600) async def main(): await create_db_tables() asyncio.create_task(subscription_check()) await dp.start_polling() if __name__ == '__main__': asyncio.run(main())
answer:Для того чтобы добавить функционал проверки корректности email, возможность выхода из состояния "Получить письма" и реагирование на другие кнопки, нам необходимо внести следующие изменения в вашего бота: 1. Добавить функцию, которая позволит пользователю отменить текущее действие и вернуться в начальное состояние. ```python @dp.message_handler(lambda message: message.text.lower() == 'отмена', state="*") async def cancel_action(message: types.Message, state: FSMContext): await state.finish() await message.answer("Действие отменено.", reply_markup=keyboard) ``` Добавьте эту кнопку "Отмена" в клавиатуру `keyboard`, чтобы пользователь всегда мог выйти из текущего состояния. 2. Проверка, является ли введенный текст правильным адресом электронной почты, и существует ли такой email в базе данных. ```python @dp.message_handler(state=EmailStates.waiting_for_email_to_fetch) async def fetch_emails_handler(message: types.Message, state: FSMContext): user_email = message.text.strip() if not re.match(r"[^@]+@[^@]+.[^@]+", user_email): await message.answer("Это не похоже на адрес электронной почты. Попробуйте снова.") return conn = await get_db_connection() cur = await conn.execute("SELECT email FROM emails WHERE email = ?", (user_email,)) email_exists = await cur.fetchone() await conn.close() if not email_exists: await message.answer("Данный адрес электронной почты не найден в базе данных. Попробуйте снова.") return # Если почта существует, продолжаем логику получения писем... ``` 3. Позволить боту реагировать на кнопки в любом состоянии, если текущее действие не требует специфической логики, относящейся к состоянию. ```python @dp.message_handler(lambda message: message.text in ['🖼 Поменять фон', '🧑💻 Профиль', '📤 Добавить почты'], state="*") async def handle_main_buttons(message: types.Message, state: FSMContext): if message.text == '🖼 Поменять фон': await change_background(message, state) elif message.text == '🧑💻 Профиль': await user_profile(message) elif message.text == '📤 Добавить почты': await request_email_data(message) else: await message.answer("Неизвестное действие.") ``` При добавлении этих изменений вам также нужно будет проверить, что все остальные стейты и обработчики сообщений корректно работают вместе с новой логикой. Не забудьте добавить кнопку "Отмена" в клавиатуру: ```python keyboard.add(KeyboardButton("Отмена")) ``` Теперь бот сможет обрабатывать запросы на более высоком уровне точности и дать пользователям возможность отменять действия, если они случайно выбрали неверные опции.
question:Лучше давай добавим inline-кнопки "Отмена" к сообщениям от бота при нажатии на кнопок
answer:Для добавления inline-клавиатуры с кнопкой "Отмена" к сообщениям от бота, нам нужно будет создать inline-клавиатуру и обработчики для неё. Первым делом создадим клавиатуру: ```python from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton # Создание inline-клавиатуры с кнопкой "Отмена" cancel_markup = InlineKeyboardMarkup().add(InlineKeyboardButton("Отмена", callback_data="cancel")) ``` Теперь мы можем использовать эту inline-клавиатуру в функциях, где пользователю нужно предложить возможность отменить действие: ```python from aiogram.dispatcher.filters import Text # Обработчик команды "Получить письма" @dp.message_handler(lambda message: message.text == "📭 Получить письма", state="*") async def request_to_fetch_emails(message: types.Message): await EmailStates.waiting_for_email_to_fetch.set() await message.answer("Введите адрес электронной почты, чтобы получить последние письма.", reply_markup=cancel_markup) # ... другие функции, где нужно предоставить возможность отмены ... ``` Теперь добавим обработчик для обработки callback запросов от inline-клавиш. Когда пользователь нажимает кнопку "Отмена", бот должен обработать это и выполнить соответствующее действие: ```python @dp.callback_query_handler(Text(equals="cancel", ignore_case=True), state="*") async def process_cancel_callback(callback_query: types.CallbackQuery, state: FSMContext): await state.finish() # Завершить текущее состояние в FSM await bot.answer_callback_query(callback_query.id) await bot.send_message(callback_query.from_user.id, "Действие отменено.", reply_markup=keyboard) ``` В этом коде, когда пользователь нажимает inline-кнопку "Отмена", текущее состояние диалога завершается, пользователю отправляется сообщение о том, что действие было отменено, и бот предлагает основную клавиатуру. Не забудьте также импортировать необходимые модули из библиотеки aiogram. После внесения этих изменений убедитесь, что бот по-прежнему корректно реагирует на другие команды и состояния. Всегда проводите тестирование после внесения изменений в код для проверки его работоспособности.