import os import asyncio import logging import sys # Добавляем путь к корню проекта sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from trueconf import Bot, Dispatcher, Router, Message from trueconf.types import FSInputFile # Наши обработчики from photo_processor import prepare_ad_photo from ad_helper import upload_photo_to_ad # Настройки from config.config import * # --- НАСТРОЙКА ЛОГИРОВАНИЯ --- logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler("photo_bot_log.txt", encoding="utf-8"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) router = Router() tc_bot = None # 🧠 ПАМЯТЬ БОТА: Храним пути к фото, ожидающим подтверждения # Пример: {'ds.krivochenko': '/tmp/ready_ds.krivochenko.jpg'} pending_photos = {} @router.message() async def handle_message(msg: Message): global tc_bot # Определяем логин пользователя user_login = msg.from_user.id.split('@')[0] if '@' in msg.from_user.id else msg.from_user.id # Проверяем, вложение ли это is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT" # ===================================================================== # СЦЕНАРИЙ 1: Бот ждет ответа, и пользователь написал текст (Да/Нет) # ===================================================================== if not is_attachment and user_login in pending_photos and msg.text: answer = msg.text.strip().lower() # Если согласен if answer in ["да", "yes", "+", "загрузить", "давай", "конечно"]: photo_path = pending_photos[user_login] await msg.answer("⏳ Подключаюсь к Active Directory и загружаю фото...") # Запускаем скрипт загрузки в AD upload_result = await asyncio.to_thread(upload_photo_to_ad, user_login, photo_path) if upload_result["success"]: await msg.answer( "🎉 **Ура! Фото успешно установлено в Active Directory!**\n\n" "Оно появится в Outlook, TrueConf и других системах в течение нескольких часов (после синхронизации серверов)." ) else: await msg.answer(f"❌ **Ошибка при загрузке:** {upload_result.get('error')}") # Убираем за собой файл и стираем пользователя из памяти if os.path.exists(photo_path): os.remove(photo_path) del pending_photos[user_login] return # Если отказался elif answer in ["нет", "no", "-", "отмена"]: photo_path = pending_photos[user_login] await msg.answer("🚫 Загрузка отменена. Можете прислать мне другую фотографию!") if os.path.exists(photo_path): os.remove(photo_path) del pending_photos[user_login] return # Если написал что-то непонятное, но мы ждем ответа else: await msg.answer("❓ Пожалуйста, напишите **Да** (чтобы загрузить фото в AD) или **Нет** (чтобы отменить). Либо просто пришлите новую фотографию.") return # ===================================================================== # СЦЕНАРИЙ 2: Пользователь прислал новую фотографию # ===================================================================== if is_attachment: file_id = getattr(msg.content, "file_id", None) if not file_id: await msg.answer("❌ Не удалось получить файл. Попробуйте еще раз.") return ready_path = f"/tmp/ready_{user_login}.jpg" raw_path = None await msg.answer("⏳ Обрабатываю ваше фото, пожалуйста, подождите...") try: downloaded_file = await tc_bot.download_file_by_id(file_id) raw_path = downloaded_file # Обрабатываем фото (с новым разрешением 240x240) result = await asyncio.to_thread(prepare_ad_photo, raw_path, ready_path, 240) if result["success"]: photo_document = FSInputFile(ready_path, filename=f"AD_{user_login}.jpg") # Отправляем фото и СРАЗУ задаем вопрос await msg.answer_photo( photo_document, photo_document, caption=( f"✅ **Фото готово!**\n\n" f"📐 Размер: 240x240 px\n" f"⚖️ Вес: {result['size_kb']} KB\n\n" f"❓ **Вам нравится результат? Загружаем в Active Directory?**\n" f"Напишите мне в ответ **Да** или **Нет**." ) ) # 🧠 ЗАПОМИНАЕМ, что этот пользователь теперь в режиме ожидания ответа pending_photos[user_login] = ready_path logger.info(f"Фото подготовлено для {user_login}, ждем подтверждения.") else: await msg.answer(f"❌ Ошибка обработки: {result.get('error')}") except Exception as e: logger.error(f"Ошибка при работе с фото для {user_login}: {e}") await msg.answer("❌ Произошла системная ошибка при обработке.") finally: # ВАЖНО: Удаляем ТОЛЬКО оригинал от TrueConf (raw_path). # Готовое фото (ready_path) оставляем на диске, оно ждет ответа "Да"! if raw_path and os.path.exists(raw_path): try: os.remove(raw_path) except: pass return # ===================================================================== # СЦЕНАРИЙ 3: Дефолтное приветствие (если просто написали текст) # ===================================================================== await msg.answer( "👋 Привет! Я корпоративный фотограф для Active Directory.\n\n" "📸 **Просто отправь мне фото**, и я автоматически:\n" "1. Найду лицо на снимке и сделаю правильный кадр.\n" "2. Улучшу резкость и сожму до стандарта 240x240 px.\n" "3. Предложу загрузить результат прямо в твой профиль!" ) async def main(): global tc_bot logger.info(f"Запуск фото-бота на сервере {TC_SERVER}...") dp = Dispatcher() dp.include_router(router) tc_bot = Bot.from_credentials( server=TC_SERVER, username=TC_LOGIN, password=TC_PASSWORD, dispatcher=dp, verify_ssl=False ) await tc_bot.run() if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logger.info("Бот остановлен пользователем.")