commit fa59858590da94f5a09623c30c7194c12366cb19 Author: Magnus Root Date: Wed Jul 30 15:39:20 2025 +0300 Переезд diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bdaab25 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +env/ diff --git a/news_tgbot.py b/news_tgbot.py new file mode 100755 index 0000000..aafaacd --- /dev/null +++ b/news_tgbot.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +import logging +from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton +from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, CallbackQueryHandler, filters, ContextTypes +import vk_api +import time + +# Введите ваши токены +TELEGRAM_TOKEN = 'ВАШ_ТОКЕН' +VK_ACCESS_TOKEN = 'ВАШ_ОТКРЫТЫЙ_ТОКЕН' +GROUP_IDS = [ + ('ID 1', 'Название группы 1'), + ('ID 2', 'Название группы 2') +] # Список ID групп ВКонтакте + +# Логирование +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO +) +logger = logging.getLogger(__name__) + +# Функция для получения новостей из конкретной группы ВКонтакте +def get_vk_news(group_id, group_name): + vk_session = vk_api.VkApi(token=VK_ACCESS_TOKEN) + vk = vk_session.get_api() + news = [] + + try: + if group_id.isdigit(): + response = vk.wall.get(owner_id=f'-{group_id}', count=5) # Используем owner_id + else: + response = vk.wall.get(domain=group_id, count=5) # Используем domain + for item in response['items']: + post = { + 'text': item['text'], + 'group_name': group_name, + 'owner_id': item['owner_id'], + 'post_id': item['id'], + 'date': item['date'], # Добавляем дату публикации + 'photos': [] + } + + # Проверяем наличие фотографий в посте + if 'attachments' in item: + for attachment in item['attachments']: + if attachment['type'] == 'photo': + max_size_photo = max(attachment['photo']['sizes'], key=lambda size: size['width'] * size['height']) + post['photos'].append(max_size_photo['url']) + + news.append(post) + except vk_api.exceptions.ApiError as e: + logger.error(f"Ошибка при получении новостей для группы {group_id}: {e}") + + return news + +# Функция для получения комментариев поста из ВКонтакте +def get_vk_comments(owner_id, post_id): + vk_session = vk_api.VkApi(token=VK_ACCESS_TOKEN) + vk = vk_session.get_api() + comments = [] + + try: + response = vk.wall.getComments(owner_id=owner_id, post_id=post_id, count=10, extended=1) + for comment in response['items']: + # Получаем информацию о пользователе + user_info = vk.users.get(user_ids=comment['from_id'])[0] + comments.append({ + 'text': comment['text'], + 'from_id': comment['from_id'], + 'from_name': f"{user_info['first_name']} {user_info['last_name']}", + 'date': comment['date'] + }) + except vk_api.exceptions.ApiError as e: + logger.error(f"Ошибка при получении комментариев для поста {owner_id}_{post_id}: {e}") + + return comments + +# Функция разбивки длинного сообщения на несколько частей +def split_message(message, max_length=4096): + return [message[i:i+max_length] for i in range(0, len(message), max_length)] + +# Функция форматирования даты +def format_date(timestamp): + return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp)) + +# Функция обработки команды /start +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if update.message: + try: + await update.message.reply_text('Привет! Я бот, который собирает новости из ВКонтакте.') + await menu(update, context) # Сразу вызываем функцию menu + except Exception as e: + logger.error(f"Ошибка при отправке сообщения: {e}") + +# Функция обработки команды /menu +async def menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + keyboard = [[group[1]] for group in GROUP_IDS] # Кнопки с названиями групп + reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True) + if update.message: + try: + await update.message.reply_text('Выберите группу:', reply_markup=reply_markup) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения: {e}") + +# Функция для формирования инлайн-кнопок под каждым сообщением новости +def generate_inline_keyboard(post_id, owner_id): + keyboard = [[InlineKeyboardButton("Показать комментарии", callback_data=f"comments_{owner_id}_{post_id}")]] + return InlineKeyboardMarkup(keyboard) + +# Функция обработки нажатий кнопок +async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = update.message.text + for group_id, group_name in GROUP_IDS: + if text == group_name: + news_items = get_vk_news(group_id, group_name) + if news_items: + for item in news_items: + post_link = f"https://vk.com/wall{item['owner_id']}_{item['post_id']}" + post_date = format_date(item['date']) + message = f"{post_link}\nДата: {post_date}\nГруппа: {item['group_name']}\n\n{item['text']}" + + # Добавляем кнопки под каждым сообщением новости + inline_keyboard = generate_inline_keyboard(item['post_id'], item['owner_id']) + + if item['photos']: + for photo in item['photos']: + await update.message.reply_photo(photo, caption=message, reply_markup=inline_keyboard) + else: + await update.message.reply_text(message, reply_markup=inline_keyboard) + + else: + await update.message.reply_text(f'Новостей нет для группы {group_name} или произошла ошибка при получении новостей.') + break + else: + await update.message.reply_text('Неверная команда. Используйте меню для выбора группы.') + +# Функция обработки нажатий на инлайн-кнопки +async def callback_query_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + query_data = query.data.split('_') + if query_data[0] == 'comments': + owner_id = query_data[1] + post_id = query_data[2] + comments = get_vk_comments(owner_id, post_id) + if comments: + comment_text = '\n\n'.join([f"{comment['text']}\n\nКомментарий от: {comment['from_name']}\nВремя: {format_date(comment['date'])}" for comment in comments]) + if len(comment_text) > 4096: + parts = split_message(comment_text) + for part in parts: + await query.message.reply_text(part) + else: + await query.message.reply_text(comment_text) + else: + await query.message.reply_text('Комментариев нет.') + + # Удаляем кнопку "Назад в меню" + await query.message.delete_reply_markup() + +# Основная функция для запуска бота +def main() -> None: + application = ApplicationBuilder().token(TELEGRAM_TOKEN).build() + + application.add_handler(CommandHandler("start", start)) + application.add_handler(CommandHandler("menu", menu)) + application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, button_handler)) + application.add_handler(CallbackQueryHandler(callback_query_handler)) + + application.run_polling() + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..89e6e8c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +python-telegram-bot==20.3 +vk_api==11.9.8 \ No newline at end of file