refactor: further optimizations for live games

This commit is contained in:
insert 2025-06-26 21:32:29 -04:00
parent 14f12210e6
commit a6e06e9e98
Signed by: insert
GPG key ID: A70775C389ACF105

View file

@ -5,6 +5,7 @@ from datetime import datetime, timedelta, timezone
import requests import requests
import asyncio import asyncio
import nextcord import nextcord
import aiohttp
import aiosqlite as sqlite3 import aiosqlite as sqlite3
from nextcord.ext import commands, application_checks, tasks from nextcord.ext import commands, application_checks, tasks
from nextcord import TextInputStyle, IntegrationType from nextcord import TextInputStyle, IntegrationType
@ -15,66 +16,71 @@ async def livegameworker(self,serverid,userid,channelid,messageid,gameid,offset)
begin = timeit.default_timer() begin = timeit.default_timer()
lastserverid = serverid lastserverid = serverid
try: try:
channel = self.bot.get_channel(channelid) async with aiohttp.ClientSession() as session:
message = await channel.fetch_message(messageid) channel = self.bot.get_channel(channelid)
data = requests.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset}").json() message = await channel.fetch_message(messageid)
if len(data["entries"]) > 0: data = await session.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset}")
data = requests.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset-((7-len(data["entries"]) if offset > 7 else offset))}").json() data = await data.json()
basedata = requests.get(f"https://mmolb.com/api/game/{gameid}").json() if len(data["entries"]) > 0:
finalstr = "" data = await session.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset-((7-len(data["entries"]) if offset > 7 else offset))}")
offsetadd = 0 data = await data.json()
for i in data["entries"]: basedata = await session.get(f"https://mmolb.com/api/game/{gameid}")
finalstr += f"\n{i['message'].replace("<strong>", "**").replace("</strong>", "**")}" basedata = await basedata.json()
offsetadd += 1 finalstr = ""
if i["event"] == "Recordkeeping": offsetadd = 0
await self.bot.db.execute(f""" for i in data["entries"]:
DELETE from liveupdate WHERE messageid = {messageid} finalstr += f"\n{i['message'].replace("<strong>", "**").replace("</strong>", "**")}"
""") offsetadd += 1
#await self.bot.db.commit() if i["event"] == "Recordkeeping":
color = tuple(int(basedata["HomeTeamColor"][i:i+2], 16) for i in (0, 2, 4)) await self.bot.db.execute(f"""
embed = nextcord.Embed(title=f"{basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} **{data["entries"][-1]["away_score"]}** vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} **{data["entries"][-1]["home_score"]}**", DELETE from liveupdate WHERE messageid = {messageid}
description=f"{"Bottom" if data["entries"][-1]["inning_side"] == 1 else "Top"} of the {data["entries"][-1]["inning"]}", """)
colour = nextcord.Color.from_rgb(color[0], color[1], color[2])) #await self.bot.db.commit()
embed.set_footer(text=gameid) color = tuple(int(basedata["HomeTeamColor"][i:i+2], 16) for i in (0, 2, 4))
match data["entries"][-1]["balls"]: embed = nextcord.Embed(title=f"{basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} **{data["entries"][-1]["away_score"]}** vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} **{data["entries"][-1]["home_score"]}**",
case 1: description=f"{"Bottom" if data["entries"][-1]["inning_side"] == 1 else "Top"} of the {data["entries"][-1]["inning"]}",
embed.add_field(name="Balls",value="🔴⭕⭕") colour = nextcord.Color.from_rgb(color[0], color[1], color[2]))
case 2: embed.set_footer(text=gameid)
embed.add_field(name="Balls",value="🔴🔴⭕") match data["entries"][-1]["balls"]:
case 3: case 1:
embed.add_field(name="Balls",value="🔴🔴🔴") embed.add_field(name="Balls",value="🔴⭕⭕")
case _: case 2:
embed.add_field(name="Balls",value="⭕⭕⭕") embed.add_field(name="Balls",value="🔴🔴⭕")
match data["entries"][-1]["strikes"]: case 3:
case 1: embed.add_field(name="Balls",value="🔴🔴🔴")
embed.add_field(name="Strikes",value="🔴⭕") case _:
case 2: embed.add_field(name="Balls",value="⭕⭕⭕")
embed.add_field(name="Strikes",value="🔴🔴") match data["entries"][-1]["strikes"]:
case _: case 1:
embed.add_field(name="Strikes",value="⭕⭕") embed.add_field(name="Strikes",value="🔴⭕")
match data["entries"][-1]["outs"]: case 2:
case 1: embed.add_field(name="Strikes",value="🔴🔴")
embed.add_field(name="Outs",value="🔴⭕") case _:
case 2: embed.add_field(name="Strikes",value="⭕⭕")
embed.add_field(name="Outs",value="🔴🔴") match data["entries"][-1]["outs"]:
case _: case 1:
embed.add_field(name="Outs",value="⭕⭕") embed.add_field(name="Outs",value="🔴⭕")
embed.add_field(name="Batting",value=data["entries"][-1]["batter"], inline=True) case 2:
embed.add_field(name="Pitching",value=data["entries"][-1]["pitcher"], inline=True) embed.add_field(name="Outs",value="🔴🔴")
embed.add_field(name="On Deck",value=data["entries"][-1]["on_deck"], inline=True) case _:
embed.add_field(name=f"Last 7 events",value=finalstr,inline=False) embed.add_field(name="Outs",value="⭕⭕")
embed.set_thumbnail(f"https://insertapp.net/mmolbbot/assets/diamond_{data["entries"][-1]["on_1b"]}_{data["entries"][-1]["on_2b"]}_{data["entries"][-1]["on_3b"]}.png") embed.add_field(name="Batting",value=data["entries"][-1]["batter"], inline=True)
embed.set_footer(text=f"Embed too big? Need historical data? consider classic mode.") embed.add_field(name="Pitching",value=data["entries"][-1]["pitcher"], inline=True)
await message.edit(content="",embed=embed) embed.add_field(name="On Deck",value=data["entries"][-1]["on_deck"], inline=True)
await self.bot.db.execute(f""" embed.add_field(name=f"Last 7 events",value=finalstr,inline=False)
UPDATE liveupdate set offset = {offset+offsetadd} WHERE messageid = '{messageid}' embed.set_thumbnail(f"https://insertapp.net/mmolbbot/assets/diamond_{data["entries"][-1]["on_1b"]}_{data["entries"][-1]["on_2b"]}_{data["entries"][-1]["on_3b"]}.png")
""") embed.set_footer(text=f"Embed too big? Need historical data? consider classic mode.")
await message.edit(content="",embed=embed)
await self.bot.db.execute(f"""
UPDATE liveupdate set offset = {offset+offsetadd} WHERE messageid = '{messageid}'
""")
except Exception as e: except Exception as e:
await self.bot.db.execute(f""" await self.bot.db.execute(f"""
DELETE from liveupdate WHERE messageid = {messageid} DELETE from liveupdate WHERE messageid = {messageid}
""") """)
#await self.bot.db.commit() #await self.bot.db.commit()
await message.edit(f"An error occoured in this live update\n{e}") if message:
await message.edit(f"An error occoured in this live update\n{e}")
warning = self.bot.get_channel(1365478368555827270) warning = self.bot.get_channel(1365478368555827270)
await warning.send(e) await warning.send(e)
except nextcord.Forbidden: except nextcord.Forbidden:
@ -90,34 +96,35 @@ async def classiclivegameworker(self,serverid,userid,channelid,gameid,offset):
begin = timeit.default_timer() begin = timeit.default_timer()
lastserverid = serverid lastserverid = serverid
try: try:
channel = self.bot.get_channel(channelid) async with aiohttp.ClientSession() as session:
data = requests.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset}").json() channel = self.bot.get_channel(channelid)
if len(data["entries"]) > 0: data = await session.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset}")
#data = requests.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset-((7-len(data["entries"]) if offset > 7 else offset))}").json() data = await data.json()
basedata = requests.get(f"https://mmolb.com/api/game/{gameid}").json() if len(data["entries"]) > 0:
finalstr = "" basedata = await session.get(f"https://mmolb.com/api/game/{gameid}")
offsetadd = 0 basedata = await basedata.json()
maysend = False finalstr = "\n>>> "
for i in data["entries"]: offsetadd = 0
if "scores" in i['message'] or "homers" in i['message']: maysend = False
finalstr += f"\n>>> {i['message'].replace(", ","\n").replace(". ","\n").replace("<strong>", "").replace("</strong>", "\n")}" for i in data["entries"]:
maysend = True if "scores" in i['message'] or "homers" in i['message']:
offsetadd += 1 finalstr += f"{i['message'].replace(", ","\n").replace(". ","\n").replace("<strong>", "").replace("</strong>", "\n")}\n"
if i["event"] == "Recordkeeping": maysend = True
await self.bot.db.execute(f""" offsetadd += 1
DELETE from liveupdate WHERE channelid = {channelid} AND gameid = '{gameid}' if i["event"] == "Recordkeeping":
""") await self.bot.db.execute(f"""
maysend = True DELETE from liveupdate WHERE channelid = {channelid} AND gameid = '{gameid}'
finalstr += f"\n> {i['message'].replace("<strong>", "**").replace("</strong>", "**")}" """)
#await self.bot.db.commit() maysend = True
if maysend: finalstr += f"{i['message'].replace("<strong>", "**").replace("</strong>", "**")}\n"
if data["entries"][-1]["inning_side"] == 1: if maysend:
await channel.send(f"Bottom of the {data["entries"][-1]["inning"]} | {basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} {data["entries"][-1]["away_score"]} vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} **{data["entries"][-1]["home_score"]}**{finalstr}") if data["entries"][-1]["inning_side"] == 1:
else: await channel.send(f"Bottom of the {data["entries"][-1]["inning"]} | {basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} {data["entries"][-1]["away_score"]} vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} **{data["entries"][-1]["home_score"]}**{finalstr}")
await channel.send(f"Top of the {data["entries"][-1]["inning"]} | {basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} **{data["entries"][-1]["away_score"]}** vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} {data["entries"][-1]["home_score"]}{finalstr}") else:
await self.bot.db.execute(f""" await channel.send(f"Top of the {data["entries"][-1]["inning"]} | {basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} **{data["entries"][-1]["away_score"]}** vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} {data["entries"][-1]["home_score"]}{finalstr}")
UPDATE liveupdate set offset = {offset+offsetadd} WHERE channelid = '{channelid}' AND gameid = '{gameid}' AND classic = 1 await self.bot.db.execute(f"""
""") UPDATE liveupdate set offset = {offset+offsetadd} WHERE channelid = '{channelid}' AND gameid = '{gameid}' AND classic = 1
""")
except Exception as e: except Exception as e:
warning = self.bot.get_channel(1365478368555827270) warning = self.bot.get_channel(1365478368555827270)
await warning.send(e) await warning.send(e)
@ -129,6 +136,85 @@ async def classiclivegameworker(self,serverid,userid,channelid,gameid,offset):
await self.bot.db.commit() await self.bot.db.commit()
await warning.send(f"Deleted {lastserverid} from the database due to 403 error") await warning.send(f"Deleted {lastserverid} from the database due to 403 error")
async def spotlightsubscriptionworker(self,serverid,channelid,classic,gameid,data):
try:
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
if test is None:
channel = self.bot.get_channel(channelid)
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))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
if classic == 0:
message = await channel.send(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, {message.id}, "{gameid}", {len(data["EventLog"])}, 0)
""")
else:
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, NULL, "{gameid}", {len(data["EventLog"])}, 1)
""")
except sqlite3.OperationalError:
await self.bot.db.close()
self.checkspotlightsubscriptions.cancel()
self.checkteamsubscriptions.cancel()
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"<@{self.bot.owner_id}> database is locked!")
except Exception as e:
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"Ignoring exception in spotlight check: {e}")
print(e)
return
async def teamsubscriptionworker(self,serverid,channelid,teamid,classic):
try:
async with aiohttp.ClientSession() as session:
game = await session.get(f"https://mmolb.com/api/game-by-team/{teamid}")
game = await game.json()
gameid = game["game_id"]
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
if test is None:
data = await session.get(f"https://mmolb.com/api/game/{gameid}")
data = await data.json()
channel = self.bot.get_channel(channelid)
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))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
if classic == 0:
message = await channel.send(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, {message.id}, "{gameid}", {len(data["EventLog"])}, 0)
""")
else:
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, NULL, "{gameid}", {len(data["EventLog"])}, 1)
""")
except KeyError:
return
except sqlite3.OperationalError:
await self.bot.db.close()
self.checkspotlightsubscriptions.cancel()
self.checkteamsubscriptions.cancel()
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"<@{self.bot.owner_id}> database is locked!")
except Exception as e:
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"Ignoring exception in {teamid}: {e}")
print(e)
return
class liveupdate(commands.Cog): class liveupdate(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
@ -408,14 +494,13 @@ class liveupdate(commands.Cog):
try: try:
begin = timeit.default_timer() begin = timeit.default_timer()
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
print("updating live games")
res = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE classic = 0") res = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE classic = 0")
res = await res.fetchall() res = await res.fetchall()
worklist = [livegameworker(self,serverid,userid,channelid,messageid,gameid,offset) for [serverid,userid,channelid,messageid,gameid,offset] in res] worklist = [livegameworker(self,serverid,userid,channelid,messageid,gameid,offset) for [serverid,userid,channelid,messageid,gameid,offset] in res]
res = await self.bot.db.execute("SELECT serverid,userid,channelid,gameid,offset FROM liveupdate WHERE classic = 1") res = await self.bot.db.execute("SELECT serverid,userid,channelid,gameid,offset FROM liveupdate WHERE classic = 1")
res = await res.fetchall() res = await res.fetchall()
worklist = worklist + [classiclivegameworker(self,serverid,userid,channelid,gameid,offset) for [serverid,userid,channelid,gameid,offset] in res] worklist = worklist + [classiclivegameworker(self,serverid,userid,channelid,gameid,offset) for [serverid,userid,channelid,gameid,offset] in res]
await asyncio.gather(*worklist) await asyncio.gather(*worklist,return_exceptions=True)
await self.bot.db.commit() await self.bot.db.commit()
timings.append(timeit.default_timer()-begin) timings.append(timeit.default_timer()-begin)
if len(timings) >= 12: if len(timings) >= 12:
@ -436,51 +521,17 @@ class liveupdate(commands.Cog):
async def checkspotlightsubscriptions(self): async def checkspotlightsubscriptions(self):
try: try:
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
print("refreshing spotlight subscriptions") async with aiohttp.ClientSession() as session:
game = requests.get("https://mmolb.com/api/spotlight").json() game = await session.get("https://mmolb.com/api/spotlight")
gameid = game["game_id"] game = await game.json()
gameid = game["game_id"]
data = await session.get(f"https://mmolb.com/api/game/{gameid}")
data = await data.json()
res = await self.bot.db.execute("SELECT serverid,channelid,classic FROM spotlightsubscriptions") res = await self.bot.db.execute("SELECT serverid,channelid,classic FROM spotlightsubscriptions")
res = await res.fetchall() res = await res.fetchall()
print(res) worklist = [spotlightsubscriptionworker(self,serverid,channelid,classic,gameid,data) for [serverid,channelid,classic] in res]
for [serverid,channelid,classic] in res: await asyncio.gather(*worklist,return_exceptions=True)
try: await self.bot.db.commit()
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
print(test)
if test is None:
print("True")
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
channel = self.bot.get_channel(channelid)
if data["State"] == "Complete":
continue
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
if classic == 0:
message = await channel.send(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, {message.id}, "{gameid}", {len(data["EventLog"])}, 0)
""")
else:
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, NULL, "{gameid}", {len(data["EventLog"])}, 1)
""")
await self.bot.db.commit()
else:
print("false")
except sqlite3.OperationalError:
await self.bot.db.close()
self.checkspotlightsubscriptions.cancel()
self.checkteamsubscriptions.cancel()
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"<@{self.bot.owner_id}> database is locked!")
except Exception as e:
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"Ignoring exception in spotlight check: {e}")
print(e)
continue
except KeyError: except KeyError:
pass pass
except Exception as e: except Exception as e:
@ -488,57 +539,18 @@ class liveupdate(commands.Cog):
print(e) print(e)
warning = self.bot.get_channel(1365478368555827270) warning = self.bot.get_channel(1365478368555827270)
await warning.send(e) await warning.send(e)
print(e)
return return
@tasks.loop(seconds=120.0) @tasks.loop(seconds=120.0)
async def checkteamsubscriptions(self): async def checkteamsubscriptions(self):
try: try:
print("refreshing team subscriptions")
await self.bot.wait_until_ready() await self.bot.wait_until_ready()
res = await self.bot.db.execute("SELECT serverid,channelid,teamid,classic FROM teamsubscriptions") res = await self.bot.db.execute("SELECT serverid,channelid,teamid,classic FROM teamsubscriptions")
res = await res.fetchall() res = await res.fetchall()
print(res) worklist = [teamsubscriptionworker(self,serverid,channelid,teamid,classic) for [serverid,channelid,teamid,classic] in res]
for [serverid,channelid,teamid,classic] in res: await asyncio.gather(*worklist,return_exceptions=True)
try: await self.bot.db.commit()
game = requests.get(f"https://mmolb.com/api/game-by-team/{teamid}").json()
gameid = game["game_id"]
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
if test is None:
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
channel = self.bot.get_channel(channelid)
if data["State"] == "Complete":
continue
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ? AND classic = ?", (channelid,gameid,classic))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
if classic == 0:
message = await channel.send(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, {message.id}, "{gameid}", {len(data["EventLog"])}, 0)
""")
else:
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({channel.guild.id}, {self.bot.application_id}, {channelid}, NULL, "{gameid}", {len(data["EventLog"])}, 1)
""")
await self.bot.db.commit()
except KeyError:
continue
except sqlite3.OperationalError:
await self.bot.db.close()
self.checkspotlightsubscriptions.cancel()
self.checkteamsubscriptions.cancel()
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"<@{self.bot.owner_id}> database is locked!")
except Exception as e:
warning = self.bot.get_channel(1365478368555827270)
await warning.send(f"Ignoring exception in {teamid}: {e}")
print(e)
continue
except Exception as e: except Exception as e:
#I know this is bad practice but these loops must be running #I know this is bad practice but these loops must be running
warning = self.bot.get_channel(1365478368555827270) warning = self.bot.get_channel(1365478368555827270)