Creative Tect 4: Project 2-Chatbot
In this project, I am planning to make a chatbot that helps you decide what to eat for today. It is always difficult for me to choose what to eat and I hope there will be a bot that can recommend something to eat. At the beginning of the project, while I haven ’t planned what characteristics or UI might have, I want to first explore how to connect my bot with API.
First, I choose mealAPI for the resource and import this as an html/json link. I want people to have two options, one for the random selection of the meal, and one you could select by category. So fetch this two function from the mealAPI:
import os
import discord
import aiohttp
from discord.ui import View, Select
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
async def get_random_recipe():
url = "https://www.themealdb.com/api/json/v1/1/random.php "
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
return data['meals '][0] if data['meals '] else None
else:
return None
async def get_categories():
url = "https://www.themealdb.com/api/json/v1/1/list.php?c=list "
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
return [category['strCategory '] for category in data['meals ']]
else:
return None
Then, as I said there would be two functions, I would provide two buttons for people to click. When I type in “What to eat ”or @ the bot, it would automatically come out the two buttons.
if message.content.startswith("What to eat?"):
our_view = MyCustomView()
await message.channel.send("I am here to recommend you some recipes for today!", view=our_view)
if client.user in message.mentions:
our_view = MyCustomView()
await message.channel.send("Thanks for mentioning Luka ", view=our_view )
The first one is the random select function. Basically, if people clicked the button I set up, it would automatically give you a random recipe, including the name of the dish, how to cook it and the image of it.
class MyCustomView(View):
def __init__(self):
super().__init__()
@discord.ui.button(label="Random Recipe ")
async def button_callback(self, interaction, button):
meal_data = await get_random_recipe()
if meal_data:
name = meal_data['strMeal ']
image = meal_data['strMealThumb ']
instructions = meal_data['strInstructions ']
response_message = f "**Name:** {name}\n**Image:** {image}\n**Instructions:** {instructions}"
else:
response_message = "Oops! Something went wrong while fetching the random recipe."
await interaction.response.send_message(response_message)
After setting up this button, the result would be like this:
The second option would be, selecting the category for the recipe, after click this button, a drop down menu would pop up, that using the category fetched in the API:
@discord.ui.button(label="Select Category ")
async def button_callback2(self, interaction, button):
categories = await get_categories()
if categories:
select = CustomSelect(options=[discord.SelectOption(label=category) for category in categories])
self.clear_items()
self.add_item(select)
await interaction.response.send_message("Select a category:", view=self)
else:
await interaction.response.send_message("Oops! Something went wrong while fetching the categories.")
The result would look like this:
However, for the first week, I was still having trouble fetching a random meal inside the category. I might have to revisit the API json to check up what exactly the functions were called, and I am not sure if they could still provide a random mean inside the category.
In this case, I cannot fix the problem of the drop down and I was interested in connecting the bot to the API again, so I changed my direction to add another API. In this case, I want to shape the conversation with the bot like, the users who are rude customers that the bot wants to satisfy them.
During the conversation, if the users are not satisfied with the first recommendation, to let angry customers cool down, the bot will recommend a cocktail for them. So I tried to connect the cocktail api.
async def get_random_cocktail():
url = "https://www.thecocktaildb.com/api/json/v1/1/random.php "
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
return data['drinks '][0] if data['drinks '] else None
else:
return None
After fetching the data from the API, I started to write down the conversation and the reaction I mentioned before:
follow_up_view = FollowUpView()
follow_up_message = await interaction.followup.send("Did you satisfy with my recommendation?", view=follow_up_view)
# Update the follow-up view with the message reference for callback
follow_up_view.message_reference = follow_up_message.to_reference()
class FollowUpView(discord.ui.View):
def __init__(self):
super().__init__()
@discord.ui.button(label="Yes ")
async def yes_button_callback(self, interaction, button):
await interaction.response.send_message("Great to hear!")
@discord.ui.button(label="Not at all! I don 't like your recommendation!", emoji="😡") # Add the angry emoji
async def no_button_callback(self, interaction, button):
cocktail_data = await get_random_cocktail()
if cocktail_data:
name = cocktail_data['strDrink ']
image = cocktail_data['strDrinkThumb ']
response_message = f "To show my apology, please try this cocktail:\n**Name:** {name}\n**Image:** {image}"
else:
response_message = "Oops! Something went wrong while fetching the random cocktail."
# Send the cocktail recommendation as a new message
await interaction.response.send_message(response_message)
And this is what the conversation looks like:
After this, I would like to add one more conversation , like the customer was still not satisfied with the result and felt angry constantly, the bot got angry too and refused service. Here is the follow up conversation:
cocktail_follow_up_view = CocktailFollowUpView()
cocktail_follow_up_message = await interaction.followup.send(
"Are you satisfied with my cocktail recommendation?", view=cocktail_follow_up_view
)
# Update the cocktail follow-up view with the message reference for callback
cocktail_follow_up_view.message_reference = cocktail_follow_up_message.to_reference()
class CocktailFollowUpView(discord.ui.View):
def __init__(self):
super().__init__()
@discord.ui.button(label="Yes! I feel better ")
async def yes_button_callback(self, interaction, button):
await interaction.response.send_message("I 'm glad you feel better!")
@discord.ui.button(label="😠😠No you are not doing a proper job ")
async def no_button_callback(self, interaction, button):
await interaction.response.send_message("I 'm a bit upset now, and I may not be able to assist you further. Find another bot! ")
The conversation would look like this:
Since I was having the idea before knowing how to draw a decision tree, I feel like it ’s quite hard to turn it into decision tree format. Also, the conversations and options are not complex, so I just keep the format as follow up questions.
Video documentation