diff options
| -rw-r--r-- | bot/decorators.py | 28 | ||||
| -rw-r--r-- | bot/exts/info/reddit.py | 6 | ||||
| -rw-r--r-- | bot/exts/recruitment/talentpool/_review.py | 12 | ||||
| -rw-r--r-- | bot/exts/utils/snekbox.py | 10 | ||||
| -rw-r--r-- | bot/resources/stars.json | 7 | ||||
| -rw-r--r-- | bot/resources/tags/identity.md | 24 | 
6 files changed, 74 insertions, 13 deletions
| diff --git a/bot/decorators.py b/bot/decorators.py index e971a5bd3..f65ec4103 100644 --- a/bot/decorators.py +++ b/bot/decorators.py @@ -107,11 +107,19 @@ def has_no_roles(*roles: t.Union[str, int]) -> t.Callable:      return commands.check(predicate) -def redirect_output(destination_channel: int, bypass_roles: t.Container[int] = None) -> t.Callable: +def redirect_output( +    destination_channel: int, +    bypass_roles: t.Optional[t.Container[int]] = None, +    channels: t.Optional[t.Container[int]] = None, +    categories: t.Optional[t.Container[int]] = None, +    ping_user: bool = True +) -> t.Callable:      """      Changes the channel in the context of the command to redirect the output to a certain channel. -    Redirect is bypassed if the author has a role to bypass redirection. +    Redirect is bypassed if the author has a bypass role or if it is in a channel that can bypass redirection. + +    If ping_user is False, it will not send a message in the destination channel.      This decorator must go before (below) the `command` decorator.      """ @@ -119,7 +127,7 @@ def redirect_output(destination_channel: int, bypass_roles: t.Container[int] = N          @command_wraps(func)          async def inner(self: Cog, ctx: Context, *args, **kwargs) -> None:              if ctx.channel.id == destination_channel: -                log.trace(f"Command {ctx.command.name} was invoked in destination_channel, not redirecting") +                log.trace(f"Command {ctx.command} was invoked in destination_channel, not redirecting")                  await func(self, ctx, *args, **kwargs)                  return @@ -128,12 +136,24 @@ def redirect_output(destination_channel: int, bypass_roles: t.Container[int] = N                  await func(self, ctx, *args, **kwargs)                  return +            elif channels and ctx.channel.id not in channels: +                log.trace(f"{ctx.author} used {ctx.command} in a channel that can bypass output redirection") +                await func(self, ctx, *args, **kwargs) +                return + +            elif categories and ctx.channel.category.id not in categories: +                log.trace(f"{ctx.author} used {ctx.command} in a category that can bypass output redirection") +                await func(self, ctx, *args, **kwargs) +                return +              redirect_channel = ctx.guild.get_channel(destination_channel)              old_channel = ctx.channel              log.trace(f"Redirecting output of {ctx.author}'s command '{ctx.command.name}' to {redirect_channel.name}")              ctx.channel = redirect_channel -            await ctx.channel.send(f"Here's the output of your command, {ctx.author.mention}") + +            if ping_user: +                await ctx.send(f"Here's the output of your command, {ctx.author.mention}")              asyncio.create_task(func(self, ctx, *args, **kwargs))              message = await old_channel.send( diff --git a/bot/exts/info/reddit.py b/bot/exts/info/reddit.py index 6790be762..e1f0c5f9f 100644 --- a/bot/exts/info/reddit.py +++ b/bot/exts/info/reddit.py @@ -4,6 +4,7 @@ import random  import textwrap  from collections import namedtuple  from datetime import datetime, timedelta +from html import unescape  from typing import List  from aiohttp import BasicAuth, ClientError @@ -179,8 +180,7 @@ class Reddit(Cog):          for post in posts:              data = post["data"] -            text = data["selftext"] -            if text: +            if text := unescape(data["selftext"]):                  text = textwrap.shorten(text, width=128, placeholder="...")                  text += "\n"  # Add newline to separate embed info @@ -188,7 +188,7 @@ class Reddit(Cog):              comments = data["num_comments"]              author = data["author"] -            title = textwrap.shorten(data["title"], width=64, placeholder="...") +            title = textwrap.shorten(unescape(data["title"]), width=64, placeholder="...")              # Normal brackets interfere with Markdown.              title = escape_markdown(title).replace("[", "⦋").replace("]", "⦌")              link = self.URL + data["permalink"] diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index 11aa3b62b..4ae1c5ad6 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -57,7 +57,7 @@ class Reviewer:          """Schedules a single user for review."""          log.trace(f"Scheduling review of user with ID {user_id}") -        user_data = self._pool.watched_users[user_id] +        user_data = self._pool.watched_users.get(user_id)          inserted_at = isoparse(user_data['inserted_at']).replace(tzinfo=None)          review_at = inserted_at + timedelta(days=MAX_DAYS_IN_POOL) @@ -81,14 +81,18 @@ class Reviewer:                  await message.add_reaction(reaction)          if update_database: -            nomination = self._pool.watched_users[user_id] +            nomination = self._pool.watched_users.get(user_id)              await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{nomination['id']}", json={"reviewed": True})      async def make_review(self, user_id: int) -> typing.Tuple[str, Optional[Emoji]]:          """Format a generic review of a user and return it with the seen emoji."""          log.trace(f"Formatting the review of {user_id}") -        nomination = self._pool.watched_users[user_id] +        # Since `watched_users` is a defaultdict, we should take care +        # not to accidentally insert the IDs of users that have no +        # active nominated by using the `watched_users.get(user_id)` +        # instead of `watched_users[user_id]`. +        nomination = self._pool.watched_users.get(user_id)          if not nomination:              log.trace(f"There doesn't appear to be an active nomination for {user_id}")              return "", None @@ -303,7 +307,7 @@ class Reviewer:              await ctx.send(f":x: Can't find a currently nominated user with id `{user_id}`")              return False -        nomination = self._pool.watched_users[user_id] +        nomination = self._pool.watched_users.get(user_id)          if nomination["reviewed"]:              await ctx.send(":x: This nomination was already reviewed, but here's a cookie :cookie:")              return False diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox.py index da95240bb..b1f1ba6a8 100644 --- a/bot/exts/utils/snekbox.py +++ b/bot/exts/utils/snekbox.py @@ -13,7 +13,7 @@ from discord.ext.commands import Cog, Context, command, guild_only  from bot.bot import Bot  from bot.constants import Categories, Channels, Roles, URLs -from bot.decorators import not_in_blacklist +from bot.decorators import redirect_output  from bot.utils import send_to_paste_service  from bot.utils.messages import wait_for_deletion @@ -280,7 +280,13 @@ class Snekbox(Cog):      @command(name="eval", aliases=("e",))      @guild_only() -    @not_in_blacklist(channels=NO_EVAL_CHANNELS, categories=NO_EVAL_CATEGORIES, override_roles=EVAL_ROLES) +    @redirect_output( +        destination_channel=Channels.bot_commands, +        bypass_roles=EVAL_ROLES, +        categories=NO_EVAL_CATEGORIES, +        channels=NO_EVAL_CHANNELS, +        ping_user=False +    )      async def eval_command(self, ctx: Context, *, code: str = None) -> None:          """          Run Python code and get the results. diff --git a/bot/resources/stars.json b/bot/resources/stars.json index 5ecad0213..3eb0a9d0d 100644 --- a/bot/resources/stars.json +++ b/bot/resources/stars.json @@ -20,6 +20,7 @@    "Céline Dion",    "Cher",    "Christina Aguilera", +  "Darude",    "David Bowie",    "Donna Summer",    "Drake", @@ -31,11 +32,14 @@    "Flo Rida",    "Frank Sinatra",    "Garth Brooks", +  "George Harrison",    "George Michael",    "George Strait", +  "Guido Van Rossum",    "James Taylor",    "Janet Jackson",    "Jay-Z", +  "John Lennon",    "Johnny Cash",    "Johnny Hallyday",    "Julio Iglesias", @@ -61,13 +65,16 @@    "Pink",    "Prince",    "Reba McEntire", +  "Rick Astley",    "Rihanna", +  "Ringo Starr",    "Robbie Williams",    "Rod Stewart",    "Santana",    "Shania Twain",    "Stevie Wonder",    "Taylor Swift", +  "The Weeknd",    "Tim McGraw",    "Tina Turner",    "Tom Petty", diff --git a/bot/resources/tags/identity.md b/bot/resources/tags/identity.md new file mode 100644 index 000000000..fb2010759 --- /dev/null +++ b/bot/resources/tags/identity.md @@ -0,0 +1,24 @@ +**Identity vs. Equality** + +Should I be using `is` or `==`? + +To check if two objects are equal, use the equality operator (`==`). +```py +x = 5 +if x == 5: +    print("x equals 5") +if x == 3: +    print("x equals 3") +# Prints 'x equals 5' +``` +To check if two objects are actually the same thing in memory, use the identity comparison operator (`is`). +```py +list_1 = [1, 2, 3] +list_2 = [1, 2, 3] +if list_1 is [1, 2, 3]: +    print("list_1 is list_2") +reference_to_list_1 = list_1 +if list_1 is reference_to_list_1: +    print("list_1 is reference_to_list_1") +# Prints 'list_1 is reference_to_list_1' +``` | 
