from typing import Optional, Self import discord import twitchio from meta import LionBot from utils.lib import utc_now from . import logger from .data import ProfileData class UserProfile: def __init__(self, bot: LionBot, profile_row): self.bot = bot self.profile_row: ProfileData.UserProfileRow = profile_row @property def cog(self): return self.bot.get_cog('ProfileCog') @property def data(self) -> ProfileData: return self.cog.data @property def profileid(self): return self.profile_row.profileid def __repr__(self): return f"" async def get_name(self): # TODO: Store a preferred name in the profile preferences # TODO Should have a multi-fetch system name = None twitches = await self.twitch_accounts() if twitches: users = await self.bot.crocbot.fetch_users( ids=[int(twitches[0].userid)] ) if users: user = users[0] name = user.display_name if not name: discords = await self.discord_accounts() if discords: user = await self.bot.fetch_user(discords[0].userid) name = user.display_name if not name: name = 'Unknown' return name async def attach_discord(self, user: discord.User | discord.Member): """ Attach a new discord user to this profile. Assumes the discord user does not itself have a profile. """ discord_row = await self.data.DiscordProfileRow.create( profileid=self.profileid, userid=user.id ) logger.info( f"Attached discord user {user!r} to profile {self!r}" ) return discord_row async def attach_twitch(self, user: twitchio.User): """ Attach a new Twitch user to this profile. """ twitch_row = await self.data.TwitchProfileRow.create( profileid=self.profileid, userid=str(user.id) ) logger.info( f"Attached twitch user {user!r} to profile {self!r}" ) return twitch_row async def discord_accounts(self) -> list[ProfileData.DiscordProfileRow]: """ Fetch the Discord accounts associated to this profile. """ return await self.data.DiscordProfileRow.fetch_where( profileid=self.profileid ).order_by( 'created_at' ) async def twitch_accounts(self) -> list[ProfileData.TwitchProfileRow]: """ Fetch the Twitch accounts associated to this profile. """ return await self.data.TwitchProfileRow.fetch_where( profileid=self.profileid ).order_by( 'created_at' ) @classmethod async def fetch(cls, bot: LionBot, profile_id: int) -> Self: profile_row = await bot.get_cog('ProfileCog').data.UserProfileRow.fetch(profile_id) if profile_row is None: raise ValueError("Provided profile_id does not exist.") return cls(bot, profile_row) @classmethod async def fetch_from_twitchid(cls, bot: LionBot, userid: int | str) -> Optional[Self]: data = bot.get_cog('ProfileCog').data rows = await data.TwitchProfileRow.fetch_where(userid=str(userid)) if rows: return await cls.fetch(bot, rows[0].profileid) @classmethod async def fetch_from_discordid(cls, bot: LionBot, userid: int) -> Optional[Self]: data = bot.get_cog('ProfileCog').data rows = await data.DiscordProfileRow.fetch_where(userid=(userid)) if rows: return await cls.fetch(bot, rows[0].profileid) @classmethod async def create(cls, bot: LionBot, **kwargs) -> Self: """ Create a new empty profile with the given initial arguments. Profiles should usually be created using `create_from_discord` or `create_from_twitch` to correctly setup initial profile preferences (e.g. name, avatar). """ # Create a new profile data = bot.get_cog('ProfileCog').data profile_row = await data.UserProfileRow.create(created_at=utc_now()) profile = await cls.fetch(bot, profile_row.profileid) return profile @classmethod async def create_from_discord(cls, bot: LionBot, user: discord.Member | discord.User, **kwargs) -> Self: """ Create a new profile using the given Discord user as a base. """ profile = await cls.create(bot, **kwargs) await profile.attach_discord(user) return profile @classmethod async def create_from_twitch(cls, bot: LionBot, user: twitchio.User, **kwargs) -> Self: """ Create a new profile using the given Twitch user as a base. """ profile = await cls.create(bot, **kwargs) await profile.attach_twitch(user) return profile