Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Пересылка постов из ВК в ТГ #37

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Arzangulyan
Copy link
Member

Добавлен функционал, позволяющий:

  1. Настраивать мониторинг указанной группы ВКонтакте
  2. Пересылать новые посты из этой группы в указанный Telegram-канал
  3. Отправлять все фотографии из поста в одном сообщении Telegram
  4. Обрабатывать другие типы вложений (видео, ссылки, документы и аудио)

Для работы функционала необходимо добавить две переменные окружения:

  • TELEGRAM_TARGET_CHANNEL_ID - ID канала Telegram для пересылки постов
  • VK_MONITORED_GROUP_ID - ID группы ВК для мониторинга

Изменения просты в использовании и не нарушают существующий функционал.

Check-List

  • Вы проверили свой код перед отправкой запроса?
  • Вы написали тесты к реализованным функциям?
  • Вы не забыли применить форматирование black и isort для Back-End или Prettier для Front-End?

@Arzangulyan Arzangulyan requested a review from dyakovri March 7, 2025 19:34
Copy link

github-actions bot commented Mar 7, 2025

💩 Code linting failed, use black and isort to fix it.

Copy link

github-actions bot commented Mar 7, 2025

Summary

Tests Skipped Failures Errors Time
5 0 💤 0 ❌ 0 🔥 1.260s ⏱️


@event(
type="wall_post_new",
group_id=lambda i: int(i) == settings.VK_MONITORED_GROUP_ID if settings.VK_MONITORED_GROUP_ID else False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Изначально предполагалось, что такие штуки прямо в коде будешь задавать. Не нужно их в настройки вносить

По факту эти @event и есть конфиг, хоть и с кодом

Comment on lines +45 to +94
async def send_to_telegram(message: str, photos: list = None):
"""Отправляет сообщение и фотографии в Telegram канал"""
if not settings.TELEGRAM_BOT_TOKEN or not settings.TELEGRAM_TARGET_CHANNEL_ID:
logger.warning("Telegram bot token or channel ID not configured")
return

bot = Bot(token=settings.TELEGRAM_BOT_TOKEN)

try:
if not photos:
# Если нет фотографий, отправляем только текст
await bot.send_message(
chat_id=settings.TELEGRAM_TARGET_CHANNEL_ID,
text=message,
parse_mode='HTML',
disable_web_page_preview=False
)
elif len(photos) == 1:
# Если только одна фотография, отправляем ее с подписью
await bot.send_photo(
chat_id=settings.TELEGRAM_TARGET_CHANNEL_ID,
photo=photos[0],
caption=message,
parse_mode='HTML'
)
else:
# Если несколько фотографий, отправляем их как медиагруппу
media_group = []

# Первая фотография с подписью (текстом сообщения)
media_group.append(InputMediaPhoto(
media=photos[0],
caption=message,
parse_mode='HTML'
))

# Все остальные фотографии без подписи
for photo_url in photos[1:]:
media_group.append(InputMediaPhoto(
media=photo_url
))

await bot.send_media_group(
chat_id=settings.TELEGRAM_TARGET_CHANNEL_ID,
media=media_group
)

logger.info(f"Message successfully sent to Telegram channel {settings.TELEGRAM_TARGET_CHANNEL_ID}")
except Exception as e:
logger.error(f"Failed to send message to Telegram: {e}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этот код я бы унес в утилиты работы с телеграммом. Возможно, такой файл уже есть. Куда-нибудь в utils/telegram.py

И задавал бы Telegram channel id через параметр, а не через настройку. Из настроек убрал бы вообще это параметр. Просто захардкодить внутри @event функции

Comment on lines +119 to +170
for attachment in attachments:
attachment_type = attachment.get("type")

# Обрабатываем видео
if attachment_type == "video":
video_data = attachment.get("video", {})
video_id = video_data.get("id")
video_owner_id = video_data.get("owner_id")
video_title = video_data.get("title", "Видео")

if video_id and video_owner_id:
attachment_texts.append(
f"\n\n<b>📹 {video_title}</b>: "
f"<a href='https://vk.com/video{video_owner_id}_{video_id}'>Смотреть видео</a>"
)

# Обрабатываем ссылки
elif attachment_type == "link":
link_data = attachment.get("link", {})
link_url = link_data.get("url")
link_title = link_data.get("title", "Ссылка")

if link_url:
attachment_texts.append(
f"\n\n<b>🔗 {link_title}</b>: <a href='{link_url}'>Открыть ссылку</a>"
)

# Обрабатываем документы
elif attachment_type == "doc":
doc_data = attachment.get("doc", {})
doc_url = doc_data.get("url")
doc_title = doc_data.get("title", "Документ")

if doc_url:
attachment_texts.append(
f"\n\n<b>📄 {doc_title}</b>: <a href='{doc_url}'>Скачать документ</a>"
)

# Обрабатываем аудио
elif attachment_type == "audio":
audio_data = attachment.get("audio", {})
audio_id = audio_data.get("id")
audio_owner_id = audio_data.get("owner_id")
audio_artist = audio_data.get("artist", "")
audio_title = audio_data.get("title", "Аудиозапись")

if audio_id and audio_owner_id:
attachment_texts.append(
f"\n\n<b>🎵 {audio_artist} - {audio_title}</b>: "
f"<a href='https://vk.com/audio{audio_owner_id}_{audio_id}'>Слушать</a>"
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Очень большая вложенность. +Проверить это не могу (нужно читать документацию ВК)

По первому пункту: лучше создать файл utils/vk.py и вынести туда отдельными функциями для каждого типа сложений + общую функцию для обработки одного типа сложений. Тогда тут будет for a in attachments: обработай вложение

photos.append(photo_url)

# Отправляем в Telegram
import asyncio
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Импорты лучше делать в начале файла


# Отправляем в Telegram
import asyncio
asyncio.run(send_to_telegram(message, photos))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вызывать асинхронный код отсюда не надо точно

Comment on lines +197 to +198
except Exception as e:
logger.exception(f"Error processing new VK post: {e}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Внутри обработчика событий и так должен быть большой try/except. Не нужно делать ещё один

WebhookStorage(
system=WebhookSystems.VK,
message=request_data,
try:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Внутри router.post и так есть обработчик исключений, тут не нужен

Comment on lines +78 to +92
db.session.commit()

# Проверяем, это ли событие создания записи на стене
is_wall_post = event_type == "wall_post_new"
if is_wall_post:
logger.info(f"Received new wall post event for group_id: {group_id}")

# Запускаем обработку в фоновом режиме
background_tasks.add_task(create_vk_chat, request_data)
background_tasks.add_task(process_event, request_data)

return PlainTextResponse('ok')
except Exception as e:
logger.exception(f"Error processing VK webhook: {e}")
return PlainTextResponse('ok') # Всегда возвращаем ok, чтобы VK не повторял запрос
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не нужно. Для этого есть обработчик событий через @event

Comment on lines +118 to +143
@router.post('/monitoring/configure')
def configure_monitoring(
config: MonitoringConfig,
user: dict[str] = Depends(UnionAuth(["social.monitoring.configure"])),
) -> dict:
"""Настраивает мониторинг группы ВК и пересылку постов в Telegram канал"""
# Здесь мы обновляем настройки приложения
# В реальном приложении нужно будет сохранять эти настройки в базу данных
# и загружать их при старте, а не менять глобальный объект

settings = get_settings()

# В данном примере мы напрямую изменяем настройки
# Но лучше будет сохранить их в базу и обновлять при перезапуске
# Для этого потребуется создать соответствующую модель БД
settings.VK_MONITORED_GROUP_ID = config.vk_group_id
settings.TELEGRAM_TARGET_CHANNEL_ID = config.telegram_channel_id

logger.info(f"Monitoring configured for VK group {config.vk_group_id} with Telegram channel {config.telegram_channel_id}")

return {
"status": "success",
"message": "Мониторинг настроен успешно",
"vk_group_id": config.vk_group_id,
"telegram_channel_id": config.telegram_channel_id
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так это та же ручка, что уже есть.....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants