feat(blog): notify users about comment activity
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
@@ -39,12 +40,14 @@ from apps.blog.permissions import (
|
|||||||
can_review_blog_posts,
|
can_review_blog_posts,
|
||||||
can_write_blog_posts,
|
can_write_blog_posts,
|
||||||
)
|
)
|
||||||
|
from apps.notifications.services import notify_user
|
||||||
from core.api.schemas import ErrorSchema, MessageSchema
|
from core.api.schemas import ErrorSchema, MessageSchema
|
||||||
from core.authentication import jwt_auth
|
from core.authentication import jwt_auth
|
||||||
|
|
||||||
|
|
||||||
blog_router = Router()
|
blog_router = Router()
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".svg"}
|
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".svg"}
|
||||||
VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".mkv", ".avi"}
|
VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".mkv", ".avi"}
|
||||||
@@ -240,6 +243,73 @@ def _interaction_payload(post: Post, user) -> BlogInteractionSchema:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _frontend_blog_url(post: Post) -> str:
|
||||||
|
root = getattr(settings, "FRONTEND_ROOT", "/") or "/"
|
||||||
|
if not root.endswith("/"):
|
||||||
|
root = f"{root}/"
|
||||||
|
return f"{root}blog/{post.slug}"
|
||||||
|
|
||||||
|
|
||||||
|
def _blog_moderator_ids() -> set[int]:
|
||||||
|
return set(
|
||||||
|
User.objects.filter(is_active=True)
|
||||||
|
.filter(
|
||||||
|
Q(is_staff=True)
|
||||||
|
| Q(is_superuser=True)
|
||||||
|
| Q(groups__permissions__content_type__app_label="blog", groups__permissions__codename="moderate_blog_comment")
|
||||||
|
| Q(user_permissions__content_type__app_label="blog", user_permissions__codename="moderate_blog_comment")
|
||||||
|
)
|
||||||
|
.distinct()
|
||||||
|
.values_list("id", flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _post_author_ids(post: Post) -> set[int]:
|
||||||
|
author_ids = set(post.writers.values_list("id", flat=True))
|
||||||
|
if post.author_id:
|
||||||
|
author_ids.add(post.author_id)
|
||||||
|
return author_ids
|
||||||
|
|
||||||
|
|
||||||
|
def _notify_blog_comment(comment: Comment) -> None:
|
||||||
|
post = comment.post
|
||||||
|
actor_id = comment.author_id
|
||||||
|
action_url = _frontend_blog_url(post)
|
||||||
|
excluded_ids = {actor_id}
|
||||||
|
|
||||||
|
if comment.parent_id and comment.parent.author_id != actor_id:
|
||||||
|
notify_user(
|
||||||
|
comment.parent.author_id,
|
||||||
|
{
|
||||||
|
"type": "blog_reply",
|
||||||
|
"title": "پاسخ جدید",
|
||||||
|
"message": f"به کامنت شما در «{post.title}» پاسخ داده شد.",
|
||||||
|
"level": "info",
|
||||||
|
"action_url": action_url,
|
||||||
|
"entity_type": "blog_comment",
|
||||||
|
"entity_id": comment.id,
|
||||||
|
"meta": {"post_id": post.id, "post_slug": post.slug, "parent_id": comment.parent_id},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
excluded_ids.add(comment.parent.author_id)
|
||||||
|
|
||||||
|
recipient_ids = (_blog_moderator_ids() | _post_author_ids(post)) - excluded_ids
|
||||||
|
for user_id in recipient_ids:
|
||||||
|
notify_user(
|
||||||
|
user_id,
|
||||||
|
{
|
||||||
|
"type": "blog_comment",
|
||||||
|
"title": "کامنت جدید",
|
||||||
|
"message": f"برای «{post.title}» کامنت جدید ثبت شد.",
|
||||||
|
"level": "info",
|
||||||
|
"action_url": action_url,
|
||||||
|
"entity_type": "blog_post",
|
||||||
|
"entity_id": post.id,
|
||||||
|
"meta": {"post_id": post.id, "post_slug": post.slug, "comment_id": comment.id},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@blog_router.get("/admin/writers", response={200: List[AuthorSchema], 403: ErrorSchema}, auth=jwt_auth)
|
@blog_router.get("/admin/writers", response={200: List[AuthorSchema], 403: ErrorSchema}, auth=jwt_auth)
|
||||||
def list_blog_writers(request):
|
def list_blog_writers(request):
|
||||||
if not (request.auth.is_superuser or request.auth.is_staff or can_review_blog_posts(request.auth)):
|
if not (request.auth.is_superuser or request.auth.is_staff or can_review_blog_posts(request.auth)):
|
||||||
@@ -619,6 +689,10 @@ def create_comment(request, slug: str, data: CommentCreateSchema):
|
|||||||
if data.parent_id:
|
if data.parent_id:
|
||||||
parent = get_object_or_404(Comment, id=data.parent_id, post=post, is_approved=True, is_hidden=False, is_deleted=False)
|
parent = get_object_or_404(Comment, id=data.parent_id, post=post, is_approved=True, is_hidden=False, is_deleted=False)
|
||||||
comment = Comment.objects.create(post=post, author=request.auth, content=data.content, parent=parent)
|
comment = Comment.objects.create(post=post, author=request.auth, content=data.content, parent=parent)
|
||||||
|
try:
|
||||||
|
_notify_blog_comment(comment)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Failed to send blog comment notifications for comment=%s", comment.id)
|
||||||
return 201, comment
|
return 201, comment
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user