Salman Fazal
Salman Fazal

Reputation: 587

Call function from another file - Discord bot

I am not familiar with Discord bots or much of Python so here's a simple question I can't figure out the answer to.

I have two files; discord_bot.py and test.py How do I forward a message from test.py to send it to a channel in Discord?

test.py

import discord_bot

discord_bot.signal(msg = "Hi")

discord_bot.py

import discord
from discord.ext import commands

TOKEN = '1234567890'
bot = commands.Bot(command_prefix='!')

@bot.command()
async def signal(ctx, *, msg):
    await ctx.send(msg)

The Discord bot works fine but calling the signal function from test is not the correct way to do it. Any help here please?

Upvotes: 2

Views: 6264

Answers (1)

chluebi
chluebi

Reputation: 1829

This is a lot to unpack.

0. Never post your discord token online. Discord may automatically invalidate your token if it's posted online.

1. You are not running your bot at the moment, add bot.run(TOKEN) at the end

2. How the commands of the discord bot extension work

@bot.command() is a decorator, if you do not know how they work, read up on it. Overly simplified, they take your function and register in the bot.

The inner workings of the commands extension are basically:

  1. Register all commands by loading the decorators
  2. Whenever a message arrives, check if it contains a prefix and if so, check if it fits a command.
  3. If both checks from 2 passed, construct a Context object, then pass that object to the function. Something like the following:
signal(ctx, *args)

This is why the ctx object can't be positional, because the way the function is called in the inner workings of the bot as a normal argument.

4. Do not try to mess with creating your own context object, unless you know what you're doing. You only need to create context objects if you're overriding the default message parser.

5. Don't use commands for this.

What you want to do, as far as I can tell: Call a command yourself. This is easy enough:

file 1:

@bot.command()
async def signal(ctx, *, msg):
    print(msg)

file 2:

from file1 import signal
import asyncio # if you don't know asyncio, read up on it

asyncio.run(signal(None, 'This is an argument'))

This works easily, it prints your stuff. But you don't want it to be printed, right? You want it to be sent in a channel. This is what you need the context object for, which I said before, to not construct yourself. So how do we actually do this?

The answer is: Don't use commands. They are used for reacting to messages, not to be called by themselves.

6. The solution you (probably) want

So the major changes here are:

  1. signal is now a normal async function with no decorator

  2. We actually specify a channel where we want the stuff to be sent in as an argument of the function

file 1:

import discord
from discord.ext import commands

TOKEN = 'do not share your token online'
bot = commands.Bot(command_prefix='!')

# as the channel_id, pass the channel_id you want the message to be sent in
async def signal(msg, channel_id):
    global bot # getting our bot variable from the global context
    channel = bot.get_channel(channel_id)
    await channel.send(msg)

bot.run(TOKEN)

Major changes here are:

  1. We use asyncio.run to call the function. Async functions cannot be called with regular syntax.
  2. You'll probably need to run file2.py to start the program. Running file1 will not load file2.

file 2

from file1 import signal
from time import sleep
import asyncio

sleep(5) # We need to give our bot time to log in, or it won't work
asyncio.run(signal('hi!', 123))

Upvotes: 5

Related Questions