0

I'm trying to make discord bot execute python code line by line similar to python interactive shell

Everything works fine but I have some problems with loops

Instead of printing i one by one, and waiting second between them, it print 0 to 9 instantly

There is a simplified example:

if 'q' in message.content:
   redirected_output = sys.stdout = StringIO()
   exec('for i in range(10):\n   print(i)\n   time.sleep(1)')
if redirected_output.getvalue():
   await message.channel.send(f'`{redirected_output.getvalue()}`')

Video

Full code here

Issue

Attempt using asyncio.sleep inside of async exec

if 'q' in message.content:                               
   redirected_output = sys.stdout = StringIO()
   await aexec('for i in range(10):\n   print(i)\n   await asyncio.sleep(1)')
   if redirected_output.getvalue():
      await message.channel.send(f'`{redirected_output.getvalue()}`')

Result is same, it print from 0 to 9 instantly

  • Please don't put all the details in links. You're only executing `send` once, at the bottom, not each time. Is there a reason that you must eval it? There's nothing dynamic in there, just write it as normal code. Also use `asyncio.sleep` not `time.sleep` – Eric Jin Jul 20 '22 at 21:04
  • Yes I must use eval (exec) because I'm getting code as string from Discord messages. Also I cannot call `asyncio.sleep` inside because it's throw `SyntaxError: 'await' outside async function` – dejan-vasic Jul 20 '22 at 22:06
  • Maybe I can try with [async exec](https://stackoverflow.com/questions/44859165/async-exec-in-python) But it's not what I wanted, it's still doesn't work using `time.sleep` – dejan-vasic Jul 20 '22 at 22:20
  • Please dont. Why do you need to exec it? The same problem is there - you’re sending the message once at the end from your output buffer. (Why do you need file-like io for that anyway?) – Eric Jin Jul 21 '22 at 02:07
  • So different approach, What u suggest? I need exec` to execute python code from string come from Discord mesage and redirect standard output to Discord. – dejan-vasic Jul 21 '22 at 04:51
  • **Please don't execute arbitrary code that other people give you.** What if I did `print(client.http.token)`? Or `__import__('os').system('rm -rf /')`? You can't redirect your output to discord (it's a web service), use `ctx.send`/`message.channel.send` for that. – Eric Jin Jul 21 '22 at 12:33
  • I can prevent people to do that with: [Prevent python to import certain module](https://stackoverflow.com/questions/1350466/preventing-python-code-from-importing-certain-modules) and [Global and Local parametars](https://www.programiz.com/python-programming/methods/built-in/exec#:~:text=we%20have%20blocked) If it becaome larger project, for now it's a small personal project with people I know and trust – dejan-vasic Jul 21 '22 at 13:35
  • No. You can't. [See my answer here.](https://stackoverflow.com/a/72834811/11107754) There will always be a way to execute something nasty: `[v for c,v in [c for c in ''.__class__.__bases__[0].__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__.items() if c == '__import__'][0]('os').system('echo hi') ` – Eric Jin Jul 21 '22 at 13:39
  • BTW, `del [c for c in ''.__class__.__bases__[0].__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.sys.modules['os']` will allow you to import os again. If you must, https://github.com/Rapptz/RoboDanny/blob/rewrite/cogs/admin.py#L215 – Eric Jin Jul 21 '22 at 13:59
  • Alright, another approach. What If I restrict user to specific directory, make some server or SSH? Somehow, any ideas? – dejan-vasic Jul 22 '22 at 11:03
  • I’m going to stop arguing about the danger of eval. It’s not really possible to redirect output like this, you need to have `await channel.send` rather than `print`s. See the link above on how to make it async. – Eric Jin Jul 22 '22 at 11:54

0 Answers0