Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ChatBot_A6/GUI.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
574 lines (508 sloc)
36.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from tkinter import * | |
from tkinter import ttk #this uses the nicer-looking ttk widgets. Placed after previous import so ttk overrides tkinter when applicable, but when not applicable, standard tkinter widgets are used. ttk was learnt about from here: https://docs.python.org/2/library/ttk.html | |
from tkinter import messagebox #for some reason, the messagebox worked without this line in idle, but it didn't work with the command prompt, even though the exact same machine and Python version were used. I added this code so it's compatible with both. | |
import webbrowser #import this so it is possible to open news articles in the web browser. | |
#importing group members' module code below | |
from Jokes import * | |
from News import * | |
from Weather import * | |
from YELP import * | |
from Entertainment import * | |
from Movies import * | |
from Sports import * | |
from ChatQuestions import * | |
from IrtizaFunctions import * | |
from FileSave import * | |
#end of group members' module coding importing | |
from random import randint #for creating random integers between two numbers | |
#All code below is written by Humza, except where noted. Most module functions called here were written by others (the author can be found by opening the respective files). | |
root = Tk() | |
#declaring global variables because variables inside function are all reset when function runs, but weather function needs to run multiple times due to asking user to type multiple things | |
#all ___Steps variables are declaed global for this same reason because they ask user to type multiple inputs | |
#also, another note about all ___Steps variables, they are all incremented (or set to 0 if function is complete) to make sure the next part of the if-statement is triggered. | |
global weatherSteps | |
weatherSteps = 0 #assign weatherSteps to 0 so it has an int value compatible with if-statement | |
global weatherCity | |
weatherCity = "" #intialise to empty string for use with if-statements; other global string variables are initialised to this for the same reason. | |
global weatherCountry | |
weatherCountry = "" | |
global feelingSteps | |
feelingSteps = 1 #initialise as 1 because this is the first question when the chatbot starts, so user should respond to this | |
global yelpSteps | |
yelpSteps = 0 | |
global yelpLocation | |
yelpLocation = "" | |
global yelpIndex | |
yelpIndex = 0 #using global variable for index to keep track of index outside of function (because variable would reset each time if it was inside function). | |
global movieSteps | |
movieSteps = 0 | |
global loginSteps | |
loginSteps = "" #using strings for these steps because different branches can be taken (such as registering new user or asking existing user for password)) | |
global currentUser | |
currentUser = "" | |
global userTraits #use list so no need to create separate variable for each trait | |
userTraits = [] #initialise to empty list because cannot do userTraits.append() otherwise. | |
global userAccount #instance of Users class | |
global passAttempt #store what user put in for password | |
class Users: | |
"""Class for holding user data abd storing names of users""" | |
chatPeople = ["User: "] #append more to list as people log on. | |
def __init__(self, name): | |
self.name = name.capitalize() #using .capitalize() because gramatttically correct names should start with capital letters, and currentUser variable is all lower case | |
self.age = read(self.name+".txt", "age") | |
self.location = read(self.name+".txt", "location").title() #using title() to make sure location is gramatically correct if accessed. | |
self.course = read(self.name+".txt", "course").title() #ensuring that it is gramatically correct | |
self.password = read(self.name+".txt", "password") | |
if self.name not in self.chatPeople: | |
self.chatPeople.append(self.name + ": ") #added ": " because I also want the colon to be highlighted, and because the responses look better if there was a space between the colon and the response. | |
else: | |
#brings current user to end of list, without duplicating, | |
#which is important because that is how the program | |
#decides which name to print before user's messages | |
self.chatPeople.remove(self.name) | |
self.chatPeople.append(self.name) | |
def chatDelay(): | |
"""Function to create small time delays between 0.5 and 1.5 seconds before the chatbot outputs a response, to make it seem like there is a person on the other side that thinks beore they responsd""" | |
chatHighlight() #call this here because chatHighlight() is usually called at end of sendClick(event) function, so the user and chatbot's names would not be highlighted at the time chat is being delayed without this here. | |
chatWindow.yview_moveto(1.0) #causes scrolling whenever function is called, because the chatbox is not usually scrolled when the delay occurs, which makes the program look weird otherwise | |
if commandEnter.get() != "": #no point running code and wasting computation time if commandEnter widget is already empty | |
commandEnter.delete(0, "end") #it would look weird for there to still be a string in the commandEnter widget while things are being printed, so clear contents of widget here. | |
root.update() #forces the GUI to update its appearance to reflect the chatHighlight(), the commandEnter.delete and other appearences. This is because the GUI pauses before these changes are shown to the user when root.after is used. | |
randNum = randint(500, 1500) #creates a random number so user doesn't wait same amount of time before every response | |
root.after(randNum) #learnt about x.after(num) from https://stackoverflow.com/questions/19887729/time-delay-tkinter | |
def sendClick(event): | |
"""Gets the user's input from the entry widget, and then inserts the user's request into the chatWindow. After that, it interprets what the user is asking for and then provides an appropriate response.""" | |
chatWindow.config(state="normal") #set state to normal so it is possible for the program to eddit the contents of chatWindow | |
request = commandEnter.get() | |
if request != "": #runs code only if the user entered something, so no code is run if nothing is entered and if no subprocess modules are running | |
chatWindow.insert(END, Users.chatPeople[-1] + request + "\n") | |
requestInterpreter(request) | |
chatWindow.yview_moveto(1.0) #brings scrollbar to bottom of chatWindow, so user doesn't need to manually scroll down if text inside chatWindow gets too big | |
#yview_moveto(1.0) learnt and adapted from https://stackoverflow.com/questions/34680138/how-to-track-the-bottom-of-a-frame-when-used-on-a-canvas-with-a-scrollbar | |
chatHighlight() | |
commandEnter.delete(0, "end") #clear contents of commandEnter entry widget so user can type new request | |
chatWindow.config(state="disabled") #set state to disabled so, once the program is finished with writing to the chatWindow widget, the user cannot edit the contents of the chat window themselves. | |
commandEnter.focus() #sets the focus to the commandEnter widget, to save user the trouble of having to click before typing the request | |
def requestInterpreter(request): | |
"""Gets the text the user entered, then processes the request by looking for keywords and finally produces the correct output depending on what the request is""" | |
chatWindow.config(state="normal") #set state to normal so it is possible for the program to eddit the contents of chatWindow | |
#note: All global ____ functions are used to tell Python not to create a local variable with this name, but to access the global variable of the same name. | |
#note: Most modules check for the presence of certain words by using ("string" in request) because this allows user to trigger modules with more natural language. | |
#note: For example, using ("joke" in request) lets the user type in a way natural to them (such as: "tell me a joke", "what's a good joke?", "do you have a favourite joke?") while all trigger joke module. | |
#note: unfortunately, some modules use dictionaries that require an exact string (or return a keyError otherwise), which is less versatile. | |
#note: I have used (request == "string") for these cases. | |
global loginSteps #initialise loginSteps here because it is used in if-statement at the start, instead of being initialised with other global login-related variables | |
if loginSteps != "verifyPassword" and loginSteps != "newPassword": #don't want request to become lowercase in this scenario because doing so would make security weaker, due to a lack of case sensitivity | |
request = request.lower() #convert user's input to lower case so checking for strings works regardless of capitalisatoin (as Python recognise lower and upper case characters as different) | |
recognised = False #variable to store whether a command was recognised, so if it isn't recognised, then the chatbot can ask for clarificatoin. | |
#all interpretation-based if-statements (deciding what to do on user's command) will have "and False:" at the end to ensure that this if-statement doesn't run if a previous if-statement was run | |
chatDelay() #introduces delay after user's input is shown, but before the chatbot outputs anything | |
#integrating Irtiza's feeling function here | |
global feelingSteps | |
if feelingSteps == 1: | |
recognised = True #so other interpretation-based if-statements after this one don't run | |
response = feeling(request) | |
chatWindow.insert(END, "Chatbot: " + str(response) + "\n") | |
feelingSteps = 0 | |
chatDelay() | |
chatWindow.insert(END, 'Chatbot: If you want help on how to use this chatbot, type "help".\n') #tell user how to find out commands without reading the readme file in case they never used the chatbot before. Don't give command list at start because the list is big and it might look untidy if it was given every time the prgram starts. | |
#end of feeling integration | |
#integrating Khadija's blacklist code into GUI here | |
profanityCheck = blackList(request) | |
if profanityCheck != None and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: " + str(profanityCheck) + "\n") | |
recognised = True #don't want any subsequent commands to be interpreted if there is any profanity found | |
#end of blacklist integration | |
#integrating Irtiza's other functions here | |
if request == "suggest some good places to eat at" and recognised == False: | |
recognised = True | |
response = food(request) | |
counter = 0 | |
while counter != 5: | |
chatWindow.insert(END, "Chatbot: " + str(response[counter]) + "\n") | |
counter +=1 | |
chatDelay() | |
if request == "i'm bored tell me a joke" or request == "Can you tell me a joke pls?" and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: " + str(joke(request)) + "\n") | |
if request == "anything good in the news today?" or request == "what's the latest news?" or "news" in request and (("bbc" or "espn" or "times" or "india" or "york" or "bible" or "mtv") not in request) and recognised == False: #makes sure user didn't ask for a specific news source, and makes sure dictionary keys are sent through | |
recognised = True | |
if request == "anything good in the news today?" or request == "what's the latest news?": | |
requestInterpreter(news(request)) #recursive function that gets one of the news sources returned by the news() function and then gets news from that source by triggering the interpreter for Vinayak and Rahul's news APIs | |
elif "news" in request: | |
requestInterpreter(news("anything good in the news today?")) #same as above branch, but gives false dictionary key value (to satisfy if-statement in Irtiza's news() function). False dictinoary key value does not matter because both keys have same output regardless. | |
#end of integration of Irtiza's functions | |
#integrating Khadija's questions/answers here | |
if request == "how are you feeling" or request == "how old are you" or request == "what is your favourite colour" or request == "where do you live" or request == "who created you" or request == "what is your hobby" or request == "when were you born" or request == "who is your master" or request == "are you my friend" or request == "do you eat and drink" or request == "what features do you have" or request == "how can you help me today" or request == "whats your favourite country" or request == "are you my father" or request == "who are you" or request == "where's my address" or request == "i'm feeling bored" or request == "are you an expert at hacking" or request == "what languages do you speak" or request == "what is your religion" or request == "where were you created" or request == "how many people were involved in creating you" or request == "what gender are you" or request == "can you sing" or request == "what are your talents" and recognised == False: | |
recognised = True | |
answer = questionAnswer(request.capitalize()) #capitalise first latter because dictionary keys all have capital first letter, but the requestInterpreter function has request.lower() at the start | |
chatWindow.insert(END, "Chatbot: " + str(answer) + "\n") | |
#end of question/answer integration | |
#integrating Benjamin's FileSave code | |
global currentUser | |
global userTraits | |
global userAccount | |
global passAttempt | |
if "log" in request and "in" in request and loginSteps == "" and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: What is your name?\n") | |
loginSteps = "checkExist" | |
elif loginSteps == "checkExist" and recognised == False: | |
recognised = True | |
currentUser = request | |
if readFile(currentUser) == "newUser": | |
chatWindow.insert(END, "Chatbot: How old are you?\n") | |
loginSteps = "userLocation" | |
else: | |
chatWindow.insert(END, "Chatbot: Please enter your password.\n") | |
loginSteps = "verifyPassword" | |
elif loginSteps == "userLocation" and recognised == False: | |
recognised = True | |
userTraits.append(request.capitalize()) #using .capitalize() because area names gematically need to start with a capital letter | |
chatWindow.insert(END, "Chatbot: Where are you from?\n") | |
loginSteps = "studyCourse" | |
elif loginSteps == "studyCourse" and recognised == False: | |
recognised = True | |
userTraits.append(request.title()) #using .title() because course names gematically usually have a capital letter at the start of each word | |
chatWindow.insert(END, "Chatbot: What course are you studying?\n") | |
loginSteps = "newPassword" | |
elif loginSteps == "newPassword" and recognised == False: | |
recognised = True | |
userTraits.append(request) | |
chatWindow.insert(END, "Chatbot: What do you want your password to be?\n") | |
loginSteps = "saveData" | |
elif loginSteps == "saveData" and recognised == False: | |
recognised = True | |
userTraits.append(request) | |
NewUser(currentUser, userTraits) | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Thank you. We have saved your information.\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: You have also been logged in.\n") | |
loginSteps = "" | |
userAccount = Users(currentUser) | |
elif loginSteps == "verifyPassword" and recognised == False: | |
recognised = True | |
passAttempt = request | |
actualPassword = read(currentUser+".txt", "password") | |
if passAttempt == actualPassword: | |
userAccount = Users(currentUser) | |
chatWindow.insert(END, "Chatbot: Welcome back, " + userAccount.name + ".\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: It's nice to see you again.\n") | |
loginSteps = "" | |
else: | |
chatWindow.insert(END, "Chatbot: Sorry, but your password is incorrect. We can't log you in.\n") | |
loginSteps = "" | |
#end of FileSave integration | |
#integrating Vinayak's joke function here | |
if "joke" in request and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: " + str(Joke_return()) + "\n") | |
#end of joke integration | |
#integrating Rahul's IMDB API here | |
global movieSteps | |
if ("imdb" in request or "movie" in request) and movieSteps == 0 and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: Which movie or TV show do you want to search for?\n") | |
movieSteps = 1 | |
elif movieSteps == 1 and recognised == False: | |
recognised = True | |
title, years, actors, story, genre, ratings = movie_tvSE(request) | |
chatWindow.insert(END, "Chatbot: " + title + ".\n") | |
chatWindow.insert(END, "Chatbot: " + years + ".\n") | |
chatWindow.insert(END, "Chatbot: " + actors + ".\n") | |
chatWindow.insert(END, "Chatbot: " + story + "\n") | |
chatWindow.insert(END, "Chatbot: " + genre + ".\n") | |
chatWindow.insert(END, "Chatbot: " + ratings + ".\n") | |
movieSteps = 0 | |
#end of IMDB integration | |
#integrating Vinayak's news APIs and Rahul's news APIs here | |
if ("bbc" in request) or ("times" in request and "india" in request) or ("new" in request and "york" in request and "times" in request) or ("mtv" in request) or ("lad" in request and "bible" in request) or ("buzz" in request and "feed" in request) or ("espn" in request) or ("sports" in request and "bible" in request) and recognised == False: | |
recognised = True | |
#first three parts of if-statement for Vinayak Saneer's news APIs and subsequent parts of if-statement for Rahul Verma's news APIs | |
descriptionList = [] #Rahul's APIs use a description, but Vinayak's don't, so initialise empty list to prevent exceptions in Vinayak's APIs | |
if "bbc" in request and "sports" not in request: #"sports" not in request so this branch doesn't run if the user types "bbc sports" | |
newsSource = "bbc" | |
articleList, urlList = newsFunc(newsSource) | |
elif "times" in request and "india" in request: | |
newsSource = "times of india" | |
articleList, urlList = newsFunc(newsSource) | |
elif "new" in request and "york" in request and "times" in request: #check for each word in string separately because user might make a mistake or hae a different spelling style (like "new york" or "newyork" | |
newsSource = "new york times" | |
articleList, urlList = newsFunc(newsSource) | |
elif "mtv" in request: | |
articleList, descriptionList, urlList = mtvAPI() | |
elif "lad" in request and "bible" in request: | |
articleList, descriptionList, urlList = lad_bibleAPI() | |
elif "buzz" in request and "feed" in request: | |
articleList, descriptionList, urlList = buzzfeedAPI() | |
elif "bbc" in request and "sports" in request: #runs only if both "bbc" and "sports" are in request, for same reason as first if-statement branch in this section | |
articleList, descriptionList, urlList = bbc_sportsAPI() | |
elif "espn" in request: | |
articleList, descriptionList, urlList = espnAPI() | |
elif "sports" in request and "bible" in request: | |
articleList, descriptionList, urlList = sports_BibleAPI() | |
for i in range(len(articleList)): #loop goes through every article in list, printing article name and asking user if they want to open article each time. | |
chatWindow.insert(END, "Chatbot: " + articleList[i]+ "\n") | |
if len(descriptionList) > 0: #checks if descriptionList is empty, because no point printing empty list | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: " + descriptionList[i]+ "\n") | |
chatDelay() #chat is not initially highlighted when the linkAsk function is called because, without this being placed here, the code would be highlighted after the sendClick function finishes. Also, used chatDelay() instead of chatHighlight (former calls latter) so user has more time to read before message box appears. | |
openArticle = linkAsk(articleList[i], urlList[i]) | |
if openArticle == None: | |
chatDelay() | |
break; #stops further news from appearing | |
if i == len(articleList) - 1: | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Finished showing news.\n") #tell user news is finished so they aren't confused about why no more news is appearing | |
chatDelay() | |
#end of Vinayak and Rahul's news integration | |
#integrating Vinayak's weather API | |
global weatherSteps | |
global weatherCity | |
global weatherCountry | |
#note: using if-elif, so weatherSteps = 1 doesn't trigger weatherSteps == 1 if-statement | |
if "weather" in request and weatherSteps == 0 and recognised == False: | |
recognised = True | |
weatherSteps = 1 | |
chatWindow.insert(END, "Chatbot: Which city do you want to check?\n") | |
elif weatherSteps == 1 and recognised == False: | |
recognised = True | |
weatherCity = request | |
chatWindow.insert(END, "Chatbot: Which country do you want to check?\n") | |
weatherSteps = 2 | |
elif weatherSteps == 2 and recognised == False: | |
recognised = True | |
try: #using try because the API might not find the area the user inputted; if found, then proceed as normal, otherwise, show user error message | |
weatherCountry = request | |
minTemp, maxTemp, description = Data_Sorting(weatherCity, weatherCountry) | |
minTemp = str(minTemp) | |
maxTemp = str(maxTemp) | |
chatWindow.insert(END, "Chatbot: The minimum temperature will be: " + str(minTemp) + ".\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: The maximum temperature will be: " + str(maxTemp) + ".\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: The average weather condition will have " + str(description) + ".\n") | |
except KeyError: #if API does not find a place with the specified city and country | |
chatWindow.insert(END, "Chatbot: We are sorry, but the specified location is not inside our databases.\n") | |
finally: | |
#resetting global variables to prevent them from interfering with future runs of weather function | |
weatherSteps = 0 | |
weatherCountry = "" | |
weatherCity = "" | |
#end of weather input integration | |
#help message made by Humza in case user doesn't know what to do and hasn't read readme file | |
if request == "help" and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: Hi there. I'm a personal assistant.\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Some of the things I can help you with are:\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Finding latest news about current affairs, sports and entertainment.\n") | |
chatWindow.insert(END, "Chatbot: To do this, type the name of your news source.\n") | |
chatWindow.insert(END, "Chatbot: Supported news sources are:\nChatbot: -BBC\nChatbot: -Times of India\nChatbot: -New York Times\nChatbot: -BBC Sports\nChatbot: -ESPN\nChatbot: -Sports Bible\nChatbot: -MTV\nChatbot: -Lad Bible\nChatbot: -Buzz Feed.\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Telling funny jokes.\n") | |
chatWindow.insert(END, 'Chatbot: To do this, type "joke" or "Tell me a joke".\n') | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Giving the weather for any city.\n") | |
chatWindow.insert(END, 'Chatbot: To do this, type "weather".\n') | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Finding local stores.\n") | |
chatWindow.insert(END, 'Chatbot: To do this, type "yelp" or "where is".\n') | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Finding information about movies.\n") | |
chatWindow.insert(END, 'Chatbot: To do this, type "IMDB" or "movie".\n') | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Logging in or making an account to personalist this chatbot.\n") | |
chatWindow.insert(END, 'Chatbot: To do this, type "login" or "log in".\n') | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: For more information, read our readme.md file.\n") | |
chatWindow.insert(END, "Chatbot: We have fun Q&A commands that are too long to list here.\n") | |
chatDelay() | |
chatWindow.insert(END, 'Chatbot: To disconnect from the chatbot, you may type "end".\n') | |
#end of Humza's help message | |
#integrating Vinayak's Yelp API | |
global yelpSteps | |
global yelpLocation | |
global yelpIndex | |
if ("yelp" in request or "find" in request or "location" in request or "locate" in request or "where is" in request) and yelpSteps == 0 and recognised == False: | |
recognised = True | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: What would you like to find?\n") | |
yelpSteps = 1 | |
elif yelpSteps == 1 and recognised == False: | |
recognised = True | |
if request != "": #checks if function is called recusively; if it isn't, then the area the user typed is set to yelpLocation | |
yelpLocation = request | |
store, rating = yelpSearch(yelpLocation, yelpIndex) | |
if rating != "": #checks if the yelpSearch() function gave an index error | |
chatWindow.insert(END, "Chatbot: The store we found is " + str(store) + ", which has a rating of " + str(rating) + ".\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Would you like to find another store?\n") | |
yelpSteps = 2 | |
else: | |
chatWindow.insert(END, store) #store is set to a message telling user a place could not be found, so printing that if index is out of range. | |
elif yelpSteps == 2 and recognised == False: | |
recognised = True | |
if "yeah" in request or "yes" in request: | |
yelpIndex += 1 | |
yelpSteps = 1 #go back to previous if-statement, which prints the store name and rating, and then asks user if they want to search for another store again. | |
requestInterpreter("") #calls current function recursively, so user doesn't need to press enter to go through the request interpreter again. | |
elif "no" in request: | |
chatWindow.insert(END, "Chatbot: Stopped searching for areas.\n") | |
#reset global variables so they don't interfere with future yelp searches. In else statement below, variables reset for same reason. | |
yelpSteps = 0 | |
yelpLocation = "" | |
yelpIndex = 0 | |
else: | |
chatWindow.insert(END, "Chatbot: Sorry, but we didn't understand that. Yelp search cancelled.\n") | |
yelpSteps = 0 | |
yelpLocation = "" | |
yelpIndex = 0 | |
#end of Yelpintegration | |
#code by Humza to close the chatbot if user wants to stop using it | |
#using (request == "end") because this removes all doubt that the user wants to end the program. | |
#for example, if I used ("end" in request), then the user would trigger the program's disconnection inadvertently | |
#if the user was to type "friend", which is dangerous when there is a strong impact like the program closing. | |
if request == "end" and recognised == False: | |
recognised = True | |
chatWindow.insert(END, "Chatbot: Disconnecting now... goodbye.") | |
#calling chatDelay() to highlight speaker names, to cause a delay before the program closes so the user can read that the program is closing | |
chatDelay() | |
root.after(2500) #extra delay because chatDelay() might not wait long enough for reader to see message. | |
root.destroy() | |
#end of code to close chatbot | |
if recognised == False: #runs if the command wasn't recognised. | |
chatWindow.insert(END, "Chatbot: Sorry, but I didn't understand your request. There might be a typo.\n") | |
chatDelay() | |
chatWindow.insert(END, "Chatbot: Can you try rephrasing it another way please?\n") | |
if Users.chatPeople[-1] != "User: " and weatherSteps == 0 and loginSteps == "" and yelpSteps == 0 and movieSteps == 0: | |
#checks if user has logged in | |
#also checks if any multi-step APIs are being used, so they are not interrupted. | |
randNum = randint(1, 10) | |
if randNum >= 9: #has 20% of producing a comment, because if a comment was produced every time, the user might get annoyed with spam. 20% chance because 2/10 values trigger if-statement (9 and 10). | |
chatDelay() | |
chatComments() | |
def chatComments(): | |
"""Function to comment on userAccount's attributes if user is logged in. No input and no output returned.""" | |
attributeChoose = randint(1, 4) #attribute chosen to comment on is made randomly. | |
attributeList = ["name", "age", "location", "course"] #intentionally omit password because do not want other people to see it. | |
#list above is actually useless because it's not in the code, but it reminds of the category of comment to write | |
commentNum = randint(1, 3) #some comments also chosen randomly (3 comments per attribute) | |
chatDelay() | |
if attributeChoose == 1: | |
if commentNum == 1: | |
chatWindow.insert(END, "Chatbot: " + userAccount.name + " is a nice name.\n") | |
elif commentNum == 2: | |
chatWindow.insert(END, "Chatbot: " + userAccount.name + " isn't a common name here.\n") | |
chatWindow.insert(END, "Chatbot: Did your family originate from another country?\n") | |
elif commentNum == 3: | |
chatWindow.insert(END, "Chatbot: " + userAccount.name + " isn't really a nice name, in my opinion...\n") | |
if attributeChoose == 2: | |
try: | |
age = int(userAccount.age) #when read from file, age is interpreted as string, so need to convert to int | |
if commentNum == 1: | |
chatWindow.insert(END, "Chatbot: You'll be " + str(age + 1) + "on your next birthday!.\n") | |
chatWindow.insert(END, "Chatbot: Maybe you should stop keeping track of your age so you don't feel like you'getting old.\n") | |
if commentNum == 2: | |
if age < 20: | |
chatWindow.insert(END, "Chatbot: You're young.\n") | |
chatWindow.insert(END, "Chatbot: Don't lose motivation if you find work hard!.\n") | |
elif age > 40: | |
chatWindow.insert(END, "Chatbot: You're kind of old\n") | |
chatWindow.insert(END, "Chatbot: You probably have a lot of experience in your chosen field.\n") | |
elif age > 20: | |
chatWindow.insert(END, "Chatbot: You're " + str(userAccount.age) + " years old, so you probably have a job, right?\n") | |
chatWindow.insert(END, "Chatbot: Don't forget to donate to charity once in a while.\n") | |
if commentNum == 3: | |
tenYearsAgo = age - 10 | |
if tenYearsAgo > 0: | |
chatWindow.insert(END, "Chatbot: You were " + str(tenYearsAgo) + " years old ten years ago.\n") | |
chatWindow.insert(END, "Chatbot: Do you feel old now?\n") | |
else: | |
chatWindow.insert(END, "Chatbot: You weren't even born ten years ago.\n") | |
chatWindow.insert(END, "Chatbot: Aren't you too young to use this chatbot?\n") | |
chatWindow.insert(END, "Chatbot: Or maybe you lied about your age?\n") | |
except ValueError: #occurs if user didn't enter an actual integer (like having a decimal place or a letter) when registering. | |
if commentNum == 1: | |
chatWindow.insert(END, "Chatbot: Hey, I know you didn't put in a valid age.\n") | |
chatWindow.insert(END, "Chatbot: Did you think I wouldn't notice?\n") | |
elif commentNum == 2: | |
chatWindow.insert(END, "Chatbot: Why did you bother putting in an invalid age?\n") | |
chatWindow.insert(END, "Chatbot: Don't you have anything better to do than lie?\n") | |
elif commentNum == 3: | |
chatWindow.insert(END, "Chatbot: I bet you think you're smart for putting in an invalid age.\n") | |
elif attributeChoose == 3: | |
if commentNum == 1: | |
chatWindow.insert(END, "Chatbot: I've never been to " + userAccount.location + " before.\n") | |
chatWindow.insert(END, "Chatbot: Is it nice?\n") | |
elif commentNum == 2: | |
chatWindow.insert(END, "Chatbot: Chatbots like me can't travel to real places like " + userAccount.location + ".\n") | |
chatWindow.insert(END, "Chatbot: I don't know what it's like.\n") | |
elif commentNum == 3: | |
chatWindow.insert(END, "Chatbot: You're probably sick of " + userAccount.location + " by now.\n") | |
chatWindow.insert(END, "Chatbot: Where would you like to travel to?\n") | |
elif attributeChoose == 4: | |
if commentNum == 1: | |
chatWindow.insert(END, "Chatbot: I wonder what kind of job you can get with a " + userAccount.course + " degree.\n") | |
elif commentNum == 2: | |
chatWindow.insert(END, "Chatbot: Did you want to study " + userAccount.course + " when you were a kid?\n") | |
elif commentNum == 3: | |
chatWindow.insert(END, "Chatbot: I hope you're not studying for a " + userAccount.course + " exam past midnight.\n") | |
def linkAsk(article, url): | |
"""Interrupts news output with a message box asking if the user wants to open the link and gives the option to cancel the news output""" | |
userClicked = messagebox.askyesnocancel("Open article", "Do you want to open " + '"' + article + '"?' + "\nWarning: clicking cancel will stop further news from appearing.") | |
if userClicked == True: | |
webbrowser.open_new_tab(url) | |
elif userClicked == None: | |
chatWindow.insert(END, "Chatbot: Request to see news has been cancelled.\n") | |
return None | |
return True #stops loop from breaking if user clicks yes or no by giving value for if-statement to test (True or None). | |
def chatHighlight(): | |
"""Changes colour of words in chatWindow at the start of each line indicating who is talking.""" | |
#Code below adapted from Stack Overflow via: https://stackoverflow.com/questions/29315898/searching-for-a-list-of-words-within-a-tkinter-text-widgit-in-python-2-7 | |
#Code changed by Humza slightly to fit chatbot situation batter | |
#remove tag at start because two tags with same name cannot exist. | |
chatWindow.tag_remove("user", "1.0", END) | |
for word in Users.chatPeople: #for loop that runs for each entry in the list so every applicable user/chat-person can have their name highlighted | |
idx = '1.0' | |
while idx: #while loop that runs while the idx variable is not equal to the last indext | |
idx = chatWindow.search(word, idx, nocase=1, stopindex=END) #increments idx variable so the loop searches for the next occurrence of User: or Chatbot: | |
if idx: | |
lastidx = '%s+%dc' % (idx, len(word)) | |
chatWindow.tag_add('user', idx, lastidx) | |
idx = lastidx | |
#removed list because only one string is being searched for this time | |
idx = '1.0' #reset idx to 1.0 because it searches from the beginning of the chatWindow for another word this time | |
while idx: #while loop that runs while the idx variable is not equal to the last indext | |
idx = chatWindow.search("Chatbot:", idx, nocase=1, stopindex=END) #increments idx variable so the loop searches for the next occurrence of User: or Chatbot: | |
if idx: | |
lastidx = '%s+%dc' % (idx, len("Chatbot:")) | |
chatWindow.tag_add('bot', idx, lastidx) | |
idx = lastidx | |
chatWindow.tag_config("user", foreground="blue") | |
chatWindow.tag_config("bot", foreground="red") | |
#end of code adapted from aforementioned Stack Overflow link | |
chatView = Frame(pady = 10) #frame to group top part of GUI (chat window and scroll bar) together. | |
chatView.pack() | |
chatWindow = Text(chatView) | |
chatWindow.insert(END, "Chatbot: Hi there! How are you feeling today?\n") #There's a function by a group member asking the user how they feel, but the user is meant to ask the chatbot questions (not other way around). Asked user at start so the function isn't wasted and to make chatbot seem more conversational. | |
chatWindow.config(state="disabled") #explained why this is disabled in sendClick() functoin. Look there if confused. | |
chatWindow.pack(side=LEFT, fill=Y) | |
chatHighlight() #called chatHighlight() here to highlight question about feeling | |
chatScroll = ttk.Scrollbar(chatView, command = chatWindow.yview) | |
chatScroll.pack(side=RIGHT, fill=Y) | |
chatWindow['yscrollcommand'] = chatScroll.set #links the scrollbar to the chat window so moving the scrollbar up/down causes the text on the chat window to move up/down as well | |
chatTalk = Frame(pady = 10, padx = 10) #frame to group bottom part of GUI (entry box and send button) together. | |
chatTalk.pack() | |
commandEnter = ttk.Entry(chatTalk, width=100) | |
commandEnter.pack(side=LEFT, padx = 10) | |
sendButton = ttk.Button(chatTalk, text = "Send") | |
sendButton.pack(side=RIGHT) | |
root.bind('<Return>', sendClick) | |
sendButton.bind("<Button-1>", sendClick) | |
commandEnter.focus() #sets the focus to the commandEnter widget when user starts program, to save user the trouble of having to click before typing the request | |
root.mainloop() |