We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[V3] Error in command repl
### Type:

- [ ] Suggestion
- [x] Bug

### Brief description of the problem

So I first tried to use the core repl function on V3 for the first time. I set a very basic value and got an error

### Steps to reproduce

1. `=repl`
2. `level = 1`

![screen shot 2017-12-10 at 19 42 22](https://user-images.githubusercontent.com/23153234/33808207-a5778400-dde2-11e7-99a7-861432d50eda.png)

Full error:

```Py
Exception in command 'repl'
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py", line 62, in wrapped
    ret = yield from coro(*args, **kwargs)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/redbot/core/dev_commands.py", line 273, in repl
    msg = self.sanitize_output(ctx, msg)
  File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/redbot/core/dev_commands.py", line 64, in sanitize_output
    result = input_.replace(token, r)
AttributeError: 'NoneType' object has no attribute 'replace'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/discord/ext/commands/bot.py", line 886, in invoke
    yield from ctx.command.invoke(ctx)
  File "/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py", line 489, in invoke
    yield from injected(*ctx.args, **ctx.kwargs)
  File "/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py", line 71, in wrapped
    raise CommandInvokeError(e) from e
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'replace'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redbot/core/dev_commands.py`
Content:
```
1 import asyncio
2 import inspect
3 import io
4 import textwrap
5 import traceback
6 from contextlib import redirect_stdout
7 from copy import copy
8 
9 import discord
10 from discord.ext import commands
11 from . import checks
12 from .i18n import CogI18n
13 from .utils.chat_formatting import box, pagify
14 """
15 Notice:
16 
17 95% of the below code came from R.Danny which can be found here:
18 
19 https://github.com/Rapptz/RoboDanny/blob/master/cogs/repl.py
20 """
21 
22 _ = CogI18n("Dev", __file__)
23 
24 
25 class Dev:
26     """Various development focused utilities."""
27 
28     def __init__(self):
29         self._last_result = None
30         self.sessions = set()
31 
32     @staticmethod
33     def cleanup_code(content):
34         """Automatically removes code blocks from the code."""
35         # remove ```py\n```
36         if content.startswith('```') and content.endswith('```'):
37             return '\n'.join(content.split('\n')[1:-1])
38 
39         # remove `foo`
40         return content.strip('` \n')
41 
42     @staticmethod
43     def get_syntax_error(e):
44         """Format a syntax error to send to the user.
45 
46         Returns a string representation of the error formatted as a codeblock.
47         """
48         if e.text is None:
49             return box('{0.__class__.__name__}: {0}'.format(e), lang="py")
50         return box(
51             '{0.text}{1:>{0.offset}}\n{2}: {0}'
52             ''.format(e, '^', type(e).__name__),
53             lang="py")
54 
55     @staticmethod
56     def get_pages(msg: str):
57         """Pagify the given message for output to the user."""
58         return pagify(msg, delims=["\n", " "], priority=True, shorten_by=10)
59 
60     @staticmethod
61     def sanitize_output(ctx: commands.Context, input_: str) -> str:
62         """Hides the bot's token from a string."""
63         token = ctx.bot.http.token
64         r = "[EXPUNGED]"
65         result = input_.replace(token, r)
66         result = result.replace(token.lower(), r)
67         result = result.replace(token.upper(), r)
68         return result
69 
70     @commands.command()
71     @checks.is_owner()
72     async def debug(self, ctx, *, code):
73         """Evaluate a statement of python code.
74 
75         The bot will always respond with the return value of the code.
76         If the return value of the code is a coroutine, it will be awaited,
77         and the result of that will be the bot's response.
78 
79         Note: Only one statement may be evaluated. Using await, yield or
80         similar restricted keywords will result in a syntax error. For multiple
81         lines or asynchronous code, see [p]repl or [p]eval.
82 
83         Environment Variables:
84             ctx      - command invokation context
85             bot      - bot object
86             channel  - the current channel object
87             author   - command author's member object
88             message  - the command's message object
89             discord  - discord.py library
90             commands - discord.py commands extension
91             _        - The result of the last dev command.
92         """
93         env = {
94             'bot': ctx.bot,
95             'ctx': ctx,
96             'channel': ctx.channel,
97             'author': ctx.author,
98             'guild': ctx.guild,
99             'message': ctx.message,
100             'discord': discord,
101             'commands': commands,
102             '_': self._last_result
103         }
104 
105         code = self.cleanup_code(code)
106 
107         try:
108             result = eval(code, env)
109         except SyntaxError as e:
110             await ctx.send(self.get_syntax_error(e))
111             return
112         except Exception as e:
113             await ctx.send(
114                 box('{}: {!s}'.format(type(e).__name__, e), lang='py'))
115             return
116 
117         if asyncio.iscoroutine(result):
118             result = await result
119 
120         self._last_result = result
121 
122         result = self.sanitize_output(ctx, str(result))
123 
124         await ctx.send_interactive(self.get_pages(result), box_lang="py")
125 
126     @commands.command(name='eval')
127     @checks.is_owner()
128     async def _eval(self, ctx, *, body: str):
129         """Execute asynchronous code.
130 
131         This command wraps code into the body of an async function and then
132         calls and awaits it. The bot will respond with anything printed to
133         stdout, as well as the return value of the function.
134 
135         The code can be within a codeblock, inline code or neither, as long
136         as they are not mixed and they are formatted correctly.
137 
138         Environment Variables:
139             ctx      - command invokation context
140             bot      - bot object
141             channel  - the current channel object
142             author   - command author's member object
143             message  - the command's message object
144             discord  - discord.py library
145             commands - discord.py commands extension
146             _        - The result of the last dev command.
147         """
148         env = {
149             'bot': ctx.bot,
150             'ctx': ctx,
151             'channel': ctx.channel,
152             'author': ctx.author,
153             'guild': ctx.guild,
154             'message': ctx.message,
155             'discord': discord,
156             'commands': commands,
157             '_': self._last_result
158         }
159 
160         body = self.cleanup_code(body)
161         stdout = io.StringIO()
162 
163         to_compile = 'async def func():\n%s' % textwrap.indent(body, '  ')
164 
165         try:
166             exec(to_compile, env)
167         except SyntaxError as e:
168             return await ctx.send(self.get_syntax_error(e))
169 
170         func = env['func']
171         result = None
172         try:
173             with redirect_stdout(stdout):
174                 result = await func()
175         except:
176             printed = "{}{}".format(stdout.getvalue(), traceback.format_exc())
177         else:
178             printed = stdout.getvalue()
179             await ctx.tick()
180 
181         if result is not None:
182             self._last_result = result
183             msg = "{}{}".format(printed, result)
184         else:
185             msg = printed
186         msg = self.sanitize_output(ctx, msg)
187 
188         await ctx.send_interactive(self.get_pages(msg), box_lang="py")
189 
190     @commands.command()
191     @checks.is_owner()
192     async def repl(self, ctx):
193         """Open an interactive REPL.
194 
195         The REPL will only recognise code as messages which start with a
196         backtick. This includes codeblocks, and as such multiple lines can be
197         evaluated.
198 
199         You may not await any code in this REPL unless you define it inside an
200         async function.
201         """
202         variables = {
203             'ctx': ctx,
204             'bot': ctx.bot,
205             'message': ctx.message,
206             'guild': ctx.guild,
207             'channel': ctx.channel,
208             'author': ctx.author,
209             '_': None,
210         }
211 
212         if ctx.channel.id in self.sessions:
213             await ctx.send(_('Already running a REPL session in this channel. '
214                              'Exit it with `quit`.'))
215             return
216 
217         self.sessions.add(ctx.channel.id)
218         await ctx.send(_('Enter code to execute or evaluate.'
219                          ' `exit()` or `quit` to exit.'))
220 
221         msg_check = lambda m: (m.author == ctx.author and
222                                m.channel == ctx.channel and
223                                m.content.startswith('`'))
224 
225         while True:
226             response = await ctx.bot.wait_for("message", check=msg_check)
227 
228             cleaned = self.cleanup_code(response.content)
229 
230             if cleaned in ('quit', 'exit', 'exit()'):
231                 await ctx.send('Exiting.')
232                 self.sessions.remove(ctx.channel.id)
233                 return
234 
235             executor = exec
236             if cleaned.count('\n') == 0:
237                 # single statement, potentially 'eval'
238                 try:
239                     code = compile(cleaned, '<repl session>', 'eval')
240                 except SyntaxError:
241                     pass
242                 else:
243                     executor = eval
244 
245             if executor is exec:
246                 try:
247                     code = compile(cleaned, '<repl session>', 'exec')
248                 except SyntaxError as e:
249                     await ctx.send(self.get_syntax_error(e))
250                     continue
251 
252             variables['message'] = response
253 
254             stdout = io.StringIO()
255 
256             msg = None
257 
258             try:
259                 with redirect_stdout(stdout):
260                     result = executor(code, variables)
261                     if inspect.isawaitable(result):
262                         result = await result
263             except:
264                 value = stdout.getvalue()
265                 msg = "{}{}".format(value, traceback.format_exc())
266             else:
267                 value = stdout.getvalue()
268                 if result is not None:
269                     msg = "{}{}".format(value, result)
270                     variables['_'] = result
271                 elif value:
272                     msg = "{}".format(value)
273 
274             msg = self.sanitize_output(ctx, msg)
275 
276             try:
277                 await ctx.send_interactive(self.get_pages(msg), box_lang="py")
278             except discord.Forbidden:
279                 pass
280             except discord.HTTPException as e:
281                 await ctx.send(_('Unexpected error: `{}`').format(e))
282 
283     @commands.command()
284     @checks.is_owner()
285     async def mock(self, ctx, user: discord.Member, *, command):
286         """Mock another user invoking a command.
287 
288         The prefix must not be entered.
289         """
290         msg = copy(ctx.message)
291         msg.author = user
292         msg.content = ctx.prefix + command
293 
294         ctx.bot.dispatch('message', msg)
295 
296     @commands.command(name="mockmsg")
297     @checks.is_owner()
298     async def mock_msg(self, ctx, user: discord.Member, *, content: str):
299         """Dispatch a message event as if it were sent by a different user.
300 
301         Only reads the raw content of the message. Attachments, embeds etc. are
302         ignored.
303         """
304         old_author = ctx.author
305         old_content = ctx.message.content
306         ctx.message.author = user
307         ctx.message.content = content
308 
309         ctx.bot.dispatch("message", ctx.message)
310 
311         # If we change the author and content back too quickly,
312         # the bot won't process the mocked message in time.
313         await asyncio.sleep(2)
314         ctx.message.author = old_author
315         ctx.message.content = old_content
316
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redbot/core/dev_commands.py b/redbot/core/dev_commands.py
--- a/redbot/core/dev_commands.py
+++ b/redbot/core/dev_commands.py
@@ -253,7 +253,7 @@
 
             stdout = io.StringIO()
 
-            msg = None
+            msg = ""
 
             try:
                 with redirect_stdout(stdout):

{"golden_diff": "diff --git a/redbot/core/dev_commands.py b/redbot/core/dev_commands.py\n--- a/redbot/core/dev_commands.py\n+++ b/redbot/core/dev_commands.py\n@@ -253,7 +253,7 @@\n \n             stdout = io.StringIO()\n \n-            msg = None\n+            msg = \"\"\n \n             try:\n                 with redirect_stdout(stdout):\n", "issue": "[V3] Error in command repl\n### Type:\r\n\r\n- [ ] Suggestion\r\n- [x] Bug\r\n\r\n### Brief description of the problem\r\n\r\nSo I first tried to use the core repl function on V3 for the first time. I set a very basic value and got an error\r\n\r\n### Steps to reproduce\r\n\r\n1. `=repl`\r\n2. `level = 1`\r\n\r\n![screen shot 2017-12-10 at 19 42 22](https://user-images.githubusercontent.com/23153234/33808207-a5778400-dde2-11e7-99a7-861432d50eda.png)\r\n\r\nFull error:\r\n\r\n```Py\r\nException in command 'repl'\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py\", line 62, in wrapped\r\n    ret = yield from coro(*args, **kwargs)\r\n  File \"/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/redbot/core/dev_commands.py\", line 273, in repl\r\n    msg = self.sanitize_output(ctx, msg)\r\n  File \"/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/redbot/core/dev_commands.py\", line 64, in sanitize_output\r\n    result = input_.replace(token, r)\r\nAttributeError: 'NoneType' object has no attribute 'replace'\r\n\r\nThe above exception was the direct cause of the following exception:\r\n\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.6/site-packages/discord/ext/commands/bot.py\", line 886, in invoke\r\n    yield from ctx.command.invoke(ctx)\r\n  File \"/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py\", line 489, in invoke\r\n    yield from injected(*ctx.args, **ctx.kwargs)\r\n  File \"/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py\", line 71, in wrapped\r\n    raise CommandInvokeError(e) from e\r\ndiscord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'replace'\r\n```\n", "before_files": [{"content": "import asyncio\nimport inspect\nimport io\nimport textwrap\nimport traceback\nfrom contextlib import redirect_stdout\nfrom copy import copy\n\nimport discord\nfrom discord.ext import commands\nfrom . import checks\nfrom .i18n import CogI18n\nfrom .utils.chat_formatting import box, pagify\n\"\"\"\nNotice:\n\n95% of the below code came from R.Danny which can be found here:\n\nhttps://github.com/Rapptz/RoboDanny/blob/master/cogs/repl.py\n\"\"\"\n\n_ = CogI18n(\"Dev\", __file__)\n\n\nclass Dev:\n    \"\"\"Various development focused utilities.\"\"\"\n\n    def __init__(self):\n        self._last_result = None\n        self.sessions = set()\n\n    @staticmethod\n    def cleanup_code(content):\n        \"\"\"Automatically removes code blocks from the code.\"\"\"\n        # remove ```py\\n```\n        if content.startswith('```') and content.endswith('```'):\n            return '\\n'.join(content.split('\\n')[1:-1])\n\n        # remove `foo`\n        return content.strip('` \\n')\n\n    @staticmethod\n    def get_syntax_error(e):\n        \"\"\"Format a syntax error to send to the user.\n\n        Returns a string representation of the error formatted as a codeblock.\n        \"\"\"\n        if e.text is None:\n            return box('{0.__class__.__name__}: {0}'.format(e), lang=\"py\")\n        return box(\n            '{0.text}{1:>{0.offset}}\\n{2}: {0}'\n            ''.format(e, '^', type(e).__name__),\n            lang=\"py\")\n\n    @staticmethod\n    def get_pages(msg: str):\n        \"\"\"Pagify the given message for output to the user.\"\"\"\n        return pagify(msg, delims=[\"\\n\", \" \"], priority=True, shorten_by=10)\n\n    @staticmethod\n    def sanitize_output(ctx: commands.Context, input_: str) -> str:\n        \"\"\"Hides the bot's token from a string.\"\"\"\n        token = ctx.bot.http.token\n        r = \"[EXPUNGED]\"\n        result = input_.replace(token, r)\n        result = result.replace(token.lower(), r)\n        result = result.replace(token.upper(), r)\n        return result\n\n    @commands.command()\n    @checks.is_owner()\n    async def debug(self, ctx, *, code):\n        \"\"\"Evaluate a statement of python code.\n\n        The bot will always respond with the return value of the code.\n        If the return value of the code is a coroutine, it will be awaited,\n        and the result of that will be the bot's response.\n\n        Note: Only one statement may be evaluated. Using await, yield or\n        similar restricted keywords will result in a syntax error. For multiple\n        lines or asynchronous code, see [p]repl or [p]eval.\n\n        Environment Variables:\n            ctx      - command invokation context\n            bot      - bot object\n            channel  - the current channel object\n            author   - command author's member object\n            message  - the command's message object\n            discord  - discord.py library\n            commands - discord.py commands extension\n            _        - The result of the last dev command.\n        \"\"\"\n        env = {\n            'bot': ctx.bot,\n            'ctx': ctx,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            'guild': ctx.guild,\n            'message': ctx.message,\n            'discord': discord,\n            'commands': commands,\n            '_': self._last_result\n        }\n\n        code = self.cleanup_code(code)\n\n        try:\n            result = eval(code, env)\n        except SyntaxError as e:\n            await ctx.send(self.get_syntax_error(e))\n            return\n        except Exception as e:\n            await ctx.send(\n                box('{}: {!s}'.format(type(e).__name__, e), lang='py'))\n            return\n\n        if asyncio.iscoroutine(result):\n            result = await result\n\n        self._last_result = result\n\n        result = self.sanitize_output(ctx, str(result))\n\n        await ctx.send_interactive(self.get_pages(result), box_lang=\"py\")\n\n    @commands.command(name='eval')\n    @checks.is_owner()\n    async def _eval(self, ctx, *, body: str):\n        \"\"\"Execute asynchronous code.\n\n        This command wraps code into the body of an async function and then\n        calls and awaits it. The bot will respond with anything printed to\n        stdout, as well as the return value of the function.\n\n        The code can be within a codeblock, inline code or neither, as long\n        as they are not mixed and they are formatted correctly.\n\n        Environment Variables:\n            ctx      - command invokation context\n            bot      - bot object\n            channel  - the current channel object\n            author   - command author's member object\n            message  - the command's message object\n            discord  - discord.py library\n            commands - discord.py commands extension\n            _        - The result of the last dev command.\n        \"\"\"\n        env = {\n            'bot': ctx.bot,\n            'ctx': ctx,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            'guild': ctx.guild,\n            'message': ctx.message,\n            'discord': discord,\n            'commands': commands,\n            '_': self._last_result\n        }\n\n        body = self.cleanup_code(body)\n        stdout = io.StringIO()\n\n        to_compile = 'async def func():\\n%s' % textwrap.indent(body, '  ')\n\n        try:\n            exec(to_compile, env)\n        except SyntaxError as e:\n            return await ctx.send(self.get_syntax_error(e))\n\n        func = env['func']\n        result = None\n        try:\n            with redirect_stdout(stdout):\n                result = await func()\n        except:\n            printed = \"{}{}\".format(stdout.getvalue(), traceback.format_exc())\n        else:\n            printed = stdout.getvalue()\n            await ctx.tick()\n\n        if result is not None:\n            self._last_result = result\n            msg = \"{}{}\".format(printed, result)\n        else:\n            msg = printed\n        msg = self.sanitize_output(ctx, msg)\n\n        await ctx.send_interactive(self.get_pages(msg), box_lang=\"py\")\n\n    @commands.command()\n    @checks.is_owner()\n    async def repl(self, ctx):\n        \"\"\"Open an interactive REPL.\n\n        The REPL will only recognise code as messages which start with a\n        backtick. This includes codeblocks, and as such multiple lines can be\n        evaluated.\n\n        You may not await any code in this REPL unless you define it inside an\n        async function.\n        \"\"\"\n        variables = {\n            'ctx': ctx,\n            'bot': ctx.bot,\n            'message': ctx.message,\n            'guild': ctx.guild,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            '_': None,\n        }\n\n        if ctx.channel.id in self.sessions:\n            await ctx.send(_('Already running a REPL session in this channel. '\n                             'Exit it with `quit`.'))\n            return\n\n        self.sessions.add(ctx.channel.id)\n        await ctx.send(_('Enter code to execute or evaluate.'\n                         ' `exit()` or `quit` to exit.'))\n\n        msg_check = lambda m: (m.author == ctx.author and\n                               m.channel == ctx.channel and\n                               m.content.startswith('`'))\n\n        while True:\n            response = await ctx.bot.wait_for(\"message\", check=msg_check)\n\n            cleaned = self.cleanup_code(response.content)\n\n            if cleaned in ('quit', 'exit', 'exit()'):\n                await ctx.send('Exiting.')\n                self.sessions.remove(ctx.channel.id)\n                return\n\n            executor = exec\n            if cleaned.count('\\n') == 0:\n                # single statement, potentially 'eval'\n                try:\n                    code = compile(cleaned, '<repl session>', 'eval')\n                except SyntaxError:\n                    pass\n                else:\n                    executor = eval\n\n            if executor is exec:\n                try:\n                    code = compile(cleaned, '<repl session>', 'exec')\n                except SyntaxError as e:\n                    await ctx.send(self.get_syntax_error(e))\n                    continue\n\n            variables['message'] = response\n\n            stdout = io.StringIO()\n\n            msg = None\n\n            try:\n                with redirect_stdout(stdout):\n                    result = executor(code, variables)\n                    if inspect.isawaitable(result):\n                        result = await result\n            except:\n                value = stdout.getvalue()\n                msg = \"{}{}\".format(value, traceback.format_exc())\n            else:\n                value = stdout.getvalue()\n                if result is not None:\n                    msg = \"{}{}\".format(value, result)\n                    variables['_'] = result\n                elif value:\n                    msg = \"{}\".format(value)\n\n            msg = self.sanitize_output(ctx, msg)\n\n            try:\n                await ctx.send_interactive(self.get_pages(msg), box_lang=\"py\")\n            except discord.Forbidden:\n                pass\n            except discord.HTTPException as e:\n                await ctx.send(_('Unexpected error: `{}`').format(e))\n\n    @commands.command()\n    @checks.is_owner()\n    async def mock(self, ctx, user: discord.Member, *, command):\n        \"\"\"Mock another user invoking a command.\n\n        The prefix must not be entered.\n        \"\"\"\n        msg = copy(ctx.message)\n        msg.author = user\n        msg.content = ctx.prefix + command\n\n        ctx.bot.dispatch('message', msg)\n\n    @commands.command(name=\"mockmsg\")\n    @checks.is_owner()\n    async def mock_msg(self, ctx, user: discord.Member, *, content: str):\n        \"\"\"Dispatch a message event as if it were sent by a different user.\n\n        Only reads the raw content of the message. Attachments, embeds etc. are\n        ignored.\n        \"\"\"\n        old_author = ctx.author\n        old_content = ctx.message.content\n        ctx.message.author = user\n        ctx.message.content = content\n\n        ctx.bot.dispatch(\"message\", ctx.message)\n\n        # If we change the author and content back too quickly,\n        # the bot won't process the mocked message in time.\n        await asyncio.sleep(2)\n        ctx.message.author = old_author\n        ctx.message.content = old_content\n", "path": "redbot/core/dev_commands.py"}], "after_files": [{"content": "import asyncio\nimport inspect\nimport io\nimport textwrap\nimport traceback\nfrom contextlib import redirect_stdout\nfrom copy import copy\n\nimport discord\nfrom discord.ext import commands\nfrom . import checks\nfrom .i18n import CogI18n\nfrom .utils.chat_formatting import box, pagify\n\"\"\"\nNotice:\n\n95% of the below code came from R.Danny which can be found here:\n\nhttps://github.com/Rapptz/RoboDanny/blob/master/cogs/repl.py\n\"\"\"\n\n_ = CogI18n(\"Dev\", __file__)\n\n\nclass Dev:\n    \"\"\"Various development focused utilities.\"\"\"\n\n    def __init__(self):\n        self._last_result = None\n        self.sessions = set()\n\n    @staticmethod\n    def cleanup_code(content):\n        \"\"\"Automatically removes code blocks from the code.\"\"\"\n        # remove ```py\\n```\n        if content.startswith('```') and content.endswith('```'):\n            return '\\n'.join(content.split('\\n')[1:-1])\n\n        # remove `foo`\n        return content.strip('` \\n')\n\n    @staticmethod\n    def get_syntax_error(e):\n        \"\"\"Format a syntax error to send to the user.\n\n        Returns a string representation of the error formatted as a codeblock.\n        \"\"\"\n        if e.text is None:\n            return box('{0.__class__.__name__}: {0}'.format(e), lang=\"py\")\n        return box(\n            '{0.text}{1:>{0.offset}}\\n{2}: {0}'\n            ''.format(e, '^', type(e).__name__),\n            lang=\"py\")\n\n    @staticmethod\n    def get_pages(msg: str):\n        \"\"\"Pagify the given message for output to the user.\"\"\"\n        return pagify(msg, delims=[\"\\n\", \" \"], priority=True, shorten_by=10)\n\n    @staticmethod\n    def sanitize_output(ctx: commands.Context, input_: str) -> str:\n        \"\"\"Hides the bot's token from a string.\"\"\"\n        token = ctx.bot.http.token\n        r = \"[EXPUNGED]\"\n        result = input_.replace(token, r)\n        result = result.replace(token.lower(), r)\n        result = result.replace(token.upper(), r)\n        return result\n\n    @commands.command()\n    @checks.is_owner()\n    async def debug(self, ctx, *, code):\n        \"\"\"Evaluate a statement of python code.\n\n        The bot will always respond with the return value of the code.\n        If the return value of the code is a coroutine, it will be awaited,\n        and the result of that will be the bot's response.\n\n        Note: Only one statement may be evaluated. Using await, yield or\n        similar restricted keywords will result in a syntax error. For multiple\n        lines or asynchronous code, see [p]repl or [p]eval.\n\n        Environment Variables:\n            ctx      - command invokation context\n            bot      - bot object\n            channel  - the current channel object\n            author   - command author's member object\n            message  - the command's message object\n            discord  - discord.py library\n            commands - discord.py commands extension\n            _        - The result of the last dev command.\n        \"\"\"\n        env = {\n            'bot': ctx.bot,\n            'ctx': ctx,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            'guild': ctx.guild,\n            'message': ctx.message,\n            'discord': discord,\n            'commands': commands,\n            '_': self._last_result\n        }\n\n        code = self.cleanup_code(code)\n\n        try:\n            result = eval(code, env)\n        except SyntaxError as e:\n            await ctx.send(self.get_syntax_error(e))\n            return\n        except Exception as e:\n            await ctx.send(\n                box('{}: {!s}'.format(type(e).__name__, e), lang='py'))\n            return\n\n        if asyncio.iscoroutine(result):\n            result = await result\n\n        self._last_result = result\n\n        result = self.sanitize_output(ctx, str(result))\n\n        await ctx.send_interactive(self.get_pages(result), box_lang=\"py\")\n\n    @commands.command(name='eval')\n    @checks.is_owner()\n    async def _eval(self, ctx, *, body: str):\n        \"\"\"Execute asynchronous code.\n\n        This command wraps code into the body of an async function and then\n        calls and awaits it. The bot will respond with anything printed to\n        stdout, as well as the return value of the function.\n\n        The code can be within a codeblock, inline code or neither, as long\n        as they are not mixed and they are formatted correctly.\n\n        Environment Variables:\n            ctx      - command invokation context\n            bot      - bot object\n            channel  - the current channel object\n            author   - command author's member object\n            message  - the command's message object\n            discord  - discord.py library\n            commands - discord.py commands extension\n            _        - The result of the last dev command.\n        \"\"\"\n        env = {\n            'bot': ctx.bot,\n            'ctx': ctx,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            'guild': ctx.guild,\n            'message': ctx.message,\n            'discord': discord,\n            'commands': commands,\n            '_': self._last_result\n        }\n\n        body = self.cleanup_code(body)\n        stdout = io.StringIO()\n\n        to_compile = 'async def func():\\n%s' % textwrap.indent(body, '  ')\n\n        try:\n            exec(to_compile, env)\n        except SyntaxError as e:\n            return await ctx.send(self.get_syntax_error(e))\n\n        func = env['func']\n        result = None\n        try:\n            with redirect_stdout(stdout):\n                result = await func()\n        except:\n            printed = \"{}{}\".format(stdout.getvalue(), traceback.format_exc())\n        else:\n            printed = stdout.getvalue()\n            await ctx.tick()\n\n        if result is not None:\n            self._last_result = result\n            msg = \"{}{}\".format(printed, result)\n        else:\n            msg = printed\n        msg = self.sanitize_output(ctx, msg)\n\n        await ctx.send_interactive(self.get_pages(msg), box_lang=\"py\")\n\n    @commands.command()\n    @checks.is_owner()\n    async def repl(self, ctx):\n        \"\"\"Open an interactive REPL.\n\n        The REPL will only recognise code as messages which start with a\n        backtick. This includes codeblocks, and as such multiple lines can be\n        evaluated.\n\n        You may not await any code in this REPL unless you define it inside an\n        async function.\n        \"\"\"\n        variables = {\n            'ctx': ctx,\n            'bot': ctx.bot,\n            'message': ctx.message,\n            'guild': ctx.guild,\n            'channel': ctx.channel,\n            'author': ctx.author,\n            '_': None,\n        }\n\n        if ctx.channel.id in self.sessions:\n            await ctx.send(_('Already running a REPL session in this channel. '\n                             'Exit it with `quit`.'))\n            return\n\n        self.sessions.add(ctx.channel.id)\n        await ctx.send(_('Enter code to execute or evaluate.'\n                         ' `exit()` or `quit` to exit.'))\n\n        msg_check = lambda m: (m.author == ctx.author and\n                               m.channel == ctx.channel and\n                               m.content.startswith('`'))\n\n        while True:\n            response = await ctx.bot.wait_for(\"message\", check=msg_check)\n\n            cleaned = self.cleanup_code(response.content)\n\n            if cleaned in ('quit', 'exit', 'exit()'):\n                await ctx.send('Exiting.')\n                self.sessions.remove(ctx.channel.id)\n                return\n\n            executor = exec\n            if cleaned.count('\\n') == 0:\n                # single statement, potentially 'eval'\n                try:\n                    code = compile(cleaned, '<repl session>', 'eval')\n                except SyntaxError:\n                    pass\n                else:\n                    executor = eval\n\n            if executor is exec:\n                try:\n                    code = compile(cleaned, '<repl session>', 'exec')\n                except SyntaxError as e:\n                    await ctx.send(self.get_syntax_error(e))\n                    continue\n\n            variables['message'] = response\n\n            stdout = io.StringIO()\n\n            msg = \"\"\n\n            try:\n                with redirect_stdout(stdout):\n                    result = executor(code, variables)\n                    if inspect.isawaitable(result):\n                        result = await result\n            except:\n                value = stdout.getvalue()\n                msg = \"{}{}\".format(value, traceback.format_exc())\n            else:\n                value = stdout.getvalue()\n                if result is not None:\n                    msg = \"{}{}\".format(value, result)\n                    variables['_'] = result\n                elif value:\n                    msg = \"{}\".format(value)\n\n            msg = self.sanitize_output(ctx, msg)\n\n            try:\n                await ctx.send_interactive(self.get_pages(msg), box_lang=\"py\")\n            except discord.Forbidden:\n                pass\n            except discord.HTTPException as e:\n                await ctx.send(_('Unexpected error: `{}`').format(e))\n\n    @commands.command()\n    @checks.is_owner()\n    async def mock(self, ctx, user: discord.Member, *, command):\n        \"\"\"Mock another user invoking a command.\n\n        The prefix must not be entered.\n        \"\"\"\n        msg = copy(ctx.message)\n        msg.author = user\n        msg.content = ctx.prefix + command\n\n        ctx.bot.dispatch('message', msg)\n\n    @commands.command(name=\"mockmsg\")\n    @checks.is_owner()\n    async def mock_msg(self, ctx, user: discord.Member, *, content: str):\n        \"\"\"Dispatch a message event as if it were sent by a different user.\n\n        Only reads the raw content of the message. Attachments, embeds etc. are\n        ignored.\n        \"\"\"\n        old_author = ctx.author\n        old_content = ctx.message.content\n        ctx.message.author = user\n        ctx.message.content = content\n\n        ctx.bot.dispatch(\"message\", ctx.message)\n\n        # If we change the author and content back too quickly,\n        # the bot won't process the mocked message in time.\n        await asyncio.sleep(2)\n        ctx.message.author = old_author\n        ctx.message.content = old_content\n", "path": "redbot/core/dev_commands.py"}]}