From 3d5f2e3de433d100cdbf9993455ad036e76765dc Mon Sep 17 00:00:00 2001 From: insert Date: Wed, 16 Jul 2025 01:04:13 -0400 Subject: [PATCH] feat: feed subscriptions --- bot.py | 5 +++ cogs/liveupdate.py | 107 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/bot.py b/bot.py index 5765105..e9467a6 100644 --- a/bot.py +++ b/bot.py @@ -21,6 +21,7 @@ class Bot(commands.Bot): await cur.execute("DELETE from teamsubscriptions WHERE serverid = ?", (guild.id,)) await cur.execute("DELETE from liveupdate WHERE serverid = ?", (guild.id,)) await cur.execute("DELETE from spotlightsubscriptions WHERE serverid = ?", (guild.id,)) + await cur.execute("DELETE from feedsubscriptions WHERE serverid = ?", (guild.id,)) await db.commit() load_dotenv() @@ -49,6 +50,10 @@ async def on_ready(): if await res.fetchone() is None: await cur.execute("CREATE TABLE teamsubscriptions(serverid INTEGER, channelid INTEGER NOT NULL, teamid TEXT, classic INTEGER NOT NULL)") await db.commit() + res = await cur.execute("SELECT name FROM sqlite_master WHERE name='feedsubscriptions'") + if await res.fetchone() is None: + await cur.execute("CREATE TABLE feedsubscriptions(serverid INTEGER, channelid INTEGER NOT NULL, teamid TEXT, offset INTEGER)") + await db.commit() #await cur.execute("ALTER TABLE liveupdate ADD classic INTEGER NOT NULL DEFAULT 0") #await cur.execute("ALTER TABLE spotlightsubscriptions ADD classic INTEGER NOT NULL DEFAULT 0") #await cur.execute("ALTER TABLE teamsubscriptions ADD classic INTEGER NOT NULL DEFAULT 0") diff --git a/cogs/liveupdate.py b/cogs/liveupdate.py index 1b02a14..160e2b0 100644 --- a/cogs/liveupdate.py +++ b/cogs/liveupdate.py @@ -6,6 +6,7 @@ import requests import asyncio import nextcord import aiohttp +import itertools import aiosqlite as sqlite3 from nextcord.ext import commands, application_checks, tasks from nextcord import TextInputStyle, IntegrationType @@ -191,6 +192,8 @@ async def teamsubscriptionworker(self,serverid,channelid,teamid,classic): data = await session.get(f"https://mmolb.com/api/game/{gameid}") data = await data.json() channel = self.bot.get_channel(channelid) + if channel is None: + return if data["State"] == "Complete": return check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic)) @@ -221,6 +224,36 @@ async def teamsubscriptionworker(self,serverid,channelid,teamid,classic): print(e) return +async def feedsubscriptionworker(self,serverid,channelid,teamid,offset): + begin = timeit.default_timer() + lastserverid = serverid + try: + async with aiohttp.ClientSession() as session: + channel = self.bot.get_channel(channelid) + if channel is None: + return + data = await session.get(f"https://mmolb.com/api/team/{teamid}") + data = await data.json() + data = data['Feed'] + if len(data) > offset: + finalstr = "" + for index in itertools.islice(data, offset, len(data)): + finalstr += f"\n{index['emoji']} Season {index['season']} Day {index['day']}: {index['text']}" + await channel.send(finalstr) + await self.bot.db.execute(f""" + UPDATE feedsubscriptions set offset = {len(data)} WHERE channelid = '{channelid}' AND teamid = '{teamid}' + """) + except Exception as e: + warning = self.bot.get_channel(1365478368555827270) + await warning.send(e) + except nextcord.Forbidden: + warning = self.bot.get_channel(1365478368555827270) + await self.bot.db.execute("DELETE from teamsubscriptions WHERE serverid = ?", (lastserverid,)) + await self.bot.db.execute("DELETE from liveupdate WHERE serverid = ?", (lastserverid,)) + await self.bot.db.execute("DELETE from spotlightsubscriptions WHERE serverid = ?", (lastserverid,)) + await self.bot.db.execute("DELETE from feedsubscriptions WHERE serverid = ?", (lastserverid,)) + await self.bot.db.commit() + await warning.send(f"Deleted {lastserverid} from the database due to 403 error") class liveupdate(commands.Cog): @@ -228,6 +261,7 @@ class liveupdate(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.updatelivegames.start() + self.checkfeedsubscriptions.start() self.checkspotlightsubscriptions.start() self.checkteamsubscriptions.start() @@ -235,6 +269,7 @@ class liveupdate(commands.Cog): self.updatelivegames.cancel() self.checkspotlightsubscriptions.cancel() self.checkteamsubscriptions.cancel() + self.checkfeedsubscriptions.cancel() @nextcord.slash_command( name="spotlightgame", @@ -390,6 +425,36 @@ class liveupdate(commands.Cog): thanksdiscord = closestteam[:20] await interaction.response.send_autocomplete(thanksdiscord) + @subscribe.subcommand( + name="feed", + description="Subscribe to a team's feed", + ) + async def feedsubscribe(self, interaction: nextcord.Interaction, team: str): + if team not in self.bot.teams_dict: + await interaction.response.send_message("Invalid Team!", ephemeral=True) + await interaction.response.defer() + teamid = self.bot.teams_dict[team] + async with aiohttp.ClientSession() as session: + data = await session.get(f"https://mmolb.com/api/team/{teamid}") + data = await data.json() + offset = len(data['Feed']) + await self.bot.db.execute(f""" + INSERT INTO feedsubscriptions VALUES + ({interaction.guild_id}, {interaction.channel_id}, "{teamid}", {offset}) + """) + await self.bot.db.commit() + await interaction.edit_original_message(content="This channel is now subscribed to feed updates") + + @feedsubscribe.on_autocomplete("team") + async def feedsubscribeac(self, interaction: nextcord.Interaction, team: str): + if not team: + thanksdiscord = self.bot.teams_list[:20] + await interaction.response.send_autocomplete(thanksdiscord) + return + closestteam = [name for name in self.bot.teams_list if name.lower().startswith(team.lower())] + thanksdiscord = closestteam[:20] + await interaction.response.send_autocomplete(thanksdiscord) + @nextcord.slash_command( name="watch", description="Get live updates on a game", @@ -474,6 +539,32 @@ class liveupdate(commands.Cog): thanksdiscord = closestteam[:20] await interaction.response.send_autocomplete(thanksdiscord) + + @unsubscribe.subcommand( + name="feed", + description="Unsubscribe to team feed updates in this channel", + ) + async def unsubscribefeed(self, interaction: nextcord.Interaction, team: str): + if team not in self.bot.teams_dict: + await interaction.response.send_message("Invalid Team!", ephemeral=True) + await interaction.response.defer() + teamid = self.bot.teams_dict[team] + await self.bot.db.execute(f""" + DELETE from feedsubscriptions WHERE channelid = ? AND teamid = ? + """,(interaction.channel_id,teamid)) + await self.bot.db.commit() + await interaction.edit_original_message(content="If existent, it has been removed") + + @unsubscribefeed.on_autocomplete("team") + async def unsubscribefeedac(self, interaction: nextcord.Interaction, team: str): + if not team: + thanksdiscord = self.bot.teams_list[:20] + await interaction.response.send_autocomplete(thanksdiscord) + return + closestteam = [name for name in self.bot.teams_list if name.lower().startswith(team.lower())] + thanksdiscord = closestteam[:20] + await interaction.response.send_autocomplete(thanksdiscord) + @nextcord.slash_command( name="liveupdatesdelete", description="DEBUG: Delete a subscribed update", @@ -577,6 +668,22 @@ class liveupdate(commands.Cog): await warning.send(e) print(e) return + + @tasks.loop(seconds=120.0) + async def checkfeedsubscriptions(self): + try: + await self.bot.wait_until_ready() + res = await self.bot.db.execute("SELECT serverid,channelid,teamid,offset FROM feedsubscriptions") + res = await res.fetchall() + worklist = [feedsubscriptionworker(self,serverid,channelid,teamid,offset) for [serverid,channelid,teamid,offset] in res] + await asyncio.gather(*worklist,return_exceptions=True) + await self.bot.db.commit() + except Exception as e: + #I know this is bad practice but these loops must be running + warning = self.bot.get_channel(1365478368555827270) + await warning.send(e) + print(e) + return def setup(bot: commands.Bot): bot.add_cog(liveupdate(bot)) \ No newline at end of file