aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2019-10-20 21:11:08 +0200
committerGravatar GitHub <[email protected]>2019-10-20 21:11:08 +0200
commit93789fe66e82760dc90d28d29301c312bdc52373 (patch)
treec4376bd37862d868fe100cababc731911d48fabd
parentMerge branch 'master' into unittest-migration (diff)
parentMerge pull request #528 from bendiller/antimalware-cog (diff)
Merge branch 'master' into unittest-migration
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/alias.py6
-rw-r--r--bot/cogs/antimalware.py56
-rw-r--r--bot/cogs/antispam.py2
-rw-r--r--bot/cogs/defcon.py5
-rw-r--r--bot/cogs/doc.py3
-rw-r--r--bot/cogs/filtering.py2
-rw-r--r--bot/cogs/moderation/infractions.py12
-rw-r--r--bot/cogs/moderation/modlog.py30
-rw-r--r--bot/cogs/moderation/superstarify.py4
-rw-r--r--bot/cogs/off_topic_names.py48
-rw-r--r--bot/cogs/site.py6
-rw-r--r--bot/cogs/snekbox.py12
-rw-r--r--bot/constants.py7
-rw-r--r--config-default.yml22
15 files changed, 163 insertions, 53 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index 19a7e5ec6..f352cd60e 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -39,6 +39,7 @@ bot.load_extension("bot.cogs.logging")
bot.load_extension("bot.cogs.security")
# Commands, etc
+bot.load_extension("bot.cogs.antimalware")
bot.load_extension("bot.cogs.antispam")
bot.load_extension("bot.cogs.bot")
bot.load_extension("bot.cogs.clean")
diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py
index 6648805e9..5190c559b 100644
--- a/bot/cogs/alias.py
+++ b/bot/cogs/alias.py
@@ -79,10 +79,10 @@ class Alias (Cog):
"""Alias for invoking <prefix>site faq."""
await self.invoke(ctx, "site faq")
- @command(name="rules", hidden=True)
- async def site_rules_alias(self, ctx: Context) -> None:
+ @command(name="rules", aliases=("rule",), hidden=True)
+ async def site_rules_alias(self, ctx: Context, *rules: int) -> None:
"""Alias for invoking <prefix>site rules."""
- await self.invoke(ctx, "site rules")
+ await self.invoke(ctx, "site rules", *rules)
@command(name="reload", hidden=True)
async def extensions_reload_alias(self, ctx: Context, *extensions: Extension) -> None:
diff --git a/bot/cogs/antimalware.py b/bot/cogs/antimalware.py
new file mode 100644
index 000000000..ababd6f18
--- /dev/null
+++ b/bot/cogs/antimalware.py
@@ -0,0 +1,56 @@
+import logging
+
+from discord import Message, NotFound
+from discord.ext.commands import Bot, Cog
+
+from bot.constants import AntiMalware as AntiMalwareConfig, Channels
+
+log = logging.getLogger(__name__)
+
+
+class AntiMalware(Cog):
+ """Delete messages which contain attachments with non-whitelisted file extensions."""
+
+ def __init__(self, bot: Bot):
+ self.bot = bot
+
+ @Cog.listener()
+ async def on_message(self, message: Message) -> None:
+ """Identify messages with prohibited attachments."""
+ rejected_attachments = False
+ detected_pyfile = False
+ for attachment in message.attachments:
+ if attachment.filename.lower().endswith('.py'):
+ detected_pyfile = True
+ break # Other detections irrelevant because we prioritize the .py message.
+ if not attachment.filename.lower().endswith(tuple(AntiMalwareConfig.whitelist)):
+ rejected_attachments = True
+
+ if detected_pyfile or rejected_attachments:
+ # Send a message to the user indicating the problem (with special treatment for .py)
+ author = message.author
+ if detected_pyfile:
+ msg = (
+ f"{author.mention}, it looks like you tried to attach a Python file - please "
+ f"use a code-pasting service such as https://paste.pythondiscord.com/ instead."
+ )
+ else:
+ meta_channel = self.bot.get_channel(Channels.meta)
+ msg = (
+ f"{author.mention}, it looks like you tried to attach a file type we don't "
+ f"allow. Feel free to ask in {meta_channel.mention} if you think this is a mistake."
+ )
+
+ await message.channel.send(msg)
+
+ # Delete the offending message:
+ try:
+ await message.delete()
+ except NotFound:
+ log.info(f"Tried to delete message `{message.id}`, but message could not be found.")
+
+
+def setup(bot: Bot) -> None:
+ """Antimalware cog load."""
+ bot.add_cog(AntiMalware(bot))
+ log.info("Cog loaded: AntiMalware")
diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py
index 1b394048a..1340eb608 100644
--- a/bot/cogs/antispam.py
+++ b/bot/cogs/antispam.py
@@ -59,7 +59,7 @@ class DeletionContext:
async def upload_messages(self, actor_id: int, modlog: ModLog) -> None:
"""Method that takes care of uploading the queue and posting modlog alert."""
- triggered_by_users = ", ".join(f"{m.display_name}#{m.discriminator} (`{m.id}`)" for m in self.members.values())
+ triggered_by_users = ", ".join(f"{m} (`{m.id}`)" for m in self.members.values())
mod_alert_message = (
f"**Triggered by:** {triggered_by_users}\n"
diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py
index 70e101baa..38a0915e5 100644
--- a/bot/cogs/defcon.py
+++ b/bot/cogs/defcon.py
@@ -90,8 +90,7 @@ class Defcon(Cog):
await member.kick(reason="DEFCON active, user is too new")
message = (
- f"{member.name}#{member.discriminator} (`{member.id}`) "
- f"was denied entry because their account is too new."
+ f"{member} (`{member.id}`) was denied entry because their account is too new."
)
if not message_sent:
@@ -254,7 +253,7 @@ class Defcon(Cog):
`change` string may be one of the following: ('enabled', 'disabled', 'updated')
"""
- log_msg = f"**Staffer:** {actor.name}#{actor.discriminator} (`{actor.id}`)\n"
+ log_msg = f"**Staffer:** {actor} (`{actor.id}`)\n"
if change.lower() == "enabled":
icon = Icons.defcon_enabled
diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py
index a13464bff..65cabe46f 100644
--- a/bot/cogs/doc.py
+++ b/bot/cogs/doc.py
@@ -336,8 +336,7 @@ class Doc(commands.Cog):
await self.bot.api_client.post('bot/documentation-links', json=body)
log.info(
- f"User @{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}) "
- "added a new documentation package:\n"
+ f"User @{ctx.author} ({ctx.author.id}) added a new documentation package:\n"
f"Package name: {package_name}\n"
f"Base url: {base_url}\n"
f"Inventory URL: {inventory_url}"
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 265ae5160..1d1d74e74 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -186,7 +186,7 @@ class Filtering(Cog):
message = (
f"The {filter_name} {_filter['type']} was triggered "
- f"by **{msg.author.name}#{msg.author.discriminator}** "
+ f"by **{msg.author}** "
f"(`{msg.author.id}`) {channel_str} with [the "
f"following message]({msg.jump_url}):\n\n"
f"{msg.content}"
diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py
index 592ead60f..f2ae7b95d 100644
--- a/bot/cogs/moderation/infractions.py
+++ b/bot/cogs/moderation/infractions.py
@@ -2,6 +2,7 @@ import logging
import textwrap
import typing as t
from datetime import datetime
+from gettext import ngettext
import dateutil.parser
import discord
@@ -436,7 +437,13 @@ class Infractions(Scheduler, commands.Cog):
# Default values for the confirmation message and mod log.
confirm_msg = f":ok_hand: applied"
- expiry_msg = f" until {expiry}" if expiry else " permanently"
+
+ # Specifying an expiry for a note or warning makes no sense.
+ if infr_type in ("note", "warning"):
+ expiry_msg = ""
+ else:
+ expiry_msg = f" until {expiry}" if expiry else " permanently"
+
dm_result = ""
dm_log_text = ""
expiry_log_text = f"Expires: {expiry}" if expiry else ""
@@ -463,7 +470,8 @@ class Infractions(Scheduler, commands.Cog):
"bot/infractions",
params={"user__id": str(user.id)}
)
- end_msg = f" ({len(infractions)} infractions total)"
+ total = len(infractions)
+ end_msg = f" ({total} infraction{ngettext('', 's', total)} total)"
# Execute the necessary actions to apply the infraction on Discord.
if action_coro:
diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py
index 118503517..88f2b6c67 100644
--- a/bot/cogs/moderation/modlog.py
+++ b/bot/cogs/moderation/modlog.py
@@ -363,7 +363,7 @@ class ModLog(Cog, name="ModLog"):
await self.send_log_message(
Icons.user_ban, Colours.soft_red,
- "User banned", f"{member.name}#{member.discriminator} (`{member.id}`)",
+ "User banned", f"{member} (`{member.id}`)",
thumbnail=member.avatar_url_as(static_format="png"),
channel_id=Channels.userlog
)
@@ -374,7 +374,7 @@ class ModLog(Cog, name="ModLog"):
if member.guild.id != GuildConstant.id:
return
- message = f"{member.name}#{member.discriminator} (`{member.id}`)"
+ message = f"{member} (`{member.id}`)"
now = datetime.utcnow()
difference = abs(relativedelta(now, member.created_at))
@@ -402,7 +402,7 @@ class ModLog(Cog, name="ModLog"):
await self.send_log_message(
Icons.sign_out, Colours.soft_red,
- "User left", f"{member.name}#{member.discriminator} (`{member.id}`)",
+ "User left", f"{member} (`{member.id}`)",
thumbnail=member.avatar_url_as(static_format="png"),
channel_id=Channels.userlog
)
@@ -419,7 +419,7 @@ class ModLog(Cog, name="ModLog"):
await self.send_log_message(
Icons.user_unban, Colour.blurple(),
- "User unbanned", f"{member.name}#{member.discriminator} (`{member.id}`)",
+ "User unbanned", f"{member} (`{member.id}`)",
thumbnail=member.avatar_url_as(static_format="png"),
channel_id=Channels.modlog
)
@@ -511,7 +511,7 @@ class ModLog(Cog, name="ModLog"):
for item in sorted(changes):
message += f"{Emojis.bullet} {item}\n"
- message = f"**{after.name}#{after.discriminator}** (`{after.id}`)\n{message}"
+ message = f"**{after}** (`{after.id}`)\n{message}"
await self.send_log_message(
Icons.user_update, Colour.blurple(),
@@ -540,14 +540,14 @@ class ModLog(Cog, name="ModLog"):
if channel.category:
response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
)
else:
response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** #{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
@@ -638,7 +638,7 @@ class ModLog(Cog, name="ModLog"):
if channel.category:
before_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{before.id}`\n"
"\n"
@@ -646,7 +646,7 @@ class ModLog(Cog, name="ModLog"):
)
after_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{before.id}`\n"
"\n"
@@ -654,7 +654,7 @@ class ModLog(Cog, name="ModLog"):
)
else:
before_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** #{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{before.id}`\n"
"\n"
@@ -662,7 +662,7 @@ class ModLog(Cog, name="ModLog"):
)
after_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** #{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{before.id}`\n"
"\n"
@@ -721,7 +721,7 @@ class ModLog(Cog, name="ModLog"):
if channel.category:
before_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
@@ -729,7 +729,7 @@ class ModLog(Cog, name="ModLog"):
)
after_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
@@ -737,7 +737,7 @@ class ModLog(Cog, name="ModLog"):
)
else:
before_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** #{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
@@ -745,7 +745,7 @@ class ModLog(Cog, name="ModLog"):
)
after_response = (
- f"**Author:** {author.name}#{author.discriminator} (`{author.id}`)\n"
+ f"**Author:** {author} (`{author.id}`)\n"
f"**Channel:** #{channel.name} (`{channel.id}`)\n"
f"**Message ID:** `{message.id}`\n"
"\n"
diff --git a/bot/cogs/moderation/superstarify.py b/bot/cogs/moderation/superstarify.py
index ccc6395d9..82f8621fc 100644
--- a/bot/cogs/moderation/superstarify.py
+++ b/bot/cogs/moderation/superstarify.py
@@ -129,7 +129,7 @@ class Superstarify(Cog):
# Log to the mod_log channel
log.trace("Logging to the #mod-log channel. This could fail because of channel permissions.")
mod_log_message = (
- f"**{member.name}#{member.discriminator}** (`{member.id}`)\n\n"
+ f"**{member}** (`{member.id}`)\n\n"
f"Superstarified member potentially tried to escape the prison.\n"
f"Restored enforced nickname: `{forced_nick}`\n"
f"Superstardom ends: **{end_timestamp_human}**"
@@ -183,7 +183,7 @@ class Superstarify(Cog):
# Log to the mod_log channel
log.trace("Logging to the #mod-log channel. This could fail because of channel permissions.")
mod_log_message = (
- f"**{member.name}#{member.discriminator}** (`{member.id}`)\n\n"
+ f"**{member}** (`{member.id}`)\n\n"
f"Superstarified by **{ctx.author.name}**\n"
f"Old nickname: `{member.display_name}`\n"
f"New nickname: `{forced_nick}`\n"
diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py
index 2977e4ebb..1f9fb0b4f 100644
--- a/bot/cogs/off_topic_names.py
+++ b/bot/cogs/off_topic_names.py
@@ -98,15 +98,42 @@ class OffTopicNames(Cog):
@otname_group.command(name='add', aliases=('a',))
@with_role(*MODERATION_ROLES)
async def add_command(self, ctx: Context, *names: OffTopicName) -> None:
- """Adds a new off-topic name to the rotation."""
+ """
+ Adds a new off-topic name to the rotation.
+
+ The name is not added if it is too similar to an existing name.
+ """
# Chain multiple words to a single one
name = "-".join(names)
- await self.bot.api_client.post(f'bot/off-topic-channel-names', params={'name': name})
- log.info(
- f"{ctx.author.name}#{ctx.author.discriminator}"
- f" added the off-topic channel name '{name}"
- )
+ existing_names = await self.bot.api_client.get('bot/off-topic-channel-names')
+ close_match = difflib.get_close_matches(name, existing_names, n=1, cutoff=0.8)
+
+ if close_match:
+ match = close_match[0]
+ log.info(
+ f"{ctx.author} tried to add channel name '{name}' but it was too similar to '{match}'"
+ )
+ await ctx.send(
+ f":x: The channel name `{name}` is too similar to `{match}`, and thus was not added. "
+ "Use `!otn forceadd` to override this check."
+ )
+ else:
+ await self._add_name(ctx, name)
+
+ @otname_group.command(name='forceadd', aliases=('fa',))
+ @with_role(*MODERATION_ROLES)
+ async def force_add_command(self, ctx: Context, *names: OffTopicName) -> None:
+ """Forcefully adds a new off-topic name to the rotation."""
+ # Chain multiple words to a single one
+ name = "-".join(names)
+ await self._add_name(ctx, name)
+
+ async def _add_name(self, ctx: Context, name: str) -> None:
+ """Adds an off-topic channel name to the site storage."""
+ await self.bot.api_client.post('bot/off-topic-channel-names', params={'name': name})
+
+ log.info(f"{ctx.author} added the off-topic channel name '{name}'")
await ctx.send(f":ok_hand: Added `{name}` to the names list.")
@otname_group.command(name='delete', aliases=('remove', 'rm', 'del', 'd'))
@@ -115,12 +142,9 @@ class OffTopicNames(Cog):
"""Removes a off-topic name from the rotation."""
# Chain multiple words to a single one
name = "-".join(names)
-
await self.bot.api_client.delete(f'bot/off-topic-channel-names/{name}')
- log.info(
- f"{ctx.author.name}#{ctx.author.discriminator}"
- f" deleted the off-topic channel name '{name}"
- )
+
+ log.info(f"{ctx.author} deleted the off-topic channel name '{name}'")
await ctx.send(f":ok_hand: Removed `{name}` from the names list.")
@otname_group.command(name='list', aliases=('l',))
@@ -152,7 +176,7 @@ class OffTopicNames(Cog):
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))
embed = Embed(
- title=f"Query results",
+ title="Query results",
colour=Colour.blue()
)
diff --git a/bot/cogs/site.py b/bot/cogs/site.py
index c3bdf85e4..d95359159 100644
--- a/bot/cogs/site.py
+++ b/bot/cogs/site.py
@@ -126,15 +126,15 @@ class Site(Cog):
invalid_indices = tuple(
pick
for pick in rules
- if pick < 0 or pick >= len(full_rules)
+ if pick < 1 or pick > len(full_rules)
)
if invalid_indices:
indices = ', '.join(map(str, invalid_indices))
- await ctx.send(f":x: Invalid rule indices {indices}")
+ await ctx.send(f":x: Invalid rule indices: {indices}")
return
- final_rules = tuple(f"**{pick}.** {full_rules[pick]}" for pick in rules)
+ final_rules = tuple(f"**{pick}.** {full_rules[pick - 1]}" for pick in rules)
await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3)
diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py
index 81185cf3e..c0390cb1e 100644
--- a/bot/cogs/snekbox.py
+++ b/bot/cogs/snekbox.py
@@ -178,7 +178,7 @@ class Snekbox(Cog):
if ctx.author.id in self.jobs:
await ctx.send(
f"{ctx.author.mention} You've already got a job running - "
- f"please wait for it to finish!"
+ "please wait for it to finish!"
)
return
@@ -186,10 +186,7 @@ class Snekbox(Cog):
await ctx.invoke(self.bot.get_command("help"), "eval")
return
- log.info(
- f"Received code from {ctx.author.name}#{ctx.author.discriminator} "
- f"for evaluation:\n{code}"
- )
+ log.info(f"Received code from {ctx.author} for evaluation:\n{code}")
self.jobs[ctx.author.id] = datetime.datetime.now()
code = self.prepare_input(code)
@@ -213,10 +210,7 @@ class Snekbox(Cog):
wait_for_deletion(response, user_ids=(ctx.author.id,), client=ctx.bot)
)
- log.info(
- f"{ctx.author.name}#{ctx.author.discriminator}'s job had a return code of "
- f"{results['returncode']}"
- )
+ log.info(f"{ctx.author}'s job had a return code of {results['returncode']}")
finally:
del self.jobs[ctx.author.id]
diff --git a/bot/constants.py b/bot/constants.py
index f4f45eb2c..4beae84e9 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -345,6 +345,7 @@ class Channels(metaclass=YAMLGetter):
help_7: int
helpers: int
message_log: int
+ meta: int
mod_alerts: int
modlog: int
off_topic_0: int
@@ -460,6 +461,12 @@ class AntiSpam(metaclass=YAMLGetter):
rules: Dict[str, Dict[str, int]]
+class AntiMalware(metaclass=YAMLGetter):
+ section = "anti_malware"
+
+ whitelist: list
+
+
class BigBrother(metaclass=YAMLGetter):
section = 'big_brother'
diff --git a/config-default.yml b/config-default.yml
index ca405337e..197743296 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -107,6 +107,7 @@ guild:
help_7: 587375768556797982
helpers: 385474242440986624
message_log: &MESSAGE_LOG 467752170159079424
+ meta: 429409067623251969
mod_alerts: 473092532147060736
modlog: &MODLOG 282638479504965634
off_topic_0: 291284109232308226
@@ -322,6 +323,27 @@ anti_spam:
max: 3
+anti_malware:
+ whitelist:
+ - '.3gp'
+ - '.3g2'
+ - '.avi'
+ - '.bmp'
+ - '.gif'
+ - '.h264'
+ - '.jpg'
+ - '.jpeg'
+ - '.m4v'
+ - '.mkv'
+ - '.mov'
+ - '.mp4'
+ - '.mpeg'
+ - '.mpg'
+ - '.png'
+ - '.tiff'
+ - '.wmv'
+
+
reddit:
request_delay: 60
subreddits: