From a121cb21130c79836f4233e983eb98fb4b7dbdf0 Mon Sep 17 00:00:00 2001 From: Shagnik Sarkar Date: Sun, 29 Mar 2026 12:16:46 +0530 Subject: [PATCH 1/2] add integration to social --- website/apps.py | 24 ++++++++++ website/signals.py | 115 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/website/apps.py b/website/apps.py index f55fedb..fca8e08 100644 --- a/website/apps.py +++ b/website/apps.py @@ -11,6 +11,8 @@ def ready(self): last_active_signal_from_answer, last_active_signal_from_reply, home_cache_invalidator, + webhook_on_create, + webhook_on_delete, ) post_save.connect( @@ -53,4 +55,26 @@ def ready(self): home_cache_invalidator, sender=AnswerComment, dispatch_uid='home_cache_invalidator_answercomment_delete', + ) + + # Webhook signals for social + post_save.connect( + webhook_on_create, + sender=Question, + dispatch_uid='webhook_question_created', + ) + post_save.connect( + webhook_on_create, + sender=Answer, + dispatch_uid='webhook_answer_created', + ) + post_delete.connect( + webhook_on_delete, + sender=Question, + dispatch_uid='webhook_question_deleted', + ) + post_delete.connect( + webhook_on_delete, + sender=Answer, + dispatch_uid='webhook_answer_deleted', ) \ No newline at end of file diff --git a/website/signals.py b/website/signals.py index 809f49d..bea086d 100644 --- a/website/signals.py +++ b/website/signals.py @@ -1,6 +1,14 @@ +import json +import logging +import threading + +import requests +from django.conf import settings from django.utils import timezone from django.core.cache import cache +logger = logging.getLogger(__name__) + HOME_CACHE_KEYS = [ 'home:categories', @@ -35,4 +43,109 @@ def last_active_signal_from_reply(sender, instance, created, **kwargs): def home_cache_invalidator(sender, instance, **kwargs): - clear_home_cache() \ No newline at end of file + clear_home_cache() + + +# webhook helpers for social + +def _send_webhook(payload): + """Fire-and-forget POST to the Social platform's webhook endpoint. + + Runs in a daemon thread so the forum response is never delayed. + Fails silently — the forum must keep working even if the Social app is down. + """ + + webhook_url = "http://localhost:8000/api/webhooks/forum" + + if not webhook_url: + return + + headers = { + 'Content-Type': 'application/json', + } + + def _post(): + try: + resp = requests.post( + webhook_url, + data=json.dumps(payload), + headers=headers, + timeout=10, + ) + logger.info( + 'Webhook sent: %s → %s (status %s)', + payload.get('event'), webhook_url, resp.status_code, + ) + except requests.RequestException as exc: + logger.warning('Webhook delivery failed: %s', exc) + + thread = threading.Thread(target=_post, daemon=True) + thread.start() + + +def _build_payload(event_type, instance, sender): + """Build a consistent webhook payload for both Question and Answer events.""" + from .models import Question, Answer + from django.contrib.auth import get_user_model + + User = get_user_model() + + + user_email = "" + try: + user = User.objects.get(id=instance.uid) + user_email = user.email + except User.DoesNotExist: + pass + + payload = { + 'event': event_type, + 'user_id': instance.uid, + 'email': user_email, + 'resource_id': instance.id, + 'timestamp': timezone.now().isoformat(), + } + + if sender == Question: + payload['category'] = instance.category + payload['tutorial'] = instance.tutorial + payload['title'] = instance.title + elif sender == Answer: + payload['question_id'] = instance.question_id + payload['category'] = instance.question.category + payload['tutorial'] = instance.question.tutorial + + return payload + + +def webhook_on_create(sender, instance, created, **kwargs): + """Signal handler: fires when a Question or Answer is saved for the first time.""" + if not created: + return # We only care about new objects, not updates + + from .models import Question, Answer + + if sender == Question: + event = 'question_created' + elif sender == Answer: + event = 'answer_created' + else: + return + + payload = _build_payload(event, instance, sender) + _send_webhook(payload) + + +def webhook_on_delete(sender, instance, **kwargs): + """Signal handler: fires when a Question or Answer is deleted.""" + from .models import Question, Answer + + if sender == Question: + event = 'question_deleted' + elif sender == Answer: + event = 'answer_deleted' + else: + return + + payload = _build_payload(event, instance, sender) + _send_webhook(payload) \ No newline at end of file From 6141abba8cbed5d712b30502eeb7089c95da74ca Mon Sep 17 00:00:00 2001 From: Shagnik Sarkar Date: Sun, 29 Mar 2026 22:19:32 +0530 Subject: [PATCH 2/2] update social URL --- website/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/signals.py b/website/signals.py index bea086d..0ff0020 100644 --- a/website/signals.py +++ b/website/signals.py @@ -55,7 +55,7 @@ def _send_webhook(payload): Fails silently — the forum must keep working even if the Social app is down. """ - webhook_url = "http://localhost:8000/api/webhooks/forum" + webhook_url = "https://social.edupyramids.org/api/webhooks/forum" if not webhook_url: return