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