diff --git a/.gitignore b/.gitignore
index 559e0a0..e6b99fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
# Persistence
-persistent_vars.json
+prst.db.*
# API-Key (keep secret at all times)
keys.py
diff --git a/bot/api/telegram.py b/bot/api/telegram.py
index 88002f4..bb6b96b 100644
--- a/bot/api/telegram.py
+++ b/bot/api/telegram.py
@@ -1,6 +1,7 @@
import emoji
import requests
import Levenshtein as lev
+import datetime
import bot.api.keys
@@ -44,7 +45,7 @@ class TelegramIO():
"""Inspects the message and reacts accordingly. Can easily be extended"""
message_data = result[0]
- self.persistence.increment("messages_read")
+ self.persistence["bot"]["messages_read"] += 1
self.offset = message_data["update_id"] + 1
if "edited_message" in message_data:
@@ -55,7 +56,7 @@ class TelegramIO():
self.chat_id = message["chat"]["id"]
author = message["from"]
- chat_members = self.persistence.read("chat_members")
+ chat_members = self.persistence["bot"]["chat_members"]
if str(author["id"]) not in chat_members:
name = ""
if "first_name" in author:
@@ -65,7 +66,7 @@ class TelegramIO():
if len(name) == 0:
name += "anonymous"
chat_members[author["id"]] = name
- self.persistence.write("chat_members", chat_members)
+ self.persistence["bot"]["chat_members"] = chat_members
self.send_message("Welcome to this chat " + name + "!")
if "text" in message:
@@ -90,7 +91,7 @@ class TelegramIO():
command = self.fuzzy_match_command(full[0])
if len(command) != 1:
if command[0] == "EXACT":
- self.persistence.increment("commands_executed")
+ self.persistence["bot"]["commands_executed"] += 1
return command[1], full[1:]
else:
send = "Did you mean " + command[1] + "
"
@@ -129,11 +130,12 @@ class TelegramIO():
def send_message(self, message):
- if message == "":
+ if message == "" or message == None:
return
- print("SENDING: " + emoji.demojize(message))
-
+ print("SENDING: " + message)
+ # message = message.replace("<","<").replace(">", ">")
+ # TODO: sanitize input but keep relevant tags
data = {
'chat_id': self.chat_id,
'text': emoji.emojize(message),
@@ -144,13 +146,14 @@ class TelegramIO():
send_url = self.base_url + "sendMessage"
try:
r = requests.post(send_url, data=data)
- print(r.status_code)
- self.persistence.increment("messages_sent")
+ if (r.status_code != 200):
+ raise Exception
+ self.persistence["bot"]["messages_sent"]
except:
out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M")
out += " @ " + "telegram.send_message"
out += " --> " + "did not send:\n" + message
- self.persistence.append_list("log", out)
+ self.persistence["bot"]["log"] += [out]
def send_photo(self, url, caption):
@@ -165,9 +168,9 @@ class TelegramIO():
send_url = self.base_url + "sendPhoto"
try:
r = requests.post(send_url, data=data)
- self.persistence.increment("photos_sent")
+ self.persistence["bot"]["photos_sent"] += 1
except:
out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M")
out += " @ " + "telegram.send_photo"
out += " --> " + "did not send:\n" + url
- self.persistence.append_list("log", out)
+ self.persistence["bot"]["log"] += [out]
diff --git a/bot/main.py b/bot/main.py
index cff0519..612e608 100644
--- a/bot/main.py
+++ b/bot/main.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from bot.api import telegram, google, weather, reddit
-from persistence import rw as pvars
import requests
import time
@@ -11,21 +10,21 @@ import emoji
class ChatBot():
""""""
- def __init__(self, name, version):
+ def __init__(self, name, version, prst):
"""Inits the Bot with a few conf. vars
Args: -> name:str - Name of the bot
- -> api_key:str - t.me api-key
-> version:str - Version number
+ -> prst:shelveObj - persistence
"""
self.version = version
self.name = name
# Persistent variable
- self.persistence = pvars.Variables("bot")
+ self.persistence = prst
# Uptime counter
self.start_time = datetime.datetime.now()
- self.persistence.increment("reboots")
+ self.persistence["bot"]["reboots"] += 1
# Available commands. Must be manually updated!
self.commands = {
@@ -43,7 +42,7 @@ class ChatBot():
"joke" : self.bot_tell_joke,
"meme" : self.bot_send_meme,
"news" : self.bot_send_news,
- "shopping" : self.bot_shopping_list,
+ "list" : self.bot_list,
}
@@ -118,7 +117,7 @@ class ChatBot():
out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M")
out += " @ " + function_name
out += " --> " + error_message
- self.persistence.append_list("log", out)
+ self.persistence["bot"]["log"] += [out]
############################################################################
@@ -144,11 +143,11 @@ class ChatBot():
ip = "not fetchable"
message = "
Status: Running :green_circle:\n"
message += "Uptime: " + delta[:delta.rfind(".")] + "\n"
- message += "Reboots: " + str(self.persistence.read("reboots")) + "\n"
+ message += "Reboots: " + str(self.persistence["bot"]["reboots"]) + "\n"
message += "IP-Adress: " + ip + "\n"
- message += "Messages read: " + str(self.persistence.read("messages_read")) + "\n"
- message += "Messages sent: " + str(self.persistence.read("messages_sent")) + "\n"
- message += "Commands executed " + str(self.persistence.read("commands_executed")) + "
"
+ message += "Messages read: " + str(self.persistence["bot"]["messages_read"]) + "\n"
+ message += "Messages sent: " + str(self.persistence["bot"]["messages_sent"]) + "\n"
+ message += "Commands executed " + str(self.persistence["bot"]["commands_executed"]) + ""
return message
@@ -245,9 +244,15 @@ class ChatBot():
self.persistence.write("log",[])
return "Log cleared"
elif "system" in args:
- send_text = self.persistence.read_ext_file("persistence/log.txt")
- return send_text
-
+ path="persistence/log.txt"
+ try:
+ file = open(path,"r")
+ content = file.read()
+ file.close()
+ return content
+ except:
+ return "could not read File"
+
send_text = ""
for event in self.persistence.read("log"):
send_text += event + "\n"
@@ -279,7 +284,7 @@ class ChatBot():
def bot_zvv(self, *args):
"""Uses the swiss travel api to return the best route between a start- and endpoint.
usage: 'start' to 'finish'"""
- if len(args) <= 3:
+ if len(args) < 3:
return "Please specify a start- and endpoint as well as a separator (the 'to')"
url = "http://transport.opendata.ch/v1/connections"
@@ -421,33 +426,49 @@ class ChatBot():
return text
- def bot_shopping_list(self, *args):
- """Shows a shopping list. Usage
- add
- print
- clear
- remove
+ def bot_list(self, *args):
+ """Shows and interacts with a list. Usage
+ list <name> <action> {object}
+ actions are: create, delete, print, clear, add, remove
+ example:
+ list new shopping : creates list name shopping
+ list shopping add bread : adds bread to the list
+ list shopping print
+ list shopping clear
"""
output = ""
# args = list(args)
- if len(args) == 0:
- return "Missing parameter(s)"
-
- if args[0] == "print":
- sl = self.persistence.global_action("read", "shopping_list")
- for ind,thing in enumerate(sl):
- output += str(ind+1) + ". " + thing + "\n"
- elif args[0] == "clear":
- self.persistence.global_action("write", "shopping_list", value=[])
- output = "Cleared list."
- elif args[0] == "add":
- if len(args) == 1:
- output = "Missing parameter"
- add = " ".join(args[1:])
- self.persistence.global_action("append_list", "shopping_list", value=add)
- output = "Added " + add + "."
- elif args[0] == "remove":
- output = "Removed test."
+ if len(args) < 2:
+ return "Missing parameters"
+ try:
+ if args[0] == "create":
+ lname = " ".join(args[1:])
+ self.persistence["global"]["lists"][lname] = []
+ output = "Created list " + lname
+ elif args[0] == "delete":
+ lname = " ".join(args[1:])
+ self.persistence["global"]["lists"].pop(lname, None) # no error if key doesnt exist
+ output = "Deleted list " + lname
+ else:
+ lname = args[0]
+ act = args[1]
+ if act == "print":
+ sl = self.persistence["global"]["lists"][lname]
+ for ind,thing in enumerate(sl):
+ output += str(ind+1) + ". " + thing + "\n"
+ elif act == "clear":
+ self.persistence["global"]["lists"][lname] = []
+ output = "Cleared list " + lname
+ elif act == "add":
+ if len(args) < 3:
+ return "Missing paramaeter"
+ add = " ".join(args[2:])
+ self.persistence["global"]["lists"][lname] += [add]
+ return "Added " + add + "."
+ elif act == "remove":
+ return "Not working yet"
+ except:
+ output = "Could not handle your request. Maybe check the keys?"
return output
diff --git a/bot_wrapper.py b/bot_wrapper.py
deleted file mode 100644
index f2acda9..0000000
--- a/bot_wrapper.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import time
-import datetime
-
-
-class ModuleWrapper():
- """Wrapper for the BOT-functionality"""
- def __init__(self, bot_module, clock_module):
- """"""
- print("Initializing bot-functionality")
- #######################################################################
-
- self.bot = bot_module
- self.clock = clock_module
-
- # 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.message_loop()
-
-
- def message_loop(self):
- """Calls the telegram entity regularly to check for activity"""
- print("Starting bot mainloop")
- while(True):
- result = self.bot.telegram.fetch_updates()
- if len(result) != 0:
- command, params = self.bot.telegram.handle_result(result)
- if command != "nothing":
- if command in self.hw_commands:
- self.react_hw_command(command,params) # hw-level
- else:
- self.bot.react_command(command,params) # sw-level
- time.sleep(5)
-
-
- def react_hw_command(self, command, params):
- """"""
- # so params is a list, and so, to pass the commands, we need to unpack it:
- self.hw_commands[command](*params)
diff --git a/clock/main.py b/clock/main.py
index 47d437e..96f2f91 100644
--- a/clock/main.py
+++ b/clock/main.py
@@ -6,15 +6,13 @@ import numpy
from clock.api import led
-import persistence.rw
-
################################################################################
#start of actual programm.
class ClockFace(object):
"""Actual functions one might need for a clock"""
- def __init__(self, text_speed=18):
+ def __init__(self, text_speed=18, prst=""):
self.IO = led.OutputHandler(32,16)
self.tspeed = text_speed
@@ -136,5 +134,5 @@ class ClockFace(object):
"""Runs a text message over the screen. Obviously needs the text"""
# keep in mind, in this case args is a tuple of all words
message_str = " ".join(args)
- print("SENDING: " + message_str)
+ print("SHOWING (CLOCK): " + message_str)
self.text_scroll(message_str)
diff --git a/clock_wrapper.py b/clock_wrapper.py
deleted file mode 100644
index af81e02..0000000
--- a/clock_wrapper.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import wrapper
-import datetime
-
-
-class ClockWrapper(wrapper.Wrapper):
- """Wrapper for the CLOCK-functionality"""
- def __init__(self, own_module, *other_modules):
- """"""
- super().__init__(own_module, *other_modules)
- print("Initializing clock-functionality")
- self.weather = {"weather":"", "high":"", "low":"", "show":"temps"}
- self.mainloop(15)
-
-
-
- def mainloop(self, sleep_delta):
- """Runs the showing of the clock-face periodically (better way?)"""
- print("Starting clock mainloop")
- self.prev_time = 0
- self.prev_weather_time = datetime.datetime.fromtimestamp(0)
-
- def perform_loop():
- if self.prev_time != datetime.datetime.now().strftime("%H:%M"):
- d = datetime.datetime.now() - self.prev_weather_time
- mins_elapsed = int(d.total_seconds()/60)
-
- if mins_elapsed >= 3*60:
- # fetch new weather every 3 hours (hard coded)
- prev_weather_time = datetime.datetime.now()
- weather = self.others[0].bot_show_weather("zurich")
- if not (":sad:" in weather):
- l1 = weather[weather.find("")+5:weather.find("\n")].replace (":","")
- # current weather situation (icon): we pick the first line, remove the start string, remove :: indicating an emoji
-
- temps_today = weather.splitlines()[4]
- low = temps_today[temps_today.find("button")+8:temps_today.find("°")]
- temps_today = temps_today[temps_today.find("°") + 1:]
- high = temps_today[temps_today.find("button")+8:temps_today.find("°")]
- self.weather["weather"] = l1
- self.weather["high"] = high
- self.weather["low"] = low
- else:
- self.weather["weather"] = "error"
- self.weather["high"] = "error"
- self.weather["low"] = "error"
-
- if mins_elapsed % 5 == 0:
- if self.weather["show"] == "weather":
- next = "temps"
- else:
- next = "weather"
- self.weather["show"] = next
-
- prev_time = datetime.datetime.now().strftime("%H:%M")
-
- self.own.set_face(self.weather)
-
- super().mainloop(sleep_delta,perform_loop)
\ No newline at end of file
diff --git a/launcher.py b/launcher.py
index 3d409c0..fc30143 100644
--- a/launcher.py
+++ b/launcher.py
@@ -4,13 +4,11 @@ import clock.main
import dashboard.main
# wrapper
-import clock_wrapper
-import bot_wrapper
-import dashboard_wrapper
+import wrapper
# misc.
from threading import Thread
-
+import shelve
class Launcher():
@@ -18,8 +16,11 @@ class Launcher():
def __init__(self):
""""""
- self.bot_module = bot.main.ChatBot("ChatterBot", "2.0")
- self.clock_module = clock.main.ClockFace()
+ self.persistence = shelve.open('persistence/prst.db', writeback=True)
+ self.init_persistence()
+ # TODO populate the persistence
+ self.bot_module = bot.main.ChatBot(name="ChatterBot", version="2.1", prst=self.persistence)
+ self.clock_module = clock.main.ClockFace(prst=self.persistence)
self.threads = []
self.threads.append(Thread(target=self.chatbot))
@@ -30,18 +31,32 @@ class Launcher():
def clock(self):
- print("Launching clock-functionality")
- self.clock = clock_wrapper.ClockWrapper(self.clock_module, self.bot_module)
+ self.clock = wrapper.ClockWrapper(self.clock_module, self.bot_module)
def chatbot(self):
- print("Launching bot-functionality")
- self.bot = bot_wrapper.ModuleWrapper(self.bot_module, self.clock_module)
+ self.bot = wrapper.BotWrapper(self.bot_module, self.clock_module)
def dashboard(self):
- print("Launching dashboard-functionality")
- self.dashboard = dashboard_wrapper.DashBoardWrapper(self.dashboard_module, self.bot_module)
+ self.dashboard = wrapper.DashBoardWrapper(self.dashboard_module, self.bot_module)
+
+ def init_persistence(self):
+ self.persistence["bot"] = {
+ "messages_read": 0,
+ "messages_sent": 0,
+ "commands_executed": 0,
+ "photos_sent": 0,
+ "log": [],
+ "chat_members": {},
+ "reboots": 0
+ }
+ self.persistence["clock"] = {}
+ self.persistence["dashboard"] = {}
+ self.persistence["global"] = {
+ "lists" : {}
+ }
+
########################################################################
## Aand liftoff!
diff --git a/persistence/__init__.py b/persistence/__init__.py
deleted file mode 100644
index dcf2c80..0000000
--- a/persistence/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Placeholder
diff --git a/persistence/persistent_init.json b/persistence/persistent_init.json
deleted file mode 100644
index 6987f6c..0000000
--- a/persistence/persistent_init.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "bot" : {
- "messages_read": 0,
- "messages_sent": 0,
- "commands_executed": 0,
- "photos_sent": 0,
- "log": [],
- "chat_members": {},
- "reboots": 0
- },
-
- "clock" : {
- "test":""
- },
- "dashboard" : {
- "test":""
- },
- "global" : {
- "shopping_list" : []
- }
-}
diff --git a/persistence/rw.py b/persistence/rw.py
deleted file mode 100644
index e658ea5..0000000
--- a/persistence/rw.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import json
-import time
-import os
-import shutil
-
-class Variables():
- """"""
-
- def __init__(self, module_name, file_name="persistence/persistent_vars.json", init_name="persistence/persistent_init.json", ):
- self.path = file_name
- self.init_path = init_name
-
- self.module = module_name
-
- self.last_action = ""
- # last performed action, if only reads are made, then the underlying var has not been changed
- # and doesn't need to be read again
- self.savefile = {}
-
- if not os.path.exists(self.path):
- shutil.copy(self.init_path, self.path)
-
-
- def global_action(self, action, name, value=""):
- old = self.module
- self.module = "global"
- action = getattr(self, action)
- if value != "":
- ret = action(name,value)
- else:
- ret = action(name)
- self.module = old
- return ret
-
- def write(self, name, value):
- pre = self.read("")
- pre[self.module][name] = value
- try:
- file = open(self.path,"w")
- json.dump(pre, file)
- file.close()
- self.last_action = "write"
- except:
- print("Config not written - critical")
-
-
- def read(self, name):
- if self.last_action == "read":
- vars = self.savefile
- else:
- file = open(self.path,"r")
- vars = json.load(file)
- file.close()
- self.savefile = vars
- self.last_action = "read"
-
- if name != "":
- vars = vars[self.module][name]
- return vars
-
-
- def increment(self, name, inc=1):
- pre = self.read(name)
- if pre:
- self.write(name, pre + inc)
- else:
- self.write(name, inc)
-
-
- def append_list(self, name, value):
- pre = self.read(name)
- pre.append(value)
- self.write(name, pre)
-
-
- def read_ext_file(self, path):
- """returns content of given file"""
- if not os.path.exists(path):
- return "File does not exist"
- file = open(path,"r")
- content = file.read()
- file.close()
- return content
\ No newline at end of file
diff --git a/wrapper.py b/wrapper.py
index 7692c9d..1cda615 100644
--- a/wrapper.py
+++ b/wrapper.py
@@ -1,4 +1,6 @@
import time
+import datetime
+
class Wrapper():
"""Wrapper skeleton for the modules (bot, clock dashboard...)"""
@@ -6,12 +8,113 @@ class Wrapper():
def __init__(self, own_module, *other_modules):
self.own = own_module
self.others = other_modules
+ print("Starting " + self.__class__.__name__ + " functionality")
def mainloop(self, sleep_delta, action):
"""sleep_delta in seconds sets the sleep period of the loop
action is a function that is performed every * seconds"""
+ print("Launching " + self.__class__.__name__ + " mainloop")
while True:
action()
time.sleep(sleep_delta)
+
+
+
+class ClockWrapper(Wrapper):
+ """Wrapper for the CLOCK-functionality"""
+ def __init__(self, own_module, *other_modules):
+ """"""
+ super().__init__(own_module, *other_modules)
+ self.weather = {"weather":"", "high":"", "low":"", "show":"temps"}
+ self.mainloop(15)
+
+
+
+ def mainloop(self, sleep_delta):
+ """Runs the showing of the clock-face periodically (better way?)"""
+
+ self.prev_time = 0
+ self.prev_weather_time = datetime.datetime.fromtimestamp(0)
+
+ def perform_loop():
+ if self.prev_time != datetime.datetime.now().strftime("%H:%M"):
+ d = datetime.datetime.now() - self.prev_weather_time
+ mins_elapsed = int(d.total_seconds()/60)
+
+ if mins_elapsed >= 3*60:
+ # fetch new weather every 3 hours (hard coded)
+ self.prev_weather_time = datetime.datetime.now()
+ weather = self.others[0].bot_show_weather("zurich")
+ if not (":sad:" in weather):
+ l1 = weather[weather.find("")+5:weather.find("\n")].replace (":","")
+ # current weather situation (icon): we pick the first line, remove the start string, remove :: indicating an emoji
+
+ temps_today = weather.splitlines()[4]
+ low = temps_today[temps_today.find("button")+8:temps_today.find("°")]
+ temps_today = temps_today[temps_today.find("°") + 1:]
+ high = temps_today[temps_today.find("button")+8:temps_today.find("°")]
+ self.weather["weather"] = l1
+ self.weather["high"] = high
+ self.weather["low"] = low
+ else:
+ self.weather["weather"] = "error"
+ self.weather["high"] = "error"
+ self.weather["low"] = "error"
+
+ if mins_elapsed % 5 == 0:
+ if self.weather["show"] == "weather":
+ next = "temps"
+ else:
+ next = "weather"
+ self.weather["show"] = next
+
+ self.prev_time = datetime.datetime.now().strftime("%H:%M")
+
+ self.own.set_face(self.weather)
+
+ super().mainloop(sleep_delta,perform_loop)
+
+
+
+class BotWrapper(Wrapper):
+ """Wrapper for the BOT-functionality"""
+ def __init__(self, own_module, *other_modules):
+ """"""
+ super().__init__(own_module, *other_modules)
+
+ self.bot = own_module
+ 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)
+
+
+ def mainloop(self, sleep_delta):
+ """Calls the telegram entity regularly to check for activity"""
+ def perform_loop():
+ result = self.bot.telegram.fetch_updates()
+ if len(result) != 0:
+ command, params = self.bot.telegram.handle_result(result)
+ if command != "nothing":
+ if command in self.hw_commands:
+ self.react_hw_command(command,params) # hw-level
+ else:
+ self.bot.react_command(command,params) # sw-level
+
+ super().mainloop(sleep_delta, perform_loop)
+
+ def react_hw_command(self, command, params):
+ """"""
+ # so params is a list, and so, to pass the commands, we need to unpack it:
+ self.hw_commands[command](*params)