Skip to content

Commit

Permalink
move configuration file to XDG_CONFIG_HOME
Browse files Browse the repository at this point in the history
  • Loading branch information
scarlehoff committed Oct 29, 2023
1 parent 76659d0 commit 97c7d90
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 78 deletions.
157 changes: 95 additions & 62 deletions src/pybliotecario/argument_parser.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
"""
Wrapper for argument parser and initialization
Wrapper for the argument parser and the initialization
"""
import os
from argparse import Action, ArgumentParser, ArgumentTypeError
import configparser
import glob
import pathlib
import importlib
import configparser
from argparse import ArgumentParser, Action, ArgumentTypeError
import os
from pathlib import Path

CONFIG_FILE = "pybliotecario.ini"
from .customconf import default_config_path, default_data_path


def validpath(value):
"""Check whether the received path is valid"""
path = pathlib.Path(value)
path = Path(value)
if not path.exists():
raise ArgumentTypeError(f"The file '{value}' can't be found")
if path.is_dir():
Expand Down Expand Up @@ -74,46 +74,63 @@ def config_module(module):
return dict_list


def configure_telegram(main_folder):
"""Configure Telegram"""
def configure_telegram():
"""Configure pybliotecario to use the Telegram API
This function walks the user through the process of creating a new bot
and storing the token and the chat_id of the user in the configuration file
"""
# Initialize the bot in telegram
print(
"""Welcome to The Wizard!
The first thing you will need is an authorization token from the botfather.
If you don't know how to get one, read here: https://core.telegram.org/bots#6-botfather"""
The first thing you will need is to create a new bot with the botfather in your telegram client.
Alternatively, you can get use a previously created bot.
Ask the botfather for an authorization token before continuing.
The instructions on how to get the token can be found here: https://core.telegram.org/bots#how-do-i-create-a-bot
"""
)
token = input("Authorization token: ")
print("Thanks, let's test this out. Say something to your bot")

# Try to fire up the bot with the given token
from pybliotecario.backend import TelegramUtil
max_timeouts = 20
tim = 0
from pybliotecario.backend.telegram_util import TelegramMessage

teleAPI = TelegramUtil(token, timeout=20)
while True:
all_updates = teleAPI.raw_updates()
from pybliotecario.backend.telegram_util import TelegramMessage
telegram_API = TelegramUtil(token, timeout=20)
print("Thanks, let's test this out. Say something (anything!) to your bot in telegram")

try:
update = TelegramMessage(all_updates[0])
except IndexError as e:
print("Timeout... waiting for updates again...")
tim += 1
if tim > max_timeouts:
raise e
continue
print(f"Message received: {update.text}")
yn = input("Was this your msg? [y/n] ")
if yn.lower() in ("y", "s"):
chat_id = update.chat_id
print(f"Your chat id is: {chat_id} and your username is: {update.username}")
for _ in range(20): # Allow for 20 tries
all_updates = telegram_API.raw_updates()

for update in all_updates:
msg = TelegramMessage(update)
print(f"Message received: {msg.text}")
yn = input("Was this your msg? [y/n] ")
if msg_found := yn.lower().startswith(("y", "s")):
chat_id = msg.chat_id
username = msg.username
print(f"Your chat id is: {chat_id} and your username: {username}")
break

if msg_found:
break
print("Try again")

yn = input("Do you want to enable the 'chivato' mode, so you get a warning if anyone other than you tries to use the bot? [yn] ")
chivato = yn.lower() in ("y", "s")
print("Please, try again")

else:
raise ValueError("There was some problem with the configuration of Telegram")

# Fill the DEFAULT options
config_dict = {"DEFAULT": {"TOKEN": token, "chat_id": chat_id, "main_folder": main_folder, "chivato": chivato}}
yn = input(
"Do you want to enable the 'chivato' mode, so you get a warning if anyone other than you tries to use the bot? [yn] "
)
chivato = yn.lower().startswith(("y", "s"))

# Now prepare the dictionary with the DEFAULT field of the config file
config_dict = {
"DEFAULT": {
"TOKEN": token,
"chat_id": chat_id,
"chivato": chivato,
}
}
return config_dict


Expand All @@ -130,11 +147,13 @@ def configure_all():
module_components = components.__name__
modules = glob.glob(folder_components + "/*.py")
for module_file in modules:
module_name = pathlib.Path(module_file).with_suffix("").name
module_name = Path(module_file).with_suffix("").name
try:
module = importlib.import_module(f"{module_components}.{module_name}")
except ModuleNotFoundError as e:
print(f"In order to use the component '{module_name}' it is necessary to install its dependencies")
print(
f"In order to use the component '{module_name}' it is necessary to install its dependencies"
)
missing_dependencies = True
continue
dict_list = config_module(module)
Expand All @@ -143,11 +162,12 @@ def configure_all():
config_dict[key] = item

if missing_dependencies:
print("""To install missing dependencies for a particular component you can install them explicitly:
print(
"""To install missing dependencies for a particular component you can install them explicitly:
~$ pip install pybliotecario[component]
or install all dependencies with
~$ pip install pybliotecario[full]""")

~$ pip install pybliotecario[full]"""
)

return config_dict

Expand All @@ -163,36 +183,49 @@ class InitAction(Action):
def __init__(self, nargs=0, **kwargs):
super().__init__(nargs=nargs, **kwargs)

def __call__(self, parser, *args, **kwargs):
def __call__(self, parser, namespace, *args, **kwargs):
"""
Configures the pybliotecario by first
configuring Telegram
and then calling the configure_me method of all components
Configures the pybliotecario by first configuring the Telegram API
and then calling the ``configure_me`` method of all components
"""
config_dict = {}
# Set up environmental stuff
home = os.environ["HOME"]
main_folder = home + "/.pybliotecario/"
os.makedirs(main_folder, exist_ok=True)
config_file = home + "/." + CONFIG_FILE
# Check whether a config file already exists
config_exists = os.path.isfile(config_file)
# If it does, you might not want to reconfigure Telegram, so let's ask
initialize = True
if config_exists:

config_path = default_config_path()
data_folder = default_data_path()

if namespace.config_file is not None:
print(
"""It seems pybliotecario's Telegram capabilities
have already been configured in this computer"""
f"WARNING! You are setting {namespace.config_file} as the configuration file instead of the default {config_path}"
)
yn = input("Do you want to configure it again? [y/n] ")
yn = input("Are you sure? Do you want to continue [y/n] ")
if not yn.lower().startswith(("y", "s")):
initialize = False
print("Don't use --config_file with --init to avoid this!")
parser.exit(0)
config_path = namespace.config_file
data_folder = Path(input(f"Where do you want your data? e.g.: {data_folder}: "))

print(f"Note: configuration will be written to {config_path}")

# Make sure the folders exist
data_folder.mkdir(exist_ok=True, parents=True)
config_path.parent.mkdir(exist_ok=True, parents=True)

# Check whether a config file already exists, if it does, ask before doing the Telegram
initialize = True
if config_exists := config_path.exists():
print("It seems pybliotecario has already been initialized in this computer")
yn = input("Do you want to start the configuration from scratch? [y/n] ")
initialize = yn.lower().startswith(("y", "s"))

if initialize:
config_dict.update(configure_telegram(main_folder))
print("Let's loop over the pybliotecario modules to configure their options")
config_dict.update(configure_telegram())
config_dict["DEFAULT"]["main_folder"] = data_folder

print("Now let's loop over all pybliotecario modules to configure their options")
config_dict.update(configure_all())
# And finally write the config file
write_config(config_dict, config_file, config_exists=config_exists)
write_config(config_dict, config_path, config_exists=config_exists)
print(f"The configuration of the pybliotecario has been written to: {config_path}")
parser.exit(0)


Expand Down
3 changes: 2 additions & 1 deletion src/pybliotecario/backend/basic_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ class Backend(ABC):

@abstractmethod
def _get_updates(self, not_empty=False):
"""Retrieve updates"""
"""Retrieve updates as a list"""
return []

def raw_updates(self):
"""Returns a raw version of the updates as implemented by the child class"""
Expand Down
7 changes: 5 additions & 2 deletions src/pybliotecario/components/component_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import sys
import logging

from pybliotecario.argument_parser import CONFIG_FILE
from ..customconf import default_config_path

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -54,7 +54,10 @@ def __init__(

def update_config(self):
"""Updates default ($HOME/.CONFIG_FILE) configuration file"""
default_config = "{0}/.{1}".format(os.environ.get("HOME"), CONFIG_FILE)
if self.configuration is not None:
print("Sorry, I forgot this set_trace here, my bad")
import ipdb; ipdb.set_trace()
default_config = default_config_path()
new_section = self.configure_me()
for key, item in new_section.items():
self.configuration[key] = item
Expand Down
2 changes: 1 addition & 1 deletion src/pybliotecario/components/github_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def configure_me(cls):
except ValueError:
print("Only integer numbers accepted")
dict_out = {
self.key_name: {
cls.key_name: {
"token": access_token,
"since_hours": hours,
}
Expand Down
15 changes: 7 additions & 8 deletions src/pybliotecario/components/wiki.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,22 @@ def __init__(self, telegram_object, configuration=None, **kwargs):

@classmethod
def configure_me(cls):
print("")
print(" # Wikipedia Module ")
print("This is the configuration helper for the wikipedia module")
print("Introduce the length of the msgs you want to obtain: ")
print(f"""
# Wikipedia Module
This is the configuration helper for the wikipedia module
Introduce the length of the msgs you want to obtain (max: {MAX_SIZE}):""")
summary_size = 0
while summary_size > MAX_SIZE or summary_size < 1:
# The max msg in telegram is 4096 UTF char.
try:
summary_size = int(input(" Max: {0} > ".format(MAX_SIZE)))
summary_size = int(input(f" > ".format(MAX_SIZE)))
except ValueError:
print("Please, write a number between 0 and {0}".format(MAX_SIZE))
print(f"Please, write a number between 0 and {MAX_SIZE}")
possible_languages = ["EN", "ES", "IT"]
language = None
print("Introduce the default language for Wikipedia pages")
while language not in possible_languages:
print("Possible choices: {0}".format(", ".join(possible_languages)))
language = input(" > (default: {0}".format(DEFAULT_LANGUAGE))
language = input(f" > Possible choices: {possible_languages} (default: {DEFAULT_LANGUAGE}) > ")
if language == "":
language = DEFAULT_LANGUAGE
dict_out = {
Expand Down
21 changes: 21 additions & 0 deletions src/pybliotecario/customconf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
"""
Define custom parsers for the config reader
and default data/config locations
"""
from configparser import ConfigParser
from copy import copy
from os import environ
from pathlib import Path

NAME = "pybliotecario"


def default_config_path(name=NAME):
"""Return the default config path looking at the value of XDG_CONFIG_HOME
Usually: $HOME/.config/pybliotecario/pybliotecario.ini
"""
config_folder = Path(environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
return config_folder / name / "pybliotecario.ini"


def default_data_path(name=NAME):
"""Return the default data path looking at the value of XDG_DATA_HOME
Usually: $HOME/.local/share/pybliotecario
"""
data_folder = Path(environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
return data_folder / name


def _parse_chat_id(value):
Expand Down
16 changes: 12 additions & 4 deletions src/pybliotecario/pybliotecario.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@

from pybliotecario.backend import TelegramUtil, TestUtil, FacebookUtil
from pybliotecario.core_loop import main_loop
from pybliotecario.customconf import CustomConfigParser
from pybliotecario.customconf import CustomConfigParser, default_config_path

# Modify argument_parser.py to read new arguments
from pybliotecario.argument_parser import parse_args, CONFIG_FILE
from pybliotecario.argument_parser import parse_args
import pybliotecario.on_cmdline as on_cmdline

logger = logging.getLogger()


def read_config(config_file=None):
"""Reads the pybliotecario config file and uploads the global configuration"""
config_files = [Path.home() / f".{CONFIG_FILE}", CONFIG_FILE]
"""Reads the pybliotecario config file and uploads 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()
# Checks as well (with lower priority) for ~/.pybliotecario.ini for backwards compatibility
old_path = Path.home() / ".pybliotecario.ini"
config_files = [old_path, default_file_path, default_file_path.name]
if old_path.exists():
logger.error(f"Deprecation notice: ~/.pybliotecario.ini is now deprecated, please move the configuration to {default_file_path}")

if config_file is not None:
config_files.append(config_file)
config = CustomConfigParser()
Expand Down

0 comments on commit 97c7d90

Please sign in to comment.