diff options
| author | 2019-02-17 13:00:41 +0100 | |
|---|---|---|
| committer | 2019-02-17 13:00:41 +0100 | |
| commit | e499e5b07641ff3dad211b98a67080e593d94c79 (patch) | |
| tree | 09367ed3b369efb2d290a6584e65cb2d3d60fc52 | |
| parent | Changing negative bot response in nominate command to be more explicit about ... (diff) | |
| parent | Merge pull request #311 from python-discord/add-team-creator (diff) | |
Merge branch 'master' into bb-improvements
Diffstat (limited to '')
| -rw-r--r-- | bot/__main__.py | 12 | ||||
| -rw-r--r-- | bot/cogs/alias.py | 16 | ||||
| -rw-r--r-- | bot/cogs/filtering.py | 52 | ||||
| -rw-r--r-- | bot/cogs/jams.py | 83 | ||||
| -rw-r--r-- | bot/cogs/modlog.py | 4 | ||||
| -rw-r--r-- | bot/cogs/rules.py | 102 | ||||
| -rw-r--r-- | bot/cogs/site.py | 16 | ||||
| -rw-r--r-- | bot/constants.py | 1 | ||||
| -rw-r--r-- | config-default.yml | 4 | 
9 files changed, 252 insertions, 38 deletions
diff --git a/bot/__main__.py b/bot/__main__.py index c9d6fd61e..6928a3960 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -38,11 +38,11 @@ else:      log.warning("Timed out while waiting for RabbitMQ")  # Internal/debug -bot.load_extension("bot.cogs.logging") -bot.load_extension("bot.cogs.security")  bot.load_extension("bot.cogs.events")  bot.load_extension("bot.cogs.filtering") +bot.load_extension("bot.cogs.logging")  bot.load_extension("bot.cogs.modlog") +bot.load_extension("bot.cogs.security")  # Commands, etc  bot.load_extension("bot.cogs.antispam") @@ -51,6 +51,7 @@ bot.load_extension("bot.cogs.bot")  bot.load_extension("bot.cogs.clean")  bot.load_extension("bot.cogs.cogs")  bot.load_extension("bot.cogs.help") +bot.load_extension("bot.cogs.rules")  # Only load this in production  if not DEBUG_MODE: @@ -59,12 +60,13 @@ if not DEBUG_MODE:  # Feature cogs  bot.load_extension("bot.cogs.alias") -bot.load_extension("bot.cogs.deployment")  bot.load_extension("bot.cogs.defcon") +bot.load_extension("bot.cogs.deployment")  bot.load_extension("bot.cogs.eval") +bot.load_extension("bot.cogs.free")  bot.load_extension("bot.cogs.fun") -bot.load_extension("bot.cogs.superstarify")  bot.load_extension("bot.cogs.information") +bot.load_extension("bot.cogs.jams")  bot.load_extension("bot.cogs.moderation")  bot.load_extension("bot.cogs.off_topic_names")  bot.load_extension("bot.cogs.reddit") @@ -72,11 +74,11 @@ bot.load_extension("bot.cogs.reminders")  bot.load_extension("bot.cogs.site")  bot.load_extension("bot.cogs.snakes")  bot.load_extension("bot.cogs.snekbox") +bot.load_extension("bot.cogs.superstarify")  bot.load_extension("bot.cogs.tags")  bot.load_extension("bot.cogs.token_remover")  bot.load_extension("bot.cogs.utils")  bot.load_extension("bot.cogs.wolfram") -bot.load_extension("bot.cogs.free")  if has_rmq:      bot.load_extension("bot.cogs.rmq") diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index 23562ad25..bf59b6d15 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -103,14 +103,6 @@ class Alias:          await self.invoke(ctx, "site faq") -    @command(name="rules", hidden=True) -    async def site_rules_alias(self, ctx): -        """ -        Alias for invoking <prefix>site rules. -        """ - -        await self.invoke(ctx, "site rules") -      @command(name="reload", hidden=True)      async def cogs_reload_alias(self, ctx, *, cog_name: str):          """ @@ -137,6 +129,14 @@ class Alias:          await self.invoke(ctx, "defcon disable") +    @command(name="exception", hidden=True) +    async def tags_get_traceback_alias(self, ctx): +        """ +        Alias for invoking <prefix>tags get traceback. +        """ + +        await self.invoke(ctx, "tags get traceback") +      @group(name="get",             aliases=("show", "g"),             hidden=True, diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 6b4469ceb..25aaf8420 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -1,6 +1,6 @@  import logging  import re -from typing import Optional +from typing import Optional, Union  import discord.errors  from dateutil.relativedelta import relativedelta @@ -189,7 +189,25 @@ class Filtering:                          log.debug(message) -                        additional_embeds = msg.embeds if filter_name == "watch_rich_embeds" else None +                        additional_embeds = None +                        additional_embeds_msg = None + +                        if filter_name == "filter_invites": +                            additional_embeds = [] +                            for invite, data in triggered.items(): +                                embed = discord.Embed(description=( +                                    f"**Members:**\n{data['members']}\n" +                                    f"**Active:**\n{data['active']}" +                                )) +                                embed.set_author(name=data["name"]) +                                embed.set_thumbnail(url=data["icon"]) +                                embed.set_footer(text=f"Guild Invite Code: {invite}") +                                additional_embeds.append(embed) +                            additional_embeds_msg = "For the following guild(s):" + +                        elif filter_name == "watch_rich_embeds": +                            additional_embeds = msg.embeds +                            additional_embeds_msg = "With the following embed(s):"                          # Send pretty mod log embed to mod-alerts                          await self.mod_log.send_log_message( @@ -201,6 +219,7 @@ class Filtering:                              channel_id=Channels.mod_alerts,                              ping_everyone=Filter.ping_everyone,                              additional_embeds=additional_embeds, +                            additional_embeds_msg=additional_embeds_msg                          )                          break  # We don't want multiple filters to trigger @@ -282,10 +301,12 @@ class Filtering:          return bool(re.search(ZALGO_RE, text)) -    async def _has_invites(self, text: str) -> bool: +    async def _has_invites(self, text: str) -> Union[dict, bool]:          """ -        Returns True if the text contains an invite which is not on the guild_invite_whitelist in -        config.yml +        Checks if there's any invites in the text content that aren't in the guild whitelist. + +        If any are detected, a dictionary of invite data is returned, with a key per invite. +        If none are detected, False is returned.          Attempts to catch some of common ways to try to cheat the system.          """ @@ -295,10 +316,13 @@ class Filtering:          text = text.replace("\\", "")          invites = re.findall(INVITE_RE, text, re.IGNORECASE) +        invite_data = dict()          for invite in invites: +            if invite in invite_data: +                continue              response = await self.bot.http_session.get( -                f"{URLs.discord_invite_api}/{invite}" +                f"{URLs.discord_invite_api}/{invite}", params={"with_counts": "true"}              )              response = await response.json()              guild = response.get("guild") @@ -311,8 +335,20 @@ class Filtering:              guild_id = int(guild.get("id"))              if guild_id not in Filter.guild_invite_whitelist: -                return True -        return False +                guild_icon_hash = guild["icon"] +                guild_icon = ( +                    "https://cdn.discordapp.com/icons/" +                    f"{guild_id}/{guild_icon_hash}.png?size=512" +                ) + +                invite_data[invite] = { +                    "name": guild["name"], +                    "icon": guild_icon, +                    "members": response["approximate_member_count"], +                    "active": response["approximate_presence_count"] +                } + +        return invite_data if invite_data else False      @staticmethod      async def _has_rich_embed(msg: Message): diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py new file mode 100644 index 000000000..e68aa3d2f --- /dev/null +++ b/bot/cogs/jams.py @@ -0,0 +1,83 @@ +import logging + +from discord import Member, PermissionOverwrite, utils +from discord.ext import commands + +from bot.constants import Roles +from bot.decorators import with_role + +log = logging.getLogger(__name__) + + +class CodeJams: +    """ +    Manages the code-jam related parts of our server +    """ + +    def __init__(self, bot: commands.Bot): +        self.bot = bot + +    @commands.command() +    @with_role(Roles.admin) +    async def createteam( +        self, ctx: commands.Context, +        team_name: str, members: commands.Greedy[Member] +    ): +        """ +        Create a team channel in the Code Jams category, assign roles and then add +        overwrites for the team. + +        The first user passed will always be the team leader. +        """ +        code_jam_category = utils.get(ctx.guild.categories, name="Code Jam") + +        if code_jam_category is None: +            log.info("Code Jam category not found, creating it.") + +            category_overwrites = { +                ctx.guild.default_role: PermissionOverwrite(read_messages=False), +                ctx.guild.me: PermissionOverwrite(read_messages=True) +            } + +            code_jam_category = await ctx.guild.create_category_channel( +                "Code Jam", +                overwrites=category_overwrites, +                reason="It's code jam time!" +            ) + +        # First member is always the team leader +        team_channel_overwrites = { +            members[0]: PermissionOverwrite( +                manage_messages=True, +                read_messages=True, +                manage_webhooks=True +            ), +            ctx.guild.default_role: PermissionOverwrite(read_messages=False), +            ctx.guild.get_role(Roles.developer): PermissionOverwrite(read_messages=False) +        } + +        # Rest of members should just have read_messages +        for member in members[1:]: +            team_channel_overwrites[member] = PermissionOverwrite(read_messages=True) + +        # Create a channel for the team +        team_channel = await ctx.guild.create_text_channel( +            team_name, +            overwrites=team_channel_overwrites, +            category=code_jam_category +        ) + +        # Assign team leader role +        await members[0].add_roles(ctx.guild.get_role(Roles.team_leader)) + +        # Assign rest of roles +        jammer_role = ctx.guild.get_role(Roles.jammer) +        for member in members: +            await member.add_roles(jammer_role) + +        await ctx.send(f":ok_hand: Team created: {team_channel.mention}") + + +def setup(bot): +    bot.add_cog(CodeJams(bot)) +    log.info("Cog loaded: CodeJams") diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py index 495795b6d..65efda5ed 100644 --- a/bot/cogs/modlog.py +++ b/bot/cogs/modlog.py @@ -115,6 +115,7 @@ class ModLog:              files: Optional[List[File]] = None,              content: Optional[str] = None,              additional_embeds: Optional[List[Embed]] = None, +            additional_embeds_msg: Optional[str] = None,              timestamp_override: Optional[datetime.datetime] = None,              footer: Optional[str] = None,      ): @@ -143,7 +144,8 @@ class ModLog:          log_message = await channel.send(content=content, embed=embed, files=files)          if additional_embeds: -            await channel.send("With the following embed(s):") +            if additional_embeds_msg: +                await channel.send(additional_embeds_msg)              for additional_embed in additional_embeds:                  await channel.send(embed=additional_embed) diff --git a/bot/cogs/rules.py b/bot/cogs/rules.py new file mode 100644 index 000000000..eee506810 --- /dev/null +++ b/bot/cogs/rules.py @@ -0,0 +1,102 @@ +import re +from typing import Optional + +from discord import Colour, Embed +from discord.ext.commands import Bot, Context, command + +from bot.constants import Channels, Roles +from bot.decorators import redirect_output +from bot.pagination import LinePaginator + +STAFF = Roles.admin, Roles.moderator, Roles.owner + + +class Rules: + +    def __init__(self, bot: Bot): +        self.bot = bot + +        # We'll get the rules from the API when the +        # site has been updated to the Django Framework. +        # Hard-code the rules for now until the new RulesView is released. + +        self.rules = ( +            "Be polite, and do not spam.", + +            "Follow the [Discord Community Guidelines](https://discordapp.com/guidelines).", + +            "Don't intentionally make other people uncomfortable - if someone asks you to stop " +            "discussing something, you should stop.", + +            "Be patient both with users asking questions, and the users answering them.", + +            "We will not help you with anything that might break a law or the terms of service " +            "of any other community, site, service, or otherwise - No piracy, brute-forcing, " +            "captcha circumvention, sneaker bots, or anything else of that nature.", + +            "Listen to and respect the staff members - we're here to help, but we're all human " +            "beings.", + +            "All discussion should be kept within the relevant channels for the subject - See the " +            "[channels page](https://pythondiscord.com/about/channels) for more information.", + +            "This is an English-speaking server, so please speak English to the best of your " +            "ability - [Google Translate](https://translate.google.com/) should be fine if you're " +            "not sure.", + +            "Keep all discussions safe for work - No gore, nudity, sexual soliciting, references " +            "to suicide, or anything else of that nature", + +            "We do not allow advertisements for communities (including other Discord servers) or " +            "commercial projects - Contact us directly if you want to discuss a partnership!" +        ) +        self.default_desc = ("The rules and guidelines that apply to this community can be found on" +                             " our [rules page](https://pythondiscord.com/about/rules). We expect" +                             " all members of the community to have read and understood these." +                             ) + +    @command(aliases=['r', 'rule'], name='rules') +    @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF) +    async def rules_command(self, ctx: Context, *, rules: Optional[str] = None): +        """ +        Provides a link to the `rules` endpoint of the website, or displays +        specific rules, if they are requested. + +        **`ctx`:** The Discord message context +        **`rules`:** The rules a user wants to get. +        """ +        rules_embed = Embed(title='Rules', color=Colour.blurple()) +        rules_embed.set_footer(text='https://pythondiscord.com/about/rules') + +        if not rules: +            # Rules were not submitted. Return the default description. +            rules_embed.description = self.default_desc +            return await ctx.send(embed=rules_embed) + +        # Split the rules input by slash, comma or space +        # Returns a list of ints if they're in range of rules index +        rules_to_get = [] +        split_rules = re.split(r'[/, ]', rules) +        for item in split_rules: +            if not item.isdigit(): +                if not item: +                    continue +                rule_match = re.search(r'\d?\d[:|-]1?\d', item) +                if rule_match: +                    a, b = sorted([int(x)-1 for x in re.split(r'[:-]', rule_match.group())]) +                    rules_to_get.extend(range(a, b+1)) +            else: +                rules_to_get.append(int(item)-1) +        final_rules = [ +            f'**{i+1}.** {self.rules[i]}' for i in sorted(rules_to_get) if i < len(self.rules) +        ] + +        if not final_rules: +            # No valid rules in rules input. Return the default description. +            rules_embed.description = self.default_desc +            return await ctx.send(embed=rules_embed) +        await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) + + +def setup(bot): +    bot.add_cog(Rules(bot)) diff --git a/bot/cogs/site.py b/bot/cogs/site.py index 442e80cd2..e5fd645fb 100644 --- a/bot/cogs/site.py +++ b/bot/cogs/site.py @@ -92,22 +92,6 @@ class Site:          await ctx.send(embed=embed) -    @site_group.command(name="rules") -    async def site_rules(self, ctx: Context): -        """Info about the server's rules.""" - -        url = f"{URLs.site_schema}{URLs.site}/about/rules" - -        embed = Embed(title="Rules") -        embed.set_footer(text=url) -        embed.colour = Colour.blurple() -        embed.description = ( -            f"The rules and guidelines that apply to this community can be found on our [rules page]({url}). " -            "We expect all members of the community to have read and understood these." -        ) - -        await ctx.send(embed=embed) -  def setup(bot):      bot.add_cog(Site(bot)) diff --git a/bot/constants.py b/bot/constants.py index ab62cd79d..5b9b45c1c 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -372,6 +372,7 @@ class Roles(metaclass=YAMLGetter):      owner: int      verified: int      helpers: int +    team_leader: int  class Guild(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index 3ce01916e..110dd12dd 100644 --- a/config-default.yml +++ b/config-default.yml @@ -134,6 +134,7 @@ guild:          verified:                           352427296948486144          helpers:                            267630620367257601          rockstars:         &ROCKSTARS_ROLE  458226413825294336 +        team_leader:                        501324292341104650  filter: @@ -162,6 +163,9 @@ filter:          - 273944235143593984  # STEM          - 348658686962696195  # RLBot          - 531221516914917387  # Pallets +        - 249111029668249601  # Gentoo +        - 327254708534116352  # Adafruit +        - 544525886180032552  # kennethreitz.org      domain_blacklist:          - pornhub.com  |