fix: Add lock to prevent update race.

This commit is contained in:
2025-11-01 11:49:27 +10:00
parent a14cc4056e
commit 1bd2e9607a

View File

@@ -1,3 +1,4 @@
import asyncio
from string import punctuation from string import punctuation
import datetime as dt import datetime as dt
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -40,6 +41,7 @@ class FocusComponent(cmds.Component):
register_channel(self.channel.name, self.channel) register_channel(self.channel.name, self.channel)
self._last_deleted: dict[int, datetime] = {} self._last_deleted: dict[int, datetime] = {}
self.hyperfocus_lock = asyncio.Lock()
# ----- API ----- # ----- API -----
async def component_load(self): async def component_load(self):
@@ -88,6 +90,10 @@ class FocusComponent(cmds.Component):
@cmds.Component.listener() @cmds.Component.listener()
async def event_message(self, payload: twitchio.ChatMessage): async def event_message(self, payload: twitchio.ChatMessage):
async with self.hyperfocus_lock:
await self.handle_message(payload)
async def handle_message(self, payload: twitchio.ChatMessage):
# Check if chatter is currently hyperfocused # Check if chatter is currently hyperfocused
profile = await self.bot.profiles.fetch_profile(payload.chatter, touch=True) profile = await self.bot.profiles.fetch_profile(payload.chatter, touch=True)
hyperfocused = await self.get_hyperfocus(profile.profileid) hyperfocused = await self.get_hyperfocus(profile.profileid)
@@ -173,35 +179,37 @@ class FocusComponent(cmds.Component):
pid = profile.profileid pid = profile.profileid
comm = await self.bot.profiles.fetch_community(ctx.broadcaster, touch=True) comm = await self.bot.profiles.fetch_community(ctx.broadcaster, touch=True)
await Hyperfocuser.table.delete_where(profileid=pid) async with self.hyperfocus_lock:
focuser = await Hyperfocuser.create( await Hyperfocuser.table.delete_where(profileid=pid)
profileid=pid, focuser = await Hyperfocuser.create(
started_at=now, profileid=pid,
ends_at=end_at, started_at=now,
started_in=comm.communityid, ends_at=end_at,
) started_in=comm.communityid,
)
await self.channel.send_hyperfocus_patch(comm.communityid, focuser) await self.channel.send_hyperfocus_patch(comm.communityid, focuser)
minutes = ceil(dur / 60) minutes = ceil(dur / 60)
await ctx.reply( await ctx.reply(
f"{ctx.chatter.name} has gone into HYPERFOCUS! " f"{ctx.chatter.name} has gone into HYPERFOCUS! "
f"They will be in emote and command only mode for the next {minutes} minutes! " f"They will be in emote and command only mode for the next {minutes} minutes! "
"Use !unfocus to come back if you need to, best of luck! ☘️🍀☘️ " "Use !unfocus to come back if you need to, best of luck! ☘️🍀☘️ "
) )
@cmds.command(name="unfocus") @cmds.command(name="unfocus")
async def unfocus_cmd(self, ctx): async def unfocus_cmd(self, ctx):
profile = await self.bot.profiles.fetch_profile(ctx.chatter, touch=True) profile = await self.bot.profiles.fetch_profile(ctx.chatter, touch=True)
row = await Hyperfocuser.fetch(profile.profileid) async with self.hyperfocus_lock:
if row: row = await Hyperfocuser.fetch(profile.profileid)
await row.delete() if row:
await self.channel.send_hyperfocus_del(profile.profileid) await row.delete()
await self.channel.send_hyperfocus_del(profile.profileid)
await ctx.reply( await ctx.reply(
"Welcome back from focus, hope it went well!" "Welcome back from focus, hope it went well!"
" Remember to have a sip and stretch if you need it~" " Remember to have a sip and stretch if you need it~"
) )
@cmds.command(name="hyperfocused") @cmds.command(name="hyperfocused")
async def hyperfocused_cmd(self, ctx, user: twitchio.User | None = None): async def hyperfocused_cmd(self, ctx, user: twitchio.User | None = None):
@@ -209,22 +217,30 @@ class FocusComponent(cmds.Component):
profile = await self.bot.profiles.fetch_profile(user, touch=False) profile = await self.bot.profiles.fetch_profile(user, touch=False)
if hyper := (await self.get_hyperfocus(profile.profileid)): async with self.hyperfocus_lock:
durstr = strfdelta(hyper.ends_at - utc_now()) if hyper := (await self.get_hyperfocus(profile.profileid)):
await ctx.reply( durstr = strfdelta(hyper.ends_at - utc_now())
f"{user.name} is in HYPERFOCUS for another {durstr}! " await ctx.reply(
"They can only write emojis and commands in this time. Good luck!" f"{user.name} is in HYPERFOCUS for another {durstr}! "
) "They can only write emojis and commands in this time. Good luck!"
elif own: )
await ctx.reply( elif own:
"You are not hyperfocused!" await ctx.reply(
" Enter HYPERFOCUS mode for e.g. 10 minutes with '!hyperfocus 10'" "You are not hyperfocused!"
) " Enter HYPERFOCUS mode for e.g. 10 minutes with '!hyperfocus 10'"
else: )
await ctx.reply(f"{user.name} is not hyperfocused!") else:
await ctx.reply(f"{user.name} is not hyperfocused!")
@cmds.command(name="addfocus") @cmds.command(name="addfocus")
async def addfocus_cmd(self, ctx): async def addfocus_cmd(self, ctx):
await ctx.reply( await ctx.reply(
"Add HYPERFOCUS to your channel by authorising me here: https://croccyfocus.thewisewolf.dev/invite" "Add HYPERFOCUS to your channel by authorising me here: https://croccyfocus.thewisewolf.dev/invite"
) )
@cmds.command(name="focuslist")
@cmds.is_moderator()
async def focuslist_cmd(self, ctx):
comm = await self.bot.profiles.fetch_community(ctx.broadcaster, touch=True)
link = f"https://croccyfocus.thewisewolf.dev/widget/?community={comm}"
await ctx.reply(f"Browser source link for your channel's hyperfocus: {link}")