From 18f0f36a713c32176440cb113ad5814da9020717 Mon Sep 17 00:00:00 2001 From: Izan Date: Mon, 20 Sep 2021 11:35:34 +0100 Subject: Fix typos and grammar in tags --- bot/resources/tags/async-await.md | 8 ++++---- bot/resources/tags/traceback.md | 12 ++++++------ bot/resources/tags/windows-path.md | 8 ++++---- bot/resources/tags/xy-problem.md | 4 ++-- bot/resources/tags/ytdl.md | 2 +- bot/resources/tags/zip.md | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bot/resources/tags/async-await.md b/bot/resources/tags/async-await.md index ff71ace07..4ab8a76fc 100644 --- a/bot/resources/tags/async-await.md +++ b/bot/resources/tags/async-await.md @@ -2,18 +2,18 @@ Python provides the ability to run multiple tasks and coroutines simultaneously with the use of the `asyncio` library, which is included in the Python standard library. -This works by running these coroutines in an event loop, where the context of which coroutine is being run is switches periodically to allow all of them to run, giving the appearance of running at the same time. This is different to using threads or processes in that all code is run in the main process and thread, although it is possible to run coroutines in threads. +This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all the coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in threads. To call an async function we can either `await` it, or run it in an event loop which we get from `asyncio`. -To create a coroutine that can be used with asyncio we need to define a function using the async keyword: +To create a coroutine that can be used with asyncio we need to define a function using the `async` keyword: ```py async def main(): await something_awaitable() ``` -Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function this would have raised an exception like: `SyntaxError: 'await' outside async function` +Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function, this would have raised the exception `SyntaxError: 'await' outside async function` -To run the top level async function from outside of the event loop we can get an event loop from `asyncio`, and then use that loop to run the function: +To run the top level async function from outside the event loop, we can get the event loop from `asyncio` and then use it to run the function: ```py from asyncio import get_event_loop diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md index e770fa86d..be5a8bf90 100644 --- a/bot/resources/tags/traceback.md +++ b/bot/resources/tags/traceback.md @@ -1,4 +1,4 @@ -Please provide a full traceback to your exception in order for us to identify your issue. +Please provide the full traceback for your exception in order to help us identify your issue. A full traceback could look like: ```py @@ -6,13 +6,13 @@ Traceback (most recent call last): File "tiny", line 3, in do_something() File "tiny", line 2, in do_something - a = 6 / 0 + a = 6 / b ZeroDivisionError: integer division or modulo by zero ``` The best way to read your traceback is bottom to top. -• Identify the exception raised (e.g. ZeroDivisionError) -• Make note of the line number, and navigate there in your program. -• Try to understand why the error occurred. +• Identify the exception raised (e.g. `ZeroDivisionError`) +• Make note of the line number (in this case 2), and navigate there in your program. +• Try to understand why the error occurred (in this case because `b` must be `0`). -To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/asking-good-questions/#examining-tracebacks) or the [official Python tutorial.](https://docs.python.org/3.7/tutorial/errors.html) +To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/guides/pydis-guides/asking-good-questions/#examining-tracebacks) or the [official Python tutorial](https://docs.python.org/3.7/tutorial/errors.html). diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md index da8edf685..3738d1bc8 100644 --- a/bot/resources/tags/windows-path.md +++ b/bot/resources/tags/windows-path.md @@ -1,10 +1,10 @@ **PATH on Windows** -If you have installed Python but you forgot to check the *Add Python to PATH* option during the installation you may still be able to access your installation with ease. +If you have installed Python, but you forgot to check the *Add Python to PATH* option during the installation, you may still be able to access your installation with ease. -If you did not uncheck the option to install the Python launcher then you will find a `py` command on your system. If you want to be able to open your Python installation by running `python` then your best option is to re-install Python. +If you did not uncheck the option to install the Python launcher then you will find a `py` command on your system. If you want to be able to open your Python installation by running `python`, then your best option is to re-install Python. -Otherwise, you can access your install using the `py` command in Command Prompt. Where you may type something with the `python` command like: +Otherwise, you can access your installation using the `py` command in Command Prompt, where you may type something with the `python` command such as: ``` C:\Users\Username> python3 my_application_file.py ``` @@ -24,7 +24,7 @@ You can also access different versions of Python using the version flag, like so C:\Users\Username> py -3.7 ... Python 3.7 starts ... C:\Users\Username> py -3.6 -... Python 3.6 stars ... +... Python 3.6 starts ... C:\Users\Username> py -2 ... Python 2 (any version installed) starts ... ``` diff --git a/bot/resources/tags/xy-problem.md b/bot/resources/tags/xy-problem.md index b77bd27e8..8c508f18c 100644 --- a/bot/resources/tags/xy-problem.md +++ b/bot/resources/tags/xy-problem.md @@ -1,7 +1,7 @@ **xy-problem** -Asking about your attempted solution rather than your actual problem. +The XY problem can be summarised as asking about your attempted solution, rather than your actual problem. Often programmers will get distracted with a potential solution they've come up with, and will try asking for help getting it to work. However, it's possible this solution either wouldn't work as they expect, or there's a much better solution instead. -For more information and examples: http://xyproblem.info/ +For more information and examples, see http://xyproblem.info/ diff --git a/bot/resources/tags/ytdl.md b/bot/resources/tags/ytdl.md index f96b7f853..68a0a0cdb 100644 --- a/bot/resources/tags/ytdl.md +++ b/bot/resources/tags/ytdl.md @@ -1,4 +1,4 @@ -Per [Python Discord's Rule 5](https://pythondiscord.com/pages/rules), we are unable to assist with questions related to youtube-dl, pytube, or other YouTube video downloaders as their usage violates YouTube's Terms of Service. +Per [Python Discord's Rule 5](https://pythondiscord.com/pages/rules), we are unable to assist with questions related to youtube-dl, pytube, or other YouTube video downloaders, as their usage violates YouTube's Terms of Service. For reference, this usage is covered by the following clauses in [YouTube's TOS](https://www.youtube.com/static?gl=GB&template=terms), as of 2021-03-17: ``` diff --git a/bot/resources/tags/zip.md b/bot/resources/tags/zip.md index 6b05f0282..6f3157f71 100644 --- a/bot/resources/tags/zip.md +++ b/bot/resources/tags/zip.md @@ -3,7 +3,7 @@ The zip function allows you to iterate through multiple iterables simultaneously ```py letters = 'abc' numbers = [1, 2, 3] -# zip(letters, numbers) --> [('a', 1), ('b', 2), ('c', 3)] +# list(zip(letters, numbers)) --> [('a', 1), ('b', 2), ('c', 3)] for letter, number in zip(letters, numbers): print(letter, number) ``` -- cgit v1.2.3 From 33b4f42b481e6b5e1686f66803dc916595e4b457 Mon Sep 17 00:00:00 2001 From: Izan Date: Sat, 25 Sep 2021 21:58:44 +0100 Subject: Bluenix Review This commit should be squashed upon PR merge. - Made changes in `async-await.md`, including changing `run_until_complete()` to `asyncio.run()` - Minor changes in `traceback.md` - Rewriting `windows-path.md` --- bot/resources/tags/async-await.md | 11 +++++------ bot/resources/tags/traceback.md | 8 ++++---- bot/resources/tags/windows-path.md | 21 ++++----------------- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/bot/resources/tags/async-await.md b/bot/resources/tags/async-await.md index 4ab8a76fc..a20332df6 100644 --- a/bot/resources/tags/async-await.md +++ b/bot/resources/tags/async-await.md @@ -2,7 +2,7 @@ Python provides the ability to run multiple tasks and coroutines simultaneously with the use of the `asyncio` library, which is included in the Python standard library. -This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all the coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in threads. +This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all other coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in threads. To call an async function we can either `await` it, or run it in an event loop which we get from `asyncio`. @@ -13,16 +13,15 @@ async def main(): ``` Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function, this would have raised the exception `SyntaxError: 'await' outside async function` -To run the top level async function from outside the event loop, we can get the event loop from `asyncio` and then use it to run the function: +To run the top level async function from outside the event loop we need to use `[asyncio.run()`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run), like this: ```py -from asyncio import get_event_loop +import asyncio async def main(): await something_awaitable() -loop = get_event_loop() -loop.run_until_complete(main()) +asyncio.run(main()) ``` -Note that in the `run_until_complete()` where we appear to be calling `main()`, this does not execute the code in `main`, rather it returns a `coroutine` object which is then handled and run by the event loop via `run_until_complete()`. +Note that in the `asyncio.run()` where we appear to be calling `main()`, this does not execute the code in `main`, rather it returns a `coroutine` object which is then handled and run by the event loop via `asyncio.run`. To learn more about asyncio and its use, see the [asyncio documentation](https://docs.python.org/3/library/asyncio.html). diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md index be5a8bf90..321737aac 100644 --- a/bot/resources/tags/traceback.md +++ b/bot/resources/tags/traceback.md @@ -7,12 +7,12 @@ Traceback (most recent call last): do_something() File "tiny", line 2, in do_something a = 6 / b -ZeroDivisionError: integer division or modulo by zero +ZeroDivisionError: division by zero ``` The best way to read your traceback is bottom to top. -• Identify the exception raised (e.g. `ZeroDivisionError`) -• Make note of the line number (in this case 2), and navigate there in your program. -• Try to understand why the error occurred (in this case because `b` must be `0`). +• Identify the exception raised (in this case `ZeroDivisionError`) +• Make note of the line number (in this case `2`), and navigate there in your program. +• Try to understand why the error occurred (in this case because `b` is `0`). To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/guides/pydis-guides/asking-good-questions/#examining-tracebacks) or the [official Python tutorial](https://docs.python.org/3.7/tutorial/errors.html). diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md index 3738d1bc8..e8f5690fb 100644 --- a/bot/resources/tags/windows-path.md +++ b/bot/resources/tags/windows-path.md @@ -1,25 +1,12 @@ **PATH on Windows** -If you have installed Python, but you forgot to check the *Add Python to PATH* option during the installation, you may still be able to access your installation with ease. +If you have installed Python but forgot to check the *Add Python to PATH* option during the installation, you may still be able to access your installation with ease. -If you did not uncheck the option to install the Python launcher then you will find a `py` command on your system. If you want to be able to open your Python installation by running `python`, then your best option is to re-install Python. +If you did not uncheck the option to install the Python launcher, then you'll instead have a `py` command which can be used in the same way. If you want to be able to access your Python installation via the `python` command, then your best option is to re-install Python (remembering to tick the `add to PATH` checkbox). -Otherwise, you can access your installation using the `py` command in Command Prompt, where you may type something with the `python` command such as: -``` -C:\Users\Username> python3 my_application_file.py -``` - -You can achieve the same result using the `py` command like this: -``` -C:\Users\Username> py -3 my_application_file.py -``` - -You can pass any options to the Python interpreter after you specify a version, for example, to install a Python module using `pip` you can run: -``` -C:\Users\Username> py -3 -m pip install numpy -``` +You can pass any options to the Python interpreter, e.g. to install the `[numpy](https://pypi.org/project/numpy/)` module from PyPI you can run `py -3 -m pip install numpy` or `python -m pip install numpy`. -You can also access different versions of Python using the version flag, like so: +You can also access different versions of Python using the version flag of the `py` command, like so: ``` C:\Users\Username> py -3.7 ... Python 3.7 starts ... -- cgit v1.2.3 From 7ec98f71b6f650794e26d73a2503bf2e472e1228 Mon Sep 17 00:00:00 2001 From: Izan Date: Sat, 25 Sep 2021 23:51:04 +0100 Subject: Bluenix Review #2 This commit should be squashed upon PR merge. - Made some more fixes in `async-await.md` --- bot/resources/tags/async-await.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/async-await.md b/bot/resources/tags/async-await.md index a20332df6..e945240ba 100644 --- a/bot/resources/tags/async-await.md +++ b/bot/resources/tags/async-await.md @@ -2,7 +2,7 @@ Python provides the ability to run multiple tasks and coroutines simultaneously with the use of the `asyncio` library, which is included in the Python standard library. -This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all other coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in threads. +This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all other coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in other threads. To call an async function we can either `await` it, or run it in an event loop which we get from `asyncio`. @@ -11,7 +11,7 @@ To create a coroutine that can be used with asyncio we need to define a function async def main(): await something_awaitable() ``` -Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function, this would have raised the exception `SyntaxError: 'await' outside async function` +Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function, it would raise the exception `SyntaxError: 'await' outside async function` To run the top level async function from outside the event loop we need to use `[asyncio.run()`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run), like this: ```py -- cgit v1.2.3 From 093fdaa2b8c89b770d7ff2b7889077dd9e21ad2d Mon Sep 17 00:00:00 2001 From: Izan Date: Sun, 26 Sep 2021 00:29:25 +0100 Subject: Bluenix Review #3 This commit should be squashed upon PR merge. - Another fix in `windows-path.md` --- bot/resources/tags/windows-path.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md index e8f5690fb..b2b0da029 100644 --- a/bot/resources/tags/windows-path.md +++ b/bot/resources/tags/windows-path.md @@ -1,8 +1,8 @@ **PATH on Windows** -If you have installed Python but forgot to check the *Add Python to PATH* option during the installation, you may still be able to access your installation with ease. +If you have installed Python but forgot to check the `Add Python to PATH` option during the installation, you may still be able to access your installation with ease. -If you did not uncheck the option to install the Python launcher, then you'll instead have a `py` command which can be used in the same way. If you want to be able to access your Python installation via the `python` command, then your best option is to re-install Python (remembering to tick the `add to PATH` checkbox). +If you did not uncheck the option to install the `py launcher`, then you'll instead have a `py` command which can be used in the same way. If you want to be able to access your Python installation via the `python` command, then your best option is to re-install Python (remembering to tick the `Add Python to PATH` checkbox). You can pass any options to the Python interpreter, e.g. to install the `[numpy](https://pypi.org/project/numpy/)` module from PyPI you can run `py -3 -m pip install numpy` or `python -m pip install numpy`. -- cgit v1.2.3 From b40459a716c01c4d63a84ac886eab8884aeffefb Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 6 Oct 2021 22:11:45 +0100 Subject: Add appeal categories to mod categories This allows us to run moderation commands in the appeal categories --- config-default.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config-default.yml b/config-default.yml index 3405934e0..57bfc2687 100644 --- a/config-default.yml +++ b/config-default.yml @@ -144,6 +144,8 @@ guild: logs: &LOGS 468520609152892958 moderators: &MODS_CATEGORY 749736277464842262 modmail: &MODMAIL 714494672835444826 + appeals: &APPEALS 890331800025563216 + appeals2: &APPEALS2 895417395261341766 voice: 356013253765234688 summer_code_jam: 861692638540857384 @@ -238,6 +240,8 @@ guild: - *MODS_CATEGORY - *MODMAIL - *LOGS + - *APPEALS + - *APPEALS2 moderation_channels: - *ADMINS -- cgit v1.2.3 From a84cab132cc4de899aaf0a804d8bb11f0e3857a8 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Tue, 8 Jun 2021 00:34:28 +0200 Subject: Kill subdomains from configuration file. This will need a coordinated rollout with @jb3, in order to make the appropriate changes in Kubernetes beforehand. --- config-default.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config-default.yml b/config-default.yml index 3405934e0..943463bc0 100644 --- a/config-default.yml +++ b/config-default.yml @@ -357,14 +357,14 @@ urls: connect_max_retries: 3 connect_cooldown: 5 site: &DOMAIN "pythondiscord.com" - site_api: &API "pydis-api.default.svc.cluster.local" + site_api: &API "site.default.svc.cluster.local/api" site_api_schema: "http://" site_paste: &PASTE !JOIN ["paste.", *DOMAIN] site_schema: &SCHEMA "https://" - site_staff: &STAFF !JOIN ["staff.", *DOMAIN] + site_staff: &STAFF !JOIN [*SCHEMA, *DOMAIN, "/staff"] paste_service: !JOIN [*SCHEMA, *PASTE, "/{key}"] - site_logs_view: !JOIN [*SCHEMA, *STAFF, "/bot/logs"] + site_logs_view: !JOIN [*STAFF, "/bot/logs"] # Snekbox snekbox_eval_api: "http://snekbox.default.svc.cluster.local/eval" -- cgit v1.2.3 From a1dd2a942fb3a521dcb4c199ebfb3ea368649b10 Mon Sep 17 00:00:00 2001 From: Izan Date: Fri, 8 Oct 2021 10:07:11 +0100 Subject: Bluenix Review #4 --- bot/resources/tags/async-await.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/async-await.md b/bot/resources/tags/async-await.md index e945240ba..52d3e47e0 100644 --- a/bot/resources/tags/async-await.md +++ b/bot/resources/tags/async-await.md @@ -13,7 +13,7 @@ async def main(): ``` Which means we can call `await something_awaitable()` directly from within the function. If this were a non-async function, it would raise the exception `SyntaxError: 'await' outside async function` -To run the top level async function from outside the event loop we need to use `[asyncio.run()`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run), like this: +To run the top level async function from outside the event loop we need to use [`asyncio.run()`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run), like this: ```py import asyncio @@ -22,6 +22,6 @@ async def main(): asyncio.run(main()) ``` -Note that in the `asyncio.run()` where we appear to be calling `main()`, this does not execute the code in `main`, rather it returns a `coroutine` object which is then handled and run by the event loop via `asyncio.run`. +Note that in the `asyncio.run()`, where we appear to be calling `main()`, this does not execute the code in `main`. Rather, it creates and returns a new `coroutine` object (i.e `mmain() is not main()`) which is then handled and run by the event loop via `asyncio.run()`. To learn more about asyncio and its use, see the [asyncio documentation](https://docs.python.org/3/library/asyncio.html). -- cgit v1.2.3 From d63406c6b96093d74799a4e7cb681d3b94abf3c8 Mon Sep 17 00:00:00 2001 From: TizzySaurus <47674925+TizzySaurus@users.noreply.github.com> Date: Fri, 8 Oct 2021 11:18:06 +0100 Subject: Fix typo Co-authored-by: Bluenix --- bot/resources/tags/async-await.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/async-await.md b/bot/resources/tags/async-await.md index 52d3e47e0..01ab28fe3 100644 --- a/bot/resources/tags/async-await.md +++ b/bot/resources/tags/async-await.md @@ -22,6 +22,6 @@ async def main(): asyncio.run(main()) ``` -Note that in the `asyncio.run()`, where we appear to be calling `main()`, this does not execute the code in `main`. Rather, it creates and returns a new `coroutine` object (i.e `mmain() is not main()`) which is then handled and run by the event loop via `asyncio.run()`. +Note that in the `asyncio.run()`, where we appear to be calling `main()`, this does not execute the code in `main`. Rather, it creates and returns a new `coroutine` object (i.e `main() is not main()`) which is then handled and run by the event loop via `asyncio.run()`. To learn more about asyncio and its use, see the [asyncio documentation](https://docs.python.org/3/library/asyncio.html). -- cgit v1.2.3 From 016f65b4d450ff538a2b5deb21f8f03d678dac84 Mon Sep 17 00:00:00 2001 From: Richard Si <63936253+ichard26@users.noreply.github.com> Date: Sun, 10 Oct 2021 13:41:24 -0400 Subject: Handle 400 when setting pre-existing doc package If you run, for example: !doc setdoc black https://black.readthedocs/en/stable/objects.inv twice over. You'll get an unhelpful "According to the API, your request is malformed." error message back. This commit adds an error handler to catch the HTTP 400 and tell the user the package already exists. --- bot/exts/info/doc/_cog.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index e7710db24..7d63bc9a9 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -13,6 +13,7 @@ import aiohttp import discord from discord.ext import commands +from bot.api import ResponseCodeError from bot.bot import Bot from bot.constants import MODERATION_ROLES, RedirectOutput from bot.converters import Inventory, PackageName, ValidURL, allowed_strings @@ -395,7 +396,14 @@ class DocCog(commands.Cog): "base_url": base_url, "inventory_url": inventory_url } - await self.bot.api_client.post("bot/documentation-links", json=body) + try: + await self.bot.api_client.post("bot/documentation-links", json=body) + except ResponseCodeError as err: + if err.status == 400 and "already exists" in err.response_json.get("package", [""])[0]: + log.info(f"Ignoring HTTP 400 as package {package_name} has already been added.") + await ctx.send(f"Package {package_name} has already already added.") + return + raise log.info( f"User @{ctx.author} ({ctx.author.id}) added a new documentation package:\n" -- cgit v1.2.3 From 72c843e73831406e013b7478d4c12d08ec27021b Mon Sep 17 00:00:00 2001 From: Richard Si <63936253+ichard26@users.noreply.github.com> Date: Sun, 10 Oct 2021 17:08:34 -0400 Subject: Fix typo in bot/exts/info/doc/_cog.py it only took 5 people looking through the diff ... Co-authored-by: Bluenix --- bot/exts/info/doc/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index 7d63bc9a9..ca6af946b 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -401,7 +401,7 @@ class DocCog(commands.Cog): except ResponseCodeError as err: if err.status == 400 and "already exists" in err.response_json.get("package", [""])[0]: log.info(f"Ignoring HTTP 400 as package {package_name} has already been added.") - await ctx.send(f"Package {package_name} has already already added.") + await ctx.send(f"Package {package_name} has already been added.") return raise -- cgit v1.2.3