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?
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:
Register all commands by loading the decorators
Whenever a message arrives, check if it contains a prefix and if so, check if it fits a command.
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:
signal is now a normal async function with no decorator
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:
We use asyncio.run to call the function. Async functions cannot be called with regular syntax.
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))
Related
I'd like to use TwitchIO to talk to Twitch chat inside another program, without needing to hijack the main loop with Bot's run().
The official documentation here (https://twitchio.readthedocs.io/en/latest/quickstart.html) shows the code being run like:
from twitchio.ext import commands
class Bot(commands.Bot):
def __init__(self):
# Initialise our Bot with our access token, prefix and a list of channels to join on boot...
# prefix can be a callable, which returns a list of strings or a string...
# initial_channels can also be a callable which returns a list of strings...
super().__init__(token='ACCESS_TOKEN', prefix='?', initial_channels=['...'])
async def event_ready(self):
# Notify us when everything is ready!
# We are logged in and ready to chat and use commands...
print(f'Logged in as | {self.nick}')
#commands.command()
async def hello(self, ctx: commands.Context):
# Here we have a command hello, we can invoke our command with our prefix and command name
# e.g ?hello
# We can also give our commands aliases (different names) to invoke with.
# Send a hello back!
# Sending a reply back to the channel is easy... Below is an example.
await ctx.send(f'Hello {ctx.author.name}!')
bot = Bot()
bot.run()
# bot.run() is blocking and will stop execution of any below code here until stopped or closed.
But as that last line says, run() will block execution.
Is there some other way of running it that doesn't block? Something like (made up)
bot.poll()
That would need to be run periodically in my program's main loop?
Are you adding any more code that uses the Bot class? If not I would suggest just making 2 processes.
The simplest way to do this is just creating 2 python files and running both of them at the same time.
If you really must run them both on the same program I would look into parallel processing. The next time you post a question I would suggest putting that "other programs" code into the question so people don't have to make those assumptions.
#ps if you need to run them in the same program edit your question to show the code you need to run together and ill take another look
Hey i am trying to get all the users in a specific guide, and i am getting part of the users and not all of them, why?, And i dont care about discord terms, i am not gonna spam servers or something like this so please help instead telling me the discord rules because i am know it well, This is the code i did,
import discord
import asyncio
intents = discord.Intents(messages=True, guilds=True, members=True)
client = discord.Client(intents=intents)
token = ""
#client.event
async def on_ready():
print("Bot Is Ready!")
guild = client.get_guild(328154277111398403)
for member in guild.members:
print(member)
await asyncio.sleep(0.1)
client.run(token, bot=False)
in the context of selfbots/userbots, discord.py doesn't actually have the ability to get the member list (or, at least, a significant portion of it), and therefore is not suited for this task. Instead, you'll either have to use a different library or code your own solution. Keep on reading for some code :)
Code:
A python lib with this support is discum.
Here's the least amount of code required to get the member list with discum:
import discum
bot = discum.Client(token='blah blah blah')
#bot.gateway.command
def helloworld(resp):
if resp.event.ready_supplemental:
bot.gateway.fetchMembers("GUILD_ID_HERE", "CHANNEL_ID_HERE")
bot.gateway.run()
And here's another example that creates a function called get_members that returns the member list: https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/examples/gettingGuildMembers.py
And here are more examples:
https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/docs/fetchingGuildMembers.md
How does it work?
Since we can't request for the member list the same way bot accounts can, we instead need to exploit the member list (yea, that members sidebar on the right that you see when going to a guild). We need to read that entire thing, piece by piece.
Essentially how it's done is the client first subscribes to member events in a channel in a guild (Luna somewhat went over this in her unofficial discord docs, but left out a lot). Then, as the user scrolls through the member list, more lazy requests are sent to get each chunk of the member list. And this is what happens in discum: lazy requests are sent until the entire member list is fetched. Here's some more info.
I am a intermediate python programmer and just started coding discord bots using the discord.py module. One question that always revolves around my head is, how does the module detect that a message is sent using on_message() coroutine which is declared in our code and no such detection construct is there?
async def on_message(mssg):
#our conditions and responses
Consider the above code. Is there an equivalent predefined coroutine in the module that calls when we declare the on_message() in our code or is there something else that makes it to detect messages and pass it to the function argument, or detect any other event? On youtube or elsewhere, they just make you learn the syntax for some reason that you have to use async...await from the documentation.
so if memory serves me correct, the on_message() is what calls the bot to listen to each message that is sent, every time a message is sent. So with your code there:
async def on_message(mssg):
#our conditions and responses
we can actually dress that up a bit with some inter-workings, like so:
#bot.listen('on_message')
it's a bot event, so every-time a message is sent, this function is called.
async def stuff(message):
pass message so that the bot knows what to scan.
if message.content.startswith("buttlerprefix"):
start it off with an if statement so that the bot has something to check the user's message against
If I typed buttlerprefix and hit enter, it would respond with this message:
msg = await message.channel.send("my prefix is `>`")
if you want to go an extra step, and keep the channels declutterd, you can set the response equal to a variable msg in the event that you want to manipulate it later, thus in this scenario, it's set to auto delete with
await asyncio.sleep(10)
await msg.delete()
So if we put all of that together:
#bot.listen('on_message')
async def stuff(message):
if message.content.startswith("buttlerprefix"):
msg = await message.channel.send("my prefix is `>`")
await asyncio.sleep(10)
await msg.delete()
we get a function that is now scanning every message that comes through the guild that this bot functions in, no matter the channel, due to the on_message() function being called every-time a message is sent which is triggered by the #bot.listen
Using discord.py and python:
Ok so basically I have this bot that updates the best prices for a certain game every minute. However, while I am doing that, other people cannot access the bot. For example, lets just say I have a command called "hello" that when called prints hello out in the chat. Since the code always runs, the user cant call the command hello because the code is too busy running the code that updates every minute. Is there any way to like make it so that the updateminute code runs while others can input commands as well?
import discord
import asyncio
import bazaar
from discord.ext import commands, tasks
client = commands.Bot(command_prefix = '.')
#client.command()
async def calculate(ctx):
while True:
await ctx.send(file2.calculate())
await asyncio.sleep(210)
#client.command()
async def hello(ctx):
await ctx.send("Hello")
client.run(token)
In file2.py:
def updateminute():
for product in product_list:
#Grab Api and stuff
#check to see whether it is profitable
time.sleep(0.3) #cause if i don't i will get a key error
#calculate the stuff
#return the result
To sum up, since the bot is too busy calculating updateminute and waiting, other people cannot access the bot. Is there any way I can try to fix this so that the bot calculates its stuff and so people can use the bots commands? Thanks!
You can look into threading! Basically, run two separate threads: one for taking requests and one for updating the prices.
You could also look into turning it into an async function, essentially making it easier to run things concurrently.
So your standard def will become async def and then to call the function you simply add an await before it so await file2.calculate()
Hope it helps and is also somewhat easier to understand
here is the code:
print('hmm1') #testing, this one prints
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='&')
client.run('my token', bot=False)
async def testFunction():
print('hmm') #<- this one does not print.
channel = await client.get_channel(708848617301082164)
message_id=715307791379595275
msg = await client.get_message(channel, message_id)
await msg.edit(content="L")
await msg.edit(content="W")
print('edited message!')
testFunction()
# none of the above works. I only get "hmm1" printed in console.
I have no clue what is happening as there is quite literally no error or output of any sort in the console. does anyone know the problem?
If you're not familiar with asynchronous functions, they need to be awaited. Examples of coroutines can be seen in msg.edit(..., as edit() is a coroutine, therefore you need to await it like so: await testFunction()
Additionally, client.get_channel() and client.get_message() aren't coroutines, so they don't need to be awaited.
As Eric mentioned, you'll also want to move your client.run('... down to the last line in your file, otherwise it'll block the rest of the script. Here's how the code should be structured:
# imports
# commands, events, functions
# last line
client.run('...
It looks like you're using some old documentation too, as d.py has moved over to rewrite (v1.x), and it looks as though the client.get_message() you were using is actually from v0.16.x.
I'd recommending wanting to read up on these changes to familiarise yourself with rewrite. Try to avoid outdated tutorials as well.
As a little headstart, your await client.get_message(channel, message_id) should become await channel.fetch_message(message_id).
References:
Rewrite docs
Client.get_channel()
TextChannel.fetch_message()