Skip to content
Permalink
master
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
import discord, asyncio, nltk, random, youtube_dl, time, requests, math, json, os
from urllib.request import urlopen
from bartek_keys import getWeatherKey, getBotKey
from discord.ext import commands
from discord.ext.commands import Bot
from discord.utils import get
from discord import Game
from geotext import GeoText
from ad_nltk import find_word_synonyms
from ad_key import getAPI
from nltk import word_tokenize
#client = discord.Client()
client = commands.Bot(command_prefix ='.')
rate_key = "095069ac1920332a79d881c9"
memes_key = 'rTZE4MlZ3ymshSfFBGKX7Iatc2app1NeCLejsne2jGvBBx2sXN'
flights_key = 'Um7Sn9CdQrmshlZwGdfNclEaBdPLp1fdlO5jsnspAUBm81IqvI'
SUPPORTED_CURRENCIES = {
"EUR": "European euro",
"USD": "US dollar",
"GBP": "Great Britain pounds",
"BRL": "Brazilian real"
}
base_currency = ""
target_currency_code = ""
CURRENCY_CODES = {
1: "EUR",
2: "USD",
3: "GBP",
4: "BRL"
}
players={}
queues={}
place_types = [
"accounting", "airport", "amusement_park", "aquarium", "art_gallery", "atm", "bakery", "bank", "bar",
"beauty_salon", "bicycle_store", "book_store", "bowling_alley", "bus_station", "cafe", "campground",
"car_dealer", "car_rental", "car_repair", "car_wash", "casino", "cemetery", "church", "city_hall",
"clothing_store", "convenience_store", "courthouse", "dentist", "department_store", "doctor", "electrician",
"electronics_store", "embassy", "fire_station", "florist", "funeral_home", "furniture_store", "gas_station", "gym",
"hair_care", "hardware_store", "hindu_temple", "home_goods_store", "hospital", "insurance_agency", "jewelry_store",
"laundry", "lawyer", "library", "liquor_store", "local_government_office", "locksmith", "lodging", "meal_delivery",
"meal_takeaway", "mosque", "movie_rental", "movie_theater", "moving_company", "museum", "night_club", "painter",
"park", "parking", "pet_store", "pharmacy", "physiotherapist", "plumber", "police", "post_office",
"real_estate_agency", "restaurant", "roofing_contractor", "rv_park", "school", "shoe_store", "shopping_mall", "spa",
"stadium", "storage", "store", "subway_station", "supermarket", "synagogue", "taxi_stand", "train_station",
"transit_station", "travel_agency", "veterinary_care", "zoo"
]
counter = 0
great_list = []
for typ in place_types:
synonyms = find_word_synonyms(typ)
counter += 1
syn_list = []
for synonym in synonyms:
f_synonym = synonym.replace("_", " ")
syn_list.append(f_synonym)
great_list.append(list(set(syn_list)))
async def chatbot_log(command, message):
"""Creates a message in the terminal, stating the details of the command used."""
msg_1 = str(message.timestamp) + ": " + message.author.nick + " used " + command
msg_2 = "Server: " + message.server.name
print("------")
print(msg_1)
print(msg_2)
print("------")
async def check_weather(message, message_tokens):
"""Checks the weather in a specified location."""
# My personal key for the OpenWeatherMap API
weather_key = getWeatherKey()
# Uses the GeoText library to generate a list...
# ... of locations in the message string.
##########################################################
# Example used for GeoText: #
# elyase (2018) GeoText [online] available from #
# <https://github.com/elyase/geotext> [29 November 2018] #
# Example can be found under "Usage". #
##########################################################
locations = GeoText(message.content)
# If GeoText returns an empty list, it assumes that...
# ...there is no city/town, so the bot asks for input.
if not locations.cities:
question = "Sure! What city(s) would you like me to check in?"
await client.send_message(message.channel, question)
locations = await client.wait_for_message(author=message.author, channel=message.channel)
locations_str = locations.content
locations = GeoText(locations_str)
while not locations.cities and not "cancel" in locations_str.lower():
question = "Sorry, I didn't quite get where you want me to check. Try again?"
await client.send_message(message.channel, question)
locations = await client.wait_for_message(author=message.author, channel=message.channel)
locations_str = locations.content
locations = GeoText(locations_str)
if "cancel" in locations_str.lower():
msg = "Sure thing!"
await client.send_message(message.channel, msg)
return
# Occasional bugs occur if you dont wait...
# ...based on my debugging. No idea why.
await asyncio.sleep(1)
final_msg = "Here you go, {0.author.mention}\n".format(message)
# Iterating through all the cities the user asked for.
for city in locations.cities:
# Formatting the city name so that it works...
# ...in hyperlinks, if it has a space in it.
if " " in city:
city_words = city.split(" ")
new_string = ""
for i in range(len(city_words)-1):
# %20 indicates a space in a hyperlink.
new_string += city_words[i] + "%20"
new_string += city_words[len(city_words)-1]
# Constructing a hyperlink to extract data from the...
# ...OpenWeatherMap API.
###################################################################
# Weather API used: #
# OpenWeather (n.d.) Current weather data [online] available from #
# <https://openweathermap.org/current> [29 November 2018] #
###################################################################
url_str = "https://api.openweathermap.org/data/2.5/weather?q="
url_str += new_string
url_str += "&appid=" + weather_key
else:
url_str = "https://api.openweathermap.org/data/2.5/weather?q="
url_str += city
url_str += "&appid=" + weather_key
print(url_str)
# Sometimes there's errors with city names.
try:
###################################################################
# Example used for opening JSON from URL: #
# Montmons (2017) How to get JSON from webpage into Python script #
# [online] available from <https://bit.ly/2DR5yZa> #
# [29 November 2018] #
# Found under "Python3 Example" in the top answer. #
###################################################################
# The OpenWeatherMap API uses JSON.
url = urlopen(url_str)
# Creating a list from the API JSON file.
weather_data = json.loads(url.read())
for i in range(len(message_tokens)):
message_tokens[i] = message_tokens[i].lower()
msg_to_print = []
# Prints appropriate data, depending on...
# ...the words the user used.
if "weather" in message_tokens:
description = weather_data["weather"][0]
temperature = weather_data["main"]
wind = weather_data["wind"]
msg_to_print = [
"Location: " + city,
"Weather: " + description["main"],
"Description: " + description["description"],
# I take away 273.15 from the temperatures, as OpenWeatherMap API...
# ...provides the data to us in K (Kelvin) instead of °C (Degrees Celsius).
"Current temperature: " + str(round(temperature["temp"] - 273.15,2)) + "°C",
"Today's high: " + str(round(temperature["temp_max"] - 273.15,2)) + "°C",
"Today's low: " + str(round(temperature["temp_min"] - 273.15,2)) + "°C"
]
elif "temperature" in message_tokens or "hot" in message_tokens or "cold" in message_tokens:
temperature = weather_data["main"]
msg_to_print = [
"Location: " + city,
"Current temperature: " + str(round(temperature["temp"] - 273.15,2)) + "°C",
"Today's high: " + str(round(temperature["temp_max"] - 273.15,2)) + "°C",
"Today's low: " + str(round(temperature["temp_min"] - 273.15,2)) + "°C"
]
elif "sunny" in message_tokens or "rain" in message_tokens or "rainy" in message_tokens or "rainfall" in message_tokens or "drizzle" in message_tokens:
description = weather_data["weather"][0]
msg_to_print = [
"Location: " + city,
"Weather: " + description["main"],
"Description: " + description["description"],
]
elif "wind" in message_tokens or "windy" in message_tokens:
wind = weather_data["wind"]
msg_to_print = [
"Location: " + city,
"Wind speed: " + str(wind["speed"]) + " m/s",
"Wind direction: " + str(wind["deg"]) + "°"
]
# In case the function is called when it shouldn't be.
else:
error_msg = "Sorry, something went wrong!"
await client.send_message(message.channel, error_msg)
return
final_msg += "```\n"
for to_print in msg_to_print:
final_msg += to_print + "\n"
final_msg += "```"
# This prints instead if something goes wrong.
except:
error_msg = "Whoops! Something went wrong. Did you write the location name correctly?"
await client.send_message(message.channel, error_msg)
await client.send_message(message.channel, final_msg)
await chatbot_log("Weather", message)
async def tell_fact(message):
###############################################################################
# Facts used: #
# livin3 (n.d.) Top 155 Interesting and Weird Fun Facts [online] #
# available from <Top 155 Interesting and Weird Fun Facts> [29 November 2018] #
###############################################################################
print("Opening JSON...")
random_facts = open("facts.json", "r")
random_facts = json.loads(random_facts.read())
print("JSON opened!")
# change_string will determine whether the original message or the reply message...
# ... from the code below will be used in the final check for what sort of...
# ... fact to send back to the Discord channel.
change_string = False
user_message = ""
type_in_message = False
for fact_type in ("interesting", "fun", "funny", "animal", "random"):
if fact_type in message.content.lower():
type_in_message = True
break
if not type_in_message:
change_string = True
await client.send_message(message.channel, "Sure! What sort of fact? (interesting, fun, funny, animal or random)")
user_message = await client.wait_for_message(author=message.author, channel=message.channel)
user_message = nltk.word_tokenize(user_message.content.lower())
# Returns when "fact" is in the reply, so that this function call doesn't interfere...
# ... with the new call caused by the "on_message" coroutine below.
if "fact" in user_message:
return
for fact_type in ("interesting", "fun", "funny", "animal", "random"):
if fact_type in user_message:
type_in_message = True
break
while not type_in_message and not "cancel" in user_message:
await client.send_message(message.channel, "Sorry, what type?")
user_message = await client.wait_for_message(author=message.author, channel=message.channel)
user_message = nltk.word_tokenize(user_message.content.lower())
if "fact" in user_message:
return
for fact_type in ("interesting", "fun", "funny", "animal", "random"):
if fact_type in user_message:
type_in_message = True
# Gives the user validation that the operation was cancelled.
if "cancel" in user_message:
await client.send_message(message.channel, "Sure thing!")
return
# I do this so that I don't have to write out the below checks twice.
# ... Instead I just use the same variable to hold the string.
if not change_string:
user_message = nltk.word_tokenize(message.content.lower())
if "interesting" in user_message:
facts = random_facts["interesting"]
fact = facts[random.randint(0,len(facts)-1)]
msg_to_print = "Here you go, " + message.author.mention + "\n"
msg_to_print += fact
await client.send_message(message.channel, msg_to_print)
# "funny" needs to come before "fun", otherwise "funny" will...
# ... always be skipped.
elif "funny" in user_message:
facts = random_facts["funny"]
fact = facts[random.randint(0,len(facts)-1)]
msg_to_print = "Here you go, " + message.author.mention + "\n"
msg_to_print += fact
await client.send_message(message.channel, msg_to_print)
elif "fun" in user_message:
facts = random_facts["fun"]
fact = facts[random.randint(0,len(facts)-1)]
msg_to_print = "Here you go, " + message.author.mention + "\n"
msg_to_print += fact
await client.send_message(message.channel, msg_to_print)
elif "animal" in message.content:
facts = random_facts["animal"]
fact = facts[random.randint(0,len(facts)-1)]
msg_to_print = "Here you go, " + message.author.mention + "\n"
msg_to_print += fact
await client.send_message(message.channel, msg_to_print)
elif "random" in user_message:
types_of_facts = (
{"type": "interesting"},
{"type": "fun"},
{"type": "funny"},
{"type": "animal"}
)
# Picking a random fact type
random_type = types_of_facts[random.randint(0,len(types_of_facts)-1)]
print(random_type)
facts = random_facts[random_type["type"]]
fact = facts[random.randint(0,len(facts)-1)]
print(fact)
msg_to_print = "Here you go, " + message.author.mention + "\n"
msg_to_print += fact
await client.send_message(message.channel, msg_to_print)
################################################################################
###########################
#Codes used to locate user#
###########################
async def start_locating(message):
"""Starts locating user by asking for the user's current location."""
ask = "Can you give me the address of your current location? I would appreciate it if it's the full address."
await client.send_message(message.channel, ask)
location = await client.wait_for_message(author=message.author, channel=message.channel)
location_str = location.content
pending = "I'll try my best to find {} .".format(location_str)
await client.send_message(message.channel, pending)
await checking_address(message, location_str)
def locating(location_str):
"""
Takes in the address given by the user and format it to make it sensible in a url.
The output is the value of the dictionary key "results" in the json data requested.
"""
address = location_str.replace(" ", "+")
url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=" + getAPI()
r = requests.get(url)
print(url)
location_json = r.json()
results = location_json["results"]
#if else statement used to check if the value of "results" is an empty list or not.
if results == [ ]:
results = 0
return results
def confirmation_status(response):
"""Checks the response for wait_for_message coroutines and assign different "status" to each response."""
if "yes" in response:
status = 1
return status
elif "no" in response:
status = 0
return status
else:
status = 2
return status
async def confirming(message, status, location_str):
"""
Asks the user if the address found is correct.
Dump location data in a file assigned with the user's id if user has confirmed the address.
"""
if status == 1:
f = open("location_data_{}.txt".format(message.author.mention), "w") #creates a data file with author_id associated
json.dump(locating(location_str), f)
print("location_data_{}.txt created".format(message.author.mention))
address = locating(location_str)[0]
formatted_address = address["formatted_address"]
geometry = address["geometry"]
location = geometry["location"]
lat = location["lat"]
lng = location["lng"]
results = [
"Formatted address: {}".format(formatted_address),
"Location:",
"Latitude: {}".format(lat),
"Longitude: {}".format(lng)
]
#format information to display on discord text channel.
msg = "```\n"
for result in results:
msg += result + "\n"
msg += "```"
await client.send_message(message.channel, "Location acquired, {}.\n".format(message.author.mention) + msg)
elif status == 0:
msg = "I'm sorry. Can I have your current address again?"
await client.send_message(message.channel, msg)
location_i = await client.wait_for_message(author=message.author, channel=message.channel)
location_i_str = location_i.content
pending = "I'll try my best to find {}.".format(location_i_str)
await client.send_message(message.channel, pending)
await checking_address(message, location_i_str)
elif status == 2:
await client.send_message(message.channel, "I don't understand that, please say 'yes' or 'no'.")
confirmation_i = await client.wait_for_message(author=message.author, channel=message.channel)
confirmation_str_i = word_tokenize(confirmation_i.content.lower())
status_i = confirmation_status(confirmation_str_i)
location_i_str = location_str
await confirming(message, status_i, location_i_str)
async def checking_address(message, location_str):
"""
Checks if location_str returns a proper address via the locating(location_str) function.
Asks user for another address if the first one yields no results.
"""
results = locating(location_str)
if results == 0: #no results from the address given
await client.send_message(message.channel, "I don't think you've given me a real address.")
await client.send_message(message.channel, "Do you still want me to locate you?")
proceed = await client.wait_for_message(author=message.author, channel=message.channel)
proceed_str = word_tokenize(proceed.content.lower())
status = confirmation_status(proceed_str)
#if else statement used to check if the user still wants to continue the procedure.
if status == 1:
await client.send_message(message.channel, "Give me your current address again.")
location = await client.wait_for_message(author=message.author, channel=message.channel)
location_str = location.content
pending = "I'll try my best to find {}.".format(location_str)
await client.send_message(message.channel, pending)
await checking_address(message, location_str)
elif status == 0:
await client.send_message(message.channel, "Type 'locate' again when you're serious about it.")
else:
await client.send_message(message.channel, "I don't think you're being serious.")
await client.send_message(message.channel, "Type 'locate' again when you're serious about it.")
else:
address = results[0]
formatted_address = address["formatted_address"]
question = "Are you talking about {} ?".format(formatted_address)
await client.send_message(message.channel, question)
confirmation = await client.wait_for_message(author=message.author, channel=message.channel)
confirmation_str = word_tokenize(confirmation.content.lower())
status = confirmation_status(confirmation_str)
await confirming(message, status, location_str)
##########################################
#Codes used to delete existing data files#
##########################################
async def os_error_handling(message, reply):
"""Redirects users to the start_locating function if their location data wasn't stored."""
status = confirmation_status(reply)
if status == 0:
await start_locating(message)
elif status == 1:
await client.say("I'm sorry. Something must have gone wrong.")
await start_locating(message)
else:
await client.say("I don't understand that, please say 'yes' or 'no'.")
c_reply = await client.wait_for_message(author=message.author, channel=message.channel)
c_reply_str = word_tokenize(c_reply.content.lower())
await os_error_handling(message, c_reply_str)
@client.command(pass_context=True)
async def location_wipe(ctx):
"""Delete the location data file created."""
print("Deleting location_data_{0.author.mention}.txt".format(ctx.message))
try:
os.remove("location_data_{0.author.mention}.txt".format(ctx.message))
await client.say("Your location data is deleted, {0.author.mention}.".format(ctx.message))
except FileNotFoundError: #if location data of the author doesn't exist.
msg = "I couldn't find your location data. Have you given me your location, {0.author.mention}?".format(ctx.message)
await client.say(msg)
reply = await client.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
reply_str = reply.content
await os_error_handling(ctx.message, reply_str)
########################################
#Codes used to search for nearby places#
########################################
##following codes are slightly buggy, but it achieves most of what is expected.
async def start_searching(message, tokens):
"""Begin searching for nearby locations."""
place_type_rlist = []
place_type_rlist2 = []
for token in tokens:
if token in place_types:
place_type_r = token
place_type_rlist.append(place_type_r)
else:
for (i, list) in enumerate(great_list): #enumerate() is used to retrieve the index of the item
for items in list:
if items in message.content.lower():
place_type_r = place_types[i]
place_type_rlist.append(place_type_r)
if place_type_rlist:
for place_type_r in set(place_type_rlist): #remove duplicants of a certain value
place_type_rlist2.append(place_type_r)
elif place_type_rlist == []: #place_types not found in message
msg = "Please type in the place type(s) you want to look for."
await client.send_message(message.channel, msg)
start_str = await client.wait_for_message(author=message.author, channel=message.channel)
await start_searching(message, word_tokenize(start_str.content))
print(place_type_rlist2)
await check_file(message, place_type_rlist2)
async def confirmation_to_locate(message, status):
"""Confirms if user want to continue to be located."""
if status == 1:
await start_locating(message)
elif status == 0:
await client.send_message(message.channel, "Ok then.")
elif status == 2:
await client.send_message(message.channel, "I don't understand that, please say 'yes' or 'no'.")
confirmation = await client.wait_for_message(author=message.author, channel=message.channel)
confirmation_str = word_tokenize(confirmation.content.lower())
status = confirmation_status(confirmation_str)
await confirmation_to_locate(message, status)
async def check_file(message, place_type_list):
"""
Checks if user's location data file exists.
Redirect user to create file if user's data file is not found.
"""
try:
f = open("location_data_{0.author.mention}.txt".format(message), "r").read()
f_json = json.loads(f)
c_address = f_json[0]
c_formatted_address = c_address["formatted_address"]
c_geometry = c_address["geometry"]
c_location = c_geometry["location"]
c_latitude = c_location["lat"]
c_longitude = c_location["lng"]
#assigning user's current location to url
url_base = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location="
url_first_components = url_base + str(c_latitude) + "," + str(c_longitude)
print(url_first_components)
await check_params(message, url_first_components, place_type_list)
except FileNotFoundError:
msg = "I don't think your location data file has been created yet, {0.author.mention}.".format(message)
msg2 = msg + " Do you want me to locate you now?"
await client.send_message(message.channel, msg2)
confirmation = await client.wait_for_message(author=message.author, channel=message.channel)
confirmation_str = confirmation.content
status = confirmation_status(confirmation_str)
await confirmation_to_locate(message, status)
async def check_radius_str(message, token_list):
"""Checks if the input is an appropriate integer for the radius parameter."""
for token in token_list:
if token.isdigit():
if int(token) <= 50:
radius = int(token)*1000
elif int(token) < 0:
radius = "not_suitable"
else:
await client.send_message(message.channel, "Maximum radius allowed is 50km.")
radius = "not_suitable"
else:
radius = "not_suitable"
return radius
async def radius_none(message):
"""Asks user repeatedly to provide a radius in the accepted format, or 'yes' or 'no'."""
msg = "Please type in a postive integer for the radius parameter."
await client.send_message(message.channel, msg)
msg = "Prefer to use the default value for the radius parameter(5km)? Please say 'yes' or 'no'."
await client.send_message(message.channel, msg)
radius_reaffirm = await client.wait_for_message(author=message.author, channel=message.channel)
radius = await check_radius_str(message, word_tokenize(radius_reaffirm.content)) #check if radius is in correct format
if radius != "not_suitable":
pass
elif radius == "not_suitable":
status = confirmation_status(word_tokenize(radius_reaffirm.content))
if status == 1:
radius = 5000
elif status == 0:
radius = await radius_none(message)
elif status == 2:
await client.send_message(message.channel, "Please follow my previous instructions.")
radius_reaffirm2 = await client.wait_for_message(author=message.author, channel=message.channel)
radius = await check_radius_str(message, word_tokenize(radius_reaffirm2.content))
if radius == "not_suitable":
radius = await radius_none(message) #function never get called for reasons unknown (bug)
return radius
async def check_radius(message, type):
"""
Asks the user if he/she would like to use the radius parameter.
Redirects user to other functions based on user's response.
"""
msg = "Would you like to search for " + type + " in a certain radius?"
await client.send_message(message.channel, msg)
radius_confirmation = await client.wait_for_message(author=message.author, channel=message.channel)
status = confirmation_status(radius_confirmation.content)
if status == 1:
msg = "Please type in the desired radius in terms of kilometres."
await client.send_message(message.channel, msg)
radius_reply = await client.wait_for_message(author=message.author, channel=message.channel)
radius = await check_radius_str(message, word_tokenize(radius_reply.content))
if radius == "not_suitable":
radius = await radius_none(message)
elif status == 0:
msg = "The default value for the radius parameter(5km) will be used then."
await client.send_message(message.channel, msg)
radius = 5000
elif status == 2:
radius = await radius_none(message)
return radius
async def check_keywords(message):
"""
Checks if user wants to use the keyword parameter.
Returns keyword if user responded 'yes'.
"""
msg = "Would you like to add keyword(s) to filter the results?"
await client.send_message(message.channel, msg)
key_word_c = await client.wait_for_message(author=message.author, channel=message.channel)
status = confirmation_status(key_word_c.content)
keyword = await keyword_confirm(message, status)
return keyword
async def keyword_confirm(message, status):
"""Process the response from the check_keywords function to determine if keyword parameter is required."""
if status == 1:
msg = "Please type in the keyword(s)."
await client.send_message(message.channel, msg)
keywords = await client.wait_for_message(author=message.author, channel=message.channel)
keyword = keywords.content.replace(" ", "+") #formatting the keywords for the url
elif status == 0:
keyword = "not_desired"
elif status == 2:
msg = "Prefer to not use the keyword parameter? Please say 'yes' or 'no'."
await client.send_message(message.channel, msg)
key_word_c = await client.wait_for_message(author=message.author, channel=message.channel)
status = confirmation_status(key_word_c.content)
keyword = await keyword_confirm(message, status)
return keyword
async def url_formatting(url, type, radius, keyword):
"""Format the values gathered to form a valid url."""
url_second = url + "&radius=" + str(radius) + "&type=" + type
if keyword == "not_desired":
url_final = url_second + "&key=" + getAPI()
else:
url_final = url_second + "&keyword=" + str(keyword) + "&key=" + getAPI()
return url_final
async def check_params(message, url, place_types_detected):
"""
Works as an 'umbrella' function which nests other functions that checks specific parameters.
Checks if the place_types detected is more than 3.
Then limits the user to choosing only three place_types.
"""
if len(place_types_detected) == 1:
type = place_types_detected[0]
radius = await check_radius(message, type)
keywords = await check_keywords(message)
url_final = await url_formatting(url, type, radius, keywords)
print(url_final)
await fetch_results(message, url_final)
elif len(place_types_detected) < 4:
type = place_types_detected
type_str = ", ".join(type)
print (type_str)
radius = await check_radius(message, type_str)
keywords = await check_keywords(message)
url_list = []
for type_i in type:
url_i = await url_formatting(url, type_i, radius, keywords)
url_list.append(url_i)
print(url_list)
num = 1 #used to separate results of different place_types
for url in url_list:
await client.send_message(message.channel, num)
num += 1
await fetch_results(message, url)
else:
msg = "Please limit the number of types of establishment to 3."
await client.send_message(message.channel, msg)
await start_searching(message, tokens)
async def fetch_results(message, url):
"""Uses the url formed by other functions to request data of nearby locations."""
r = requests.get(url)
places_json = r.json()
results = places_json['results'] #assign results to the results dictionary
if results == [ ]:
await client.send_message(message.channel, "Your request has yielded no results.")
else:
result_list = []
#enumerate() to retain index number in each iteration
for (i, result) in enumerate(results):
list = []
result = results[i]
name = result['name']
list.append(name)
try:
if result['opening_hours']:
opening_hours = result['opening_hours']
if opening_hours == { }:
open_now = "Info not available"
elif opening_hours['open_now']:
open_now = str(opening_hours['open_now'])
list.append(open_now)
except KeyError:
open_now = "Info not available"
list.append(open_now)
if result['vicinity']: #if vicinity is a valid key in the results dictionary
vicinity = result['vicinity']
list.append(vicinity)
else:
vicinity = "Info not available"
list.append(vicinity)
result_list.append(list)
print(list)
if len(result_list) == 1: #different response based on number of places.
await client.send_message(message.channel, "This is the place that I've found.")
else:
await client.send_message(message.channel, "These are the places that I've found.")
for list in result_list:
if list[0]:
name = list[0]
else:
name = "Info not available"
open_now = list[1] #open_now has been 'pruned' beforehand
try:
vicinity = list[2]
except IndexError:
vicinity = list[1]
results = [
"Name: " + name,
"Open now: " + str(open_now),
"Vicinity: " + vicinity
]
#Method inspired by codes in bartek_nltk.py
msg = "```\n"
for result in results:
msg += result + "\n"
msg += "```"
await client.send_message(message.channel, msg)
###The Youtube Music Player###
@client.command(pass_context=True)
async def summon(ctx):
"""joins the voice channel that the user is currently in"""
if ctx.message.author.voice_channel:
vc = ctx.message.author.voice.voice_channel
msg = "The bot has joined your voice channel"
await client.send_message(ctx.message.channel, msg)
await client.join_voice_channel(vc)
else:
msg = "Join a voice channel and try again"
await client.send_message(ctx.message.channel, msg)
async def ycjoin(ctx):
"""Seperate function that joins the voice channel for .play command since async functions can't be called from other async functions"""
if ctx.message.author.voice_channel:
vc = ctx.message.author.voice.voice_channel
msg = "The bot has joined your voice channel"
await client.send_message(ctx.message.channel, msg)
await client.join_voice_channel(vc)
else:
msg = "Join a voice channel and try again"
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def disconnect(ctx):
"""Disconnects from the voice channel that client is currently in"""
server = ctx.message.server
if ctx.client.voice_client_in(server):
voice_client = client.voice_client_in(server)
msg = "The bot has now disconnected from the voice channel"
await client.send_message(ctx.message.channel, msg)
await voice_client.disconnect()
else:
msg = "The chatbot is not currently in a voice channel"
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def play(ctx,*,url):
"""Downloads the audio from a youtube video via url to play through the voice channel"""
try:
server = ctx.message.server
if ctx.client.voice_client_in(server):
server = ctx.message.server
voice_client = client.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, ytdl_options={'default_search': 'auto'}, after=lambda: check_queue(server.id))
players[server.id] = player
player.start()
msg = "Now playing.... " + str(player.title) + " This video is " +str(player.duration) + " ...seconds long"
await client.send_message(ctx.message.channel, msg)
else:
await ycjoin(ctx)
if ctx.client.voice_client_in(server):
server = ctx.message.server
voice_client = client.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, ytdl_options={'default_search': 'auto'}, after=lambda: check_queue(server.id))
players[server.id] = player
player.start()
msg = "Now playing.... " + str(player.title)
await client.send_message(ctx.message.channel, msg)
except:
msg = "Sorry, I couldn't find that video you wanted "
await client.send_message(ctx.message.channel, msg)
#@client.command functions are not callable in the same or other command functions otherwise it causes an error. So i created a seperate non-command (ycjoin) function to join a voice
#channel and then play the video if the bot was not already in the voice channel. (ycjoin and summon functions are identical)
#the "x" in the argument is needed otherwise the bot will only use the first word of what i want it to search for. It forces the bot to take everything after .play as the
#non-keyworded variable length argument list
@client.command(pass_context=True)
async def pause(ctx):
"""This pauses the music that is currently playing through the bot"""
server = ctx.message.server
try:
if ctx.client.voice_client_in(server):
if players[server.id].is_playing()==True:
players[server.id].pause()
msg = "The audio is now paused."
await client.send_message(ctx.message.channel, msg)
elif players[server.id].is_playing()==False:
msg = "There is nothing currently playing"
await client.send_message(ctx.message.channel, msg)
if not ctx.client.voice_client_in(server):
msg = "The client is not in a voice channel"
await client.send_message(ctx.message.channel, msg)
except:
msg = "There is no video that is currently loaded. Play something first."
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def resume(ctx):
"""This will resume the previously paused audio stream"""
server = ctx.message.server
try:
if ctx.client.voice_client_in(server):
if players[server.id].is_playing()==False:
server = ctx.message.server
players[server.id].resume()
msg = "The audio feed has now resumed."
await client.send_message(ctx.message.channel, msg)
elif players[server.id].is_playing()==True:
msg = "It's already playing buddy"
await client.send_message(ctx.message.channel, msg)
if not ctx.client.voice_client_in(server):
msg = "The client is not in a voice channel"
await client.send_message(ctx.message.channel, msg)
except:
msg = "There is no video that is currently loaded. Play something first."
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def stop(ctx):
"""Completely cuts off the audio feed and can't be resumed unlike pause. Only works if there is no queue. Otherwise it just acts as a skip function"""
server = ctx.message.server
players[server.id].stop()
msg = "The audio feed has now been stopped."
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def info(ctx):
"""This provides the user information on the YouTube video that is currently playing. Title, duration, upload date and the uploader"""
server = ctx.message.server
player = players[server.id]
msg = "This video is called " + str(player.title) + " and it is " +str(player.duration) + " ...seconds long. It was uploaded on " +str(player.upload_date) + " by " + str(player.uploader)
await client.send_message(ctx.message.channel, msg)
@client.command(pass_context=True)
async def queue(ctx,*,url):
"""This allows the user to queue multiple videos to be played by the bot in the order they were entered. Use .play first beforehand"""
server = ctx.message.server
if ctx.bot.voice_client_in(server):
voice_client = bot.voice_client_in(server)
player = await voice_client.create_ytdl_player(url,ytdl_options={'default_search': 'auto'},after=lambda: check_queue(server.id))
if server.id in queues:
queues[server.id].append(player)
else:
queues[server.id] = [player]
msg = "Next up is... " + str(player.title) + " This video will be " +str(player.duration) + " ...seconds long. This video is now in queue. Remember, you must use .play to play a video first before .queue to place others in the queue."
await bot.send_message(ctx.message.channel, msg)
else:
await ycjoin(ctx)
msg = "I have now joined your voice channel. If nothing is playing, Try .play to play a video before placing others in the queue using .queue."
await bot.send_message(ctx.message.channel, msg)
def check_queue(id):
if queues[id] != []:
player = queues[id].pop(0)
players[id] = player
player.start()
@client.command(pass_context=True)
async def skip(ctx):
"""Skips the current song in the queue."""
server = ctx.message.server
players[server.id].stop()
msg = "Skipped!"
await client.send_message(ctx.message.channel, msg)
################################################################################
@client.command(pass_context=True)
async def exchangerate(ctx,):
url = ("https://v3.exchangerate-api.com/bulk/095069ac1920332a79d881c9/USD")
response = requests.get(url)
data = response.json()
print(data)
if __name__ == '__main__':
await client.say("Welcome to Currency Converter")
await client.say("Enter the amount you wish to convert: ")
message = await client.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
await client.say("Choose a base currency among our supported currencies:")
message1 = await client.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
for currencies in CURRENCY_CODES:
if message1 == currencies:
try:
url = "https://v3.exchangerate-api.com/bulk/095069ac1920332a79d881c9/USD"
url += rate_key
print(url)
url_rate = urlopen(url)
data = json.loads(url_rate.read())
except requests.exceptions.RequestException:
print('HTTP Request failed')
else:
await client.say("Invalid code")
await client.say("Choose a target currency among our supported currencies:")
target = await client.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
if target == CURRENCY_CODES:
try:
url = "https://v3.exchangerate-api.com/bulk/095069ac1920332a79d881c9/USD"
url += rate_key
print(url)
url_rate = urlopen(url)
exchangerate = json.loads(url_rate.read())
except requests.exceptions.RequestException:
print('HTTP Request failed')
else:
await client.say("Invalid code")
target_currency = CURRENCY_CODES
print("url_rate")
@client.command(pass_context=True)
async def cinema(ctx):
try:
movie = requests.get(
url="https://api.internationalshowtimes.com/v4/movies/",
params={
"countries": "GB",
},
headers={
"X-API-Key": "fghdqbFMn1QjoOJmBUm29Y7IA818QTU6",
},
)
print('Response HTTP Status Code: {status_code}'.format(
status_code=response.status_code))
print('Response HTTP Response Body: {content}'.format(
content=response.content))
except requests.exceptions.RequestException:
print('HTTP Request failed')
@client.command(pass_context=True)
async def memes(ctx):
memes = requests.get("https://ronreiter-meme-generator.p.rapidapi.com/meme?meme=Grumpy-Cat-Star-Wars&font_size=50&top=when+they+say&bottom=the+food+is+gone",
headers={
"X-Mashape-Key": "rTZE4MlZ3ymshSfFBGKX7Iatc2app1NeCLejsne2jGvBBx2sXN",
"X-Mashape-Host": "ronreiter-meme-generator.p.rapidapi.com"
}
)
try:
url_new = "https://ronreiter-meme-generator.p.rapidapi.com/meme=Grumpy-Cat-Star-Wars&font_size=50&top=when+they+say&bottom=the+food+is+gone"
url_new += memes_key
print(url_new)
url = urlopen(url_new)
meme_generator = json.loads(url.read())
except requests.exceptions.RequestException:
print('HTTP Request failed')
ctr = ["France","United+Kingdom","Portugal","Russia","Bulgaria"]
crc = "GBP"
locale ="en-US"
departure = ""
land = ""
outbound= ""
inbound= ""
cabinclass = ["economy","business","first"]
adults = 1
children = 0
infants = 0
response = requests.post("https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/pricing/v1.0",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"X-Mashape-Key": "Um7Sn9CdQrmshlZwGdfNclEaBdPLp1fdlO5jsnspAUBm81IqvI",
"X-Mashape-Host": "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com"},
data={
"country":ctr,
"currency":crc,
"locale":locale,
"originPlace":departure,
"destinationPlace":land,
"outboundDate":outbound,
"inboundDate":inbound,
"cabinClass": cabinclass,
"adults":adults,
"children":children,
"infants":infants,
"includeCarriers":"",
"excludeCarriers":"",
"groupPricing":"false"
}
)
#print(response.json())
ctrsearcher = requests.get("https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-EU/?query=France",
headers={
"X-Mashape-Key": "Um7Sn9CdQrmshlZwGdfNclEaBdPLp1fdlO5jsnspAUBm81IqvI",
"X-Mashape-Host": "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com"
}
)
#print(ctrsearcher.json())
user_message = ""
@client.command(pass_context=True)
async def flights(ctx):
msg = 'I see you want to travel.From where do you want to flight?'
await client.say(msg)
user_message = await client.wait_for_message(author=ctx.message.author, channel = ctx.message.channel)
if user_message != ctr:
question = "Sure! What country would you like me to check for?"
await client.say(question)
message = await client.wait_for_message(author=ctx.message.author, channel=ctx.message.channel)
message = message.content
for word in ctr:
if message == ctr:
try:
url = "https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-EU/?query=France"
url += flights_key
print(url)
url_add = urlopen(url)
flights = json.loads(url_add.read())
except requests.exceptions.RequestException:
print('HTTP Request failed')
else:
client.say("Sorry. I couldn't find it.")
# Functions
chat_restriction = ["die", "fuck", "cunt", "gay", "faggot"]
async def user_is_Bot(user):
author_roles = user.roles
has_right_role = False
for role in author_roles:
if role == discord.utils.get(user.server.roles):
has_right_role = True
return has_right_role
async def user_is_Admin(user):
author_roles = user.roles
has_right_role = False
for role in author_roles:
if role == discord.utils.get(user.server.roles):
has_right_role = True
return has_right_role
async def user_is_Gaybitch(user):
author_roles = user.roles
has_right_role = False
for role in author_roles:
if role == discord.utils.get(user.server.roles):
has_right_role = True
return has_right_role
##Used in the role fuction
@client.command(pass_context=True)
async def clear(ctx,amount=100):
channel = ctx.message.channel
messages = []
async for message in client.logs_from(channel, limit=int(amount) +1):
messages.append(message)
await client.delete_messages(messages)
await client.say('Message deleted')
# clear the wanted amount of messages between 1 and 100, I added (amount)+1, because it will not delete the last message, which will be the command.
@client.command(pass_context=True)
async def commands(ctx):
author = ctx.message.author
embed = discord.Embed(
title = 'Chatbot commands',
description =
"Use **.** in front of the command"
"Example: .hello",
colour = discord.Colour.blue()
)
embed.add_field(name="Chat commands", value=
"``hello`` - Greet the Chatbot \n"
"``how`` - Checks how is the Chatbot \n"
"``Whatareyoudoing`` - Checks what is going to do tonight \n"
"``clear``- clear the amount of messages you want \n " # max 100(+1), wont delete messages older than 14days.
"``role`` - add or remove roles from the server \n"
"``checks`` - checks if the role exists \n"
"``kick`` - kicks members from the channel \n"
"``ban`` - bans members from the channel \n"
"``mute`` - mutes members from the channel \n"
"``unmute`` - unmutes members from the channel \n"
"``flights`` - gives all the airports from the selected country \n"
"``memes`` - randomly generators memes \n"
"``exchangerate`` - convert currency \n "
, inline=False)
embed.set_thumbnail(url=ctx.message.author.avatar_url)
embed.set_footer(text='Requested by ' + ctx.message.author.name + '.', icon_url=ctx.message.author.avatar_url)
# Synthesizes all the commands into an embed message so that they could be executed by the user.
avaialbe_roles = ["Admin", "Gaybitch", "Bot"]
@client.command(pass_context=True, name="role")
async def role(ctx, role: discord.Role=True, user: discord.user=True):
server = ctx.message.server
await user_is_Bot(ctx.message.author)
await user_is_Gaybitch(ctx.message.author)
await user_is_Admin(ctx.message.author)
role is False and user is True
await client.say("That role exists")
if role not in avaialbe_roles and user==True:
await client.add_roles(ctx.message.author, role)
await client.say("role has been added to roles.")
elif role not in avaialbe_roles and user==True:
await client.remove_roles(ctx.message.author, role)
await client.say("role has been removed.")
elif role not in server.roles:
await client.remove_roles(ctx.message.author, role)
await client.say("role has been removed from.")
else:
await client.say("Silly human, you do not have permission to use this command!")
@client.command(pass_context=True, name="check")
async def check(ctx, role: discord.Role=True):
server = ctx.message.server
current_roles = server.roles
if role in current_roles:
await client.say("That role exists")
return
else:
if role != server.roles:
await client.say("That role doesn't exist. Would you like to make it?")
reply = await client.wait_for_message(author=ctx.message.author, channel = ctx.message.channel)
if reply == "yes":
await client.edit_role(ctx.message.author, role)
await client.say("Role has been added to the server.")
elif reply == "no":
await client.say("No worries.")
@client.command(pass_context=True)
async def kick(ctx, member: discord.Member=None):
if not member:
await client.say("Please specify a member.")
return
else:
await client.say(f"{member.mention} got kicked.")
await client.kick(member)
@client.command(pass_context=True)
async def ban(ctx, member: discord.Member=None):
if not member:
await client.say("Please specify a member.")
return
else:
await client.say(f"{member.mention} got banned.")
await client.ban(member)
@client.command(pass_context=True)
async def mute(ctx, member: discord.Member=None):
if not member:
await client.say("Please specify a member.")
return
else:
await client.say(f"{member.mention} got muted.")
await client.mute(member)
@client.command(pass_context=True)
async def unmute(ctx, member: discord.Member=None):
if not member:
await client.say("Please specify a member.")
return
else:
await client.say(f"{member.mention} got unmuted.")
await client.unmute(member)
################################################################################
########################
#Random answers command#
########################
@client.command(name = 'Questions',
description = 'Answers basic question with random answers.',
brief = 'Answers lit questions.',
aliases = ['quest','q',],
pass_context = True)
async def eightball(context):
possible_responses = ['Yes', 'No', 'Maybe', "I'll think about it"]
await client.say(random.choice(possible_responses)+ " " + context.message.author.mention)
#####################
#Calculator Commands#
#####################
#Square Command
@client.command(name = 'sqr',
description = 'Squares your number.',
brief = 'Square function',
aliases = ['square','^'])
async def square(number):
squared_value = int(number) * int(number)
await client.say(str(number)+ " squared is " + str(squared_value))
#Divide Command
@client.command(name = 'div',
description = 'Divides the first number by second one.',
brief = 'Dividing function',
aliases = ['/','divide'])
async def divide(number1,number2):
divided_value = int(number1)/int(number2)
await client.say('The answer is: ' + str(divided_value))
#Plus Command
@client.command(name = 'plus',
description = 'Adds together both numbers.',
brief = 'Plus function',
aliases = ['+'])
async def plus(number1,number2):
plus_value = int(number1) + int(number2)
await client.say('The answer is: ' + str(plus_value))
#Minus Command
@client.command(name = 'minus',
description = 'Minuses the second number from the first number.',
brief = 'Minus function',
aliases = ['-','min'])
async def minus(number1,number2):
minus_value = int(number1) - int(number2)
await client.say('The answer is: ' + str(minus_value))
#Multiply Command
@client.command(name = 'multiply',
description = 'Multiplies first number by second one.',
brief = 'Multiply function',
aliases = ['*','multi'])
async def multiply(number1,number2):
multiply_value = int(number1) * int(number2)
await client.say('The answer is: ' + str(multiply_value))
###################
#The clear command#
###################
#@client.command(name = 'clear',
# description = 'Clears the chat.',
# brief = 'Clears the chat',
# aliases = ['c', 'feetusdeletus'],
# pass_context=True)
#async def clear(ctx, amount = 100):
# channel = ctx.message.channel
# messages = []
# async for message in client.logs_from(channel, limit=int(amount) + 2):
# messages.append(message)
# await client.delete_messages(messages)
# await client.say('Messages deleted fam')
#########################
#Time Command#In Working#
#########################
#@client.command(name = 'time',
# brief = 'Under construction.')
#async def time():
# msg = "The time is currently "
# await client.say(message.channel, msg + str(message.timestamp))
################
#logout command#
################
#@client.command(name = 'logout',
# description = 'Logouts Chad.',
# brief = 'Logouts Chad',
# aliases = ['l','bye'],
# pass_context = True)
#async def logout(context):
# possible_responses = ['See you later alligator',"Fam's out", "See you in a while"]
# await client.say(random.choice(possible_responses)+ " " + context.message.author.mention)
# await client.close()
#############
#Pin Command#
#############
@client.command(name = 'pin',
description = 'Pins your message.',
brief = 'Pins a message',
aliases = ['p'],
pass_context = True)
async def pin(context):
message = context.message
await client.pin_message(message)
await client.say('I pinned your message.')
###############
#UnPin Command#
###############
@client.command(name = 'unpin',
description = 'Unpins your messages',
brief = 'Unpins a message',
aliases = ['unp'],
pass_context = True)
async def unpin(context):
message = context.message
await client.unpin_message(message)
await client.say('I unpined your message.')
##########
#Pin List#
##########
@client.command(name = 'pinlist',
pass_context = True)
async def pinlist(context):
command = client.get_all_channels
await client.pins_from(command.channel)
await client.say('Your pinned messages.')
################################################################################
# discord.py coroutine triggered when the bot joins a server.
@client.event
async def on_server_join(server):
print("Joining " + server.name)
await client.create_channel(server, "chatbot")
msg = "Hello @everyone ! Please use this channel to use the Chatbot."
await asyncio.sleep(2)
chatbot_channel = discord.utils.get(server.channels, name="chatbot")
await client.send_message(chatbot_channel, msg)
print("Finished joining " + server.name)
print("----------")
# discord.py coroutine triggered when a message is sent to a channel...
# ... on a server the bot is in.
@client.event
async def on_message(message):
if message.author == client.user or message.channel.name != "chatbot":
return
# List of words that will trigger the bot to run my weather coroutine.
weather_words = (
"weather",
"temperature",
"hot",
"cold",
"sun",
"sunny",
"rainy",
"rain",
"rainfall",
"drizzle",
"wind",
"windy"
)
# Using the NLTK library to break the user's message...
# ... as this separates words and punctuation, which...
# ... is necessary for comparisons below to work properly.
message_tokens = nltk.word_tokenize(message.content)
for token in message_tokens:
if token.lower() in weather_words:
await check_weather(message, message_tokens)
break
elif "fact" in token.lower():
await tell_fact(message)
break
holder = message.content.split(" ")
for word in holder:
if word in chat_restriction:
await asyncio.sleep(3)
await client.delete_message(message)
await client.send_message(message.channel,"Hey! You are not allowed to say this!")
break
if message.content.startswith('.hello'):
msg = 'Hello {0.author.mention}, Welcome to the server'.format(message)
await client.send_message(message.channel, msg)
if message.content.startswith('.AI'):
msg = 'Artificial Intelligence will take over....'.format(message)
await client.send_message(message.channel, msg)
if message.content.startswith('.music'):
msg = 'Music player is not ready yet'.format(message)
await client.send_message(message.channel, msg)
if message.content.startswith('.command'):
msg = '!AI, !music, !hello, !private, !logoff, !command, !time, .summon, .disconnect, .play, .pause, .resume, .stop, .queue, .info'.format(message)
await client.send_message(message.channel, msg)
if message.content.startswith('.private'):
msg = 'Hello {0.author.mention}, this is a private message'.format(message)
await client.send_message(message.author, msg)
if message.content.startswith('.logoff'):
msg = 'Logging off!'.format(message)
await client.send_message(message.channel, msg)
await client.close()
if message.content.startswith('.time'):
msg = 'The time is '.format(message)
await client.send_message(message.channel, msg+str(message.timestamp))
if "locate" in message_tokens or "location" in message_tokens:
await start_locating(message)
if "find" in message_tokens or "search" in message_tokens:
await start_searching(message, message_tokens)
await client.process_commands(message)
@client.event
async def on_ready():
print("Logged in as " + client.user.name + ", ID: " + client.user.id)
print("----------")
# Random numbers needed for the "tell_fact" coroutine.
random.seed()
# Starting the bot.
client.run(getBotKey())