diff --git a/.gitignore b/.gitignore index 8cf4112..851f65f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /database/tg-bot-database +/settings.ini +/myenv/ +/venv/ diff --git a/database/db.py b/database/db.py index d38600a..d5923bf 100644 --- a/database/db.py +++ b/database/db.py @@ -605,9 +605,11 @@ class BotDB: f"Запуск функции update_username_and_full_name: user_id={user_id}, username={username}, full_name={full_name}") try: self.connect() - self.cursor.execute("UPDATE our_users SET username = ?, full_name = ? WHERE user_id = ?", (username, full_name, user_id,)) + self.cursor.execute("UPDATE our_users SET username = ?, full_name = ? WHERE user_id = ?", + (username, full_name, user_id,)) self.conn.commit() - self.logger.info(f"Функция update_username_and_full_name. Данные пользователя: user_id={user_id} успешно обновлены") + self.logger.info( + f"Функция update_username_and_full_name. Данные пользователя: user_id={user_id} успешно обновлены") return True except sqlite3.Error as error: self.logger.error(f"Ошибка в функции update_username_and_full_name: {error}") @@ -823,18 +825,69 @@ class BotDB: finally: self.close() + def add_post_from_telegram_in_db(self, group_message_id, message_id, is_last_message_id, content_name, text): + self.logger.info( + f"Запуск функции add_post_from_telegram_in_db: group_message_id={group_message_id}, message_id={message_id}" + f", is_last_message_id={is_last_message_id}, content_name={content_name}, text={text}") + try: + self.connect() + self.cursor.execute( + "INSERT INTO posts_from_telegram (group_message_id, message_id, is_last_message_id, content_name, text)" + "VALUES (?, ?, ?, ?, ?)", (group_message_id, message_id, is_last_message_id, content_name, text)) + self.conn.commit() + self.logger.info(f"Функция add_post_from_telegram_in_db отработала успешно") + return True + except Exception as e: + self.logger.error(f"Ошибка в функции add_post_from_telegram_in_db при добавлении поста в базу данных: {e}") + return False + + def get_post_content_from_telegram_by_last_id(self, last_post_id: int): + self.logger.info( + f"Запуск функции get_post_content_from_telegram_by_last_id, идентификатор поста {last_post_id}") + try: + self.connect() + main_post_sql = self.cursor.execute("SELECT group_message_id " + "FROM posts_from_telegram WHERE message_id = ?", (last_post_id,)) + main_post = main_post_sql.fetchone()[0] + except Exception as e: + self.logger.error(f"Ошибка в функции get_post_content_from_telegram_by_last_id {str(e)}") + return False + try: + result = self.cursor.execute("SELECT content_name " + "FROM posts_from_telegram WHERE group_message_id = ?", (main_post,)) + post_content = result.fetchall() + self.logger.info(f"Функция get_post_content_from_telegram_by_last_id получила текст поста: {post_content}") + return post_content + finally: + self.close() + + def get_post_text_from_telegram_by_last_id(self, last_post_id: int): + self.logger.info(f"Запуск функции get_post_text_from_telegram_by_last_id, идентификатор поста {last_post_id}") + try: + self.connect() + result = self.cursor.execute("SELECT text " + "FROM posts_from_telegram WHERE message_id = ?", (last_post_id,)) + text = result.fetchone()[0] + self.logger.info(f"Функция get_post_text_from_telegram_by_last_id получила text") + return text + except Exception as e: + self.logger.error(f"Ошибка в функции get_post_text_from_telegram_by_last_id {str(e)}") + def add_audio_record(self, file_name, author_id, date_added, listen_count, file_id): """Добавляет информацию о войсе юзера в БД""" self.logger.info( - f"Запуск функции add_audio_record (file_name = {file_name}, author_id = {author_id}, date_added = {date_added}") + f"Запуск функции add_audio_record (file_name = {file_name}, author_id = {author_id}," + f" date_added = {date_added}") try: self.connect() result = self.cursor.execute( - "INSERT INTO `audio_message_reference` (file_name, author_id, date_added, listen_count, file_id) VALUES (?, ?, ?, ?, ?)", + "INSERT INTO `audio_message_reference` (file_name, author_id, date_added, listen_count, file_id) " + "VALUES (?, ?, ?, ?, ?)", (file_name, author_id, date_added, listen_count, file_id)) self.conn.commit() self.logger.info( - f"Аудио успешно добавлено в БД (file_name = {file_name}, author_id = {author_id}, date_added = {date_added}") + f"Аудио успешно добавлено в БД (file_name = {file_name}, author_id = {author_id}, " + f"date_added = {date_added}") return None except sqlite3.Error as error: print(error) @@ -871,7 +924,8 @@ class BotDB: try: self.connect() result = self.cursor.execute( - "SELECT `file_id` FROM `audio_message_reference` WHERE `author_id` = ? ORDER BY date_added DESC LIMIT 1", + "SELECT `file_id` FROM `audio_message_reference` WHERE `author_id` = ? " + "ORDER BY date_added DESC LIMIT 1", (user_id,)) return result.fetchone()[0] except sqlite3.Error as error: @@ -884,7 +938,11 @@ class BotDB: try: self.connect() result = self.cursor.execute( - "SELECT `file_name` FROM `audio_message_reference` WHERE `author_id` = ? ORDER BY date_added DESC LIMIT 1", + "SELECT `file_name` " + "FROM `audio_message_reference` " + "WHERE `author_id` = ? " + "ORDER BY date_added " + "DESC LIMIT 1", (user_id,)) return result.fetchone()[0] except sqlite3.Error as error: diff --git a/helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc b/helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..b532bc4 Binary files /dev/null and b/helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc differ diff --git a/helper_bot/handlers/callback/callback_handlers.py b/helper_bot/handlers/callback/callback_handlers.py index c3136aa..888b4e9 100644 --- a/helper_bot/handlers/callback/callback_handlers.py +++ b/helper_bot/handlers/callback/callback_handlers.py @@ -8,7 +8,7 @@ from helper_bot.keyboards.main import create_keyboard_with_pagination, get_reply create_keyboard_for_ban_reason from helper_bot.utils.base_dependency_factory import BaseDependencyFactory from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \ - get_banned_users_buttons, delete_user_blacklist, get_help_message_id, send_media_group_message + get_banned_users_buttons, delete_user_blacklist, get_help_message_id, send_media_group_with_caption from logs.custom_logger import logger callback_router = Router() @@ -31,8 +31,8 @@ BotDB = bdf.get_db() ) async def post_for_group(call: CallbackQuery, state: FSMContext): logger.info( - f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})') - if call.data == 'publish' and call.message.content_type == 'text' and call.message.text != "^": + f'Получен callback-запрос с действием: {call.data} от пользователя {call.from_user.full_name} (ID сообщения: {call.message.message_id})') + if call.message.content_type == 'text' and call.message.text != "^": try: await send_text_message(MAIN_PUBLIC, call.message, call.message.text) await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id) @@ -43,7 +43,7 @@ async def post_for_group(call: CallbackQuery, state: FSMContext): text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") logger.error(f'Ошибка при публикации текста в канал {MAIN_PUBLIC}: {str(e)}') await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3) - elif call.data == 'publish' and call.message.content_type == 'photo': + elif call.message.content_type == 'photo': try: await send_photo_message(MAIN_PUBLIC, call.message, call.message.photo[-1].file_id, call.message.caption) await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id) @@ -54,18 +54,18 @@ async def post_for_group(call: CallbackQuery, state: FSMContext): text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") logger.error(f'Ошибка при публикации фотографии в канал {MAIN_PUBLIC}: {str(e)}') await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3) - elif call.data == 'publish' and call.message.text == "^": - print('Попали куда надо') + elif call.message.text == "^": user_data = await state.get_data() - print(f'В CALLBACK ГЕТ ДАТА: {user_data}') - media_group_message_id = get_help_message_id(call.message.message_id, user_data) - print(f'ПОЛУЧАЮ МЕССАГ ГРУПП АЙДИ ИЗ ГЕТ ДАТЫ: {media_group_message_id}') - print(f'ИДЕНТИФИКАТОР СООБЩЕНИЯ У КОТОРОГО ЖМУ КОЛЛБЭК: {call.message.message_id}') - await call.bot.copy_message(chat_id=MAIN_PUBLIC, from_chat_id=GROUP_FOR_POST, message_id=media_group_message_id, reply_markup=None) - await call.bot.delete_message(chat_id=MAIN_PUBLIC, message_id=media_group_message_id) + # Получил идентификатор последнего предложенного сообщения из кэша + help_message = get_help_message_id(call.message.message_id, user_data) + media_group_id = help_message-1 + post_content = BotDB.get_post_content_from_telegram_by_last_id(media_group_id) + post_text = BotDB.get_post_text_from_telegram_by_last_id(media_group_id) + await send_media_group_with_caption(bot=call.bot, chat_id=MAIN_PUBLIC, post_content=post_content, post_text=post_text) await call.answer(text='Выложено!', show_alert=True, cache_time=3) + @callback_router.callback_query( F.data == "decline" ) diff --git a/helper_bot/handlers/private/__pycache__/main.cpython-312.pyc b/helper_bot/handlers/private/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..bb4cb4c Binary files /dev/null and b/helper_bot/handlers/private/__pycache__/main.cpython-312.pyc differ diff --git a/helper_bot/handlers/private/private_handlers.py b/helper_bot/handlers/private/private_handlers.py index f4695ba..7821d27 100644 --- a/helper_bot/handlers/private/private_handlers.py +++ b/helper_bot/handlers/private/private_handlers.py @@ -17,9 +17,10 @@ from helper_bot.middlewares.blacklist_middleware import BlacklistMiddleware from helper_bot.utils import messages from helper_bot.utils.base_dependency_factory import BaseDependencyFactory from helper_bot.utils.helper_func import get_first_name, get_text_message, send_text_message, send_photo_message, \ - process_photo_album, send_media_group_message, check_username_and_full_name + send_media_group_message, process_photo_album, check_username_and_full_name from logs.custom_logger import logger + private_router = Router() private_router.message.middleware(AlbumMiddleware()) @@ -189,20 +190,28 @@ async def suggest_router(message: types.Message, state: FSMContext, album: list await state.set_state("START") elif message.media_group_id is not None: post_caption = " " + + # Получаем сообщение и проверяем есть ли подпись. Если подпись есть, то преобразуем ее через функцию if album[0].caption: lower_caption = album[0].caption.lower() post_caption, is_anonymous = get_text_message(lower_caption, message.from_user.full_name, message.from_user.username) - media_group = process_photo_album(album, post_caption) - media_group_message_id = await send_media_group_message(GROUP_FOR_POST, message, - media_group) - print(f'Отправил в чат предложки сообщения, media_group_message_id:{media_group_message_id}\n\n') + + #Иначе обрабатываем фото и получаем медиагруппу + media_group = await process_photo_album(album, post_caption) + + #Отправляем медиагруппу в секретный чат + media_group_message_id = await send_media_group_message(GROUP_FOR_POST, message, media_group) sleep(0.2) + + #Получаем клавиатуру и отправляем еще одно текстовое сообщение с кнопками markup = get_reply_keyboard_for_post() help_message_id = await send_text_message(GROUP_FOR_POST, message, "^", markup) + + #Записываем в state идентификаторы текстового сообщения И последнего сообщения медиагруппы await state.update_data(media_group_message_id=media_group_message_id, help_message_id=help_message_id) - d = await state.get_data() - print(f'ЗАПИСАЛ В state.update_data {d}') + + #Получаем клавиатуру для пользователя, благодарим за пост, и возвращаем в дефолтное сообщение markup_for_user = get_reply_keyboard(BotDB, message.from_user.id) success_send_message = messages.get_message(get_first_name(message), 'SUCCESS_SEND_MESSAGE') await message.answer(success_send_message, reply_markup=markup_for_user) diff --git a/helper_bot/utils/__pycache__/helper_func.cpython-312.pyc b/helper_bot/utils/__pycache__/helper_func.cpython-312.pyc index 1ed1cd0..229091a 100644 Binary files a/helper_bot/utils/__pycache__/helper_func.cpython-312.pyc and b/helper_bot/utils/__pycache__/helper_func.cpython-312.pyc differ diff --git a/helper_bot/utils/helper_func.py b/helper_bot/utils/helper_func.py index 768b6e9..9dbd94b 100644 --- a/helper_bot/utils/helper_func.py +++ b/helper_bot/utils/helper_func.py @@ -1,9 +1,10 @@ from datetime import datetime, timedelta from aiogram import types -from aiogram.types import InputMediaPhoto +from aiogram.types import InputMediaPhoto, FSInputFile from helper_bot.utils.base_dependency_factory import BaseDependencyFactory +from logs.custom_logger import logger bdf = BaseDependencyFactory() @@ -39,7 +40,29 @@ def get_text_message(post_text: str, first_name: str, username: str): return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}', is_anonymous -def process_photo_album(album, post_caption: str = ''): +async def download_photo(message: types.Message, file_id: str): + """ + Скачивает фото по file_id из Telegram. + + Args: + message: сообщение + file_id: File ID фотографии. + filename: Имя файла, под которым будет сохранено фото. + + Returns: + Путь к сохраненному файлу, если файл был скачан успешно, иначе None. + """ + try: + file = await message.bot.get_file(file_id) + file_path = file.file_path + await message.bot.download_file(file_path=file_path, destination=file_path) + return file_path + except Exception as e: + logger.error(f"Ошибка скачивания фотографии: {e}") + return None + + +async def process_photo_album(album, post_caption: str = ''): """ Создает список InputMediaPhoto для альбома. @@ -51,13 +74,41 @@ def process_photo_album(album, post_caption: str = ''): Список InputMediaPhoto. """ photo_media = [] + #Циклом проходимся по собранному миддлварью объекту album for i, message in enumerate(album): - print(f'message_id в функции process_photo_album: {message.message_id}\n\n') - if i == 0: + file_id = message.photo[-1].file_id + file_path = await download_photo(message, file_id=file_id) + # Если это последняя фото в массиве, то добавляем подпись. Остальные фото просто преобразуем в InputMediaPhoto + # и формируем объект MediaGroup + if i == len(album) - 1: photo_media.append(InputMediaPhoto(media=message.photo[-1].file_id, caption=post_caption)) else: photo_media.append(InputMediaPhoto(media=message.photo[-1].file_id)) - return photo_media + return photo_media # Возвращаем идентификатор медиа-группы + + +async def add_in_db_media(sent_message, post_caption: str = ''): + """ + Идентификатор медиа-группы + + Args: + sent_message: sent_message объект из Telegram API. + post_caption: Текст подписи к первому фото. + + Returns: + Список InputMediaPhoto. + """ + media_group_message_id = sent_message[0].message_id # Получаем идентификатор медиа-группы + for i, message in enumerate(sent_message): + file_id = message.photo[-1].file_id + file_path = await download_photo(message, file_id=file_id) + if i == 0: + BotDB.add_post_from_telegram_in_db(media_group_message_id, message.message_id, 0, file_path, post_caption) + elif i == len(sent_message) - 1: + BotDB.add_post_from_telegram_in_db(media_group_message_id, message.message_id, 1, file_path, post_caption) + else: + BotDB.add_post_from_telegram_in_db(media_group_message_id, message.message_id, 0, file_path, post_caption) + return media_group_message_id async def send_media_group_message(chat_id: int, message: types.Message, media_group: list[InputMediaPhoto]): @@ -65,12 +116,37 @@ async def send_media_group_message(chat_id: int, message: types.Message, media_g chat_id=chat_id, media=media_group, ) - print(f'ПОЛНЫЙ ОБЪЕКТ ОТПРАВЛЕННЫХ СООБЩЕНИЙ : {sent_message}\n\n') + await add_in_db_media(sent_message, post_caption=sent_message[-1].caption) message_id = sent_message[-1].message_id - print(f'ИДЕНТИФИКАТОР ПОСЛЕДНГО СООБЩЕНИЯ: {message_id}') return message_id +async def send_media_group_with_caption(bot, chat_id: int, post_content: list[tuple[str]], post_text: str): + """ + Отправляет медиа-группу с подписью к последнему файлу. + + Args: + bot: Экземпляр бота aiogram. + chat_id: ID чата для отправки. + post_content: Список кортежей с путями к файлам. + post_text: Текст подписи. + """ + media = [] + for file_path in post_content: + try: + file = FSInputFile(path=file_path[0]) + media.append(types.InputMediaPhoto(media=file)) + except FileNotFoundError: + logger.error(f"Файл не найден: {file_path[0]}") + return + + # Добавляем подпись к последнему файлу + if media: + media[-1].caption = post_text + + await bot.send_media_group(chat_id=chat_id, media=media) + + async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None): if markup is None: sent_message = await message.bot.send_message( @@ -160,7 +236,7 @@ def get_banned_users_buttons(): def get_help_message_id(media_group_message_id: int, data: dict) -> int: """ - Получает идентификатор сообщения помощи по идентификатору сообщения группы. + Получает идентификатор текстового сообщения по идентификатору сообщения группы. Args: media_group_message_id: Идентификатор сообщения группы @@ -169,9 +245,8 @@ def get_help_message_id(media_group_message_id: int, data: dict) -> int: Returns: Идентификатор сообщения помощи. """ - - if 'help_message_id' in data and 'media_group_message_id' in data: - return data['media_group_message_id'] + if 'help_message_id' in data: + return data['help_message_id'] # Возвращаем help_message_id else: return 0 @@ -188,7 +263,6 @@ def check_username_and_full_name(user_id: int, username: str, full_name: str): def unban_notifier(self): # Получение сегодняшней даты в формате DD-MM-YYYY current_date = datetime.now() - print('Мы в функции unban_notifier') today = current_date.strftime("%d-%m-%Y") # Получение списка разблокированных пользователей unblocked_users = self.BotDB.get_users_for_unblock_today(today) diff --git a/run_helper.py b/run_helper.py index ad7b9df..efa1bb1 100644 --- a/run_helper.py +++ b/run_helper.py @@ -4,4 +4,4 @@ from helper_bot.utils.base_dependency_factory import BaseDependencyFactory if __name__ == '__main__': - asyncio.run(start_bot(BaseDependencyFactory())) + asyncio.run(start_bot(BaseDependencyFactory())) \ No newline at end of file