# -*- encoding: utf-8 -*- import logging import requests from datetime import timedelta from django.conf import settings from django.utils import timezone from django.http import JsonResponse from django.views.generic import View, TemplateView from django.shortcuts import redirect from django.contrib.auth import authenticate, login log = logging.getLogger(__name__) class DiscordLoginAction(View): def get(self, request, *args, **kwargs): return redirect(settings.DISCORD_OAUTH2_URL) class DiscordLoginRedirect(View): def get(self, request, *args, **kwargs): """ Endpoint function directed to once authorized from Discord. This method will "login" the user. """ if request.GET.get("error"): return redirect("auth:login") code = request.GET.get("code") exchange_data = self.exchange_code(code) access_token = exchange_data["access_token"] # Get raw user data from discord raw_user_data = self.get_raw_user_data(access_token) # Add the token and expires datetime to the user data token_expire_datetime = timezone.now() + timedelta(seconds=exchange_data["expires_in"]) raw_user_data["token_expires"] = token_expire_datetime raw_user_data["access_token"] = access_token raw_user_data["refresh_token"] = exchange_data["refresh_token"] # authenticate (creates user if not exists) and login discord_user = authenticate(request, discord_user_data=raw_user_data) login(request, discord_user) redirect_url = request.GET.get("redirect") or "home:index" return redirect(redirect_url) def exchange_code(self, code: str) -> dict: """ Exchanges the given code for an access token. A call is made to the Discord API. """ log.debug("exchanging code for access token") request_data = settings.DISCORD_CODE_EXCHANGE_REQUEST request_data["data"]["code"] = code # Fetch the access token response = requests.post( url=f"{settings.DISCORD_API_URL}/oauth2/token", data=request_data["data"], headers=request_data["headers"] ) return response.json() def refresh_token(self, refresh_token: str) -> dict: """ Refresh the access token if expired, using the refresh token. Returns a new access token, expire time and refresh token. """ log.debug("refreshing access token") request_data = settings.DISCORD_REFRESH_TOKEN_REQUEST request_data["data"]["refresh_token"] = refresh_token response = requests.post( url=f"{settings.DISCORD_API_URL}/oauth2/token", data=request_data["data"], headers=request_data["headers"] ) return response.json() def get_raw_user_data(self, access_token: str): """ Fetches the raw user data using the given access token. A call is made to the Discord API. """ log.debug("fetching raw user data") response = requests.get( url=f"{settings.DISCORD_API_URL}/users/@me", headers={"Authorization": f"Bearer {access_token}"} ) data = response.json() # Assign the default avatar if not data.get("avatar"): data["avatar"] = int(data["id"]) % 5 return data class Login(TemplateView): template_name = "accounts/login.html" class GuildChannelsView(View): def get(self, request, *args, **kwargs): log.debug("fetching channels from guid") guild_id = request.GET.get("guild") response = requests.get( url=f"{settings.DISCORD_API_URL}/guilds/{guild_id}/channels", headers={"Authorization": f"Bot {settings.BOT_TOKEN}"} ) return JsonResponse(response.json(), status=response.status_code, safe=False)