2
votes

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

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 = 'Njc3MjA2MTA5MjM2MjMyMjMx.XkQ3sQ.Ew47BnNqRyZpZSHjze8eGTpd2Q'
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? bot.run(TOKEN)

1
What channel are you trying to send this message to? Sorry if it's not much help, but I would suggest using the discord methods to find the channel you want to send this message to, since it's not explicit when you are running discord_bot.signal from test.py. - Aiden Blishen Cuneo
If I don't speify a channel, it sends it to the default channel - general - S Faz
I think the steps you might need to take would be to firstly get the list of all channels your bot can see, then find the one named "general" and send the message to it. So it would be something like: [a for a in bot.get_channels() if a.name == 'general'][0].send(msg) (I haven't tested this line of code so please don't use it) - Aiden Blishen Cuneo
Although that would not work if your bot can see multiple "general" channels. - Aiden Blishen Cuneo
Okay I guess I forgot to mention this, when I call the signal() function from the test.py file, it does not enter the signal() in the discord_bot.py at all (tried with prints). And there is only one channel at the moment. - S Faz

1 Answers

3
votes

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