diff options
| author | 2023-04-11 17:35:14 +0300 | |
|---|---|---|
| committer | 2023-04-11 14:35:14 +0000 | |
| commit | e94a53cbc97fa42340660953627a3593b5c191e9 (patch) | |
| tree | 65d1b65891da1b0850fedd08938a09ddd3c96b4f | |
| parent | Bump pytest from 7.2.2 to 7.3.0 (#2525) (diff) | |
Remove dead or unused code (#2528)
Diffstat (limited to '')
| -rw-r--r-- | bot/constants.py | 11 | ||||
| -rw-r--r-- | bot/exts/events/__init__.py | 0 | ||||
| -rw-r--r-- | bot/exts/events/code_jams/__init__.py | 8 | ||||
| -rw-r--r-- | bot/exts/events/code_jams/_channels.py | 113 | ||||
| -rw-r--r-- | bot/exts/events/code_jams/_cog.py | 239 | ||||
| -rw-r--r-- | bot/rules/mentions.py | 70 | ||||
| -rw-r--r-- | tests/bot/exts/events/__init__.py | 0 | ||||
| -rw-r--r-- | tests/bot/exts/events/test_code_jams.py | 170 | 
8 files changed, 0 insertions, 611 deletions
| diff --git a/bot/constants.py b/bot/constants.py index eefae11e2..4b3f6a6a7 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -326,17 +326,6 @@ class _Colours(EnvConfig):  Colours = _Colours() -class _Free(EnvConfig): -    EnvConfig.Config.env_prefix = "free_" - -    activity_timeout = 600 -    cooldown_per = 60.0 -    cooldown_rate = 1 - - -Free = _Free() - -  class _HelpChannels(EnvConfig):      EnvConfig.Config.env_prefix = "help_channels_" diff --git a/bot/exts/events/__init__.py b/bot/exts/events/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/bot/exts/events/__init__.py +++ /dev/null diff --git a/bot/exts/events/code_jams/__init__.py b/bot/exts/events/code_jams/__init__.py deleted file mode 100644 index 2f858d1f9..000000000 --- a/bot/exts/events/code_jams/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from bot.bot import Bot - - -async def setup(bot: Bot) -> None: -    """Load the CodeJams cog.""" -    from bot.exts.events.code_jams._cog import CodeJams - -    await bot.add_cog(CodeJams(bot)) diff --git a/bot/exts/events/code_jams/_channels.py b/bot/exts/events/code_jams/_channels.py deleted file mode 100644 index e8cf5f7bf..000000000 --- a/bot/exts/events/code_jams/_channels.py +++ /dev/null @@ -1,113 +0,0 @@ -import typing as t - -import discord - -from bot.constants import Categories, Channels, Roles -from bot.log import get_logger - -log = get_logger(__name__) - -MAX_CHANNELS = 50 -CATEGORY_NAME = "Code Jam" - - -async def _get_category(guild: discord.Guild) -> discord.CategoryChannel: -    """ -    Return a code jam category. - -    If all categories are full or none exist, create a new category. -    """ -    for category in guild.categories: -        if category.name == CATEGORY_NAME and len(category.channels) < MAX_CHANNELS: -            return category - -    return await _create_category(guild) - - -async def _create_category(guild: discord.Guild) -> discord.CategoryChannel: -    """Create a new code jam category and return it.""" -    log.info("Creating a new code jam category.") - -    category_overwrites = { -        guild.default_role: discord.PermissionOverwrite(read_messages=False), -        guild.me: discord.PermissionOverwrite(read_messages=True) -    } - -    category = await guild.create_category_channel( -        CATEGORY_NAME, -        overwrites=category_overwrites, -        reason="It's code jam time!" -    ) - -    await _send_status_update( -        guild, f"Created a new category with the ID {category.id} for this Code Jam's team channels." -    ) - -    return category - - -def _get_overwrites( -        members: list[tuple[discord.Member, bool]], -        guild: discord.Guild, -) -> dict[t.Union[discord.Member, discord.Role], discord.PermissionOverwrite]: -    """Get code jam team channels permission overwrites.""" -    team_channel_overwrites = { -        guild.default_role: discord.PermissionOverwrite(read_messages=False), -        guild.get_role(Roles.code_jam_event_team): discord.PermissionOverwrite(read_messages=True) -    } - -    for member, _ in members: -        team_channel_overwrites[member] = discord.PermissionOverwrite( -            read_messages=True -        ) - -    return team_channel_overwrites - - -async def create_team_channel( -        guild: discord.Guild, -        team_name: str, -        members: list[tuple[discord.Member, bool]], -        team_leaders: discord.Role -) -> None: -    """Create the team's text channel.""" -    await _add_team_leader_roles(members, team_leaders) - -    # Get permission overwrites and category -    team_channel_overwrites = _get_overwrites(members, guild) -    code_jam_category = await _get_category(guild) - -    # Create a text channel for the team -    await code_jam_category.create_text_channel( -        team_name, -        overwrites=team_channel_overwrites, -    ) - - -async def create_team_leader_channel(guild: discord.Guild, team_leaders: discord.Role) -> None: -    """Create the Team Leader Chat channel for the Code Jam team leaders.""" -    category: discord.CategoryChannel = guild.get_channel(Categories.summer_code_jam) - -    team_leaders_chat = await category.create_text_channel( -        name="team-leaders-chat", -        overwrites={ -            guild.default_role: discord.PermissionOverwrite(read_messages=False), -            team_leaders: discord.PermissionOverwrite(read_messages=True) -        } -    ) - -    await _send_status_update(guild, f"Created {team_leaders_chat.mention} in the {category} category.") - - -async def _send_status_update(guild: discord.Guild, message: str) -> None: -    """Inform the events lead with a status update when the command is ran.""" -    channel: discord.TextChannel = guild.get_channel(Channels.code_jam_planning) - -    await channel.send(f"<@&{Roles.events_lead}>\n\n{message}") - - -async def _add_team_leader_roles(members: list[tuple[discord.Member, bool]], team_leaders: discord.Role) -> None: -    """Assign the team leader role to the team leaders.""" -    for member, is_leader in members: -        if is_leader: -            await member.add_roles(team_leaders) diff --git a/bot/exts/events/code_jams/_cog.py b/bot/exts/events/code_jams/_cog.py deleted file mode 100644 index 86c357863..000000000 --- a/bot/exts/events/code_jams/_cog.py +++ /dev/null @@ -1,239 +0,0 @@ -import asyncio -import csv -import typing as t -from collections import defaultdict - -import discord -from discord import Colour, Embed, Guild, Member -from discord.ext import commands - -from bot.bot import Bot -from bot.constants import Emojis, Roles -from bot.exts.events.code_jams import _channels -from bot.log import get_logger -from bot.utils.members import get_or_fetch_member -from bot.utils.services import PasteTooLongError, PasteUploadError, send_to_paste_service - -log = get_logger(__name__) - -TEAM_LEADERS_COLOUR = 0x11806a -DELETION_REACTION = "\U0001f4a5" - - -class CodeJams(commands.Cog): -    """Manages the code-jam related parts of our server.""" - -    def __init__(self, bot: Bot): -        self.bot = bot - -    @commands.group(aliases=("cj", "jam")) -    @commands.has_any_role(Roles.admins) -    async def codejam(self, ctx: commands.Context) -> None: -        """A Group of commands for managing Code Jams.""" -        if ctx.invoked_subcommand is None: -            await ctx.send_help(ctx.command) - -    @codejam.command() -    async def create(self, ctx: commands.Context, csv_file: t.Optional[str] = None) -> None: -        """ -        Create code-jam teams from a CSV file or a link to one, specifying the team names, leaders and members. - -        The CSV file must have 3 columns: 'Team Name', 'Team Member Discord ID', and 'Team Leader'. - -        This will create the text channels for the teams, and give the team leaders their roles. -        """ -        async with ctx.typing(): -            if csv_file: -                async with self.bot.http_session.get(csv_file) as response: -                    if response.status != 200: -                        await ctx.send(f"Got a bad response from the URL: {response.status}") -                        return - -                    csv_file = await response.text() - -            elif ctx.message.attachments: -                csv_file = (await ctx.message.attachments[0].read()).decode("utf8") -            else: -                raise commands.BadArgument("You must include either a CSV file or a link to one.") - -            teams = defaultdict(list) -            reader = csv.DictReader(csv_file.splitlines()) - -            for row in reader: -                member = await get_or_fetch_member(ctx.guild, int(row["Team Member Discord ID"])) - -                if member is None: -                    log.trace(f"Got an invalid member ID: {row['Team Member Discord ID']}") -                    continue - -                teams[row["Team Name"]].append((member, row["Team Leader"].upper() == "Y")) - -            team_leaders = await ctx.guild.create_role(name="Code Jam Team Leaders", colour=TEAM_LEADERS_COLOUR) - -            for team_name, team_members in teams.items(): -                await _channels.create_team_channel(ctx.guild, team_name, team_members, team_leaders) - -            await _channels.create_team_leader_channel(ctx.guild, team_leaders) -            await ctx.send(f"{Emojis.check_mark} Created Code Jam with {len(teams)} teams.") - -    @codejam.command() -    @commands.has_any_role(Roles.admins) -    async def end(self, ctx: commands.Context) -> None: -        """ -        Delete all code jam channels. - -        A confirmation message is displayed with the categories and channels to be deleted.. Pressing the added reaction -        deletes those channels. -        """ -        def predicate_deletion_emoji_reaction(reaction: discord.Reaction, user: discord.User) -> bool: -            """Return True if the reaction :boom: was added by the context message author on this message.""" -            return ( -                reaction.message.id == message.id -                and user.id == ctx.author.id -                and str(reaction) == DELETION_REACTION -            ) - -        # A copy of the list of channels is stored. This is to make sure that we delete precisely the channels displayed -        # in the confirmation message. -        categories = self.jam_categories(ctx.guild) -        category_channels = {category: category.channels.copy() for category in categories} - -        confirmation_message = await self._build_confirmation_message(category_channels) -        message = await ctx.send(confirmation_message) -        await message.add_reaction(DELETION_REACTION) -        try: -            await self.bot.wait_for( -                'reaction_add', -                check=predicate_deletion_emoji_reaction, -                timeout=10 -            ) - -        except asyncio.TimeoutError: -            await message.clear_reaction(DELETION_REACTION) -            await ctx.send("Command timed out.", reference=message) -            return - -        else: -            await message.clear_reaction(DELETION_REACTION) -            for category, channels in category_channels.items(): -                for channel in channels: -                    await channel.delete(reason="Code jam ended.") -                await category.delete(reason="Code jam ended.") - -            await message.add_reaction(Emojis.check_mark) - -    @staticmethod -    async def _build_confirmation_message( -        categories: dict[discord.CategoryChannel, list[discord.abc.GuildChannel]] -    ) -> str: -        """Sends details of the channels to be deleted to the pasting service, and formats the confirmation message.""" -        def channel_repr(channel: discord.abc.GuildChannel) -> str: -            """Formats the channel name and ID and a readable format.""" -            return f"{channel.name} ({channel.id})" - -        def format_category_info(category: discord.CategoryChannel, channels: list[discord.abc.GuildChannel]) -> str: -            """Displays the category and the channels within it in a readable format.""" -            return f"{channel_repr(category)}:\n" + "\n".join("  - " + channel_repr(channel) for channel in channels) - -        deletion_details = "\n\n".join( -            format_category_info(category, channels) for category, channels in categories.items() -        ) - -        try: -            message = await send_to_paste_service(deletion_details) -        except PasteTooLongError: -            message = "**Too long to upload to paste service.**" -        except PasteUploadError: -            message = "**Failed to upload to paste service.**" - -        return f"Are you sure you want to delete all code jam channels?\n\nThe channels to be deleted: {message}" - -    @codejam.command() -    @commands.has_any_role(Roles.admins, Roles.code_jam_event_team) -    async def info(self, ctx: commands.Context, member: Member) -> None: -        """ -        Send an info embed about the member with the team they're in. - -        The team is found by searching the permissions of the team channels. -        """ -        channel = self.team_channel(ctx.guild, member) -        if not channel: -            await ctx.send(":x: I can't find the team channel for this member.") -            return - -        embed = Embed( -            title=str(member), -            colour=Colour.og_blurple() -        ) -        embed.add_field(name="Team", value=self.team_name(channel), inline=True) - -        await ctx.send(embed=embed) - -    @codejam.command() -    @commands.has_any_role(Roles.admins) -    async def move(self, ctx: commands.Context, member: Member, new_team_name: str) -> None: -        """Move participant from one team to another by changing the user's permissions for the relevant channels.""" -        old_team_channel = self.team_channel(ctx.guild, member) -        if not old_team_channel: -            await ctx.send(":x: I can't find the team channel for this member.") -            return - -        if old_team_channel.name == new_team_name or self.team_name(old_team_channel) == new_team_name: -            await ctx.send(f"`{member}` is already in `{new_team_name}`.") -            return - -        new_team_channel = self.team_channel(ctx.guild, new_team_name) -        if not new_team_channel: -            await ctx.send(f":x: I can't find a team channel named `{new_team_name}`.") -            return - -        await old_team_channel.set_permissions(member, overwrite=None, reason=f"Participant moved to {new_team_name}") -        await new_team_channel.set_permissions( -            member, -            overwrite=discord.PermissionOverwrite(read_messages=True), -            reason=f"Participant moved from {old_team_channel.name}" -        ) - -        await ctx.send( -            f"Participant moved from `{self.team_name(old_team_channel)}` to `{self.team_name(new_team_channel)}`." -        ) - -    @codejam.command() -    @commands.has_any_role(Roles.admins) -    async def remove(self, ctx: commands.Context, member: Member) -> None: -        """Remove the participant from their team. Does not remove the participants or leader roles.""" -        channel = self.team_channel(ctx.guild, member) -        if not channel: -            await ctx.send(":x: I can't find the team channel for this member.") -            return - -        await channel.set_permissions( -            member, -            overwrite=None, -            reason=f"Participant removed from the team  {self.team_name(channel)}." -        ) -        await ctx.send(f"Removed the participant from `{self.team_name(channel)}`.") - -    @staticmethod -    def jam_categories(guild: Guild) -> list[discord.CategoryChannel]: -        """Get all the code jam team categories.""" -        return [category for category in guild.categories if category.name == _channels.CATEGORY_NAME] - -    @staticmethod -    def team_channel(guild: Guild, criterion: t.Union[str, Member]) -> t.Optional[discord.TextChannel]: -        """Get a team channel through either a participant or the team name.""" -        for category in CodeJams.jam_categories(guild): -            for channel in category.channels: -                if isinstance(channel, discord.TextChannel): -                    if ( -                        # If it's a string. -                        criterion == channel.name or criterion == CodeJams.team_name(channel) -                        # If it's a member. -                        or criterion in channel.overwrites -                    ): -                        return channel - -    @staticmethod -    def team_name(channel: discord.TextChannel) -> str: -        """Retrieves the team name from the given channel.""" -        return channel.name.replace("-", " ").title() diff --git a/bot/rules/mentions.py b/bot/rules/mentions.py deleted file mode 100644 index ca1d0c01c..000000000 --- a/bot/rules/mentions.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Dict, Iterable, List, Optional, Tuple - -from discord import DeletedReferencedMessage, Member, Message, MessageType, NotFound - -import bot -from bot.log import get_logger - -log = get_logger(__name__) - - -async def apply( -    last_message: Message, recent_messages: List[Message], config: Dict[str, int] -) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: -    """ -    Detects total mentions exceeding the limit sent by a single user. - -    Excludes mentions that are bots, themselves, or replied users. - -    In very rare cases, may not be able to determine a -    mention was to a reply, in which case it is not ignored. -    """ -    relevant_messages = tuple( -        msg -        for msg in recent_messages -        if msg.author == last_message.author -    ) -    # We use `msg.mentions` here as that is supplied by the api itself, to determine who was mentioned. -    # Additionally, `msg.mentions` includes the user replied to, even if the mention doesn't occur in the body. -    # In order to exclude users who are mentioned as a reply, we check if the msg has a reference -    # -    # While we could use regex to parse the message content, and get a list of -    # the mentions, that solution is very prone to breaking. -    # We would need to deal with codeblocks, escaping markdown, and any discrepancies between -    # our implementation and discord's markdown parser which would cause false positives or false negatives. -    total_recent_mentions = 0 -    for msg in relevant_messages: -        # We check if the message is a reply, and if it is try to get the author -        # since we ignore mentions of a user that we're replying to -        reply_author = None - -        if msg.type == MessageType.reply: -            ref = msg.reference - -            if not (resolved := ref.resolved): -                # It is possible, in a very unusual situation, for a message to have a reference -                # that is both not in the cache, and deleted while running this function. -                # In such a situation, this will throw an error which we catch. -                try: -                    resolved = await bot.instance.get_partial_messageable(resolved.channel_id).fetch_message( -                        resolved.message_id -                    ) -                except NotFound: -                    log.info('Could not fetch the reference message as it has been deleted.') - -            if resolved and not isinstance(resolved, DeletedReferencedMessage): -                reply_author = resolved.author - -        for user in msg.mentions: -            # Don't count bot or self mentions, or the user being replied to (if applicable) -            if user.bot or user in {msg.author, reply_author}: -                continue -            total_recent_mentions += 1 - -    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/tests/bot/exts/events/__init__.py b/tests/bot/exts/events/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/bot/exts/events/__init__.py +++ /dev/null diff --git a/tests/bot/exts/events/test_code_jams.py b/tests/bot/exts/events/test_code_jams.py deleted file mode 100644 index 684f7abcd..000000000 --- a/tests/bot/exts/events/test_code_jams.py +++ /dev/null @@ -1,170 +0,0 @@ -import unittest -from unittest.mock import AsyncMock, MagicMock, create_autospec, patch - -from discord import CategoryChannel -from discord.ext.commands import BadArgument - -from bot.constants import Roles -from bot.exts.events import code_jams -from bot.exts.events.code_jams import _channels, _cog -from tests.helpers import ( -    MockAttachment, MockBot, MockCategoryChannel, MockContext, MockGuild, MockMember, MockRole, MockTextChannel, -    autospec -) - -TEST_CSV = b"""\ -Team Name,Team Member Discord ID,Team Leader -Annoyed Alligators,12345,Y -Annoyed Alligators,54321,N -Oscillating Otters,12358,Y -Oscillating Otters,74832,N -Oscillating Otters,19903,N -Annoyed Alligators,11111,N -""" - - -def get_mock_category(channel_count: int, name: str) -> CategoryChannel: -    """Return a mocked code jam category.""" -    category = create_autospec(CategoryChannel, spec_set=True, instance=True) -    category.name = name -    category.channels = [MockTextChannel() for _ in range(channel_count)] - -    return category - - -class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase): -    """Tests for `codejam create` command.""" - -    def setUp(self): -        self.bot = MockBot() -        self.admin_role = MockRole(name="Admins", id=Roles.admins) -        self.command_user = MockMember([self.admin_role]) -        self.guild = MockGuild([self.admin_role]) -        self.ctx = MockContext(bot=self.bot, author=self.command_user, guild=self.guild) -        self.cog = _cog.CodeJams(self.bot) - -    async def test_message_without_attachments(self): -        """If no link or attachments are provided, commands.BadArgument should be raised.""" -        self.ctx.message.attachments = [] - -        with self.assertRaises(BadArgument): -            await self.cog.create(self.cog, self.ctx, None) - -    @patch.object(_channels, "create_team_channel") -    @patch.object(_channels, "create_team_leader_channel") -    async def test_result_sending(self, create_leader_channel, create_team_channel): -        """Should call `ctx.send` when everything goes right.""" -        self.ctx.message.attachments = [MockAttachment()] -        self.ctx.message.attachments[0].read = AsyncMock() -        self.ctx.message.attachments[0].read.return_value = TEST_CSV - -        team_leaders = MockRole() - -        self.guild.get_member.return_value = MockMember() - -        self.ctx.guild.create_role = AsyncMock() -        self.ctx.guild.create_role.return_value = team_leaders -        self.cog.add_roles = AsyncMock() - -        await self.cog.create(self.cog, self.ctx, None) - -        create_team_channel.assert_awaited() -        create_leader_channel.assert_awaited_once_with( -            self.ctx.guild, team_leaders -        ) -        self.ctx.send.assert_awaited_once() - -    async def test_link_returning_non_200_status(self): -        """When the URL passed returns a non 200 status, it should send a message informing them.""" -        self.bot.http_session.get.return_value = mock = MagicMock() -        mock.status = 404 -        await self.cog.create(self.cog, self.ctx, "https://not-a-real-link.com") - -        self.ctx.send.assert_awaited_once() - -    @patch.object(_channels, "_send_status_update") -    async def test_category_doesnt_exist(self, update): -        """Should create a new code jam category.""" -        subtests = ( -            [], -            [get_mock_category(_channels.MAX_CHANNELS, _channels.CATEGORY_NAME)], -            [get_mock_category(_channels.MAX_CHANNELS - 2, "other")], -        ) - -        for categories in subtests: -            update.reset_mock() -            self.guild.reset_mock() -            self.guild.categories = categories - -            with self.subTest(categories=categories): -                actual_category = await _channels._get_category(self.guild) - -                update.assert_called_once() -                self.guild.create_category_channel.assert_awaited_once() -                category_overwrites = self.guild.create_category_channel.call_args[1]["overwrites"] - -                self.assertFalse(category_overwrites[self.guild.default_role].read_messages) -                self.assertTrue(category_overwrites[self.guild.me].read_messages) -                self.assertEqual(self.guild.create_category_channel.return_value, actual_category) - -    async def test_category_channel_exist(self): -        """Should not try to create category channel.""" -        expected_category = get_mock_category(_channels.MAX_CHANNELS - 2, _channels.CATEGORY_NAME) -        self.guild.categories = [ -            get_mock_category(_channels.MAX_CHANNELS - 2, "other"), -            expected_category, -            get_mock_category(0, _channels.CATEGORY_NAME), -        ] - -        actual_category = await _channels._get_category(self.guild) -        self.assertEqual(expected_category, actual_category) - -    async def test_channel_overwrites(self): -        """Should have correct permission overwrites for users and roles.""" -        leader = (MockMember(), True) -        members = [leader] + [(MockMember(), False) for _ in range(4)] -        overwrites = _channels._get_overwrites(members, self.guild) - -        for member, _ in members: -            self.assertTrue(overwrites[member].read_messages) - -    @patch.object(_channels, "_get_overwrites") -    @patch.object(_channels, "_get_category") -    @autospec(_channels, "_add_team_leader_roles", pass_mocks=False) -    async def test_team_channels_creation(self, get_category, get_overwrites): -        """Should create a text channel for a team.""" -        team_leaders = MockRole() -        members = [(MockMember(), True)] + [(MockMember(), False) for _ in range(5)] -        category = MockCategoryChannel() -        category.create_text_channel = AsyncMock() - -        get_category.return_value = category -        await _channels.create_team_channel(self.guild, "my-team", members, team_leaders) - -        category.create_text_channel.assert_awaited_once_with( -            "my-team", -            overwrites=get_overwrites.return_value -        ) - -    async def test_jam_roles_adding(self): -        """Should add team leader role to leader and jam role to every team member.""" -        leader_role = MockRole(name="Team Leader") - -        leader = MockMember() -        members = [(leader, True)] + [(MockMember(), False) for _ in range(4)] -        await _channels._add_team_leader_roles(members, leader_role) - -        leader.add_roles.assert_awaited_once_with(leader_role) -        for member, is_leader in members: -            if not is_leader: -                member.add_roles.assert_not_awaited() - - -class CodeJamSetup(unittest.IsolatedAsyncioTestCase): -    """Test for `setup` function of `CodeJam` cog.""" - -    async def test_setup(self): -        """Should call `bot.add_cog`.""" -        bot = MockBot() -        await code_jams.setup(bot) -        bot.add_cog.assert_awaited_once() | 
