diff options
| -rw-r--r-- | bot/exts/events/code_jams/_channels.py | 113 | ||||
| -rw-r--r-- | bot/exts/events/code_jams/_cog.py | 113 | ||||
| -rw-r--r-- | bot/exts/filters/antimalware.py | 2 | ||||
| -rw-r--r-- | bot/exts/filters/antispam.py | 2 | ||||
| -rw-r--r-- | bot/exts/filters/filtering.py | 2 | ||||
| -rw-r--r-- | tests/bot/exts/events/test_code_jams.py | 64 | 
6 files changed, 150 insertions, 146 deletions
diff --git a/bot/exts/events/code_jams/_channels.py b/bot/exts/events/code_jams/_channels.py new file mode 100644 index 000000000..8b199a3c2 --- /dev/null +++ b/bot/exts/events/code_jams/_channels.py @@ -0,0 +1,113 @@ +import logging +import typing as t + +import discord + +from bot.constants import Categories, Channels, Roles + +log = logging.getLogger(__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 team leader role, the jammer role and their team role.""" +    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 index 87ae847f6..2d0873de7 100644 --- a/bot/exts/events/code_jams/_cog.py +++ b/bot/exts/events/code_jams/_cog.py @@ -3,16 +3,14 @@ import logging  import typing as t  from collections import defaultdict -import discord  from discord.ext import commands  from bot.bot import Bot -from bot.constants import Categories, Channels, Emojis, Roles +from bot.constants import Emojis, Roles +from bot.exts.events.code_jams import _channels  log = logging.getLogger(__name__) -MAX_CHANNELS = 50 -CATEGORY_NAME = "Code Jam"  TEAM_LEADERS_COLOUR = 0x11806a @@ -67,110 +65,7 @@ class CodeJams(commands.Cog):              team_leaders = await ctx.guild.create_role(name="Code Jam Team Leaders", colour=TEAM_LEADERS_COLOUR)              for team_name, members in teams.items(): -                await self.create_team_channel(ctx.guild, team_name, members, team_leaders) +                await _channels.create_team_channel(ctx.guild, team_name, members, team_leaders) -            await self.create_team_leader_channel(ctx.guild, 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.") - -    async def get_category(self, 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 self.create_category(guild) - -    async def create_category(self, 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 self.send_status_update( -            guild, f"Created a new category with the ID {category.id} for this Code Jam's team channels." -        ) - -        return category - -    @staticmethod -    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( -        self, -        guild: discord.Guild, -        team_name: str, -        members: list[tuple[discord.Member, bool]], -        team_leaders: discord.Role -    ) -> None: -        """Create the team's text channel.""" -        await self.add_team_leader_roles(members, team_leaders) - -        # Get permission overwrites and category -        team_channel_overwrites = self.get_overwrites(members, guild) -        code_jam_category = await self.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(self, 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 self.send_status_update(guild, f"Created {team_leaders_chat.mention} in the {category} category.") - -    async def send_status_update(self, 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}") - -    @staticmethod -    async def add_team_leader_roles(members: list[tuple[discord.Member, bool]], team_leaders: discord.Role) -> None: -        """Assign team leader role, the jammer role and their team role.""" -        for member, is_leader in members: -            if is_leader: -                await member.add_roles(team_leaders) - - -def setup(bot: Bot) -> None: -    """Load the CodeJams cog.""" -    bot.add_cog(CodeJams(bot)) diff --git a/bot/exts/filters/antimalware.py b/bot/exts/filters/antimalware.py index 3f6213db3..0eedeb0fb 100644 --- a/bot/exts/filters/antimalware.py +++ b/bot/exts/filters/antimalware.py @@ -7,7 +7,7 @@ from discord.ext.commands import Cog  from bot.bot import Bot  from bot.constants import Channels, Filter, URLs -from bot.exts.events.code_jams._cog import CATEGORY_NAME as JAM_CATEGORY_NAME +from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME  log = logging.getLogger(__name__) diff --git a/bot/exts/filters/antispam.py b/bot/exts/filters/antispam.py index 124905cb4..1830e23b8 100644 --- a/bot/exts/filters/antispam.py +++ b/bot/exts/filters/antispam.py @@ -17,7 +17,7 @@ from bot.constants import (      Guild as GuildConfig, Icons,  )  from bot.converters import Duration -from bot.exts.events.code_jams._cog import CATEGORY_NAME as JAM_CATEGORY_NAME +from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME  from bot.exts.moderation.modlog import ModLog  from bot.utils import lock, scheduling  from bot.utils.messages import format_user, send_attachments diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py index 0810425e2..10cc7885d 100644 --- a/bot/exts/filters/filtering.py +++ b/bot/exts/filters/filtering.py @@ -19,7 +19,7 @@ from bot.constants import (      Channels, Colours, Filter,      Guild, Icons, URLs  ) -from bot.exts.events.code_jams._cog import CATEGORY_NAME as JAM_CATEGORY_NAME +from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME  from bot.exts.moderation.modlog import ModLog  from bot.utils.messages import format_user  from bot.utils.regex import INVITE_RE diff --git a/tests/bot/exts/events/test_code_jams.py b/tests/bot/exts/events/test_code_jams.py index d7b8aa4d2..b9ee1e363 100644 --- a/tests/bot/exts/events/test_code_jams.py +++ b/tests/bot/exts/events/test_code_jams.py @@ -1,14 +1,15 @@  import unittest -from unittest.mock import AsyncMock, MagicMock, create_autospec +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.code_jams import _cog +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 +    MockGuild, MockMember, MockRole, MockTextChannel, autospec  )  TEST_CSV = b"""\ @@ -49,7 +50,9 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):          with self.assertRaises(BadArgument):              await self.cog.create(self.cog, self.ctx, None) -    async def test_result_sending(self): +    @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() @@ -61,14 +64,12 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):          self.ctx.guild.create_role = AsyncMock()          self.ctx.guild.create_role.return_value = team_leaders -        self.cog.create_team_channel = AsyncMock() -        self.cog.create_team_leader_channel = AsyncMock()          self.cog.add_roles = AsyncMock()          await self.cog.create(self.cog, self.ctx, None) -        self.cog.create_team_channel.assert_awaited() -        self.cog.create_team_leader_channel.assert_awaited_once_with( +        create_team_channel.assert_awaited() +        create_leader_channel.assert_awaited_once_with(              self.ctx.guild, team_leaders          )          self.ctx.send.assert_awaited_once() @@ -81,25 +82,24 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):          self.ctx.send.assert_awaited_once() -    async def test_category_doesnt_exist(self): +    @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(_cog.MAX_CHANNELS, _cog.CATEGORY_NAME)], -            [get_mock_category(_cog.MAX_CHANNELS - 2, "other")], +            [get_mock_category(_channels.MAX_CHANNELS, _channels.CATEGORY_NAME)], +            [get_mock_category(_channels.MAX_CHANNELS - 2, "other")],          ) -        self.cog.send_status_update = AsyncMock() -          for categories in subtests: -            self.cog.send_status_update.reset_mock() +            update.reset_mock()              self.guild.reset_mock()              self.guild.categories = categories              with self.subTest(categories=categories): -                actual_category = await self.cog.get_category(self.guild) +                actual_category = await _channels._get_category(self.guild) -                self.cog.send_status_update.assert_called_once() +                update.assert_called_once()                  self.guild.create_category_channel.assert_awaited_once()                  category_overwrites = self.guild.create_category_channel.call_args[1]["overwrites"] @@ -109,45 +109,41 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):      async def test_category_channel_exist(self):          """Should not try to create category channel.""" -        expected_category = get_mock_category(_cog.MAX_CHANNELS - 2, _cog.CATEGORY_NAME) +        expected_category = get_mock_category(_channels.MAX_CHANNELS - 2, _channels.CATEGORY_NAME)          self.guild.categories = [ -            get_mock_category(_cog.MAX_CHANNELS - 2, "other"), +            get_mock_category(_channels.MAX_CHANNELS - 2, "other"),              expected_category, -            get_mock_category(0, _cog.CATEGORY_NAME), +            get_mock_category(0, _channels.CATEGORY_NAME),          ] -        actual_category = await self.cog.get_category(self.guild) +        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 = self.cog.get_overwrites(members, self.guild) +        overwrites = _channels._get_overwrites(members, self.guild)          for member, _ in members:              self.assertTrue(overwrites[member].read_messages) -    async def test_team_channels_creation(self): +    @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() -        self.cog.get_overwrites = MagicMock() -        self.cog.get_category = AsyncMock() -        self.cog.get_category.return_value = category -        self.cog.add_team_leader_roles = AsyncMock() - -        await self.cog.create_team_channel(self.guild, "my-team", members, team_leaders) -        self.cog.add_team_leader_roles.assert_awaited_once_with(members, team_leaders) -        self.cog.get_overwrites.assert_called_once_with(members, self.guild) -        self.cog.get_category.assert_awaited_once_with(self.guild) +        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=self.cog.get_overwrites.return_value +            overwrites=get_overwrites.return_value          )      async def test_jam_roles_adding(self): @@ -156,7 +152,7 @@ class JamCodejamCreateTests(unittest.IsolatedAsyncioTestCase):          leader = MockMember()          members = [(leader, True)] + [(MockMember(), False) for _ in range(4)] -        await self.cog.add_team_leader_roles(members, leader_role) +        await _channels._add_team_leader_roles(members, leader_role)          leader.add_roles.assert_awaited_once_with(leader_role)          for member, is_leader in members: @@ -170,5 +166,5 @@ class CodeJamSetup(unittest.TestCase):      def test_setup(self):          """Should call `bot.add_cog`."""          bot = MockBot() -        _cog.setup(bot) +        code_jams.setup(bot)          bot.add_cog.assert_called_once()  |