aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/seasons/evergreen/space.py140
1 files changed, 76 insertions, 64 deletions
diff --git a/bot/seasons/evergreen/space.py b/bot/seasons/evergreen/space.py
index d94dcbf4..e841102f 100644
--- a/bot/seasons/evergreen/space.py
+++ b/bot/seasons/evergreen/space.py
@@ -5,6 +5,7 @@ from typing import Any, Dict, Optional, Union
from urllib.parse import urlencode
from discord import Embed
+from discord.ext import tasks
from discord.ext.commands import BadArgument, Cog, Context, Converter, group
from bot.bot import SeasonalBot
@@ -22,10 +23,10 @@ APOD_DEFAULT_PARAMS = {
class DateConverter(Converter):
- """Parse `str` into `datetime` or `int` object."""
+ """Parse SOL or earth date (in format YYYY-MM-DD) into `int` or `datetime`. When invalid input, raise error."""
async def convert(self, ctx: Context, argument: str) -> Union[int, datetime]:
- """Parse `str` into `datetime` or `int`. When invalid value, raise error."""
+ """Parse date (SOL or earth) into `datetime` or `int`. When invalid value, raise error."""
if argument.isdigit():
return int(argument)
try:
@@ -42,6 +43,20 @@ class Space(Cog):
self.bot = bot
self.http_session = bot.http_session
+ self.rovers = {}
+ self.get_rovers.start()
+
+ @tasks.loop(hours=24)
+ async def get_rovers(self) -> None:
+ """Get listing of rovers from NASA API and info about their start and end dates."""
+ data = await self.fetch_from_nasa("mars-photos/api/v1/rovers", params={"api_key": Tokens.nasa})
+
+ for rover in data["rovers"]:
+ self.rovers[rover["name"].lower()] = {
+ "min_date": rover["landing_date"],
+ "max_date": rover["max_date"]
+ }
+
@group(name="space", invoke_without_command=True)
async def space(self, ctx: Context) -> None:
"""Head command that contains commands about space."""
@@ -49,8 +64,11 @@ class Space(Cog):
@space.command(name="apod")
async def apod(self, ctx: Context, date: Optional[str] = None) -> None:
- """Get Astronomy Picture of Day from NASA API. Date is optional parameter, what formatting is YYYY-MM-DD."""
- # Make copy of parameters
+ """
+ Get Astronomy Picture of Day from NASA API. Date is optional parameter, what formatting is YYYY-MM-DD.
+
+ If date is not specified, this will get today APOD.
+ """
params = APOD_DEFAULT_PARAMS.copy()
# Parse date to params, when provided. Show error message when invalid formatting
if date:
@@ -60,48 +78,39 @@ class Space(Cog):
await ctx.send(f"Invalid date {date}. Please make sure your date is in format YYYY-MM-DD.")
return
- # Do request to NASA API
result = await self.fetch_from_nasa("planetary/apod", params)
- # Create embed from result
- embed = Embed(title=f"Astronomy Picture of the Day - {result['date']}", description=result["explanation"])
- embed.set_image(url=result["url"])
- embed.set_footer(text="Powered by NASA API")
-
- await ctx.send(embed=embed)
+ await ctx.send(embed=await self.create_nasa_embed(
+ f"Astronomy Picture of the Day - {result['date']}",
+ result["explanation"],
+ result["url"]
+ ))
@space.command(name="nasa")
async def nasa(self, ctx: Context, *, search_term: Optional[str] = None) -> None:
"""Get random NASA information/facts + image. Support `search_term` parameter for more specific search."""
- # Create params for request, create URL and do request
params = {
"media_type": "image"
}
if search_term:
params["q"] = search_term
- async with self.http_session.get(url=f"{NASA_IMAGES_BASE_URL}/search?{urlencode(params)}") as resp:
- data = await resp.json()
-
- # Check is there any items returned
+ data = await self.fetch_from_nasa("search", params, NASA_IMAGES_BASE_URL)
if len(data["collection"]["items"]) == 0:
await ctx.send(f"Can't find any items with search term `{search_term}`.")
return
- # Get (random) item from result, that will be shown
item = random.choice(data["collection"]["items"])
- # Create embed and send it
- embed = Embed(title=item["data"][0]["title"], description=item["data"][0]["description"])
- embed.set_image(url=item["links"][0]["href"])
- embed.set_footer(text="Powered by NASA API")
-
- await ctx.send(embed=embed)
+ await ctx.send(embed=await self.create_nasa_embed(
+ item["data"][0]["title"],
+ item["data"][0]["description"],
+ item["links"][0]["href"]
+ ))
@space.command(name="epic")
async def epic(self, ctx: Context, date: Optional[str] = None) -> None:
"""Get one of latest random image of earth from NASA EPIC API. Support date parameter, format is YYYY-MM-DD."""
- # Parse date if provided
if date:
try:
show_date = datetime.strptime(date, "%Y-%m-%d").date().isoformat()
@@ -111,32 +120,24 @@ class Space(Cog):
else:
show_date = None
- # Generate URL and make request to API
- async with self.http_session.get(
- url=f"{NASA_EPIC_BASE_URL}/api/natural{f'/date/{show_date}' if show_date else ''}"
- ) as resp:
- data = await resp.json()
-
+ data = await self.fetch_from_nasa(
+ f"api/natural{f'/date/{show_date}' if show_date else ''}",
+ base=NASA_EPIC_BASE_URL
+ )
if len(data) < 1:
await ctx.send("Can't find any images in this date.")
return
- # Get random item from result that will be shown
item = random.choice(data)
- # Split date for image URL
year, month, day = item["date"].split(" ")[0].split("-")
-
image_url = f"{NASA_EPIC_BASE_URL}/archive/natural/{year}/{month}/{day}/jpg/{item['image']}.jpg"
- # Create embed, fill and send it
- embed = Embed(title="Earth Image", description=item["caption"])
- embed.set_image(url=image_url)
- embed.set_footer(text=f"Identifier: {item['identifier']} \u2022 Powered by NASA API")
-
- await ctx.send(embed=embed)
+ await ctx.send(embed=await self.create_nasa_embed(
+ "Earth Image", item["caption"], image_url, f" \u2022 Identifier: {item['identifier']}"
+ ))
- @space.command(name="mars")
+ @space.group(name="mars", invoke_without_command=True)
async def mars(self,
ctx: Context,
date: DateConverter,
@@ -145,19 +146,18 @@ class Space(Cog):
"""
Get random Mars image by date. Support both SOL (martian solar day) and earth date and rovers.
- Earth date formatting is YYYY-MM-DD. Current max date is 2019-09-28 and min 2012-08-06.
- Rovers images dates:
- - Curiosity -> 2012-08-06 until 2019-09-28
- - Opportunity -> 2004-01-25 until 2018-06-11
- - Spirit -> 2004-01-04 until 2010-03-21
+ Earth date formatting is YYYY-MM-DD. Use `.space mars dates` to get all currently available rovers.
"""
- # Check does user provided correct rover
rover = rover.lower()
- if rover not in ["curiosity", "opportunity", "spirit"]:
- await ctx.send(f"Invalid rover `{rover}`. Rovers: `Curiosity`, `Opportunity`, `Spirit`")
+ if rover not in self.rovers:
+ await ctx.send(
+ (
+ f"Invalid rover `{rover}`.\n"
+ f"**Rovers:** `{'`, `'.join(f'{r.capitalize()}' for r in self.rovers)}`"
+ )
+ )
return
- # Create API request parameters, try to parse date
params = {
"api_key": Tokens.nasa
}
@@ -167,37 +167,49 @@ class Space(Cog):
params["earth_date"] = date.date().isoformat()
result = await self.fetch_from_nasa(f"mars-photos/api/v1/rovers/{rover}/photos", params)
-
- # Check for empty result
if len(result["photos"]) < 1:
err_msg = (
f"We can't find result in date "
f"{date.date().isoformat() if isinstance(date, datetime) else f'{date} SOL'}.\n"
- f"**Note:** Dates must match with rover's working dates. Please use `{ctx.prefix}help space mars` to "
+ f"**Note:** Dates must match with rover's working dates. Please use `{ctx.prefix}space mars dates` to "
"see working dates for each rover."
)
await ctx.send(err_msg)
return
- # Get random item from result, generate embed with it and send
item = random.choice(result["photos"])
-
- embed = Embed(title=f"{item['rover']['name']}'s {item['camera']['full_name']} Mars Image")
- embed.set_image(url=item["img_src"])
- embed.set_footer(text="Powered by NASA API")
-
- await ctx.send(embed=embed)
-
- async def fetch_from_nasa(self, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:
+ await ctx.send(embed=await self.create_nasa_embed(
+ f"{item['rover']['name']}'s {item['camera']['full_name']} Mars Image", "", item["img_src"],
+ ))
+
+ @mars.command(name="dates", aliases=["d", "date", "rover", "rovers", "r"])
+ async def dates(self, ctx: Context) -> None:
+ """Get current available rovers photo date ranges."""
+ await ctx.send("\n".join(
+ f"**{r.capitalize()}:** {i['min_date']} **-** {i['max_date']}" for r, i in self.rovers.items()
+ ))
+
+ async def fetch_from_nasa(self,
+ endpoint: str,
+ params: Optional[Dict[str, Any]] = None,
+ base: Optional[str] = NASA_BASE_URL
+ ) -> Dict[str, Any]:
"""Fetch information from NASA API, return result."""
- # Generate request URL from base URL, endpoint and parsed params
- async with self.http_session.get(url=f"{NASA_BASE_URL}/{endpoint}?{urlencode(params)}") as resp:
+ if params is None:
+ params = {}
+ async with self.http_session.get(url=f"{base}/{endpoint}?{urlencode(params)}") as resp:
return await resp.json()
+ async def create_nasa_embed(self, title: str, description: str, image: str, footer: Optional[str] = "") -> Embed:
+ """Generate NASA commands embeds. Required: title, description and image URL, footer (addition) is optional."""
+ return Embed(
+ title=title,
+ description=description
+ ).set_image(url=image).set_footer(text="Powered by NASA API" + footer)
+
def setup(bot: SeasonalBot) -> None:
"""Load Space Cog."""
- # Check does bot have NASA API key in .env, when not, don't load Cog and print warning
if not Tokens.nasa:
logger.warning("Can't find NASA API key. Not loading Space Cog.")
return