Ekaropolus d1149ff471
All checks were successful
continuous-integration/drone/push Build is passing
Messenger app
2025-09-06 02:33:53 -06:00

114 lines
4.0 KiB
Python

# pxy_messenger/views.py
import hashlib, hmac, json, logging, os
from django.conf import settings
from django.http import HttpResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
# Optional: if you created a model to dedupe events (you have one in admin!)
from .models import MessengerEvent # keep if present
import requests
log = logging.getLogger(__name__)
GRAPH_API = os.getenv("META_GRAPH_API", "https://graph.facebook.com/v18.0")
def _verify_get(request):
mode = request.GET.get("hub.mode")
token = request.GET.get("hub.verify_token")
chal = request.GET.get("hub.challenge", "")
if mode == "subscribe" and token == getattr(settings, "MESSENGER_VERIFY_TOKEN", ""):
return HttpResponse(chal)
return HttpResponseForbidden("Bad verify token")
def _verify_signature(request):
app_secret = getattr(settings, "FACEBOOK_APP_SECRET", "")
if not app_secret:
return True # dev mode (no signature check)
provided = request.headers.get("X-Hub-Signature-256", "")
body = request.body
# keep a copy for troubleshooting
try:
with open("/tmp/m_sig_body.bin", "wb") as f:
f.write(body)
except Exception:
pass
calc_hex = hmac.new(app_secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
expected = "sha256=" + calc_hex
log.info("[messenger] sig check: provided=%r expected=%r body_len=%d", provided, expected, len(body))
return hmac.compare_digest(provided, expected)
def _send_text(psid: str, text: str) -> bool:
token = getattr(settings, "PAGE_ACCESS_TOKEN", "")
if not token:
log.warning("[messenger] no PAGE_ACCESS_TOKEN; would send to %s: %s", psid, text)
return False
url = f"{GRAPH_API}/me/messages"
params = {"access_token": token}
payload = {
"messaging_type": "RESPONSE",
"recipient": {"id": psid},
"message": {"text": text},
}
try:
r = requests.post(url, params=params, json=payload, timeout=10)
if r.status_code != 200:
log.error("[messenger] Send API error %s: %s", r.status_code, r.text)
return False
log.info("[messenger] sent to %s ok: %s", psid, r.text)
return True
except Exception as e:
log.exception("[messenger] Send API exception: %s", e)
return False
@csrf_exempt
def webhook(request):
if request.method == "GET":
return _verify_get(request)
if request.method != "POST":
return HttpResponse(status=405)
if not _verify_signature(request):
return HttpResponseForbidden("Invalid signature")
try:
payload = json.loads(request.body.decode("utf-8") or "{}")
except Exception:
payload = {}
log.info("[messenger] payload: %s", json.dumps(payload)[:2000])
# Iterate entries and messaging events
for entry in payload.get("entry", []):
for m in entry.get("messaging", []):
sender = (m.get("sender") or {}).get("id")
recipient = (m.get("recipient") or {}).get("id")
# dedupe by message mid when present
mid = (m.get("message") or {}).get("mid") \
or (m.get("delivery") or {}).get("mids", [None])[0]
if mid:
# store if not seen (your admin shows MessengerEvent already)
obj, created = MessengerEvent.objects.get_or_create(
mid=mid,
defaults={
"sender_id": sender or "",
"page_id": recipient or "",
},
)
if not created:
log.info("[messenger] duplicate mid %s — skipping", mid)
continue
# Basic text echo
if "message" in m and "text" in m["message"] and sender:
txt = m["message"]["text"].strip()
reply = f"You said: {txt}"
_send_text(sender, reply)
# must 200 quickly
return HttpResponse("OK")