LukinterLab
LukInterLab

LukInterLab- -LukInterLab

profile-pic

новые статьи публикуем в Телеграмме

14 июня 2025 г. 13:04
Изображение поста
Я внес следующие изменения: Добавил полный URL сайта в ссылки на посты Добавил подробное логирование для отслеживания процесса отправки Улучшил обработку ошибок Теперь, чтобы проверить работу отправки в Telegram: Создайте новый пост или отредактируйте существующий Заполните поле "ПОСТ для ТЕЛЕГРАММА" (это описание, которое будет отправлено в Telegram) Добавьте изображение в поле "Превью поста" (если есть) Измените статус на "Опубликовано" Пост должен автоматически отправиться в Telegram. Если этого не происходит, проверьте: Правильность токена бота и ID канала в settings.py Права бота в канале (он должен быть администратором) Логи Django для просмотра возможных ошибок Также можно проверить работу бота вручную, отправив тестовое сообщение через API Telegram:

Давайте проверим функционал отправки в Telegram. Сначала проверим, правильно ли работает сигнал для отправки в Telegram.

from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse
from taggit.managers import TaggableManager
from ckeditor.fields import RichTextField
from ckeditor_uploader.fields import RichTextUploadingField
from mptt.models import MPTTModel, TreeForeignKey
from django.db.models.signals import post_save
from django.dispatch import receiver
from .telegram_utils import send_to_telegram
from django.conf import settings
from django.core.cache import cache
from django.utils import timezone

User = get_user_model()




class Post(models.Model):
    """  Модель статей для сайта """

    class PostManager(models.Manager):
        """  Кастомный менеджер для модели статей      """

        def all(self):
            """ Список статей (SQL запрос с фильтрацией для страницы списка статей)  """
            return self.get_queryset().select_related('author', 'category')

        def detail(self):
            """
            Детальная статья (SQL запрос с фильтрацией для страницы со статьёй)
            """
            return self.get_queryset() \
                .select_related('author', 'category') \
                .prefetch_related('comments', 'comments__author', 'comments__author__profile') \
                .filter(status='published')

    objects = PostManager()

    STATUS_OPTIONS = (
        ('published', 'Опубликовано'),
        ('draft', 'Черновик')
    )
    title = models.CharField(verbose_name='Заголовок', max_length=100)
    category = TreeForeignKey('Category',
                      related_name='posts',
                      on_delete=models.CASCADE, verbose_name='Категория')
    slug = models.SlugField(verbose_name='URL', max_length=255, blank=True, unique_for_date='created')
    description = models.TextField(verbose_name='ПОСТ для ТЕЛЕГРАММА', blank=True)
    content = RichTextUploadingField(verbose_name='Основной текст', blank=True)
    kartinka = models.ImageField(verbose_name='Превью поста', blank=True, null=True, upload_to='images/',)
    video = RichTextUploadingField(config_name='vstavka', verbose_name='Видео', blank=True, null=True, )
    status = models.CharField(choices=STATUS_OPTIONS, default='draft', verbose_name='Статус поста', max_length=10)
    created = models.DateTimeField(auto_now_add=True, verbose_name='Время добавления')
    updated = models.DateTimeField(auto_now=True, verbose_name='Время обновления')
    author = models.ForeignKey(User, verbose_name='Автор', on_delete=models.CASCADE, related_name='author_posts',
                               default=1)
    updater = models.ForeignKey(User, verbose_name='Обновил', on_delete=models.SET_NULL, null=True,
                                related_name='updater_posts', blank=True)
    telegram_posted_at = models.DateTimeField(verbose_name='Время публикации в Телеге', blank=True, null=True)
    fixed = models.BooleanField(verbose_name='Выполнено', default=False)
    views = models.IntegerField(verbose_name='Просмотры', default=0)
    tags = TaggableManager(blank=True)

    class Meta:
        db_table = 'app_posts'
        ordering = ['-updated']
        indexes = [models.Index(fields=['-fixed', '-created', 'status'])]
        verbose_name = 'Статья'
        verbose_name_plural = 'Статьи'

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        """Метод получения URL-адреса объекта"""
        return reverse('Blog:post_detail', args=[self.id, self.slug])  # args=[self.id, self.slug]) kwargs={'slug': self.slug})

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__kartinka = self.kartinka if self.pk else None


class Category(MPTTModel):
    """ Модель категорий с вложенностью """
    kartinka_cat = models.FileField(
        verbose_name='картинка телеграмма',
        blank=True,
        upload_to='Telega/',
    )
    title = models.CharField(max_length=355, verbose_name='Название категории')
    slug = models.SlugField(max_length=255, verbose_name='URL категории', blank=True, unique=True)
    chat_id = models.CharField(verbose_name='id канала', max_length=100, null=True, blank=True)
    chat_url = models.CharField(verbose_name='url канала', max_length=100, null=True, blank=True)
    chat = models.CharField(verbose_name='канал', max_length=100, null=True, blank=True)
    description_chat = models.TextField(verbose_name='Описание канала', max_length=500, null=True, blank=True)
    description = models.TextField(verbose_name='Описание категории', max_length=500, null=True, blank=True)
    activ = models.BooleanField(verbose_name='Активно', default=False)
    parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True,
                            db_index=True,
                            related_name='children',
                            verbose_name='Родительская категория'
                            )
    
    class MPTTMeta:
        """Сортировка по вложенности"""
        order_insertion_by = ('title',)

    class Meta:
        """Сортировка, название модели в админ панели, таблица в данными"""
        ordering = ['title']
        indexes = [models.Index(fields=['title'])]
        verbose_name = 'Категория'
        verbose_name_plural = 'Категории'
        db_table = 'app_categories'
    
   
    def __str__(self):
        """Возвращение заголовка статьи"""
        return self.title

    def get_absolute_url(self):
        return reverse('Blog:post_list_by_category',
                       args=[self.slug])  # args=[self.slug]) kwargs={'slug': self.slug})


class Comment(models.Model):
    """Модель комментариев"""

    post = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name='Статья', related_name='comments')
    author_comment = models.CharField(max_length=80, verbose_name='Автор комментария')
    content = models.TextField(verbose_name='Текст комментария', max_length=3000)
    created = models.DateTimeField(verbose_name='Время добавления', auto_now_add=True)
    updated = models.DateTimeField(verbose_name='Время обновления', auto_now=True)
    active = models.BooleanField(default=True)
    email = models.EmailField()

    class Meta:
        db_table = 'app_comments'
        indexes = [models.Index(fields=['-created', 'updated', 'active'])]
        ordering = ['-created']
        verbose_name = 'Комментарий'
        verbose_name_plural = 'Комментарии'

    def __str__(self):
        return 'Комментарий от {} на {}'.format(self.author_comment, self.post)


@receiver(post_save, sender=Post)
def publish_to_telegram(sender, instance, created, **kwargs):
    """
    Обработчик сигналов для публикации сообщений в Telegram при их публикации
    """
    if instance.status == 'published' and not instance.telegram_posted_at:
        send_to_telegram(instance)

Теперь проверим функцию отправки в Telegram:

import requests
from django.conf import settings
from datetime import datetime
from django.core.files.storage import default_storage
import os
import logging

logger = logging.getLogger(__name__)

def send_to_telegram(post):
    """
    Отправьте сообщение в Telegram-канал с изображением и отформатированным сообщением
    """
    if not settings.TELEGRAM_BOT_TOKEN or not settings.TELEGRAM_CHANNEL_ID:
        logger.error("Telegram settings are not configured")
        return False
        
    bot_token = settings.TELEGRAM_BOT_TOKEN
    channel_id = settings.TELEGRAM_CHANNEL_ID
    
    # Получаем полный URL сайта
    site_url = settings.SITE_URL if hasattr(settings, 'SITE_URL') else 'https://lukinterlab.ru'
    
    # Подготовьте текст сообщения
    message = f"*{post.title}*\n\n"
    if post.description:
        message += f"{post.description}\n\n"
    message += f"Читать далее: {site_url}{post.get_absolute_url()}"
    
    logger.info(f"Attempting to send post '{post.title}' to Telegram")
    
    # Сначала попробуйте отправить фотографию с подписью
    if post.kartinka:
        try:
            # Получите полный путь к изображению
            image_path = post.kartinka.path
            
            # Подготовьте URL API для отправки фотографии
            api_url = f"https://api.telegram.org/bot{bot_token}/sendPhoto"
            
            # Подготовка файлов и данных
            files = {
                'photo': open(image_path, 'rb')
            }
            data = {
                'chat_id': channel_id,
                'caption': message,
                'parse_mode': 'Markdown'
            }
            
            # Отправьте фотографию с подписью
            response = requests.post(api_url, files=files, data=data)
            response.raise_for_status()
            
            # Обновите поле telegram_posted_at в сообщении
            post.telegram_posted_at = datetime.now()
            post.save(update_fields=['telegram_posted_at'])
            
            logger.info(f"Successfully sent post '{post.title}' with image to Telegram")
            return True
            
        except Exception as e:
            logger.error(f"Error sending photo to Telegram: {str(e)}")
            # Если отправить фотографию не удастся, вернитесь к отправке только текстового сообщения
            try:
                # Prepare API URL for sending message
                api_url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
                
                # Prepare data
                data = {
                    "chat_id": channel_id,
                    "text": message,
                    "parse_mode": "Markdown"
                }
                
                # Отправить сообщение
                response = requests.post(api_url, data=data)
                response.raise_for_status()
                
                # Обновите поле telegram_posted_at в сообщении
                post.telegram_posted_at = datetime.now()
                post.save(update_fields=['telegram_posted_at'])
                
                logger.info(f"Successfully sent post '{post.title}' (text only) to Telegram")
                return True
            except Exception as e:
                logger.error(f"Error sending message to Telegram: {str(e)}")
                return False
    else:
        # If no image, just send the message
        try:
            # Prepare API URL for sending message
            api_url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
            
            # Prepare data
            data = {
                "chat_id": channel_id,
                "text": message,
                "parse_mode": "Markdown"
            }
            
            # Отправить сообщение
            response = requests.post(api_url, data=data)
            response.raise_for_status()
            
            # Обновите поле telegram_posted_at в сообщении
            post.telegram_posted_at = datetime.now()
            post.save(update_fields=['telegram_posted_at'])
            
            logger.info(f"Successfully sent post '{post.title}' (text only) to Telegram")
            return True
        except Exception as e:
            logger.error(f"Error sending message to Telegram: {str(e)}")
            return False 

Теперь давайте добавим настройку SITE_URL в settings.py:

// ... existing code ...

# Site URL for absolute links
SITE_URL = 'https://lukinterlab.ru'

// ... rest of the settings ...

Я внес следующие изменения:

  1. Добавил полный URL сайта в ссылки на посты
  1. Добавил подробное логирование для отслеживания процесса отправки
  1. Улучшил обработку ошибок

Теперь, чтобы проверить работу отправки в Telegram:

  1. Создайте новый пост или отредактируйте существующий
  1. Заполните поле "ПОСТ для ТЕЛЕГРАММА" (это описание, которое будет отправлено в Telegram)
  1. Добавьте изображение в поле "Превью поста" (если есть)
  1. Измените статус на "Опубликовано"

Пост должен автоматически отправиться в Telegram. Если этого не происходит, проверьте:

  1. Правильность токена бота и ID канала в settings.py
  1. Права бота в канале (он должен быть администратором)
  1. Логи Django для просмотра возможных ошибок

Также можно проверить работу бота вручную, отправив тестовое сообщение через API Telegram:

 

Пока комментариев нет.

Оставьте коментарий