173 lines
8.1 KiB
Python
Executable file
173 lines
8.1 KiB
Python
Executable file
#!/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()
|