aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2018-09-28 11:00:21 +0200
committerGravatar Leon Sandøy <[email protected]>2018-09-28 11:00:21 +0200
commitd4a2ff17e6b6b2224ad7355cd0e635cdb20db9cf (patch)
tree7e559681f7bf9c250f52af28342a2fb25e23d94e
parentimproving a comment. why is this a commit. I'm so sorry. (diff)
parentmaybe next time the mod log breaks we can fix it before it's been 2 weeks. I'... (diff)
Merge branch 'master' of gitlab.com:python-discord/projects/bot
-rw-r--r--bot/cogs/filtering.py4
-rw-r--r--bot/cogs/modlog.py2
-rw-r--r--bot/cogs/snekbox.py43
-rw-r--r--bot/cogs/tags.py25
4 files changed, 53 insertions, 21 deletions
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 586c99174..70254fd88 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -111,8 +111,8 @@ class Filtering:
message = (
f"The {filter_name} {_filter['type']} was triggered "
f"by **{msg.author.name}#{msg.author.discriminator}** "
- f"(`{msg.author.id}`) in <#{msg.channel.id}> with the "
- f"following message:\n\n"
+ f"(`{msg.author.id}`) in <#{msg.channel.id}> with [the "
+ f"following message]({msg.jump_url}):\n\n"
f"{msg.content}"
)
diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py
index d302634d1..9c81661ba 100644
--- a/bot/cogs/modlog.py
+++ b/bot/cogs/modlog.py
@@ -360,7 +360,7 @@ class ModLog:
now = datetime.datetime.utcnow()
difference = abs(relativedelta(now, member.created_at))
- message += "\n\n**Account age:** " + humanize_delta(member.created_at)
+ message += "\n\n**Account age:** " + humanize_delta(difference)
if difference.days < 1 and difference.months < 1 and difference.years < 1: # New user account!
message = f"{Emojis.new} {message}"
diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py
index 6f618a2c7..fb9164194 100644
--- a/bot/cogs/snekbox.py
+++ b/bot/cogs/snekbox.py
@@ -32,6 +32,23 @@ except Exception as e:
"""
ESCAPE_REGEX = re.compile("[`\u202E\u200B]{3,}")
+FORMATTED_CODE_REGEX = re.compile(
+ r"^\s*" # any leading whitespace from the beginning of the string
+ r"(?P<delim>(?P<block>```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block
+ r"(?(block)(?:(?P<lang>[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline)
+ r"(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code
+ r"(?P<code>.*?)" # extract all code inside the markup
+ r"\s*" # any more whitespace before the end of the code markup
+ r"(?P=delim)" # match the exact same delimiter from the start again
+ r"\s*$", # any trailing whitespace until the end of the string
+ re.DOTALL | re.IGNORECASE # "." also matches newlines, case insensitive
+)
+RAW_CODE_REGEX = re.compile(
+ r"^(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code
+ r"(?P<code>.*?)" # extract all the rest as code
+ r"\s*$", # any trailing whitespace until the end of the string
+ re.DOTALL # "." also matches newlines
+)
BYPASS_ROLES = (Roles.owner, Roles.admin, Roles.moderator, Roles.helpers)
WHITELISTED_CHANNELS = (Channels.bot,)
WHITELISTED_CHANNELS_STRING = ', '.join(f"<#{channel_id}>" for channel_id in WHITELISTED_CHANNELS)
@@ -54,8 +71,6 @@ class Snekbox:
Safe evaluation using Snekbox
"""
- jobs = None # type: dict
-
def __init__(self, bot: Bot):
self.bot = bot
self.jobs = {}
@@ -85,18 +100,20 @@ class Snekbox:
log.info(f"Received code from {ctx.author.name}#{ctx.author.discriminator} for evaluation:\n{code}")
self.jobs[ctx.author.id] = datetime.datetime.now()
- while code.startswith("\n"):
- code = code[1:]
-
- if code.startswith("```") and code.endswith("```"):
- code = code[3:-3]
-
- if code.startswith("python"):
- code = code[6:]
- elif code.startswith("py"):
- code = code[2:]
+ # Strip whitespace and inline or block code markdown and extract the code and some formatting info
+ match = FORMATTED_CODE_REGEX.fullmatch(code)
+ if match:
+ code, block, lang, delim = match.group("code", "block", "lang", "delim")
+ code = textwrap.dedent(code)
+ if block:
+ info = (f"'{lang}' highlighted" if lang else "plain") + " code block"
+ else:
+ info = f"{delim}-enclosed inline code"
+ log.trace(f"Extracted {info} for evaluation:\n{code}")
+ else:
+ code = textwrap.dedent(RAW_CODE_REGEX.fullmatch(code).group("code"))
+ log.trace(f"Eval message contains not or badly formatted code, stripping whitespace only:\n{code}")
- code = textwrap.dedent(code.strip())
code = textwrap.indent(code, " ")
code = CODE_TEMPLATE.replace("{CODE}", code)
diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py
index 7499b2b1c..e6f9ecd89 100644
--- a/bot/cogs/tags.py
+++ b/bot/cogs/tags.py
@@ -1,6 +1,7 @@
import logging
import random
import time
+from typing import Optional
from discord import Colour, Embed
from discord.ext.commands import (
@@ -11,6 +12,7 @@ from discord.ext.commands import (
from bot.constants import (
Channels, Cooldowns, ERROR_REPLIES, Keys, Roles, URLs
)
+from bot.converters import ValidURL
from bot.decorators import with_role
from bot.pagination import LinePaginator
@@ -107,12 +109,13 @@ class Tags:
return tag_data
- async def post_tag_data(self, tag_name: str, tag_content: str) -> dict:
+ async def post_tag_data(self, tag_name: str, tag_content: str, image_url: Optional[str]) -> dict:
"""
Send some tag_data to our API to have it saved in the database.
:param tag_name: The name of the tag to create or edit.
:param tag_content: The content of the tag.
+ :param image_url: The image URL of the tag, can be `None`.
:return: json response from the API in the following format:
{
'success': bool
@@ -121,7 +124,8 @@ class Tags:
params = {
'tag_name': tag_name,
- 'tag_content': tag_content
+ 'tag_content': tag_content,
+ 'image_url': image_url
}
response = await self.bot.http_session.post(URLs.site_tags_api, headers=self.headers, json=params)
@@ -226,6 +230,8 @@ class Tags:
else:
embed.description = tag_data['tag_content']
+ if tag_data['image_url'] is not None:
+ embed.set_image(url=tag_data['image_url'])
# If not, prepare an error message.
else:
@@ -257,13 +263,20 @@ class Tags:
@tags_group.command(name='set', aliases=('add', 'edit', 's'))
@with_role(Roles.admin, Roles.owner, Roles.moderator)
- async def set_command(self, ctx: Context, tag_name: TagNameConverter, *, tag_content: TagContentConverter):
+ async def set_command(
+ self,
+ ctx: Context,
+ tag_name: TagNameConverter,
+ tag_content: TagContentConverter,
+ image_url: ValidURL = None
+ ):
"""
Create a new tag or edit an existing one.
:param ctx: discord message context
:param tag_name: The name of the tag to create or edit.
:param tag_content: The content of the tag.
+ :param image_url: An optional image for the tag.
"""
tag_name = tag_name.lower().strip()
@@ -271,12 +284,13 @@ class Tags:
embed = Embed()
embed.colour = Colour.red()
- tag_data = await self.post_tag_data(tag_name, tag_content)
+ tag_data = await self.post_tag_data(tag_name, tag_content, image_url)
if tag_data.get("success"):
log.debug(f"{ctx.author} successfully added the following tag to our database: \n"
f"tag_name: {tag_name}\n"
- f"tag_content: '{tag_content}'")
+ f"tag_content: '{tag_content}'\n"
+ f"image_url: '{image_url}'")
embed.colour = Colour.blurple()
embed.title = "Tag successfully added"
embed.description = f"**{tag_name}** added to tag database."
@@ -284,6 +298,7 @@ class Tags:
log.error("There was an unexpected database error when trying to add the following tag: \n"
f"tag_name: {tag_name}\n"
f"tag_content: '{tag_content}'\n"
+ f"image_url: '{image_url}'\n"
f"response: {tag_data}")
embed.title = "Database error"
embed.description = ("There was a problem adding the data to the tags database. "