Source code for chat_archive.utils

# Easy to use offline chat archive.
#
# Author: Peter Odding <peter@peterodding.com>
# Last Change: August 1, 2018
# URL: https://github.com/xolox/python-chat-archive

"""Utility functions for the `chat-archive` program."""

# Standard library modules.
import datetime
import getpass
import logging
import os
import pwd
import time

# External dependencies.
from humanfriendly import format
from qpass import PasswordStore

# Initialize a logger for this module.
logger = logging.getLogger(__name__)


[docs]def ensure_directory_exists(pathname): """ Create a directory if it doesn't exist yet. :param pathname: The pathname of the directory (a string). """ if not os.path.isdir(pathname): os.makedirs(pathname)
[docs]def get_full_name(): """ Find the full name of the current user on the local system based on ``/etc/passwd``. :returns: A string with the full name of the current user or an empty string when this information is not available. """ try: entry = pwd.getpwuid(os.getuid()) gecos = entry.pw_gecos.split(",") return gecos[0] except Exception: return ""
[docs]def get_secret(options, value_option, name_option, description): """ Get a secret needed to connect to a chat service (like a password or API token). :param options: A dictionary with configuration options. :param value_option: The name of the configuration option that defines the value of a secret (a string). :param name_option: The name of the configuration option that defines the name of a secret in ``~/.password-store`` (a string). See also :func:`get_secret_from_store()`. :param description: A description of the type of secret that the operator will be prompted for (a string). :returns: The password (a string). """ if value_option in options: logger.debug("Getting password from configuration option %r ..", value_option) return options[value_option] elif name_option in options: logger.debug("Getting password from password store (%s) ..", options[name_option]) return get_secret_from_store(name=options[name_option], directory=options.get("password-store")) else: logger.debug("Prompting operator for interactive password entry ..") return prompt_for_password("Please enter %s: " % description)
[docs]def get_secret_from_store(name, directory=None): """ Use :mod:`qpass` to get a secret from ``~/.password-store``. :param name: The name of a password or a search pattern that matches a single entry in the password store (a string). :param directory: The directory to use (a string, defaults to ``~/.password-store``). :returns: The secret (a string). :raises: :exc:`exceptions.ValueError` when the given `name` doesn't match any entries or matches multiple entries in the password store. """ kw = dict(directory=directory) if directory else {} store = PasswordStore(**kw) matches = store.smart_search(name) if len(matches) != 1: msg = "Expected exactly one match in password database! (input: %s)" raise ValueError(format(msg, name)) return matches[0].password
[docs]def prompt_for_password(prompt_text): """Interactively prompt the operator for a password.""" return getpass.getpass(prompt_text)
[docs]def utc_to_local(utc_value): """Convert a UTC :class:`~datetime.datetime` object to the local timezone.""" epoch = time.mktime(utc_value.timetuple()) offset = datetime.datetime.fromtimestamp(epoch) - datetime.datetime.utcfromtimestamp(epoch) return utc_value + offset