From 47c4321b1fbd49896a6935c58e1bec9dbaf6918f Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:02:52 -0800 Subject: Added how_to_get_help constant. --- bot/constants.py | 1 + config-default.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 95e22513f..6b86d33a3 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -406,6 +406,7 @@ class Channels(metaclass=YAMLGetter): meta: int python_general: int + how_to_get_help: int cooldown: int diff --git a/config-default.yml b/config-default.yml index d3b267159..913d5ca09 100644 --- a/config-default.yml +++ b/config-default.yml @@ -158,6 +158,7 @@ guild: python_general: &PY_GENERAL 267624335836053506 # Python Help: Available + how_to_get_help: 704250143020417084 cooldown: 720603994149486673 # Topical -- cgit v1.2.3 From 0f46bf58bea83da9434b53ddfda3ce8331829588 Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:07:37 -0800 Subject: Added dynamic available help channels message --- bot/exts/help_channels/_cog.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 0995c8a79..c4ec820b5 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,6 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot +from bot.constants import Channels, Categories from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -20,6 +21,9 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ +AVAILABLE_HELP_CHANNELS = """ +Currently available help channel(s): {available} +""" class HelpChannels(commands.Cog): @@ -72,6 +76,11 @@ class HelpChannels(commands.Cog): self.last_notification: t.Optional[datetime] = None + # Acquiring and modifying the channel to dynamically update the available help channels message. + self.how_to_get_help: discord.TextChannel = None + self.available_help_channels: t.Set[int] = set() + self.dynamic_message: discord.Message = None + # Asyncio stuff self.queue_tasks: t.List[asyncio.Task] = [] self.init_task = self.bot.loop.create_task(self.init_cog()) @@ -114,6 +123,9 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) + self.available_help_channels.remove(message.channel.id) + await self.update_available_help_channels() + # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -275,6 +287,15 @@ class HelpChannels(commands.Cog): # This may confuse users. So would potentially long delays for the cog to become ready. self.close_command.enabled = True + # Getting channels that need to be included in the dynamic message. + task = asyncio.create_task(self.update_available_help_channels()) + self.queue_tasks.append(task) + + await task + + log.trace(f"Dynamic available help message updated.") + self.queue_tasks.remove(task) + await self.init_available() _stats.report_counts() @@ -332,6 +353,10 @@ class HelpChannels(commands.Cog): category_id=constants.Categories.help_available, ) + # Adding the help channel to the dynamic message, and editing/sending that message. + self.available_help_channels.add(channel.id) + await self.update_available_help_channels() + _stats.report_counts() async def move_to_dormant(self, channel: discord.TextChannel) -> None: @@ -461,3 +486,26 @@ class HelpChannels(commands.Cog): self.queue_tasks.remove(task) return channel + + async def update_available_help_channels(self) -> None: + """Updates the dynamic message within #how-to-get-help for available help channels.""" + if not self.available_help_channels: + available_channels_category = await channel_utils.try_get_channel(Categories.help_available) + self.available_help_channels = set( + c.id for c in available_channels_category.channels if 'help-' in c.name + ) + + if self.how_to_get_help is None: + self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) + + if self.dynamic_message is None: + self.dynamic_message = await self.how_to_get_help.history(limit=1).next() + + available_channels = AVAILABLE_HELP_CHANNELS.format( + available=', '.join(f"<#{c}>" for c in self.available_help_channels) + ) + + try: + await self.dynamic_message.edit(content=available_channels) + except discord.Forbidden: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From 5f17d4d19d0952c91ead096a52b206eea86851fe Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:18:20 -0800 Subject: Fixed up linting errors. --- bot/exts/help_channels/_cog.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index c4ec820b5..943b63a42 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Channels, Categories +from bot.constants import Categories, Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -293,7 +293,7 @@ class HelpChannels(commands.Cog): await task - log.trace(f"Dynamic available help message updated.") + log.trace("Dynamic available help message updated.") self.queue_tasks.remove(task) await self.init_available() @@ -499,7 +499,8 @@ class HelpChannels(commands.Cog): self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) if self.dynamic_message is None: - self.dynamic_message = await self.how_to_get_help.history(limit=1).next() + last_message = await self.how_to_get_help.history(limit=1) + self.dynamic_message = next(last_message) available_channels = AVAILABLE_HELP_CHANNELS.format( available=', '.join(f"<#{c}>" for c in self.available_help_channels) -- cgit v1.2.3 From 219ac90d494793a99d77ef5a4e912151e936b1d8 Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 17:12:25 -0800 Subject: Fixed logic in case dynamic message doesn't exist. --- bot/exts/help_channels/_cog.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 943b63a42..730635f08 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -495,18 +495,16 @@ class HelpChannels(commands.Cog): c.id for c in available_channels_category.channels if 'help-' in c.name ) - if self.how_to_get_help is None: - self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - - if self.dynamic_message is None: - last_message = await self.how_to_get_help.history(limit=1) - self.dynamic_message = next(last_message) - available_channels = AVAILABLE_HELP_CHANNELS.format( available=', '.join(f"<#{c}>" for c in self.available_help_channels) ) - try: - await self.dynamic_message.edit(content=available_channels) - except discord.Forbidden: - self.dynamic_message = await self.how_to_get_help.send(available_channels) + if self.how_to_get_help is None: + self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) + + if self.dynamic_message is None: + try: + self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) + await self.dynamic_message.edit(content=available_channels) + except discord.NotFound: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From 12d8670de79011dc1095293ddc7b256f033fcead Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:22:21 -0800 Subject: 'None' is now shown if no channels are available. --- bot/exts/help_channels/_cog.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 730635f08..2f14146ab 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -123,12 +123,13 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - self.available_help_channels.remove(message.channel.id) - await self.update_available_help_channels() - # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") + # Removing the help channel from the dynamic message, and editing/sending that message. + self.available_help_channels.remove(message.channel.id) + await self.update_available_help_channels() + def create_channel_queue(self) -> asyncio.Queue: """ Return a queue of dormant channels to use for getting the next available channel. @@ -496,15 +497,15 @@ class HelpChannels(commands.Cog): ) available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(f"<#{c}>" for c in self.available_help_channels) + available=', '.join(f"<#{c}>" for c in self.available_help_channels) or None ) if self.how_to_get_help is None: self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - if self.dynamic_message is None: - try: + try: + if self.dynamic_message is None: self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) - await self.dynamic_message.edit(content=available_channels) - except discord.NotFound: - self.dynamic_message = await self.how_to_get_help.send(available_channels) + await self.dynamic_message.edit(content=available_channels) + except discord.NotFound: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From d2a10cd1b758625ea165e599c0271d9e43f0ab8a Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:35:11 -0800 Subject: Alphabetized config-default.yml. --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index 913d5ca09..fc1f3b3a8 100644 --- a/config-default.yml +++ b/config-default.yml @@ -158,8 +158,8 @@ guild: python_general: &PY_GENERAL 267624335836053506 # Python Help: Available - how_to_get_help: 704250143020417084 cooldown: 720603994149486673 + how_to_get_help: 704250143020417084 # Topical discord_py: 343944376055103488 -- cgit v1.2.3 From 3a4d38bc27dc8214af91ee8ab598a5b60897815f Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:53:54 -0800 Subject: Removed unnecessary update method call. --- bot/exts/help_channels/_cog.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 2f14146ab..d50197339 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -123,12 +123,11 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - # Not awaited because it may indefinitely hold the lock while waiting for a channel. - scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") - # Removing the help channel from the dynamic message, and editing/sending that message. self.available_help_channels.remove(message.channel.id) - await self.update_available_help_channels() + + # Not awaited because it may indefinitely hold the lock while waiting for a channel. + scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") def create_channel_queue(self) -> asyncio.Queue: """ -- cgit v1.2.3 From 88b88ba5c42f7db2d253493fa9b3749287d31ffb Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 19:14:02 -0800 Subject: Replaced fetching available category for old one. --- bot/exts/help_channels/_cog.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index d50197339..4520b408d 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Categories, Channels +from bot.constants import Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -490,9 +490,8 @@ class HelpChannels(commands.Cog): async def update_available_help_channels(self) -> None: """Updates the dynamic message within #how-to-get-help for available help channels.""" if not self.available_help_channels: - available_channels_category = await channel_utils.try_get_channel(Categories.help_available) self.available_help_channels = set( - c.id for c in available_channels_category.channels if 'help-' in c.name + c.id for c in self.available_category.channels if 'help-' in c.name ) available_channels = AVAILABLE_HELP_CHANNELS.format( -- cgit v1.2.3 From 2940ba31bb5118f7c9668d14c3d9ffa7611b3890 Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 5 Feb 2021 02:55:50 -0800 Subject: Modified the dynamic to be bold to catch eyes. --- bot/exts/help_channels/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 4520b408d..e9333b9a6 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -22,7 +22,7 @@ HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ AVAILABLE_HELP_CHANNELS = """ -Currently available help channel(s): {available} +**Currently available help channel(s):** {available} """ -- cgit v1.2.3 From 2fdb0e5fce6d246cfc67962e235b4d53622f03d7 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:13:40 +0400 Subject: Make `KeyError` tag --- bot/resources/tags/keyerror.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 bot/resources/tags/keyerror.md diff --git a/bot/resources/tags/keyerror.md b/bot/resources/tags/keyerror.md new file mode 100644 index 000000000..d0c069004 --- /dev/null +++ b/bot/resources/tags/keyerror.md @@ -0,0 +1,17 @@ +Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary. \ +While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. +## __The `dict.get` method__ +The [dict.get](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, or None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. +```py +>>> my_dict = {"foo": 1, "bar": 2} +>>> print(my_dict.get("foo")) +1 +>>> print(my_dict.get("foobar")) +None +>>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, in case the key doesn't exist +3 +>>> print(my_dict) +{'foo': 1, 'bar': 2} # note that the new key was NOT added to the dictionary +``` +\ +Some other methods that can be used for handling KeyErrors gracefully are the [dict.setdefault](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [collections.defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict). -- cgit v1.2.3 From 2c4d7f41432bb620d83d3403fe4ab9317bc1129f Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:39:36 +0400 Subject: Update and rename keyerror.md to dict-get.md --- bot/resources/tags/dict-get.md | 13 +++++++++++++ bot/resources/tags/keyerror.md | 17 ----------------- 2 files changed, 13 insertions(+), 17 deletions(-) create mode 100644 bot/resources/tags/dict-get.md delete mode 100644 bot/resources/tags/keyerror.md diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md new file mode 100644 index 000000000..b22db7af5 --- /dev/null +++ b/bot/resources/tags/dict-get.md @@ -0,0 +1,13 @@ +Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary.\ +While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. +__**The `dict.get` method**__ +The [`dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, or None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. +```py +>>> my_dict = {"foo": 1, "bar": 2} +>>> print(my_dict.get("foobar")) +None +>>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, in case the key doesn't exist +3 +``` + +Some other methods that can be used for handling KeyErrors gracefully are the [`dict.setdefault`](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict). diff --git a/bot/resources/tags/keyerror.md b/bot/resources/tags/keyerror.md deleted file mode 100644 index d0c069004..000000000 --- a/bot/resources/tags/keyerror.md +++ /dev/null @@ -1,17 +0,0 @@ -Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary. \ -While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. -## __The `dict.get` method__ -The [dict.get](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, or None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. -```py ->>> my_dict = {"foo": 1, "bar": 2} ->>> print(my_dict.get("foo")) -1 ->>> print(my_dict.get("foobar")) -None ->>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, in case the key doesn't exist -3 ->>> print(my_dict) -{'foo': 1, 'bar': 2} # note that the new key was NOT added to the dictionary -``` -\ -Some other methods that can be used for handling KeyErrors gracefully are the [dict.setdefault](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [collections.defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict). -- cgit v1.2.3 From 721472352f05c561625932a88bcf3c10c98c130d Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:43:29 +0400 Subject: Fix faulty heading --- bot/resources/tags/dict-get.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md index b22db7af5..d9cc6a691 100644 --- a/bot/resources/tags/dict-get.md +++ b/bot/resources/tags/dict-get.md @@ -1,6 +1,8 @@ Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary.\ -While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. +While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them.\ + __**The `dict.get` method**__ + The [`dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, or None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. ```py >>> my_dict = {"foo": 1, "bar": 2} -- cgit v1.2.3 From 3a08d74e6ecc5a038ccfc97e973dac161171c6c2 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:50:45 +0400 Subject: Make `defaultdict` tag --- bot/resources/tags/defaultdict.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bot/resources/tags/defaultdict.md diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md new file mode 100644 index 000000000..a15ebff2a --- /dev/null +++ b/bot/resources/tags/defaultdict.md @@ -0,0 +1,20 @@ +**[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** + +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. + +```py +>>> from collections import defaultdict +>>> my_dict = defaultdict(int, {"foo": 1, "bar": 2}) +>>> print(my_dict) +defaultdict(, {'foo': 1, 'bar': 2}) +``` + +In this example, we've used the `int` function - this means that if we try to access a non-existent key, it provides the default value of 0. + +```py +>>> print(my_dict["foobar"]) +0 +>>> print(my_dict) +defaultdict(, {'foo': 1, 'bar': 2, 'foobar': 0}) +``` -- cgit v1.2.3 From a6d65fd04e932a8cc860f5e8ab07f05a4a2f51d1 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:53:49 +0400 Subject: Refer to `defaultdict` tag in `dict-get` --- bot/resources/tags/dict-get.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md index d9cc6a691..6f8299dc7 100644 --- a/bot/resources/tags/dict-get.md +++ b/bot/resources/tags/dict-get.md @@ -12,4 +12,4 @@ None 3 ``` -Some other methods that can be used for handling KeyErrors gracefully are the [`dict.setdefault`](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict). +Some other methods that can be used for handling KeyErrors gracefully are the [`dict.setdefault`](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) (check out the `!defaultdict` tag). -- cgit v1.2.3 From c346c2becd2967058a73bc900800afbfb8dbe6d5 Mon Sep 17 00:00:00 2001 From: Senjan21 <53477086+Senjan21@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:12:14 +0100 Subject: Fix #1371 error via adding errors. --- bot/errors.py | 17 ++++++++++++++++- bot/exts/backend/error_handler.py | 4 +++- bot/exts/moderation/infraction/_utils.py | 5 +++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/bot/errors.py b/bot/errors.py index 65d715203..016d9bd17 100644 --- a/bot/errors.py +++ b/bot/errors.py @@ -1,4 +1,5 @@ -from typing import Hashable +from typing import Hashable, Union +from discord import Member, User class LockedResourceError(RuntimeError): @@ -18,3 +19,17 @@ class LockedResourceError(RuntimeError): f"Cannot operate on {self.type.lower()} `{self.id}`; " "it is currently locked and in use by another operation." ) + + +class InvalidInfractedUser(Exception): + """ + Exception raised upon attempt of infracting an invalid user." + + Attributes: + `user` -- User or Member which is invalid + """ + + def __init__(self, user: Union[Member, User], reason: str = "User infracted is a bot."): + self.user = user + + super().__init__(reason) diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index ed7962b06..d2cce5558 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -12,7 +12,7 @@ from bot.api import ResponseCodeError from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES, Icons, MODERATION_ROLES from bot.converters import TagNameConverter -from bot.errors import LockedResourceError +from bot.errors import InvalidInfractedUser, LockedResourceError from bot.exts.backend.branding._errors import BrandingError from bot.utils.checks import InWhitelistCheckFailure @@ -82,6 +82,8 @@ class ErrorHandler(Cog): elif isinstance(e.original, BrandingError): await ctx.send(embed=self._get_error_embed(random.choice(ERROR_REPLIES), str(e.original))) return + elif isinstance(e.original, InvalidInfractedUser): + await ctx.send(f"Cannot infract that user. {e.original.reason}") else: await self.handle_unexpected_error(ctx, e.original) return # Exit early to avoid logging. diff --git a/bot/exts/moderation/infraction/_utils.py b/bot/exts/moderation/infraction/_utils.py index d0dc3f0a1..e766c1e5c 100644 --- a/bot/exts/moderation/infraction/_utils.py +++ b/bot/exts/moderation/infraction/_utils.py @@ -7,6 +7,7 @@ from discord.ext.commands import Context from bot.api import ResponseCodeError from bot.constants import Colours, Icons +from bot.errors import InvalidInfractedUser log = logging.getLogger(__name__) @@ -79,6 +80,10 @@ async def post_infraction( active: bool = True ) -> t.Optional[dict]: """Posts an infraction to the API.""" + if isinstance(user, (discord.Member, discord.User)) and user.bot: + log.trace(f"Posting of {infr_type} infraction for {user} to the API aborted. User is a bot.") + raise InvalidInfractedUser(user) + log.trace(f"Posting {infr_type} infraction for {user} to the API.") payload = { -- cgit v1.2.3 From 4f1f45652ab980502098b98e5534d64f810b4b53 Mon Sep 17 00:00:00 2001 From: Senjan21 <53477086+Senjan21@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:19:02 +0100 Subject: Linting fix. --- bot/errors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/errors.py b/bot/errors.py index 016d9bd17..a6fc33312 100644 --- a/bot/errors.py +++ b/bot/errors.py @@ -1,4 +1,5 @@ from typing import Hashable, Union + from discord import Member, User @@ -23,7 +24,7 @@ class LockedResourceError(RuntimeError): class InvalidInfractedUser(Exception): """ - Exception raised upon attempt of infracting an invalid user." + Exception raised upon attempt of infracting an invalid user. Attributes: `user` -- User or Member which is invalid -- cgit v1.2.3 From 6d9e17634ac10ce911e68d544a52aaa928298929 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 01:25:44 -0800 Subject: Reformatted string constant for available help channels. --- bot/exts/help_channels/_cog.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index e9333b9a6..fbfc585a4 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -21,9 +21,7 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = """ -**Currently available help channel(s):** {available} -""" +AVAILABLE_HELP_CHANNELS = """**Currently available help channel(s):** {available}""" class HelpChannels(commands.Cog): -- cgit v1.2.3 From bbce0a8cb17d0771a4823e67675cf4dc26f72b2a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 11:37:34 +0200 Subject: Create local-file tag about sending local files to Discord --- bot/resources/tags/local-file.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 bot/resources/tags/local-file.md diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md new file mode 100644 index 000000000..309ca4820 --- /dev/null +++ b/bot/resources/tags/local-file.md @@ -0,0 +1,24 @@ +Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of `discord.File` class: +```py +# When you know the file exact path, you can pass it. +file = discord.File("/this/is/path/to/my/file.png", filename="file.png") + +# When you have the file-like object, then you can pass this instead path. +with open("/this/is/path/to/my/file.png", "rb") as f: + file = discord.File(f) +``` +When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. +Please note that `filename` can't contain underscores. This is Discord limitation. + +`discord.Embed` instance has method `set_image` what can be used to set attachment as image: +```py +embed = discord.Embed() +# Set other fields +embed.set_image("attachment://file.png") # Filename here must be exactly same as attachment filename. +``` +After this, you can send embed and attachment to Discord: +```py +await channel.send(file=file, embed=embed) +``` +This example uses `discord.Channel` for sending, but any `discord.Messageable` can be used for sending. + -- cgit v1.2.3 From dc72923a4c74207fc405764a8e8afc1b4b239b37 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 01:52:42 -0800 Subject: Available channels are no longer stored as IDs. --- bot/exts/help_channels/_cog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index fbfc585a4..dae9b5730 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -76,7 +76,7 @@ class HelpChannels(commands.Cog): # Acquiring and modifying the channel to dynamically update the available help channels message. self.how_to_get_help: discord.TextChannel = None - self.available_help_channels: t.Set[int] = set() + self.available_help_channels: t.Set[discord.TextChannel] = set() self.dynamic_message: discord.Message = None # Asyncio stuff @@ -122,7 +122,7 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) # Removing the help channel from the dynamic message, and editing/sending that message. - self.available_help_channels.remove(message.channel.id) + self.available_help_channels.remove(message.channel) # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -352,7 +352,7 @@ class HelpChannels(commands.Cog): ) # Adding the help channel to the dynamic message, and editing/sending that message. - self.available_help_channels.add(channel.id) + self.available_help_channels.add(channel) await self.update_available_help_channels() _stats.report_counts() @@ -489,11 +489,11 @@ class HelpChannels(commands.Cog): """Updates the dynamic message within #how-to-get-help for available help channels.""" if not self.available_help_channels: self.available_help_channels = set( - c.id for c in self.available_category.channels if 'help-' in c.name + c for c in self.available_category.channels if not _channel.is_excluded_channel(c) ) available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(f"<#{c}>" for c in self.available_help_channels) or None + available=', '.join(c.mention for c in self.available_help_channels) or None ) if self.how_to_get_help is None: -- cgit v1.2.3 From 2b437bfc4858d6ed08eee43defd9a97584140706 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 02:09:20 -0800 Subject: Removed unnecessary task creation. --- bot/exts/help_channels/_cog.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index dae9b5730..2dbe930d3 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -286,13 +286,8 @@ class HelpChannels(commands.Cog): self.close_command.enabled = True # Getting channels that need to be included in the dynamic message. - task = asyncio.create_task(self.update_available_help_channels()) - self.queue_tasks.append(task) - - await task - + await self.update_available_help_channels() log.trace("Dynamic available help message updated.") - self.queue_tasks.remove(task) await self.init_available() _stats.report_counts() -- cgit v1.2.3 From 88fba5fd3489988320431b8a96879941988b5f13 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 02:21:20 -0800 Subject: Formatted available constant, added missing dynamic message trace --- bot/exts/help_channels/_cog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 2dbe930d3..554c27c95 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -21,7 +21,7 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = """**Currently available help channel(s):** {available}""" +AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}" class HelpChannels(commands.Cog): @@ -500,3 +500,4 @@ class HelpChannels(commands.Cog): await self.dynamic_message.edit(content=available_channels) except discord.NotFound: self.dynamic_message = await self.how_to_get_help.send(available_channels) + log.trace("A dynamic message was sent for later modification because one couldn't be found.") -- cgit v1.2.3 From d0c87c7f12ca20ec9be54bf0d299ca23a5e559db Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 12:54:45 +0200 Subject: discord.Channel -> discord.TextChannel --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 309ca4820..a587139ee 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,5 +20,5 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.Channel` for sending, but any `discord.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any `discord.Messageable` can be used for sending. -- cgit v1.2.3 From 44be3e8a7411a715b502802863dfc1fb2d6658c3 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 12:56:52 +0200 Subject: discord.Messageable -> discord.abc.Messageable --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index a587139ee..d78258fa2 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,5 +20,5 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any `discord.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. -- cgit v1.2.3 From 6db50230970188b4b1a24ec0b4ff84b4896cc78a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:03:35 +0200 Subject: Remove additional newline from end of tag --- bot/resources/tags/local-file.md | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index d78258fa2..9e4e0e551 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -21,4 +21,3 @@ After this, you can send embed and attachment to Discord: await channel.send(file=file, embed=embed) ``` This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. - -- cgit v1.2.3 From 1a9d820638acce176f73867b6b321c8c1dbfb479 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:38:39 +0200 Subject: Ignore attachment-only messages for duplicates antispam rule --- bot/rules/duplicates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/rules/duplicates.py b/bot/rules/duplicates.py index 455764b53..23aefd3dc 100644 --- a/bot/rules/duplicates.py +++ b/bot/rules/duplicates.py @@ -13,6 +13,7 @@ async def apply( if ( msg.author == last_message.author and msg.content == last_message.content + and (msg.content and not msg.attachments) ) ) -- cgit v1.2.3 From 84a46c9ab27f0a593c413f5ee09ba19cf5fb1d1b Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:45:07 +0200 Subject: Lower max attachments per 10 seconds to 3 --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index d3b267159..d323a946d 100644 --- a/config-default.yml +++ b/config-default.yml @@ -367,7 +367,7 @@ anti_spam: rules: attachments: interval: 10 - max: 9 + max: 3 burst: interval: 10 -- cgit v1.2.3 From e1fa3182254727c564afc86d87fc7043b2444c3c Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:56:50 +0200 Subject: Mention instance in comment about Messageable --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 9e4e0e551..c28e14a05 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,4 +20,4 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any instance of `discord.abc.Messageable` can be used for sending. -- cgit v1.2.3 From 90a9bac84cdac0288c256157f1b5769b0cd2b973 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 14:03:41 +0200 Subject: Add hyperlinks for local file tag discord.py references --- bot/resources/tags/local-file.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index c28e14a05..344f35667 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -1,4 +1,4 @@ -Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of `discord.File` class: +Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of [`discord.File`](https://discordpy.readthedocs.io/en/latest/api.html#discord.File) class: ```py # When you know the file exact path, you can pass it. file = discord.File("/this/is/path/to/my/file.png", filename="file.png") @@ -10,14 +10,14 @@ with open("/this/is/path/to/my/file.png", "rb") as f: When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. Please note that `filename` can't contain underscores. This is Discord limitation. -`discord.Embed` instance has method `set_image` what can be used to set attachment as image: +[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instance has method [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) what can be used to set attachment as image: ```py embed = discord.Embed() # Set other fields -embed.set_image("attachment://file.png") # Filename here must be exactly same as attachment filename. +embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any instance of `discord.abc.Messageable` can be used for sending. +This example uses [`discord.TextChannel`](https://discordpy.readthedocs.io/en/latest/api.html#discord.TextChannel) for sending, but any instance of [`discord.abc.Messageable`](https://discordpy.readthedocs.io/en/latest/api.html#discord.abc.Messageable) can be used for sending. -- cgit v1.2.3 From fbdfaeafb5c3381d545657a395efec07daaea092 Mon Sep 17 00:00:00 2001 From: swfarnsworth Date: Sat, 6 Feb 2021 10:30:54 -0500 Subject: Rewrite to use simpler examples. The previous examples might have been confusing for some readers. I also removed the part about inverting a dict because I think that's out of scope and would require more explanation given all the consequences that could have. --- bot/resources/tags/dictcomps.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/bot/resources/tags/dictcomps.md b/bot/resources/tags/dictcomps.md index 11867d77b..c9f9e62f7 100644 --- a/bot/resources/tags/dictcomps.md +++ b/bot/resources/tags/dictcomps.md @@ -1,20 +1,15 @@ -**Dictionary Comprehensions** - -Like lists, there is a convenient way of creating dictionaries: +Dictionary comprehensions (*dict comps*) provide a convenient way to make dictionaries, just like list comps: ```py ->>> ftoc = {f: round((5/9)*(f-32)) for f in range(-40,101,20)} ->>> print(ftoc) -{-40: -40, -20: -29, 0: -18, 20: -7, 40: 4, 60: 16, 80: 27, 100: 38} +>>> {word.lower(): len(word) for word in ('I', 'love', 'Python')} +{'i': 1, 'love': 4, 'python': 6} ``` -In the example above, I created a dictionary of temperatures in Fahrenheit, that are mapped to (*roughly*) their Celsius counterpart within a small range. These comprehensions are useful for succinctly creating dictionaries from some other sequence. +The syntax is very similar to list comps except that you surround it with curly braces and have two expressions: one for the key and one for the value. -They are also very useful for inverting the key value pairs of a dictionary that already exists, such that the value in the old dictionary is now the key, and the corresponding key is now its value: +One can use a dict comp to change an existing dictionary using its `items` method ```py ->>> ctof = {v:k for k, v in ftoc.items()} ->>> print(ctof) -{-40: -40, -29: -20, -18: 0, -7: 20, 4: 40, 16: 60, 27: 80, 38: 100} +>>> first_dict = {'i': 1, 'love': 4, 'python': 6} +>>> {key.upper(): value * 2 for key, value in first_dict.items()} +{'I': 2, 'LOVE': 8, 'PYTHON': 12} ``` -Also like list comprehensions, you can add a conditional to it in order to filter out items you don't want. - -For more information and examples, check [PEP 274](https://www.python.org/dev/peps/pep-0274/) +For more information and examples, check out [PEP 274](https://www.python.org/dev/peps/pep-0274/) -- cgit v1.2.3 From 9655acb6e4b19eec9aadb5cc1b7ed76ef55aff82 Mon Sep 17 00:00:00 2001 From: swfarnsworth Date: Sat, 6 Feb 2021 10:33:36 -0500 Subject: More robust example with no reference to Python versions or `str.format`. The example emphasizes that you can evaluate expressions in the curly braces. Python 3.5 has already reached EOL, so anyone who doesn't have f-strings at this point is probably running 2.7 anyway. I also removed the information about `str.format` to reduce the scope. --- bot/resources/tags/f-strings.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/bot/resources/tags/f-strings.md b/bot/resources/tags/f-strings.md index 69bc82487..4f12640aa 100644 --- a/bot/resources/tags/f-strings.md +++ b/bot/resources/tags/f-strings.md @@ -1,17 +1,9 @@ -In Python, there are several ways to do string interpolation, including using `%s`\'s and by using the `+` operator to concatenate strings together. However, because some of these methods offer poor readability and require typecasting to prevent errors, you should for the most part be using a feature called format strings. +Creating a Python string with your variables using the `+` operator can be difficult to write and read. F-strings (*format-strings*) make it easy to insert values into a string. If you put an `f` in front of the first quote, you can then put Python expressions between curly braces in the string. -**In Python 3.6 or later, we can use f-strings like this:** ```py -snake = "Pythons" -print(f"{snake} are some of the largest snakes in the world") -``` -**In earlier versions of Python or in projects where backwards compatibility is very important, use str.format() like this:** -```py -snake = "Pythons" - -# With str.format() you can either use indexes -print("{0} are some of the largest snakes in the world".format(snake)) - -# Or keyword arguments -print("{family} are some of the largest snakes in the world".format(family=snake)) +>>> snake = "pythons" +>>> number = 21 +>>> f"There are {number * 2} {snake} on the plane." +"There are 42 pythons on the plane." ``` +Note that even when you include an expression that isn't a string, like `number * 2`, Python will handle converting it to a string. -- cgit v1.2.3 From d333a777aff579ac9d4f38467345fb946dd46bc3 Mon Sep 17 00:00:00 2001 From: swfarnsworth Date: Sat, 6 Feb 2021 10:35:27 -0500 Subject: New example to emphasize the mapping functionality rather than filtering. Previously, the example only conveyed how the `if` statement of list comps could be used to filter a list, whereas the mapping functionality is what people primarily use list comps for. --- bot/resources/tags/listcomps.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/bot/resources/tags/listcomps.md b/bot/resources/tags/listcomps.md index 0003b9bb8..ba00a4bf7 100644 --- a/bot/resources/tags/listcomps.md +++ b/bot/resources/tags/listcomps.md @@ -1,14 +1,19 @@ -Do you ever find yourself writing something like: +Do you ever find yourself writing something like this? ```py -even_numbers = [] -for n in range(20): - if n % 2 == 0: - even_numbers.append(n) +>>> squares = [] +>>> for n in range(5): +... squares.append(n ** 2) +[0, 1, 4, 9, 16] ``` -Using list comprehensions can simplify this significantly, and greatly improve code readability. If we rewrite the example above to use list comprehensions, it would look like this: +Using list comprehensions can make this both shorter and more readable. As a list comprehension, the same code would look like this: ```py -even_numbers = [n for n in range(20) if n % 2 == 0] +>>> [n ** 2 for n in range(5)] +[0, 1, 4, 9, 16] +``` +List comprehensions also get an `if` statement: +```python +>>> [n ** 2 for n in range(5) if n % 2 == 0] +[0, 4, 16] ``` -This also works for generators, dicts and sets by using `()` or `{}` instead of `[]`. -For more info, see [this pythonforbeginners.com post](http://www.pythonforbeginners.com/basics/list-comprehensions-in-python) or [PEP 202](https://www.python.org/dev/peps/pep-0202/). +For more info, see [this pythonforbeginners.com post](http://www.pythonforbeginners.com/basics/list-comprehensions-in-python). -- cgit v1.2.3 From 8f51f239f2ded1d7176a94039d2332ef74532a95 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:07:07 +0200 Subject: Fix grammar of local-file tag --- bot/resources/tags/local-file.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 344f35667..52539c64e 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -10,13 +10,13 @@ with open("/this/is/path/to/my/file.png", "rb") as f: When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. Please note that `filename` can't contain underscores. This is Discord limitation. -[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instance has method [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) what can be used to set attachment as image: +[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py embed = discord.Embed() # Set other fields embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` -After this, you can send embed and attachment to Discord: +After this, you send an embed with an attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -- cgit v1.2.3 From 496129080733096ab7eddd03128750b9fd3a53a2 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:17:00 +0200 Subject: Add back removed 'can' to local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 52539c64e..a4aeee736 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -16,7 +16,7 @@ embed = discord.Embed() # Set other fields embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` -After this, you send an embed with an attachment to Discord: +After this, you can send an embed with an attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -- cgit v1.2.3 From 65ea1657de016a3ba1e58412950ae4bf735bf0fe Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:19:40 +0200 Subject: Add missing 'a' article in local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index a4aeee736..29fce3ff5 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -8,7 +8,7 @@ with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. -Please note that `filename` can't contain underscores. This is Discord limitation. +Please note that `filename` can't contain underscores. This is a Discord limitation.. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py -- cgit v1.2.3 From 691f2393f0fed5d17ec641d5006ea2e486015614 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:21:55 +0200 Subject: Remove unnecessary period from local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 29fce3ff5..fdce5605c 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -8,7 +8,7 @@ with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. -Please note that `filename` can't contain underscores. This is a Discord limitation.. +Please note that `filename` can't contain underscores. This is a Discord limitation. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py -- cgit v1.2.3 From 174f70e216e327e30a9df6902619944f47eea5ad Mon Sep 17 00:00:00 2001 From: Senjan21 <53477086+Senjan21@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:10:17 +0100 Subject: add reason attribute --- bot/errors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/errors.py b/bot/errors.py index a6fc33312..ab0adcd42 100644 --- a/bot/errors.py +++ b/bot/errors.py @@ -32,5 +32,6 @@ class InvalidInfractedUser(Exception): def __init__(self, user: Union[Member, User], reason: str = "User infracted is a bot."): self.user = user + self.reason = reason super().__init__(reason) -- cgit v1.2.3 From 255f2215279d08386152b12dc8adec037538cba7 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Sat, 6 Feb 2021 21:16:23 +0400 Subject: Reword comment in example --- bot/resources/tags/dict-get.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md index 6f8299dc7..7657f420a 100644 --- a/bot/resources/tags/dict-get.md +++ b/bot/resources/tags/dict-get.md @@ -1,5 +1,5 @@ Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary.\ -While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them.\ +While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. __**The `dict.get` method**__ @@ -8,7 +8,7 @@ The [`dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) metho >>> my_dict = {"foo": 1, "bar": 2} >>> print(my_dict.get("foobar")) None ->>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, in case the key doesn't exist +>>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, because the key doesn't exist 3 ``` -- cgit v1.2.3 From df22551dbf7a4dae4e374eb1dd95d9354b73474c Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Sat, 6 Feb 2021 21:41:13 +0400 Subject: Fix trailing whitespaces --- bot/resources/tags/defaultdict.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md index a15ebff2a..9361d6f2a 100644 --- a/bot/resources/tags/defaultdict.md +++ b/bot/resources/tags/defaultdict.md @@ -1,6 +1,6 @@ **[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** -The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. ```py -- cgit v1.2.3 From 5dc8f0e7e0cf150a9a89787b518fdbb7f8f2ba5c Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Sat, 6 Feb 2021 23:28:04 +0400 Subject: Correct examples, reword description --- bot/resources/tags/defaultdict.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md index 9361d6f2a..b6c3175fc 100644 --- a/bot/resources/tags/defaultdict.md +++ b/bot/resources/tags/defaultdict.md @@ -1,20 +1,21 @@ **[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** -The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically insert the key and generate a default value for it. While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. ```py >>> from collections import defaultdict ->>> my_dict = defaultdict(int, {"foo": 1, "bar": 2}) ->>> print(my_dict) -defaultdict(, {'foo': 1, 'bar': 2}) +>>> my_dict = defaultdict(int) +>>> my_dict +defaultdict(, {}) ``` -In this example, we've used the `int` function - this means that if we try to access a non-existent key, it provides the default value of 0. +In this example, we've used the `int` class which returns 0 when called like a function, so any missing key will get a default value of 0. You can also get an empty list by default with `list` or an empty string with `str`. ```py ->>> print(my_dict["foobar"]) +>>> my_dict["foo"] 0 ->>> print(my_dict) -defaultdict(, {'foo': 1, 'bar': 2, 'foobar': 0}) +>>> my_dict["bar"] += 5 +>>> my_dict +defaultdict(, {'foo': 0, 'bar': 5}) ``` -- cgit v1.2.3 From fc451e1b3c375a73b000cea21f822b7f95d900d7 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 22:04:10 +0200 Subject: Put filename between backticks --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index fdce5605c..ae41d589c 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -7,7 +7,7 @@ file = discord.File("/this/is/path/to/my/file.png", filename="file.png") with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` -When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. +When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing `filename` to it is not necessary. Please note that `filename` can't contain underscores. This is a Discord limitation. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: -- cgit v1.2.3 From 90eeeb046b392c1b770c44b766dd2ce78816b8bb Mon Sep 17 00:00:00 2001 From: Steele Farnsworth <32915757+swfarnsworth@users.noreply.github.com> Date: Sat, 6 Feb 2021 16:51:35 -0500 Subject: Removed extra blank line. It added more vertical white space than was wanted. Co-authored-by: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> --- bot/resources/tags/dictcomps.md | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/resources/tags/dictcomps.md b/bot/resources/tags/dictcomps.md index c9f9e62f7..6c8018761 100644 --- a/bot/resources/tags/dictcomps.md +++ b/bot/resources/tags/dictcomps.md @@ -11,5 +11,4 @@ One can use a dict comp to change an existing dictionary using its `items` metho >>> {key.upper(): value * 2 for key, value in first_dict.items()} {'I': 2, 'LOVE': 8, 'PYTHON': 12} ``` - For more information and examples, check out [PEP 274](https://www.python.org/dev/peps/pep-0274/) -- cgit v1.2.3 From 9a9eb8fc6f62ac8527f08cba6f72537c13522291 Mon Sep 17 00:00:00 2001 From: Steele Farnsworth <32915757+swfarnsworth@users.noreply.github.com> Date: Sat, 6 Feb 2021 16:52:36 -0500 Subject: "handle converting" -> "convert ... for you". Per Gustav's suggestion. Co-authored-by: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> --- bot/resources/tags/f-strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/f-strings.md b/bot/resources/tags/f-strings.md index 4f12640aa..5ccafe723 100644 --- a/bot/resources/tags/f-strings.md +++ b/bot/resources/tags/f-strings.md @@ -6,4 +6,4 @@ Creating a Python string with your variables using the `+` operator can be diffi >>> f"There are {number * 2} {snake} on the plane." "There are 42 pythons on the plane." ``` -Note that even when you include an expression that isn't a string, like `number * 2`, Python will handle converting it to a string. +Note that even when you include an expression that isn't a string, like `number * 2`, Python will convert it to a string for you. -- cgit v1.2.3 From 1cb760e158259264bc9cf575a609bd2b6e64d1f3 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Sun, 7 Feb 2021 15:14:54 +0300 Subject: Revert "Dynamic available help channels message" --- bot/constants.py | 1 - bot/exts/help_channels/_cog.py | 40 ---------------------------------------- config-default.yml | 1 - 3 files changed, 42 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 6b86d33a3..95e22513f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -406,7 +406,6 @@ class Channels(metaclass=YAMLGetter): meta: int python_general: int - how_to_get_help: int cooldown: int diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 554c27c95..0995c8a79 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,6 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -21,7 +20,6 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}" class HelpChannels(commands.Cog): @@ -74,11 +72,6 @@ class HelpChannels(commands.Cog): self.last_notification: t.Optional[datetime] = None - # Acquiring and modifying the channel to dynamically update the available help channels message. - self.how_to_get_help: discord.TextChannel = None - self.available_help_channels: t.Set[discord.TextChannel] = set() - self.dynamic_message: discord.Message = None - # Asyncio stuff self.queue_tasks: t.List[asyncio.Task] = [] self.init_task = self.bot.loop.create_task(self.init_cog()) @@ -121,9 +114,6 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - # Removing the help channel from the dynamic message, and editing/sending that message. - self.available_help_channels.remove(message.channel) - # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -285,10 +275,6 @@ class HelpChannels(commands.Cog): # This may confuse users. So would potentially long delays for the cog to become ready. self.close_command.enabled = True - # Getting channels that need to be included in the dynamic message. - await self.update_available_help_channels() - log.trace("Dynamic available help message updated.") - await self.init_available() _stats.report_counts() @@ -346,10 +332,6 @@ class HelpChannels(commands.Cog): category_id=constants.Categories.help_available, ) - # Adding the help channel to the dynamic message, and editing/sending that message. - self.available_help_channels.add(channel) - await self.update_available_help_channels() - _stats.report_counts() async def move_to_dormant(self, channel: discord.TextChannel) -> None: @@ -479,25 +461,3 @@ class HelpChannels(commands.Cog): self.queue_tasks.remove(task) return channel - - async def update_available_help_channels(self) -> None: - """Updates the dynamic message within #how-to-get-help for available help channels.""" - if not self.available_help_channels: - self.available_help_channels = set( - c for c in self.available_category.channels if not _channel.is_excluded_channel(c) - ) - - available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(c.mention for c in self.available_help_channels) or None - ) - - if self.how_to_get_help is None: - self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - - try: - if self.dynamic_message is None: - self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) - await self.dynamic_message.edit(content=available_channels) - except discord.NotFound: - self.dynamic_message = await self.how_to_get_help.send(available_channels) - log.trace("A dynamic message was sent for later modification because one couldn't be found.") diff --git a/config-default.yml b/config-default.yml index fc1f3b3a8..d3b267159 100644 --- a/config-default.yml +++ b/config-default.yml @@ -159,7 +159,6 @@ guild: # Python Help: Available cooldown: 720603994149486673 - how_to_get_help: 704250143020417084 # Topical discord_py: 343944376055103488 -- cgit v1.2.3 From 160bf89303436e3ba0ff566241a206a120a25d66 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Tue, 9 Feb 2021 13:37:28 +0300 Subject: Moves Off Topic Name Translator Breaks out the off topic name translation functionality into its own function. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/converters.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/bot/converters.py b/bot/converters.py index 0d9a519df..80ce99459 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -357,27 +357,38 @@ class Duration(DurationDelta): class OffTopicName(Converter): """A converter that ensures an added off-topic name is valid.""" + ALLOWED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!?'`-" + + @classmethod + def translate_name(cls, name: str, *, from_unicode: bool = True) -> str: + """ + Translates `name` into a format that is allowed in discord channel names. + + If `from_unicode` is True, the name is translated from a discord-safe format, back to normalized text. + """ + if from_unicode: + table = str.maketrans(cls.ALLOWED_CHARACTERS, '๐– ๐–ก๐–ข๐–ฃ๐–ค๐–ฅ๐–ฆ๐–ง๐–จ๐–ฉ๐–ช๐–ซ๐–ฌ๐–ญ๐–ฎ๐–ฏ๐–ฐ๐–ฑ๐–ฒ๐–ณ๐–ด๐–ต๐–ถ๐–ท๐–ธ๐–นวƒ๏ผŸโ€™โ€™-') + else: + table = str.maketrans('๐– ๐–ก๐–ข๐–ฃ๐–ค๐–ฅ๐–ฆ๐–ง๐–จ๐–ฉ๐–ช๐–ซ๐–ฌ๐–ญ๐–ฎ๐–ฏ๐–ฐ๐–ฑ๐–ฒ๐–ณ๐–ด๐–ต๐–ถ๐–ท๐–ธ๐–นวƒ๏ผŸโ€™โ€™-', cls.ALLOWED_CHARACTERS) + + return name.translate(table) + async def convert(self, ctx: Context, argument: str) -> str: """Attempt to replace any invalid characters with their approximate Unicode equivalent.""" - allowed_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!?'`-" - # Chain multiple words to a single one argument = "-".join(argument.split()) if not (2 <= len(argument) <= 96): raise BadArgument("Channel name must be between 2 and 96 chars long") - elif not all(c.isalnum() or c in allowed_characters for c in argument): + elif not all(c.isalnum() or c in self.ALLOWED_CHARACTERS for c in argument): raise BadArgument( "Channel name must only consist of " "alphanumeric characters, minus signs or apostrophes." ) # Replace invalid characters with unicode alternatives. - table = str.maketrans( - allowed_characters, '๐– ๐–ก๐–ข๐–ฃ๐–ค๐–ฅ๐–ฆ๐–ง๐–จ๐–ฉ๐–ช๐–ซ๐–ฌ๐–ญ๐–ฎ๐–ฏ๐–ฐ๐–ฑ๐–ฒ๐–ณ๐–ด๐–ต๐–ถ๐–ท๐–ธ๐–นวƒ๏ผŸโ€™โ€™-' - ) - return argument.translate(table) + return self.translate_name(argument) class ISODateTime(Converter): -- cgit v1.2.3 From 66cda4fd2a0b26e2f9e983f1597a15bfb9527143 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Tue, 9 Feb 2021 13:38:12 +0300 Subject: Makes Off Topic Name Search Case Insensitive Modifies the off topic channel name search to match upper and lower cased letters, as well as punctuation. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/exts/fun/off_topic_names.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bot/exts/fun/off_topic_names.py b/bot/exts/fun/off_topic_names.py index 7fc93b88c..845b8175c 100644 --- a/bot/exts/fun/off_topic_names.py +++ b/bot/exts/fun/off_topic_names.py @@ -139,10 +139,20 @@ class OffTopicNames(Cog): @has_any_role(*MODERATION_ROLES) async def search_command(self, ctx: Context, *, query: OffTopicName) -> None: """Search for an off-topic name.""" - result = await self.bot.api_client.get('bot/off-topic-channel-names') - in_matches = {name for name in result if query in name} - close_matches = difflib.get_close_matches(query, result, n=10, cutoff=0.70) - lines = sorted(f"โ€ข {name}" for name in in_matches.union(close_matches)) + query = OffTopicName.translate_name(query, from_unicode=False).lower() + + # Map normalized names to returned names for search purposes + result = { + OffTopicName.translate_name(name, from_unicode=False).lower(): name + for name in await self.bot.api_client.get('bot/off-topic-channel-names') + } + + # Search normalized keys + in_matches = {name for name in result.keys() if query in name} + close_matches = difflib.get_close_matches(query, result.keys(), n=10, cutoff=0.70) + + # Send Results + lines = sorted(f"โ€ข {result[name]}" for name in in_matches.union(close_matches)) embed = Embed( title="Query results", colour=Colour.blue() -- cgit v1.2.3 From 9d8162a688023a3b5e830057b09c2ab2e132582f Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Feb 2021 01:07:43 +0000 Subject: Migrate API utilities to use internal DNS routing --- bot/api.py | 2 +- bot/constants.py | 1 + config-default.yml | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/api.py b/bot/api.py index d93f9f2ba..6ce9481f4 100644 --- a/bot/api.py +++ b/bot/api.py @@ -53,7 +53,7 @@ class APIClient: @staticmethod def _url_for(endpoint: str) -> str: - return f"{URLs.site_schema}{URLs.site_api}/{quote_url(endpoint)}" + return f"{URLs.site_api_schema}{URLs.site_api}/{quote_url(endpoint)}" async def close(self) -> None: """Close the aiohttp session.""" diff --git a/bot/constants.py b/bot/constants.py index 95e22513f..91e41e334 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -530,6 +530,7 @@ class URLs(metaclass=YAMLGetter): site: str site_api: str site_schema: str + site_api_schema: str # Site endpoints site_logs_view: str diff --git a/config-default.yml b/config-default.yml index d3b267159..c585151c9 100644 --- a/config-default.yml +++ b/config-default.yml @@ -335,9 +335,10 @@ keys: urls: # PyDis site vars site: &DOMAIN "pythondiscord.com" - site_api: &API !JOIN ["api.", *DOMAIN] + site_api: &API "pydis-api.default.svc.cluster.local" site_paste: &PASTE !JOIN ["paste.", *DOMAIN] site_schema: &SCHEMA "https://" + site_api_schema: "http://" site_staff: &STAFF !JOIN ["staff.", *DOMAIN] paste_service: !JOIN [*SCHEMA, *PASTE, "/{key}"] -- cgit v1.2.3 From 578a0e48514fd9f902cde45db557fa1f3425c289 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Feb 2021 01:07:54 +0000 Subject: Migrate ping command to ping internal API --- bot/exts/utils/ping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/ping.py b/bot/exts/utils/ping.py index 572fc934b..e62811b91 100644 --- a/bot/exts/utils/ping.py +++ b/bot/exts/utils/ping.py @@ -37,7 +37,7 @@ class Latency(commands.Cog): bot_ping = f"{bot_ping:.{ROUND_LATENCY}f} ms" try: - delay = await aioping.ping(URLs.site, family=socket.AddressFamily.AF_INET) * 1000 + delay = await aioping.ping(URLs.site_api, family=socket.AddressFamily.AF_INET) * 1000 site_ping = f"{delay:.{ROUND_LATENCY}f} ms" except TimeoutError: -- cgit v1.2.3 From 9111f9f247fb1292add29929c660e9633e4f31da Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Feb 2021 01:57:00 +0000 Subject: Alphabetical sorting in config-default.yml --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index c585151c9..d7415c821 100644 --- a/config-default.yml +++ b/config-default.yml @@ -336,9 +336,9 @@ urls: # PyDis site vars site: &DOMAIN "pythondiscord.com" site_api: &API "pydis-api.default.svc.cluster.local" + site_api_schema: "http://" site_paste: &PASTE !JOIN ["paste.", *DOMAIN] site_schema: &SCHEMA "https://" - site_api_schema: "http://" site_staff: &STAFF !JOIN ["staff.", *DOMAIN] paste_service: !JOIN [*SCHEMA, *PASTE, "/{key}"] -- cgit v1.2.3 From bafa6a9dbf61ae30ef235537408f0b073a88dd19 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Feb 2021 02:13:42 +0000 Subject: ICMP is disabled in production, so we can't ping the API --- bot/exts/utils/ping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/ping.py b/bot/exts/utils/ping.py index e62811b91..572fc934b 100644 --- a/bot/exts/utils/ping.py +++ b/bot/exts/utils/ping.py @@ -37,7 +37,7 @@ class Latency(commands.Cog): bot_ping = f"{bot_ping:.{ROUND_LATENCY}f} ms" try: - delay = await aioping.ping(URLs.site_api, family=socket.AddressFamily.AF_INET) * 1000 + delay = await aioping.ping(URLs.site, family=socket.AddressFamily.AF_INET) * 1000 site_ping = f"{delay:.{ROUND_LATENCY}f} ms" except TimeoutError: -- cgit v1.2.3 From ea35aa9c77a81f46ea14acf36862c42f3ffe9016 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Thu, 11 Feb 2021 09:37:33 +0400 Subject: Split example codeblock in two --- bot/resources/tags/dict-get.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md index 7657f420a..867f0b7d9 100644 --- a/bot/resources/tags/dict-get.md +++ b/bot/resources/tags/dict-get.md @@ -1,15 +1,17 @@ Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary.\ While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. -__**The `dict.get` method**__ +**The `dict.get` method** -The [`dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, or None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. +The [`dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get) method will return the value for the key if it exists, and None (or a default value that you specify) if the key doesn't exist. Hence it will _never raise_ a KeyError. ```py >>> my_dict = {"foo": 1, "bar": 2} >>> print(my_dict.get("foobar")) None ->>> print(my_dict.get("foobar", 3)) # here 3 is the default value to be returned, because the key doesn't exist +``` +Below, 3 is the default value to be returned, because the key doesn't exist- +```py +>>> print(my_dict.get("foobar", 3)) 3 ``` - -Some other methods that can be used for handling KeyErrors gracefully are the [`dict.setdefault`](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method, or by using [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) (check out the `!defaultdict` tag). +Some other methods for handling `KeyError`s gracefully are the [`dict.setdefault`](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method and [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) (check out the `!defaultdict` tag). -- cgit v1.2.3 From a3749786c3ff90397427032ef219b590ee4e2837 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Thu, 11 Feb 2021 10:20:06 +0400 Subject: Remove reference to `try - except` --- bot/resources/tags/dict-get.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/resources/tags/dict-get.md b/bot/resources/tags/dict-get.md index 867f0b7d9..e02df03ab 100644 --- a/bot/resources/tags/dict-get.md +++ b/bot/resources/tags/dict-get.md @@ -1,5 +1,4 @@ -Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary.\ -While you can use a `try` and `except` block to catch the `KeyError`, Python also gives you some other neat ways to handle them. +Often while using dictionaries in Python, you may run into `KeyErrors`. This error is raised when you try to access a key that isn't present in your dictionary. Python gives you some neat ways to handle them. **The `dict.get` method** -- cgit v1.2.3 From 84c0aa4268f91027cd71016e01a00ffe59151cc2 Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 11 Feb 2021 02:40:42 -0800 Subject: Added base of the pypi command. --- bot/exts/info/pypi.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 bot/exts/info/pypi.py diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py new file mode 100644 index 000000000..9567516c2 --- /dev/null +++ b/bot/exts/info/pypi.py @@ -0,0 +1,35 @@ +from discord import Embed +from discord.ext.commands import Cog, Context, command + +from bot.bot import Bot +from bot.constants import NEGATIVE_REPLIES + +URL = "https://pypi.org/pypi/{package}/json" + + +class PyPi(Cog): + """Cog for getting information about PyPi packages.""" + + def __init__(self, bot: Bot): + self.bot = bot + + @command(name="pypi", aliases=("package", "pack")) + async def get_package_info(self, ctx: Context, package: str) -> None: + """Getting information about a specific package.""" + embed = Embed(title="PyPi package information") + + async with self.bot.http_session.get(URL.format(package_name=package)) as response: + if response.status == 404: + return await ctx.send(f"Package with name '{package}' could not be found.") + elif response.status == 200 and response.content_type == "application/json": + response_json = await response.json() + info = response_json["info"] + else: + return await ctx.send("There was an error when fetching your PyPi package.") + + await ctx.send(embed=embed) + + +def setup(bot: Bot) -> None: + """Load the PyPi cog.""" + bot.add_cog(PyPi(bot)) -- cgit v1.2.3 From 1610f330fbc583df2c161629b7d8d72b77b9253d Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 11 Feb 2021 03:49:59 -0800 Subject: Added more fields and responses. --- bot/exts/info/pypi.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 9567516c2..e4c90090d 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -1,10 +1,16 @@ +import logging +from random import choice + from discord import Embed from discord.ext.commands import Cog, Context, command from bot.bot import Bot -from bot.constants import NEGATIVE_REPLIES +from bot.constants import NEGATIVE_REPLIES, Colours URL = "https://pypi.org/pypi/{package}/json" +FIELDS = ["author", "requires_python", "description", "license"] + +log = logging.getLogger(__name__) class PyPi(Cog): @@ -16,16 +22,30 @@ class PyPi(Cog): @command(name="pypi", aliases=("package", "pack")) async def get_package_info(self, ctx: Context, package: str) -> None: """Getting information about a specific package.""" - embed = Embed(title="PyPi package information") + embed = Embed(title=choice(NEGATIVE_REPLIES), colour=Colours.soft_red) - async with self.bot.http_session.get(URL.format(package_name=package)) as response: + async with self.bot.http_session.get(URL.format(package=package)) as response: if response.status == 404: - return await ctx.send(f"Package with name '{package}' could not be found.") + embed.description = f"Package could not be found." + elif response.status == 200 and response.content_type == "application/json": response_json = await response.json() info = response_json["info"] + + embed.title = "Python Package Index" + embed.colour = Colours.soft_green + embed.description = f"[{info['name']} v{info['version']}]({info['download_url']})\n" + + for field in FIELDS: + embed.add_field( + name=field.replace("_", " ").title(), + value=info[field], + inline=False, + ) + else: - return await ctx.send("There was an error when fetching your PyPi package.") + embed.description = "There was an error when fetching your PyPi package." + log.trace(f"Error when fetching PyPi package: {response.status}.") await ctx.send(embed=embed) -- cgit v1.2.3 From ded34bc8ab063064fbd50199d07bbeec1db884ad Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 11 Feb 2021 03:54:40 -0800 Subject: Made flake8 very happy. --- bot/exts/info/pypi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index e4c90090d..544b52b49 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -5,7 +5,7 @@ from discord import Embed from discord.ext.commands import Cog, Context, command from bot.bot import Bot -from bot.constants import NEGATIVE_REPLIES, Colours +from bot.constants import Colours, NEGATIVE_REPLIES URL = "https://pypi.org/pypi/{package}/json" FIELDS = ["author", "requires_python", "description", "license"] @@ -26,7 +26,7 @@ class PyPi(Cog): async with self.bot.http_session.get(URL.format(package=package)) as response: if response.status == 404: - embed.description = f"Package could not be found." + embed.description = "Package could not be found." elif response.status == 200 and response.content_type == "application/json": response_json = await response.json() -- cgit v1.2.3 From ed7fde738db677ced25388a53ed9bd539f4490fb Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 00:14:48 -0800 Subject: Empty fields have been accounted for by getting usually non-empty ones. --- bot/exts/info/pypi.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 544b52b49..7a5d7f4b7 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -8,7 +8,7 @@ from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES URL = "https://pypi.org/pypi/{package}/json" -FIELDS = ["author", "requires_python", "description", "license"] +FIELDS = ["author", "requires_python", "summary", "license"] log = logging.getLogger(__name__) @@ -34,14 +34,15 @@ class PyPi(Cog): embed.title = "Python Package Index" embed.colour = Colours.soft_green - embed.description = f"[{info['name']} v{info['version']}]({info['download_url']})\n" + embed.description = f"[{info['name']} v{info['version']}]({info['package_url']})\n" for field in FIELDS: - embed.add_field( - name=field.replace("_", " ").title(), - value=info[field], - inline=False, - ) + if field_value := info[field]: + embed.add_field( + name=field.replace("_", " ").title(), + value=field_value, + inline=False, + ) else: embed.description = "There was an error when fetching your PyPi package." -- cgit v1.2.3 From 2a9f349429694d48cca86af972ef327a57af552d Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 00:20:51 -0800 Subject: Accounting for completely empty fields that only contain whitespaces. --- bot/exts/info/pypi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 7a5d7f4b7..990a5c905 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -37,7 +37,8 @@ class PyPi(Cog): embed.description = f"[{info['name']} v{info['version']}]({info['package_url']})\n" for field in FIELDS: - if field_value := info[field]: + # Field could be completely empty, in some cases can be a string with whitespaces. + if field_value := info[field].strip(): embed.add_field( name=field.replace("_", " ").title(), value=field_value, -- cgit v1.2.3 From 889de9b678a044331f02eef647c7d1c963f37edd Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 00:49:27 -0800 Subject: Finalized logic to account for null cases. --- bot/exts/info/pypi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 990a5c905..4ad72b673 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -37,11 +37,11 @@ class PyPi(Cog): embed.description = f"[{info['name']} v{info['version']}]({info['package_url']})\n" for field in FIELDS: - # Field could be completely empty, in some cases can be a string with whitespaces. - if field_value := info[field].strip(): + # Field could be completely empty, in some cases can be a string with whitespaces, or None. + if info[field] and not info[field].isspace(): embed.add_field( name=field.replace("_", " ").title(), - value=field_value, + value=info[field], inline=False, ) -- cgit v1.2.3 From bcab6614bba3ca71edeb134089846570e0e47547 Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 01:22:12 -0800 Subject: Moved hyperlink to title. --- bot/exts/info/pypi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 4ad72b673..c7ec22fc6 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -32,9 +32,9 @@ class PyPi(Cog): response_json = await response.json() info = response_json["info"] - embed.title = "Python Package Index" + embed.title = f"{info['name']} v{info['version']}" + embed.url = info['package_url'] embed.colour = Colours.soft_green - embed.description = f"[{info['name']} v{info['version']}]({info['package_url']})\n" for field in FIELDS: # Field could be completely empty, in some cases can be a string with whitespaces, or None. -- cgit v1.2.3 From 94ad4dd207226d7d1a2b080ffe47352e7c2b9e73 Mon Sep 17 00:00:00 2001 From: Xithrius <15021300+Xithrius@users.noreply.github.com> Date: Fri, 12 Feb 2021 02:09:17 -0800 Subject: Made docstring more specific. Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/info/pypi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index c7ec22fc6..79931c665 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -21,7 +21,7 @@ class PyPi(Cog): @command(name="pypi", aliases=("package", "pack")) async def get_package_info(self, ctx: Context, package: str) -> None: - """Getting information about a specific package.""" + """Provide information about a specific package from PyPI.""" embed = Embed(title=choice(NEGATIVE_REPLIES), colour=Colours.soft_red) async with self.bot.http_session.get(URL.format(package=package)) as response: -- cgit v1.2.3 From 9ce9ab617ba0fdacb1922e2ed2007ed05e53c526 Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 13:10:25 -0800 Subject: Added colours yellow, blue, and white. --- bot/constants.py | 9 ++++++--- config-default.yml | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 91e41e334..8a93ff9cf 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -246,13 +246,16 @@ class Colours(metaclass=YAMLGetter): section = "style" subsection = "colours" + blue: int bright_green: int - soft_green: int - soft_orange: int - soft_red: int orange: int pink: int purple: int + soft_green: int + soft_orange: int + soft_red: int + white: int + yellow: int class DuckPond(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index d7415c821..25bbcc3c5 100644 --- a/config-default.yml +++ b/config-default.yml @@ -24,13 +24,16 @@ bot: style: colours: + blue: 0x3775a8 bright_green: 0x01d277 - soft_green: 0x68c290 - soft_orange: 0xf9cb54 - soft_red: 0xcd6d6d orange: 0xe67e22 pink: 0xcf84e0 purple: 0xb734eb + soft_green: 0x68c290 + soft_orange: 0xf9cb54 + soft_red: 0xcd6d6d + white: 0xfffffe + yellow: 0xffd241 emojis: badge_bug_hunter: "<:bug_hunter_lvl1:743882896372269137>" -- cgit v1.2.3 From aa0b60534d1b8cef2e34bbaf50709553c71a14ff Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 13:12:24 -0800 Subject: Rotating colours in embed, title now links to package. --- bot/exts/info/pypi.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index c7ec22fc6..c7d4d321c 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -1,5 +1,6 @@ +import itertools import logging -from random import choice +import random from discord import Embed from discord.ext.commands import Cog, Context, command @@ -8,7 +9,9 @@ from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES URL = "https://pypi.org/pypi/{package}/json" -FIELDS = ["author", "requires_python", "summary", "license"] +FIELDS = ("author", "requires_python", "summary", "license") +PYPI_ICON = "https://cdn.discordapp.com/emojis/766274397257334814.png" +PYPI_COLOURS = itertools.cycle((Colours.yellow, Colours.blue, Colours.white)) log = logging.getLogger(__name__) @@ -21,8 +24,12 @@ class PyPi(Cog): @command(name="pypi", aliases=("package", "pack")) async def get_package_info(self, ctx: Context, package: str) -> None: - """Getting information about a specific package.""" - embed = Embed(title=choice(NEGATIVE_REPLIES), colour=Colours.soft_red) + """Provide information about a specific package from PyPI.""" + embed = Embed( + title=random.choice(NEGATIVE_REPLIES), + colour=Colours.soft_red + ) + embed.set_thumbnail(url=PYPI_ICON) async with self.bot.http_session.get(URL.format(package=package)) as response: if response.status == 404: @@ -34,7 +41,7 @@ class PyPi(Cog): embed.title = f"{info['name']} v{info['version']}" embed.url = info['package_url'] - embed.colour = Colours.soft_green + embed.colour = next(PYPI_COLOURS) for field in FIELDS: # Field could be completely empty, in some cases can be a string with whitespaces, or None. -- cgit v1.2.3 From 059940b5ae3cc2921303579ebf161835fe09076d Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 12 Feb 2021 15:47:06 -0800 Subject: Taking only the first line of multiline fields. --- bot/exts/info/pypi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index c7d4d321c..cf45b068f 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -44,11 +44,16 @@ class PyPi(Cog): embed.colour = next(PYPI_COLOURS) for field in FIELDS: + field_data = info[field] + # Field could be completely empty, in some cases can be a string with whitespaces, or None. - if info[field] and not info[field].isspace(): + if field_data and not field_data.isspace(): + if '\n' in field_data and field == "license": + field_data = field_data.split('\n')[0] + embed.add_field( name=field.replace("_", " ").title(), - value=info[field], + value=field_data, inline=False, ) -- cgit v1.2.3 From 8fcb4c6ee7718143c949aa41627064635b2b364b Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 13 Feb 2021 08:24:04 +0200 Subject: Move Git SHA defining at end of Dockerfile to re-enable caching Defining SHA at the beginning of build breaks caching, so this should be avoided. --- Dockerfile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d0380b44..994b8ee49 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,10 @@ FROM python:3.8-slim -# Define Git SHA build argument -ARG git_sha="development" - # Set pip to have cleaner logs and no saved cache ENV PIP_NO_CACHE_DIR=false \ PIPENV_HIDE_EMOJIS=1 \ PIPENV_IGNORE_VIRTUALENVS=1 \ - PIPENV_NOSPIN=1 \ - GIT_SHA=$git_sha + PIPENV_NOSPIN=1 RUN apt-get -y update \ && apt-get install -y \ @@ -25,6 +21,12 @@ WORKDIR /bot COPY Pipfile* ./ RUN pipenv install --system --deploy +# Define Git SHA build argument +ARG git_sha="development" + +# Set Git SHA environment variable here to enable caching +ENV GIT_SHA=$git_sha + # Copy the source code in last to optimize rebuilding the image COPY . . -- cgit v1.2.3 From 88b5b32696c876ee0aa5299eb78bb0d775c5b800 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 13 Feb 2021 00:25:09 -0800 Subject: Escaping markdown in all fields that are created. --- bot/exts/info/pypi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index cf45b068f..73ec31870 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -7,6 +7,7 @@ from discord.ext.commands import Cog, Context, command from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES +from discord.utils import escape_markdown URL = "https://pypi.org/pypi/{package}/json" FIELDS = ("author", "requires_python", "summary", "license") @@ -53,7 +54,7 @@ class PyPi(Cog): embed.add_field( name=field.replace("_", " ").title(), - value=field_data, + value=escape_markdown(field_data), inline=False, ) -- cgit v1.2.3 From f14c391e3a1228953cda29be7993c9a5ec51ca6f Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 13 Feb 2021 00:34:33 -0800 Subject: Made flake8 even happier. --- bot/exts/info/pypi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py index 73ec31870..3e326e8bb 100644 --- a/bot/exts/info/pypi.py +++ b/bot/exts/info/pypi.py @@ -4,10 +4,10 @@ import random from discord import Embed from discord.ext.commands import Cog, Context, command +from discord.utils import escape_markdown from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES -from discord.utils import escape_markdown URL = "https://pypi.org/pypi/{package}/json" FIELDS = ("author", "requires_python", "summary", "license") -- cgit v1.2.3 From 55f7e7085fe821b4af7e59e811148808a3a40738 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sun, 14 Feb 2021 22:06:44 +0000 Subject: Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 994b8ee49..1a75e5669 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN pipenv install --system --deploy # Define Git SHA build argument ARG git_sha="development" -# Set Git SHA environment variable here to enable caching +# Set Git SHA environment variable for Sentry ENV GIT_SHA=$git_sha # Copy the source code in last to optimize rebuilding the image -- cgit v1.2.3 From aa5e39c3866a9100fda242221106bf6d2caae38c Mon Sep 17 00:00:00 2001 From: Senjan21 <53477086+Senjan21@users.noreply.github.com> Date: Fri, 19 Feb 2021 10:07:35 +0100 Subject: Delete free.md Reasoning behind this is it is rarely used and when its used its often just to test something or as an attempt to close a help channel. --- bot/resources/tags/free.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 bot/resources/tags/free.md diff --git a/bot/resources/tags/free.md b/bot/resources/tags/free.md deleted file mode 100644 index 1493076c7..000000000 --- a/bot/resources/tags/free.md +++ /dev/null @@ -1,5 +0,0 @@ -**We have a new help channel system!** - -Please see <#704250143020417084> for further information. - -A more detailed guide can be found on [our website](https://pythondiscord.com/pages/resources/guides/help-channels/). -- cgit v1.2.3 From 0f4365e2430d40f17ab9a545d3e8614a4b3a9669 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 20 Feb 2021 11:54:52 +0200 Subject: Remove attachments check in duplicates filter --- bot/rules/duplicates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/rules/duplicates.py b/bot/rules/duplicates.py index 23aefd3dc..8e4fbc12d 100644 --- a/bot/rules/duplicates.py +++ b/bot/rules/duplicates.py @@ -13,7 +13,7 @@ async def apply( if ( msg.author == last_message.author and msg.content == last_message.content - and (msg.content and not msg.attachments) + and msg.content ) ) -- cgit v1.2.3 From 7f980be37a572f1998160ce6a2221504e414d285 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Sat, 20 Feb 2021 11:55:09 +0200 Subject: Update CODEOWNERS --- .github/CODEOWNERS | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ad813d893..7217cb443 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,11 +7,15 @@ bot/exts/utils/extensions.py @MarkKoz bot/exts/utils/snekbox.py @MarkKoz @Akarys42 bot/exts/help_channels/** @MarkKoz @Akarys42 bot/exts/moderation/** @Akarys42 @mbaruh @Den4200 @ks129 -bot/exts/info/** @Akarys42 @mbaruh @Den4200 +bot/exts/info/** @Akarys42 @Den4200 +bot/exts/info/information.py @mbaruh bot/exts/filters/** @mbaruh bot/exts/fun/** @ks129 bot/exts/utils/** @ks129 +# Rules +bot/rules/** @mbaruh + # Utils bot/utils/extensions.py @MarkKoz bot/utils/function.py @MarkKoz -- cgit v1.2.3 From e3b980e53c13fd5dcaf51408f97c99b629c1a6ec Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 20 Feb 2021 11:55:26 +0200 Subject: Set max attachment from 3 -> 6 --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index d323a946d..e9dce7845 100644 --- a/config-default.yml +++ b/config-default.yml @@ -367,7 +367,7 @@ anti_spam: rules: attachments: interval: 10 - max: 3 + max: 6 burst: interval: 10 -- cgit v1.2.3 From 93c3327414dabd12236e47210be2be1151b71719 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 21 Feb 2021 13:50:37 +0100 Subject: Show the last three characters of censored tokens --- bot/exts/filters/token_remover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/filters/token_remover.py b/bot/exts/filters/token_remover.py index bd6a1f97a..33b39cc2d 100644 --- a/bot/exts/filters/token_remover.py +++ b/bot/exts/filters/token_remover.py @@ -147,7 +147,7 @@ class TokenRemover(Cog): channel=msg.channel.mention, user_id=token.user_id, timestamp=token.timestamp, - hmac='x' * len(token.hmac), + hmac='x' * (len(token.hmac) - 3) + token.hmac[-3:], ) @classmethod -- cgit v1.2.3 From 04e233685e163d1e513a21acd236c2385536b0b7 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 21 Feb 2021 13:51:57 +0100 Subject: Ping the mods if a token present in the server is found no matter the kind --- bot/exts/filters/token_remover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/filters/token_remover.py b/bot/exts/filters/token_remover.py index 33b39cc2d..93f1f3c33 100644 --- a/bot/exts/filters/token_remover.py +++ b/bot/exts/filters/token_remover.py @@ -135,7 +135,7 @@ class TokenRemover(Cog): user_id=user_id, user_name=str(user), kind="BOT" if user.bot else "USER", - ), not user.bot + ), True else: return UNKNOWN_USER_LOG_MESSAGE.format(user_id=user_id), False -- cgit v1.2.3 From 27e60e94bd1fe6784c2b7674433bb175255fa217 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 21 Feb 2021 14:06:54 +0100 Subject: Update token remover unittests --- tests/bot/exts/filters/test_token_remover.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bot/exts/filters/test_token_remover.py b/tests/bot/exts/filters/test_token_remover.py index f99cc3370..51feae9cb 100644 --- a/tests/bot/exts/filters/test_token_remover.py +++ b/tests/bot/exts/filters/test_token_remover.py @@ -291,7 +291,7 @@ class TokenRemoverTests(unittest.IsolatedAsyncioTestCase): channel=self.msg.channel.mention, user_id=token.user_id, timestamp=token.timestamp, - hmac="x" * len(token.hmac), + hmac="xxxxxxxxxxxxxxxxxxxxxxxxjf4", ) @autospec("bot.exts.filters.token_remover", "UNKNOWN_USER_LOG_MESSAGE") @@ -318,7 +318,7 @@ class TokenRemoverTests(unittest.IsolatedAsyncioTestCase): return_value = TokenRemover.format_userid_log_message(msg, token) - self.assertEqual(return_value, (known_user_log_message.format.return_value, False)) + self.assertEqual(return_value, (known_user_log_message.format.return_value, True)) known_user_log_message.format.assert_called_once_with( user_id=472265943062413332, -- cgit v1.2.3 From 584ed52c7107c7d3e3b838ee1e8df3a22ae95e35 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sun, 21 Feb 2021 23:07:38 +0000 Subject: Update max available channels to 3 Partially resolves #1427 --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index beaf89f2c..8e9a29a51 100644 --- a/config-default.yml +++ b/config-default.yml @@ -470,7 +470,7 @@ help_channels: deleted_idle_minutes: 5 # Maximum number of channels to put in the available category - max_available: 2 + max_available: 3 # Maximum number of channels across all 3 categories # Note Discord has a hard limit of 50 channels per category, so this shouldn't be > 50 -- cgit v1.2.3 From 1daf01ef9a3853252d4cadab5fc6abce14df3557 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Mon, 22 Feb 2021 11:35:12 +0000 Subject: Rewrite inline codeblock tag --- bot/resources/tags/inline.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/bot/resources/tags/inline.md b/bot/resources/tags/inline.md index a6a7c35d6..4ece74ef7 100644 --- a/bot/resources/tags/inline.md +++ b/bot/resources/tags/inline.md @@ -1,16 +1,7 @@ **Inline codeblocks** -In addition to multi-line codeblocks, discord has support for inline codeblocks as well. These are small codeblocks that are usually a single line, that can fit between non-codeblocks on the same line. +Inline codeblocks look `like this`. To create them you surround text with single backticks, so \`hello\` would become `hello`. -The following is an example of how it's done: +Note that backticks are not quotes, see [this](https://superuser.com/questions/254076/how-do-i-type-the-tick-and-backtick-characters-on-windows/254077#254077) if you are struggling to find the backtick key. -The \`\_\_init\_\_\` method customizes the newly created instance. - -And results in the following: - -The `__init__` method customizes the newly created instance. - -**Note:** -โ€ข These are **backticks** not quotes -โ€ข Avoid using them for multiple lines -โ€ข Useful for negating formatting you don't want +For how to make multiline codeblocks see the `!codeblock` tag. -- cgit v1.2.3 From b116688be7d8b3d83c88a78969e2118e0504fadc Mon Sep 17 00:00:00 2001 From: wookie184 Date: Mon, 22 Feb 2021 11:38:45 +0000 Subject: Add pep 8 song to pep 8 tag --- bot/resources/tags/pep8.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/pep8.md b/bot/resources/tags/pep8.md index cab4c4db8..57b176122 100644 --- a/bot/resources/tags/pep8.md +++ b/bot/resources/tags/pep8.md @@ -1,3 +1,5 @@ -**PEP 8** is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like `flake8` to verify that the code they\'re writing complies with the style guide. +**PEP 8** is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like flake8 to verify that the code they're writing complies with the style guide. -You can find the PEP 8 document [here](https://www.python.org/dev/peps/pep-0008). +More information: +โ€ข [PEP 8 document](https://www.python.org/dev/peps/pep-0008) +โ€ข [Our PEP 8 song!](https://www.youtube.com/watch?v=hgI0p1zf31k) :notes: -- cgit v1.2.3 From 0b11d7dfb408f4e5fe6248ae8377ddc7aa1aa5ee Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 03:48:35 +0100 Subject: Add truncate_message util --- bot/utils/messages.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bot/utils/messages.py b/bot/utils/messages.py index 077dd9569..c01fa5d0e 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -154,3 +154,12 @@ async def send_denial(ctx: Context, reason: str) -> None: def format_user(user: discord.abc.User) -> str: """Return a string for `user` which has their mention and ID.""" return f"{user.mention} (`{user.id}`)" + + +def truncate_message(message: discord.Message, limit: int) -> str: + """Returns a truncated version of the message content, up to the specified limit.""" + text = message.content + if len(text) > limit: + return text[:limit-3] + "..." + else: + return text -- cgit v1.2.3 From e1d269d82eed8a01d3d3b0ff33d05e3c79324007 Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 04:00:01 +0100 Subject: Add function to DM users when opening help channel --- bot/exts/help_channels/_message.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index 2bbd4bdd6..12ac4035d 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -8,6 +8,7 @@ import bot from bot import constants from bot.exts.help_channels import _caches from bot.utils.channel import is_in_category +from bot.utils.messages import truncate_message log = logging.getLogger(__name__) @@ -92,6 +93,38 @@ async def is_empty(channel: discord.TextChannel) -> bool: return False +async def dm_on_open(message: discord.Message) -> None: + """ + DM claimant with a link to the claimed channel's first message, with a 100 letter preview of the message. + + Does nothing if the user has DMs disabled. + """ + embed = discord.Embed( + title="Help channel opened", + description=f"You claimed {message.channel.mention}.", + colour=bot.constants.Colours.bright_green, + timestamp=message.created_at, + ) + + embed.set_thumbnail(url=constants.Icons.green_questionmark) + embed.add_field( + name="Your message", value=truncate_message(message, limit=100), inline=False + ) + embed.add_field( + name="Want to go there?", + value=f"[Jump to message!]({message.jump_url})", + inline=False, + ) + + try: + await message.author.send(embed=embed) + log.trace(f"Sent DM to {message.author.id} after claiming help channel.") + except discord.errors.Forbidden: + log.trace( + f"Ignoring to send DM to {message.author.id} after claiming help channel: DMs disabled." + ) + + async def notify(channel: discord.TextChannel, last_notification: t.Optional[datetime]) -> t.Optional[datetime]: """ Send a message in `channel` notifying about a lack of available help channels. -- cgit v1.2.3 From e6483d633ac6ecc2a88051442108d9c88e5f7745 Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 04:00:58 +0100 Subject: Add green question mark to default config Add green question mark to default config Add green question mark to config --- bot/constants.py | 1 + config-default.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 8a93ff9cf..69bc82b89 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -326,6 +326,7 @@ class Icons(metaclass=YAMLGetter): filtering: str green_checkmark: str + green_questionmark: str guild_update: str hash_blurple: str diff --git a/config-default.yml b/config-default.yml index 8e9a29a51..7d9afaa0e 100644 --- a/config-default.yml +++ b/config-default.yml @@ -90,6 +90,7 @@ style: filtering: "https://cdn.discordapp.com/emojis/472472638594482195.png" green_checkmark: "https://raw.githubusercontent.com/python-discord/branding/master/icons/checkmark/green-checkmark-dist.png" + green_questionmark: "https://raw.githubusercontent.com/python-discord/branding/master/icons/checkmark/green-question-mark-dist.png" guild_update: "https://cdn.discordapp.com/emojis/469954765141442561.png" hash_blurple: "https://cdn.discordapp.com/emojis/469950142942806017.png" -- cgit v1.2.3 From e34ea2f1c108d1900e251d17b38563536345d2de Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 04:07:05 +0100 Subject: Send DM when user claims help channel --- bot/exts/help_channels/_cog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 0995c8a79..a18ddc900 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -102,6 +102,7 @@ class HelpChannels(commands.Cog): await _cooldown.revoke_send_permissions(message.author, self.scheduler) await _message.pin(message) + await _message.dm_on_open(message) # Add user with channel for dormant check. await _caches.claimants.set(message.channel.id, message.author.id) -- cgit v1.2.3 From bb9e56c3cb874ef76ab82db02ce8242117e0da92 Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 11:08:41 +0100 Subject: Update embed field title to be more formal --- bot/exts/help_channels/_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index 12ac4035d..95aca067a 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -111,7 +111,7 @@ async def dm_on_open(message: discord.Message) -> None: name="Your message", value=truncate_message(message, limit=100), inline=False ) embed.add_field( - name="Want to go there?", + name="Conversation", value=f"[Jump to message!]({message.jump_url})", inline=False, ) -- cgit v1.2.3 From cae0d84757e026976f1a9e87d52c581669b7b8e8 Mon Sep 17 00:00:00 2001 From: Gustav Odinger <65498475+gustavwilliam@users.noreply.github.com> Date: Tue, 23 Feb 2021 11:14:31 +0100 Subject: Use textwrap.shorten instead of custom function This applies to the help channel DM embed, where the user is sent a truncated version of their message. --- bot/exts/help_channels/_message.py | 6 ++++-- bot/utils/messages.py | 9 --------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index 95aca067a..4113e51c5 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -1,4 +1,5 @@ import logging +import textwrap import typing as t from datetime import datetime @@ -8,7 +9,6 @@ import bot from bot import constants from bot.exts.help_channels import _caches from bot.utils.channel import is_in_category -from bot.utils.messages import truncate_message log = logging.getLogger(__name__) @@ -108,7 +108,9 @@ async def dm_on_open(message: discord.Message) -> None: embed.set_thumbnail(url=constants.Icons.green_questionmark) embed.add_field( - name="Your message", value=truncate_message(message, limit=100), inline=False + name="Your message", + value=textwrap.shorten(message.content, width=100, placeholder="..."), + inline=False, ) embed.add_field( name="Conversation", diff --git a/bot/utils/messages.py b/bot/utils/messages.py index c01fa5d0e..077dd9569 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -154,12 +154,3 @@ async def send_denial(ctx: Context, reason: str) -> None: def format_user(user: discord.abc.User) -> str: """Return a string for `user` which has their mention and ID.""" return f"{user.mention} (`{user.id}`)" - - -def truncate_message(message: discord.Message, limit: int) -> str: - """Returns a truncated version of the message content, up to the specified limit.""" - text = message.content - if len(text) > limit: - return text[:limit-3] + "..." - else: - return text -- cgit v1.2.3 From d71ac9f6e240ffd2d4195d9dbbf5740a0c2413a1 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Tue, 23 Feb 2021 19:24:18 +0300 Subject: Fixes Problems With Help Channel DM Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/exts/help_channels/_cog.py | 5 ++++- bot/exts/help_channels/_message.py | 8 +++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index a18ddc900..6abf99810 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -102,7 +102,10 @@ class HelpChannels(commands.Cog): await _cooldown.revoke_send_permissions(message.author, self.scheduler) await _message.pin(message) - await _message.dm_on_open(message) + try: + await _message.dm_on_open(message) + except Exception as e: + log.warning("Error occurred while sending DM:", exc_info=e) # Add user with channel for dormant check. await _caches.claimants.set(message.channel.id, message.author.id) diff --git a/bot/exts/help_channels/_message.py b/bot/exts/help_channels/_message.py index 4113e51c5..36388f9bd 100644 --- a/bot/exts/help_channels/_message.py +++ b/bot/exts/help_channels/_message.py @@ -107,11 +107,9 @@ async def dm_on_open(message: discord.Message) -> None: ) embed.set_thumbnail(url=constants.Icons.green_questionmark) - embed.add_field( - name="Your message", - value=textwrap.shorten(message.content, width=100, placeholder="..."), - inline=False, - ) + formatted_message = textwrap.shorten(message.content, width=100, placeholder="...") + if formatted_message: + embed.add_field(name="Your message", value=formatted_message, inline=False) embed.add_field( name="Conversation", value=f"[Jump to message!]({message.jump_url})", -- cgit v1.2.3 From 44eb00ca03dae1b3d5faf40be63fae04ca515790 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Wed, 24 Feb 2021 18:27:25 +0100 Subject: Add off-topic etiquette to the off-topic tag --- bot/resources/tags/off-topic.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/resources/tags/off-topic.md b/bot/resources/tags/off-topic.md index c7f98a813..6a864a1d5 100644 --- a/bot/resources/tags/off-topic.md +++ b/bot/resources/tags/off-topic.md @@ -6,3 +6,5 @@ There are three off-topic channels: โ€ข <#463035268514185226> Their names change randomly every 24 hours, but you can always find them under the `OFF-TOPIC/GENERAL` category in the channel list. + +Please read our [off-topic etiquette](https://pythondiscord.com/pages/resources/guides/off-topic-etiquette/) before participating in conversations. -- cgit v1.2.3