Python: Eval dictionary value from matched string key (discord.py)

Viewed 82

So I'm writing a discord bot with discord.py, and I'm wishing to call a function based on user input. I need to take the user's input, match it to a dictionary key, and have the value call the function.

Such as:
User sends message like "!b command arg"
If Command matches a dictionary key, execute value like "command"="function()"

What I have now is

cmds = open('commands.txt', 'rU') 
for line in cmds:
    if firstCommand == line:
        "something to pull relevant dict value"
        exec(something)

I know absolutely that this won't work. But I'm stuck on how to make it work. I hope I've made enough sense such that I can get some help on this, cause I'm stuck but definitely feel like this should be possible.
For the record, firstCommand already has !b removed, as well as all text after the first command (args). splitCommand has all the args as a list after [0].

1 Answers

It seems like you should be using Cogs here, but what you are trying to do is entirely possible. Here's how :

I have a test file with the following functions :

def foo():
    print("This is working!")

Doing this will let me call the function outside the main file :

@bot.command()
async def func(ctx, funcname, *args, **kwargs):
    if not hasattr(__import__("test"), funcname):
        await ctx.send("Function not found!")
        return
    getattr(__import__("test"), funcname)(*args, **kwargs)

(replace test with your module name)

This will result in the following : discord message stdout

You can do a lot more cooler things if you can pass in context and the function accepts it.

Here's what I mean : test.py :

def foo(_):
    # We'll use an throwaway variable name because we don't need ctx here
    print("This is working!")


async def bar(ctx):
    # To demonstrate a function that also takes a positional argument
    await ctx.send(f"This is working!, hello there {ctx.author.name}")

main.py :

@bot.command()
async def func(ctx, funcname, *args, **kwargs):
    if not hasattr(__import__("test"), funcname):
        await ctx.send("Function not found!")
        return
    function = getattr(__import__("test"), funcname)
    if asyncio.iscoroutinefunction(function):
        await function(ctx, *args, **kwargs)
        return
    function(ctx, *args, **kwargs)
    # Do notice how I passed in the context there
    # and how coro functions are accomoated for.

Result : final result

Full code of main.py :

import discord
import os
import asyncio
from discord.ext import commands

bot = commands.Bot(command_prefix="?")


@bot.event
async def on_ready():
    print(f"Sucessfully logged in as {bot.user}")


@bot.command()
async def func(ctx, funcname, *args, **kwargs):
    if not hasattr(__import__("test"), funcname):
        await ctx.send("Function not found!")
        return
    function = getattr(__import__("test"), funcname)
    if asyncio.iscoroutinefunction(function):
        await function(ctx, *args, **kwargs)
        return
    function(ctx, *args, **kwargs)
    # Do notice how I passed in the context there
    # and how coro functions are accomoated for.


bot.run(os.getenv("DISCORD_TOKEN"))
Related