aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar scragly <[email protected]>2019-11-14 03:39:49 +1000
committerGravatar GitHub <[email protected]>2019-11-14 03:39:49 +1000
commitd5b6ef42243a92c8abe3fae9b01cae3e8653f86b (patch)
treea7610f4bc3cc59f748a578ee5e2497de464a2afc
parentPrevent await warnings for MockBot's create_task (diff)
parentMerge pull request #656 from python-discord/checkpoint-changes (diff)
Merge branch 'master' into unittest-helpers-improvements
-rw-r--r--bot/cogs/verification.py75
-rw-r--r--bot/cogs/watchchannels/bigbrother.py10
-rw-r--r--bot/cogs/watchchannels/talentpool.py17
-rw-r--r--tests/bot/rules/test_links.py101
4 files changed, 167 insertions, 36 deletions
diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py
index 5b115deaa..b5e8d4357 100644
--- a/bot/cogs/verification.py
+++ b/bot/cogs/verification.py
@@ -1,12 +1,16 @@
import logging
from datetime import datetime
-from discord import Message, NotFound, Object
+from discord import Colour, Message, NotFound, Object
from discord.ext import tasks
from discord.ext.commands import Bot, Cog, Context, command
from bot.cogs.moderation import ModLog
-from bot.constants import Bot as BotConfig, Channels, Event, Roles
+from bot.constants import (
+ Bot as BotConfig,
+ Channels, Colours, Event,
+ Filter, Icons, Roles
+)
from bot.decorators import InChannelCheckFailure, in_channel, without_role
log = logging.getLogger(__name__)
@@ -31,7 +35,7 @@ If you'd like to unsubscribe from the announcement notifications, simply send `!
PERIODIC_PING = (
f"@everyone To verify that you have read our rules, please type `{BotConfig.prefix}accept`."
- f" Ping <@&{Roles.admin}> if you encounter any problems during the verification process."
+ f" If you encounter any problems during the verification process, ping the <@&{Roles.admin}> role in this channel."
)
@@ -53,32 +57,59 @@ class Verification(Cog):
if message.author.bot:
return # They're a bot, ignore
+ if message.channel.id != Channels.verification:
+ return # Only listen for #checkpoint messages
+
+ # if a user mentions a role or guild member
+ # alert the mods in mod-alerts channel
+ if message.mentions or message.role_mentions:
+ log.debug(
+ f"{message.author} mentioned one or more users "
+ f"and/or roles in {message.channel.name}"
+ )
+
+ embed_text = (
+ f"{message.author.mention} sent a message in "
+ f"{message.channel.mention} that contained user and/or role mentions."
+ f"\n\n**Original message:**\n>>> {message.content}"
+ )
+
+ # Send pretty mod log embed to mod-alerts
+ await self.mod_log.send_log_message(
+ icon_url=Icons.filtering,
+ colour=Colour(Colours.soft_red),
+ title=f"User/Role mentioned in {message.channel.name}",
+ text=embed_text,
+ thumbnail=message.author.avatar_url_as(static_format="png"),
+ channel_id=Channels.mod_alerts,
+ ping_everyone=Filter.ping_everyone,
+ )
+
ctx = await self.bot.get_context(message) # type: Context
if ctx.command is not None and ctx.command.name == "accept":
return # They used the accept command
- if ctx.channel.id == Channels.verification: # We're in the verification channel
- for role in ctx.author.roles:
- if role.id == Roles.verified:
- log.warning(f"{ctx.author} posted '{ctx.message.content}' "
- "in the verification channel, but is already verified.")
- return # They're already verified
-
- log.debug(f"{ctx.author} posted '{ctx.message.content}' in the verification "
- "channel. We are providing instructions how to verify.")
- await ctx.send(
- f"{ctx.author.mention} Please type `!accept` to verify that you accept our rules, "
- f"and gain access to the rest of the server.",
- delete_after=20
- )
+ for role in ctx.author.roles:
+ if role.id == Roles.verified:
+ log.warning(f"{ctx.author} posted '{ctx.message.content}' "
+ "in the verification channel, but is already verified.")
+ return # They're already verified
+
+ log.debug(f"{ctx.author} posted '{ctx.message.content}' in the verification "
+ "channel. We are providing instructions how to verify.")
+ await ctx.send(
+ f"{ctx.author.mention} Please type `!accept` to verify that you accept our rules, "
+ f"and gain access to the rest of the server.",
+ delete_after=20
+ )
- log.trace(f"Deleting the message posted by {ctx.author}")
+ log.trace(f"Deleting the message posted by {ctx.author}")
- try:
- await ctx.message.delete()
- except NotFound:
- log.trace("No message found, it must have been deleted by another bot.")
+ try:
+ await ctx.message.delete()
+ except NotFound:
+ log.trace("No message found, it must have been deleted by another bot.")
@command(name='accept', aliases=('verify', 'verified', 'accepted'), hidden=True)
@without_role(Roles.verified)
diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py
index c516508ca..49783bb09 100644
--- a/bot/cogs/watchchannels/bigbrother.py
+++ b/bot/cogs/watchchannels/bigbrother.py
@@ -6,7 +6,7 @@ from discord import User
from discord.ext.commands import Bot, Cog, Context, group
from bot.cogs.moderation.utils import post_infraction
-from bot.constants import Channels, Roles, Webhooks
+from bot.constants import Channels, MODERATION_ROLES, Webhooks
from bot.decorators import with_role
from .watchchannel import WatchChannel, proxy_user
@@ -27,13 +27,13 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):
)
@group(name='bigbrother', aliases=('bb',), invoke_without_command=True)
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def bigbrother_group(self, ctx: Context) -> None:
"""Monitors users by relaying their messages to the Big Brother watch channel."""
await ctx.invoke(self.bot.get_command("help"), "bigbrother")
@bigbrother_group.command(name='watched', aliases=('all', 'list'))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def watched_command(self, ctx: Context, update_cache: bool = True) -> None:
"""
Shows the users that are currently being monitored by Big Brother.
@@ -44,7 +44,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):
await self.list_watched_users(ctx, update_cache)
@bigbrother_group.command(name='watch', aliases=('w',))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def watch_command(self, ctx: Context, user: Union[User, proxy_user], *, reason: str) -> None:
"""
Relay messages sent by the given `user` to the `#big-brother` channel.
@@ -91,7 +91,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):
await ctx.send(msg)
@bigbrother_group.command(name='unwatch', aliases=('uw',))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def unwatch_command(self, ctx: Context, user: Union[User, proxy_user], *, reason: str) -> None:
"""Stop relaying messages by the given `user`."""
active_watches = await self.bot.api_client.get(
diff --git a/bot/cogs/watchchannels/talentpool.py b/bot/cogs/watchchannels/talentpool.py
index 176c6f760..4ec42dcc1 100644
--- a/bot/cogs/watchchannels/talentpool.py
+++ b/bot/cogs/watchchannels/talentpool.py
@@ -7,14 +7,13 @@ from discord import Color, Embed, Member, User
from discord.ext.commands import Bot, Cog, Context, group
from bot.api import ResponseCodeError
-from bot.constants import Channels, Guild, Roles, Webhooks
+from bot.constants import Channels, Guild, MODERATION_ROLES, STAFF_ROLES, Webhooks
from bot.decorators import with_role
from bot.pagination import LinePaginator
from bot.utils import time
from .watchchannel import WatchChannel, proxy_user
log = logging.getLogger(__name__)
-STAFF_ROLES = Roles.owner, Roles.admin, Roles.moderator, Roles.helpers # <- In constants after the merge?
class TalentPool(WatchChannel, Cog, name="Talentpool"):
@@ -31,13 +30,13 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
)
@group(name='talentpool', aliases=('tp', 'talent', 'nomination', 'n'), invoke_without_command=True)
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def nomination_group(self, ctx: Context) -> None:
"""Highlights the activity of helper nominees by relaying their messages to the talent pool channel."""
await ctx.invoke(self.bot.get_command("help"), "talentpool")
@nomination_group.command(name='watched', aliases=('all', 'list'))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def watched_command(self, ctx: Context, update_cache: bool = True) -> None:
"""
Shows the users that are currently being monitored in the talent pool.
@@ -48,7 +47,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
await self.list_watched_users(ctx, update_cache)
@nomination_group.command(name='watch', aliases=('w', 'add', 'a'))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*STAFF_ROLES)
async def watch_command(self, ctx: Context, user: Union[Member, User, proxy_user], *, reason: str) -> None:
"""
Relay messages sent by the given `user` to the `#talent-pool` channel.
@@ -113,7 +112,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
await ctx.send(msg)
@nomination_group.command(name='history', aliases=('info', 'search'))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def history_command(self, ctx: Context, user: Union[User, proxy_user]) -> None:
"""Shows the specified user's nomination history."""
result = await self.bot.api_client.get(
@@ -142,7 +141,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
)
@nomination_group.command(name='unwatch', aliases=('end', ))
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def unwatch_command(self, ctx: Context, user: Union[User, proxy_user], *, reason: str) -> None:
"""
Ends the active nomination of the specified user with the given reason.
@@ -170,13 +169,13 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
self._remove_user(user.id)
@nomination_group.group(name='edit', aliases=('e',), invoke_without_command=True)
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def nomination_edit_group(self, ctx: Context) -> None:
"""Commands to edit nominations."""
await ctx.invoke(self.bot.get_command("help"), "talentpool", "edit")
@nomination_edit_group.command(name='reason')
- @with_role(Roles.owner, Roles.admin, Roles.moderator)
+ @with_role(*MODERATION_ROLES)
async def edit_reason_command(self, ctx: Context, nomination_id: int, *, reason: str) -> None:
"""
Edits the reason/unnominate reason for the nomination with the given `id` depending on the status.
diff --git a/tests/bot/rules/test_links.py b/tests/bot/rules/test_links.py
new file mode 100644
index 000000000..be832843b
--- /dev/null
+++ b/tests/bot/rules/test_links.py
@@ -0,0 +1,101 @@
+import unittest
+from typing import List, NamedTuple, Tuple
+
+from bot.rules import links
+from tests.helpers import async_test
+
+
+class FakeMessage(NamedTuple):
+ author: str
+ content: str
+
+
+class Case(NamedTuple):
+ recent_messages: List[FakeMessage]
+ relevant_messages: Tuple[FakeMessage]
+ culprit: Tuple[str]
+ total_links: int
+
+
+def msg(author: str, total_links: int) -> FakeMessage:
+ """Makes a message with *total_links* links."""
+ content = " ".join(["https://pydis.com"] * total_links)
+ return FakeMessage(author=author, content=content)
+
+
+class LinksTests(unittest.TestCase):
+ """Tests applying the `links` rule."""
+
+ def setUp(self):
+ self.config = {
+ "max": 2,
+ "interval": 10
+ }
+
+ @async_test
+ async def test_links_within_limit(self):
+ """Messages with an allowed amount of links."""
+ cases = (
+ [msg("bob", 0)],
+ [msg("bob", 2)],
+ [msg("bob", 3)], # Filter only applies if len(messages_with_links) > 1
+ [msg("bob", 1), msg("bob", 1)],
+ [msg("bob", 2), msg("alice", 2)] # Only messages from latest author count
+ )
+
+ for recent_messages in cases:
+ last_message = recent_messages[0]
+
+ with self.subTest(
+ last_message=last_message,
+ recent_messages=recent_messages,
+ config=self.config
+ ):
+ self.assertIsNone(
+ await links.apply(last_message, recent_messages, self.config)
+ )
+
+ @async_test
+ async def test_links_exceeding_limit(self):
+ """Messages with a a higher than allowed amount of links."""
+ cases = (
+ Case(
+ [msg("bob", 1), msg("bob", 2)],
+ (msg("bob", 1), msg("bob", 2)),
+ ("bob",),
+ 3
+ ),
+ Case(
+ [msg("alice", 1), msg("alice", 1), msg("alice", 1)],
+ (msg("alice", 1), msg("alice", 1), msg("alice", 1)),
+ ("alice",),
+ 3
+ ),
+ Case(
+ [msg("alice", 2), msg("bob", 3), msg("alice", 1)],
+ (msg("alice", 2), msg("alice", 1)),
+ ("alice",),
+ 3
+ )
+ )
+
+ for recent_messages, relevant_messages, culprit, total_links in cases:
+ last_message = recent_messages[0]
+
+ with self.subTest(
+ last_message=last_message,
+ recent_messages=recent_messages,
+ relevant_messages=relevant_messages,
+ culprit=culprit,
+ total_links=total_links,
+ config=self.config
+ ):
+ desired_output = (
+ f"sent {total_links} links in {self.config['interval']}s",
+ culprit,
+ relevant_messages
+ )
+ self.assertTupleEqual(
+ await links.apply(last_message, recent_messages, self.config),
+ desired_output
+ )