Repository: python-discord/sir-lancebot
Issue: Automatic linking issues catches a error when message sent to it on DMs.

## Description
The Bot should skip when a message is sent to it on DMs since it is checking for the category and the DMChannel has no category.
```py
    message.channel.category.id in WHITELISTED_CATEGORIES
AttributeError: 'DMChannel' object has no attribute 'category'
```


## Steps to Reproduce
Send a DM message to your test bot, and check your terminal.


##  Behaviour
Should ignore the message which isn't being done currently,


## Possible Solutions
```py
if isinstance(message.channel, discord.DMChannel):
            # This command is only Server specific
            return
```


## Would you like to implement a fix?

***Note: For high-priority or critical bugs, fixes may be implemented by staff.***
<!-- Replace [ ] with [x] with your choice -->

- [ ] I'd like to implement the bug fix
- [x] Anyone can implement the bug fix


Changed files:
bot/exts/evergreen/issues.py, bot/exts/evergreen/wolfram.py, bot/exts/easter/easter_riddle.py, bot/exts/halloween/candy_collection.py

diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py
index 3c612eb1..a93b3745 100644
--- a/bot/exts/easter/easter_riddle.py
+++ b/bot/exts/easter/easter_riddle.py
@@ -7,7 +7,7 @@ from pathlib import Path
 import discord
 from discord.ext import commands
 
-from bot.constants import Colours
+from bot.constants import Colours, NEGATIVE_REPLIES
 
 log = logging.getLogger(__name__)
 
@@ -36,6 +36,17 @@ class EasterRiddle(commands.Cog):
         if self.current_channel:
             return await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!")
 
+        # Don't let users start in a DM
+        if not ctx.guild:
+            await ctx.send(
+                embed=discord.Embed(
+                    title=random.choice(NEGATIVE_REPLIES),
+                    description="You can't start riddles in DMs",
+                    colour=discord.Colour.red()
+                )
+            )
+            return
+
         self.current_channel = ctx.message.channel
 
         random_question = random.choice(RIDDLE_QUESTIONS)
diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py
index bbcbf611..4a73d20b 100644
--- a/bot/exts/evergreen/issues.py
+++ b/bot/exts/evergreen/issues.py
@@ -7,7 +7,16 @@ from enum import Enum
 import discord
 from discord.ext import commands, tasks
 
-from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS
+from bot.constants import (
+    Categories,
+    Channels,
+    Colours,
+    ERROR_REPLIES,
+    Emojis,
+    NEGATIVE_REPLIES,
+    Tokens,
+    WHITELISTED_CHANNELS
+)
 
 log = logging.getLogger(__name__)
 
@@ -150,10 +159,20 @@ class Issues(commands.Cog):
             user: str = "python-discord"
     ) -> None:
         """Command to retrieve issue(s) from a GitHub repository."""
-        if not(
+        if not ctx.guild or not(
             ctx.channel.category.id in WHITELISTED_CATEGORIES
             or ctx.channel.id in WHITELISTED_CHANNELS
         ):
+            await ctx.send(
+                embed=discord.Embed(
+                    title=random.choice(NEGATIVE_REPLIES),
+                    description=(
+                        "You can't run this command in this channel. "
+                        f"Try again in <#{Channels.community_bot_commands}>"
+                    ),
+                    colour=discord.Colour.red()
+                )
+            )
             return
 
         result = await self.fetch_issues(set(numbers), repository, user)
@@ -180,7 +199,8 @@ class Issues(commands.Cog):
     @commands.Cog.listener()
     async def on_message(self, message: discord.Message) -> None:
         """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching <repo>#<issue>."""
-        if not(
+        # Ignore messages not in whitelisted categories / channels, only when in guild.
+        if message.guild and not (
             message.channel.category.id in WHITELISTED_CATEGORIES
             or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE
         ):
@@ -190,6 +210,18 @@ class Issues(commands.Cog):
         links = []
 
         if message_repo_issue_map:
+            if not message.guild:
+                await message.channel.send(
+                    embed=discord.Embed(
+                        title=random.choice(NEGATIVE_REPLIES),
+                        description=(
+                            "You can't retreive issues from DMs. "
+                            f"Try again in <#{Channels.community_bot_commands}>"
+                        ),
+                        colour=discord.Colour.red()
+                    )
+                )
+                return
             for repo_issue in message_repo_issue_map:
                 if not self.check_in_block(message, " ".join(repo_issue)):
                     result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord")
diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py
index 437d9e1a..14ec1041 100644
--- a/bot/exts/evergreen/wolfram.py
+++ b/bot/exts/evergreen/wolfram.py
@@ -62,7 +62,8 @@ def custom_cooldown(*ignore: List[int]) -> Callable:
             # if the invoked command is help we don't want to increase the ratelimits since it's not actually
             # invoking the command/making a request, so instead just check if the user/guild are on cooldown.
             guild_cooldown = not guildcd.get_bucket(ctx.message).get_tokens() == 0  # if guild is on cooldown
-            if not any(r.id in ignore for r in ctx.author.roles):  # check user bucket if user is not ignored
+            # check the message is in a guild, and check user bucket if user is not ignored
+            if ctx.guild and not any(r.id in ignore for r in ctx.author.roles):
                 return guild_cooldown and not usercd.get_bucket(ctx.message).get_tokens() == 0
             return guild_cooldown
 
diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py
index 0cb37ecd..40e21f40 100644
--- a/bot/exts/halloween/candy_collection.py
+++ b/bot/exts/halloween/candy_collection.py
@@ -47,6 +47,9 @@ class CandyCollection(commands.Cog):
     @commands.Cog.listener()
     async def on_message(self, message: discord.Message) -> None:
         """Randomly adds candy or skull reaction to non-bot messages in the Event channel."""
+        # Ignore messages in DMs
+        if not message.guild:
+            return
         # make sure its a human message
         if message.author.bot:
             return