From bf835e252907238abae2df8530b76888d846075e Mon Sep 17 00:00:00 2001 From: Interitio Date: Mon, 28 Jul 2025 14:49:17 +1000 Subject: [PATCH] Add subathon timer websocket. --- src/modules/subathons/component.py | 68 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/modules/subathons/component.py b/src/modules/subathons/component.py index 477fe02..f743f5e 100644 --- a/src/modules/subathons/component.py +++ b/src/modules/subathons/component.py @@ -8,10 +8,58 @@ from twitchio.ext import commands as cmds from datamodels import BotChannel, Communities, UserProfile from meta import CrocBot from utils.lib import utc_now, strfdelta +from sockets import Channel, register_channel from . import logger from .data import SubathonData, Subathon, RunningSubathon, SubathonContribution, SubathonGoal + +class TimerChannel(Channel): + name = 'Timer' + + def __init__(self, cog: 'SubathonComponent', **kwargs): + super().__init__(**kwargs) + self.cog = cog + + self.communityid = 1 + + async def on_connection(self, websocket, event): + await super().on_connection(websocket, event) + await self.send_set( + **await self.get_args_for(self.communityid), + websocket=websocket, + ) + + async def send_updates(self): + await self.send_set( + **await self.get_args_for(self.communityid), + ) + + async def get_args_for(self, channelid): + active = await self.cog.get_active_subathon(channelid) + if active is not None: + ending = utc_now() + timedelta(seconds=await active.get_remaining()) + return { + 'end_at': ending, + 'running': active.running + } + else: + return { + 'end_at': utc_now, + 'running': False, + } + + async def send_set(self, end_at, running, websocket=None): + await self.send_event({ + 'type': "DO", + 'method': 'setTimer', + 'args': { + 'end_at': end_at.isoformat(), + 'running': running, + } + }, websocket=websocket) + + class ActiveSubathon: def __init__(self, subathondata: Subathon, runningdata: RunningSubathon | None): self.subathondata = subathondata @@ -74,6 +122,8 @@ class SubathonComponent(cmds.Component): def __init__(self, bot: CrocBot): self.bot = bot self.data = bot.dbconn.load_registry(SubathonData()) + self.channel = TimerChannel(self) + register_channel('SubTimer', self.channel) # ----- API ----- @@ -135,7 +185,7 @@ class SubathonComponent(cmds.Component): f"{name} contributed {bits_payload.bits} bit{pl} and added {added} to the timer! Thank you <3", sender=self.bot.bot_id ) - # TODO: Websocket update + await self.channel.send_updates() await self.goalcheck(active, bits_payload.broadcaster) # Check goals @@ -174,7 +224,7 @@ class SubathonComponent(cmds.Component): f"{name} contributed {score} sub{pl} and added {added} to the timer! Thank you <3", sender=self.bot.bot_id ) - # TODO: Websocket update + await self.channel.send_updates() # Check goals await self.goalcheck(active, sub_payload.broadcaster) @@ -209,7 +259,7 @@ class SubathonComponent(cmds.Component): f"{name} contributed {score} subs and added {added} to the timer! Thank you <3", sender=self.bot.bot_id ) - # TODO: Websocket update + await self.channel.send_updates() # Check goals await self.goalcheck(active, gift_payload.broadcaster) @@ -225,7 +275,7 @@ class SubathonComponent(cmds.Component): "Paused the subathon timer because the stream went offline!", sender=self.bot.bot_id ) - # TODO: Websocket update + await self.channel.send_updates() # ----- Commands ----- @@ -239,7 +289,7 @@ class SubathonComponent(cmds.Component): goals = await active.get_goals() total_goals = len(goals) donegoals = len([goal for goal in goals if score >= goal.required_score]) - goalstr = f"{donegoals}/{total_goals} achieved" + goalstr = f"{donegoals}/{total_goals} goals achieved" secs = await active.get_remaining() remaining = strfdelta(timedelta(seconds=secs)) @@ -275,7 +325,7 @@ class SubathonComponent(cmds.Component): score_time=timescore ) await ctx.reply("Setup a new subathon! Use !subathon resume to get the timer running.") - # TODO: Websocket broadcast + await self.channel.send_updates() # subathon stop @group_subathon.command(name='stop') @@ -286,7 +336,7 @@ class SubathonComponent(cmds.Component): if (active := await self.get_active_subathon(cid)) is not None: if active.running: await active.pause() - # TODO: Websocket update + await self.channel.send_updates() await active.subathondata.update(ended_at=utc_now()) total = await active.get_score() dursecs = active.get_duration() @@ -308,7 +358,7 @@ class SubathonComponent(cmds.Component): if active.running: await active.pause() await ctx.reply("Subathon timer paused!") - # TODO: Websocket update + await self.channel.send_updates() else: await ctx.reply("Subathon timer already paused!") else: @@ -324,7 +374,7 @@ class SubathonComponent(cmds.Component): if not active.running: await active.resume() await ctx.reply("Subathon timer resumed!") - # TODO: Websocket update + await self.channel.send_updates() else: await ctx.reply("Subathon timer already running!") else: