feat: Add last_seen cache to dispatch.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
from collections import defaultdict
|
||||||
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_seen: dict[int, dict[int, datetime]] = defaultdict(dict)
|
||||||
self._last_deleted: dict[int, datetime] = {}
|
self._last_deleted: dict[int, datetime] = {}
|
||||||
self.hyperfocus_lock = asyncio.Lock()
|
self.hyperfocus_lock = asyncio.Lock()
|
||||||
|
|
||||||
@@ -47,6 +49,44 @@ class FocusComponent(cmds.Component):
|
|||||||
async def component_load(self):
|
async def component_load(self):
|
||||||
await self.data.init()
|
await self.data.init()
|
||||||
|
|
||||||
|
async def dispatch_focuser_update(
|
||||||
|
self,
|
||||||
|
profileid: int,
|
||||||
|
communityid: int | None = None,
|
||||||
|
focuser: Hyperfocuser | None = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Dispatch the given profile's hyperfocus status along the channel.
|
||||||
|
|
||||||
|
If the communityid is given, ensures that community receives the update.
|
||||||
|
"""
|
||||||
|
if focuser is None:
|
||||||
|
focuser = await self.get_hyperfocus(profileid)
|
||||||
|
elif focuser.profileid != profileid:
|
||||||
|
raise ValueError("Mis-matching profileid and focuser provided")
|
||||||
|
|
||||||
|
targets = set()
|
||||||
|
if communityid is not None:
|
||||||
|
targets.add(communityid)
|
||||||
|
|
||||||
|
now = utc_now()
|
||||||
|
|
||||||
|
if focuser is not None:
|
||||||
|
# If we are sending active, send to all last seens newer than a certain date, and delete any old ones
|
||||||
|
last_seen_cids = self._last_seen[profileid].items()
|
||||||
|
for cid, last_seen in last_seen_cids:
|
||||||
|
if (now - last_seen).total_seconds() < 4 * 3600:
|
||||||
|
targets.add(cid)
|
||||||
|
for cid in targets:
|
||||||
|
await self.channel.send_hyperfocus_patch(cid, focuser)
|
||||||
|
else:
|
||||||
|
# If we are deleting, send to *all* last seens, and then delete any old ones
|
||||||
|
# targets.update(self._last_seen[profileid].keys())
|
||||||
|
# for cid in targets:
|
||||||
|
await self.channel.send_hyperfocus_del(profileid)
|
||||||
|
# TODO: Cleanup old entries in last_seen
|
||||||
|
# TODO: Would prefer to use stream time and database member last seen instead
|
||||||
|
|
||||||
async def get_hyperfocus(self, profileid: int) -> Hyperfocuser | None:
|
async def get_hyperfocus(self, profileid: int) -> Hyperfocuser | None:
|
||||||
"""
|
"""
|
||||||
Get the Hyperfocuser if the user is hyperfocused.
|
Get the Hyperfocuser if the user is hyperfocused.
|
||||||
@@ -96,6 +136,11 @@ class FocusComponent(cmds.Component):
|
|||||||
async def handle_message(self, payload: twitchio.ChatMessage):
|
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)
|
||||||
|
comm = await self.bot.profiles.fetch_community(payload.broadcaster, touch=True)
|
||||||
|
|
||||||
|
now = utc_now()
|
||||||
|
self._last_seen[profile.profileid][comm.communityid] = now
|
||||||
|
|
||||||
hyperfocused = await self.get_hyperfocus(profile.profileid)
|
hyperfocused = await self.get_hyperfocus(profile.profileid)
|
||||||
|
|
||||||
# If they are, check the message content for deletion
|
# If they are, check the message content for deletion
|
||||||
@@ -103,7 +148,7 @@ class FocusComponent(cmds.Component):
|
|||||||
# If we need to delete, run delete and send message
|
# If we need to delete, run delete and send message
|
||||||
notify = ( #
|
notify = ( #
|
||||||
not (last := self._last_deleted.get(profile.profileid))
|
not (last := self._last_deleted.get(profile.profileid))
|
||||||
or (utc_now() - last).total_seconds() > 30
|
or (now - last).total_seconds() > 30
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await self.focus_delete_message(payload)
|
await self.focus_delete_message(payload)
|
||||||
@@ -115,7 +160,7 @@ class FocusComponent(cmds.Component):
|
|||||||
deleted = False
|
deleted = False
|
||||||
|
|
||||||
if notify:
|
if notify:
|
||||||
self._last_deleted[profile.profileid] = utc_now()
|
self._last_deleted[profile.profileid] = now
|
||||||
try:
|
try:
|
||||||
if deleted:
|
if deleted:
|
||||||
await payload.broadcaster.send_message(
|
await payload.broadcaster.send_message(
|
||||||
@@ -136,16 +181,12 @@ class FocusComponent(cmds.Component):
|
|||||||
|
|
||||||
if hyperfocused:
|
if hyperfocused:
|
||||||
# Send an update to the channel
|
# Send an update to the channel
|
||||||
# TODO: Nicer and more efficient caching of which channel has which users
|
|
||||||
# TODO: Possible race condition with the commands. Should use locks.
|
|
||||||
comm = await self.bot.profiles.fetch_community(
|
|
||||||
payload.broadcaster, touch=True
|
|
||||||
)
|
|
||||||
await self.channel.send_hyperfocus_patch(comm.communityid, hyperfocused)
|
await self.channel.send_hyperfocus_patch(comm.communityid, hyperfocused)
|
||||||
|
|
||||||
# ------ Commands -----
|
# ------ Commands -----
|
||||||
@cmds.command(
|
@cmds.command(
|
||||||
name="hyperfocus", aliases=["hyperf", "hyper", "hypercrocus", "hyperofcus"]
|
name="hyperfocus",
|
||||||
|
aliases=["hfocus", "hyperf", "hyper", "hypercrocus", "hyperofcus"],
|
||||||
)
|
)
|
||||||
async def hyperfocus_cmd(self, ctx, *, duration: str | None = None):
|
async def hyperfocus_cmd(self, ctx, *, duration: str | None = None):
|
||||||
now = utc_now()
|
now = utc_now()
|
||||||
@@ -188,7 +229,7 @@ class FocusComponent(cmds.Component):
|
|||||||
started_in=comm.communityid,
|
started_in=comm.communityid,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.channel.send_hyperfocus_patch(comm.communityid, focuser)
|
await self.dispatch_focuser_update(pid, comm.communityid, focuser)
|
||||||
|
|
||||||
minutes = ceil(dur / 60)
|
minutes = ceil(dur / 60)
|
||||||
await ctx.reply(
|
await ctx.reply(
|
||||||
@@ -197,7 +238,7 @@ class FocusComponent(cmds.Component):
|
|||||||
"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", aliases=["uncrocus"])
|
||||||
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)
|
||||||
async with self.hyperfocus_lock:
|
async with self.hyperfocus_lock:
|
||||||
@@ -239,10 +280,11 @@ class FocusComponent(cmds.Component):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@cmds.command(name="focuslist")
|
@cmds.command(name="focuslist")
|
||||||
@cmds.is_moderator()
|
|
||||||
async def focuslist_cmd(self, ctx):
|
async def focuslist_cmd(self, ctx):
|
||||||
comm = await self.bot.profiles.fetch_community(ctx.broadcaster, touch=True)
|
comm = await self.bot.profiles.fetch_community(ctx.broadcaster, touch=True)
|
||||||
link = (
|
link = (
|
||||||
f"https://croccyfocus.thewisewolf.dev/widget/?community={comm.communityid}"
|
f"https://croccyfocus.thewisewolf.dev/widget/?community={comm.communityid}"
|
||||||
)
|
)
|
||||||
await ctx.reply(f"Browser source link for your channel's hyperfocus: {link}")
|
await ctx.reply(
|
||||||
|
f"Browser source link for your channel's hyperfocus: {link} (For troubleshooting: your community id is {comm.communityid})"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user