134 lines
3.9 KiB
Python

# -*- 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)