news_tgbot/news_tgbot.py
2025-07-30 15:39:20 +03:00

173 lines
8.1 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()