diff --git a/src/pybliotecario/argument_parser.py b/src/pybliotecario/argument_parser.py index e1a1ec7..f36341c 100644 --- a/src/pybliotecario/argument_parser.py +++ b/src/pybliotecario/argument_parser.py @@ -1,6 +1,7 @@ """ Wrapper for the argument parser and the initialization """ + from argparse import Action, ArgumentParser, ArgumentTypeError import configparser import glob @@ -93,7 +94,7 @@ def configure_telegram(): token = input("Authorization token: ") # Try to fire up the bot with the given token - telegram_API = TelegramUtil(token, timeout=20) + telegram_API = TelegramUtil(token=token, timeout=20) print("Thanks, let's test this out. Say something (anything!) to your bot in telegram") for _ in range(20): # Allow for 20 tries diff --git a/src/pybliotecario/backend/basic_backend.py b/src/pybliotecario/backend/basic_backend.py index 8bb001a..43ae94d 100644 --- a/src/pybliotecario/backend/basic_backend.py +++ b/src/pybliotecario/backend/basic_backend.py @@ -8,6 +8,7 @@ """ from abc import ABC, abstractmethod +from configparser import ConfigParser import json import logging import urllib @@ -160,7 +161,14 @@ class Backend(ABC): """ - _max_size = 99999 + def __init__(self, config=None, debug=False, **kwargs): + if config is None: + # If no config is passed, generate and empty one + config = ConfigParser() + self._max_size = 99999 + self._quiet = config.getboolean("DEFAULT", "quiet", fallback=False) + self._config = config + self._debug = debug @abstractmethod def _get_updates(self, not_empty=False): @@ -175,6 +183,12 @@ def raw_updates(self): def send_message(self, text, chat, **kwargs): """Sends a message to the chat""" + def send_quiet_message(self, text, chat, **kwargs): + """Like ``send_message`` but only sends the message if quiet is set to False + otherwise, do nothing""" + if not self._quiet: + return self.send_message(text, chat, **kwargs) + @property @abstractmethod def _message_class(self): diff --git a/src/pybliotecario/backend/facebook_util.py b/src/pybliotecario/backend/facebook_util.py index 65c2f93..ac5329c 100644 --- a/src/pybliotecario/backend/facebook_util.py +++ b/src/pybliotecario/backend/facebook_util.py @@ -79,20 +79,31 @@ class FacebookUtil(Backend): _message_class = FacebookMessage _max_size = MAX_SIZE - def __init__(self, PAGE_TOKEN, VERIFY_TOKEN, host="0.0.0.0", port=3000, debug=False): + def __init__(self, config=None, host="0.0.0.0", port=3000, **kwargs): + super().__init__(config, **kwargs) + if config is None: + raise ValueError("A configuration with a FACEBOOK section must be given") + + try: + fb_config = config["FACEBOOK"] + except KeyError: + raise ValueError("No facebook section found for facebook in pybliotecario.ini") + if not _HAS_FLASK: # Raise the error now - raise ModuleNotFoundError("No module named 'flask'") + raise ModuleNotFoundError("No module named 'flask', needed for Facebook backend") + + verify_token = fb_config.get("verify") + page_token = fb_config.get("app_token") - self.page_access_token = PAGE_TOKEN - self.verify_token = VERIFY_TOKEN + self.page_access_token = page_token + self.verify_token = verify_token self.port = port self.host = host app = Flask(__name__) # Load the listener into the webhook endpoint app.add_url_rule("/webhook", "webhook", self.listener, methods=["POST", "GET"]) self.flask_app = app - self.debug = debug self.action_function = None self.auth = {"access_token": self.page_access_token} @@ -121,7 +132,7 @@ def act_on_updates(self, action_function, not_empty=False): opens the webhook to wait ofr updates and act on them """ self.action_function = action_function - self.flask_app.run(host=self.host, port=self.port, debug=self.debug) + self.flask_app.run(host=self.host, port=self.port, debug=self._debug) def _get_updates(self, not_empty=False): """This class skips get_updates and uses act_on_updates directly""" @@ -184,8 +195,14 @@ def send_file(self, filepath, chat): if __name__ == "__main__": + from configparser import ConfigParser logger.info("Testing FB Util") verify = "your_verify_token" app_token = "your_app_key" - fb_util = FacebookUtil(app_token, verify, debug=True) + config = ConfigParser() + config["FACEBOOK"] = { + "verify": verify, + "app_token": app_token + } + fb_util = FacebookUtil(config, debug=True) fb_util.act_on_updates(lambda x: print(x)) diff --git a/src/pybliotecario/backend/telegram_util.py b/src/pybliotecario/backend/telegram_util.py index 1cb804d..9ec2ab3 100644 --- a/src/pybliotecario/backend/telegram_util.py +++ b/src/pybliotecario/backend/telegram_util.py @@ -106,13 +106,18 @@ class TelegramUtil(Backend): _message_class = TelegramMessage - def __init__(self, TOKEN, debug=False, timeout=300): + def __init__(self, config=None, token=None, timeout=300, **kwargs): + super().__init__(config, **kwargs) + if token is None: + if config is None: + raise ValueError("Either a config or a token must be provided for Telegram") + token = config.defaults().get("token") + self.offset = None - self.debug = debug self.timeout = timeout # Build app the API urls - base_URL = TELEGRAM_URL + f"bot{TOKEN}/" - self.base_fileURL = TELEGRAM_URL + f"file/bot{TOKEN}/" + base_URL = TELEGRAM_URL + f"bot{token}/" + self.base_fileURL = TELEGRAM_URL + f"file/bot{token}/" self.send_msg = base_URL + "sendMessage" self.send_img = base_URL + "sendPhoto" self.send_doc = base_URL + "sendDocument" @@ -185,7 +190,7 @@ def _get_updates(self, not_empty=False): if not updates and not_empty: return self._get_updates(not_empty=True) - if self.debug: + if self._debug: logger.info("Request url: %s", url) logger.info("Obtained updates: %s", updates) @@ -254,7 +259,7 @@ def download_file(self, file_id, file_path): if __name__ == "__main__": logger.info("Testing TelegramUtil") token = "must put a token here to test" - ut = TelegramUtil(token, debug=True) + ut = TelegramUtil(token=token, debug=True) # noinspection PyProtectedMember results = ut._get_updates() for res in results: diff --git a/src/pybliotecario/core_loop.py b/src/pybliotecario/core_loop.py index 42076e1..f66ff3d 100644 --- a/src/pybliotecario/core_loop.py +++ b/src/pybliotecario/core_loop.py @@ -2,6 +2,7 @@ This module manages the core loop of the pybliotecario when it is called with daemon mode -d """ + from datetime import datetime import logging from pathlib import Path @@ -119,7 +120,7 @@ def act_on_message(message): # Otherwise just save the msg to the log and send a funny reply _write_to_daily_log(main_folder, message.text) random_msg = still_alive() - tele_api.send_message(random_msg, chat_id) + tele_api.send_quiet_message(random_msg, chat_id) except_counter = 0 except Exception as e: logger.error(f"This message produced an exception: {e}") diff --git a/src/pybliotecario/customconf.py b/src/pybliotecario/customconf.py index 99cc354..be56e8b 100644 --- a/src/pybliotecario/customconf.py +++ b/src/pybliotecario/customconf.py @@ -2,6 +2,7 @@ Define custom parsers for the config reader and default data/config locations """ + from configparser import ConfigParser from copy import copy from os import environ diff --git a/src/pybliotecario/on_cmd_message.py b/src/pybliotecario/on_cmd_message.py index 5b2666b..598023b 100644 --- a/src/pybliotecario/on_cmd_message.py +++ b/src/pybliotecario/on_cmd_message.py @@ -6,6 +6,7 @@ This is a design choice as this way it is not necessary to have all dependencies if you want to run only some submodules of the pybliotecario. """ + import importlib import logging diff --git a/src/pybliotecario/pybliotecario.py b/src/pybliotecario/pybliotecario.py index d722086..a092468 100755 --- a/src/pybliotecario/pybliotecario.py +++ b/src/pybliotecario/pybliotecario.py @@ -18,7 +18,7 @@ def read_config(config_file=None): - """Reads the pybliotecario config file and uploads the global configuration + """Reads the pybliotecario config file and updates the global configuration By default looks always in the default file path (in XDG_CONFIG_HOME) and the current folder """ default_file_path = default_config_path() @@ -107,19 +107,13 @@ def main(cmdline_arg=None, tele_api=None, config=None): ) sys.exit(-1) - tele_api = TelegramUtil(api_token, debug=args.debug) + tele_api = TelegramUtil(config=config, debug=args.debug) elif args.backend.lower() == "test": tele_api = TestUtil("/tmp/test_file.txt") elif args.backend.lower() == "facebook": - try: - fb_config = config["FACEBOOK"] - except KeyError: - raise ValueError("No facebook section found for facebook in pybliotecario.ini") - verify_token = fb_config.get("verify") - app_token = fb_config.get("app_token") - tele_api = FacebookUtil(app_token, verify_token, debug=args.debug) + tele_api = FacebookUtil(config=config, debug=args.debug) # Check whether we have chat id - chat_id = fb_config.get("chat_id") + chat_id = config["FACEBOOK"].get("chat_id") if chat_id is not None: config.set("DEFAULT", "chat_id", chat_id)