big changes
This commit is contained in:
		
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,8 @@ | ||||
| **/VE | ||||
|  | ||||
| #VS CODE files | ||||
| .vscode | ||||
|  | ||||
| # Persistence | ||||
| prst.* | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,8 @@ FIRST = range(1) | ||||
| class Alias(BotFunc): | ||||
|     """create a new command for command-paths you often use""" | ||||
|  | ||||
|     def __init__(self, dispatcher, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, dispatcher, db): | ||||
|         super().__init__(db) | ||||
|         self.dispatcher = dispatcher | ||||
|         # do not interact with him yet! | ||||
|  | ||||
| @@ -25,7 +25,6 @@ class Alias(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         test = self.dispatcher | ||||
|         print(self.dispatcher.handlers[0]) | ||||
|         keyboard = [ | ||||
| @@ -34,6 +33,7 @@ class Alias(BotFunc): | ||||
|             [InlineKeyboardButton("Delete alias", callback_data="delete")], | ||||
|         ] | ||||
|         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) | ||||
|         return FIRST | ||||
|      | ||||
|   | ||||
| @@ -37,7 +37,6 @@ class Clock(BotFunc): | ||||
|         return handler | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         keyboard = [ | ||||
|             [InlineKeyboardButton("Make a wake-light", callback_data="wake")], | ||||
|             [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.") | ||||
|         self.clock.run(output,(number, duration)) | ||||
|         return ConversationHandler.END | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # TODO FIx this to work with the new backend | ||||
| @@ -6,8 +6,8 @@ FIRST, EXECUTE = range(2) | ||||
| class Help(BotFunc): | ||||
|     """Shows the functions and their usage""" | ||||
|      | ||||
|     def __init__(self, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, db): | ||||
|         super().__init__(db) | ||||
|         self.available_commands = {} | ||||
|  | ||||
|  | ||||
| @@ -39,7 +39,6 @@ class Help(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         keyboard = [ | ||||
|             [ | ||||
|                 InlineKeyboardButton("All commands", callback_data="all"), | ||||
| @@ -47,6 +46,7 @@ class Help(BotFunc): | ||||
|             ] | ||||
|         ] | ||||
|         reply_markup = InlineKeyboardMarkup(keyboard) | ||||
|         super().log_activity(read=True, execute=True, send=True) # at this point every step has been fulfilled | ||||
|         if update.message: | ||||
|             update.message.reply_text("What exactly do you want?", reply_markup=reply_markup) | ||||
|         else: | ||||
|   | ||||
| @@ -9,8 +9,8 @@ NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5) | ||||
| class Lists(BotFunc): | ||||
|     """Create and edit lists""" | ||||
|  | ||||
|     def __init__(self, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, db): | ||||
|         super().__init__(db) | ||||
|         self.current_name = "" | ||||
|  | ||||
|  | ||||
| @@ -40,10 +40,12 @@ class Lists(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         keyboard = [[InlineKeyboardButton(k, callback_data="list-"+k)] for k in self.persistence["global"]["lists"]] + [[InlineKeyboardButton("New list", callback_data="new")]] | ||||
|         lists = self.db.lists.select() | ||||
|         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) | ||||
|         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) | ||||
|         return NAME | ||||
|  | ||||
| @@ -94,16 +96,16 @@ class Lists(BotFunc): | ||||
|  | ||||
|     def new_listname(self, update: Update, context: CallbackContext) -> None: | ||||
|         name = update.message.text | ||||
|         if name not in self.persistence["global"]["lists"]: | ||||
|             self.persistence["global"]["lists"][name] = [] | ||||
|  | ||||
|         try: | ||||
|             data = self.db.lists(name=name, content="") | ||||
|             data.save() | ||||
|             keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] | ||||
|             reply_markup = InlineKeyboardMarkup(keyboard) | ||||
|             self.current_name = name | ||||
|             update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) | ||||
|             return ACTION | ||||
|         else: | ||||
|             update.message.reply_text("Oh no! That list already exists") | ||||
|         except Exception as e: | ||||
|             update.message.reply_text("Oh no! Encountered exception: {}".format(e)) | ||||
|             return ConversationHandler.END | ||||
|  | ||||
|      | ||||
| @@ -117,7 +119,10 @@ class Lists(BotFunc): | ||||
|     def list_remove(self, update: Update, context: CallbackContext) -> None: | ||||
|         query = update.callback_query | ||||
|         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) | ||||
|  | ||||
|         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: | ||||
|         query = update.callback_query | ||||
|         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")]] | ||||
|         reply_markup = InlineKeyboardMarkup(keyboard) | ||||
|         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: | ||||
|         query = update.callback_query | ||||
|         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") | ||||
|         return ConversationHandler.END | ||||
|  | ||||
| @@ -145,7 +150,9 @@ class Lists(BotFunc): | ||||
|     def list_print(self, update: Update, context: CallbackContext) -> None: | ||||
|         query = update.callback_query | ||||
|         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")]] | ||||
|         reply_markup = InlineKeyboardMarkup(keyboard) | ||||
|         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: | ||||
|         name = update.message.text | ||||
|         self.persistence["global"]["lists"][self.current_name] += [name] | ||||
|         item = update.message.text | ||||
|         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")]] | ||||
|         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 | ||||
|  | ||||
|  | ||||
| @@ -166,9 +178,13 @@ class Lists(BotFunc): | ||||
|         ind = int(query.data) | ||||
|         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) | ||||
|         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")]] | ||||
|         reply_markup = InlineKeyboardMarkup(keyboard)       | ||||
|   | ||||
| @@ -3,13 +3,16 @@ from .template import * | ||||
|  | ||||
| class Plain(BotFunc): | ||||
|     """Not a command: just keeps logs and usage_data""" | ||||
|     def __init__(self, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, db): | ||||
|         super().__init__(db) | ||||
|      | ||||
|     def create_handler(self): | ||||
|         h = MessageHandler(Filters.text, callback=self.add_to_log) | ||||
|         return h | ||||
|  | ||||
|     def add_to_log(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         super().increase_counter("receive_activity") | ||||
|         super().log_activity( | ||||
|             read = True, | ||||
|             send = False, | ||||
|             execute = False | ||||
|         ) | ||||
|   | ||||
| @@ -24,9 +24,10 @@ class Joke(BotFunc): | ||||
|  | ||||
|  | ||||
|     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)]] | ||||
|         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) | ||||
|         return CHOOSE_NUM | ||||
|  | ||||
| @@ -71,7 +72,6 @@ class Meme(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|  | ||||
|         keyboard = [ | ||||
|             [InlineKeyboardButton("General", callback_data="memes"),], | ||||
| @@ -81,6 +81,7 @@ class Meme(BotFunc): | ||||
|             [InlineKeyboardButton("Biology", callback_data="biologymemes"),], | ||||
|         ] | ||||
|         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) | ||||
|         return CHOOSE_TOPIC | ||||
|  | ||||
| @@ -105,6 +106,7 @@ class Meme(BotFunc): | ||||
|         memes = self.api.get_random_rising(data[0], int(data[1]), "photo") | ||||
|         if len(memes) != 0: | ||||
|             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"]) | ||||
|         else: | ||||
|            update.effective_chat.send_message("Sorry, the meme won't yeet.") | ||||
|   | ||||
| @@ -5,8 +5,8 @@ SEARCH, MORE = range(2) | ||||
| class Search(BotFunc): | ||||
|     """Browse the web for a topic.""" | ||||
|      | ||||
|     def __init__(self, api, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, api, db): | ||||
|         super().__init__(db) | ||||
|         self.available_commands = {} | ||||
|         self.api = api | ||||
|  | ||||
| @@ -25,7 +25,6 @@ class Search(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|  | ||||
|         update.message.reply_text("What are we searching?") | ||||
|         return SEARCH | ||||
| @@ -43,6 +42,7 @@ class Search(BotFunc): | ||||
|         message = first["text"] + "\n(" + first["url"] + ")\n\n" | ||||
|  | ||||
|         update.message.reply_text(text = message, reply_markup=reply_markup) | ||||
|         super().log_activity(read = True, execute = True, send = True) | ||||
|         return MORE | ||||
|      | ||||
|  | ||||
| @@ -55,4 +55,5 @@ class Search(BotFunc): | ||||
|             message += r["text"] + "\n(" + r["url"] + ")\n\n" | ||||
|  | ||||
|         query.edit_message_text(message) | ||||
|         super().log_activity(read = False, execute = False, send = True) | ||||
|         return ConversationHandler.END | ||||
| @@ -13,8 +13,8 @@ FIRST = 1 | ||||
| class Status(BotFunc): | ||||
|     """Shows a short status of the program.""" | ||||
|      | ||||
|     def __init__(self, name, version, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, name, version, db): | ||||
|         super().__init__(db) | ||||
|         self.start_time = datetime.datetime.now() | ||||
|         self.name = name | ||||
|         self.version = version | ||||
| @@ -33,7 +33,6 @@ class Status(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         keyboard = [ | ||||
|             [ | ||||
|                 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) | ||||
|         else: | ||||
|             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 | ||||
|  | ||||
|  | ||||
| @@ -85,6 +86,7 @@ class Status(BotFunc): | ||||
|         with open("persistence/complete.log") as l: | ||||
|             query.message.reply_document(l) | ||||
|  | ||||
|         super().log_activity(read = False, execute = False, send = True) | ||||
|         return ConversationHandler.END | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import logging | ||||
| import datetime | ||||
| from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode | ||||
| from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters | ||||
| from telegram.ext import ( | ||||
| @@ -15,19 +16,21 @@ import datetime | ||||
|  | ||||
| class BotFunc(): | ||||
|     """Base class for a specific bot-functionality""" | ||||
|     def __init__(self, prst): | ||||
|     def __init__(self, db): | ||||
|         self.logger = logging.getLogger(__name__) | ||||
|         self.persistence = prst | ||||
|         self.db = db | ||||
|  | ||||
|  | ||||
|     def entry_point(self): | ||||
|         self.increase_counter("execute_activity") | ||||
|     def log_activity(self, **kwargs): | ||||
|         # 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 | ||||
| @@ -6,9 +6,9 @@ FIRST = 1 | ||||
|  | ||||
| class Weather(BotFunc): | ||||
|     """Shows a weatherforecast for a given location""" | ||||
|     def __init__(self, api, prst): | ||||
|     def __init__(self, api, db): | ||||
|         """initialize api and persistence""" | ||||
|         super().__init__(prst) | ||||
|         super().__init__(db) | ||||
|         self.api = api | ||||
|         self.city = "" | ||||
|  | ||||
| @@ -31,7 +31,6 @@ class Weather(BotFunc): | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         """Reacts the call of the command. Prints the first buttons""" | ||||
|         super().entry_point() | ||||
|         keyboard = [ | ||||
|             [ | ||||
|                 InlineKeyboardButton("Zürich", callback_data="city-zurich"), | ||||
| @@ -78,6 +77,7 @@ class Weather(BotFunc): | ||||
|             text = "Weather: \n\n" + weather, | ||||
|             parse_mode = ParseMode.HTML | ||||
|         ) | ||||
|         super().log_activity(read = True, execute = True, send = True) | ||||
|         return ConversationHandler.END | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,8 @@ START, DEST = range(2) | ||||
| class Zvv(BotFunc): | ||||
|     """Connects to the swiss travel-api to get public transport routes""" | ||||
|  | ||||
|     def __init__(self, prst): | ||||
|         super().__init__(prst) | ||||
|     def __init__(self, db): | ||||
|         super().__init__(db) | ||||
|         self.start = "" | ||||
|         self.dest = "" | ||||
|         pass | ||||
| @@ -27,7 +27,6 @@ class Zvv(BotFunc): | ||||
|  | ||||
|  | ||||
|     def entry_point(self, update: Update, context: CallbackContext) -> None: | ||||
|         super().entry_point() | ||||
|         update.message.reply_text("What is the start point?") | ||||
|         return START | ||||
|  | ||||
| @@ -43,7 +42,8 @@ class Zvv(BotFunc): | ||||
|         loc = update.message.text | ||||
|         self.dest = loc | ||||
|         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 | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								bot/main.py
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								bot/main.py
									
									
									
									
									
								
							| @@ -16,7 +16,7 @@ class ChatBot(): | ||||
|                 -> prst:dict - persistence (overloaded dict that writes to json file) | ||||
|                 -> 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.version = version | ||||
| @@ -36,22 +36,22 @@ class ChatBot(): | ||||
|          | ||||
|     def add_commands(self): | ||||
|         # Mark modules as available | ||||
|         prst = self.persistence | ||||
|         self.help_module = self.commands.help.Help(prst) | ||||
|         db = self.db | ||||
|         self.help_module = self.commands.help.Help(db) | ||||
|         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, | ||||
|             "status" : self.commands.status.Status(self.name, self.version, prst), | ||||
|             "zvv" : self.commands.zvv.Zvv(prst), | ||||
|             "list" : self.commands.lists.Lists(prst), | ||||
|             # "alias" : commands.alias.Alias(self.dispatcher, prst), | ||||
|             "joke" : self.commands.reddit.Joke(self.api_reddit, prst), | ||||
|             "meme" : self.commands.reddit.Meme(self.api_reddit, prst), | ||||
|             # "news" : self.commands.reddit.News(self.api_reddit, prst), | ||||
|             "search" : self.commands.search.Search(self.api_search, prst), | ||||
|             "status" : self.commands.status.Status(self.name, self.version, db), | ||||
|             "zvv" : self.commands.zvv.Zvv(db), | ||||
|             "list" : self.commands.lists.Lists(db), | ||||
|             # "alias" : commands.alias.Alias(self.dispatcher, db), | ||||
|             "joke" : self.commands.reddit.Joke(self.api_reddit, db), | ||||
|             "meme" : self.commands.reddit.Meme(self.api_reddit, db), | ||||
|             # "news" : self.commands.reddit.News(self.api_reddit, db), | ||||
|             "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 | ||||
|      | ||||
| @@ -61,7 +61,7 @@ class ChatBot(): | ||||
|         self.help_module.add_commands(self.sub_modules) | ||||
|     	 | ||||
|     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.telegram.start_polling() | ||||
|         # self.telegram.idle() | ||||
|   | ||||
| @@ -15,6 +15,11 @@ class FetchUpdates: | ||||
|         self.last_fetch = {} | ||||
|  | ||||
|  | ||||
|     def start(self): | ||||
|         # dummy for errorless launching | ||||
|         pass | ||||
|      | ||||
|  | ||||
|     def get_updates(self): | ||||
|         update_url = "http://" + self.base_url + "/getupdates" | ||||
|         result = self.call_api(update_url) | ||||
| @@ -30,6 +35,7 @@ class FetchUpdates: | ||||
|      | ||||
|  | ||||
|     def fetch_data(self): | ||||
|         try: | ||||
|             if self.update_calls == 0: | ||||
|                 fetch = self.get_last()    | ||||
|             else: | ||||
| @@ -38,7 +44,7 @@ class FetchUpdates: | ||||
|                     fetch = self.last_fetch | ||||
|                 else: | ||||
|                     self.last_fetch = fetch | ||||
|         try: | ||||
|              | ||||
|             data = fetch["data"] | ||||
|             has_queue = fetch["has_queue"] | ||||
|         except: | ||||
| @@ -60,6 +66,6 @@ class FetchUpdates: | ||||
|             if result.pop("status") == "ok": | ||||
|                 ret = result | ||||
|         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 | ||||
|   | ||||
| @@ -73,6 +73,6 @@ class BroadcastUpdates: | ||||
|             "status" : "ok", | ||||
|             **kwargs | ||||
|         } | ||||
|         print(ret) | ||||
|          | ||||
|         return jsonify(ret) | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class ReceiverLauncher(launcher.Launcher): | ||||
|         # active: periodically takes readouts | ||||
|         self.clock_hardware_module = c_out.ClockFace() | ||||
|         # 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 | ||||
|  | ||||
|         super().__init__( | ||||
|   | ||||
| @@ -23,8 +23,6 @@ class ClockBackend: | ||||
|  | ||||
|      | ||||
|     def clock_loop(self):         | ||||
|         print("looping") | ||||
|          | ||||
|         t = int(datetime.datetime.now().strftime("%H%M")) | ||||
|  | ||||
|         if t % 5 == 0: | ||||
|   | ||||
| @@ -13,7 +13,7 @@ class SensorReadout: | ||||
|         self.sensor_modules = { # we already call them, they are objects and not classes anymore | ||||
|             "temperature" : hardware.sensors.TemperatureModule(), | ||||
|             "humidity" : hardware.sensors.HumidityModule(), | ||||
|             "brightness" : hardware.sensors.BrightnessModule(), | ||||
|             "luminosity" : hardware.sensors.BrightnessModule(), | ||||
|             # more to come? | ||||
|         } | ||||
|  | ||||
| @@ -21,23 +21,36 @@ class SensorReadout: | ||||
|         helpers.timer.RepeatedTimer(300, self.spread_measure) | ||||
|  | ||||
|     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 name in self.sensor_modules.keys(): | ||||
|                 measure = self.sensor_modules[name].readout() | ||||
|                 results[name].append(measure) | ||||
|                 measurements[name].append(measure) | ||||
|                 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): | ||||
|         current_minute = int(datetime.datetime.now().timestamp() // 60) | ||||
|     # def save_results(self, results): | ||||
|     #     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(): | ||||
|             keep_value = sum(results[name]) / len(results[name]) | ||||
|             self.persistence["clock"]["sensors"][name] += [keep_value] | ||||
|     #     for name in results.keys(): | ||||
|     #         keep_value = sum(results[name]) / len(results[name]) | ||||
|     #         self.persistence["clock"]["sensors"][name] += [keep_value] | ||||
|  | ||||
|      | ||||
|     def save_results(self, **results): | ||||
|         data = self.db.sensors( | ||||
|             time=datetime.datetime.now(), | ||||
|             **results, | ||||
|             ) | ||||
|         data.save() | ||||
|   | ||||
| @@ -13,21 +13,17 @@ class ClockFace: | ||||
|         """""" | ||||
|         # added by the launcher, we have self.modules (dict) | ||||
|  | ||||
|  | ||||
|  | ||||
|         self.IO = hardware.led.get_handler() | ||||
|         self.shape = self.IO.shape # (16,32) for now | ||||
|         # TODO handle differently! | ||||
|         self.MOP = helpers.computations.MatrixOperations() | ||||
|          | ||||
|          | ||||
|         self.kill_output = False | ||||
|  | ||||
|     def start(self): | ||||
|         helpers.timer.RepeatedTimer(60, self.clock_loop) | ||||
|         # schedule for in 60 seconds | ||||
|         self.clock_loop() | ||||
|         # run once now | ||||
|         # TODO start as a thread | ||||
|         # helpers.timer.RepeatedTimer(60, self.clock_loop) | ||||
|         # # schedule for in 60 seconds | ||||
|         Thread(target = self.clock_loop).start() | ||||
|  | ||||
|  | ||||
| # TODO Turn off when button pressed? | ||||
| @@ -38,13 +34,18 @@ class ClockFace: | ||||
|         t_minutes = int(datetime.datetime.now().strftime("%H%M")) | ||||
|  | ||||
|         has_queue, data = self.modules["receive"].fetch_data() | ||||
|         self.set_brightness() | ||||
|          | ||||
|         if data == {}: | ||||
|             matrices = self.MOP.get_fallback() | ||||
|         else: | ||||
|             matrices = [np.asarray(d).astype(int) for d in data["matrices"]] | ||||
|          | ||||
|         if not self.kill_output: | ||||
|             self.IO.put(matrices) | ||||
|         else: | ||||
|             z = np.zeros((16,16,3)) | ||||
|             self.IO.put([z,z,z]) | ||||
|          | ||||
|         if has_queue: | ||||
|             tnext = 1 | ||||
| @@ -59,3 +60,14 @@ class ClockFace: | ||||
|         time.sleep(max(delta.total_seconds(), 0)) | ||||
|         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 | ||||
|  | ||||
|   | ||||
| @@ -5,17 +5,23 @@ logger = logging.getLogger(__name__) | ||||
| class TempSim: | ||||
|     """Simulates a temperature for running on windows""" | ||||
|     temperature = 23 # return a celsius value | ||||
|     humidity = 0.3 | ||||
|     humidity = 30 | ||||
|      | ||||
|  | ||||
| class LightSim: | ||||
|     def input(self, *args): | ||||
|         return 1 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class SensorModule: | ||||
|     def __init__(self): | ||||
|         logger.info("Using module " + self.__class__.__name__) | ||||
|  | ||||
| class LightSim: | ||||
|     def input(self, *args): | ||||
|         return 1 | ||||
|  | ||||
|  | ||||
| ## Real sensors! | ||||
| try: | ||||
|     import board | ||||
|     import adafruit_dht  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| from persistence import p_io | ||||
| from persistence import p_io, p_out | ||||
|  | ||||
| import logging | ||||
| import os | ||||
| @@ -24,6 +24,8 @@ class Launcher: | ||||
|     def __init__(self, **modules): | ||||
|         """""" | ||||
|         self.persistence = p_io.PersistentDict("persistence/prst.json") | ||||
|         self.db = p_out.DBLogging() | ||||
|  | ||||
|         self.logger = logging.getLogger(__name__) | ||||
|         self.logger.info(self.__class__.__name__ + " initialized") | ||||
|  | ||||
| @@ -42,6 +44,7 @@ class Launcher: | ||||
|             self.logger.info("Starting module "+ module.__class__.__name__) | ||||
|             module.modules = self.modules | ||||
|             module.persistence = self.persistence | ||||
|             module.db = self.db | ||||
|             module.start() | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,15 +1,39 @@ | ||||
| from peewee import * | ||||
| import datetime | ||||
| import logging | ||||
| logger = logging.getLogger(__name__) | ||||
| from threading import Thread | ||||
|  | ||||
| #db = SqliteDatabase('data.db') | ||||
| db = MySQLDatabase("AIO_sensors", host="192.168.1.101", port=3306, user="pi", passwd="supersecret") | ||||
| # whyyy? | ||||
| from . import keys | ||||
| dbk = keys.db_keys | ||||
|  | ||||
| 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() | ||||
|          | ||||
|  | ||||
|     class Meta: | ||||
|         database = db | ||||
|  | ||||
|  | ||||
| class SensorMetric(Metric): | ||||
|     # this is a continuous metric | ||||
| @@ -19,11 +43,16 @@ class SensorMetric(Metric): | ||||
|  | ||||
|  | ||||
| class ChatMetric(Metric): | ||||
|     # this gets cumulated over one hour (or one day, or...) | ||||
|     activity = CharField() | ||||
|     read = BooleanField() | ||||
|     send = BooleanField() | ||||
|     execute = BooleanField() | ||||
|  | ||||
|  | ||||
| class ErrorMetric(Metric): | ||||
|     # same as above | ||||
|     error = CharField() | ||||
|     error = TextField() | ||||
|  | ||||
|  | ||||
| class List(DBModel): | ||||
|     name = CharField(unique=True) | ||||
|     content = TextField() # unlimited length, use to serialise list into | ||||
| @@ -1,32 +1,58 @@ | ||||
| from models import db | ||||
| from models import * | ||||
| import datetime as dt | ||||
| from random import randint | ||||
| from . import models | ||||
|  | ||||
|  | ||||
| def create_tables(): | ||||
|     with db: | ||||
|         db.create_tables([SensorMetric, ChatMetric, ErrorMetric]) | ||||
| class DBLogging: | ||||
|     """Create a connection to a remote database and log some quantities that will be visualized otherwhere""" | ||||
|     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, | ||||
|             error = "Could not load module" | ||||
|         ) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # writin to the db gets handled through the model directly | ||||
|  | ||||
| # 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
									
								
							
							
						
						
									
										
											BIN
										
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -2,7 +2,7 @@ | ||||
| from bot import main | ||||
| from clock import c_back | ||||
| from broadcast import b_out  | ||||
| from dashboard import d_out | ||||
| # from dashboard import d_out | ||||
|  | ||||
| import launcher | ||||
|  | ||||
| @@ -12,9 +12,9 @@ class BroadcastLauncher(launcher.Launcher): | ||||
|     """Launcher for all server-side modules. The hard-computations""" | ||||
|     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.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 | ||||
|  | ||||
|         # "sensors" : self.sensors, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Remy Moll
					Remy Moll