mmolbbot/cogs/liveupdate.py

400 lines
20 KiB
Python

import hashlib
import json
from pathlib import Path
from datetime import datetime, timedelta, timezone
import requests
import asyncio
import nextcord
import aiosqlite as sqlite3
from nextcord.ext import commands, application_checks, tasks
from nextcord import TextInputStyle, IntegrationType
class liveupdate(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.updatelivegames.start()
self.checkspotlightsubscriptions.start()
self.checkteamsubscriptions.start()
def cog_unload(self):
self.updatelivegames.cancel()
self.checkspotlightsubscriptions.cancel()
self.checkteamsubscriptions.cancel()
@nextcord.slash_command(
name="spotlightgame",
description="Watch the spotlight game",
integration_types=[
IntegrationType.guild_install,
],
contexts=[
nextcord.InteractionContextType.guild,
nextcord.InteractionContextType.private_channel,
],
force_global=True,
)
async def spotlightwatch(self, interaction: nextcord.Interaction):
await interaction.response.defer()
game = requests.get("https://mmolb.com/api/spotlight").json()
gameid = game["game_id"]
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
await interaction.edit_original_message(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
message = await interaction.original_message()
if data["State"] == "Complete":
return
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({interaction.guild_id}, {interaction.user.id}, {interaction.channel_id}, {message.id}, "{gameid}", {len(data["EventLog"])})
""")
await self.bot.db.commit()
@nextcord.slash_command(name="subscribe", description="Subscribe to...",
integration_types=[
IntegrationType.guild_install,
],
contexts=[
nextcord.InteractionContextType.guild,
nextcord.InteractionContextType.private_channel,
],
force_global=True,
default_member_permissions=nextcord.Permissions(manage_channels=True),)
async def subscribe(self, interaction: nextcord.Interaction):
#This is never called
pass
@subscribe.subcommand(
name="spotlight",
description="Subscribe to every spotlight game, sending them in this channel",
)
async def spotlightsubscribe(self, interaction: nextcord.Interaction):
await interaction.response.defer()
game = requests.get("https://mmolb.com/api/spotlight").json()
gameid = game["game_id"]
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
await interaction.edit_original_message(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
message = await interaction.original_message()
if data["State"] != "Complete":
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({interaction.guild_id}, {interaction.user.id}, {interaction.channel_id}, {message.id}, "{gameid}", {len(data["EventLog"])})
""")
await self.bot.db.execute(f"""
INSERT INTO spotlightsubscriptions VALUES
({interaction.guild_id}, {interaction.channel_id})
""")
await self.bot.db.commit()
await interaction.followup.send(content="From now on every spotlight game will be sent in this channel", ephemeral=True)
@subscribe.subcommand(
name="team",
description="Subscribe to every game for the specified team, sending them in this channel",
)
async def teamsubscribe(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]
try:
game = requests.get(f"https://mmolb.com/api/game-by-team/{teamid}").json()
gameid = game["game_id"]
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
await interaction.edit_original_message(content=f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
message = await interaction.original_message()
if data["State"] != "Complete":
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({interaction.guild_id}, {interaction.user.id}, {interaction.channel_id}, {message.id}, "{gameid}", {len(data["EventLog"])})
""")
except KeyError:
await interaction.edit_original_message(content="This channel is now subscribed to updates")
await self.bot.db.execute(f"""
INSERT INTO teamsubscriptions VALUES
({interaction.guild_id}, {interaction.channel_id}, "{teamid}")
""")
await self.bot.db.commit()
await interaction.followup.send(content="From now on every game for the specified team will be sent in this channel", ephemeral=True)
@teamsubscribe.on_autocomplete("team")
async def teamsubscribeac(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="liveupdates",
description="Get live updates on a game",
integration_types=[
IntegrationType.guild_install,
],
contexts=[
nextcord.InteractionContextType.guild,
nextcord.InteractionContextType.private_channel,
],
force_global=True,
)
async def liveupdatecreate(self, interaction: nextcord.Interaction, gameid: str):
data = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
await interaction.response.send_message(f"{data["AwayTeamName"]} {data["AwayTeamEmoji"]} **{data["EventLog"][-1]["away_score"]}** vs {data["HomeTeamName"]} {data["HomeTeamEmoji"]} **{data["EventLog"][-1]["home_score"]}**")
message = await interaction.original_message()
if data["State"] == "Complete":
return
await self.bot.db.execute(f"""
INSERT INTO liveupdate VALUES
({interaction.guild_id}, {interaction.user.id}, {interaction.channel_id}, {message.id}, "{gameid}", {len(data["EventLog"])})
""")
await self.bot.db.commit()
@nextcord.slash_command(name="unsubscribe", description="Unsubscribe to...",
integration_types=[
IntegrationType.guild_install,
],
contexts=[
nextcord.InteractionContextType.guild,
nextcord.InteractionContextType.private_channel,
],
force_global=True,
default_member_permissions=nextcord.Permissions(manage_channels=True),
)
async def unsubscribe(self, interaction: nextcord.Interaction):
#This is never called
pass
@unsubscribe.subcommand(
name="spotlight",
description="Unsubscribe to spotlight game updates in this channel",
)
async def unsubscribespotlightgame(self, interaction: nextcord.Interaction):
await self.bot.db.execute(f"""
DELETE from spotlightsubscriptions WHERE channelid = {interaction.channel_id}
""")
await self.bot.db.commit()
await interaction.response.send_message("If existent, it has been removed")
@unsubscribe.subcommand(
name="team",
description="Unsubscribe to team game updates in this channel",
)
async def unsubscribeteamgame(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 teamsubscriptions 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")
@unsubscribeteamgame.on_autocomplete("team")
async def unsubscribeteamgameac(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="Delete a subscribed update",
integration_types=[
IntegrationType.guild_install,
],
contexts=[
nextcord.InteractionContextType.guild,
nextcord.InteractionContextType.private_channel,
],
force_global=True,
)
async def liveupdatedelete(self, interaction: nextcord.Interaction, messageid: str):
await interaction.response.defer(ephemeral=True)
await self.bot.db.execute(f"""
DELETE from liveupdate WHERE messageid = ?
""", (int(messageid),))
await self.bot.db.commit()
await interaction.edit_original_message(content="stopped updates for message") #TODO This will be a button
@tasks.loop(seconds=20.0)
async def updatelivegames(self):
try:
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")
res = await res.fetchall()
print(res)
lastserverid = 0
for [serverid,userid,channelid,messageid,gameid,offset] in res:
lastserverid = serverid
try:
channel = self.bot.get_channel(channelid)
message = await channel.fetch_message(messageid)
data = requests.get(f"https://mmolb.com/api/game/{gameid}/live?after={offset}").json()
if len(data["entries"]) > 0:
splitstr = message.content.split("\n")
if len(splitstr) > 0:
splitstr.pop(0)
try:
while len(splitstr)>(5-len(data["entries"])):
splitstr.pop(0)
except IndexError:
print("Warning: index error, ignoring") #Not sure why this happens so far but ignorning the error doesn't break anything
print(splitstr)
basedata = requests.get(f"https://mmolb.com/api/game/{gameid}").json()
finalstr = f"{basedata["AwayTeamName"]} {basedata["AwayTeamEmoji"]} **{data["entries"][-1]["away_score"]}** vs {basedata["HomeTeamName"]} {basedata["HomeTeamEmoji"]} **{data["entries"][-1]["home_score"]}**"
offsetadd = 0
for i in splitstr:
finalstr += f"\n{i}"
for i in data["entries"]:
finalstr += f"\n{i['message'].replace("<strong>", "**").replace("</strong>", "**")}"
offsetadd += 1
await self.bot.db.execute(f"""
UPDATE liveupdate set offset = {offset+offsetadd} WHERE messageid = '{messageid}'
""") #Could do this for every meessage subscribed to the game but since the messages go one by one... maybe I should change that
await self.bot.db.commit()
if i["event"] == "Recordkeeping":
await self.bot.db.execute(f"""
DELETE from liveupdate WHERE messageid = {messageid}
""")
await self.bot.db.commit()
await message.edit(finalstr)
except Exception as e:
await self.bot.db.execute(f"""
DELETE from liveupdate WHERE messageid = {messageid}
""")
await self.bot.db.commit()
await message.edit(f"An error occoured in this live update\n{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.commit()
await warning.send(f"Deleted {lastserverid} from the database due to 403 error")
except Exception as e:
warning = self.bot.get_channel(1365478368555827270)
await warning.send(e)
print(e)
@tasks.loop(seconds=120.0)
async def checkspotlightsubscriptions(self):
try:
await self.bot.wait_until_ready()
print("refreshing spotlight subscriptions")
game = requests.get("https://mmolb.com/api/spotlight").json()
gameid = game["game_id"]
res = await self.bot.db.execute("SELECT serverid,channelid FROM spotlightsubscriptions")
res = await res.fetchall()
print(res)
for [serverid,channelid] in res:
try:
check = await self.bot.db.execute("SELECT serverid,userid,channelid,messageid,gameid,offset FROM liveupdate WHERE channelid = ? AND gameid = ?", (channelid,gameid))
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 = ?", (channelid,gameid))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
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"])})
""")
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:
pass
except Exception as e:
#I know this is bad practice but these loops must be running
print(e)
warning = self.bot.get_channel(1365478368555827270)
await warning.send(e)
print(e)
return
@tasks.loop(seconds=120.0)
async def checkteamsubscriptions(self):
try:
print("refreshing team subscriptions")
await self.bot.wait_until_ready()
res = await self.bot.db.execute("SELECT serverid,channelid,teamid FROM teamsubscriptions")
res = await res.fetchall()
print(res)
for [serverid,channelid,teamid] in res:
try:
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 = ?", (channelid,gameid))
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 = ?", (channelid,gameid))
test = await check.fetchone()
if test is None: #no idea why it has to have two checks
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"])})
""")
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:
#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))