BotBuilder Python - Handling multiple dialog and intent - python

I have the following code to handle multiple intents,
Code
async def on_message_activity(self, turn_context: TurnContext):
recognizer_result = await self.luis.recognize(self.recognizer, turn_context)
intent = self.luis.get_top_intent(recognizer_result)
await self.process_intent(turn_context, recognizer_result, intent)
async def process_intent(self, turn_context: TurnContext, recognizer_result, intent):
if intent == 'Greeting_Wishes':
await greeting_wishes(turn_context, user_info)
elif intent == 'Greeting_Question':
await greeting_question(turn_context)
elif intent == 'Movement':
dialog = Movement(recognizer_result)
await DialogHelper.run_dialog(
dialog,
turn_context,
self.dialog_state
)
Problem
Greeting intent is working fine
Movement intent is properly taking to the configured dialog but after asking a couple of inputs to the user and when the user enters their value it is either going back to greeting intent or going nowhere since the intent is None
Can someone help how to handle multiple intents with dialogs?
Any help would be appreciated!

I ended up having one main dialog and extended the other dialogs depending upon the other intent. Look at the code sample below,
async def on_message_activity(self, turn_context: TurnContext):
recognizer_result = await self.luis.recognize(self.recognizer, turn_context)
intent = self.luis.get_top_intent(recognizer_result)
await self.process_intent(turn_context, recognizer_result, intent)
async def process_intent(self, turn_context: TurnContext, recognizer_result, intent):
if intent == "Greeting_Wishes" and not entity:
await greeting_wishes(turn_context, user_info)
return
if intent == "Greeting_Question" and not entity:
await greeting_question(turn_context)
return
dialog = MainDialog(self.luis, intent, recognizer_result)
await DialogHelper.run_dialog(
dialog,
turn_context,
self.conversation_state.create_property("DialogState")
)
main_dialog.py
Within the main dialog, I'll check for the intent and begin the appropriate dialog.
class MainDialog(ComponentDialog):
def __init__(self, luis, intent, recognizer_result):
super(MainDialog, self).__init__(MainDialog.__name__)
self.luis = luis
self.intent = intent
self.recognizer_result = recognizer_result
self.add_dialog(SampleDialog1())
self.add_dialog(SampleDialog2())
self.add_dialog(
WaterfallDialog(
"main_dialog_id", [self.main_step]
)
)
self.initial_dialog_id = "main_dialog_id"
async def main_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
dialog_detail = self.luis.get_entities(self.intent, self.recognizer_result)
if self.intent == "one":
return await step_context.begin_dialog(SampleDialog1.__name__, dialog_detail)
elif self.intent == "two":
return await step_context.begin_dialog(SampleDialog2.__name__, dialog_detail)

Related

How to end an interaction in discord embed buttons (Python)

I have made a meme command which looks like this
Is there any way to end the interaction on clicking the End Interaction button. Please help me with the end_interaction function.
Code:
#commands.command(aliases=['m'], description='Posts memes from r/memes')
async def meme(self, ctx):
submissions = await get_memes('memes')
button1 = Button(label='Next Meme', style=discord.ButtonStyle.green)
button2 = Button(label='End Interaction', style=discord.ButtonStyle.red)
async def next_meme(interaction):
if len(submissions) == 0:
await interaction.response.edit_message(content='No more memes available')
return
submissions.pop(0)
embed.title = submissions[0].title
embed.url = submissions[0].url
embed.set_image(url=submissions[0].url)
await interaction.response.edit_message(embed=embed)
async def end_interaction(interaction):
pass
# I have no idea what to do here
view = View()
view.add_item(button1)
view.add_item(button2)
embed = discord.Embed(title=submissions[0].title, url=submissions[0].url, colour=discord.Colour.random())
embed.set_image(url=submissions[0].url)
await ctx.send(embed=embed, view=view)
button1.callback = next_meme
button2.callback = end_interaction
If I understand what you're trying to do properly, can't you try to change the button1 & button2 callback to None? (or any other way to kind of "unbind" the button to the functions).
EDIT: Seems there is a disabled bool on the buttons, you can try something like
async def end_interaction(interaction):
button1.disabled = True
button2.disabled = True
Source of the edit info: https://stackoverflow.com/a/71013761/18312347

discord.py - edit the interaction message after a timeout in discord.ui.Select

How can I access the interaction message and edit it?
discord.ui.Select
class SearchMenu(discord.ui.Select):
def __init__(self, ctx, bot, data):
self.ctx = ctx
self.bot = bot
self.data = data
self.player = Player
values = []
for index, track in enumerate(self.data[:9]):
values.append(
discord.SelectOption(
label=track.title,
value=index + 1,
description=track.author,
emoji=f"{index + 1}\U0000fe0f\U000020e3"
)
)
values.append(discord.SelectOption(label='Cancel', description='Exit the search menu.', emoji="🔒"))
super().__init__(placeholder='Click on the Dropdown.', min_values=1, max_values=1, options=values)
async def callback(self, interaction: discord.Interaction):
if self.values[0] == "Cancel":
embed = Embed(emoji=self.ctx.emoji.whitecheck, description="This interaction has been deleted.")
return await interaction.message.edit(embed=embed, view=None)
discord.ui.View
class SearchMenuView(discord.ui.View):
def __init__(self, options, ctx, bot):
super().__init__(timeout=60.0)
self.ctx = ctx
self.add_item(SearchMenu(ctx, bot, options))
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.response.send_message(embed=embed, ephemeral=True)
return False
else:
return True
async def on_timeout(self):
embed = Embed(emoji=self.ctx.emoji.whitecross, description="Interaction has timed out. Please try again.")
await self.message.edit(embed=embed, view=None)
If I try to edit the interaction like this I am getting
-> AttributeError: 'SearchMenuView' object has no attribute 'message'
After 60 seconds the original message should be replaced with the embed in the timeout.
You're trying to ask the View to send a message, which is not a method in discord.ui.View.
You could defer the response and don't let it timeout and allow the user to try again?
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.channel.send(embed=embed, delete_after=60)
await interaction.response.defer()
return True
view = MyView()
view.message = await channel.send('...', view=view)
After that you can use self.message in on_timeout (or somewhere else you don't have access to interaction.message) to edit it.
Source: https://discord.com/channels/336642139381301249/669155775700271126/860883838657495040

Multiple wait_for messages discord.py

What I'm trying to do: This is a similar situation to a question I previously asked, I am making a multiplayer game. I wanted to get multiple user inputs at the same time, basically the situation in another question I had found (multiple wait_for_messages wanted discord.py).
My Problem: I need to send messages to a different set of people each time. Basically, one game may have 3 players, while another might have 5 players. The original aim was also to send a message simultaneously to a user's dms and have them answer the input from there. I am unsure how to define the amount of wait_fors I need in my code.
Code attempt 1:
# inside a cog
#commands.command()
async def bagpie(self, ctx, *members: discord.Member):
members = list(members)
if ctx.author not in members:
members.append(ctx.author)
if len(members) > 3:
return
# making checks
def check1(msg):
return msg.author == members[0]
def check2(msg):
return msg.author == members[1]
def check3(msg):
return msg.author == members[2]
# waiting for multiple messages with the checks
ret = await asyncio.gather(
await self.bot.wait_for("message", timeout=10, check=check1),
await self.bot.wait_for("message", timeout=10, check=check2),
await self.bot.wait_for("message", timeout=10, check=check3),
return_exceptions = True
)
# Test for errors
ret = [r if not isinstance(r, Exception) else None for r in ret]
msg1, msg2, msg3 = ret # *ret would cause an error to occur
# setup embed to send final product
fin_string = ""
count = 0
msg_list = [msg1, msg2, msg3]
for member in members:
fin_string += f"{member} says: {msg_list[count]}"
count += 1
embed = discord.Embed(title="This is what you said!", description=fin_string)
await ctx.send(embed=embed)
Result: As you can see, despite there being inputs given, the bot does not respond and instead reverts to the timeout.
Code Attempt 2:
# couldn't get this to work in a cog, so this one is not in a cog
# checks if it's a dm and if author is given member
def is_dm_check(member):
def inner_check(msg):
return msg.author.id==member.id and msg.guild is None
return inner_check
# sends message to member and waits for the message
async def get_input(member):
await member.send("Please provide an input:")
return client.wait_for('message', check=is_dm_check(member), timeout=75)
#client.command()
async def play_game(ctx, members: commands.Greedy[discord.Member]=None):
if not members:
await ctx.send("No Players")
return
if ctx.author not in members:
members.append(ctx.author)
responses = asyncio.gather(*map(get_input, members))
for member, response in zip(members, responses):
await ctx.send(f"{member.mention}: {response.content}")
Result: Messages were supposedly sent simultaneously, however it still throws an error.

Multiple Choice Reaction python (discord.py)

I'm trying to do a multiple choice button / reaction in discord using python (discord.py) -- something similar to the image below:
For example when it reacts to 2️⃣, that shows page 2, when it reacts to 3️⃣, that shows page 3 ...
Could anyone help me, please?
import discord
from discord.ext import commands
class Wiki(commands.Cog):
def __init__(self,bot):
self.bot=bot
#commands
#commands.command(name="wiki",aliases=["w"])
async def wiki(self,ctx):
page1=discord.Embed(
title='Page 1/3',
description='Description1',
colour=discord.Colour.orange()
)
page2=discord.Embed(
title='Page 2/3',
description='Description2',
colour=discord.Colour.orange()
)
page3=discord.Embed(
title='Page 3/3',
description='Description3',
colour=discord.Colour.orange()
)
pages=[page1,page2,page3]
message= await ctx.send(embed=page1)
await message.add_reaction('1️⃣')
await message.add_reaction('2️⃣')
await message.add_reaction('3️⃣')
emoji=""
if emoji=="1️⃣":
await message.edit_message(message,embed=pages[0])
if emoji=="2️⃣":
await message.edit_message(message,embed=pages[1])
if emoji=="3️⃣":
await message.edit_message(message,embed=pages[2])
def setup(bot):
bot.add_cog(Wiki(bot))
Take a look at the documentation for wait_for, which is how you should treat this kind of case. I won't spoonfeed you the code though, you should give it a fair shot yourself first.
Also, discord.py already has something built-in for embeds that change pages automatically based on reactions called menus, so it'll probably be easier to just use that instead of re-implementing it yourself.
Answering my own question.
I know it's not the best answer, but it works XD.
Any suggestion will be well accepted. Thank you all so much for your help.
import asyncio
import discord
from discord.ext import commands
class Wiki(commands.Cog):
def __init__(self,bot):
self.bot=bot
#commands
#commands.command(name="wiki",aliases=["w"])
async def wiki(self,ctx):
first_run = True
while True:
if first_run:
page1=discord.Embed(title='Page 1/3',description='Description1',colour=discord.Colour.orange())
first_run=False
msg = await ctx.send(embed=page1)
reactmoji = ["1️⃣","2️⃣","3️⃣"]
for react in reactmoji:
await msg.add_reaction(react)
def check_react(reaction, user):
if reaction.message.id != msg.id:
return False
if user != ctx.message.author:
return False
if str(reaction.emoji) not in reactmoji:
return False
return True
try:
res, user = await self.bot.wait_for('reaction_add', check=check_react)
except asyncio.TimeoutError:
return await msg.clear_reactions()
if user != ctx.message.author:
pass
elif '1️⃣' in str(res.emoji):
print('<<1️⃣>>')
await msg.remove_reaction("1️⃣",user)
await msg.edit(embed=page1)
elif '2️⃣' in str(res.emoji):
print('<<2️⃣>>')
page2=discord.Embed(title='Page 2/3',description='Description2',colour=discord.Colour.orange())
await msg.remove_reaction("2️⃣",user)
await msg.edit(embed=page2)
elif '3️⃣' in str(res.emoji):
print('<<3️⃣>>')
page3=discord.Embed(title='Page 3/3',description='Description3',colour=discord.Colour.orange())
await msg.remove_reaction("3️⃣",user)
await msg.edit(embed=page3)
def setup(bot):
bot.add_cog(Wiki(bot))

How to add AdaptiveCard to QnA Bot in Python

I wish to use some AdaptiveCard with my QnA python bot.
I use sample code from botbuilder repo:
from botbuilder.ai.qna import QnAMaker, QnAMakerEndpoint
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
from botbuilder.schema import ChannelAccount
from config import DefaultConfig
class QnABot(ActivityHandler):
def __init__(self, config: DefaultConfig):
self.qna_maker = QnAMaker(
QnAMakerEndpoint(
knowledge_base_id=config.QNA_KNOWLEDGEBASE_ID,
endpoint_key=config.QNA_ENDPOINT_KEY,
host=config.QNA_ENDPOINT_HOST,
)
)
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity(
"Hallo!"
)
async def on_message_activity(self, turn_context: TurnContext):
# The actual call to the QnA Maker service.
response = await self.qna_maker.get_answers(turn_context)
if response and len(response) > 0:
await turn_context.send_activity(MessageFactory.text(response[0].answer))
else:
await turn_context.send_activity("Ich kann nicht")
So how I have to configure qna_maker.get_answers() and rows in QnA Knowledge Base, for using AdaptiveCards like an answer for a question?
UPD
What I wish to return answer inside card:
card = HeroCard(
title="Answer Title here",
subtitle="short answer",
buttons=[task_module_action], #task_module_action - button which have to show long variant of answer
)

Categories

Resources