Interactivity & storage overhaul complete.

Proof-of-concept adding and removing questions complete
This commit is contained in:
Gabriel 2022-05-20 06:35:33 -04:00
parent 6c71c30454
commit ee0d8c579d
6 changed files with 262 additions and 199 deletions

View file

@ -26,6 +26,8 @@ You may want to set data.json as a volume to be able to make changes on the fly
![matrix room screenshot](screenshots/example2.png) ![matrix room screenshot](screenshots/example2.png)
![Adding questions from matrix](screenshots/adding-questions.gif)
## Features in the works ## Features in the works
* Alerts * Alerts
* Dynamic configuration * Dynamic configuration

View file

@ -1,161 +1,233 @@
import random import random
from turtle import update
import features import features
jokes = [
["Testing","Testing you!"],
["singing pokemon","Jiggalypuff! 🎙️"]
]
#jokes = []
def handle_command(state,sender,message): api = features.API()
debug = False
def handle_command(sender, message):
state = api.get_user_state(sender)
for command in commands: for command in commands:
if message.find(command)==0: if message.find(command) == 0:
result = commands[command](state,message,sender) result = commands[command](state, message, sender)
return result return result
if len(state) > 0: if len(state) > 0:
if debug == 1:
return commands[state[0]](state, message, sender)
try: try:
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:
return { return {
"state":[], "response": f"Error⚠"
"response":f"Error⚠ \n {e}"
} }
return { return {
"response":None, "response": "",
"state":state
} }
"""
Knock-knock implimentation def get_faq_string():
return api.get_faq_string()
when you receive message, read state.
user: knock knock
bot: who's there? -> bot responds and registers state def nevermind(state, message, sender):
(user.state=[knock-knock]) api.update_user_state(sender, [])
user: {statement}
bot: {statement} who? -> bot responds and stores statement
(user.stage=[knock-knock,{statement}])
user: {punchline}
bot: Nice one! -> bot saves knock-knock joke
"""
def nevermind(state,message,sender):
return { return {
"response":"starting over.", "response": "starting over."
"state":[]
} }
def knock_knock(state,message,sender): # faq
#return {response:"",state:""}
try:
if state[0] != "knock knock":
#clear the state
state = []
except:
pass
if len(state) == 0:
state.append("knock knock")
return {
"response":"Who's there?",
"state": state
}
if len(state) == 1:
state.append(message)
return {
"response":message + " who?",
"state":state
}
if len(state) == 2:
state.append(message)
jokes.append([
state[1],state[2]
])
print("joke registered.")
return {
"response":"Hah! very funny!",
"state":[]
}
return {
"response":"Cleared.",
"state":[]
}
"""'joke' command
------------- def get_faq(state, message, sender):
user:!joke
bot:Knock knock -> bot initializes state
(user.state=[joke])
user: Who's there?
bot: {statement} -> bot responds with chosen joke
(user.state=[joke,chosenjoke])
user: {statement} who?
bot:
"""
def joke(state,message,sender):
if len(jokes) == 0:
return {
"response":"I don't know any jokes...",
"state":[]
}
if len(state) == 0: if len(state) == 0:
return { return {
"response":"Knock knock", "response": api.get_faq_string()
"state":["joke"] }
return {
"response": api.get_faq_string()
}
def add_question(state, message, sender):
if len(state) == 0:
api.update_user_state(sender, ["!add question"])
return {
'response': "Please enter the question title."
} }
elif len(state) == 1: elif len(state) == 1:
if message.find("there?") > 0 : state.append(message)
#get random joke index api.update_user_state(sender, state)
state.append(random.randint(0,len(jokes)-1)) return {
return { 'response': 'Please enter the answer to the question.'
"response":jokes[state[1]][0], }
"state":state
}
else:
return {
"response":"ask, \"who's there?\"",
"state":state
}
elif len(state) == 2: elif len(state) == 2:
if message.find("who?") > 0: state.append(message)
api.update_user_state(sender, state)
state = api.get_user_state(sender)
return {
'response': f"Your question is:\n---\n# {state[1]}\n{state[2]}\n\n---\nIf this is what you want respond with `confirm`\n If you'd like to start over respond with `restart`\n Respond with `nevermind` to cancel entirely"
}
elif len(state) == 3:
if message == 'confirm':
api.add_question({
'question': state[1],
'answer': state[2]
})
api.update_user_state(sender, [])
return { return {
"response":jokes[state[1]][1], 'response': 'Question added.'
"state":[] }
elif message == 'restart':
api.update_user_state(sender, ['!add question'])
return {
'response': 'Please enter the question title.'
} }
else: else:
return { return {
"response":"ask, \""+ jokes[state[1]][0] +" who?\"", 'response': f"Your question is:\n# {state[1]}\n{state[2]}\n\nIf this is what you want respond with `confirm`\n If you'd like to start over respond with `restart`\n Respond with `nevermind` to cancel entirely"
"state":state
} }
return {
'response': None
}
def update_question(state, message, sender):
if len(api.data['faq']['questions']) == 0:
api.update_user_state(sender, [])
return {
'response': "No questions registered."
}
elif len(state)==0:
api.update_user_state(sender,"!update question")
return {
"response":f"Choose a question to update:\n {api.get_faq_questions}"
}
return {
'response': None,
}
def remove_question(state, message, sender):
if len(api.data['faq']['questions']) == 0:
api.update_user_state(sender, [])
return {
'response': "No questions registered."
}
elif len(state)==0:
api.update_user_state(sender,["!remove question"])
return {
"response":f"Choose a question to remove:\n {api.get_faq_questions()}"
}
elif len(state) == 1:
try:
int(message)
except:
return {"response":"Please enter a number."}
state.append(message)
if int(state[1]) < len(api.data['faq']['questions']) and int(state[1]) >=0:
if debug == True:
api.remove_question(int(state[1]))
api.update_user_state(sender,[])
return {
'response':'Question removed'
}
else:
try:
api.update_user_state(sender,[])
api.remove_question(int(state[1]))
return {
'response':'Question removed'
}
except:
pass
api.update_user_state(sender,["!remove question"])
return {
'response':'Please enter the correct question #'
}
return { return {
"response":None, 'response': None,
"state":[]
} }
"""
def update_header(state, message, sender):
return {
'response': None,
}
def remove_header(state, message, sender):
return {
'response': None,
}
def update_footer(state, message, sender):
return {
'response': None,
}
def remove_footer(state, message, sender):
return {
'response': None,
}
def add_admin(state, message, sender):
return {
'response': None,
}
def remove_admin(state, message, sender):
return {
'response': None,
}
def appointments():
return {
"response": None,
state: []
}
"""
def appointments():
return {
"response":None,
state:[]
}
commands = { commands = {
"nevermind":nevermind, "nevermind": nevermind,
"knock knock":knock_knock, "!faq": get_faq,
"joke":joke "!add question": add_question,
"!remove question": remove_question,
"!update question": update_question,
"!update header": update_header,
"!remove header": remove_header,
"!update footer": update_footer,
"!remove footer": remove_footer,
"!add admin": add_admin,
"!remove admin": remove_admin,
} }
if __name__ == '__main__': if __name__ == '__main__':
user = "admin"
msg = "" msg = ""
debug = True
while msg != "exit": while msg != "exit":
msg = input("user:") msg = input("user:")
user = handle_command(user,msg) print("bot:"+handle_command(user, msg)['response'])
#print("state:",user) # print("state:",user)

View file

@ -5,17 +5,17 @@
"faq": { "faq": {
"header": "# Simple example FAQ", "header": "# Simple example FAQ",
"questions": [{ "questions": [{
"question": "# What is the meaning of life?", "question": "What is the meaning of life?",
"answer": "42.", "answer": "42.",
"key_phrases": ["meaning", "life"] "key_phrases": ["meaning", "life"]
}, },
{ {
"question": "# Who is the coolest pokemon?", "question": "Who is the coolest pokemon?",
"answer": "Mewtwo! 🐈", "answer": "Mewtwo! 🐈",
"key_phrases": ["coolest", "pokemon"] "key_phrases": ["coolest", "pokemon"]
}, },
{ {
"question": "# What is the coolest programming language?", "question": "What is the coolest programming language?",
"answer": "🐍 python!", "answer": "🐍 python!",
"key_phrases": ["coolest", "programming", "language"] "key_phrases": ["coolest", "programming", "language"]
} }

View file

@ -11,20 +11,23 @@ class API:
with open('data.json') as f: with open('data.json') as f:
self.data = json.loads(f.read()) self.data = json.loads(f.read())
# Only refresh data when a change is made # Only refresh data when a change is made
def update_data(self):
with open('data.json','r') as f:
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))
return True return True
return False return False
#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
@ -33,6 +36,7 @@ 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)
@ -42,38 +46,83 @@ class API:
else: else:
return True return True
#state management
def get_user_state(self,handle):
self.update_data()
if handle not in self.data["users"]:
self.data["users"][handle]={"state":[]}
self.save()
return self.data["users"][handle]["state"]
def update_user_state(self,handle,state):
self.update_data()
current_state = self.get_user_state(handle)
self.data["users"][handle]["state"] = state
self.save()
return True
#FAQ #FAQ
def get_faq_string(self):
self.update_data()
faq = self.data["faq"]
return faq["header"] + "".join(
["\n# "+q["question"]+"\n"+q["answer"]+"\n" for q in faq["questions"]]
) + "\n"+ faq["footer"]
def get_faq_questions(self):
self.update_data()
questions = self.data['faq']['questions']
response = ""
index = 0
for question in questions:
response += f"[{index}]:{question['question']}\n"
index += 1
return response
def get_header(self): def get_header(self):
self.update_data()
return self.data["faq"]["header"] return self.data["faq"]["header"]
def set_header(self,hdr): def set_header(self,hdr):
self.update_data()
#return bool #return bool
self.data["faq"]["header"] = hdr self.data["faq"]["header"] = hdr
self.save() self.save()
return True return True
def get_questions(self): def get_questions(self):
self.update_data()
#return questions #return questions
return self.data["faq"]["questions"] return self.data["faq"]["questions"]
def add_question(self,qtn): def add_question(self,qtn):
self.update_data()
#return bool #return bool
self.data["faq"]["questions"].append(qtn) self.data["faq"]["questions"].append(qtn)
self.save() self.save()
return True return True
def remove_question(self,qtn_i): def remove_question(self,qtn_i):
self.update_data()
#return bool #return bool
self.data["faq"]["questions"].pop(qtn_i) self.data["faq"]["questions"].pop(qtn_i)
self.save() self.save()
return True return True
def update_question(self,qtn_i,qtn): def update_question(self,qtn_i,qtn):
self.update_data()
#return bool #return bool
self.data["faq"]["questions"][qtn_i]=qtn self.data["faq"]["questions"][qtn_i]=qtn
return True return True
#Meetings #Meetings
def get_meetings(): def get_meetings(self):
self.update_data()
pass pass
def request_meeting(): def request_meeting(self):
self.update_data()
pass pass
def accept_meeting(): def accept_meeting(self):
self.update_data()
pass pass

74
main.py
View file

@ -3,58 +3,22 @@ import nio
import simplematrixbotlib as botlib import simplematrixbotlib as botlib
import commands import commands
#TODO move state functionality entirely to features.py
data = json.loads(open('data.json').read()) data = json.loads(open('data.json').read())
dfaq = data["faq"]
userstate = data['users']
faq_text = dfaq["header"] + "".join(["\n"+q["question"]+"\n"+q["answer"]
for q in dfaq["questions"]]) + "\n\n"+dfaq["footer"]
creds = botlib.Creds( creds = botlib.Creds(
data["homeserver"], data["homeserver"],
data['username'], data['username'],
data['password'] data['password']
) )
bot = botlib.Bot(creds) bot = botlib.Bot(creds)
PREFIX = '!'
def load_faq():
dfaq = json.loads(open('data.json').read())["faq"]
return dfaq["header"] + "".join(["\n"+q["question"]+"\n"+q["answer"] for q in dfaq["questions"]]) + "\n\n"+dfaq["footer"]
def save_userstate():
with open('data.json','w') as f:
f.write(json.dumps(data,indent=4))
@bot.listener.on_message_event @bot.listener.on_message_event
async def faq(room, message): async def faq(room, message):
if message.sender not in userstate: result = commands.handle_command(message.sender, message.body)
userstate[message.sender] = {"state":[]} if result['response'] != None and result['response'] != "":
save_userstate()
state = userstate[message.sender]['state']
result = commands.handle_command(state,message.sender,message.body)
if result['response']!= None:
state = result['state']
userstate[message.sender]['state'] = result['state']
save_userstate()
await bot.api.send_markdown_message( await bot.api.send_markdown_message(
room.room_id, room.room_id,
result['response'] result['response']
) )
match = botlib.MessageMatch(room, message, bot, PREFIX)
#print(f"{message.sender} : {message.body}")
if match.is_not_from_this_bot() and match.prefix() and match.command(
"faq"):
await bot.api.send_markdown_message(
room.room_id,
load_faq())
@bot.listener.on_custom_event(nio.InviteMemberEvent) @bot.listener.on_custom_event(nio.InviteMemberEvent)
@ -62,31 +26,7 @@ async def example(room, event):
if event.membership == "join": if event.membership == "join":
await bot.api.send_markdown_message( await bot.api.send_markdown_message(
room.room_id, room.room_id,
load_faq() commands.get_faq_string()
) )
def get_questions():
return json.loads(open('data.json').read())["faq"]["questions"]
@bot.listener.on_message_event
async def faqresponse(room, message):
match = botlib.MessageMatch(room, message, bot, PREFIX)
if match.is_not_from_this_bot():
response = ""
questions = get_questions()
for q in questions:
if sum([ 1 for kp in q["key_phrases"] if kp in message.body]) == len(q["key_phrases"]):
response += q["question"] + "\n" + q["answer"]+"\n"
if response != "":
await bot.api.send_markdown_message(
room.room_id,
response + dfaq["footer"]
)
bot.run() bot.run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB