I'm trying the Bot Framework SDK V4 Python GA. Once I detect the intent with LUIS, I want to be able to process some business logic and respond back. I want to be able to send messages while the business logic
as I want to user to know that the logic is being processed and need him to wait for a moment. I understand that bots aren't generally used for long running processes, but I've an use case where this is needed. I'm trying to pass the turncontext to the business logic and send a message from there, but it throws the following error.
can't pickle coroutine objects
I'm new to async programming and not sure what's exactly happening here. Below is what I've tried. I've tried doing the same by placing the business logic in a different class altogether, but got the same issue. The initial message from on_message_activity goes well, but when trying to send the message from business, it throws the above error. What am I missing here?
async def someUseCase(self,turncontext: TurnContext):
await turncontext.send_activity(MessageFactory.text("Processing your query. Give me a moment."))
output = someLongRunningBusinessLogic()
return MessageFactory.text(output)
async def on_message_activity(self, turn_context: TurnContext):
luisResult = await self.LuisRecog.recognize(turn_context)
print(luisResult.get_top_scoring_intent())
intent = LuisRecognizer.top_intent(luisResult,min_score=0.40)
if intent != "None":
await turn_context.send_activity("processing your query...")
return await turn_context.send_activity(self.someUseCase(turn_context))
else:
await turn_context.send_activity(MessageFactory.text("No intent detected."))
async def functions return awaitables that should be awaited. The error you encountered is likely because you were trying to pass a coroutine to a function that expected an activity on this line:
return await turn_context.send_activity(self.someUseCase(turn_context))
send_activity expects an activity but someUseCase returns a coroutine.
You can read more about coroutines in the Python documentation: https://docs.python.org/3/library/asyncio-task.html
Related
I am trying to write a discord.py bot. The following is an abridgement of my full code.
intents = discord.Intents.default()
intents.message_content = True
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(intents=intents, command_prefix="!", sort_commands=False)
client = discord.Client(intents=intents)
#bot.event
async def on_ready():
print(f'{bot.user.name} has connected to Discord!')
channel = bot.get_channel(12345678912345678912)
await channel.send("This is a test.")
bot.run(TOKEN)
Sometimes, the code above will work as intended, printing the connection message and sending the desired message into the target channel.
This is not always the case, however! I am getting an issue where the bot.get_channel() command returns none instead of the correct data. I assume this means that the channel cannot be found, except for the fact that the channel does exist.
The error that is sent to the console is;
AttributeError: 'NoneType' object has no attribute 'send'
I am very, very new to discord.py so I would appreciate any help I can get to better understand what's going on under the hood here. Thank you very much for your help.
The get_channel method is a cache lookup method, so it's not always reliable, or the bot doesn't have access to the channel, and since it doesn't raise an exception if the cache wasn't found and returns None instead, you can't know the reason for sure. You can use fetch_channel to make an API request after get_channel if it returns none; this method will raise an exception if the channel was not found or it doesn't have permission to fetch.
channel = (bot.get_channel(1234567890) or await bot.fetch_channel(1234567890))
await channel.send("This is a test.")
Also it seems like you have both discord.ext.commands.Bot and discord.Client instance, please choose one and remove the other.
The accepted answer is not recommended.
The reason it doesn't work is because the cache isn't populated yet in on_ready, but you should not make API requests (fetch_X & send) in on_ready! Making API calls in there has a high chance for Discord to just disconnect your bot.
Also, on_ready gets triggered multiple times, so you'll end up sending this message constantly even though you only started it once.
There's rarely a reason to do anything at all in there. If you want something to run once on startup you can create a Task & start it in setup_hook.
This is a continuation of this short forum (How forward message to other contact with telethon).
Problem
I replaced entity with the group id for GC A and it works since I type something in GC B the bot forwards it to GC A however, when I message GC A the bot still forwards the messages to GC A which I don't want, I just want it to not react.
await client.forward_messages(entity, event.message)
The bot forwards every new message because the event type is new messages so I was thinking, is there a way to filter it so that it only triggers when there are new messages on a specific group?
#client.on(events.NewMessage)
async def main(event):
Solutions Ive tried
Looking at the documentation (https://docs.telethon.dev/en/latest/modules/client.html#telethon.client.messages.MessageMethods.forward_messages) there is an example with the argument "from_chat". So I placed the group id of GC B but it doesn't work.
await client.forward_messages(chat, message_id, from_chat)
I also tried making the argument look like this to copy the examples better but It does not work
await client.forward_messages(entity("group ID"), event.message, from_chat("group_id"))
For me worked this code:
#client.on(events.NewMessage(chats = FROM_CHANNEL_ID))
async def main(event):
await event.forward_to(TO_CHAT_ID)
Try it, may it will work for you to.
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
I want my Discord bot to send a certain message, For example: "Hello" when he joins a new server. The bot should search for the top channel to write to and then send the message there.
i saw this, but this isn't helpful to me
async def on_guild_join(guild):
general = find(lambda x: x.name == 'general', guild.text_channels)
if general and general.permissions_for(guild.me).send_messages:
await general.send('Hello {}!'.format(guild.name))```
The code you used is actually very helpful, as it contains all of the building blocks you need:
The on_guild_join event
The list of all channels in order from top to bottom (according to the official reference). If you want to get the "top channel" you can therefore just use guild.text_channels[0]
checking the permissions of said channel
async def on_guild_join(guild):
general = guild.text_channels[0]
if general and general.permissions_for(guild.me).send_messages:
await general.send('Hello {}!'.format(guild.name))
else:
# raise an error
Now one problem you might encounter is the fact that if the top channel is something like an announcement channel, you might not have permissions to message in it. So logically you would want to try the next channel and if that doesn't work, the next etc. You can do this in a normal for loop:
async def on_guild_join(guild):
for general in guild.text_channels:
if general and general.permissions_for(guild.me).send_messages:
await general.send('Hello {}!'.format(guild.name))
return
print('I could not send a message in any channel!')
So in actuality, the piece of code you said was "not useful" was actually the key to doing the thing you want. Next time please be concise and say what of it is not useful instead of just saying "This whole thing is not useful because it does not do the thing I want".
I'm new to discord.py and trying to make a translator bot. When the user reacts with a certain flag, the bot translates it, but the event is never getting called hence I have no code to translate any messages yet. I know it's not getting called because the program isn't printing an 'x' to the console.
#client.event
async def on_reaction_add(reaction, user):
channel = reaction.message.channel
print('x')
await client.send_message(channel, '{} has added {} to the the message {}'.format(user.name, reaction.emoji, reaction.message.content))
await client.process_commands(reaction.message)
Probably a bit late to this thread but, the answer above is a valid answer. But you can also use on_raw_reaction_add which gets called even if the messages aren't in the Bot's cache.
Called when a message has a reaction added. Unlike on_reaction_add(), this is called regardless of the state of the internal message cache.
Documentation Link
Example:
#commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
channel = await self.bot.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
user = await self.bot.fetch_user(payload.user_id)
emoji = payload.emoji
await channel.send("Hello")
There isn't much valid reason for why the event isn't registered/called.
One of which is stated in the docs: http://discordpy.readthedocs.io/en/async/api.html#discord.on_reaction_add. Try adding a reaction immediately to a message that is sent after the bot is online. Since messages sent before the bot is online will not be recognized by the bot (not in Client.messages).
if the message is not found in the Client.messages cache, then this
event will not be called.
Another possible reason is that this function was never defined before the client loop commenced. Verify your indentation. And/Or try placing the function directly under client = Bot(...), to check if this is the problem.
If neither of the aforementioned solves your problem, please post a minimal, complete, verifiable example (a short runnable code from top to bottom that indicates your problem).