Interactivity & storage overhaul complete.
Proof-of-concept adding and removing questions complete
This commit is contained in:
parent
6c71c30454
commit
ee0d8c579d
6 changed files with 262 additions and 199 deletions
|
@ -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)
|
||||
|
||||
|
||||
![Adding questions from matrix](screenshots/adding-questions.gif)
|
||||
## Features in the works
|
||||
* Alerts
|
||||
* Dynamic configuration
|
||||
|
|
294
commands.py
294
commands.py
|
@ -1,161 +1,233 @@
|
|||
import random
|
||||
from turtle import update
|
||||
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:
|
||||
if message.find(command)==0:
|
||||
result = commands[command](state,message,sender)
|
||||
if message.find(command) == 0:
|
||||
result = commands[command](state, message, sender)
|
||||
return result
|
||||
if len(state) > 0:
|
||||
if debug == 1:
|
||||
return commands[state[0]](state, message, sender)
|
||||
try:
|
||||
result = commands[state[0]](state,message,sender)
|
||||
result = commands[state[0]](state, message, sender)
|
||||
return result
|
||||
except Exception as e:
|
||||
return {
|
||||
"state":[],
|
||||
"response":f"⚠️Error⚠️ \n {e}"
|
||||
"response": f"⚠️Error⚠️"
|
||||
}
|
||||
|
||||
return {
|
||||
"response":None,
|
||||
"state":state
|
||||
"response": "",
|
||||
}
|
||||
|
||||
"""
|
||||
Knock-knock implimentation
|
||||
|
||||
when you receive message, read state.
|
||||
user: knock knock
|
||||
bot: who's there? -> bot responds and registers state
|
||||
(user.state=[knock-knock])
|
||||
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 get_faq_string():
|
||||
return api.get_faq_string()
|
||||
|
||||
def nevermind(state,message,sender):
|
||||
|
||||
def nevermind(state, message, sender):
|
||||
api.update_user_state(sender, [])
|
||||
return {
|
||||
"response":"starting over.",
|
||||
"state":[]
|
||||
"response": "starting over."
|
||||
}
|
||||
|
||||
def knock_knock(state,message,sender):
|
||||
#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":[]
|
||||
}
|
||||
# faq
|
||||
|
||||
return {
|
||||
"response":"Cleared.",
|
||||
"state":[]
|
||||
}
|
||||
|
||||
"""'joke' command
|
||||
-------------
|
||||
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":[]
|
||||
}
|
||||
|
||||
def get_faq(state, message, sender):
|
||||
if len(state) == 0:
|
||||
return {
|
||||
"response":"Knock knock",
|
||||
"state":["joke"]
|
||||
"response": api.get_faq_string()
|
||||
}
|
||||
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:
|
||||
if message.find("there?") > 0 :
|
||||
#get random joke index
|
||||
state.append(random.randint(0,len(jokes)-1))
|
||||
state.append(message)
|
||||
api.update_user_state(sender, state)
|
||||
return {
|
||||
"response":jokes[state[1]][0],
|
||||
"state":state
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"response":"ask, \"who's there?\"",
|
||||
"state":state
|
||||
'response': 'Please enter the answer to the question.'
|
||||
}
|
||||
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":jokes[state[1]][1],
|
||||
"state":[]
|
||||
'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 {
|
||||
'response': 'Question added.'
|
||||
}
|
||||
elif message == 'restart':
|
||||
api.update_user_state(sender, ['!add question'])
|
||||
return {
|
||||
'response': 'Please enter the question title.'
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"response":"ask, \""+ jokes[state[1]][0] +" who?\"",
|
||||
"state":state
|
||||
'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"
|
||||
}
|
||||
|
||||
return {
|
||||
"response":None,
|
||||
"state":[]
|
||||
'response': None
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
def appointments():
|
||||
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 {
|
||||
"response":None,
|
||||
state:[]
|
||||
'response': None,
|
||||
|
||||
}
|
||||
|
||||
|
||||
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: []
|
||||
}
|
||||
|
||||
|
||||
commands = {
|
||||
"nevermind":nevermind,
|
||||
"knock knock":knock_knock,
|
||||
"joke":joke
|
||||
"nevermind": nevermind,
|
||||
"!faq": get_faq,
|
||||
"!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__':
|
||||
user = "admin"
|
||||
msg = ""
|
||||
debug = True
|
||||
while msg != "exit":
|
||||
msg = input("user:")
|
||||
user = handle_command(user,msg)
|
||||
#print("state:",user)
|
||||
print("bot:"+handle_command(user, msg)['response'])
|
||||
# print("state:",user)
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
"faq": {
|
||||
"header": "# Simple example FAQ",
|
||||
"questions": [{
|
||||
"question": "# What is the meaning of life?",
|
||||
"question": "What is the meaning of life?",
|
||||
"answer": "42.",
|
||||
"key_phrases": ["meaning", "life"]
|
||||
},
|
||||
{
|
||||
"question": "# Who is the coolest pokemon?",
|
||||
"question": "Who is the coolest pokemon?",
|
||||
"answer": "Mewtwo! 🐈",
|
||||
"key_phrases": ["coolest", "pokemon"]
|
||||
},
|
||||
{
|
||||
"question": "# What is the coolest programming language?",
|
||||
"question": "What is the coolest programming language?",
|
||||
"answer": "🐍 python!",
|
||||
"key_phrases": ["coolest", "programming", "language"]
|
||||
}
|
||||
|
|
61
features.py
61
features.py
|
@ -11,20 +11,23 @@ class API:
|
|||
with open('data.json') as f:
|
||||
self.data = json.loads(f.read())
|
||||
# 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):
|
||||
with open('data.json','w') as f:
|
||||
f.write(json.dumps(self.data,indent=2))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
#Administration
|
||||
|
||||
|
||||
def is_admin(self,handle):
|
||||
self.update_data()
|
||||
#return bool
|
||||
pass
|
||||
def add_admin(self,handle):
|
||||
self.update_data()
|
||||
#return bool
|
||||
if self.is_admin(handle):
|
||||
return True
|
||||
|
@ -33,6 +36,7 @@ class API:
|
|||
self.save()
|
||||
return True
|
||||
def remove_admin(self,handle):
|
||||
self.update_data()
|
||||
#return bool
|
||||
if self.is_admin(handle):
|
||||
i = self.data["admins"].index(handle)
|
||||
|
@ -42,38 +46,83 @@ class API:
|
|||
else:
|
||||
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
|
||||
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):
|
||||
self.update_data()
|
||||
return self.data["faq"]["header"]
|
||||
def set_header(self,hdr):
|
||||
self.update_data()
|
||||
#return bool
|
||||
self.data["faq"]["header"] = hdr
|
||||
self.save()
|
||||
return True
|
||||
def get_questions(self):
|
||||
self.update_data()
|
||||
#return questions
|
||||
return self.data["faq"]["questions"]
|
||||
def add_question(self,qtn):
|
||||
self.update_data()
|
||||
#return bool
|
||||
self.data["faq"]["questions"].append(qtn)
|
||||
self.save()
|
||||
return True
|
||||
def remove_question(self,qtn_i):
|
||||
self.update_data()
|
||||
#return bool
|
||||
self.data["faq"]["questions"].pop(qtn_i)
|
||||
self.save()
|
||||
return True
|
||||
def update_question(self,qtn_i,qtn):
|
||||
self.update_data()
|
||||
#return bool
|
||||
self.data["faq"]["questions"][qtn_i]=qtn
|
||||
return True
|
||||
|
||||
#Meetings
|
||||
def get_meetings():
|
||||
def get_meetings(self):
|
||||
self.update_data()
|
||||
pass
|
||||
def request_meeting():
|
||||
def request_meeting(self):
|
||||
self.update_data()
|
||||
pass
|
||||
def accept_meeting():
|
||||
def accept_meeting(self):
|
||||
self.update_data()
|
||||
pass
|
||||
|
||||
|
||||
|
|
68
main.py
68
main.py
|
@ -3,90 +3,30 @@ import nio
|
|||
import simplematrixbotlib as botlib
|
||||
import commands
|
||||
|
||||
#TODO move state functionality entirely to features.py
|
||||
|
||||
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(
|
||||
data["homeserver"],
|
||||
data['username'],
|
||||
data['password']
|
||||
)
|
||||
)
|
||||
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
|
||||
async def faq(room, message):
|
||||
if message.sender not in userstate:
|
||||
userstate[message.sender] = {"state":[]}
|
||||
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()
|
||||
result = commands.handle_command(message.sender, message.body)
|
||||
if result['response'] != None and result['response'] != "":
|
||||
await bot.api.send_markdown_message(
|
||||
room.room_id,
|
||||
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)
|
||||
async def example(room, event):
|
||||
if event.membership == "join":
|
||||
await bot.api.send_markdown_message(
|
||||
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()
|
||||
|
|
BIN
screenshots/adding-questions.gif
Normal file
BIN
screenshots/adding-questions.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 MiB |
Loading…
Reference in a new issue