aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Xithrius <[email protected]>2021-05-07 17:24:13 -0700
committerGravatar GitHub <[email protected]>2021-05-07 17:24:13 -0700
commit5a084e640e19bdb71ed86d6079a3f0e1e6bb96bf (patch)
tree72e7d72dd2e4fc67e511533ad18b2bfdb4116ca7
parentMissed out removing one argument from pban (diff)
parentMerge pull request #1572 from Qwerty-133/message_edit (diff)
Merge branch 'main' into enhancement/ban
-rw-r--r--bot/decorators.py28
-rw-r--r--bot/exts/info/reddit.py6
-rw-r--r--bot/exts/moderation/modlog.py4
-rw-r--r--bot/exts/recruitment/talentpool/_review.py12
-rw-r--r--bot/exts/utils/extensions.py6
-rw-r--r--bot/exts/utils/snekbox.py10
-rw-r--r--bot/resources/stars.json7
-rw-r--r--bot/resources/tags/identity.md24
-rw-r--r--docker-compose.yml20
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