From c3c7a7f7c75d251c0af38089a4c29d7af25ad6fe Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 29 Jul 2018 13:38:29 +0200 Subject: Ignore invocation deletion event for clean cog --- bot/cogs/clean.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py index 85c9ec781..da0a5a9f2 100644 --- a/bot/cogs/clean.py +++ b/bot/cogs/clean.py @@ -169,6 +169,7 @@ class Clean: # Always start by deleting the invocation if not invocation_deleted: + self.mod_log.ignore_message_deletion(message.id) await message.delete() invocation_deleted = True continue -- cgit v1.2.3 From dc4642ee36c79fa1bd3c2189352cf45e6405e4db Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 29 Jul 2018 17:09:33 +0000 Subject: Add the `AntiSpam` cog. --- bot/__main__.py | 1 + bot/cogs/antispam.py | 161 ++++++++++++++++++++++++++++++++++++++++++++ bot/constants.py | 9 ++- bot/rules/__init__.py | 12 ++++ bot/rules/attachments.py | 30 +++++++++ bot/rules/burst.py | 27 ++++++++ bot/rules/burst_shared.py | 22 ++++++ bot/rules/chars.py | 28 ++++++++ bot/rules/discord_emojis.py | 35 ++++++++++ bot/rules/duplicates.py | 31 +++++++++ bot/rules/links.py | 31 +++++++++ bot/rules/mentions.py | 28 ++++++++ bot/rules/newlines.py | 28 ++++++++ bot/rules/role_mentions.py | 28 ++++++++ config-default.yml | 76 ++++++++++++++++++++- 15 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 bot/cogs/antispam.py create mode 100644 bot/rules/__init__.py create mode 100644 bot/rules/attachments.py create mode 100644 bot/rules/burst.py create mode 100644 bot/rules/burst_shared.py create mode 100644 bot/rules/chars.py create mode 100644 bot/rules/discord_emojis.py create mode 100644 bot/rules/duplicates.py create mode 100644 bot/rules/links.py create mode 100644 bot/rules/mentions.py create mode 100644 bot/rules/newlines.py create mode 100644 bot/rules/role_mentions.py diff --git a/bot/__main__.py b/bot/__main__.py index 9e5806690..9a2ef9319 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -43,6 +43,7 @@ bot.load_extension("bot.cogs.security") bot.load_extension("bot.cogs.events") # Commands, etc +bot.load_extension("bot.cogs.antispam") bot.load_extension("bot.cogs.bigbrother") bot.load_extension("bot.cogs.bot") bot.load_extension("bot.cogs.clean") diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py new file mode 100644 index 000000000..04b030889 --- /dev/null +++ b/bot/cogs/antispam.py @@ -0,0 +1,161 @@ +import asyncio +import logging +import textwrap +from datetime import datetime, timedelta +from typing import Dict, List + +from dateutil.relativedelta import relativedelta +from discord import Member, Message, Object, TextChannel +from discord.ext.commands import Bot + +from bot import rules +from bot.constants import AntiSpam as AntiSpamConfig, Channels, Colours, Guild as GuildConfig, Icons +from bot.utils.time import humanize as humanize_delta + + +log = logging.getLogger(__name__) + +RULE_FUNCTION_MAPPING = { + 'attachments': rules.apply_attachments, + 'burst': rules.apply_burst, + 'burst_shared': rules.apply_burst_shared, + 'chars': rules.apply_chars, + 'discord_emojis': rules.apply_discord_emojis, + 'duplicates': rules.apply_duplicates, + 'links': rules.apply_links, + 'mentions': rules.apply_mentions, + 'newlines': rules.apply_newlines, + 'role_mentions': rules.apply_role_mentions +} + + +class AntiSpam: + def __init__(self, bot: Bot): + self.bot = bot + self.muted_role = None + + async def on_ready(self): + role_id = AntiSpamConfig.punishment['role_id'] + self.muted_role = Object(role_id) + + async def on_message(self, message: Message): + if message.guild.id != GuildConfig.id or message.author.bot: + return + + # Fetch the rule configuration with the highest rule interval. + max_interval_config = max( + AntiSpamConfig.rules.values(), + key=lambda config: config['interval'] + ) + max_interval = max_interval_config['interval'] + + # Store history messages since `interval` seconds ago in a list to prevent unnecessary API calls. + earliest_relevant_at = datetime.utcnow() - timedelta(seconds=max_interval) + relevant_messages = [ + msg async for msg in message.channel.history(after=earliest_relevant_at, reverse=False) + ] + + for rule_name in AntiSpamConfig.rules: + rule_config = AntiSpamConfig.rules[rule_name] + rule_function = RULE_FUNCTION_MAPPING[rule_name] + + # Create a list of messages that were sent in the interval that the rule cares about. + latest_interesting_stamp = datetime.utcnow() - timedelta(seconds=rule_config['interval']) + messages_for_rule = [ + msg for msg in relevant_messages if msg.created_at > latest_interesting_stamp + ] + result = await rule_function(message, messages_for_rule, rule_config) + + # If the rule returns `None`, that means the message didn't violate it. + # If it doesn't, it returns a tuple in the form `(str, Iterable[discord.Member])` + # which contains the reason for why the message violated the rule and + # an iterable of all members that violated the rule. + if result is not None: + reason, members, relevant_messages = result + full_reason = f"`{rule_name}` rule: {reason}" + for member in members: + + # Fire it off as a background task to ensure + # that the sleep doesn't block further tasks + self.bot.loop.create_task( + self.punish(message, member, rule_config, full_reason) + ) + + await self.maybe_delete_messages(message.channel, relevant_messages) + break + + async def punish(self, msg: Message, member: Member, rule_config: Dict[str, int], reason: str): + # Sanity check to ensure we're not lagging behind + if self.muted_role not in member.roles: + remove_role_after = AntiSpamConfig.punishment['remove_after'] + duration_delta = relativedelta(seconds=remove_role_after) + human_duration = humanize_delta(duration_delta) + + mod_alert_channel = self.bot.get_channel(Channels.mod_alerts) + if mod_alert_channel is not None: + await mod_alert_channel.send( + f"<:messagefiltered:473092874289020929> Spam detected in {msg.channel.mention}. " + f"See the message and mod log for further details." + ) + else: + log.warning( + "Tried logging spam event to the mod-alerts channel, but it could not be found." + ) + + await member.add_roles(self.muted_role, reason=reason) + description = textwrap.dedent(f""" + **Channel**: {msg.channel.mention} + **User**: {msg.author.mention} (`{msg.author.id}`) + **Reason**: {reason} + Role will be removed after {human_duration}. + """) + + modlog = self.bot.get_cog('ModLog') + await modlog.send_log_message( + icon_url=Icons.user_mute, colour=Colours.soft_red, + title="User muted", text=description + ) + + await asyncio.sleep(remove_role_after) + await member.remove_roles(self.muted_role, reason="AntiSpam mute expired") + + await modlog.send_log_message( + icon_url=Icons.user_mute, colour=Colours.soft_green, + title="User unmuted", + text=f"Was muted by `AntiSpam` cog for {human_duration}." + ) + + async def maybe_delete_messages(self, channel: TextChannel, messages: List[Message]): + # Is deletion of offending messages actually enabled? + if AntiSpamConfig.clean_offending: + + # If we have more than one message, we can use bulk delete. + if len(messages) > 1: + await channel.delete_messages(messages) + + # Otherwise, the bulk delete endpoint will throw up. + # Delete the message directly instead. + else: + await messages[0].delete() + + +def validate_config(): + for name, config in AntiSpamConfig.rules.items(): + if name not in RULE_FUNCTION_MAPPING: + raise ValueError( + f"Unrecognized antispam rule `{name}`. " + f"Valid rules are: {', '.join(RULE_FUNCTION_MAPPING)}" + ) + + for required_key in ('interval', 'max'): + if required_key not in config: + raise ValueError( + f"`{required_key}` is required but was not " + f"set in rule `{name}`'s configuration." + ) + + +def setup(bot: Bot): + validate_config() + bot.add_cog(AntiSpam(bot)) + log.info("Cog loaded: AntiSpam") diff --git a/bot/constants.py b/bot/constants.py index 58bf62b15..ad6236cd9 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -14,7 +14,7 @@ import logging import os from collections.abc import Mapping from pathlib import Path -from typing import List +from typing import Dict, List import yaml from yaml.constructor import ConstructorError @@ -370,6 +370,13 @@ class URLs(metaclass=YAMLGetter): paste_service: str +class AntiSpam(metaclass=YAMLGetter): + section = 'anti_spam' + + punishment: Dict[str, Dict[str, int]] + rules: Dict[str, Dict[str, int]] + + # Debug mode DEBUG_MODE = True if 'local' in os.environ.get("SITE_URL", "local") else False diff --git a/bot/rules/__init__.py b/bot/rules/__init__.py new file mode 100644 index 000000000..a01ceae73 --- /dev/null +++ b/bot/rules/__init__.py @@ -0,0 +1,12 @@ +# flake8: noqa + +from .attachments import apply as apply_attachments +from .burst import apply as apply_burst +from .burst_shared import apply as apply_burst_shared +from .chars import apply as apply_chars +from .discord_emojis import apply as apply_discord_emojis +from .duplicates import apply as apply_duplicates +from .links import apply as apply_links +from .mentions import apply as apply_mentions +from .newlines import apply as apply_newlines +from .role_mentions import apply as apply_role_mentions diff --git a/bot/rules/attachments.py b/bot/rules/attachments.py new file mode 100644 index 000000000..47b927101 --- /dev/null +++ b/bot/rules/attachments.py @@ -0,0 +1,30 @@ +"""Detects total attachments exceeding the limit sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if ( + msg.author == last_message.author + and len(msg.attachments) > 0 + ) + ) + total_recent_attachments = sum(len(msg.attachments) for msg in relevant_messages) + + if total_recent_attachments > config['max']: + return ( + f"sent {total_recent_attachments} attachments in {config['max']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/burst.py b/bot/rules/burst.py new file mode 100644 index 000000000..80c79be60 --- /dev/null +++ b/bot/rules/burst.py @@ -0,0 +1,27 @@ +"""Detects repeated messages sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + total_relevant = len(relevant_messages) + + if total_relevant > config['max']: + return ( + f"sent {total_relevant} messages in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/burst_shared.py b/bot/rules/burst_shared.py new file mode 100644 index 000000000..2cb7b5200 --- /dev/null +++ b/bot/rules/burst_shared.py @@ -0,0 +1,22 @@ +"""Detects repeated messages sent by multiple users.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + total_recent = len(recent_messages) + + if total_recent > config['max']: + return ( + f"sent {total_recent} messages in {config['interval']}s", + set(msg.author for msg in recent_messages), + recent_messages + ) + return None diff --git a/bot/rules/chars.py b/bot/rules/chars.py new file mode 100644 index 000000000..d05e3cd83 --- /dev/null +++ b/bot/rules/chars.py @@ -0,0 +1,28 @@ +"""Detects total message char count exceeding the limit sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + + total_recent_chars = sum(len(msg.content) for msg in relevant_messages) + + if total_recent_chars > config['max']: + return ( + f"sent {total_recent_chars} characters in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/discord_emojis.py b/bot/rules/discord_emojis.py new file mode 100644 index 000000000..e4f957ddb --- /dev/null +++ b/bot/rules/discord_emojis.py @@ -0,0 +1,35 @@ +"""Detects total Discord emojis (excluding Unicode emojis) exceeding the limit sent by a single user.""" + +import re +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +DISCORD_EMOJI_RE = re.compile(r"<:\w+:\d+>") + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + + total_emojis = sum( + len(DISCORD_EMOJI_RE.findall(msg.content)) + for msg in relevant_messages + ) + + if total_emojis > config['max']: + return ( + f"sent {total_emojis} emojis in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/duplicates.py b/bot/rules/duplicates.py new file mode 100644 index 000000000..763fc9983 --- /dev/null +++ b/bot/rules/duplicates.py @@ -0,0 +1,31 @@ +"""Detects duplicated messages sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if ( + msg.author == last_message.author + and msg.content == last_message.content + ) + ) + + total_duplicated = len(relevant_messages) + + if total_duplicated > config['max']: + return ( + f"sent {total_duplicated} duplicated messages in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/links.py b/bot/rules/links.py new file mode 100644 index 000000000..dfeb38c61 --- /dev/null +++ b/bot/rules/links.py @@ -0,0 +1,31 @@ +"""Detects total links exceeding the limit sent by a single user.""" + +import re +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +LINK_RE = re.compile(r"(https?://[^\s]+)") + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + total_links = sum(len(LINK_RE.findall(msg.content)) for msg in relevant_messages) + + if total_links > config['max']: + return ( + f"sent {total_links} links in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/mentions.py b/bot/rules/mentions.py new file mode 100644 index 000000000..45c47b6ba --- /dev/null +++ b/bot/rules/mentions.py @@ -0,0 +1,28 @@ +"""Detects total mentions exceeding the limit sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + + total_recent_mentions = sum(len(msg.mentions) for msg in relevant_messages) + + if total_recent_mentions > config['max']: + return ( + f"sent {total_recent_mentions} mentions in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/newlines.py b/bot/rules/newlines.py new file mode 100644 index 000000000..a6a1a52d0 --- /dev/null +++ b/bot/rules/newlines.py @@ -0,0 +1,28 @@ +"""Detects total newlines exceeding the set limit sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + + total_recent_newlines = sum(msg.content.count('\n') for msg in relevant_messages) + + if total_recent_newlines > config['max']: + return ( + f"sent {total_recent_newlines} newlines in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/bot/rules/role_mentions.py b/bot/rules/role_mentions.py new file mode 100644 index 000000000..2177a73b5 --- /dev/null +++ b/bot/rules/role_mentions.py @@ -0,0 +1,28 @@ +"""Detects total role mentions exceeding the limit sent by a single user.""" + +from typing import Dict, Iterable, List, Optional, Tuple + +from discord import Member, Message + + +async def apply( + last_message: Message, + recent_messages: List[Message], + config: Dict[str, int] +) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: + + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) + + total_recent_mentions = sum(len(msg.role_mentions) for msg in relevant_messages) + + if total_recent_mentions > config['max']: + return ( + f"sent {total_recent_mentions} role mentions in {config['interval']}s", + (last_message.author,), + relevant_messages + ) + return None diff --git a/config-default.yml b/config-default.yml index 8ef74f6c3..9a4f3d4fc 100644 --- a/config-default.yml +++ b/config-default.yml @@ -57,6 +57,9 @@ style: user_unban: "https://cdn.discordapp.com/emojis/469952898692808704.png" user_update: "https://cdn.discordapp.com/emojis/469952898684551168.png" + user_mute: "https://cdn.discordapp.com/emojis/472472640100106250.png" + user_unmute: "https://cdn.discordapp.com/emojis/472472640100106250.png" + guild: id: 267624335836053506 @@ -78,6 +81,7 @@ guild: help_5: 454941769734422538 helpers: 385474242440986624 message_log: &MESSAGE_LOG 467752170159079424 + mod_alerts: 473092793431097354 modlog: &MODLOG 282638479504965634 off_topic_0: 291284109232308226 off_topic_1: 463035241142026251 @@ -99,7 +103,7 @@ guild: owner: 267627879762755584 verified: 352427296948486144 helpers: 267630620367257601 - muted: 277914926603829249 + muted: &MUTED_ROLE 277914926603829249 keys: @@ -158,3 +162,73 @@ urls: gitlab_bot_repo: "https://gitlab.com/python-discord/projects/bot" omdb: "http://omdbapi.com" paste_service: "https://paste.pydis.com/{key}" + + +anti_spam: + # Clean messages that violate a rule. + clean_offending: true + + punishment: + role_id: *MUTED_ROLE + remove_after: 600 + + rules: + attachments: + interval: 10 + max: 3 + + burst: + interval: 10 + max: 7 + + burst_shared: + interval: 10 + max: 20 + + chars: + interval: 5 + max: 3_000 + + duplicates: + interval: 10 + max: 3 + + discord_emojis: + interval: 10 + max: 6 + + links: + interval: 10 + max: 4 + + mentions: + interval: 10 + max: 5 + + newlines: + interval: 10 + max: 100 + + role_mentions: + interval: 10 + max: 3 + + discord_emojis: + interval: 10 + max: 6 + + links: + interval: 10 + max: 4 + + mentions: + interval: 10 + max: 5 + + newlines: + interval: 10 + max: 20 + + role_mentions: + interval: 10 + max: 3 -- cgit v1.2.3 From d7040fa11b9396f26da37eb1c3b4eaa478315be9 Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Sun, 29 Jul 2018 18:25:15 +0000 Subject: [Events] Basic role persistence --- bot/cogs/events.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++--- bot/constants.py | 1 + 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/bot/cogs/events.py b/bot/cogs/events.py index a7111b8a0..721e51e3d 100644 --- a/bot/cogs/events.py +++ b/bot/cogs/events.py @@ -1,20 +1,24 @@ import logging -from discord import Embed, Member +from discord import Colour, Embed, Member, Object from discord.ext.commands import ( BadArgument, Bot, BotMissingPermissions, CommandError, CommandInvokeError, Context, NoPrivateMessage, UserInputError ) +from bot.cogs.modlog import ModLog from bot.constants import ( - Channels, DEBUG_MODE, Guild, - Keys, Roles, URLs + Channels, Colours, DEBUG_MODE, + Guild, Icons, Keys, + Roles, URLs ) from bot.utils import chunks log = logging.getLogger(__name__) +RESTORE_ROLES = (str(Roles.muted), str(Roles.announcements)) + class Events: """No commands, just event handlers.""" @@ -22,6 +26,10 @@ class Events: def __init__(self, bot: Bot): self.bot = bot + @property + def mod_log(self) -> ModLog: + return self.bot.get_cog("ModLog") + async def send_updated_users(self, *users, replace_all=False): users = list(filter(lambda user: str(Roles.verified) in user["roles"], users)) @@ -85,6 +93,15 @@ class Events: log.exception(f"Failed to delete {len(users)} users") return {} + async def get_user(self, user_id): + response = await self.bot.http_session.get( + url=URLs.site_user_api, + params={"user_id": user_id}, + headers={"X-API-Key": Keys.site_api} + ) + + return await response.json()["data"] + async def on_command_error(self, ctx: Context, e: CommandError): command = ctx.command parent = None @@ -194,6 +211,29 @@ class Events: async def on_member_join(self, member: Member): role_ids = [str(r.id) for r in member.roles] # type: List[str] + new_roles = [] + + try: + user_objs = await self.get_user(str(member.id)) + except Exception as e: + log.exception("Failed to persist roles") + + await self.mod_log.send_log_message( + Icons.crown_red, Colour(Colours.soft_red), "Failed to persist roles", + f"```py\n{e}\n```", + member.avatar_url_as(static_format="png") + ) + else: + if user_objs: + old_roles = user_objs[0].get("roles", []) + + for role in RESTORE_ROLES: + if role in old_roles: + new_roles.append(Object(int(role))) + + for role in new_roles: + if str(role) not in role_ids: + role_ids.append(str(role.id)) changes = await self.send_updated_users({ "avatar": member.avatar_url_as(format="png"), @@ -205,6 +245,18 @@ class Events: log.debug(f"User {member.id} joined; changes: {changes}") + if new_roles: + await member.add_roles( + *new_roles, + reason="Roles restored" + ) + + await self.mod_log.send_log_message( + Icons.crown_blurple, Colour.blurple(), "Roles restored", + f"Restored {len(new_roles)} roles", + member.avatar_url_as(static_format="png") + ) + async def on_member_remove(self, member: Member): changes = await self.send_delete_users({ "user_id": str(member.id) diff --git a/bot/constants.py b/bot/constants.py index ad6236cd9..68155e800 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -301,6 +301,7 @@ class Roles(metaclass=YAMLGetter): devops: int jammer: int moderator: int + muted: int owner: int verified: int muted: int -- cgit v1.2.3 From 221a1944e73da4a2062e2e0db3a794733bbcc50c Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Sun, 29 Jul 2018 20:06:17 +0100 Subject: `'generator' object is not subscriptable` --- bot/cogs/events.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/events.py b/bot/cogs/events.py index 721e51e3d..0b9b75a00 100644 --- a/bot/cogs/events.py +++ b/bot/cogs/events.py @@ -100,7 +100,8 @@ class Events: headers={"X-API-Key": Keys.site_api} ) - return await response.json()["data"] + resp = await response.json() + return resp["data"] async def on_command_error(self, ctx: Context, e: CommandError): command = ctx.command -- cgit v1.2.3 From 367f7a8d853250ee659e0a8ffd30f79ad3abd41c Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 29 Jul 2018 23:06:13 +0200 Subject: Commenting out antispam until we solve a couple of issues with it --- bot/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/__main__.py b/bot/__main__.py index 9a2ef9319..95aee407e 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -43,7 +43,7 @@ bot.load_extension("bot.cogs.security") bot.load_extension("bot.cogs.events") # Commands, etc -bot.load_extension("bot.cogs.antispam") +# bot.load_extension("bot.cogs.antispam") bot.load_extension("bot.cogs.bigbrother") bot.load_extension("bot.cogs.bot") bot.load_extension("bot.cogs.clean") -- cgit v1.2.3 From a66f1c647706ae5bd671b4aa90a989e25182eae3 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 29 Jul 2018 23:40:27 +0200 Subject: Removing the python-levenshtein package because it significantly slows down builds and has almost zero benefit for us. It may provide marginally faster snake disambiguation in the snake cog, but that is certainly not worth the trade-off. --- Pipfile | 1 - Pipfile.lock | 59 ++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Pipfile b/Pipfile index d94234b22..b6f7aeb6f 100644 --- a/Pipfile +++ b/Pipfile @@ -18,7 +18,6 @@ lxml = "*" pyyaml = "*" yarl = "==1.1.1" fuzzywuzzy = "*" -python-levenshtein = "*" pillow = "*" aio-pika = "*" python-dateutil = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 864eb574a..8b43235bb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c7d1bad1549c322484f6751447115ded9299df039cb6321bcc1a1fa1359481dc" + "sha256": "9c22a342245c638b196b519a8afb8a2c66410d76283746cfdd89f19ff7dce94c" }, "pipfile-spec": 6, "requires": { @@ -74,11 +74,13 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:11a9a27b7d3bddc6d86f59fb76afb70e921a25ac2d6cc55b40d072bd68435a76", - "sha256:7015e76bf32f1f574636c4288399a6de66ce08fb7b2457f628a8d70c0fbabb11", - "sha256:808b6ac932dccb0a4126558f7dfdcf41710dd44a4ef497a0bb59a77f9f078e89" + "sha256:2545357585a6cc7d050d3c43a86eba2c0b91b9e7ac8a3965e64a6ead6a1a9a3d", + "sha256:272081ad78c5495ba67083a0e50920163701fa6fe67fbb5eefeb21b5dd88c40b", + "sha256:4ddc90ad88bccc005a71d8ef32f7b1cd8f935475cd561c4122b2f87de45d28ab", + "sha256:5a3d659840960a4107047b6328d6d4cdaaee69939bf11adc07466a1856c99a80", + "sha256:bd43a3b26d2886acd63070c43da821b60dea603eb6d45bab0294aac6129adbfa" ], - "version": "==4.6.0" + "version": "==4.6.1" }, "certifi": { "hashes": [ @@ -247,18 +249,30 @@ "sha256:026449b64e559226cdb8e6d8c931b5965d8fc90ec18ebbb0baa04c5b36503c72", "sha256:03dbb224ee196ef30ed2156d41b579143e1efeb422974719a5392fc035e4f574", "sha256:03eb0e04f929c102ae24bc436bf1c0c60a4e63b07ebd388e84d8b219df3e6acd", + "sha256:087b0551ce2d19b3f092f2b5f071a065f7379e748867d070b29999cc83db15e3", + "sha256:091a0656688d85fd6e10f49a73fa3ab9b37dbfcb2151f5a3ab17f8b879f467ee", + "sha256:0f3e2d0a9966161b7dfd06d147f901d72c3a88ea1a833359b92193b8e1f68e1c", + "sha256:114398d0e073b93e1d7da5b5ab92ff4b83c0180625c8031911425e51f4365d2e", "sha256:1be66b9a89e367e7d20d6cae419794997921fe105090fafd86ef39e20a3baab2", + "sha256:1c5e93c40d4ce8cb133d3b105a869be6fa767e703f6eb1003eb4b90583e08a59", "sha256:1e977a3ed998a599bda5021fb2c2889060617627d3ae228297a529a082a3cd5c", "sha256:22cf3406d135cfcc13ec6228ade774c8461e125c940e80455f500638429be273", "sha256:24adccf1e834f82718c7fc8e3ec1093738da95144b8b1e44c99d5fc7d3e9c554", "sha256:2a3e362c97a5e6a259ee9cd66553292a1f8928a5bdfa3622fdb1501570834612", + "sha256:3518f9fc666cbc58a5c1f48a6a23e9e6ceef69665eab43cdad5144de9383e72c", + "sha256:3709339f4619e8c9b00f53079e40b964f43c5af61fb89a923fe24437167298bb", "sha256:3832e26ecbc9d8a500821e3a1d3765bda99d04ae29ffbb2efba49f5f788dc934", + "sha256:452d159024faf37cc080537df308e8fa0026076eb38eb75185d96ed9642bd6d7", "sha256:4fd1f0c2dc02aaec729d91c92cd85a2df0289d88e9f68d1e8faba750bb9c4786", "sha256:4fda62030f2c515b6e2e673c57caa55cb04026a81968f3128aae10fc28e5cc27", "sha256:5044d75a68b49ce36a813c82d8201384207112d5d81643937fc758c05302f05b", "sha256:522184556921512ec484cb93bd84e0bab915d0ac5a372d49571c241a7f73db62", "sha256:5914cff11f3e920626da48e564be6818831713a3087586302444b9c70e8552d9", + "sha256:653d48fe46378f40e3c2b892be88d8440efbb2c9df78559da44c63ad5ecb4142", "sha256:6661a7908d68c4a133e03dac8178287aa20a99f841ea90beeb98a233ae3fd710", + "sha256:6735a7e560df6f0deb78246a6fe056cf2ae392ba2dc060ea8a6f2535aec924f1", + "sha256:6d26a475a19cb294225738f5c974b3a24599438a67a30ed2d25638f012668026", + "sha256:791f07fe13937e65285f9ef30664ddf0e10a0230bdb236751fa0ca67725740dd", "sha256:79258a8df3e309a54c7ef2ef4a59bb8e28f7e4a8992a3ad17c24b1889ced44f3", "sha256:7d74c20b8f1c3e99d3f781d3b8ff5abfefdd7363d61e23bdeba9992ff32cc4b4", "sha256:81918afeafc16ba5d9d0d4e9445905f21aac969a4ebb6f2bff4b9886da100f4b", @@ -266,12 +280,20 @@ "sha256:84d5d31200b11b3c76fab853b89ac898bf2d05c8b3da07c1fcc23feb06359d6e", "sha256:989981db57abffb52026b114c9a1f114c7142860a6d30a352d28f8cbf186500b", "sha256:a3d7511d3fad1618a82299aab71a5fceee5c015653a77ffea75ced9ef917e71a", + "sha256:a4a6ac01b8c2f9d2d83719f193e6dea493e18445ce5bfd743d739174daa974d9", + "sha256:acb90eb6c7ed6526551a78211d84c81e33082a35642ff5fe57489abc14e6bf6e", "sha256:b3ef168d4d6fd4fa6685aef7c91400f59f7ab1c0da734541f7031699741fb23f", "sha256:c1c5792b6e74bbf2af0f8e892272c2a6c48efa895903211f11b8342e03129fea", "sha256:c5dcb5a56aebb8a8f2585042b2f5c496d7624f0bcfe248f0cc33ceb2fd8d39e7", + "sha256:d16f90810106822833a19bdb24c7cb766959acf791ca0edf5edfec674d55c8ee", + "sha256:dcdc9cd9880027688007ff8f7c8e7ae6f24e81fae33bfd18d1e691e7bda4855f", + "sha256:e2807aad4565d8de15391a9548f97818a14ef32624015c7bf3095171e314445e", "sha256:e2bed4a04e2ca1050bb5f00865cf2f83c0b92fd62454d9244f690fcd842e27a4", "sha256:e87a527c06319428007e8c30511e1f0ce035cb7f14bb4793b003ed532c3b9333", + "sha256:ebcfc33a6c34984086451e230253bc33727bd17b4cdc4b39ec03032c3a6fc9e9", "sha256:f63e420180cbe22ff6e32558b612e75f50616fc111c5e095a4631946c782e109", + "sha256:f7717eb360d40e7598c30cc44b33d98f79c468d9279379b66c1e28c568e0bf47", + "sha256:f8582e1ab155302ea9ef1235441a0214919f4f79c4c7c21833ce9eec58181781", "sha256:f8b3d413c5a8f84b12cd4c5df1d8e211777c9852c6be3ee9c094b626644d3eab" ], "index": "pypi", @@ -313,6 +335,11 @@ "pyparsing": { "hashes": [ "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", + "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", + "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", + "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", + "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", + "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" ], "version": "==2.2.0" @@ -330,16 +357,8 @@ "sha256:a292e22c5e03105a05a746ade6209d43db1c4c763b91c75c8486e81d10904d85", "sha256:e3636824d35ba6a15fc39f573588cba63cf46322a5dc86fb2f280229077e9fbe" ], - "markers": "python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'", "version": "==0.1.9" }, - "python-levenshtein": { - "hashes": [ - "sha256:033a11de5e3d19ea25c9302d11224e1a1898fe5abd23c61c7c360c25195e3eb1" - ], - "index": "pypi", - "version": "==0.12.0" - }, "pytz": { "hashes": [ "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", @@ -404,7 +423,6 @@ "sha256:68ca7ff70785cbe1e7bccc71a48b5b6d965d79ca50629606c7861a21b206d9dd", "sha256:9de47f375baf1ea07cdb3436ff39d7a9c76042c10a769c52353ec46e4e8fc3b9" ], - "markers": "python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'", "version": "==1.1.0" }, "sympy": { @@ -419,7 +437,6 @@ "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" ], - "markers": "python_version != '3.0.*' and python_version < '4' and python_version != '3.3.*' and python_version != '3.2.*' and python_version >= '2.6' and python_version != '3.1.*'", "version": "==1.23" }, "websockets": { @@ -592,6 +609,11 @@ "pyparsing": { "hashes": [ "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", + "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", + "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", + "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", + "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", + "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" ], "version": "==2.2.0" @@ -622,11 +644,11 @@ }, "safety": { "hashes": [ - "sha256:32d41b8bbd736db749aa2162de6c0bb11c2113c7bc0357476491f96cd5d58299", - "sha256:34227360409ffb1bc2657e5b6ff3472a32d72b917617cd3d2914ddf078c263b9" + "sha256:2689fe629bafe9450796d36578aa112820ff65038578aee004f60b9db1ba4ae8", + "sha256:cd04e57ff8cf8984ff2cb11973e1d5469dae681e25d4edfccb1ef08cc107b2c0" ], "index": "pypi", - "version": "==1.8.2" + "version": "==1.8.3" }, "six": { "hashes": [ @@ -640,7 +662,6 @@ "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" ], - "markers": "python_version != '3.0.*' and python_version < '4' and python_version != '3.3.*' and python_version != '3.2.*' and python_version >= '2.6' and python_version != '3.1.*'", "version": "==1.23" } } -- cgit v1.2.3