From 7ece67b4f1c25970aaf87e315eb62a2509842af0 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 8 May 2021 20:14:23 +0100 Subject: Add constants for Metabase cog --- bot/constants.py | 8 ++++++++ config-default.yml | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 7b2a38079..e2a35e892 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -558,6 +558,14 @@ class Reddit(metaclass=YAMLGetter): secret: Optional[str] subreddits: list +class Metabase(metaclass=YAMLGetter): + section = "metabase" + + username: Optional[str] + password: Optional[str] + url: str + max_session_age: int + class AntiSpam(metaclass=YAMLGetter): section = 'anti_spam' diff --git a/config-default.yml b/config-default.yml index 46475f845..a2ca8a447 100644 --- a/config-default.yml +++ b/config-default.yml @@ -429,6 +429,13 @@ reddit: subreddits: - 'r/Python' +metabase: + username: !ENV "METABASE_USERNAME" + password: !ENV "METABASE_PASSWORD" + url: "http://metabase.default.svc.cluster.local/api" + # 14 days, see https://www.metabase.com/docs/latest/operations-guide/environment-variables.html#max_session_age + max_session_age: 20160 + big_brother: header_message_limit: 15 -- cgit v1.2.3 From bb8c3b7a3342bed5d66eac62d34dc863c6e039ee Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 8 May 2021 20:18:24 +0100 Subject: Add new cog for extracting data from metabase Metabase generates report from site and metricity data. Quite often we export these reports to csv, transform them and the pipe into an int e. This cog aims to reduce the time taken for that, by giving admins the ability to export data from a report directly into a hastebin. The auth flow is cached, as the login endpoint is ratelimitted. We want to ensure that we use a valid session token until it expires to reduce the number of calls to this endpoint. --- bot/exts/moderation/metabase.py | 160 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 bot/exts/moderation/metabase.py diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py new file mode 100644 index 000000000..3d5e6e0ff --- /dev/null +++ b/bot/exts/moderation/metabase.py @@ -0,0 +1,160 @@ +import json +import logging +from datetime import timedelta +from typing import Optional + +import arrow +from aiohttp.client_exceptions import ClientResponseError +from arrow import Arrow +from async_rediscache import RedisCache +from discord.ext.commands import BadArgument, Cog, Context, group, has_any_role + +from bot.bot import Bot +from bot.constants import Metabase as MetabaseConfig, Roles +from bot.utils import send_to_paste_service +from bot.utils.channel import is_mod_channel +from bot.utils.scheduling import Scheduler + +log = logging.getLogger(__name__) + +BASE_HEADERS = { + "Content-Type": "application/json" +} + + +class Metabase(Cog): + """Commands for admins to interact with metabase.""" + + session_info = RedisCache() + + def __init__(self, bot: Bot) -> None: + self.bot = bot + self._session_scheduler = Scheduler(self.__class__.__name__) + + self.session_token = None # session_info["session_token"]: str + self.session_expiry = None # session_info["session_expiry"]: UtcPosixTimestamp + self.headers = BASE_HEADERS + + self.init_task = self.bot.loop.create_task(self.init_cog()) + + async def init_cog(self) -> None: + """Initialise the metabase session.""" + expiry_time = await self.session_info.get("session_expiry") + if expiry_time: + expiry_time = Arrow.utcfromtimestamp(expiry_time) + + if expiry_time is None or expiry_time < arrow.utcnow(): + # Force a refresh and end the task + await self.refresh_session() + return + + # Cached token is in date, so get it and schedule a refresh for later + self.session_token = await self.session_info.get("session_token") + self.headers["X-Metabase-Session"] = self.session_token + + self._session_scheduler.schedule_at(expiry_time, 0, self.refresh_session()) + + async def refresh_session(self) -> None: + """Refresh metabase session token.""" + data = { + "username": MetabaseConfig.username, + "password": MetabaseConfig.password + } + async with self.bot.http_session.post(f"{MetabaseConfig.url}/session", json=data) as resp: + json_data = await resp.json() + self.session_token = json_data.get("id") + + self.headers["X-Metabase-Session"] = self.session_token + log.info("Successfully updated metabase session.") + + # When the creds are going to expire + refresh_time = arrow.utcnow() + timedelta(minutes=MetabaseConfig.max_session_age) + + # Cache the session info, since login in heavily ratelimitted + await self.session_info.set("session_token", self.session_token) + await self.session_info.set("session_expiry", refresh_time.timestamp()) + + self._session_scheduler.schedule_at(refresh_time, 0, self.refresh_session()) + + @group(name="metabase", invoke_without_command=True) + async def metabase_group(self, ctx: Context) -> None: + """A group of commands for interacting with metabase.""" + await ctx.send_help(ctx.command) + + @metabase_group.command(name="extract") + async def metabase_extract(self, ctx: Context, question_id: int, extension: Optional[str] = "csv") -> None: + """ + Extract data from a metabase question. + + You can find the question_id at the end of the url on metabase. + I.E. /question/{question_id} + + If, instead of an id, there is a long URL, make sure to save the question first. + + If you want to extract data from a question within a dashboard, click the + question title at the top left of the chart to go directly to that page. + + Valid extensions are: csv and json. + """ + async with ctx.typing(): + + # Make sure we have a session token before running anything + await self.init_task + + if extension not in ("csv", "json"): + # "api" and "xlsx" are supported by metabase's api, but wouldn't work for exporting to pastebin + raise BadArgument(f"{extension} is not a valid extension!") + + url = f"{MetabaseConfig.url}/card/{question_id}/query/{extension}" + async with self.bot.http_session.post(url, headers=self.headers) as resp: + try: + resp.raise_for_status() + except ClientResponseError as e: + if e.status == 403: + # User doesn't have access to the given question + log.warn(f"Failed to auth with Metabase for question {question_id}.") + await ctx.send(f":x: {ctx.author.mention} Failed to auth with Metabase for that question.") + else: + # User credentials are invalid, or the refresh failed + # Delete the expiry time, to force a refresh on next startup + await self.session_info.delete("session_expiry") + log.exception("Session token is invalid or refresh failed.") + await ctx.send(f":x: {ctx.author.mention} Session token is invalid or refresh failed.") + return + + if extension == "csv": + out = await resp.text() + elif extension == "json": + out = await resp.json() + out = json.dumps(out, indent=4, sort_keys=True) + + paste_link = await send_to_paste_service(out, extension=extension) + log.warn(paste_link) + paste_link = 'redacted' + await ctx.send(f":+1: {ctx.author.mention} Here's your link: {paste_link}") + + # This cannot be static (must have a __func__ attribute). + async def cog_check(self, ctx: Context) -> bool: + """Only allow admins inside moderator channels to invoke the commands in this cog.""" + checks = [ + await has_any_role(Roles.admins).predicate(ctx), + is_mod_channel(ctx.channel) + ] + return all(checks) + + def cog_unload(self) -> None: + """Cancel the init task and scheduled tasks.""" + # It's important to wait for init_taskto be cancelled before cancelling scheduled + # tasks. Otherwise, it's possible for _session_scheduler to schedule another task + # after cancel_all has finished, despite _init_task.cancel being called first. + # This is cause cancel() on its own doesn't block until the task is cancelled. + self.init_task.cancel() + self.init_task.add_done_callback(lambda _: self._session_scheduler.cancel_all()) + + +def setup(bot: Bot) -> None: + """Load the Metabase cog.""" + if not all((MetabaseConfig.username, MetabaseConfig.password)): + log.error("Credentials not provided, cog not loaded.") + return + bot.add_cog(Metabase(bot)) -- cgit v1.2.3 From 4b186029de6c9bd34f0fd2ba1dc51c3d8eedc61b Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 10:00:12 +0100 Subject: Remove metabase redaction of link used while testing --- bot/exts/moderation/metabase.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index 3d5e6e0ff..d59d57da1 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -129,8 +129,6 @@ class Metabase(Cog): out = json.dumps(out, indent=4, sort_keys=True) paste_link = await send_to_paste_service(out, extension=extension) - log.warn(paste_link) - paste_link = 'redacted' await ctx.send(f":+1: {ctx.author.mention} Here's your link: {paste_link}") # This cannot be static (must have a __func__ attribute). -- cgit v1.2.3 From 24fbbf6b557653c9a370279781a0be9687a9706c Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 10:39:51 +0100 Subject: Save query outputs to the internal eval environment for ease of access --- bot/exts/moderation/metabase.py | 16 +++++++++++++++- bot/exts/utils/internal.py | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index d59d57da1..ba38eac7c 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -1,6 +1,8 @@ +import csv import json import logging from datetime import timedelta +from io import StringIO from typing import Optional import arrow @@ -35,6 +37,8 @@ class Metabase(Cog): self.session_expiry = None # session_info["session_expiry"]: UtcPosixTimestamp self.headers = BASE_HEADERS + self.exports = {} # Saves the output of each question, so internal eval can access it + self.init_task = self.bot.loop.create_task(self.init_cog()) async def init_cog(self) -> None: @@ -124,12 +128,22 @@ class Metabase(Cog): if extension == "csv": out = await resp.text() + # Save the output for user with int e + with StringIO(out) as f: + self.exports[question_id] = list(csv.DictReader(f)) + elif extension == "json": out = await resp.json() + # Save the output for user with int e + self.exports[question_id] = out + # Format it nicely for human eyes out = json.dumps(out, indent=4, sort_keys=True) paste_link = await send_to_paste_service(out, extension=extension) - await ctx.send(f":+1: {ctx.author.mention} Here's your link: {paste_link}") + await ctx.send( + f":+1: {ctx.author.mention} Here's your link: {paste_link}\n" + f"I've also saved it to `metabase[{question_id}]`, within the internal eval environment for you!" + ) # This cannot be static (must have a __func__ attribute). async def cog_check(self, ctx: Context) -> bool: diff --git a/bot/exts/utils/internal.py b/bot/exts/utils/internal.py index 6f2da3131..668e2f2e7 100644 --- a/bot/exts/utils/internal.py +++ b/bot/exts/utils/internal.py @@ -156,6 +156,9 @@ class Internal(Cog): "contextlib": contextlib } + if metabase := self.bot.get_cog("Metabase"): + env["metabase"] = metabase.exports + self.env.update(env) # Ignore this code, it works -- cgit v1.2.3 From 51fc84ce35f860927b71add4ed96b2b7fec73cb6 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 10:41:21 +0100 Subject: Add comment to int e for context with Metabase loading --- bot/exts/utils/internal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/utils/internal.py b/bot/exts/utils/internal.py index 668e2f2e7..6a3ddb6e5 100644 --- a/bot/exts/utils/internal.py +++ b/bot/exts/utils/internal.py @@ -156,6 +156,7 @@ class Internal(Cog): "contextlib": contextlib } + # If the Metabase cog is loaded, insert all the saved exports into the env if metabase := self.bot.get_cog("Metabase"): env["metabase"] = metabase.exports -- cgit v1.2.3 From f7739956687a21cb7d67b8070ad78b85953469f4 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 10:44:10 +0100 Subject: Fix minor grammer issues with metabase comments --- bot/exts/moderation/metabase.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index ba38eac7c..465242910 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -119,8 +119,8 @@ class Metabase(Cog): log.warn(f"Failed to auth with Metabase for question {question_id}.") await ctx.send(f":x: {ctx.author.mention} Failed to auth with Metabase for that question.") else: - # User credentials are invalid, or the refresh failed - # Delete the expiry time, to force a refresh on next startup + # User credentials are invalid, or the refresh failed. + # Delete the expiry time, to force a refresh on next startup. await self.session_info.delete("session_expiry") log.exception("Session token is invalid or refresh failed.") await ctx.send(f":x: {ctx.author.mention} Session token is invalid or refresh failed.") @@ -128,14 +128,15 @@ class Metabase(Cog): if extension == "csv": out = await resp.text() - # Save the output for user with int e + # Save the output for use with int e with StringIO(out) as f: self.exports[question_id] = list(csv.DictReader(f)) elif extension == "json": out = await resp.json() - # Save the output for user with int e + # Save the output for use with int e self.exports[question_id] = out + # Format it nicely for human eyes out = json.dumps(out, indent=4, sort_keys=True) -- cgit v1.2.3 From 5add89c563405cc184da60fe1b944a5a2260d949 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Sun, 9 May 2021 11:35:22 +0100 Subject: Update warn to warning, due to deprecation This commit also includes a minor docstring change Co-authored-by: Numerlor <25886452+Numerlor@users.noreply.github.com> --- bot/exts/moderation/metabase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index 465242910..c40b6b7e9 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -116,7 +116,7 @@ class Metabase(Cog): except ClientResponseError as e: if e.status == 403: # User doesn't have access to the given question - log.warn(f"Failed to auth with Metabase for question {question_id}.") + log.warning(f"Failed to auth with Metabase for question {question_id}.") await ctx.send(f":x: {ctx.author.mention} Failed to auth with Metabase for that question.") else: # User credentials are invalid, or the refresh failed. @@ -157,7 +157,7 @@ class Metabase(Cog): def cog_unload(self) -> None: """Cancel the init task and scheduled tasks.""" - # It's important to wait for init_taskto be cancelled before cancelling scheduled + # It's important to wait for init_task to be cancelled before cancelling scheduled # tasks. Otherwise, it's possible for _session_scheduler to schedule another task # after cancel_all has finished, despite _init_task.cancel being called first. # This is cause cancel() on its own doesn't block until the task is cancelled. -- cgit v1.2.3 From 2b5da3fb3aa2c32306a3bb7a6fd086277bd8bc94 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Sun, 9 May 2021 11:36:57 +0100 Subject: Remove unneeded context manager in Metabase cog StringIO as it has no underlying connections, so a context manager is not needed Co-authored-by: Numerlor <25886452+Numerlor@users.noreply.github.com> --- bot/exts/moderation/metabase.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index c40b6b7e9..83c63c0f2 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -129,8 +129,7 @@ class Metabase(Cog): if extension == "csv": out = await resp.text() # Save the output for use with int e - with StringIO(out) as f: - self.exports[question_id] = list(csv.DictReader(f)) + self.exports[question_id] = list(csv.DictReader(StringIO(out))) elif extension == "json": out = await resp.json() -- cgit v1.2.3 From a870c1a72e5a8664bac5049ad60e323bea7e399d Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 11:41:20 +0100 Subject: Use allowed strings converter in Metabase cog This removes the need to manually validate user input. Co-authored-by: Numerlor <25886452+Numerlor@users.noreply.github.com> --- bot/exts/moderation/metabase.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index 83c63c0f2..9bd325925 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -3,16 +3,16 @@ import json import logging from datetime import timedelta from io import StringIO -from typing import Optional import arrow from aiohttp.client_exceptions import ClientResponseError from arrow import Arrow from async_rediscache import RedisCache -from discord.ext.commands import BadArgument, Cog, Context, group, has_any_role +from discord.ext.commands import Cog, Context, group, has_any_role from bot.bot import Bot from bot.constants import Metabase as MetabaseConfig, Roles +from bot.converters import allowed_strings from bot.utils import send_to_paste_service from bot.utils.channel import is_mod_channel from bot.utils.scheduling import Scheduler @@ -86,7 +86,12 @@ class Metabase(Cog): await ctx.send_help(ctx.command) @metabase_group.command(name="extract") - async def metabase_extract(self, ctx: Context, question_id: int, extension: Optional[str] = "csv") -> None: + async def metabase_extract( + self, + ctx: Context, + question_id: int, + extension: allowed_strings("csv", "json") = "csv" + ) -> None: """ Extract data from a metabase question. @@ -105,10 +110,6 @@ class Metabase(Cog): # Make sure we have a session token before running anything await self.init_task - if extension not in ("csv", "json"): - # "api" and "xlsx" are supported by metabase's api, but wouldn't work for exporting to pastebin - raise BadArgument(f"{extension} is not a valid extension!") - url = f"{MetabaseConfig.url}/card/{question_id}/query/{extension}" async with self.bot.http_session.post(url, headers=self.headers) as resp: try: -- cgit v1.2.3 From bc7c0990a151cd5a0f5d1681aa25014b392b567f Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 11:46:03 +0100 Subject: Pass raise_for_status as a kwarg for better readibility This means the handling of the response comes directly after the context manager rather than having to handle errors. --- bot/exts/moderation/metabase.py | 54 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index 9bd325925..f419edd05 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -111,34 +111,32 @@ class Metabase(Cog): await self.init_task url = f"{MetabaseConfig.url}/card/{question_id}/query/{extension}" - async with self.bot.http_session.post(url, headers=self.headers) as resp: - try: - resp.raise_for_status() - except ClientResponseError as e: - if e.status == 403: - # User doesn't have access to the given question - log.warning(f"Failed to auth with Metabase for question {question_id}.") - await ctx.send(f":x: {ctx.author.mention} Failed to auth with Metabase for that question.") - else: - # User credentials are invalid, or the refresh failed. - # Delete the expiry time, to force a refresh on next startup. - await self.session_info.delete("session_expiry") - log.exception("Session token is invalid or refresh failed.") - await ctx.send(f":x: {ctx.author.mention} Session token is invalid or refresh failed.") - return - - if extension == "csv": - out = await resp.text() - # Save the output for use with int e - self.exports[question_id] = list(csv.DictReader(StringIO(out))) - - elif extension == "json": - out = await resp.json() - # Save the output for use with int e - self.exports[question_id] = out - - # Format it nicely for human eyes - out = json.dumps(out, indent=4, sort_keys=True) + try: + async with self.bot.http_session.post(url, headers=self.headers, raise_for_status=True) as resp: + if extension == "csv": + out = await resp.text() + # Save the output for use with int e + self.exports[question_id] = list(csv.DictReader(StringIO(out))) + + elif extension == "json": + out = await resp.json() + # Save the output for use with int e + self.exports[question_id] = out + + # Format it nicely for human eyes + out = json.dumps(out, indent=4, sort_keys=True) + except ClientResponseError as e: + if e.status == 403: + # User doesn't have access to the given question + log.warning(f"Failed to auth with Metabase for question {question_id}.") + await ctx.send(f":x: {ctx.author.mention} Failed to auth with Metabase for that question.") + else: + # User credentials are invalid, or the refresh failed. + # Delete the expiry time, to force a refresh on next startup. + await self.session_info.delete("session_expiry") + log.exception("Session token is invalid or refresh failed.") + await ctx.send(f":x: {ctx.author.mention} Session token is invalid or refresh failed.") + return paste_link = await send_to_paste_service(out, extension=extension) await ctx.send( -- cgit v1.2.3 From 00362198d0fef36e8973b8b034d161f5abd3113e Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 11:56:16 +0100 Subject: Revert changes to int e This commit reverts the changes to int e, by giving the invoker instructions on how to access the export via int e. This means the metabase exports are not inserted to every int e envrionment. I have also added a seperate message in this commit to handle when the paste service is offline. --- bot/exts/moderation/metabase.py | 8 ++++++-- bot/exts/utils/internal.py | 4 ---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index f419edd05..6f05a320b 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -139,9 +139,13 @@ class Metabase(Cog): return paste_link = await send_to_paste_service(out, extension=extension) + if paste_link: + message = f":+1: {ctx.author.mention} Here's your link: {paste_link}" + else: + message = f":x: {ctx.author.mention} Link service is unavailible." await ctx.send( - f":+1: {ctx.author.mention} Here's your link: {paste_link}\n" - f"I've also saved it to `metabase[{question_id}]`, within the internal eval environment for you!" + f"{message}\nYou can also access this data within internal eval by doing: " + f"`bot.get_cog('Metabase').exports[{question_id}]`" ) # This cannot be static (must have a __func__ attribute). diff --git a/bot/exts/utils/internal.py b/bot/exts/utils/internal.py index 6a3ddb6e5..6f2da3131 100644 --- a/bot/exts/utils/internal.py +++ b/bot/exts/utils/internal.py @@ -156,10 +156,6 @@ class Internal(Cog): "contextlib": contextlib } - # If the Metabase cog is loaded, insert all the saved exports into the env - if metabase := self.bot.get_cog("Metabase"): - env["metabase"] = metabase.exports - self.env.update(env) # Ignore this code, it works -- cgit v1.2.3 From 8c731f6cf2362d7ee89492e57236c803bd700a55 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 22:20:04 +0100 Subject: Type hint the Metabase cog! Co-authored-by: Xithrius --- bot/exts/moderation/metabase.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index 6f05a320b..e1531c467 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -3,6 +3,7 @@ import json import logging from datetime import timedelta from io import StringIO +from typing import Dict, List, Optional import arrow from aiohttp.client_exceptions import ClientResponseError @@ -33,11 +34,11 @@ class Metabase(Cog): self.bot = bot self._session_scheduler = Scheduler(self.__class__.__name__) - self.session_token = None # session_info["session_token"]: str - self.session_expiry = None # session_info["session_expiry"]: UtcPosixTimestamp + self.session_token: Optional[str] = None # session_info["session_token"]: str + self.session_expiry: Optional[float] = None # session_info["session_expiry"]: UtcPosixTimestamp self.headers = BASE_HEADERS - self.exports = {} # Saves the output of each question, so internal eval can access it + self.exports: Dict[int, List[Dict]] = {} # Saves the output of each question, so internal eval can access it self.init_task = self.bot.loop.create_task(self.init_cog()) -- cgit v1.2.3 From c7eb358cf6e747ea30acbd349597814349961459 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 May 2021 22:21:03 +0100 Subject: Move a long comment in Metabse cog into the func doc string Co-authored-by: Xithrius --- bot/exts/moderation/metabase.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py index e1531c467..db5f04d83 100644 --- a/bot/exts/moderation/metabase.py +++ b/bot/exts/moderation/metabase.py @@ -159,11 +159,14 @@ class Metabase(Cog): return all(checks) def cog_unload(self) -> None: - """Cancel the init task and scheduled tasks.""" - # It's important to wait for init_task to be cancelled before cancelling scheduled - # tasks. Otherwise, it's possible for _session_scheduler to schedule another task - # after cancel_all has finished, despite _init_task.cancel being called first. - # This is cause cancel() on its own doesn't block until the task is cancelled. + """ + Cancel the init task and scheduled tasks. + + It's important to wait for init_task to be cancelled before cancelling scheduled + tasks. Otherwise, it's possible for _session_scheduler to schedule another task + after cancel_all has finished, despite _init_task.cancel being called first. + This is cause cancel() on its own doesn't block until the task is cancelled. + """ self.init_task.cancel() self.init_task.add_done_callback(lambda _: self._session_scheduler.cancel_all()) -- cgit v1.2.3 From b85da2582b6bf2c4e103a17c4448efd6ae40cb90 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 13 May 2021 15:24:34 +0100 Subject: Delete reddit cog constants These were added back accidentaly as part of a merge conflict. --- bot/constants.py | 8 -------- config-default.yml | 7 ------- 2 files changed, 15 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 0a3bc6578..2c5c04b2e 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -546,14 +546,6 @@ class URLs(metaclass=YAMLGetter): paste_service: str - -class Reddit(metaclass=YAMLGetter): - section = "reddit" - - client_id: Optional[str] - secret: Optional[str] - subreddits: list - class Metabase(metaclass=YAMLGetter): section = "metabase" diff --git a/config-default.yml b/config-default.yml index ebd253c2c..c59bff524 100644 --- a/config-default.yml +++ b/config-default.yml @@ -418,13 +418,6 @@ anti_spam: -reddit: - client_id: !ENV "REDDIT_CLIENT_ID" - secret: !ENV "REDDIT_SECRET" - subreddits: - - 'r/Python' - - metabase: username: !ENV "METABASE_USERNAME" password: !ENV "METABASE_PASSWORD" -- cgit v1.2.3