diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f636bac --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Aktuelle Datei", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/bot2/commands/__init__.py b/bot2/commands/__init__.py index fd8c34c..561818f 100644 --- a/bot2/commands/__init__.py +++ b/bot2/commands/__init__.py @@ -1,5 +1,2 @@ # Placeholder -from . import clock -from . import help -from . import weather -from . import status \ No newline at end of file +from . import clock, help, weather, status, zvv, lists, alias, plaintext diff --git a/bot2/commands/alias.py b/bot2/commands/alias.py new file mode 100644 index 0000000..5e94ed6 --- /dev/null +++ b/bot2/commands/alias.py @@ -0,0 +1,65 @@ +from .template import * + +FIRST = range(1) +class Alias(BotFunc): + """create a new command for command-paths you often use""" + + def __init__(self, dispatcher, prst): + super().__init__(prst) + self.dispatcher = dispatcher + # do not interact with him yet! + + def create_handler(self): + conv_handler = ConversationHandler( + entry_points=[CommandHandler('alias', self.entry_point)], + states={ + FIRST: [ + CallbackQueryHandler(self.print_all, pattern="^all$"), + CallbackQueryHandler(self.create_alias, pattern="^new$"), + CallbackQueryHandler(self.delete_alias, pattern='^delete$'), + ] + }, + fallbacks=[CommandHandler('alias', self.entry_point)], + ) + return conv_handler + + + def entry_point(self, update: Update, context: CallbackContext) -> None: + super().entry_point() + test = self.dispatcher + print(self.dispatcher.handlers[0]) + keyboard = [ + [InlineKeyboardButton("All aliases", callback_data="all")], + [InlineKeyboardButton("Create new alias", callback_data="new")], + [InlineKeyboardButton("Delete alias", callback_data="delete")], + ] + 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_alias = "" + for k in self.persistence["bot"]["aliases"]: + all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n" + + query.edit_message_text(text="List of all commands:\n" + all_alias) + return ConversationHandler.END + + + def create_alias(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + + all_alias = "" + for k in self.persistence["bot"]["aliases"]: + all_alias += k + " - " + self.persistence["bot"]["aliases"] +"\n" + + query.edit_message_text(text="List of all commands:\n" + all_alias) + return ConversationHandler.END + + def delete_alias(self, update: Update, context: CallbackContext) -> None: + return ConversationHandler.END \ No newline at end of file diff --git a/bot2/commands/clock.py b/bot2/commands/clock.py index e69de29..6b54824 100644 --- a/bot2/commands/clock.py +++ b/bot2/commands/clock.py @@ -0,0 +1,23 @@ +from .template import * + +CHOOSE, ARGS = range(2) + +class Clock(BotFunc): + """pass on commands to clock-module""" + def __init__(self, prst, hw_commands): + super().__init__(prst) + self.hw_commands = hw_commands + + def create_handler(self): + handler = ConversationHandler( + entry_points=[CommandHandler("clock", self.entry_point)], + states={ + CHOOSE : [], + ARGS : [] + }, + fallbacks=[CommandHandler('clock', self.entry_point)], + ) + return handler + + def entry_point(self): + super().entry_point() \ No newline at end of file diff --git a/bot2/commands/help.py b/bot2/commands/help.py index c4ba2dc..ece0299 100644 --- a/bot2/commands/help.py +++ b/bot2/commands/help.py @@ -1,19 +1,13 @@ -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext -from telegram.ext import ( - Updater, - CommandHandler, - CallbackQueryHandler, - ConversationHandler, - CallbackContext, -) +from .template import * FIRST = 1 -class Help(): + +class Help(BotFunc): """Shows the functions and their usage""" - def __init__(self): + def __init__(self, prst): + super().__init__(prst) self.available_commands = {} @@ -25,7 +19,7 @@ class Help(): CallbackQueryHandler(self.print_all, pattern="^all$"), CallbackQueryHandler(self.choose_specific, pattern="^specific$"), CallbackQueryHandler(self.print_one, pattern='func-'), - ] + ], }, fallbacks=[CommandHandler('help', self.entry_point)], ) @@ -34,9 +28,13 @@ class Help(): def add_commands(self, commands): # commands is a dict {"name": class} for k in commands: - self.available_commands[k] = commands[k].__doc__ + if k != "plaintext": + self.available_commands[k] = commands[k].__doc__ + + def entry_point(self, update: Update, context: CallbackContext) -> None: + super().entry_point() keyboard = [ [ InlineKeyboardButton("All commands", callback_data="all"), @@ -51,12 +49,11 @@ class Help(): 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" + for h in self.available_commands: + all_cmd += h + " - `" + self.available_commands[h] + "`\n" - query.edit_message_text(text="List of all commands:\n" + all_cmd) + query.edit_message_text(text="List of all commands:\n" + all_cmd, parse_mode = ParseMode.MARKDOWN) return ConversationHandler.END @@ -66,8 +63,8 @@ class Help(): 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 ) @@ -77,11 +74,12 @@ class Help(): def print_one(self, update: Update, context: CallbackContext) -> None: """Show new choice of buttons""" query = update.callback_query - data = query.data.replace("func-", "") - + name = query.data.replace("func-", "") query.answer() - message = self.available_commands[data] + + message = name + ": `" + self.available_commands[name] + "`" query.edit_message_text( - text= message + text= message, + parse_mode = ParseMode.MARKDOWN_V2 ) return ConversationHandler.END diff --git a/bot2/commands/lists.py b/bot2/commands/lists.py new file mode 100644 index 0000000..5c4a476 --- /dev/null +++ b/bot2/commands/lists.py @@ -0,0 +1,177 @@ +from .template import * + +import datetime +import requests + +NAME, NEW, ACTION, ITEMADD, ITEMREMOVE = range(5) + + +class Lists(BotFunc): + """Create and edit lists""" + + def __init__(self, prst): + super().__init__(prst) + self.current_name = "" + + + def create_handler(self): + conv_handler = ConversationHandler( + entry_points=[CommandHandler('list', self.entry_point)], + states={ + NAME: [ + CallbackQueryHandler(self.choose_list, pattern="^list-"), + CallbackQueryHandler(self.new_list, pattern="^new$"), + ], + NEW : [MessageHandler(Filters.text, callback=self.new_listname)], + ACTION: [ + CallbackQueryHandler(self.list_add, pattern="^add$"), + CallbackQueryHandler(self.list_remove, pattern="^remove$"), + CallbackQueryHandler(self.list_clear, pattern="^clear$"), + CallbackQueryHandler(self.list_delete, pattern="^delete$"), + CallbackQueryHandler(self.list_print, pattern="^print$"), + CallbackQueryHandler(self.list_menu, pattern="^overview$"), + ], + ITEMADD : [MessageHandler(Filters.text, callback=self.list_add_item)], + ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)] + }, + fallbacks=[CommandHandler('list', self.entry_point)], + ) + return conv_handler + + + 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")]] + + reply_markup = InlineKeyboardMarkup(keyboard) + update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup) + return NAME + + + def choose_list(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + data = query.data + name = data.replace("list-","") + query.answer() + self.current_name = name + + keyboard = [ + [InlineKeyboardButton("Add item", callback_data="add")], + [InlineKeyboardButton("Remove item", callback_data="remove")], + [InlineKeyboardButton("Clear list", callback_data="clear")], + [InlineKeyboardButton("Print list", callback_data="print")], + [InlineKeyboardButton("Delete list", callback_data="delete")], + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + query.edit_message_text("Very well. For " + name + " the following actions are available:", reply_markup=reply_markup) + return ACTION + + + def list_menu(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + + keyboard = [ + [InlineKeyboardButton("Add item", callback_data="add")], + [InlineKeyboardButton("Remove item", callback_data="remove")], + [InlineKeyboardButton("Clear list", callback_data="clear")], + [InlineKeyboardButton("Print list", callback_data="print")], + [InlineKeyboardButton("Delete list", callback_data="delete")], + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + query.edit_message_text("Very well. For " + self.current_name + " the following actions are available:", reply_markup=reply_markup) + return ACTION + + + def new_list(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + query.edit_message_text("What's the name of the new list?") + return NEW + + + 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] = [] + + 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") + return ConversationHandler.END + + + def list_add(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + query.edit_message_text("What would you like to add?") + return ITEMADD + + + 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])] + reply_markup = InlineKeyboardMarkup(keyboard) + + query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup) + return ITEMREMOVE + + + def list_clear(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + self.persistence["global"]["lists"][self.current_name] = [] + 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) + return ACTION + + + def list_delete(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + query.answer() + self.persistence["global"]["lists"].pop(self.current_name, None) + query.edit_message_text("List " + self.current_name + " deleted") + return ConversationHandler.END + + + 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]) + 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) + return ACTION + + + def list_add_item(self, update: Update, context: CallbackContext) -> None: + name = update.message.text + self.persistence["global"]["lists"][self.current_name] += [name] + 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) + return ACTION + + + def list_remove_index(self, update: Update, context: CallbackContext) -> None: + query = update.callback_query + ind = int(query.data) + query.answer() + + old = self.persistence["global"]["lists"][self.current_name] + name = old.pop(ind) + self.persistence["global"]["lists"][self.current_name] = old + + keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] + reply_markup = InlineKeyboardMarkup(keyboard) + + query.edit_message_text("Removed " + name, reply_markup=reply_markup) + return ACTION diff --git a/bot2/commands/plaintext.py b/bot2/commands/plaintext.py new file mode 100644 index 0000000..6ff8a0d --- /dev/null +++ b/bot2/commands/plaintext.py @@ -0,0 +1,15 @@ +from .template import * + + +class Plain(BotFunc): + """Not a command: just keeps logs and usage_data""" + def __init__(self, prst): + super().__init__(prst) + + 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") \ No newline at end of file diff --git a/bot2/commands/status.py b/bot2/commands/status.py index cfa1911..7833943 100644 --- a/bot2/commands/status.py +++ b/bot2/commands/status.py @@ -1,35 +1,29 @@ -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext -from telegram.ext import ( - Updater, - CommandHandler, - CallbackQueryHandler, - ConversationHandler, - CallbackContext, -) +from .template import * import datetime import requests import socket import numpy as np +import os + + FIRST = 1 -class Status(): +class Status(BotFunc): """Shows a short status of the program.""" - def __init__(self, name, version, prst, logger): + def __init__(self, name, version, prst): + super().__init__(prst) 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-"), + CallbackQueryHandler(self.send_log, pattern="^full$"), ] }, fallbacks=[CommandHandler('status', self.entry_point)], @@ -38,22 +32,14 @@ class Status(): def entry_point(self, update: Update, context: CallbackContext) -> None: + super().entry_point() user = update.message.from_user keyboard = [ [ - InlineKeyboardButton("Status", callback_data="status-simple"), - InlineKeyboardButton("With log", callback_data="status-full"), + InlineKeyboardButton("And the log?", callback_data="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" @@ -68,25 +54,30 @@ class Status(): 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"
+ 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"
+ 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"
+ 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"
+ message += "Commands executed `" + str(tot_e) + "`\n"
- if wanted == "full":
- message += str(dir(self.logger))
+ update.message.reply_text(message, reply_markup=reply_markup)
+ return FIRST
- query.edit_message_text(
- text= message
- )
- return ConversationHandler.END
+ def send_log(self, update: Update, context: CallbackContext) -> None:
+ query = update.callback_query
+ wanted = query.data.replace("status-","")
+ query.answer()
+ with open("persistence/complete.log") as l:
+ query.message.reply_document(l)
+
+ return ConversationHandler.END
\ No newline at end of file
diff --git a/bot2/commands/template.py b/bot2/commands/template.py
new file mode 100644
index 0000000..ba97c8b
--- /dev/null
+++ b/bot2/commands/template.py
@@ -0,0 +1,33 @@
+import logging
+from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, ParseMode
+from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters
+from telegram.ext import (
+ Updater,
+ CommandHandler,
+ CallbackQueryHandler,
+ ConversationHandler,
+ CallbackContext,
+)
+
+
+import datetime
+
+
+class BotFunc():
+ """Base class for a specific bot-functionality"""
+ def __init__(self, prst):
+ self.logger = logging.getLogger(__name__)
+ self.persistence = prst
+
+
+ def entry_point(self):
+ self.increase_counter("execute_activity")
+
+
+ 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
\ No newline at end of file
diff --git a/bot2/commands/weather.py b/bot2/commands/weather.py
index 696066d..c847a68 100644
--- a/bot2/commands/weather.py
+++ b/bot2/commands/weather.py
@@ -1,23 +1,18 @@
-from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
-from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext
-from telegram.ext import (
- Updater,
- CommandHandler,
- CallbackQueryHandler,
- ConversationHandler,
- CallbackContext,
-)
+from .template import *
import datetime
+
FIRST = 1
-class Weather():
+class Weather(BotFunc):
"""Shows a weatherforecast for a given location"""
- def __init__(self, api):
+ def __init__(self, api, prst):
"""initialize api and persistence"""
+ super().__init__(prst)
self.api = api
self.city = ""
+
def create_handler(self):
"""returns the handlers with button-logic"""
conv_handler = ConversationHandler(
@@ -36,6 +31,7 @@ class Weather():
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"),
@@ -44,7 +40,10 @@ class Weather():
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
- update.message.reply_text("Which city?", reply_markup=reply_markup)
+ if update.message:
+ update.message.reply_text("Which city?", reply_markup=reply_markup)
+ else:
+ update.callback_query.edit_message_text("Which city", reply_markup=reply_markup)
return FIRST
diff --git a/bot2/commands/zvv.py b/bot2/commands/zvv.py
new file mode 100644
index 0000000..436bf07
--- /dev/null
+++ b/bot2/commands/zvv.py
@@ -0,0 +1,86 @@
+from .template import *
+
+import datetime
+import requests
+
+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)
+ self.start = ""
+ self.dest = ""
+ pass
+
+ def create_handler(self):
+ conv_handler = ConversationHandler(
+ entry_points=[CommandHandler('zvv', self.entry_point)],
+ states={
+ START: [MessageHandler(Filters.text, callback=self.get_start)],
+ DEST: [MessageHandler(Filters.text, callback=self.get_dest)]
+ },
+ fallbacks=[CommandHandler('zvv', self.entry_point)],
+ )
+ return conv_handler
+
+
+ def entry_point(self, update: Update, context: CallbackContext) -> None:
+ super().entry_point()
+ update.message.reply_text("What is the start point?")
+ return START
+
+
+ def get_start(self, update: Update, context: CallbackContext) -> None:
+ loc = update.message.text
+ self.start = loc
+ update.message.reply_text("Ok. Going from " + loc + ", what is the destination?")
+ return DEST
+
+
+ def get_dest(self, update: Update, context: CallbackContext) -> None:
+ loc = update.message.text
+ self.dest = loc
+ route = self.get_result()
+ update.message.reply_text("Her are the routes I've got:\n" + route)
+ return ConversationHandler.END
+
+
+ def get_result(self):
+ url = "http://transport.opendata.ch/v1/connections"
+
+ start = self.start
+ dest = self.dest
+
+ data = {"from" : start, "to" : dest, "limit" : 2}
+ try:
+ routes = requests.get(url, params=data).json()
+ result = routes["connections"]
+ text = result[0]["from"]["station"]["name"] + " ⏩ " + result[0]["to"]["station"]["name"] + "\n\n"
+ for con in result:
+ text += "Start: " + datetime.datetime.fromtimestamp(int(con["from"]["departureTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
+ text += "🏁 " + datetime.datetime.fromtimestamp(int(con["to"]["arrivalTimestamp"])).strftime("%d/%m - %H:%M") + "\n"
+ text += "⏳ " + con["duration"] + "\n"
+ text += "🗺️ Route:\n"
+
+ for step in con["sections"]:
+ 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 += "➡️ Linie " + self.number_to_emoji(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:
+ text += "Walk."
+ text += "\n"
+ return text
+ except:
+ return "Invalid api call."
+
+ def number_to_emoji(self, number):
+ out = ""
+ numbers = ["0️⃣","1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣"]
+ for i in str(number):
+ out += numbers[int(i)]
+ return str(out)
\ No newline at end of file
diff --git a/bot2/main.py b/bot2/main.py
index ade1932..2b534ab 100644
--- a/bot2/main.py
+++ b/bot2/main.py
@@ -1,16 +1,14 @@
-# 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
+import logging
+logger = logging.getLogger(__name__)
+
class ChatBot():
"""better framwork - unites all functions"""
- def __init__(self, name, version, hw_commands, prst, logger):
+ def __init__(self, name, version, hw_commands, prst):
"""Inits the Bot with a few conf. vars
Args: -> name:str - Name of the bot
-> version:str - Version number
@@ -24,42 +22,40 @@ class ChatBot():
# 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),
+ self.telegram = Updater(api.keys.telegram_api, use_context=True)
+ self.dispatcher = self.telegram.dispatcher
+ # Mark them as available
+ self.help_module = commands.help.Help(prst)
+ self.sub_modules = {
+ "weather": commands.weather.Weather(self.api_weather, prst),
+ "help" : self.help_module,
+ "status" : commands.status.Status(name, version, prst),
+ "zvv" : commands.zvv.Zvv(prst),
+ "list" : commands.lists.Lists(prst),
+ #"alias" : commands.alias.Alias(self.dispatcher, prst),
+ "clock" : commands.clock.Clock(prst, hw_commands),
+ "plaintext" : commands.plaintext.Plain(prst) # for handling non-command messages that should simply contribute to statistics
}
- # "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())
+ self.help_module.add_commands(self.sub_modules)
+
+ def START(self):
+ self.telegram.start_polling()
+ # self.telegram.idle()
\ No newline at end of file
diff --git a/dashboard/main.py b/dashboard/main.py
index 8770cba..99aa3a4 100644
--- a/dashboard/main.py
+++ b/dashboard/main.py
@@ -52,7 +52,7 @@ class DashBoard():
return kids
- def launch_dashboard(self):
+ def START(self):
self.app.run_server(host=self.host_ip, port=80)#, debug=True)
diff --git a/launch.sh b/launch.sh
index 2c9cbe0..635b0b5 100644
--- a/launch.sh
+++ b/launch.sh
@@ -2,4 +2,4 @@
sleep 30
cd /home/pi/AIO
-sudo python3 launcher.py > persistence/log.txt
+sudo python3 launcher.py
diff --git a/launcher.py b/launcher.py
index ca1e826..67d5867 100644
--- a/launcher.py
+++ b/launcher.py
@@ -1,5 +1,6 @@
# functionality
import bot.main
+import bot2.main
import clock.main
import dashboard.main
# wrapper
@@ -28,16 +29,19 @@ class Launcher():
self.persistence["global"]["reboots"] += 1
self.clock_module = clock.main.ClockFace(prst=self.persistence)
- self.bot_module = bot.main.ChatBot(name="ChatterBot", version="2.3", prst=self.persistence, hw_commands=self.clock_module.commands)
+ self.bot_module = bot2.main.ChatBot(name="Norbit", version="3.0a", prst=self.persistence, hw_commands=self.clock_module.commands)
self.dashboard_module = dashboard.main.DashBoard(host_ip="0.0.0.0", prst=self.persistence)
self.threads = []
- self.threads.append(Thread(target=self.chatbot))
+ #self.threads.append(Thread(target=self.chatbot))
+
self.threads.append(Thread(target=self.clock))
self.threads.append(Thread(target=self.dashboard))
+
for i in self.threads:
i.start()
-
+ self.chatbot()
+
def clock(self):
self.clock = wrapper.ClockWrapper(self.clock_module, self.bot_module)
@@ -50,7 +54,7 @@ class Launcher():
def init_persistence(self):
- self.logger.warn("New Persistence created")
+ self.logger.warn("No persistence found, created a new one")
self.persistence["bot"] = {
"send_activity" : {"hour":[], "count":[]},
@@ -70,7 +74,4 @@ class Launcher():
########################################################################
## Aand liftoff!
-
-
-
Launcher()
\ No newline at end of file
diff --git a/persistence/main.py b/persistence/main.py
index 699cfdc..5a4d083 100644
--- a/persistence/main.py
+++ b/persistence/main.py
@@ -71,4 +71,9 @@ class HookedDict(dict):
ret_val = super().__getitem__(key)
if type(ret_val) == dict:
ret_val = HookedDict(key, self, ret_val)
- return ret_val
\ No newline at end of file
+ return ret_val
+
+ def pop(self, k, d=None):
+ retvalue = super().pop(k, d)
+ self.parent.__setitem__(self.name, self)
+ return retvalue
diff --git a/t.py b/t.py
index 85fb935..36c5e78 100644
--- a/t.py
+++ b/t.py
@@ -3,7 +3,9 @@ import persistence.main
import logging
logging.basicConfig(
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ level=logging.INFO,
+ #filename='persistence/complete.log',
)
logger = logging.getLogger(__name__)
prst = persistence.main.PersistentDict("persistence/prst.json")
diff --git a/wrapper.py b/wrapper.py
index 7c3b1d5..dfe9a66 100644
--- a/wrapper.py
+++ b/wrapper.py
@@ -1,24 +1,23 @@
import time
import datetime
+import logging
+import threading
+logger = logging.getLogger(__name__)
class Wrapper():
- """Wrapper skeleton for the modules (bot, clock dashboard...)"""
+ """Wrapper skeleton for the modules (bot, clock, dashboard ... maybe more to come?)"""
def __init__(self, own_module, *other_modules):
self.own = own_module
self.others = other_modules
- print("Starting " + self.__class__.__name__ + " functionality")
+
+ logger.debug("Starting " + self.own.__class__.__name__ + " through wrapper.")
-
- def mainloop(self, sleep_delta, action):
- """sleep_delta in seconds sets the sleep period of the loop
- action is a function that is performed every * seconds"""
- print("Launching " + self.__class__.__name__ + " mainloop")
- while True:
- action()
- time.sleep(sleep_delta)
+ def external_action(self, func, *args, **kwargs):
+ """do a special action initiated by other modules"""
+ logger.info("External request to " + self.own.__class__.__name__ + ".")
@@ -30,46 +29,46 @@ class ClockWrapper(Wrapper):
super().__init__(own_module, *other_modules)
self.weather = {"weather":"", "high":"", "low":"", "show":"temps"}
self.weather_raw = {}
- self.mainloop(15)
+ self.START()
- def mainloop(self, sleep_delta):
- """Runs the showing of the clock-face periodically (better way?)"""
-
- self.prev_time = "0"
-
+ def START(self): # I prefer the name tick_tack
+ """Runs the showing of the clock-face periodically: update every minute"""
def perform_loop():
- if self.prev_time != datetime.datetime.now().strftime("%H%M"):
+ logger.warning("NEW TIME")
+ t = int(datetime.datetime.now().strftime("%H%M"))
- if int(self.prev_time) % 5 == 0:
- weather = self.others[0].weather.show_weather([47.3769, 8.5417]) # zürich
+ if t % 5 == 0:
+ # switch secondary face every 5 minutes
+ weather = self.others[0].api_weather.show_weather([47.3769, 8.5417]) # zürich
- if weather != self.weather_raw and len(weather) != 0:
- td = weather[1]
+ if weather != self.weather_raw and len(weather) != 0:
+ td = weather[1]
- low = td["temps"][0]
- high = td["temps"][1]
- self.weather["weather"] = td["short"]
- self.weather["high"] = high
- self.weather["low"] = low
- elif len(weather) == 0:
- self.weather["weather"] = "error"
- self.weather["high"] = "error"
- self.weather["low"] = "error"
- # if weather == self.weather.raw do nothing
+ low = td["temps"][0]
+ high = td["temps"][1]
+ self.weather["weather"] = td["short"]
+ self.weather["high"] = high
+ self.weather["low"] = low
+ elif len(weather) == 0:
+ self.weather["weather"] = "error"
+ self.weather["high"] = "error"
+ self.weather["low"] = "error"
+ # if weather == self.weather.raw do nothing
- if self.weather["show"] == "weather":
- next = "temps"
- else:
- next = "weather"
- self.weather["show"] = next
+ if self.weather["show"] == "weather":
+ next = "temps"
+ else:
+ next = "weather"
+ self.weather["show"] = next
- self.prev_time = datetime.datetime.now().strftime("%H%M")
+ self.prev_time = datetime.datetime.now().strftime("%H%M")
- self.own.set_face(self.weather)
-
- super().mainloop(sleep_delta,perform_loop)
+ self.own.set_face(self.weather)
+
+ RepeatedTimer(60, perform_loop)
+
@@ -78,23 +77,7 @@ class BotWrapper(Wrapper):
def __init__(self, own_module, *other_modules):
""""""
super().__init__(own_module, *other_modules)
-
- self.bot = own_module
- self.clock = other_modules[0]
-
- self.mainloop(10)
-
-
- def mainloop(self, sleep_delta):
- """Calls the telegram entity regularly to check for activity"""
- def perform_loop():
- self.bot.react_chats()
- # num = self.bot.telegram.fetch_updates()
- # for message in range(num):
- # command, params = self.bot.react_command() # returns None if handled internally
- # if command != None:
- # self.clock.external_action(command, params)
- super().mainloop(sleep_delta, perform_loop)
+ self.own.START()
@@ -104,5 +87,34 @@ class DashBoardWrapper(Wrapper):
super().__init__(own_module, other_modules)
# self.mainloop(1 * 3600) # 1 hour refresh-cycle
# cannot get called through mainloop, will use the included callback-functionality of Dash
- own_module.bot = other_modules[0]
- own_module.launch_dashboard()
\ No newline at end of file
+ self.own.bot = other_modules[0]
+ self.own.START()
+
+
+
+class RepeatedTimer(object):
+ def __init__(self, interval, function, *args, **kwargs):
+ self._timer = None
+ self.interval = interval
+ self.function = function
+ self.args = args
+ self.kwargs = kwargs
+ self.is_running = False
+ self.next_call = time.time()
+ self.start()
+
+ def _run(self):
+ self.is_running = False
+ self.start()
+ self.function(*self.args, **self.kwargs)
+
+ def start(self):
+ if not self.is_running:
+ self.next_call += self.interval
+ self._timer = threading.Timer(self.next_call - time.time(), self._run)
+ self._timer.start()
+ self.is_running = True
+
+ def stop(self):
+ self._timer.cancel()
+ self.is_running = False