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 :

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 :

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"))