Profile schema and discord hook

This commit is contained in:
2025-08-25 23:23:41 +10:00
parent fc3bdcbb5c
commit 5fa0df66f5
8 changed files with 307 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import logging
logger = logging.getLogger(__name__)
from .discord import setup

81
profiles/data.py Normal file
View File

@@ -0,0 +1,81 @@
from data import Registry, Table, RowModel
from data.columns import String, Integer, Timestamp
class UserProfile(RowModel):
_tablename_ = 'user_profiles'
_cache_ = {}
profileid = Integer(primary=True)
nickname = String()
timezone = String()
locale_hint = String()
locale = String()
avatar = String()
migrated = Integer()
created_at = Timestamp()
last_seen = Timestamp()
_timestamp = Timestamp()
class DiscordProfileLink(RowModel):
_tablename_ = 'profiles_discord'
_cache_ = {}
linkid = Integer()
profileid = Integer()
userid = Integer(primary=True)
linked_at = Timestamp()
class TwitchProfileLink(RowModel):
_tablename_ = 'profiles_twitch'
_cache_ = {}
linkid = Integer()
profileid = Integer()
userid = String(primary=True)
linked_at = Timestamp()
class Community(RowModel):
_tablename_ = 'communities'
_cache_ = {}
communityid = Integer(primary=True)
migrated = Integer()
created_at = Timestamp()
last_seen = Timestamp()
_timestamp = Timestamp()
class DiscordCommunityLink(RowModel):
_tablename_ = 'communities_discord'
_cache_ = {}
linkid = Integer()
guildid = Integer(primary=True)
communityid = Integer()
linked_at = Timestamp()
class TwitchCommunityLink(RowModel):
_tablename_ = 'communities_twitch'
_cache_ = {}
linkid = Integer()
channelid = String(primary=True)
communityid = Integer()
linked_at = Timestamp()
class ProfilesData(Registry):
VERSION = ('PROFILES', 1)
user_profiles = UserProfile.table
profiles_discord = DiscordProfileLink.table
profiles_twitch = TwitchProfileLink.table
communities = Community.table
communities_discord = DiscordCommunityLink.table
communities_twitch = TwitchCommunityLink.table

View File

@@ -0,0 +1,5 @@
from .. import logger
async def setup(bot):
from .cog import ProfilesCog
await bot.add_cog(ProfilesCog(bot))

89
profiles/discord/cog.py Normal file
View File

@@ -0,0 +1,89 @@
from typing import Optional
import asyncio
import discord
from discord.ext import commands as cmds
from discord import app_commands as appcmds
from meta import LionBot, LionCog, LionContext
from meta.logger import log_wrap
from utils.lib import utc_now
from ..data import ProfilesData, UserProfile, DiscordProfileLink, Community, DiscordCommunityLink
from ..profiles import ProfilesRegistry
class ProfilesCog(LionCog):
def __init__(self, bot: LionBot):
self.bot = bot
self.data = bot.db.load_registry(ProfilesData())
self.profiles = ProfilesRegistry(self.data)
async def cog_load(self):
await self.data.init()
await self.bot.version_check(*self.data.VERSION)
await self.profiles.init()
async def bot_check_once(self, ctx: LionContext):
profile = await self.fetch_profile(ctx.author, interaction=ctx.interaction, touch=True)
ctx.profile = profile
if ctx.guild:
community = await self.fetch_community(ctx.guild, interaction=ctx.interaction, touch=True)
ctx.community = community
else:
ctx.community = None
return True
@log_wrap(isolate=True, action="Fetch Profile")
async def fetch_profile(
self,
user: discord.User | discord.Member,
interaction: Optional[discord.Interaction] = None,
touch: bool = False,
) -> UserProfile:
"""
Fetch or create the profile for the given user.
"""
async with self.bot.db.connection() as conn:
self.bot.db.conn = conn
async with conn.transaction():
profile = await self.profiles.get_profile_discord(user.id)
if profile is None:
# Create a new profile
# Then link with discord
args = {
'nickname': user.display_name,
}
if interaction:
if interaction.locale:
args['locale_hint'] = interaction.locale.language_code
if user.avatar:
args['avatar'] = user.avatar.url
profile = await UserProfile.create(**args)
await DiscordProfileLink.create(profileid=profile.profileid, userid=user.id)
elif touch:
await profile.update(last_seen=utc_now())
return profile
@log_wrap(isolate=True, action="Fetch Community")
async def fetch_community(
self,
guild: discord.Guild,
interaction: Optional[discord.Interaction] = None,
touch: bool = False,
) -> Community:
"""
Fetch or create the community for the given guild.
"""
async with self.bot.db.connection() as conn:
self.bot.db.conn = conn
async with conn.transaction():
comm = await self.profiles.get_community_discord(guild.id)
if comm is None:
# Create a new Community and link to Discord
comm = await Community.create()
await DiscordCommunityLink.create(guildid=guild.id, communityid=comm.communityid)
elif touch:
await comm.update(last_seen=utc_now())
return comm

46
profiles/profiles.py Normal file
View File

@@ -0,0 +1,46 @@
from typing import Optional
from .data import (
ProfilesData,
UserProfile,
DiscordProfileLink,
TwitchProfileLink,
Community,
DiscordCommunityLink,
TwitchCommunityLink,
)
class ProfilesRegistry:
VERSION = ProfilesData.VERSION
def __init__(self, data: ProfilesData):
self.data = data
async def init(self):
await self.data.init()
async def get_profile(self, profileid: int) -> Optional[UserProfile]:
return await UserProfile.fetch(profileid)
async def get_profile_discord(self, userid: int) -> Optional[UserProfile]:
link = await DiscordProfileLink.fetch(userid)
if link:
return await UserProfile.fetch(link.profileid)
async def get_profile_twitch(self, userid: str) -> Optional[UserProfile]:
link = await TwitchProfileLink.fetch(userid)
if link:
return await UserProfile.fetch(link.profileid)
async def get_community(self, communityid: int) -> Optional[Community]:
return await Community.fetch(communityid)
async def get_community_discord(self, guildid: int) -> Optional[Community]:
link = await DiscordCommunityLink.fetch(guildid)
if link:
return await Community.fetch(link.communityid)
async def get_community_twitch(self, channelid: str) -> Optional[Community]:
link = await TwitchCommunityLink.fetch(channelid)
if link:
return await Community.fetch(link.communityid)