Fixed persistence, better bot commands.

Better bot structure
This commit is contained in:
Remy Moll 2020-12-13 12:32:57 +01:00
parent 6f80f26de1
commit 36ce0ee634
9 changed files with 339 additions and 224 deletions

4
.gitignore vendored
View File

@ -1,6 +1,6 @@
# Persistence # Persistence
prst.db.* prst.*
log.txt
# API-Key (keep secret at all times) # API-Key (keep secret at all times)
keys.py keys.py

View File

@ -1,23 +1,21 @@
import emoji import emoji
import requests import requests
import Levenshtein as lev
import datetime import datetime
import bot.api.keys import bot.api.keys
class TelegramIO(): class TelegramIO():
def __init__(self, persistence, commands): def __init__(self, persistence):
"""Inits the Telegram-Interface """Inits the Telegram-Interface
""" """
self.base_url = "https://api.telegram.org/bot" + bot.api.keys.telegram_api + "/" self.base_url = "https://api.telegram.org/bot" + bot.api.keys.telegram_api + "/"
self.persistence = persistence self.persistence = persistence
self.commands = commands
# Dynamic variables for answering # Dynamic variables for answering
self.chat_id = "" self.chat_id = ""
self.offset = 0 self.offset = 0
self.message_id = "" self.message_id = ""
self.message_queue = []
def update_commands(self,commands): def update_commands(self,commands):
@ -35,38 +33,37 @@ class TelegramIO():
try: try:
result = requests.post(update_url,data=data) result = requests.post(update_url,data=data)
result = result.json()["result"] result = result.json()["result"]
self.message_queue = result
except: except:
result = "" result = []
return result return len(result)
def handle_result(self, result): def process_message(self):
"""Inspects the message and reacts accordingly. Can easily be extended""" """Inspects the first message from self.message_queue and reacts accordingly."""
message_data = result[0] message_data = self.message_queue.pop(0)
self.persistence["bot"]["messages_read"] += 1 self.persistence["bot"]["messages_read"] += 1
self.offset = message_data["update_id"] + 1 self.offset = message_data["update_id"] + 1
if "edited_message" in message_data: if "edited_message" in message_data:
return "nothing", "happened" return
message = message_data["message"] message = message_data["message"]
self.message_id = message["message_id"] self.message_id = message["message_id"]
self.chat_id = message["chat"]["id"] self.chat_id = message["chat"]["id"]
author = message["from"] author = message["from"]
chat_members = self.persistence["bot"]["chat_members"] if author["id"] not in self.persistence["bot"]["chat_members"]:
if str(author["id"]) not in chat_members:
name = "" name = ""
if "first_name" in author: if "first_name" in author:
name += author["first_name"] + " " name += author["first_name"] + " "
if "last_name" in author: if "last_name" in author:
name += author["last_name"] name += author["last_name"]
if len(name) == 0: if len(name) == 0:
name += "anonymous" name = "anonymous"
chat_members[author["id"]] = name self.persistence["bot"]["chat_members"][author["id"]] = name
self.persistence["bot"]["chat_members"] = chat_members
self.send_message("Welcome to this chat " + name + "!") self.send_message("Welcome to this chat " + name + "!")
if "text" in message: if "text" in message:
@ -75,45 +72,12 @@ class TelegramIO():
if "entities" in message: if "entities" in message:
for entry in message["entities"]: for entry in message["entities"]:
if entry["type"] == "bot_command": if entry["type"] == "bot_command":
return self.handle_command(message["text"][1:]) return message["text"] #self.handle_command(message["text"][1:])
elif "photo" in message: elif "photo" in message:
print("Photo received, what do I do?") print("Photo received, what do I do?")
return "nothing", "happened" return
def handle_command(self, command):
"""Handles commands and stuff, using a bash-like syntax:
/[command] [argument 1] [argument 2] ...
"""
full = command.split(" ")
command = self.fuzzy_match_command(full[0])
if len(command) != 1:
if command[0] == "EXACT":
self.persistence["bot"]["commands_executed"] += 1
return command[1], full[1:]
else:
send = "Did you mean <code>" + command[1] + "</code>"
for i in range(2,len(command)):
send += " or <code>" + command[1] + "</code>"
send += "?"
self.send_message(send)
else:
self.send_message("Command <code>" + full[0] + "</code> not found. Please try again.")
return "nothing", ["happened"]
def fuzzy_match_command(self, input):
matches = ["not exact"]
for command in self.commands.keys():
if lev.ratio(input.lower(),command) > 0.8:
matches.append(command)
if lev.ratio(input.lower(),command) == 1:
return ["EXACT", command]
return matches
def send_thinking_note(self): def send_thinking_note(self):

138
bot/framework.py Normal file
View File

@ -0,0 +1,138 @@
import datetime
from bot.api import telegram, google, weather, reddit
import Levenshtein as lev
class BotFramework():
"""Main functionality for a bot """
def __init__(self, name, version, prst):
"""Inits the Bot with a few conf. vars
Args: -> name:str - Name of the bot
-> version:str - Version number
-> prst:shelveObj - persistence
"""
self.version = version
self.name = name
# Persistent variable
self.persistence = prst
# Uptime counter
self.start_time = datetime.datetime.now()
self.emoji_dict = {
"a" : ":regional_indicator_symbol_letter_a:",
"b" : ":regional_indicator_symbol_letter_b:",
"c" : ":regional_indicator_symbol_letter_c:",
"d" : ":regional_indicator_symbol_letter_d:",
"e" : ":regional_indicator_symbol_letter_e:",
"f" : ":regional_indicator_symbol_letter_f:",
"g" : ":regional_indicator_symbol_letter_g:",
"h" : ":regional_indicator_symbol_letter_h:",
"i" : ":regional_indicator_symbol_letter_i:",
"j" : ":regional_indicator_symbol_letter_j:",
"k" : ":regional_indicator_symbol_letter_k:",
"l" : ":regional_indicator_symbol_letter_l:",
"m" : ":regional_indicator_symbol_letter_m:",
"n" : ":regional_indicator_symbol_letter_n:",
"o" : ":regional_indicator_symbol_letter_o:",
"p" : ":regional_indicator_symbol_letter_p:",
"q" : ":regional_indicator_symbol_letter_q:",
"r" : ":regional_indicator_symbol_letter_r:",
"s" : ":regional_indicator_symbol_letter_s:",
"t" : ":regional_indicator_symbol_letter_t:",
"u" : ":regional_indicator_symbol_letter_u:",
"v" : ":regional_indicator_symbol_letter_v:",
"w" : ":regional_indicator_symbol_letter_w:",
"x" : ":regional_indicator_symbol_letter_x:",
"y" : ":regional_indicator_symbol_letter_y:",
"z" : ":regional_indicator_symbol_letter_z:",
"0" : ":keycap_digit_zero:",
"1" : ":keycap_digit_one:",
"2" : ":keycap_digit_two:",
"3" : ":keycap_digit_three:",
"4" : ":keycap_digit_four:",
"5" : ":keycap_digit_five:",
"6" : ":keycap_digit_six:",
"7" : ":keycap_digit_seven:",
"8" : ":keycap_digit_eight:",
"9" : ":keycap_digit_nine:",
}
self.telegram = telegram.TelegramIO(self.persistence)
def react_chats(self):
"""Checks unanswered messages and answers them"""
num = self.telegram.fetch_updates()
for i in range(num):
self.react_command()
def react_command(self):
"""Reacts if a new command is present
Returns command, params iff the command is a hardware-one (for the clock), else None"""
message = self.telegram.process_message()
if message == None:
return
message = message[1:] #remove first "/"
tmp = message.split(" ")
cmd = tmp[0]
params = tmp[1:]
def call_command(cmd, par):
result = self.commands[cmd](*par)
# *params means the list is unpacked and handed over as separate arguments.
self.telegram.send_message(result)
if self.is_command(cmd): # first word
call_command(cmd, params)
elif cmd in self.persistence["bot"]["aliases"]:
dealias = self.persistence["bot"]["aliases"][cmd].split(" ") # as a list
new_cmd = dealias[0]
params = dealias[1:] + params
self.telegram.send_message("Substituted <code>" + cmd + "</code> to <code>" + self.persistence["bot"]["aliases"][cmd] + "</code> and got:")
call_command(new_cmd, params)
else:
self.telegram.send_message("Command <code>" + tmp[0] + "</code> not found.")
def is_command(self, input):
"""checks if we have a command. Returns true if yes and False if not
Also sends a mesage if close to an existing command
"""
max_match = 0
command_candidate = ""
for command in self.commands.keys():
match = lev.ratio(input.lower(),command)
if match > 0.7 and match > max_match:
max_match = match
command_candidate = command
if max_match == 1:
return True
if max_match != 0:
self.telegram.send_message("Did you mean <code>" + command_candidate + "</code>")
return False
def emojify_word(self,word):
""""""
string_emoji = ""
for letter in word:
if letter in self.emoji_dict:
string_emoji += self.emoji_dict[letter.lower()]
else:
string_emoji += letter
return string_emoji
def write_bot_log(self, function_name, error_message):
""""""
out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M")
out += " @ " + function_name
out += " --> " + error_message
self.persistence["bot"]["log"] += [out]

View File

@ -1,33 +1,27 @@
# -*- coding: utf-8 -*-
from bot.api import telegram, google, weather, reddit from bot.api import telegram, google, weather, reddit
import datetime
import requests import requests
import time import time
import json import json
import datetime import datetime
import emoji import emoji
class ChatBot(): import bot.framework as FW
class ChatBot(FW.BotFramework):
"""""" """"""
def __init__(self, name, version, prst): def __init__(self, name, version, prst, hw_commands):
"""Inits the Bot with a few conf. vars """Inits the Bot with a few conf. vars
Args: -> name:str - Name of the bot Args: -> name:str - Name of the bot
-> version:str - Version number -> version:str - Version number
-> prst:shelveObj - persistence -> prst:shelveObj - persistence
""" """
super().__init__(name, version, prst)
self.version = version
self.name = name
# Persistent variable
self.persistence = prst
# Uptime counter
self.start_time = datetime.datetime.now()
self.persistence["bot"]["reboots"] += 1
# Available commands. Must be manually updated! # Available commands. Must be manually updated!
self.commands = { self.commands = dict({
"help" : self.bot_show_help, "help" : self.bot_show_help,
"status" : self.bot_print_status, "status" : self.bot_print_status,
"log" : self.bot_print_log, "log" : self.bot_print_log,
@ -43,81 +37,10 @@ class ChatBot():
"meme" : self.bot_send_meme, "meme" : self.bot_send_meme,
"news" : self.bot_send_news, "news" : self.bot_send_news,
"list" : self.bot_list, "list" : self.bot_list,
"alias" : self.bot_save_alias,
}, **hw_commands)
# concat bot_commands + hw-commands
}
self.emoji_dict = {
"a" : ":regional_indicator_symbol_letter_a:",
"b" : ":regional_indicator_symbol_letter_b:",
"c" : ":regional_indicator_symbol_letter_c:",
"d" : ":regional_indicator_symbol_letter_d:",
"e" : ":regional_indicator_symbol_letter_e:",
"f" : ":regional_indicator_symbol_letter_f:",
"g" : ":regional_indicator_symbol_letter_g:",
"h" : ":regional_indicator_symbol_letter_h:",
"i" : ":regional_indicator_symbol_letter_i:",
"j" : ":regional_indicator_symbol_letter_j:",
"k" : ":regional_indicator_symbol_letter_k:",
"l" : ":regional_indicator_symbol_letter_l:",
"m" : ":regional_indicator_symbol_letter_m:",
"n" : ":regional_indicator_symbol_letter_n:",
"o" : ":regional_indicator_symbol_letter_o:",
"p" : ":regional_indicator_symbol_letter_p:",
"q" : ":regional_indicator_symbol_letter_q:",
"r" : ":regional_indicator_symbol_letter_r:",
"s" : ":regional_indicator_symbol_letter_s:",
"t" : ":regional_indicator_symbol_letter_t:",
"u" : ":regional_indicator_symbol_letter_u:",
"v" : ":regional_indicator_symbol_letter_v:",
"w" : ":regional_indicator_symbol_letter_w:",
"x" : ":regional_indicator_symbol_letter_x:",
"y" : ":regional_indicator_symbol_letter_y:",
"z" : ":regional_indicator_symbol_letter_z:",
"0" : ":keycap_digit_zero:",
"1" : ":keycap_digit_one:",
"2" : ":keycap_digit_two:",
"3" : ":keycap_digit_three:",
"4" : ":keycap_digit_four:",
"5" : ":keycap_digit_five:",
"6" : ":keycap_digit_six:",
"7" : ":keycap_digit_seven:",
"8" : ":keycap_digit_eight:",
"9" : ":keycap_digit_nine:",
}
self.telegram = telegram.TelegramIO(self.persistence, self.commands)
def add_commands(self, commands):
"""adds new commands to an existing list"""
self.commands = {**self.commands, **commands}
self.telegram.update_commands(self.commands)
def react_command(self, command, params):
""""""
result = self.commands[command](*params)
#*params means the list is unpacked and handed over as separate arguments.
self.telegram.send_message(result)
def emojify_word(self,word):
""""""
string_emoji = ""
for letter in word:
if letter in self.emoji_dict:
string_emoji += self.emoji_dict[letter.lower()]
else:
string_emoji += letter
return string_emoji
def write_bot_log(self, function_name, error_message):
""""""
out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M")
out += " @ " + function_name
out += " --> " + error_message
self.persistence["bot"]["log"] += [out]
############################################################################ ############################################################################
@ -137,13 +60,14 @@ class ChatBot():
def bot_print_status(self, *args): def bot_print_status(self, *args):
"""Prints the bots current status and relevant information""" """Prints the bots current status and relevant information"""
delta = str(datetime.datetime.now() - self.start_time) delta = str(datetime.datetime.now() - self.start_time)
message = "BeebBop, this is " + self.name + " (V." + self.version + ")\n"
try: try:
ip = requests.get('https://api.ipify.org').text ip = requests.get('https://api.ipify.org').text
except: except:
ip = "not fetchable" ip = "not fetchable"
message = "<pre>Status: Running :green_circle:\n" message += "<pre>Status: Running :green_circle:\n"
message += "Uptime: " + delta[:delta.rfind(".")] + "\n" message += "Uptime: " + delta[:delta.rfind(".")] + "\n"
message += "Reboots: " + str(self.persistence["bot"]["reboots"]) + "\n" message += "Reboots: " + str(self.persistence["global"]["reboots"]) + "\n"
message += "IP-Adress: " + ip + "\n" message += "IP-Adress: " + ip + "\n"
message += "Messages read: " + str(self.persistence["bot"]["messages_read"]) + "\n" message += "Messages read: " + str(self.persistence["bot"]["messages_read"]) + "\n"
message += "Messages sent: " + str(self.persistence["bot"]["messages_sent"]) + "\n" message += "Messages sent: " + str(self.persistence["bot"]["messages_sent"]) + "\n"
@ -217,10 +141,22 @@ class ChatBot():
def bot_show_help(self, *args): def bot_show_help(self, *args):
"""Shows a list of all commands and their description""" """Show a help message.
Usage: help {keyword}
Keywords:
* no kw - list of all commands
* full - all commands and their docstring
* command-name - specific command and its docstring
"""
description = False description = False
if "full" in args: if len(args) > 0:
description = True if args[0] == "full":
description = True
elif args[0] in self.commands:
send_text = "<b>" + args[0] + "</b>\n"
send_text += "<code>" + self.commands[args[0]].__doc__ + "</code>"
return send_text
send_text = "BeebBop, this is " + self.name + " (V." + self.version + ")\n" send_text = "BeebBop, this is " + self.name + " (V." + self.version + ")\n"
send_text += "Here is what I can do up to now: \n" send_text += "Here is what I can do up to now: \n"
@ -236,12 +172,16 @@ class ChatBot():
def bot_print_log(self, *args): def bot_print_log(self, *args):
"""Shows an error-log, mostly of bad api-requests. Usage """Show an error-log, mostly of bad api-requests.
log clear - clears log
log system - shows python output""" Usage: log {keyword}
Keywords:
* clear - clears log
* system - shows python output
"""
if "clear" in args: if "clear" in args:
self.persistence.write("log",[]) self.persistence["bot"]["log"] = []
return "Log cleared" return "Log cleared"
elif "system" in args: elif "system" in args:
path="persistence/log.txt" path="persistence/log.txt"
@ -254,7 +194,7 @@ class ChatBot():
return "could not read File" return "could not read File"
send_text = "" send_text = ""
for event in self.persistence.read("log"): for event in self.persistence["bot"]["log"]:
send_text += event + "\n" send_text += event + "\n"
if send_text == "": if send_text == "":
send_text += "No errors up to now" send_text += "No errors up to now"
@ -262,7 +202,13 @@ class ChatBot():
def bot_show_wikipedia(self, *args): def bot_show_wikipedia(self, *args):
"""Shows the wikipedia entry for a given term""" """Shows the wikipedia entry for a given term
Usage: wikipedia &lt;language&gt; &lt;term&gt;
Keywords:
* language - de, fr, en ...
* term - search term, can consist of multiple words
"""
if len(args) == 0: if len(args) == 0:
return "Please provide the first argument for language (de or fr or en or ...) and then your query" return "Please provide the first argument for language (de or fr or en or ...) and then your query"
args = list(args) args = list(args)
@ -283,7 +229,12 @@ class ChatBot():
def bot_zvv(self, *args): def bot_zvv(self, *args):
"""Uses the swiss travel api to return the best route between a start- and endpoint. """Uses the swiss travel api to return the best route between a start- and endpoint.
usage: 'start' to 'finish'"""
Usage: zvv &lt;start&gt; 'to' &lt;finish&gt;
Keywords:
* start - start point (can be more than 1 word9
* end - end point
"""
if len(args) < 3: if len(args) < 3:
return "Please specify a start- and endpoint as well as a separator (the 'to')" return "Please specify a start- and endpoint as well as a separator (the 'to')"
@ -360,7 +311,12 @@ class ChatBot():
def bot_tell_joke(self, *args): def bot_tell_joke(self, *args):
"""Tells you the top joke on r/jokes""" """Tells you the top joke on r/jokes
Usage: joke {number}
Keywords:
* number - number of jokes
"""
params_sorted = self.match_reddit_params(*args) params_sorted = self.match_reddit_params(*args)
@ -427,20 +383,33 @@ class ChatBot():
def bot_list(self, *args): def bot_list(self, *args):
"""Shows and interacts with a list. Usage """Interacts with a list (like a shopping list eg.)
list &lt;name&gt; &lt;action&gt; {object}
actions are: create, delete, print, clear, add, remove Usage list &lt;name&gt; &lt;action&gt; {object}
example: Keyword:
list new shopping : creates list name shopping * name - name of list
* action - create, delete, all, print, clear, add, remove
* object - might not be needed: index to delete, or item to add
Example usage:
list create shopping : creates list name shopping
list shopping add bread : adds bread to the list list shopping add bread : adds bread to the list
list shopping print list shopping print
list shopping clear list shopping clear
list all
""" """
output = "" output = ""
# args = list(args) # args = list(args)
if len(args) < 2: if len(args) == 0:
return "Missing parameters" return "Missing parameters"
try: try:
if args[0] == "all":
try:
return "Existing lists are: " + list(self.persistence["global"]["lists"].keys()).join(" ")
except:
return "No lists created."
if len(args) < 2:
return "Missing parameters"
if args[0] == "create": if args[0] == "create":
lname = " ".join(args[1:]) lname = " ".join(args[1:])
self.persistence["global"]["lists"][lname] = [] self.persistence["global"]["lists"][lname] = []
@ -454,6 +423,7 @@ class ChatBot():
act = args[1] act = args[1]
if act == "print": if act == "print":
sl = self.persistence["global"]["lists"][lname] sl = self.persistence["global"]["lists"][lname]
output += "Content of " + lname + ":\n"
for ind,thing in enumerate(sl): for ind,thing in enumerate(sl):
output += str(ind+1) + ". " + thing + "\n" output += str(ind+1) + ". " + thing + "\n"
elif act == "clear": elif act == "clear":
@ -474,7 +444,48 @@ class ChatBot():
def bot_save_alias(self, *args): def bot_save_alias(self, *args):
"""Save a shortcut for special commands (+params) """Save a shortcut for special commands (+params)
usage: /alias sa shopping add
Means: /sa will now be treated as input /shopping add"""
return "Does this look finished to you?" Usage: alias &lt;alias-name&gt; {&lt;alias-name&gt; &lt;command&gt;}
Keywords:
* action - all, add, delete or clear (deleta all)
* alias-name - short name
* command - command to be executed, can contain arguments for the command
Example usage:
* alias sa list shopping add
* alias sp list shopping print
Means that '/sa ...' will now be treated as if typed '/list shopping add ...'
"""
# args = list(args)
if len(args) == 0:
return "Missing parameters"
try:
if args[0] == "clear":
self.persistence["bot"]["aliases"] = {}
return "All aliases cleared"
elif args[0] == "all":
try:
output = "Existing aliases are:\n"
for j, k in self.persistence["bot"]["aliases"].items():
output += j + " -&gt; " + k + "\n"
return output
except:
return "No aliases created."
if len(args) < 2:
return "Missing parameters"
if args[0] == "delete":
ak = args[1]
self.persistence["bot"]["aliases"].pop(ak, None) # no error if key doesnt exist
return "Deleted alias " + ak
if len(args) < 3:
return "Missing parameters"
if args[0] == "add":
ak = args[1]
cmd = " ".join(args[2:])
self.persistence["bot"]["aliases"][ak] = cmd
return "Created alias for " + ak
except:
return "Could not handle your request. Maybe check the keys?"
return "Bad input..."

View File

@ -13,6 +13,8 @@ class ClockFace(object):
"""Actual functions one might need for a clock""" """Actual functions one might need for a clock"""
def __init__(self, text_speed=18, prst=""): def __init__(self, text_speed=18, prst=""):
""""""
self.persistence = prst
self.IO = led.OutputHandler(32,16) self.IO = led.OutputHandler(32,16)
self.tspeed = text_speed self.tspeed = text_speed
@ -21,6 +23,12 @@ class ClockFace(object):
self.output_queue = [] self.output_queue = []
# Threads to execute next # Threads to execute next
self.commands = {
"blink" : self.alarm_blink,
"wakeup" : self.wake_light,
"showmessage" : self.show_message,
}
self.weather = "" self.weather = ""
self.brightness_overwrite = {"value" : 1, "duration" : 0} self.brightness_overwrite = {"value" : 1, "duration" : 0}
@ -82,6 +90,11 @@ class ClockFace(object):
############################################################################ ############################################################################
### Higher level commands, accessible from the chat-bot ### Higher level commands, accessible from the chat-bot
def external_action(self, command, params):
""""""
self.commands[command](*params)
def wake_light(self, duration=600): def wake_light(self, duration=600):
"""Simulates a sunris, takes one optional parameter: the duration""" """Simulates a sunris, takes one optional parameter: the duration"""
def output(duration): def output(duration):
@ -99,11 +112,10 @@ class ClockFace(object):
self.IO.set_matrix(ones,colors=[col]) self.IO.set_matrix(ones,colors=[col])
time.sleep(int(duration) / 20) time.sleep(int(duration) / 20)
self.run(output,(duration,)) self.run(output,(duration,))
def alarm_blink(self, duration, frequency): def alarm_blink(self, duration=0, frequency=0):
"""Blinks the whole screen (red-black). Duration in seconds, frequency in Hertz""" """Blinks the whole screen (red-black). Duration in seconds, frequency in Hertz"""
def output(duration, frequency): def output(duration, frequency):
self.set_brightness(value=1) self.set_brightness(value=1)
@ -118,8 +130,8 @@ class ClockFace(object):
time.sleep(1/frequency) time.sleep(1/frequency)
self.IO.set_matrix(empty) self.IO.set_matrix(empty)
time.sleep(1/frequency) time.sleep(1/frequency)
if not(duration == 0 or frequency == 0):
self.run(output,(duration, frequency)) self.run(output,(duration, frequency))
def image_show(self, image, duration): def image_show(self, image, duration):

View File

@ -1,24 +0,0 @@
import wrapper
from threading import Thread
import time
class DashBoardWrapper(wrapper.Wrapper):
def __init__(self, own_module, *other_modules):
""""""
super().__init__(own_module, other_modules)
print("Initializing DASHBOARD-functionality")
# mainloop
def mainloop(self):
super(DashBoardWrapper, self).mainloop(sleep_delta = 3600*3) #3hours refresh-cycle
self.set_weather()
self.set_shopping_list()
self.set_bot_logs()
self.set_joke()
self.bot.refresh()
def set_weather(self):
weather = self.bot.bot_show_weather("zurich")
...
self.own.set_weather(weather)

View File

@ -2,7 +2,6 @@
import bot.main import bot.main
import clock.main import clock.main
import dashboard.main import dashboard.main
# wrapper # wrapper
import wrapper import wrapper
@ -10,17 +9,17 @@ import wrapper
from threading import Thread from threading import Thread
import shelve import shelve
class Launcher(): class Launcher():
"""Launches all other submodules""" """Launches all other submodules"""
def __init__(self): def __init__(self):
"""""" """"""
self.persistence = shelve.open('persistence/prst.db', writeback=True) self.persistence = shelve.DbfilenameShelf("persistence/prst.db", writeback = True)
self.init_persistence() if len(self.persistence) == 0:
# TODO populate the persistence self.init_persistence()
self.bot_module = bot.main.ChatBot(name="ChatterBot", version="2.1", prst=self.persistence) self.persistence["global"]["reboots"] += 1
self.clock_module = clock.main.ClockFace(prst=self.persistence) self.clock_module = clock.main.ClockFace(prst=self.persistence)
self.bot_module = bot.main.ChatBot(name="ChatterBot", version="2.1", prst=self.persistence, hw_commands=self.clock_module.commands)
self.threads = [] self.threads = []
self.threads.append(Thread(target=self.chatbot)) self.threads.append(Thread(target=self.chatbot))
@ -42,6 +41,7 @@ class Launcher():
self.dashboard = wrapper.DashBoardWrapper(self.dashboard_module, self.bot_module) self.dashboard = wrapper.DashBoardWrapper(self.dashboard_module, self.bot_module)
def init_persistence(self): def init_persistence(self):
print("New Persistence created")
self.persistence["bot"] = { self.persistence["bot"] = {
"messages_read": 0, "messages_read": 0,
"messages_sent": 0, "messages_sent": 0,
@ -49,12 +49,13 @@ class Launcher():
"photos_sent": 0, "photos_sent": 0,
"log": [], "log": [],
"chat_members": {}, "chat_members": {},
"reboots": 0 "aliases" : {}
} }
self.persistence["clock"] = {} self.persistence["clock"] = {}
self.persistence["dashboard"] = {} self.persistence["dashboard"] = {}
self.persistence["global"] = { self.persistence["global"] = {
"lists" : {} "lists" : {},
"reboots": 0
} }

1
persistence/__init__.py Normal file
View File

@ -0,0 +1 @@
#placeholder

View File

@ -19,6 +19,8 @@ class Wrapper():
while True: while True:
action() action()
time.sleep(sleep_delta) time.sleep(sleep_delta)
self.own.persistence.sync()
@ -87,34 +89,44 @@ class BotWrapper(Wrapper):
self.bot = own_module self.bot = own_module
self.clock = other_modules[0] self.clock = other_modules[0]
# available hw-commands. Must be updated manually!
self.hw_commands = {
"blink" : self.clock.alarm_blink,
"wakeup" : self.clock.wake_light,
"showmessage" : self.clock.show_message,
}
self.bot.add_commands(self.hw_commands)
self.mainloop(10) self.mainloop(10)
def mainloop(self, sleep_delta): def mainloop(self, sleep_delta):
"""Calls the telegram entity regularly to check for activity""" """Calls the telegram entity regularly to check for activity"""
def perform_loop(): def perform_loop():
result = self.bot.telegram.fetch_updates() self.bot.react_chats()
if len(result) != 0: # num = self.bot.telegram.fetch_updates()
command, params = self.bot.telegram.handle_result(result) # for message in range(num):
if command != "nothing": # command, params = self.bot.react_command() # returns None if handled internally
if command in self.hw_commands: # if command != None:
self.react_hw_command(command,params) # hw-level # self.clock.external_action(command, params)
else: super().mainloop(sleep_delta, perform_loop)
self.bot.react_command(command,params) # sw-level
from threading import Thread
class DashBoardWrapper(Wrapper):
def __init__(self, own_module, *other_modules):
"""Wrapper for the dashboard functionality"""
super().__init__(own_module, other_modules)
# self.mainloop(2 * 3600) # 2 hours refresh-cycle
def mainloop(self, sleep_delta):
def perform_loop():
self.set_weather()
self.set_shopping_list()
self.set_bot_logs()
self.set_joke()
self.bot.refresh()
super().mainloop(sleep_delta, perform_loop) super().mainloop(sleep_delta, perform_loop)
def react_hw_command(self, command, params):
"""""" def set_weather(self):
# so params is a list, and so, to pass the commands, we need to unpack it: weather = self.bot.bot_show_weather("zurich")
self.hw_commands[command](*params) ...
self.own.set_weather(weather)