big changes

This commit is contained in:
Remy Moll 2021-06-29 14:59:37 +02:00
parent ffc903b8f2
commit 21599019fd
25 changed files with 283 additions and 151 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
**/VE
#VS CODE files
.vscode
# Persistence # Persistence
prst.* prst.*

View File

@ -4,8 +4,8 @@ FIRST = range(1)
class Alias(BotFunc): class Alias(BotFunc):
"""create a new command for command-paths you often use""" """create a new command for command-paths you often use"""
def __init__(self, dispatcher, prst): def __init__(self, dispatcher, db):
super().__init__(prst) super().__init__(db)
self.dispatcher = dispatcher self.dispatcher = dispatcher
# do not interact with him yet! # do not interact with him yet!
@ -25,7 +25,6 @@ class Alias(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
test = self.dispatcher test = self.dispatcher
print(self.dispatcher.handlers[0]) print(self.dispatcher.handlers[0])
keyboard = [ keyboard = [
@ -34,6 +33,7 @@ class Alias(BotFunc):
[InlineKeyboardButton("Delete alias", callback_data="delete")], [InlineKeyboardButton("Delete alias", callback_data="delete")],
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
super().log_activity(receive=True, execute=False, send=True)
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup) update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
return FIRST return FIRST

View File

@ -37,7 +37,6 @@ class Clock(BotFunc):
return handler return handler
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
keyboard = [ keyboard = [
[InlineKeyboardButton("Make a wake-light", callback_data="wake")], [InlineKeyboardButton("Make a wake-light", callback_data="wake")],
[InlineKeyboardButton("Blink as alarm", callback_data="alarm")], [InlineKeyboardButton("Blink as alarm", callback_data="alarm")],
@ -209,3 +208,11 @@ class Clock(BotFunc):
update.message.reply_text("Ok. Showing art for the next "+ str(duration) + " hours.") update.message.reply_text("Ok. Showing art for the next "+ str(duration) + " hours.")
self.clock.run(output,(number, duration)) self.clock.run(output,(number, duration))
return ConversationHandler.END return ConversationHandler.END
# TODO FIx this to work with the new backend

View File

@ -6,8 +6,8 @@ FIRST, EXECUTE = range(2)
class Help(BotFunc): class Help(BotFunc):
"""Shows the functions and their usage""" """Shows the functions and their usage"""
def __init__(self, prst): def __init__(self, db):
super().__init__(prst) super().__init__(db)
self.available_commands = {} self.available_commands = {}
@ -39,7 +39,6 @@ class Help(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
keyboard = [ keyboard = [
[ [
InlineKeyboardButton("All commands", callback_data="all"), InlineKeyboardButton("All commands", callback_data="all"),
@ -47,6 +46,7 @@ class Help(BotFunc):
] ]
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
if update.message: if update.message:
update.message.reply_text("What exactly do you want?", reply_markup=reply_markup) update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
else: else:

View File

@ -9,8 +9,8 @@ NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5)
class Lists(BotFunc): class Lists(BotFunc):
"""Create and edit lists""" """Create and edit lists"""
def __init__(self, prst): def __init__(self, db):
super().__init__(prst) super().__init__(db)
self.current_name = "" self.current_name = ""
@ -40,10 +40,12 @@ class Lists(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point() lists = self.db.lists.select()
keyboard = [[InlineKeyboardButton(k, callback_data="list-"+k)] for k in self.persistence["global"]["lists"]] + [[InlineKeyboardButton("New list", callback_data="new")]] sl = [l.name for l in lists]
keyboard = [[InlineKeyboardButton(k, callback_data="list-"+k)] for k in sl] + [[InlineKeyboardButton("New list", callback_data="new")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
super().log_activity(read=True, execute=False, send=True)
update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup) update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup)
return NAME return NAME
@ -94,16 +96,16 @@ class Lists(BotFunc):
def new_listname(self, update: Update, context: CallbackContext) -> None: def new_listname(self, update: Update, context: CallbackContext) -> None:
name = update.message.text name = update.message.text
if name not in self.persistence["global"]["lists"]: try:
self.persistence["global"]["lists"][name] = [] data = self.db.lists(name=name, content="")
data.save()
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
self.current_name = name self.current_name = name
update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup)
return ACTION return ACTION
else: except Exception as e:
update.message.reply_text("Oh no! That list already exists") update.message.reply_text("Oh no! Encountered exception: {}".format(e))
return ConversationHandler.END return ConversationHandler.END
@ -117,7 +119,10 @@ class Lists(BotFunc):
def list_remove(self, update: Update, context: CallbackContext) -> None: def list_remove(self, update: Update, context: CallbackContext) -> None:
query = update.callback_query query = update.callback_query
query.answer() query.answer()
keyboard = [[InlineKeyboardButton(k, callback_data=i)] for i,k in enumerate(self.persistence["global"]["lists"][self.current_name])] it = self.db.lists.get(self.db.lists.name == self.current_name)
sl = it.content.split("<-->")
keyboard = [[InlineKeyboardButton(k, callback_data=i)] for i,k in enumerate(sl)]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup) query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup)
@ -127,7 +132,7 @@ class Lists(BotFunc):
def list_clear(self, update: Update, context: CallbackContext) -> None: def list_clear(self, update: Update, context: CallbackContext) -> None:
query = update.callback_query query = update.callback_query
query.answer() query.answer()
self.persistence["global"]["lists"][self.current_name] = [] self.db.lists.update(content="").where(self.db.lists.name == self.current_name).execute()
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text("List " + self.current_name + " cleared", reply_markup=reply_markup) query.edit_message_text("List " + self.current_name + " cleared", reply_markup=reply_markup)
@ -137,7 +142,7 @@ class Lists(BotFunc):
def list_delete(self, update: Update, context: CallbackContext) -> None: def list_delete(self, update: Update, context: CallbackContext) -> None:
query = update.callback_query query = update.callback_query
query.answer() query.answer()
self.persistence["global"]["lists"].pop(self.current_name, None) self.db.lists.delete().where(self.db.lists.name == self.current_name).execute()
query.edit_message_text("List " + self.current_name + " deleted") query.edit_message_text("List " + self.current_name + " deleted")
return ConversationHandler.END return ConversationHandler.END
@ -145,7 +150,9 @@ class Lists(BotFunc):
def list_print(self, update: Update, context: CallbackContext) -> None: def list_print(self, update: Update, context: CallbackContext) -> None:
query = update.callback_query query = update.callback_query
query.answer() query.answer()
content = "\n".join(self.persistence["global"]["lists"][self.current_name]) it = self.db.lists.get(self.db.lists.name == self.current_name)
content = it.content.split("<-->")
content = "\n".join(content)
keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text("Content of " + self.current_name + ":\n" + content, reply_markup=reply_markup) query.edit_message_text("Content of " + self.current_name + ":\n" + content, reply_markup=reply_markup)
@ -153,11 +160,16 @@ class Lists(BotFunc):
def list_add_item(self, update: Update, context: CallbackContext) -> None: def list_add_item(self, update: Update, context: CallbackContext) -> None:
name = update.message.text item = update.message.text
self.persistence["global"]["lists"][self.current_name] += [name] it = self.db.lists.get(self.db.lists.name == self.current_name)
sl = it.content
print("SLSLLLL: {} -- {}".format(sl, type(sl)))
sl += item + "<-->"
self.db.lists.update(content=sl).where(self.db.lists.name == self.current_name).execute()
keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text("Added " + name, reply_markup=reply_markup) update.message.reply_text("Added " + item, reply_markup=reply_markup)
return ACTION return ACTION
@ -166,9 +178,13 @@ class Lists(BotFunc):
ind = int(query.data) ind = int(query.data)
query.answer() query.answer()
old = self.persistence["global"]["lists"][self.current_name] it = self.db.lists.get(self.db.lists.name == self.current_name)
old = it.content.split("<-->")
# todo make better
name = old.pop(ind) name = old.pop(ind)
self.persistence["global"]["lists"][self.current_name] = old new = "<-->".join(old)
self.db.lists.update(content=new).where(self.db.lists.name == self.current_name).execute()
keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)

View File

@ -3,13 +3,16 @@ from .template import *
class Plain(BotFunc): class Plain(BotFunc):
"""Not a command: just keeps logs and usage_data""" """Not a command: just keeps logs and usage_data"""
def __init__(self, prst): def __init__(self, db):
super().__init__(prst) super().__init__(db)
def create_handler(self): def create_handler(self):
h = MessageHandler(Filters.text, callback=self.add_to_log) h = MessageHandler(Filters.text, callback=self.add_to_log)
return h return h
def add_to_log(self, update: Update, context: CallbackContext) -> None: def add_to_log(self, update: Update, context: CallbackContext) -> None:
super().entry_point() super().log_activity(
super().increase_counter("receive_activity") read = True,
send = False,
execute = False
)

View File

@ -24,9 +24,10 @@ class Joke(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
keyboard = [[InlineKeyboardButton(str(i), callback_data=str(i)) for i in range(1,11)]] keyboard = [[InlineKeyboardButton(str(i), callback_data=str(i)) for i in range(1,11)]]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
update.message.reply_text("How many jokes?", reply_markup=reply_markup) update.message.reply_text("How many jokes?", reply_markup=reply_markup)
return CHOOSE_NUM return CHOOSE_NUM
@ -71,7 +72,6 @@ class Meme(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
keyboard = [ keyboard = [
[InlineKeyboardButton("General", callback_data="memes"),], [InlineKeyboardButton("General", callback_data="memes"),],
@ -81,6 +81,7 @@ class Meme(BotFunc):
[InlineKeyboardButton("Biology", callback_data="biologymemes"),], [InlineKeyboardButton("Biology", callback_data="biologymemes"),],
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled
update.message.reply_text("What kind of memes?", reply_markup=reply_markup) update.message.reply_text("What kind of memes?", reply_markup=reply_markup)
return CHOOSE_TOPIC return CHOOSE_TOPIC
@ -105,6 +106,7 @@ class Meme(BotFunc):
memes = self.api.get_random_rising(data[0], int(data[1]), "photo") memes = self.api.get_random_rising(data[0], int(data[1]), "photo")
if len(memes) != 0: if len(memes) != 0:
for m in memes: for m in memes:
super().log_activity(read=False, execute=False, send=True) # we just sent an additional message
update.effective_chat.send_photo(photo = m["image"],caption = m["caption"]) update.effective_chat.send_photo(photo = m["image"],caption = m["caption"])
else: else:
update.effective_chat.send_message("Sorry, the meme won't yeet.") update.effective_chat.send_message("Sorry, the meme won't yeet.")

View File

@ -5,8 +5,8 @@ SEARCH, MORE = range(2)
class Search(BotFunc): class Search(BotFunc):
"""Browse the web for a topic.""" """Browse the web for a topic."""
def __init__(self, api, prst): def __init__(self, api, db):
super().__init__(prst) super().__init__(db)
self.available_commands = {} self.available_commands = {}
self.api = api self.api = api
@ -25,7 +25,6 @@ class Search(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
update.message.reply_text("What are we searching?") update.message.reply_text("What are we searching?")
return SEARCH return SEARCH
@ -43,6 +42,7 @@ class Search(BotFunc):
message = first["text"] + "\n(" + first["url"] + ")\n\n" message = first["text"] + "\n(" + first["url"] + ")\n\n"
update.message.reply_text(text = message, reply_markup=reply_markup) update.message.reply_text(text = message, reply_markup=reply_markup)
super().log_activity(read = True, execute = True, send = True)
return MORE return MORE
@ -55,4 +55,5 @@ class Search(BotFunc):
message += r["text"] + "\n(" + r["url"] + ")\n\n" message += r["text"] + "\n(" + r["url"] + ")\n\n"
query.edit_message_text(message) query.edit_message_text(message)
super().log_activity(read = False, execute = False, send = True)
return ConversationHandler.END return ConversationHandler.END

View File

@ -13,8 +13,8 @@ FIRST = 1
class Status(BotFunc): class Status(BotFunc):
"""Shows a short status of the program.""" """Shows a short status of the program."""
def __init__(self, name, version, prst): def __init__(self, name, version, db):
super().__init__(prst) super().__init__(db)
self.start_time = datetime.datetime.now() self.start_time = datetime.datetime.now()
self.name = name self.name = name
self.version = version self.version = version
@ -33,7 +33,6 @@ class Status(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
keyboard = [ keyboard = [
[ [
InlineKeyboardButton("And the log?", callback_data="full"), InlineKeyboardButton("And the log?", callback_data="full"),
@ -75,6 +74,8 @@ class Status(BotFunc):
update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN) update.message.reply_text(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
else: else:
update._effective_chat.send_message(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN) update._effective_chat.send_message(message, reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN)
super().log_activity(read = True, execute = True, send = True)
return FIRST return FIRST
@ -85,6 +86,7 @@ class Status(BotFunc):
with open("persistence/complete.log") as l: with open("persistence/complete.log") as l:
query.message.reply_document(l) query.message.reply_document(l)
super().log_activity(read = False, execute = False, send = True)
return ConversationHandler.END return ConversationHandler.END

View File

@ -1,4 +1,5 @@
import logging import logging
import datetime
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters
from telegram.ext import ( from telegram.ext import (
@ -15,19 +16,21 @@ import datetime
class BotFunc(): class BotFunc():
"""Base class for a specific bot-functionality""" """Base class for a specific bot-functionality"""
def __init__(self, prst): def __init__(self, db):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.persistence = prst self.db = db
def entry_point(self): def log_activity(self, **kwargs):
self.increase_counter("execute_activity") # mark that a new command has been executed
data = self.db.chats(
time=datetime.datetime.now(),
**kwargs
)
# kwargs can look like
# receive=True,
# execute=True,
# send=False,
data.save()
def increase_counter(self, counter_name):
current_hour = int(datetime.datetime.now().timestamp() // 3600)
if len(self.persistence["bot"][counter_name]["hour"]) == 0 or current_hour != self.persistence["bot"][counter_name]["hour"][-1]:
self.persistence["bot"][counter_name]["hour"].append(current_hour)
self.persistence["bot"][counter_name]["count"].append(1)
else:
self.persistence["bot"][counter_name]["count"][-1] += 1

View File

@ -6,9 +6,9 @@ FIRST = 1
class Weather(BotFunc): class Weather(BotFunc):
"""Shows a weatherforecast for a given location""" """Shows a weatherforecast for a given location"""
def __init__(self, api, prst): def __init__(self, api, db):
"""initialize api and persistence""" """initialize api and persistence"""
super().__init__(prst) super().__init__(db)
self.api = api self.api = api
self.city = "" self.city = ""
@ -31,7 +31,6 @@ class Weather(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
"""Reacts the call of the command. Prints the first buttons""" """Reacts the call of the command. Prints the first buttons"""
super().entry_point()
keyboard = [ keyboard = [
[ [
InlineKeyboardButton("Zürich", callback_data="city-zurich"), InlineKeyboardButton("Zürich", callback_data="city-zurich"),
@ -78,6 +77,7 @@ class Weather(BotFunc):
text = "Weather: \n\n" + weather, text = "Weather: \n\n" + weather,
parse_mode = ParseMode.HTML parse_mode = ParseMode.HTML
) )
super().log_activity(read = True, execute = True, send = True)
return ConversationHandler.END return ConversationHandler.END

View File

@ -8,8 +8,8 @@ START, DEST = range(2)
class Zvv(BotFunc): class Zvv(BotFunc):
"""Connects to the swiss travel-api to get public transport routes""" """Connects to the swiss travel-api to get public transport routes"""
def __init__(self, prst): def __init__(self, db):
super().__init__(prst) super().__init__(db)
self.start = "" self.start = ""
self.dest = "" self.dest = ""
pass pass
@ -27,7 +27,6 @@ class Zvv(BotFunc):
def entry_point(self, update: Update, context: CallbackContext) -> None: def entry_point(self, update: Update, context: CallbackContext) -> None:
super().entry_point()
update.message.reply_text("What is the start point?") update.message.reply_text("What is the start point?")
return START return START
@ -43,7 +42,8 @@ class Zvv(BotFunc):
loc = update.message.text loc = update.message.text
self.dest = loc self.dest = loc
route = self.get_result() route = self.get_result()
update.message.reply_text("Her are the routes I've got:\n" + route) update.message.reply_text("Here are the routes I've got:\n" + route)
super().log_activity(read=True, execute=True, send=True)
return ConversationHandler.END return ConversationHandler.END

View File

@ -16,7 +16,7 @@ class ChatBot():
-> prst:dict - persistence (overloaded dict that writes to json file) -> prst:dict - persistence (overloaded dict that writes to json file)
-> logger - logging object to unify log messages -> logger - logging object to unify log messages
""" """
# added by the launcher, we have self.modules (dict) and persistence # added by the launcher, we have self.modules (dict) and persistence and db
self.name = name self.name = name
self.version = version self.version = version
@ -36,22 +36,22 @@ class ChatBot():
def add_commands(self): def add_commands(self):
# Mark modules as available # Mark modules as available
prst = self.persistence db = self.db
self.help_module = self.commands.help.Help(prst) self.help_module = self.commands.help.Help(db)
self.sub_modules = { self.sub_modules = {
"weather": self.commands.weather.Weather(self.api_weather, prst), "weather": self.commands.weather.Weather(self.api_weather, db),
"help" : self.help_module, "help" : self.help_module,
"status" : self.commands.status.Status(self.name, self.version, prst), "status" : self.commands.status.Status(self.name, self.version, db),
"zvv" : self.commands.zvv.Zvv(prst), "zvv" : self.commands.zvv.Zvv(db),
"list" : self.commands.lists.Lists(prst), "list" : self.commands.lists.Lists(db),
# "alias" : commands.alias.Alias(self.dispatcher, prst), # "alias" : commands.alias.Alias(self.dispatcher, db),
"joke" : self.commands.reddit.Joke(self.api_reddit, prst), "joke" : self.commands.reddit.Joke(self.api_reddit, db),
"meme" : self.commands.reddit.Meme(self.api_reddit, prst), "meme" : self.commands.reddit.Meme(self.api_reddit, db),
# "news" : self.commands.reddit.News(self.api_reddit, prst), # "news" : self.commands.reddit.News(self.api_reddit, db),
"search" : self.commands.search.Search(self.api_search, prst), "search" : self.commands.search.Search(self.api_search, db),
# ... # ...
"plaintext" : self.commands.plaintext.Plain(prst) # for handling non-command messages that should simply contribute to statistics "plaintext" : self.commands.plaintext.Plain(db) # for handling non-command messages that should simply contribute to statistics
} }
# must be a class that has a method create_handler # must be a class that has a method create_handler
@ -61,7 +61,7 @@ class ChatBot():
self.help_module.add_commands(self.sub_modules) self.help_module.add_commands(self.sub_modules)
def start(self): def start(self):
self.sub_modules = {"clock" : self.commands.clock.Clock(self.persistence, self.modules["clock"], self.api_art)} self.sub_modules = {"clock" : self.commands.clock.Clock(self.db, self.modules["clock"], self.api_art)}
self.add_commands() self.add_commands()
self.telegram.start_polling() self.telegram.start_polling()
# self.telegram.idle() # self.telegram.idle()

View File

@ -15,6 +15,11 @@ class FetchUpdates:
self.last_fetch = {} self.last_fetch = {}
def start(self):
# dummy for errorless launching
pass
def get_updates(self): def get_updates(self):
update_url = "http://" + self.base_url + "/getupdates" update_url = "http://" + self.base_url + "/getupdates"
result = self.call_api(update_url) result = self.call_api(update_url)
@ -30,6 +35,7 @@ class FetchUpdates:
def fetch_data(self): def fetch_data(self):
try:
if self.update_calls == 0: if self.update_calls == 0:
fetch = self.get_last() fetch = self.get_last()
else: else:
@ -38,7 +44,7 @@ class FetchUpdates:
fetch = self.last_fetch fetch = self.last_fetch
else: else:
self.last_fetch = fetch self.last_fetch = fetch
try:
data = fetch["data"] data = fetch["data"]
has_queue = fetch["has_queue"] has_queue = fetch["has_queue"]
except: except:
@ -60,6 +66,6 @@ class FetchUpdates:
if result.pop("status") == "ok": if result.pop("status") == "ok":
ret = result ret = result
except: except:
logger.error("Bad api call for method {}.".format(url[:url.rfind("/")])) logger.error("Bad api call for method {}.".format(url[url.rfind("/"):]))
return ret return ret

View File

@ -73,6 +73,6 @@ class BroadcastUpdates:
"status" : "ok", "status" : "ok",
**kwargs **kwargs
} }
print(ret)
return jsonify(ret) return jsonify(ret)

View File

@ -14,7 +14,7 @@ class ReceiverLauncher(launcher.Launcher):
# active: periodically takes readouts # active: periodically takes readouts
self.clock_hardware_module = c_out.ClockFace() self.clock_hardware_module = c_out.ClockFace()
# active: periodically calls fetcher # active: periodically calls fetcher
self.receive_module = b_in.FetchUpdates(server_ip="192.168.1.110", port="1111") self.receive_module = b_in.FetchUpdates(server_ip="localhost", port="1111")
# passive: fetches data on demand # passive: fetches data on demand
super().__init__( super().__init__(

View File

@ -23,8 +23,6 @@ class ClockBackend:
def clock_loop(self): def clock_loop(self):
print("looping")
t = int(datetime.datetime.now().strftime("%H%M")) t = int(datetime.datetime.now().strftime("%H%M"))
if t % 5 == 0: if t % 5 == 0:

View File

@ -13,7 +13,7 @@ class SensorReadout:
self.sensor_modules = { # we already call them, they are objects and not classes anymore self.sensor_modules = { # we already call them, they are objects and not classes anymore
"temperature" : hardware.sensors.TemperatureModule(), "temperature" : hardware.sensors.TemperatureModule(),
"humidity" : hardware.sensors.HumidityModule(), "humidity" : hardware.sensors.HumidityModule(),
"brightness" : hardware.sensors.BrightnessModule(), "luminosity" : hardware.sensors.BrightnessModule(),
# more to come? # more to come?
} }
@ -21,23 +21,36 @@ class SensorReadout:
helpers.timer.RepeatedTimer(300, self.spread_measure) helpers.timer.RepeatedTimer(300, self.spread_measure)
def spread_measure(self): def spread_measure(self):
results = dict((el,[]) for el in self.sensor_modules.keys()) # create an empty dict with a list for each readout-type measurements = dict((el,[]) for el in self.sensor_modules.keys())
# create an empty dict with a list for each readout-type
for _ in range(5): # number of measures to average out for _ in range(5): # number of measures to average out
for name in self.sensor_modules.keys(): for name in self.sensor_modules.keys():
measure = self.sensor_modules[name].readout() measure = self.sensor_modules[name].readout()
results[name].append(measure) measurements[name].append(measure)
time.sleep(3) time.sleep(3)
self.save_results(results) results = {}
for e in measurements.keys():
lst = measurements[e]
results[e] = int(sum(lst) / len(lst))
self.save_results(**results)
def save_results(self, results): # def save_results(self, results):
current_minute = int(datetime.datetime.now().timestamp() // 60) # current_minute = int(datetime.datetime.now().timestamp() // 60)
self.persistence["clock"]["sensors"]["time"] += [current_minute] # self.persistence["clock"]["sensors"]["time"] += [current_minute]
for name in results.keys(): # for name in results.keys():
keep_value = sum(results[name]) / len(results[name]) # keep_value = sum(results[name]) / len(results[name])
self.persistence["clock"]["sensors"][name] += [keep_value] # self.persistence["clock"]["sensors"][name] += [keep_value]
def save_results(self, **results):
data = self.db.sensors(
time=datetime.datetime.now(),
**results,
)
data.save()

View File

@ -13,21 +13,17 @@ class ClockFace:
"""""" """"""
# added by the launcher, we have self.modules (dict) # added by the launcher, we have self.modules (dict)
self.IO = hardware.led.get_handler() self.IO = hardware.led.get_handler()
self.shape = self.IO.shape # (16,32) for now self.shape = self.IO.shape # (16,32) for now
# TODO handle differently! # TODO handle differently!
self.MOP = helpers.computations.MatrixOperations() self.MOP = helpers.computations.MatrixOperations()
self.kill_output = False
def start(self): def start(self):
helpers.timer.RepeatedTimer(60, self.clock_loop) # helpers.timer.RepeatedTimer(60, self.clock_loop)
# schedule for in 60 seconds # # schedule for in 60 seconds
self.clock_loop() Thread(target = self.clock_loop).start()
# run once now
# TODO start as a thread
# TODO Turn off when button pressed? # TODO Turn off when button pressed?
@ -38,13 +34,18 @@ class ClockFace:
t_minutes = int(datetime.datetime.now().strftime("%H%M")) t_minutes = int(datetime.datetime.now().strftime("%H%M"))
has_queue, data = self.modules["receive"].fetch_data() has_queue, data = self.modules["receive"].fetch_data()
self.set_brightness()
if data == {}: if data == {}:
matrices = self.MOP.get_fallback() matrices = self.MOP.get_fallback()
else: else:
matrices = [np.asarray(d).astype(int) for d in data["matrices"]] matrices = [np.asarray(d).astype(int) for d in data["matrices"]]
if not self.kill_output:
self.IO.put(matrices) self.IO.put(matrices)
else:
z = np.zeros((16,16,3))
self.IO.put([z,z,z])
if has_queue: if has_queue:
tnext = 1 tnext = 1
@ -59,3 +60,14 @@ class ClockFace:
time.sleep(max(delta.total_seconds(), 0)) time.sleep(max(delta.total_seconds(), 0))
self.clock_loop() self.clock_loop()
def set_brightness(self):
"""Kill the brightness at night"""
is_WE = datetime.datetime.now().weekday() > 4
now = int(datetime.datetime.now().strftime("%H%M"))
if (is_WE and (now > 1000 and now < 2200)) or ((not is_WE) and (now > 830 and now < 2130)):
self.kill_output = False
else:
self.kill_output = True

View File

@ -5,17 +5,23 @@ logger = logging.getLogger(__name__)
class TempSim: class TempSim:
"""Simulates a temperature for running on windows""" """Simulates a temperature for running on windows"""
temperature = 23 # return a celsius value temperature = 23 # return a celsius value
humidity = 0.3 humidity = 30
class LightSim:
def input(self, *args):
return 1
class SensorModule: class SensorModule:
def __init__(self): def __init__(self):
logger.info("Using module " + self.__class__.__name__) logger.info("Using module " + self.__class__.__name__)
class LightSim:
def input(self, *args):
return 1
## Real sensors!
try: try:
import board import board
import adafruit_dht import adafruit_dht

View File

@ -1,4 +1,4 @@
from persistence import p_io from persistence import p_io, p_out
import logging import logging
import os import os
@ -24,6 +24,8 @@ class Launcher:
def __init__(self, **modules): def __init__(self, **modules):
"""""" """"""
self.persistence = p_io.PersistentDict("persistence/prst.json") self.persistence = p_io.PersistentDict("persistence/prst.json")
self.db = p_out.DBLogging()
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.logger.info(self.__class__.__name__ + " initialized") self.logger.info(self.__class__.__name__ + " initialized")
@ -42,6 +44,7 @@ class Launcher:
self.logger.info("Starting module "+ module.__class__.__name__) self.logger.info("Starting module "+ module.__class__.__name__)
module.modules = self.modules module.modules = self.modules
module.persistence = self.persistence module.persistence = self.persistence
module.db = self.db
module.start() module.start()

View File

@ -1,15 +1,39 @@
from peewee import * from peewee import *
import datetime
import logging
logger = logging.getLogger(__name__)
from threading import Thread
#db = SqliteDatabase('data.db') from . import keys
db = MySQLDatabase("AIO_sensors", host="192.168.1.101", port=3306, user="pi", passwd="supersecret") dbk = keys.db_keys
# whyyy?
class Metric(Model):
db = PostgresqlDatabase(dbk["name"], user=dbk["username"], password=dbk["password"], host=dbk["url"], port=dbk["port"], autorollback=True)
class DBModel(Model):
# specific to the above DB
class Meta:
database = db
def save(self):
# fail-safe writing of the db-object. Usually threaded because the caller is threaded
try:
# db.connect()
super().save()
# db.close()
except Exception as e:
logger.error("Could not write to db. Dropping content of {}".format(self.__class__.__name__))
print(e)
# db.atomic().rollback()
class Metric(DBModel):
time = DateTimeField() time = DateTimeField()
class Meta:
database = db
class SensorMetric(Metric): class SensorMetric(Metric):
# this is a continuous metric # this is a continuous metric
@ -19,11 +43,16 @@ class SensorMetric(Metric):
class ChatMetric(Metric): class ChatMetric(Metric):
# this gets cumulated over one hour (or one day, or...) read = BooleanField()
activity = CharField() send = BooleanField()
execute = BooleanField()
class ErrorMetric(Metric): class ErrorMetric(Metric):
# same as above # same as above
error = CharField() error = TextField()
class List(DBModel):
name = CharField(unique=True)
content = TextField() # unlimited length, use to serialise list into

View File

@ -1,32 +1,58 @@
from models import db from . import models
from models import *
import datetime as dt
from random import randint
def create_tables(): class DBLogging:
with db: """Create a connection to a remote database and log some quantities that will be visualized otherwhere"""
db.create_tables([SensorMetric, ChatMetric, ErrorMetric]) def __init__(self):
self.db = models.db
self.sensors = models.SensorMetric
self.chats = models.ChatMetric
self.errors = models.ErrorMetric
self.lists = models.List
# self.create_tables()
# def create_tables(self):
# with self.db as db:
# db.create_tables([self.sensors, self.chats, self.errors, self.lists])
create_tables()
# read from json, excel, txt ... whatever
now = dt.datetime.timestamp(dt.datetime.now())
for i in range(1000):
with db:
sensor_data = SensorMetric.create(
time = now + i,
temperature = 23,
humidity = 30 + randint(0,20),
luminosity = 1
)
chat = ChatMetric(
time = now + i,
activity = "Hello world"
)
errors = ErrorMetric(
time = now + i, # writin to the db gets handled through the model directly
error = "Could not load module"
) # create_tables()
# # read from json, excel, txt ... whatever
# now = dt.datetime.timestamp(dt.datetime.now())
# for i in range(1000):
# with db:
# sensor_data = SensorMetric.create(
# time = now + i,
# temperature = 23,
# humidity = 30 + randint(0,20),
# luminosity = 1
# )
# chat = ChatMetric(
# time = now + i,
# activity = "Hello world"
# )
# errors = ErrorMetric(
# time = now + i,
# error = "Could not load module"
# )

BIN
requirements.txt Normal file

Binary file not shown.

View File

@ -2,7 +2,7 @@
from bot import main from bot import main
from clock import c_back from clock import c_back
from broadcast import b_out from broadcast import b_out
from dashboard import d_out # from dashboard import d_out
import launcher import launcher
@ -12,9 +12,9 @@ class BroadcastLauncher(launcher.Launcher):
"""Launcher for all server-side modules. The hard-computations""" """Launcher for all server-side modules. The hard-computations"""
def __init__(self): def __init__(self):
self.bot_module = main.ChatBot(name="Norbit", version="3.0") # ??? self.bot_module = main.ChatBot(name="Norbit", version="4.0a") # ???
self.clock_backend_module = c_back.ClockBackend() # threaded through threading.Timer self.clock_backend_module = c_back.ClockBackend() # threaded through threading.Timer
self.broadcast_module = b_out.BroadcastUpdates(port="1111") # threaded as Thread self.broadcast_module = b_out.BroadcastUpdates(port="1111") # Thread
# self.dashboard_module = d_out.DashBoard(port="80") # ??? threaded as Thread # self.dashboard_module = d_out.DashBoard(port="80") # ??? threaded as Thread
# "sensors" : self.sensors, # "sensors" : self.sensors,