Repository: python-discord/bot

Issue:
Make sure that messages from private channels can't be ducked
Since our `#duck-pond` channel can be viewed by all our Helpers and we *apparently* can't trust our Moderators (me included :pensive:), we need to add a check to make sure that messages from channels not viewable by Helpers can't be relayed to the duck pond. 

This can be done by adding a new check function in [`bot/bot/exts/fun/duck_pond.py`](https://github.com/python-discord/bot/blob/master/bot/exts/fun/duck_pond.py) and using it in `on_raw_reaction_add()`.


Discussion:
I'd like to work on this 😄 

Resolving patch:
diff --git a/bot/exts/fun/duck_pond.py b/bot/exts/fun/duck_pond.py
--- a/bot/exts/fun/duck_pond.py
+++ b/bot/exts/fun/duck_pond.py
@@ -3,7 +3,7 @@
 from typing import Union
 
 import discord
-from discord import Color, Embed, Member, Message, RawReactionActionEvent, User, errors
+from discord import Color, Embed, Member, Message, RawReactionActionEvent, TextChannel, User, errors
 from discord.ext.commands import Cog, Context, command
 
 from bot import constants
@@ -44,6 +44,17 @@ def is_staff(member: Union[User, Member]) -> bool:
                     return True
         return False
 
+    @staticmethod
+    def is_helper_viewable(channel: TextChannel) -> bool:
+        """Check if helpers can view a specific channel."""
+        guild = channel.guild
+        helper_role = guild.get_role(constants.Roles.helpers)
+        # check channel overwrites for both the Helper role and @everyone and
+        # return True for channels that they have permissions to view.
+        helper_overwrites = channel.overwrites_for(helper_role)
+        default_overwrites = channel.overwrites_for(guild.default_role)
+        return default_overwrites.view_channel is None or helper_overwrites.view_channel is True
+
     async def has_green_checkmark(self, message: Message) -> bool:
         """Check if the message has a green checkmark reaction."""
         for reaction in message.reactions:
@@ -107,7 +118,7 @@ async def relay_message(self, message: Message) -> None:
             except discord.HTTPException:
                 log.exception("Failed to send an attachment to the webhook")
 
-    async def locked_relay(self, message: discord.Message) -> bool:
+    async def locked_relay(self, message: Message) -> bool:
         """Relay a message after obtaining the relay lock."""
         if self.relay_lock is None:
             # Lazily load the lock to ensure it's created within the
@@ -162,6 +173,10 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent) -> None:
         if channel is None:
             return
 
+        # Was the message sent in a channel Helpers can see?
+        if not self.is_helper_viewable(channel):
+            return
+
         message = await channel.fetch_message(payload.message_id)
         member = discord.utils.get(message.guild.members, id=payload.user_id)
 
@@ -201,7 +216,7 @@ async def on_raw_reaction_remove(self, payload: RawReactionActionEvent) -> None:
 
     @command(name="duckify", aliases=("duckpond", "pondify"))
     @has_any_role(constants.Roles.admins)
-    async def duckify(self, ctx: Context, message: discord.Message) -> None:
+    async def duckify(self, ctx: Context, message: Message) -> None:
         """Relay a message to the duckpond, no ducks required!"""
         if await self.locked_relay(message):
             await ctx.message.add_reaction("🦆")


Test patch: