diff --git a/bot/framework.py b/bot/framework.py index 0d437d7..b4fd3a2 100644 --- a/bot/framework.py +++ b/bot/framework.py @@ -2,6 +2,10 @@ import datetime from bot.api import telegram, google, weather, reddit import Levenshtein as lev + + + + class BotFramework(): """Main functionality for a bot """ @@ -19,45 +23,6 @@ class BotFramework(): self.persistence = prst # Uptime counter self.start_time = datetime.datetime.now() - - self.emoji_dict = { - "a" : ":regional_indicator_symbol_letter_a:", - "b" : ":regional_indicator_symbol_letter_b:", - "c" : ":regional_indicator_symbol_letter_c:", - "d" : ":regional_indicator_symbol_letter_d:", - "e" : ":regional_indicator_symbol_letter_e:", - "f" : ":regional_indicator_symbol_letter_f:", - "g" : ":regional_indicator_symbol_letter_g:", - "h" : ":regional_indicator_symbol_letter_h:", - "i" : ":regional_indicator_symbol_letter_i:", - "j" : ":regional_indicator_symbol_letter_j:", - "k" : ":regional_indicator_symbol_letter_k:", - "l" : ":regional_indicator_symbol_letter_l:", - "m" : ":regional_indicator_symbol_letter_m:", - "n" : ":regional_indicator_symbol_letter_n:", - "o" : ":regional_indicator_symbol_letter_o:", - "p" : ":regional_indicator_symbol_letter_p:", - "q" : ":regional_indicator_symbol_letter_q:", - "r" : ":regional_indicator_symbol_letter_r:", - "s" : ":regional_indicator_symbol_letter_s:", - "t" : ":regional_indicator_symbol_letter_t:", - "u" : ":regional_indicator_symbol_letter_u:", - "v" : ":regional_indicator_symbol_letter_v:", - "w" : ":regional_indicator_symbol_letter_w:", - "x" : ":regional_indicator_symbol_letter_x:", - "y" : ":regional_indicator_symbol_letter_y:", - "z" : ":regional_indicator_symbol_letter_z:", - "0" : ":keycap_digit_zero:", - "1" : ":keycap_digit_one:", - "2" : ":keycap_digit_two:", - "3" : ":keycap_digit_three:", - "4" : ":keycap_digit_four:", - "5" : ":keycap_digit_five:", - "6" : ":keycap_digit_six:", - "7" : ":keycap_digit_seven:", - "8" : ":keycap_digit_eight:", - "9" : ":keycap_digit_nine:", - } self.telegram = telegram.TelegramIO(self.persistence) self.weather = weather.WeatherFetch() @@ -121,17 +86,6 @@ class BotFramework(): return False - def emojify_word(self,word): - """""" - string_emoji = "" - for letter in word: - if letter in self.emoji_dict: - string_emoji += self.emoji_dict[letter.lower()] - else: - string_emoji += letter - return string_emoji - - def write_bot_log(self, function_name, error_message): """""" out = datetime.datetime.now().strftime("%d.%m.%y - %H:%M") diff --git a/bot/main.py b/bot/main.py index 891a212..36ff752 100644 --- a/bot/main.py +++ b/bot/main.py @@ -70,7 +70,6 @@ class ChatBot(FW.BotFramework): except: ip = "not fetchable" local_ips = "not fetchable" - local_ips = [i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)] message += "
Status: Running :green_circle:\n"
         message += "Uptime: " + delta[:delta.rfind(".")] + "\n"
@@ -270,7 +269,7 @@ class ChatBot(FW.BotFramework):
                     if step["journey"] != None:
                         text += step["journey"]["passList"][0]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][0]["departureTimestamp"])).strftime("%H:%M") + ")\n"
 
-                        text += ":right_arrow: Linie " + self.emojify_word(step["journey"]["number"]) + "\n"
+                        text += ":right_arrow: Linie " + step["journey"]["number"] + "\n"
 
                         text += step["journey"]["passList"][-1]["station"]["name"] + " (" + datetime.datetime.fromtimestamp(int(step["journey"]["passList"][-1]["arrivalTimestamp"])).strftime("%H:%M") +")\n"
                     else:
diff --git a/bot2/__init__.py b/bot2/__init__.py
new file mode 100644
index 0000000..dcf2c80
--- /dev/null
+++ b/bot2/__init__.py
@@ -0,0 +1 @@
+# Placeholder
diff --git a/bot2/api/__init__.py b/bot2/api/__init__.py
new file mode 100644
index 0000000..924f953
--- /dev/null
+++ b/bot2/api/__init__.py
@@ -0,0 +1,4 @@
+from . import google
+from . import keys
+from . import reddit
+from . import weather
\ No newline at end of file
diff --git a/bot2/api/google.py b/bot2/api/google.py
new file mode 100644
index 0000000..2ff701c
--- /dev/null
+++ b/bot2/api/google.py
@@ -0,0 +1,20 @@
+import googlesearch
+
+
+def query(params):
+    param_string = ""
+    for word in params:
+        param_string += word + "+"
+    param_string = param_string[:-1]
+    search_url = "https://google.com/search?q=" + param_string
+
+    try:
+        res = googlesearch.search(param_string.replace("+"," ") ,num=5,start=0,stop=5)
+        send_string = "Results for " + param_string.replace("+"," ") + ":\n\n"
+        for url in res:
+            send_string += url + "\n\n"
+        send_string += "Search url:\n" + search_url
+    except:
+        send_string = "Search url:\n" + search_url
+
+    return send_string
diff --git a/bot2/api/reddit.py b/bot2/api/reddit.py
new file mode 100644
index 0000000..49ee0d3
--- /dev/null
+++ b/bot2/api/reddit.py
@@ -0,0 +1,50 @@
+import praw
+try:
+    import bot.api.keys as keys
+except:
+    import keys
+
+stream = praw.Reddit(client_id = keys.reddit_id, client_secret = keys.reddit_secret, user_agent=keys.reddit_user_agent)
+
+def get_top(subreddit, number, return_type="text"):
+    if return_type == "text":
+        message = ""
+        try:
+            for submission in stream.subreddit(subreddit).top(limit=number):
+                if not submission.stickied:
+                    message += "" + submission.title + "" + "\n" + submission.selftext + "\n\n\n"
+            return message
+        except:
+            return "Api call failed, sorry"
+    else:
+        images = []
+        try:
+            for submission in stream.subreddit(subreddit).top(limit=number):
+                if not submission.stickied:
+                    t = {"image": submission.url, "caption": submission.title}
+                    images.append(t)
+            return images
+        except:
+            return ["Api call failed, sorry"]
+
+
+def get_random_rising(subreddit, number, return_type="text"):
+    if return_type == "text":
+        message = ""
+        try:
+            for submission in stream.subreddit(subreddit).random_rising(limit=number):
+                if not submission.stickied:
+                    message += "" + submission.title + "" + "\n" + submission.selftext + "\n\n\n"
+            return message
+        except:
+            return "Api call failed, sorry"
+    else:
+        images = []
+        try:
+            for submission in stream.subreddit(subreddit).random_rising(limit=number):
+                if not submission.stickied:
+                    t = {"image": submission.url, "caption": submission.title}
+                    images.append(t)
+            return images
+        except:
+            return ["Api call failed, sorry"]
diff --git a/bot2/api/weather.py b/bot2/api/weather.py
new file mode 100644
index 0000000..a7225b8
--- /dev/null
+++ b/bot2/api/weather.py
@@ -0,0 +1,61 @@
+import requests
+# import api.keys
+import datetime
+
+class WeatherFetch():
+    def __init__(self, key):
+        self.last_fetch = datetime.datetime.fromtimestamp(0)
+        self.last_weather = ""
+
+        self.url = "https://api.openweathermap.org/data/2.5/onecall?"
+        self.key = key
+
+    def show_weather(self, location):
+        delta = datetime.datetime.now() - self.last_fetch
+        if delta.total_seconds()/60 > 60 or "\n" not in self.last_weather: # 1 hour passed:
+
+            
+            data = {"lat" : location[0], "lon" : location[1], "exclude" : "minutely,hourly", "appid" : self.key, "units" : "metric"}
+            # today = datetime.datetime.today().weekday()
+            # days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
+
+            try:
+                weather = requests.get(self.url,params=data).json()
+                # categories = {"Clouds": ":cloud:", "Rain": ":cloud_with_rain:", "Thunderstorm": "thunder_cloud_rain", "Drizzle": ":droplet:", "Snow": ":cloud_snow:", "Clear": ":sun:", "Mist": "Mist", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",}
+                now = weather["current"]
+                ret_weather = []
+                ret_weather.append({
+                    "short" : now["weather"][0]["main"],
+                    "temps" : [int(now["temp"])]
+                    })
+                weather_days = weather["daily"]
+                for i, day in enumerate(weather_days):
+                    ret_weather.append({
+                        "short" : day["weather"][0]["main"],
+                        "temps" : [int(day["temp"]["min"]),int(day["temp"]["max"])]
+                        })
+            except:
+                ret_weather = []
+
+            
+            #     now = weather["current"]
+            #     message = "Now: " + categories[now["weather"][0]["main"]] + "\n"
+            #     message += ":thermometer: " + str(int(now["temp"])) + "Β°\n\n"
+
+            #     weather_days = weather["daily"]
+                
+            #     for i, day in enumerate(weather_days):
+            #         if i == 0:
+            #             message += "" + "Today" + ": " + categories[day["weather"][0]["main"]] + "\n"
+            #         else:
+            #             message += "" + days[(today + i + 1) % 7] + ": " + categories[day["weather"][0]["main"]] + "\n"
+            #         message += ":thermometer: :fast_down_button: " + str(int(day["temp"]["min"])) + "Β° , :thermometer: :fast_up_button: " + str(int(day["temp"]["max"])) + "Β°\n\n"
+            # except:
+            #     message = "Query failed, it's my fault, I'm sorry :sad:"
+            
+            self.last_weather = ret_weather
+            self.last_fetch = datetime.datetime.now()
+        else:
+            ret_weather = self.last_weather
+
+        return ret_weather
diff --git a/bot2/commands/__init__.py b/bot2/commands/__init__.py
new file mode 100644
index 0000000..fd8c34c
--- /dev/null
+++ b/bot2/commands/__init__.py
@@ -0,0 +1,5 @@
+# Placeholder
+from . import clock
+from . import help
+from . import weather
+from . import status
\ No newline at end of file
diff --git a/bot2/commands/clock.py b/bot2/commands/clock.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot2/commands/help.py b/bot2/commands/help.py
new file mode 100644
index 0000000..c4ba2dc
--- /dev/null
+++ b/bot2/commands/help.py
@@ -0,0 +1,87 @@
+from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
+from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
+from telegram.ext import (
+    Updater,
+    CommandHandler,
+    CallbackQueryHandler,
+    ConversationHandler,
+    CallbackContext,
+)
+
+FIRST = 1
+
+class Help():
+    """Shows the functions and their usage"""
+    
+    def __init__(self):
+        self.available_commands = {}
+
+
+    def create_handler(self):
+        conv_handler = ConversationHandler(
+            entry_points=[CommandHandler('help', self.entry_point)],
+            states={
+                FIRST: [
+                    CallbackQueryHandler(self.print_all, pattern="^all$"),
+                    CallbackQueryHandler(self.choose_specific, pattern="^specific$"),
+                    CallbackQueryHandler(self.print_one, pattern='func-'),
+                ]
+            },
+            fallbacks=[CommandHandler('help', self.entry_point)],
+        )
+        return conv_handler
+
+    def add_commands(self, commands):
+        # commands is a dict {"name": class}
+        for k in commands:
+            self.available_commands[k] = commands[k].__doc__
+
+    def entry_point(self, update: Update, context: CallbackContext) -> None:
+        keyboard = [
+            [
+                InlineKeyboardButton("All commands", callback_data="all"),
+                InlineKeyboardButton("Just one", callback_data="specific"),
+            ]
+        ]
+        reply_markup = InlineKeyboardMarkup(keyboard)
+        update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
+        return FIRST
+
+
+    def print_all(self, update: Update, context: CallbackContext) -> None:
+        query = update.callback_query
+        query.answer()
+
+        all_cmd = ""
+        for k in self.available_commands:
+            all_cmd += k + " - " + self.available_commands[k] +"\n"
+
+        query.edit_message_text(text="List of all commands:\n" + all_cmd)
+        return ConversationHandler.END
+
+
+    def choose_specific(self, update: Update, context: CallbackContext) -> None:
+        query = update.callback_query
+        query.answer()
+
+        
+        keyboard = [[InlineKeyboardButton(k, callback_data="func-" + k)] for k in self.available_commands]
+
+        reply_markup = InlineKeyboardMarkup(keyboard)
+        query.edit_message_text(
+            text="What command should be printed?", reply_markup=reply_markup
+        )
+        return FIRST
+
+
+    def print_one(self, update: Update, context: CallbackContext) -> None:
+        """Show new choice of buttons"""
+        query = update.callback_query
+        data = query.data.replace("func-", "")
+
+        query.answer()
+        message = self.available_commands[data]
+        query.edit_message_text(
+            text= message
+        )
+        return ConversationHandler.END
diff --git a/bot2/commands/status.py b/bot2/commands/status.py
new file mode 100644
index 0000000..cfa1911
--- /dev/null
+++ b/bot2/commands/status.py
@@ -0,0 +1,92 @@
+from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
+from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
+from telegram.ext import (
+    Updater,
+    CommandHandler,
+    CallbackQueryHandler,
+    ConversationHandler,
+    CallbackContext,
+)
+
+import datetime
+import requests
+import socket
+import numpy as np
+FIRST = 1
+
+class Status():
+    """Shows a short status of the program."""
+    
+    def __init__(self, name, version, prst, logger):
+        self.start_time = datetime.datetime.now()
+        self.name = name
+        self.version = version
+        self.persistence = prst
+        self.logger = logger
+
+    def create_handler(self):
+        conv_handler = ConversationHandler(
+            entry_points=[CommandHandler('status', self.entry_point)],
+            states={
+                FIRST: [
+                    CallbackQueryHandler(self.print_status, pattern="^status-"),
+                ]
+            },
+            fallbacks=[CommandHandler('status', self.entry_point)],
+        )
+        return conv_handler
+
+
+    def entry_point(self, update: Update, context: CallbackContext) -> None:
+        user = update.message.from_user
+        keyboard = [
+            [
+                InlineKeyboardButton("Status", callback_data="status-simple"),
+                InlineKeyboardButton("With log", callback_data="status-full"),
+            ]
+        ]
+        reply_markup = InlineKeyboardMarkup(keyboard)
+        update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
+        return FIRST
+
+
+    def print_status(self, update: Update, context: CallbackContext) -> None:
+        query = update.callback_query
+        wanted = query.data.replace("status-","")
+        query.answer()
+
+        delta = str(datetime.datetime.now() - self.start_time)
+        message = "BeebBop, this is " + self.name + " (V." + self.version + ")\n"
+
+        try:
+            ip = requests.get('https://api.ipify.org').text
+            with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
+                s.connect(('8.8.8.8', 80))
+                (addr, port) = s.getsockname()
+            local_ips = addr
+        except:
+            ip = "not fetchable"
+            local_ips = "not fetchable"
+
+        message += "
Status: Running 🟒\n"
+        message += "Uptime: " + delta[:delta.rfind(".")] + "\n"
+        message += "Reboots: " + str(self.persistence["global"]["reboots"]) + "\n"
+        message += "IP (public): " + ip + "\n"
+        message += "IP (private): " + str(local_ips) + "\n"
+        tot_r = np.array(self.persistence["bot"]["receive_activity"]["count"]).sum()
+        message += "Total messages read: " + str(tot_r) + "\n"
+
+        tot_s = np.array(self.persistence["bot"]["send_activity"]["count"]).sum()
+        message += "Total messages sent: " + str(tot_s) + "\n"
+
+        tot_e = np.array(self.persistence["bot"]["execute_activity"]["count"]).sum()
+        message += "Commands executed " + str(tot_e) + "
\n" + + if wanted == "full": + message += str(dir(self.logger)) + + query.edit_message_text( + text= message + ) + + return ConversationHandler.END diff --git a/bot2/commands/weather.py b/bot2/commands/weather.py new file mode 100644 index 0000000..696066d --- /dev/null +++ b/bot2/commands/weather.py @@ -0,0 +1,114 @@ +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext +from telegram.ext import ( + Updater, + CommandHandler, + CallbackQueryHandler, + ConversationHandler, + CallbackContext, +) + +import datetime +FIRST = 1 + +class Weather(): + """Shows a weatherforecast for a given location""" + def __init__(self, api): + """initialize api and persistence""" + self.api = api + self.city = "" + + def create_handler(self): + """returns the handlers with button-logic""" + conv_handler = ConversationHandler( + entry_points=[CommandHandler('weather', self.entry_point)], + states={ + FIRST: [ + CallbackQueryHandler(self.choose_city, pattern="^city-"), + CallbackQueryHandler(self.choose_time, pattern="^time-"), + ] + }, + fallbacks=[CommandHandler('weather', self.entry_point)], + ) + + return conv_handler + + + def entry_point(self, update: Update, context: CallbackContext) -> None: + """Reacts the call of the command. Prints the first buttons""" + keyboard = [ + [ + InlineKeyboardButton("ZΓΌrich", callback_data="city-zurich"), + InlineKeyboardButton("Freiburg", callback_data="city-freiburg"), + InlineKeyboardButton("Mulhouse", callback_data="city-mulhouse"), + ] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + update.message.reply_text("Which city?", reply_markup=reply_markup) + return FIRST + + + def choose_city(self, update: Update, context: CallbackContext) -> None: + """Prompt same text & keyboard as `start` does but not as new message""" + # Get CallbackQuery from Update + query = update.callback_query + data = query.data + self.city = data.replace("city-","") + query.answer() + keyboard = [ + [ + InlineKeyboardButton("Now", callback_data="time-now"), + InlineKeyboardButton("Tomorrow", callback_data="time-tomorrow"), + InlineKeyboardButton("7 days", callback_data="time-7"), + ] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + query.edit_message_text( + text = "Which time?", reply_markup=reply_markup + ) + return FIRST + + + def choose_time(self, update: Update, context: CallbackContext) -> None: + """Show new choice of buttons""" + query = update.callback_query + query.answer() + forecast_time = query.data.replace("time-","") + weather = self.get_weather(self.city, forecast_time) + query.edit_message_text( + text = "Weather: \n" + weather + ) + return ConversationHandler.END + + + def get_weather(self, city, forecast_time) -> None: + """get the weather that matches the given params""" + locations = {"freiburg": [47.9990, 7.8421], "zurich": [47.3769, 8.5417], "mulhouse": [47.7508, 7.3359]} + + city = locations[city] + + categories = {"Clouds": "☁", "Rain": "🌧", "Thunderstorm": "🌩", "Drizzle": ":droplet:", "Snow": "❄", "Clear": "β˜€", "Mist": "🌫", "Smoke": "Smoke", "Haze": "Haze", "Dust": "Dust", "Fog": "Fog", "Sand": "Sand", "Dust": "Dust", "Ash": "Ash", "Squall": "Squall", "Tornado": "Tornado",} + days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + today = datetime.datetime.today().weekday() + weather = self.api.show_weather(city) + message = "" + if forecast_time == "now" or forecast_time == "7": + now = weather.pop(0) + message += "Now: " + categories[now["short"]] + "\n" + message += "🌑" + str(now["temps"][0]) + "Β°\n\n" + tod = weather.pop(0) + message += "" + "Today" + ": " + categories[tod["short"]] + "\n" + message += "🌑 ❄ " + str(tod["temps"][0]) + "Β° , 🌑 πŸ”₯ " + str(tod["temps"][1]) + "Β°\n\n" + + if forecast_time == "tomorrow" or forecast_time == "7": + tom = weather.pop(0) + print(tom) + message += "" + "Tomorrow" + ": " + categories[tom["short"]] + "\n" + message += "🌑 ❄ " + str(tom["temps"][0]) + "Β° , 🌑 πŸ”₯ " + str(tom["temps"][1]) + "Β°\n\n" + + if forecast_time == "7": + for i, day in enumerate(weather): + message += "" + days[(today + i + 2) % 7] + ": " + categories[day["short"]] + "\n" + message += "🌑 ❄ " + str(day["temps"][0]) + "Β° , 🌑 πŸ”₯ " + str(day["temps"][1]) + "Β°\n\n" + + return message diff --git a/bot2/main.py b/bot2/main.py new file mode 100644 index 0000000..ade1932 --- /dev/null +++ b/bot2/main.py @@ -0,0 +1,65 @@ +# TODO remove in the end +import logging + +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext + +#from .commands import * +from . import api, commands + +class ChatBot(): + """better framwork - unites all functions""" + + def __init__(self, name, version, hw_commands, prst, logger): + """Inits the Bot with a few conf. vars + Args: -> name:str - Name of the bot + -> version:str - Version number + -> hw_commands - dict with commands executable by the clock module + -> prst:dict - persistence (overloaded dict that writes to json file) + -> logger - logging object to unify log messages + """ + + # Import submodules + self.api_weather = api.weather.WeatherFetch(api.keys.weather_api) + # self.reddit_api = api.reddit.RedditFetch() + # and so on + + # Mark them as available + self.help_module = commands.help.Help() + self.sub_modules = { + "weather": commands.weather.Weather(self.api_weather), + "help" : self.help_module, + "status" : commands.status.Status(name, version, prst, logger), + + } + # "log" : self.bot_print_log, + # "lorem" : self.bot_print_lorem, + # "weather" : self.bot_show_weather, + # "google" : self.bot_google_search, + # "events" : self.bot_print_events, + # "wikipedia" : self.bot_show_wikipedia, + # "zvv" : self.bot_zvv, + # "cronjob" : self.bot_cronjob, + # "joke" : self.bot_tell_joke, + # "meme" : self.bot_send_meme, + # "news" : self.bot_send_news, + # "list" : self.bot_list, + # "alias" : self.bot_save_alias, + # }, **hw_commands) + # concat bot_commands + hw-commands + # must be a class that has a method create_handler + + self.telegram = Updater(api.keys.telegram_api, use_context=True) + self.dispatcher = self.telegram.dispatcher + + self.add_commands() + self.telegram.start_polling() + self.telegram.idle() + + + + def add_commands(self): + self.help_module.add_commands(self.sub_modules) + for k in self.sub_modules: + self.dispatcher.add_handler(self.sub_modules[k].create_handler()) + diff --git a/dashboard/main.py b/dashboard/main.py index 361512d..8770cba 100644 --- a/dashboard/main.py +++ b/dashboard/main.py @@ -232,7 +232,7 @@ class DashBoard(): inverse=True, ) except: - card = card = dbc.Card([ + card = dbc.Card([ dbc.CardBody([ html.H4("Could not load XKCD", className="card-title"), ]) diff --git a/launcher.py b/launcher.py index ab89924..ca1e826 100644 --- a/launcher.py +++ b/launcher.py @@ -5,15 +5,24 @@ import dashboard.main # wrapper import wrapper import persistence.main -# misc. + +# various +import logging from threading import Thread +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO +) + class Launcher(): """Launches all other submodules""" def __init__(self): """""" self.persistence = persistence.main.PersistentDict("persistence/prst.json") + self.logger = logging.getLogger(__name__) + self.logger.info("Starting") + if len(self.persistence) == 0: self.init_persistence() self.persistence["global"]["reboots"] += 1 @@ -33,16 +42,16 @@ class Launcher(): def clock(self): self.clock = wrapper.ClockWrapper(self.clock_module, self.bot_module) - def chatbot(self): self.bot = wrapper.BotWrapper(self.bot_module, self.clock_module) - def dashboard(self): self.dashboard = wrapper.DashBoardWrapper(self.dashboard_module, self.bot_module) + def init_persistence(self): - print("New Persistence created") + self.logger.warn("New Persistence created") + self.persistence["bot"] = { "send_activity" : {"hour":[], "count":[]}, "receive_activity" : {"hour":[], "count":[]}, @@ -61,4 +70,7 @@ class Launcher(): ######################################################################## ## Aand liftoff! + + + Launcher() \ No newline at end of file diff --git a/persistence/main.py b/persistence/main.py index ea2df13..699cfdc 100644 --- a/persistence/main.py +++ b/persistence/main.py @@ -4,10 +4,12 @@ import os class PersistentDict(dict): - """""" - def __init__(self, file_name, *args, **kwargs): + """Extended dict that writes its content to a file every time a value is changed""" + def __init__(self, file_name, *args, **kwargs): + """initialization of the dict and of the required files""" super().__init__(*args, **kwargs) + self.path = file_name self.last_action = "" if not os.path.exists(self.path): @@ -15,7 +17,7 @@ class PersistentDict(dict): f.write("{}") self.read_dict() - + ## helper - functions def write_dict(self): with open(self.path, "w") as f: json.dump(self, f) @@ -28,7 +30,7 @@ class PersistentDict(dict): super().__setitem__(key, tmp[key]) self.last_action = "r" - + ## extended dictionary - logic def __setitem__(self, key, value): if self.last_action != "r": self.read_dict() @@ -51,7 +53,11 @@ class PersistentDict(dict): self.write_dict() return {} + + class HookedDict(dict): + """helper class to detect writes to a child-dictionary and triger a write in PersistentDict""" + def __init__(self, own_name, parent_dict, *args, **kwargs): super().__init__(*args, **kwargs) self.name = own_name diff --git a/t.py b/t.py new file mode 100644 index 0000000..85fb935 --- /dev/null +++ b/t.py @@ -0,0 +1,11 @@ +import bot2.main +import persistence.main + +import logging +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO +) +logger = logging.getLogger(__name__) +prst = persistence.main.PersistentDict("persistence/prst.json") + +test = bot2.main.ChatBot("Test", ".1", "HW", prst, logger) \ No newline at end of file