EasyAppointments

This commit is contained in:
Gabriel 2022-09-28 11:02:35 -04:00
parent a89dcede99
commit 337cd46f24
7 changed files with 345 additions and 26 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
data.json data.json
session.txt session.txt
__pycache__ __pycache__
easyappointments-flow.md

View file

@ -1,5 +1,6 @@
import random import random
import features import features
import json
api = features.API() api = features.API()
debug = False debug = False
@ -17,8 +18,11 @@ def handle_command(sender, message):
result = commands[state[0]](state, message, sender) result = commands[state[0]](state, message, sender)
return result return result
except Exception as e: except Exception as e:
if __name__ != '__main__':
state = []
api.update_user_state(sender, state)
return { return {
"response": f"Error⚠" "response": f"Error⚠\n{e}"
} }
return { return {
@ -202,13 +206,202 @@ def meetings(state,message,sender):
'response':api.appointments.list_upcoming_appointments(sender) 'response':api.appointments.list_upcoming_appointments(sender)
} }
def appointments(): def register_customer(state,message,sender):
if api.appointments.is_customer(sender):
return {'response':"Already registered."}
if state == ['!register2']:
return {
'response':'What is your first name?'
}
return { if state == []:
"response": None, state = ['!register']
state: [] api.update_user_state(sender, state)
return {
'response':'What is your first name?'
}
if len(state) == 1:
state.append(message)
api.update_user_state(sender, state)
return {
'response':'What is your last name?'
}
if len(state) == 2:
state.append(message)
api.update_user_state(sender, state)
return {
'response':'What is your e-mail?'
}
if len(state) == 3:
state.append(message)
api.update_user_state(sender, state)
return {
'response':"What is your phone number?"
}
if len(state) == 4:
state.append(message)
api.update_user_state(sender, state)
resp = f"You've entered: `{state[1]} {state[2]} ({state[3]} / {state[4]})`\n**Is this correct?**\nIf so, respond with:Yes\nIf not, to restart respond with :No\nYou can always cancel with:nevermind"
return {
'response':resp
}
if len(state) == 5:
if "yes" in message.lower():
data = {
"firstName":state[1],
"lastName":state[2],
"email":state[3],
"phone":state[4],
"notes":f"matrix:{sender}"
}
if sender == "admin":
dat = json.dumps(data)
print(f"Data:\n{dat}")
if api.appointments.register_customer(data):
if(state[0]=='!register2'):
state = ['!book']
api.update_user_state(sender, state)
return {'response':"Successfully registered.\n\n"+booking(['!book'],'',sender)['response']}
state = []
api.update_user_state(sender, state)
return{'response':"Successfully registered."}
else:
return{'response':"Error, please try again."}
api.update_user_state(sender, [])
return {
'response':''
} }
def select_service(state,message,sender):
if len(state) >= 2:
return select_provider(state, message, sender)
# ['!book','select-service']
if message == "!book":
return {'response':api.select_service()}
else:
try:
state.append(api.appointments.get_services()[int(message)]['id'])
api.update_user_state(sender, state)
return booking(state, "", sender)
except:
return {'response':'Error selecting service\nTo cancel type:nevermind'}
def select_provider(state,message,sender):
if len(state) >= 3:
return select_time(state, message, sender)
if message == "":
return {'response':api.select_provider()}
try:
state.append(api.appointments.get_providers()[int(message)]['id'])
api.update_user_state(sender, state)
return booking(state,"",sender)
except:
return {'response':'Error selecting provider\nTo cancel type:nevermind'}
def select_time(state,message,sender):
if len(state) >= 5:
return booking(state, message, sender)
api.update_data()
# ['!book','select-time']
if len(state) == 3:
msg = api.select_times(state[1],state[2],message)
if msg == False:
return {'response':"Please enter a date you'd like to book (\"YYYY-MM-DD\" format)"}
state.append(message)
api.update_user_state(sender, state)
return {'response':msg}
if len(state) == 4:
times = api.appointments.get_availabilities(state[1], state[2], state[3])
if message in times:
state.append(message)
time = None
try:
time = times[int(message)-1]
state.append(time)
api.update_user_state(sender, state)
return booking(state, message, sender)
except:
return {'response':api.select_times(state[1],state[2],state[3])}
return {'response':error}
def booking(state,message,sender):
if len(state) > 5:
state = []
if state==[]:
state.append("!book")
api.update_user_state(sender, state)
"""
To book an appointment, you need (in order):
* the customer ID (now mapped to mxid) (0?)
* The service id (1)
* the provider id (2)
* the start date & time (3)
"""
#Quick checks
customer = api.appointments.is_customer(sender)
if customer == False:
return register_customer([], "", sender)
#Service check
service = None
if len(api.appointments.services) == 0:
return {'response':"Error: there are no services"}
if len(api.appointments.services) == 1:
service = api.appointments.get_services()[0]
state.append(service)
api.update_user_state(sender, state)
return booking(state, message, sender)
if len(state) >= 2:
service = state[1]
if service == None:
return select_service(state, message, sender)
#Provider check
provider = None
if len(api.appointments.providers) == 0:
return {'response':'error: there are no providers'}
if len(state) >= 3:
provider = state[2]
elif len(api.appointments.providers) == 1:
provider = api.appointments.get_providers()[0]['id']
if service != None:
state.append(provider)
api.update_user_state(sender, state)
if provider == None:
return select_provider(state, message, sender)
time = None
if len(state) == 5:
time = state[4]
if time == None:
return select_time(state, message, sender)
data = {
"start": state[3] + " "+ state[4]+":00",
"customerId":int(customer),
"providerId":int(provider),
"serviceId":int(service)
}
result = api.appointments.register_appointment(data)
if result != False:
state = []
api.update_user_state(sender, state)
return{'response':'Successfully registered appointment!'}
else:
state = []
api.update_user_state(sender, state)
print(result)
return{'response':"Error: please try again"}
return{'response':"Error: please try again"}
def services(state,message,sender):
return {
'response':api.list_services()
}
commands = { commands = {
"nevermind": nevermind, "nevermind": nevermind,
@ -222,7 +415,11 @@ commands = {
"!remove footer": remove_footer, "!remove footer": remove_footer,
"!add admin": add_admin, "!add admin": add_admin,
"!remove admin": remove_admin, "!remove admin": remove_admin,
"meetings":meetings "!meetings":meetings,
"!register":register_customer,
"!register2":register_customer,
"!book":booking,
"!services":services
} }
@ -235,3 +432,4 @@ if __name__ == '__main__':
msg = input("user:") msg = input("user:")
print("bot:"+handle_command(user, msg)['response']) print("bot:"+handle_command(user, msg)['response'])
# print("state:",user) # print("state:",user)

View file

@ -2,6 +2,10 @@
"username": "bot", "username": "bot",
"password": "hunter2", "password": "hunter2",
"homeserver": "http://localhost:8008", "homeserver": "http://localhost:8008",
"easyappointments": {
"token": "secrettoken",
"url": "http://localhost/easyappointments/index.php"
},
"faq": { "faq": {
"header": "# Simple example FAQ", "header": "# Simple example FAQ",
"questions": [{ "questions": [{

View file

@ -4,12 +4,15 @@ class easyappointments:
def __init__(self,token,url): def __init__(self,token,url):
self.token = token self.token = token
self.url = url self.url = url
self.headers = {"Authorization":"Bearer secrettoken"} self.headers = {"Authorization":f"Bearer {token}"}
self.appointments = {} self.appointments = {}
self.services = {} self.services = {}
self.providers = {} self.providers = {}
self.customers = {} self.customers = {}
self.update_data() try:
self.update_data()
except:
print("Failed to connect to easyappointments!")
def get_appointments(self): def get_appointments(self):
data = requests.get(self.url+'/api/v1/appointments',headers=self.headers) data = requests.get(self.url+'/api/v1/appointments',headers=self.headers)
@ -24,6 +27,13 @@ class easyappointments:
data = requests.get(self.url+'/api/v1/customers',headers=self.headers) data = requests.get(self.url+'/api/v1/customers',headers=self.headers)
return data.json() return data.json()
def get_availabilities(self,service,provider,date):
try:
data = requests.get(self.url+f"/api/v1/availabilities?serviceId={service}&providerId={provider}&date={date}",headers=self.headers)
return data.json()
except:
return False
def update_data(self): def update_data(self):
appointments = self.get_appointments() appointments = self.get_appointments()
for a in appointments: for a in appointments:
@ -43,6 +53,7 @@ class easyappointments:
for c in customers: for c in customers:
if mxid in c['notes']: if mxid in c['notes']:
return c['id'] return c['id']
return False return False
def is_provider(self,mxid): def is_provider(self,mxid):
@ -50,7 +61,7 @@ class easyappointments:
for p in providers: for p in providers:
if mxid in p['notes']: if mxid in p['notes']:
return p['id'] return p['id']
return -1
def get_upcoming_appointments(self,mxid): def get_upcoming_appointments(self,mxid):
#get services & customers #get services & customers
id = self.is_provider(mxid) id = self.is_provider(mxid)
@ -64,6 +75,9 @@ class easyappointments:
else: else:
return [] return []
def list_upcoming_appointments(self,mxid): def list_upcoming_appointments(self,mxid):
apts = self.get_upcoming_appointments(mxid) apts = self.get_upcoming_appointments(mxid)
if apts == []: if apts == []:
@ -75,6 +89,8 @@ class easyappointments:
output += f"## {service} with {customer}\nTime:{a['start']}\n" output += f"## {service} with {customer}\nTime:{a['start']}\n"
return output return output
def register_customer(self,customer_data): def register_customer(self,customer_data):
#customer data #customer data
""" """
@ -86,8 +102,50 @@ class easyappointments:
"notes":"matrix:mxid" "notes":"matrix:mxid"
} }
""" """
id = requests.post(self.url+'/api/v1/customers',headers=self.headers,data=json.dumps(customer_data)).json()['id'] try:
return id id = requests.post(self.url+'/api/v1/customers',headers=self.headers,data=json.dumps(customer_data)).json()['id']
self.update_data()
return id
except:
return False
def register_appointment(self,appointment_data):
#appointment data
"""{
"start":"YY-MM-DD HH:MM:SS",
"customerId":9,
"providerId":4,
"serviceId:4
}
"""
print("Registering appointment:")
print(json.dumps(appointment_data))
result = requests.post(
self.url+'/api/v1/appointments',
headers = self.headers,
data=json.dumps(appointment_data)
)
id = result.json()['id']
if id:
return id
else:
return False
if __name__ == '__main__':
cdat = {
"firstName":"Test",
"lastName":"",
"email":"test@testing.xa",
"phone":"888-888-8888",
"notes":"matrix:@lol:testing"
}
adat = {
"start":"2022-09-28 09:00:00",
"customerId":8,
"providerId":4,
"serviceId":2
}
easy = easyappointments('secrettoken', 'http://localhost:8787/easyappointments/index.php')

1
error.log Normal file
View file

@ -0,0 +1 @@
Error:@gabriel:libresolutions.network: !bookError:@gabriel:libresolutions.network: !bookError:@gabriel:libresolutions.network: !register

View file

@ -1,11 +1,18 @@
import json import json
import requests import requests
import easyappointments import easyappointments
import datetime
""" """
Features Features
This is the main API for executing functions with data This is the main API for executing functions with data
""" """
def check_date(date):
try:
datetime.datetime.strptime(date,"%Y-%m-%d")
except:
return False
return True
class API: class API:
def __init__(self): def __init__(self):
@ -16,6 +23,7 @@ class API:
def update_data(self): def update_data(self):
with open('data.json','r') as f: with open('data.json','r') as f:
self.data = json.loads(f.read()) self.data = json.loads(f.read())
def save(self): def save(self):
with open('data.json','w') as f: with open('data.json','w') as f:
f.write(json.dumps(self.data,indent=2)) f.write(json.dumps(self.data,indent=2))
@ -25,11 +33,9 @@ class API:
#Administration #Administration
def is_admin(self,handle): def is_admin(self,handle):
self.update_data()
#return bool #return bool
pass pass
def add_admin(self,handle): def add_admin(self,handle):
self.update_data()
#return bool #return bool
if self.is_admin(handle): if self.is_admin(handle):
return True return True
@ -38,7 +44,6 @@ class API:
self.save() self.save()
return True return True
def remove_admin(self,handle): def remove_admin(self,handle):
self.update_data()
#return bool #return bool
if self.is_admin(handle): if self.is_admin(handle):
i = self.data["admins"].index(handle) i = self.data["admins"].index(handle)
@ -51,7 +56,6 @@ class API:
#state management #state management
def get_user_state(self,handle): def get_user_state(self,handle):
self.update_data()
if handle not in self.data["users"]: if handle not in self.data["users"]:
self.data["users"][handle]={"state":[]} self.data["users"][handle]={"state":[]}
self.save() self.save()
@ -67,7 +71,6 @@ class API:
#FAQ #FAQ
def get_faq_string(self): def get_faq_string(self):
self.update_data()
faq = self.data["faq"] faq = self.data["faq"]
return faq["header"] + "".join( return faq["header"] + "".join(
["\n# "+q["question"]+"\n"+q["answer"]+"\n" for q in faq["questions"]] ["\n# "+q["question"]+"\n"+q["answer"]+"\n" for q in faq["questions"]]
@ -121,6 +124,57 @@ class API:
self.update_data() self.update_data()
self.appointments.list_upcoming_appointments(mxid) self.appointments.list_upcoming_appointments(mxid)
pass pass
def list_services(self):
msg = "## Services:"
for i in self.appointments.services:
service = self.appointments.services[i]
name = service['name']
price = str(service['price']) + service['currency']
description = service['description']
msg += f"\n### {name}\nPrice:{price}\n{description}"
return msg
def select_service(self):
msg = "## Choose a service:"
counter = 1
for i in self.appointments.services:
service = self.appointments.services[i]
name = f"#{counter}: " + service['name']
price = str(service['price']) + service['currency']
description = service['description']
msg += f"\n### {name}\nPrice:{price}\n{description}"
counter += 1
msg += "\nPlease enter the # of the service:"
return msg
def select_provider(self):
msg = "## Choose a provider:"
counter = 1
for p in self.appointments.get_providers():
name = p['firstName']+" "+p['lastName']
msg += f"\n* #{counter}: {name}"
counter += 1
msg += "\nPlease enter the # of the provider"
return msg
def select_times(self,service,provider,date):
data = self.appointments.get_availabilities(service, provider, date)
if data == False or check_date(date) == False:
return False
msg = f"## Choose a time for {date}:\n"
counter = 1
for t in data:
msg += f"{counter}){t} "
counter +=1
msg +="\nPlease enter the # of time"
return msg
def request_meeting(self): def request_meeting(self):
self.update_data() self.update_data()
pass pass
@ -130,5 +184,5 @@ class API:
if __name__ == '__main__': if __name__ == '__main__':
storeAPI = API() api = API()
print("Storage loaded.\n",storeAPI.data['username']) print("Storage loaded.\n",api.data['username'])

17
main.py
View file

@ -13,13 +13,16 @@ bot = botlib.Bot(creds)
@bot.listener.on_message_event @bot.listener.on_message_event
async def message(room, message): async def message(room, message):
result = commands.handle_command(message.sender, message.body) try:
if result['response'] != None and result['response'] != "": result = commands.handle_command(message.sender, message.body)
await bot.api.send_markdown_message( if result['response'] != None and result['response'] != "":
room.room_id, await bot.api.send_markdown_message(
result['response'] room.room_id,
) result['response']
)
except:
with open("error.log",'a') as f:
f.write(f"Error:{message}")
@bot.listener.on_custom_event(nio.InviteMemberEvent) @bot.listener.on_custom_event(nio.InviteMemberEvent)
async def example(room, event): async def example(room, event):