diff options
| author | 2020-07-23 15:13:03 -0400 | |
|---|---|---|
| committer | 2020-07-23 15:13:03 -0400 | |
| commit | f0aa72969fc3f4dba3571a13013537f2b81effe1 (patch) | |
| tree | f7a42f23af47666db3cfa9e7f0a1bce8abf43476 | |
| parent | Check that embed desc is not Empty before stripping. (diff) | |
| parent | Disabled burst_shared filter temporarily (diff) | |
Merge branch 'master' into bug/1036/empty-embed-fields
| -rw-r--r-- | bot/cogs/jams.py | 51 | ||||
| -rw-r--r-- | bot/cogs/utils.py | 36 | ||||
| -rw-r--r-- | config-default.yml | 4 | ||||
| -rw-r--r-- | tests/bot/cogs/test_jams.py | 62 | 
4 files changed, 88 insertions, 65 deletions
diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index a48dbc49a..b3102db2f 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -1,7 +1,7 @@  import logging  import typing as t -from discord import CategoryChannel, Guild, Member, PermissionOverwrite, Role, utils +from discord import CategoryChannel, Guild, Member, PermissionOverwrite, Role  from discord.ext import commands  from more_itertools import unique_everseen @@ -11,6 +11,9 @@ from bot.decorators import with_role  log = logging.getLogger(__name__) +MAX_CHANNELS = 50 +CATEGORY_NAME = "Code Jam" +  class CodeJams(commands.Cog):      """Manages the code-jam related parts of our server.""" @@ -50,30 +53,38 @@ class CodeJams(commands.Cog):              f"**Team Members:** {' '.join(member.mention for member in members[1:])}"          ) +    async def get_category(self, guild: Guild) -> CategoryChannel: +        """ +        Return a code jam category. + +        If all categories are full or none exist, create a new category. +        """ +        for category in guild.categories: +            # Need 2 available spaces: one for the text channel and one for voice. +            if category.name == CATEGORY_NAME and MAX_CHANNELS - len(category.channels) >= 2: +                return category + +        return await self.create_category(guild) +      @staticmethod -    async def get_category(guild: Guild) -> CategoryChannel: -        """Create a Code Jam category if it doesn't exist and return it.""" -        code_jam_category = utils.get(guild.categories, name="Code Jam") - -        if code_jam_category is None: -            log.info("Code Jam category not found, creating it.") - -            category_overwrites = { -                guild.default_role: PermissionOverwrite(read_messages=False), -                guild.me: PermissionOverwrite(read_messages=True) -            } - -            code_jam_category = await guild.create_category_channel( -                "Code Jam", -                overwrites=category_overwrites, -                reason="It's code jam time!" -            ) +    async def create_category(guild: Guild) -> CategoryChannel: +        """Create a new code jam category and return it.""" +        log.info("Creating a new code jam category.") -        return code_jam_category +        category_overwrites = { +            guild.default_role: PermissionOverwrite(read_messages=False), +            guild.me: PermissionOverwrite(read_messages=True) +        } + +        return await guild.create_category_channel( +            CATEGORY_NAME, +            overwrites=category_overwrites, +            reason="It's code jam time!" +        )      @staticmethod      def get_overwrites(members: t.List[Member], guild: Guild) -> t.Dict[t.Union[Member, Role], PermissionOverwrite]: -        """Get Code Jam team channels permission overwrites.""" +        """Get code jam team channels permission overwrites."""          # First member is always the team leader          team_channel_overwrites = {              members[0]: PermissionOverwrite( diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 697bf60ce..017f3419e 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -12,6 +12,8 @@ from discord.ext.commands import BadArgument, Cog, Context, command  from bot.bot import Bot  from bot.constants import Channels, MODERATION_ROLES, STAFF_ROLES  from bot.decorators import in_whitelist, with_role +from bot.pagination import LinePaginator +from bot.utils import messages  log = logging.getLogger(__name__) @@ -117,25 +119,18 @@ class Utils(Cog):      @command()      @in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_ROLES)      async def charinfo(self, ctx: Context, *, characters: str) -> None: -        """Shows you information on up to 25 unicode characters.""" +        """Shows you information on up to 50 unicode characters."""          match = re.match(r"<(a?):(\w+):(\d+)>", characters)          if match: -            embed = Embed( -                title="Non-Character Detected", -                description=( -                    "Only unicode characters can be processed, but a custom Discord emoji " -                    "was found. Please remove it and try again." -                ) +            return await messages.send_denial( +                ctx, +                "**Non-Character Detected**\n" +                "Only unicode characters can be processed, but a custom Discord emoji " +                "was found. Please remove it and try again."              ) -            embed.colour = Colour.red() -            await ctx.send(embed=embed) -            return -        if len(characters) > 25: -            embed = Embed(title=f"Too many characters ({len(characters)}/25)") -            embed.colour = Colour.red() -            await ctx.send(embed=embed) -            return +        if len(characters) > 50: +            return await messages.send_denial(ctx, f"Too many characters ({len(characters)}/50)")          def get_info(char: str) -> Tuple[str, str]:              digit = f"{ord(char):x}" @@ -148,15 +143,14 @@ class Utils(Cog):              info = f"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}"              return info, u_code -        charlist, rawlist = zip(*(get_info(c) for c in characters)) - -        embed = Embed(description="\n".join(charlist)) -        embed.set_author(name="Character Info") +        char_list, raw_list = zip(*(get_info(c) for c in characters)) +        embed = Embed().set_author(name="Character Info")          if len(characters) > 1: -            embed.add_field(name='Raw', value=f"`{''.join(rawlist)}`", inline=False) +            # Maximum length possible is 502 out of 1024, so there's no need to truncate. +            embed.add_field(name='Full Raw Text', value=f"`{''.join(raw_list)}`", inline=False) -        await ctx.send(embed=embed) +        await LinePaginator.paginate(char_list, ctx, embed, max_lines=10, max_size=2000, empty=False)      @command()      async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None) -> None: diff --git a/config-default.yml b/config-default.yml index ad6149f6f..d0262be33 100644 --- a/config-default.yml +++ b/config-default.yml @@ -459,10 +459,6 @@ anti_spam:              interval: 10              max: 7 -        burst_shared: -            interval: 10 -            max: 20 -          chars:              interval: 5              max: 3_000 diff --git a/tests/bot/cogs/test_jams.py b/tests/bot/cogs/test_jams.py index 81fbcb798..b4ad8535f 100644 --- a/tests/bot/cogs/test_jams.py +++ b/tests/bot/cogs/test_jams.py @@ -1,11 +1,22 @@  import unittest -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, create_autospec -from bot.cogs.jams import CodeJams, setup +from discord import CategoryChannel + +from bot.cogs import jams  from bot.constants import Roles  from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockTextChannel +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 JamCreateTeamTests(unittest.IsolatedAsyncioTestCase):      """Tests for `createteam` command.""" @@ -15,11 +26,7 @@ class JamCreateTeamTests(unittest.IsolatedAsyncioTestCase):          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 = CodeJams(self.bot) - -        utils_patcher = patch("bot.cogs.jams.utils") -        self.utils_mock = utils_patcher.start() -        self.addCleanup(utils_patcher.stop) +        self.cog = jams.CodeJams(self.bot)      async def test_too_small_amount_of_team_members_passed(self):          """Should `ctx.send` and exit early when too small amount of members.""" @@ -29,7 +36,6 @@ class JamCreateTeamTests(unittest.IsolatedAsyncioTestCase):                  self.cog.add_roles = AsyncMock()                  self.ctx.reset_mock() -                self.utils_mock.reset_mock()                  members = (MockMember() for _ in range(case))                  await self.cog.createteam(self.cog, self.ctx, "foo", members) @@ -61,22 +67,39 @@ class JamCreateTeamTests(unittest.IsolatedAsyncioTestCase):          self.cog.add_roles.assert_awaited_once()          self.ctx.send.assert_awaited_once() -    async def test_category_dont_exist(self): -        """Should create code jam category.""" -        self.utils_mock.get.return_value = None +    async def test_category_doesnt_exist(self): +        """Should create a new code jam category.""" +        subtests = ( +            [], +            [get_mock_category(jams.MAX_CHANNELS - 1, jams.CATEGORY_NAME)], +            [get_mock_category(jams.MAX_CHANNELS - 2, "other")], +        ) + +        for categories in subtests: +            self.guild.reset_mock() +            self.guild.categories = categories -        await self.cog.get_category(self.guild) +            with self.subTest(categories=categories): +                actual_category = await self.cog.get_category(self.guild) -        self.guild.create_category_channel.assert_awaited_once() -        category_overwrites = self.guild.create_category_channel.call_args[1]["overwrites"] +                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.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.""" -        await self.cog.get_category(self.guild) -        self.guild.create_category_channel.assert_not_awaited() +        expected_category = get_mock_category(jams.MAX_CHANNELS - 2, jams.CATEGORY_NAME) +        self.guild.categories = [ +            get_mock_category(jams.MAX_CHANNELS - 2, "other"), +            expected_category, +            get_mock_category(0, jams.CATEGORY_NAME), +        ] + +        actual_category = await self.cog.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.""" @@ -103,7 +126,6 @@ class JamCreateTeamTests(unittest.IsolatedAsyncioTestCase):      async def test_team_channels_creation(self):          """Should create new voice and text channel for team.""" -        self.utils_mock.get.return_value = "foo"          members = [MockMember() for _ in range(5)]          self.cog.get_overwrites = MagicMock() @@ -147,5 +169,5 @@ class CodeJamSetup(unittest.TestCase):      def test_setup(self):          """Should call `bot.add_cog`."""          bot = MockBot() -        setup(bot) +        jams.setup(bot)          bot.add_cog.assert_called_once()  |