migrate to yr's api

good code is overrated
This commit is contained in:
LBlend committed 2024-01-16 03:24:56 +01:00
1 parent 93005cd098
commit 516d0eabc1
1 file changed
+50 -116
+50 -116
View File
@@ -2,13 +2,12 @@ from datetime import datetime
from datetime import timedelta
import discord
import pytz
import requests
from discord import app_commands
from discord.ext import commands
from discord.ext import tasks
from cogs.utils import embed_templates
class Aurora(commands.Cog):
"""Aurora Borealis forecasts and alerts"""
@@ -22,11 +21,8 @@ class Aurora(commands.Cog):
self.bot = bot
self.NEUF_LAT = 59.9323960544179
self.NEUF_LON = 10.712627459170472
self.notified = datetime(2000, 9, 11) # Used to prevent spamming aurora alerts
self.aurora_alarm.start()
# self.aurora_alarm.start()
async def get_forecast(self) -> dict | None:
"""
@@ -35,88 +31,33 @@ class Aurora(commands.Cog):
Returns
-------
(dict) The forecast data | None
{
"aurora_forecast_time": (str) Time the aurora forecast was made formatted as a discord timestamp
"aurora_percentage": (int) Percentage chance of aurora
"cloud_forecast_time": (str) Time the cloud cover forecast was made formatted as a discord timestamp
"cloud_cover_percentage": (int) Percentage cloud cover
}
"""
# Get the aurora forecast data from NOAA
forecast_data = requests.get("https://services.swpc.noaa.gov/json/ovation_aurora_latest.json").json()
if not forecast_data:
self.bot.logger.warning("Failed to get aurora forecast data from NOAA")
return None
aurora_forecast_time = forecast_data.get("Observation Time")
# Make sure longitude is in range 0-360. For some reason we need to do this.
# I don't know why. Maybe because I ~~stole~~ borrowed the code :P
longitude = self.NEUF_LON % 360
forecast_dict = {}
for forecast_item in forecast_data["coordinates"]:
forecast_dict[forecast_item[0], forecast_item[1]] = forecast_item[2]
aurora_percentage = forecast_dict.get((int(longitude), int(self.NEUF_LAT)), 0)
# Get the cloud cover data from MET Norway
cloud_data = requests.get(
"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=59.9323960544179&lon=10.712627459170472",
headers={"User-Agent": "UiO Gaming Bot"},
).json()
if not cloud_data:
self.bot.logger.warning("Failed to get cloud cover data from MET Norway")
data = requests.get("https://www.yr.no/api/v0/locations/1-72837/auroraforecast?language=nb").json()
if not data or data["status"]["code"] != "Ok":
self.bot.logger.warning("Failed to get forecast data")
return None
cloud_cover = cloud_data["properties"]["timeseries"][0]["data"]["instant"]["details"]["cloud_area_fraction"]
cloud_forecast_time = cloud_data["properties"]["timeseries"][0]["time"]
valid_sightings = []
for interval in data["shortIntervals"]:
# Convert to datetime objects, convert timezone to utc and strip timezone info
start = datetime.fromisoformat(interval["start"]).astimezone(pytz.utc).replace(tzinfo=None)
end = datetime.fromisoformat(interval["end"]).astimezone(pytz.utc).replace(tzinfo=None)
# Convert time to discord timestamps
try:
aurora_forecast_time = discord.utils.format_dt(datetime.fromisoformat(aurora_forecast_time), style="R")
except ValueError as e:
self.bot.logger.warning(e)
aurora_forecast_time = f"`{aurora_forecast_time}`"
# If sighting is not in within the next 12 hours ignore
if (start - datetime.now()) > timedelta(hours=12):
continue
try:
cloud_forecast_time = discord.utils.format_dt(datetime.fromisoformat(cloud_forecast_time), style="R")
except ValueError as e:
self.bot.logger.warning(e)
cloud_forecast_time = f"`{cloud_forecast_time}`"
# We're gonna trust this value. Hopefully it takes sunlight, cloud cover as well as solar activity into account
# We're using an undocumented API so who knows. They probably know what they're doing over there.
if interval["auroraValue"] >= 0.5:
valid_sightings.append(interval)
return {
"aurora_forecast_time": aurora_forecast_time,
"aurora_percentage": aurora_percentage,
"cloud_forecast_time": cloud_forecast_time,
"cloud_cover_percentage": cloud_cover,
}
if not valid_sightings:
return None
def get_forecast_embed(self, forecast: dict) -> discord.Embed:
"""
Format the forecast data into a standaridized embed. DRY
Parameters
----------
forecast (dict): The forecast data
{
"aurora_forecast_time": (str) Time the aurora forecast was made formatted as a discord timestamp
"aurora_percentage": (int) Percentage chance of aurora
"cloud_forecast_time": (str) Time the cloud cover forecast was made formatted as a discord timestamp
"cloud_cover_percentage": (int) Percentage cloud cover
}
"""
aurora_time = forecast.get("aurora_forecast_time")
cloud_time = forecast.get("cloud_forecast_time")
aurora_percentage = forecast.get("aurora_percentage")
cloud_percentage = forecast.get("cloud_cover_percentage")
embed = discord.Embed(title="Nordlysvarsel for Chateau Neuf", color=0x00EA8D)
embed.description = f"Nordlysvarsel hentet: {aurora_time}\nSkydekkevarsel hentet: {cloud_time}"
embed.add_field(name="Sjanse for å se nordlys", value=f"{aurora_percentage}%")
embed.add_field(name="Skydekke", value=f"{cloud_percentage}%")
embed.set_footer(text="Nordlysdata fra NOAA | Skydekkedata fra MET Norge")
return embed
most_likely_sighting = max(valid_sightings, key=lambda x: x["auroraValue"])
return most_likely_sighting
@tasks.loop(minutes=10)
async def aurora_alarm(self):
@@ -132,47 +73,40 @@ class Aurora(commands.Cog):
)
return
forecast = await self.get_forecast()
if not forecast:
self.bot.logger.warning("aurora_alarm: Failed to get forecast data")
if not (forecast := await self.get_forecast()):
self.bot.logger.info("aurora_alarm: Fetch forecast")
return
if forecast.get("aurora_percentage") >= 50 and forecast.get("cloud_cover_percentage") <= 50:
channel = self.bot.get_channel(747542544291987597)
if not channel:
self.bot.logger.warning("aurora_alarm: Failed to get aurora channel")
return
channel = self.bot.get_channel(747542544291987599)
if not channel:
self.bot.logger.warning("aurora_alarm: Failed to get aurora channel")
return
embed = self.get_forecast_embed(forecast)
await channel.send(
content="SJANSE FOR NORDLYS AKKURAT NÅ PÅ NEUF! Se ut vinduet istedenfor på den dumme skjermen din",
embed=embed,
)
self.notified = datetime.now() # Update the last notification time
try:
start = discord.utils.format_dt(datetime.fromisoformat(forecast["start"]), style="t")
except ValueError as e:
self.bot.logger.warning(e)
start = f"`{forecast['start']}`"
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name="nordlys", description="Se nordlysvarselet for Neuf akkurat nå")
async def aurora_forecast(self, interaction: discord.Interaction):
"""
See the aurora forecast at Chateau Neuf right now
try:
end = discord.utils.format_dt(datetime.fromisoformat(forecast["end"]), style="t")
except ValueError as e:
self.bot.logger.warning(e)
end = f"`{forecast['end']}`"
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
forecast = await self.get_forecast()
if not forecast:
return await interaction.response.send_message(
embed=embed_templates.error_fatal(
interaction,
text="Kunne ikke hente nordlysvarsel akkurat nå. Et av APIene feilet (ikke min feil :P)",
)
)
embed = self.get_forecast_embed(forecast)
await interaction.response.send_message(embed=embed)
embed = discord.Embed(
title="Nordlysvarsel",
description=f"### Størst sjanse mellom {start} og {end}",
color=discord.Color.purple(),
)
embed.add_field(name="Sjanse for Nordlys", value=f"{round(forecast['auroraValue'] * 100)}%", inline=False)
embed.add_field(name="KP-Indeks", value=forecast["kpIndex"])
embed.add_field(name="Skydekke", value=f"{forecast['cloudCover']['value']}%")
await channel.send(
content="SJANSE FOR NORDLYS AKKURAT NÅ PÅ NEUF! Se ut vinduet istedenfor på den dumme skjermen din",
embed=embed,
)
self.notified = datetime.now() # Update the last notification time
async def setup(bot: commands.Bot):