# posts/tasks.py from celery import shared_task from django.utils import timezone from .models import PostJob, Post from .utils import render_post_text, build_keyboard, send_post_to_chat @shared_task(bind=True, rate_limit="25/m") # мягкий лимит def run_post_jobs(self): jobs = (PostJob.objects .select_related("post","chat") .filter(status=Post.QUEUED, run_at__lte=timezone.now())[:50]) for job in jobs: try: text = render_post_text(job.post) kb = build_keyboard(job.post.buttons) send_post_to_chat(job.chat.chat_id, job.post, text, kb) # обёртка над PTB Bot job.status = Post.SENT job.save(update_fields=["status"]) except Exception as e: job.attempts += 1 job.last_error = str(e)[:2000] if job.attempts >= job.max_attempts: job.status = Post.FAILED else: # экспоненциальный backoff job.run_at = timezone.now() + timezone.timedelta(minutes=2 ** min(job.attempts,4)) job.save()