From 2e02f39c297e3a353dbdee69b4da10cc1ded8b43 Mon Sep 17 00:00:00 2001 From: Interitio Date: Wed, 3 Sep 2025 20:19:21 +1000 Subject: [PATCH] feat: Extend Context and add Profile module. --- src/meta/__init__.py | 1 + src/meta/bot.py | 49 +++++++++++++++++++++++++++++++++----------- src/meta/context.py | 4 ++++ 3 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/meta/context.py diff --git a/src/meta/__init__.py b/src/meta/__init__.py index c01addd..9eef7af 100644 --- a/src/meta/__init__.py +++ b/src/meta/__init__.py @@ -1,4 +1,5 @@ from .args import args from .bot import Bot +from .context import Context from .config import Conf, conf from .logger import setup_main_logger, log_context, log_action_stack, log_app, set_logging_context, logging_context, with_log_ctx, persist_task diff --git a/src/meta/bot.py b/src/meta/bot.py index 79cf7f4..bf8266b 100644 --- a/src/meta/bot.py +++ b/src/meta/bot.py @@ -1,5 +1,5 @@ import logging -from typing import Optional +from typing import TYPE_CHECKING, Any, Literal, Optional, overload from twitchio.authentication import UserTokenPayload from twitchio.ext import commands @@ -10,6 +10,10 @@ from botdata import BotData, UserAuth, BotChannel, VersionHistory from constants import BOTUSER_SCOPES, CHANNEL_SCOPES, SCHEMA_VERSIONS from .config import Conf +from .context import Context + +if TYPE_CHECKING: + from modules.profiles.profiles.twitch.component import ProfilesComponent logger = logging.getLogger(__name__) @@ -24,6 +28,7 @@ class Bot(commands.Bot): super().__init__(*args, **kwargs) + # Whether we should do eventsub via webhooks or websockets if config.bot.get('eventsub_secret', None): self.using_webhooks = True else: @@ -36,6 +41,28 @@ class Bot(commands.Bot): self.joined: dict[str, BotChannel] = {} + # Make the type checker happy about fetching components by name + # TODO: Move to stubs + + @property + def profiles(self): + return self.get_component('ProfilesComponent') + + @overload + def get_component(self, name: Literal['ProfilesComponent']) -> 'ProfilesComponent': + ... + + @overload + def get_component(self, name: str) -> Optional[commands.Component]: + ... + + def get_component(self, name: str) -> Optional[commands.Component]: + return super().get_component(name) + + def get_context(self, payload, *, cls: Any = None) -> Context: + cls = cls or Context + return cls(payload, bot=self) + async def event_ready(self): # logger.info(f"Logged in as {self.nick}. User id is {self.user_id}") logger.info("Logged in as %s", self.bot_id) @@ -151,17 +178,15 @@ class Bot(commands.Bot): # Save the token and scopes to data # Wrap this in a transaction so if it fails halfway we rollback correctly - async with self.dbconn.connection() as conn: - self.dbconn.conn = conn - async with conn.transaction(): - row = await UserAuth.fetch_or_create(userid, token=token, refresh_token=refresh) - if row.token != token or row.refresh_token != refresh: - await row.update(token=token, refresh_token=refresh) - await self.data.user_auth_scopes.delete_where(userid=userid) - await self.data.user_auth_scopes.insert_many( - ('userid', 'scope'), - *((userid, scope) for scope in new_scopes) - ) + # TODO + row = await UserAuth.fetch_or_create(userid, token=token, refresh_token=refresh) + if row.token != token or row.refresh_token != refresh: + await row.update(token=token, refresh_token=refresh) + await self.data.user_auth_scopes.delete_where(userid=userid) + await self.data.user_auth_scopes.insert_many( + ('userid', 'scope'), + *((userid, scope) for scope in new_scopes) + ) logger.info("Updated auth token for user '%s' with scopes: %s", resp.user_id, ', '.join(new_scopes)) return resp diff --git a/src/meta/context.py b/src/meta/context.py new file mode 100644 index 0000000..2001830 --- /dev/null +++ b/src/meta/context.py @@ -0,0 +1,4 @@ +from twitchio.ext import commands as cmds + +class Context(cmds.Context): + ...