from typing import TypedDict, NotRequired from nyekiapi.lib import settings, cache from nyekiapi.lib.requests import httpx from nyekiapi.lib.integrations import Profile CACHE_BY_ID_KEY = "discord:profile:id:%s" class DiscordProfile(TypedDict): id: str username: str global_name: str | None discriminator: str avatar: str | None banner: NotRequired[str | None] def _get_username(user: DiscordProfile) -> str: if user["discriminator"] == "0": return user["username"] return f"{user['username']}#{user['discriminator']}" def _get_avatar_url(user: DiscordProfile) -> str: if user["avatar"] is None: if user["discriminator"] == "0": return f"https://cdn.discordapp.com/embed/avatars/{(int(user['id']) >> 22) % 6}.png?size=256" else: return f"https://cdn.discordapp.com/embed/avatars/{int(user['discriminator']) % 6}.png?size=256" else: return f"https://cdn.discordapp.com/avatars/{user['id']}/{user['avatar']}.png?size=256" def _get_banner_url(user: DiscordProfile) -> str | None: if "banner" not in user or user["banner"] is None: return None return f"https://cdn.discordapp.com/banners/{user['id']}/{user['banner']}.png?size=2048" async def get_profile_by_id(id: str) -> Profile: profile = await cache.get(CACHE_BY_ID_KEY % id) if profile is not None: return Profile.from_json(profile) response = await httpx.get( f"https://discord.com/api/v10/users/{id}", headers={"Authorization": f"Bot {settings.DISCORD_TOKEN}"}, ) response.raise_for_status() data: DiscordProfile = response.json() profile = Profile( id=data["id"], url=f"https://discord.com/users/{data['id']}", name=data.get("global_name") or _get_username(data), username=_get_username(data), avatar_url=_get_avatar_url(data), banner_url=_get_banner_url(data), ) await cache.set(CACHE_BY_ID_KEY % id, profile, expires_in=10 * 60) return profile