aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2020-07-18 13:47:40 +0200
committerGravatar Leon Sandøy <[email protected]>2020-07-18 13:47:40 +0200
commit4d1b6a3abee00d9729ce333a25a2440d00d509f1 (patch)
treed073735c14993013a36c8b537897b0655a0eac10
parentMake the cache more convenient to access. (diff)
Add AllowDenyLists cog.
This includes commands to add, remove and show the items in the whitelists and blacklists for the different list types. Commands are limited to Moderators+.
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/allow_deny_lists.py144
2 files changed, 145 insertions, 0 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index 49388455a..932aa705c 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -53,6 +53,7 @@ bot.load_extension("bot.cogs.verification")
# Feature cogs
bot.load_extension("bot.cogs.alias")
+bot.load_extension("bot.cogs.allow_deny_lists")
bot.load_extension("bot.cogs.defcon")
bot.load_extension("bot.cogs.dm_relay")
bot.load_extension("bot.cogs.duck_pond")
diff --git a/bot/cogs/allow_deny_lists.py b/bot/cogs/allow_deny_lists.py
new file mode 100644
index 000000000..d03c774ec
--- /dev/null
+++ b/bot/cogs/allow_deny_lists.py
@@ -0,0 +1,144 @@
+import logging
+
+from discord import Colour, Embed
+from discord.ext.commands import BadArgument, Cog, Context, group
+
+from bot import constants
+from bot.api import ResponseCodeError
+from bot.bot import Bot
+from bot.converters import ValidAllowDenyListType
+from bot.pagination import LinePaginator
+from bot.utils.checks import with_role_check
+
+log = logging.getLogger(__name__)
+
+
+class AllowDenyLists(Cog):
+ """Commands for blacklisting and whitelisting things."""
+
+ def __init__(self, bot: Bot) -> None:
+ self.bot = bot
+
+ async def _add_data(self, ctx: Context, allowed: bool, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Add an item to an allow or denylist."""
+ payload = {
+ 'allowed': allowed,
+ 'type': list_type,
+ 'content': content,
+ }
+ allow_type = "whitelist" if allowed else "blacklist"
+
+ # Try to add the item to the database
+ try:
+ item = await self.bot.api_client.post(
+ "bot/allow_deny_lists",
+ json=payload
+ )
+ except ResponseCodeError as e:
+ if e.status == 500:
+ await ctx.message.add_reaction("❌")
+ raise BadArgument(
+ f"Unable to add the item to the {allow_type}. "
+ "The item probably already exists. Keep in mind that a "
+ "blacklist and a whitelist for the same item cannot co-exist, "
+ "and we do not permit any duplicates."
+ )
+ raise
+
+ # Insert the item into the cache
+ type_ = item.get("type")
+ allowed = item.get("allowed")
+ metadata = {
+ "content": item.get("content"),
+ "id": item.get("id"),
+ "created_at": item.get("created_at"),
+ "updated_at": item.get("updated_at"),
+ }
+ self.bot.allow_deny_list_cache.setdefault(f"{type_}.{allowed}", []).append(metadata)
+ await ctx.message.add_reaction("✅")
+
+ async def _delete_data(self, ctx: Context, allowed: bool, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Remove an item from an allow or denylist."""
+ item = None
+
+ for allow_list in self.bot.allow_deny_list_cache.get(f"{list_type}.{allowed}", []):
+ if content == allow_list.get("content"):
+ item = allow_list
+ break
+
+ if item is not None:
+ await self.bot.api_client.delete(
+ f"bot/allow_deny_lists/{item.get('id')}"
+ )
+ self.bot.allow_deny_list_cache[f"{list_type}.{allowed}"].remove(item)
+ await ctx.message.add_reaction("✅")
+
+ async def _list_all_data(self, ctx: Context, allowed: bool, list_type: ValidAllowDenyListType) -> None:
+ """Paginate and display all items in an allow or denylist."""
+ result = self.bot.allow_deny_list_cache.get(f"{list_type}.{allowed}", [])
+ lines = sorted(f"• {item.get('content')}" for item in result)
+ allowed_string = "Whitelisted" if allowed else "Blacklisted"
+ embed = Embed(
+ title=f"{allowed_string} {list_type.lower()} items ({len(result)} total)",
+ colour=Colour.blue()
+ )
+
+ if result:
+ await LinePaginator.paginate(lines, ctx, embed, max_lines=15, empty=False)
+ else:
+ embed.description = "Hmmm, seems like there's nothing here yet."
+ await ctx.send(embed=embed)
+
+ @group(aliases=("allowlist", "allow", "al", "wl"))
+ async def whitelist(self, ctx: Context) -> None:
+ """Group for whitelisting commands."""
+ if not ctx.invoked_subcommand:
+ await ctx.send_help(ctx.command)
+
+ @group(aliases=("denylist", "deny", "bl", "dl"))
+ async def blacklist(self, ctx: Context) -> None:
+ """Group for blacklisting commands."""
+ if not ctx.invoked_subcommand:
+ await ctx.send_help(ctx.command)
+
+ @whitelist.command(name="add", aliases=("a", "set"))
+ async def allow_add(self, ctx: Context, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Add an item to the specified allowlist."""
+ await self._add_data(ctx, True, list_type, content)
+
+ @blacklist.command(name="add", aliases=("a", "set"))
+ async def deny_add(self, ctx: Context, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Add an item to the specified denylist."""
+ await self._add_data(ctx, False, list_type, content)
+
+ @whitelist.command(name="remove", aliases=("delete", "rm",))
+ async def allow_delete(self, ctx: Context, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Remove an item from the specified allowlist."""
+ await self._delete_data(ctx, True, list_type, content)
+
+ @blacklist.command(name="remove", aliases=("delete", "rm",))
+ async def deny_delete(self, ctx: Context, list_type: ValidAllowDenyListType, content: str) -> None:
+ """Remove an item from the specified denylist."""
+ await self._delete_data(ctx, False, list_type, content)
+
+ @whitelist.command(name="get", aliases=("list", "ls", "fetch", "show"))
+ async def allow_get(self, ctx: Context, list_type: ValidAllowDenyListType) -> None:
+ """Get the contents of a specified allowlist."""
+ await self._list_all_data(ctx, True, list_type)
+
+ @blacklist.command(name="get", aliases=("list", "ls", "fetch", "show"))
+ async def deny_get(self, ctx: Context, list_type: ValidAllowDenyListType) -> None:
+ """Get the contents of a specified denylist."""
+ await self._list_all_data(ctx, False, list_type)
+
+ def cog_check(self, ctx: Context) -> bool:
+ """Only allow moderators to invoke the commands in this cog."""
+ checks = [
+ with_role_check(ctx, *constants.MODERATION_ROLES),
+ ]
+ return all(checks)
+
+
+def setup(bot: Bot) -> None:
+ """Load the AllowDenyLists cog."""
+ bot.add_cog(AllowDenyLists(bot))