aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/jams.py51
-rw-r--r--bot/cogs/utils.py36
-rw-r--r--config-default.yml4
-rw-r--r--tests/bot/cogs/test_jams.py62
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()