diff options
author | 2021-05-07 17:24:13 -0700 | |
---|---|---|
committer | 2021-05-07 17:24:13 -0700 | |
commit | 5a084e640e19bdb71ed86d6079a3f0e1e6bb96bf (patch) | |
tree | 72e7d72dd2e4fc67e511533ad18b2bfdb4116ca7 | |
parent | Missed out removing one argument from pban (diff) | |
parent | Merge pull request #1572 from Qwerty-133/message_edit (diff) |
Merge branch 'main' into enhancement/ban
-rw-r--r-- | bot/decorators.py | 28 | ||||
-rw-r--r-- | bot/exts/info/reddit.py | 6 | ||||
-rw-r--r-- | bot/exts/moderation/modlog.py | 4 | ||||
-rw-r--r-- | bot/exts/recruitment/talentpool/_review.py | 12 | ||||
-rw-r--r-- | bot/exts/utils/extensions.py | 6 | ||||
-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 | ||||
-rw-r--r-- | docker-compose.yml | 20 |
9 files changed, 100 insertions, 17 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/moderation/modlog.py b/bot/exts/moderation/modlog.py index e92f76c9a..be65ade6e 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -12,6 +12,7 @@ from deepdiff import DeepDiff from discord import Colour from discord.abc import GuildChannel from discord.ext.commands import Cog, Context +from discord.utils import escape_markdown from bot.bot import Bot from bot.constants import Categories, Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, Roles, URLs @@ -640,9 +641,10 @@ class ModLog(Cog, name="ModLog"): channel = msg_before.channel channel_name = f"{channel.category}/#{channel.name}" if channel.category else f"#{channel.name}" + cleaned_contents = (escape_markdown(msg.clean_content).split() for msg in (msg_before, msg_after)) # Getting the difference per words and group them by type - add, remove, same # Note that this is intended grouping without sorting - diff = difflib.ndiff(msg_before.clean_content.split(), msg_after.clean_content.split()) + diff = difflib.ndiff(*cleaned_contents) diff_groups = tuple( (diff_type, tuple(s[2:] for s in diff_words)) for diff_type, diff_words in itertools.groupby(diff, key=lambda s: s[0]) 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/extensions.py b/bot/exts/utils/extensions.py index 418db0150..8a1ed98f4 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -109,7 +109,7 @@ class Extensions(commands.Cog): blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) if blacklisted: - msg = f":x: The following extension(s) may not be unloaded:```{blacklisted}```" + msg = f":x: The following extension(s) may not be unloaded:```\n{blacklisted}```" else: if "*" in extensions or "**" in extensions: extensions = set(self.bot.extensions.keys()) - UNLOAD_BLACKLIST @@ -212,7 +212,7 @@ class Extensions(commands.Cog): if failures: failures = "\n".join(f"{ext}\n {err}" for ext, err in failures.items()) - msg += f"\nFailures:```{failures}```" + msg += f"\nFailures:```\n{failures}```" log.debug(f"Batch {verb}ed extensions.") @@ -239,7 +239,7 @@ class Extensions(commands.Cog): log.exception(f"Extension '{ext}' failed to {verb}.") error_msg = f"{e.__class__.__name__}: {e}" - msg = f":x: Failed to {verb} extension `{ext}`:\n```{error_msg}```" + msg = f":x: Failed to {verb} extension `{ext}`:\n```\n{error_msg}```" else: msg = f":ok_hand: Extension successfully {verb}ed: `{ext}`." log.debug(msg[10:]) 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' +``` diff --git a/docker-compose.yml b/docker-compose.yml index 8afdd6ef1..bdfedf5c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,20 @@ version: "3.7" +x-logging: &logging + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + +x-restart-policy: &restart_policy + restart: always + services: postgres: + << : *logging + << : *restart_policy image: postgres:12-alpine environment: POSTGRES_DB: pysite @@ -13,11 +25,15 @@ services: POSTGRES_USER: pysite redis: + << : *logging + << : *restart_policy image: redis:5.0.9 ports: - "127.0.0.1:6379:6379" snekbox: + << : *logging + << : *restart_policy image: ghcr.io/python-discord/snekbox:latest init: true ipc: none @@ -26,6 +42,8 @@ services: privileged: true web: + << : *logging + << : *restart_policy image: ghcr.io/python-discord/site:latest command: ["run", "--debug"] networks: @@ -46,6 +64,8 @@ services: STATIC_ROOT: /var/www/static bot: + << : *logging + << : *restart_policy build: context: . dockerfile: Dockerfile |