Skip to content

Commit

Permalink
Merge branch 'main' into truncated
Browse files Browse the repository at this point in the history
  • Loading branch information
tsu-ki authored Dec 3, 2024
2 parents 2758836 + 3842c9a commit 6db3f39
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGR
SENTRY_DSN=https://[email protected]/0

SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=
SLACK_CLIENT_SECRET=

GITHUB_ACCESS_TOKEN="abc123"
2 changes: 2 additions & 0 deletions blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
deletions,
follow_user,
get_score,
github_webhook,
invite_friend,
profile,
profile_edit,
Expand Down Expand Up @@ -638,6 +639,7 @@
),
path("delete_time_entry/", delete_time_entry, name="delete_time_entry"),
path("assign-badge/<str:username>/", assign_badge, name="assign_badge"),
path("github-webhook/", github_webhook, name="github-webhook"),
]

if settings.DEBUG:
Expand Down
125 changes: 125 additions & 0 deletions website/views/user.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import hashlib
import hmac
import json
import os
from datetime import datetime, timezone
Expand Down Expand Up @@ -28,6 +30,7 @@
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import DetailView, ListView, TemplateView, View
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken
Expand Down Expand Up @@ -874,3 +877,125 @@ def assign_badge(request, username):
UserBadge.objects.create(user=user, badge=badge, awarded_by=request.user, reason=reason)
messages.success(request, f"{badge.title} badge assigned to {user.username}.")
return redirect("profile", slug=username)


@csrf_exempt
def github_webhook(request):
if request.method == "POST":
# Validate GitHub signature
signature = request.headers.get("X-Hub-Signature-256")
if not validate_signature(request.body, signature):
return JsonResponse({"status": "error", "message": "Unauthorized request"}, status=403)

payload = json.loads(request.body)
event_type = request.headers.get("X-GitHub-Event", "")

event_handlers = {
"pull_request": handle_pull_request_event,
"push": handle_push_event,
"pull_request_review": handle_review_event,
"issues": handle_issue_event,
"status": handle_status_event,
"fork": handle_fork_event,
"create": handle_create_event,
}

handler = event_handlers.get(event_type)
if handler:
return handler(payload)
else:
return JsonResponse({"status": "error", "message": "Unhandled event type"}, status=400)
else:
return JsonResponse({"status": "error", "message": "Invalid method"}, status=400)


def handle_pull_request_event(payload):
if payload["action"] == "closed" and payload["pull_request"]["merged"]:
pr_user_profile = UserProfile.objects.filter(
github_url=payload["pull_request"]["user"]["html_url"]
).first()
if pr_user_profile:
pr_user_instance = pr_user_profile.user
assign_github_badge(pr_user_instance, "First PR Merged")
return JsonResponse({"status": "success"}, status=200)


def handle_push_event(payload):
pusher_profile = UserProfile.objects.filter(github_url=payload["sender"]["html_url"]).first()
if pusher_profile:
pusher_user = pusher_profile.user
if payload.get("commits"):
assign_github_badge(pusher_user, "First Commit")
return JsonResponse({"status": "success"}, status=200)


def handle_review_event(payload):
reviewer_profile = UserProfile.objects.filter(github_url=payload["sender"]["html_url"]).first()
if reviewer_profile:
reviewer_user = reviewer_profile.user
assign_github_badge(reviewer_user, "First Code Review")
return JsonResponse({"status": "success"}, status=200)


def handle_issue_event(payload):
print("issue closed")
if payload["action"] == "closed":
closer_profile = UserProfile.objects.filter(
github_url=payload["sender"]["html_url"]
).first()
if closer_profile:
closer_user = closer_profile.user
assign_github_badge(closer_user, "First Issue Closed")
return JsonResponse({"status": "success"}, status=200)


def handle_status_event(payload):
user_profile = UserProfile.objects.filter(github_url=payload["sender"]["html_url"]).first()
if user_profile:
user = user_profile.user
build_status = payload["state"]
if build_status == "success":
assign_github_badge(user, "First CI Build Passed")
elif build_status == "failure":
assign_github_badge(user, "First CI Build Failed")
return JsonResponse({"status": "success"}, status=200)


def handle_fork_event(payload):
user_profile = UserProfile.objects.filter(github_url=payload["sender"]["html_url"]).first()
if user_profile:
user = user_profile.user
assign_github_badge(user, "First Fork Created")
return JsonResponse({"status": "success"}, status=200)


def handle_create_event(payload):
if payload["ref_type"] == "branch":
user_profile = UserProfile.objects.filter(github_url=payload["sender"]["html_url"]).first()
if user_profile:
user = user_profile.user
assign_github_badge(user, "First Branch Created")
return JsonResponse({"status": "success"}, status=200)


def assign_github_badge(user, action_title):
try:
badge, created = Badge.objects.get_or_create(title=action_title, type="automatic")
if not UserBadge.objects.filter(user=user, badge=badge).exists():
UserBadge.objects.create(user=user, badge=badge)
print(f"Assigned '{action_title}' badge to {user.username}")
else:
print(f"{user.username} already has the '{action_title}' badge.")
except Badge.DoesNotExist:
print(f"Badge '{action_title}' does not exist.")


def validate_signature(payload, signature):
if not signature:
return False

secret = bytes(os.environ.get("GITHUB_ACCESS_TOKEN", ""), "utf-8")
computed_hmac = hmac.new(secret, payload, hashlib.sha256)
computed_signature = f"sha256={computed_hmac.hexdigest()}"

return hmac.compare_digest(computed_signature, signature)

0 comments on commit 6db3f39

Please sign in to comment.