diff --git a/.gitignore b/.gitignore index aeaf289..6b7b046 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.json *.txt +*.png __pycache__ \ No newline at end of file diff --git a/api/carbon.py b/api/carbon.py new file mode 100644 index 0000000..0f4053e --- /dev/null +++ b/api/carbon.py @@ -0,0 +1,80 @@ +import requests +import numpy as np +import matplotlib.pyplot as plt +import datetime +from util import logging + +def current_level() -> []: + url = "https://api.carbonintensity.org.uk/intensity" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + + data = response.json()["data"][0] + return [data["from"], data["intensity"]["forecast"], data["intensity"]["actual"], data["intensity"]["index"]] + +def level_at(date: str) -> []: + url = f"https://api.carbonintensity.org.uk/intensity/date/{date}" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + return [] + + if len(response.json()["data"]) == 0: + return [] + + data = response.json()["data"][0] + return [data["from"], data["intensity"]["forecast"], data["intensity"]["actual"], data["intensity"]["index"]] + +def level_from_to(start: str, stop: str) -> str: + url = f"https://api.carbonintensity.org.uk/intensity/{start}/{stop}" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + return "" + + if len(response.json()["data"]) == 0: + return [] + + data = response.json()["data"] + + x = [] + measured = [] + indices = [] + + for i in range(len(data)): + intensity = data[i]["intensity"] + x.append(datetime.datetime.strptime(data[i]["from"], "%Y-%m-%dT%H:%MZ")) + measured.append(intensity["actual"]) + indices.append(intensity["index"]) + + curr_color = indices[0] + curr_color_index = 0 + for i in range(len(measured)): + if indices[i] != curr_color: + color = "" + if curr_color == "very low": + color = "#5bcc32" + if curr_color == "low": + color = "#87cc32" + if curr_color == "moderate": + color = "#ccc932" + if curr_color == "high": + color = "#cc8e32" + if curr_color == "very high": + color = "#cc4f32" + + plt.plot(x[curr_color_index:i+1], measured[curr_color_index:i+1], color) + curr_color_index = i + curr_color = indices[i] + + plt.xlabel("Time & Date") + plt.ylabel("Carbon Intensity [gCO2/kWh]") + plt.xticks(rotation=45) + plt.gcf().subplots_adjust(bottom=0.2) + plt.savefig("plot.png") + plt.clf() + return "plot.png" \ No newline at end of file diff --git a/cogs/api/carbon.py b/cogs/api/carbon.py new file mode 100644 index 0000000..d4f05cb --- /dev/null +++ b/cogs/api/carbon.py @@ -0,0 +1,38 @@ +import discord +from discord.ext import commands +from api import carbon +from util import embed + +class Carbon(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + + @commands.group(name="carbon", usage="carbon [subcommand]", description="Can accumulate data about carbon levels in the UK") + async def carbon(self, ctx): + pass + + @carbon.command(name="now", usage="carbon now", description="Gets the latest available carbon intensity data") + async def now(self, ctx): + data = carbon.current_level() + await ctx.send(embed=embed.make_embed_fields_footer("Carbon intensity in the UK", f"The current carbon intensity is considered **{data[3]}**.", f"{data[0]} | All values in gCO2/kWh", ("Measured", data[2]), ("Predicted", data[1]))) + + @carbon.command(name="at", usage="carbon at ", description="Gets the carbon levels at the given date (YYYY-MM-DD)") + async def at(self, ctx: discord.Client, date: str): + data = carbon.level_at(date) + if len(data) == 0: + await ctx.send(embed=embed.make_error_embed(f"There is no data available for {date}.")) + else: + await ctx.send(embed=embed.make_embed_fields_footer("Carbon intensity in the UK", f"The carbon intensity for that date is considered **{data[3]}**.", f"{data[0]} | All values in gCO2/kWh", ("Measured", data[2]), ("Predicted", data[1]))) + + @carbon.command(name="during", usage="carbon during ", description="Creates a diagram about carbon levels in the given time period (YYYY-MM-DD)") + async def during(self, ctx: discord.Client, start: str, stop: str): + path = carbon.level_from_to(start, stop) + if path == "": + await ctx.send(embed=embed.make_error_embed(f"There is no data available for the given time period.")) + else: + data = embed.make_embed_image("Carbon intensity in the UK", path) + await ctx.send(embed=data[0], file=data[1]) + +def setup(client: discord.Client): + client.add_cog(Carbon(client)) \ No newline at end of file diff --git a/cogs/help.py b/cogs/help.py index 02ef761..d2a28e2 100644 --- a/cogs/help.py +++ b/cogs/help.py @@ -20,7 +20,29 @@ class Help(commands.MinimalHelpCommand): await self.context.send(embed=embed.make_error_embed("Command not found")) async def send_group_help(self, group): - await self.context.send(embed=embed.make_error_embed("Command not found")) + try: + subcmds = "" + if group.commands != []: + for command in group.commands: + subcmds += "`" + command.name + "`, " + subcmds = subcmds[:-2] + else: + subcmds = "`None`" + + alias = "" + if group.aliases != []: + for i in range(len(group.aliases)): + if i == len(group.aliases) - 1: + alias = alias + '`' + group.aliases[i] + '`' + else: + alias = alias + '`' + group.aliases[i] + '`' + ', ' + else: + alias = "`None`" + + await self.context.send(embed=embed.make_embed_fields_ninl(group.name, group.description, ("Usage", f"`{config.settings['prefix']}{group.usage}`"), ("Aliases", alias), ("Subcommands", subcmds))) + except Exception as e: + logging.error(str(e)) + await self.context.send(embed=embed.make_error_embed("Command not found")) async def send_command_help(self, command): try: @@ -34,7 +56,10 @@ class Help(commands.MinimalHelpCommand): else: alias = "`None`" - await self.context.send(embed=embed.make_embed_fields_ninl(command.name, command.description, ("Usage", f"`{config.settings['prefix']}{command.usage}`"), ("Aliases", alias))) + command_name = command.name + if command.parent != None: + command_name += f" ({command.parent})" + await self.context.send(embed=embed.make_embed_fields_ninl(command_name, command.description, ("Usage", f"`{config.settings['prefix']}{command.usage}`"), ("Aliases", alias))) except Exception as e: logging.error(str(e)) await self.context.send(embed=embed.make_error_embed("Command not found")) diff --git a/util/embed.py b/util/embed.py index 7fef2f5..cfdd8d9 100644 --- a/util/embed.py +++ b/util/embed.py @@ -20,9 +20,22 @@ def make_embed_fields(title: str, desc: str, *fields: tuple) -> discord.Embed: embed.add_field(name=name, value=value) return embed - +def make_embed_fields_footer(title: str, desc: str, footer: str, *fields: tuple) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16)) + embed.set_footer(text=footer) + for name, value in fields: + embed.add_field(name=name, value=value) + return embed + def make_embed_fields_ninl(title: str, desc: str, *fields: tuple) -> discord.Embed: embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16), inline=False) for name, value in fields: - embed.add_field(name=name, value=value) - return embed \ No newline at end of file + embed.add_field(name=name, value=value, inline=False) + return embed + +def make_embed_image(title: str, path: str) -> (discord.Embed, discord.File): + embed = discord.Embed(title=title, colour=int(config.settings["color"], 16)) + attachment = discord.File(path, filename="image.png") + embed.set_image(url='attachment://image.png') + + return (embed, attachment) \ No newline at end of file