from django.conf import settings from django.http import HttpRequest from typing import Optional, Any, Dict, Literal, TYPE_CHECKING, cast from allauth.account.adapter import DefaultAccountAdapter # type: ignore[import] from allauth.account.models import EmailConfirmation, EmailAddress # type: ignore[import] from allauth.socialaccount.adapter import DefaultSocialAccountAdapter # type: ignore[import] from allauth.socialaccount.models import SocialLogin # type: ignore[import] from django.contrib.auth import get_user_model from django.contrib.sites.shortcuts import get_current_site if TYPE_CHECKING: from django.contrib.auth.models import AbstractUser User = get_user_model() class CustomAccountAdapter(DefaultAccountAdapter): def is_open_for_signup(self, request: HttpRequest) -> Literal[True]: """ Whether to allow sign ups. """ return True def get_email_confirmation_url(self, request: HttpRequest, emailconfirmation: EmailConfirmation) -> str: """ Constructs the email confirmation (activation) url. """ get_current_site(request) # Ensure the key is treated as a string for the type checker key = cast(str, getattr(emailconfirmation, "key", "")) return f"{settings.LOGIN_REDIRECT_URL}verify-email?key={key}" def send_confirmation_mail(self, request: HttpRequest, emailconfirmation: EmailConfirmation, signup: bool) -> None: """ Sends the confirmation email. """ current_site = get_current_site(request) activate_url = self.get_email_confirmation_url(request, emailconfirmation) # Cast key to str for typing consistency and template context key = cast(str, getattr(emailconfirmation, "key", "")) # Determine template early if signup: email_template = "account/email/email_confirmation_signup" else: email_template = "account/email/email_confirmation" # Cast the possibly-unknown email_address to EmailAddress so the type checker knows its attributes email_address = cast(EmailAddress, getattr(emailconfirmation, "email_address", None)) # Safely obtain email string (fallback to any top-level email on confirmation) email_str = cast(str, getattr(email_address, "email", getattr(emailconfirmation, "email", ""))) # Safely obtain the user object, cast to the project's User model for typing user_obj = cast("AbstractUser", getattr(email_address, "user", None)) # Explicitly type the context to avoid partial-unknown typing issues ctx: Dict[str, Any] = { "user": user_obj, "activate_url": activate_url, "current_site": current_site, "key": key, } # Remove unnecessary cast; ctx is already Dict[str, Any] self.send_mail(email_template, email_str, ctx) # type: ignore class CustomSocialAccountAdapter(DefaultSocialAccountAdapter): def is_open_for_signup(self, request: HttpRequest, sociallogin: SocialLogin) -> Literal[True]: """ Whether to allow social account sign ups. """ return True def populate_user( self, request: HttpRequest, sociallogin: SocialLogin, data: Dict[str, Any] ) -> "AbstractUser": # type: ignore[override] """ Hook that can be used to further populate the user instance. """ user = super().populate_user(request, sociallogin, data) # type: ignore if getattr(sociallogin.account, "provider", None) == "discord": # type: ignore user.discord_id = getattr(sociallogin.account, "uid", None) # type: ignore return cast("AbstractUser", user) # Ensure return type is explicit def save_user( self, request: HttpRequest, sociallogin: SocialLogin, form: Optional[Any] = None ) -> "AbstractUser": # type: ignore[override] """ Save the newly signed up social login. """ user = super().save_user(request, sociallogin, form) # type: ignore if user is None: raise ValueError("User creation failed") return cast("AbstractUser", user) # Ensure return type is explicit