From 52c48a11f02238471db620118ec2528afd4b870d Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Sun, 3 Oct 2021 00:01:54 +0300 Subject: [PATCH] Planet reader. This is something I wanted to do for a long time. Basically it's a nice UI for me to browse some of the sites I frequent (sort of a news reader), but geared towards a terminal. So this will just open a terminal web browser (although a graphical browser can be used just as well) with one of the configured sites so I don't have to type much. There's tab completion for even less typing. Honestly, it's been on my todo list from the days I used to carry with me my PocketCHIP and I could have just deleted this task a long time ago. But I recently learned about Python's webbrowser module and I wanted to try it out. I also learned how to do tab completion in Bash so that's nice too. --- .bash_completion.d/planet | 25 +++++++++ .config/planet/config.yaml | 25 +++++++++ Documents/bin/planet | 106 +++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 .bash_completion.d/planet create mode 100644 .config/planet/config.yaml create mode 100755 Documents/bin/planet diff --git a/.bash_completion.d/planet b/.bash_completion.d/planet new file mode 100644 index 0000000..756b4f4 --- /dev/null +++ b/.bash_completion.d/planet @@ -0,0 +1,25 @@ +# vim: ft=bash + +_planet () { + local cur prev words cword opts + _init_completion || return + opts='-h --help -l --list -C --config -b --browser' + browsers='mozilla firefox netscape galeon epiphany skipstone kfmclient + konqueror kfm mosaic opera grail links elinks lynx w3m windows-default + macosx safari google-chrome chrome chromium chromium-browser' + + if [[ $prev == -C ]] || [[ $prev == --config ]] + then + _filedir '*.yaml|yml' + elif [[ $prev == -b ]] || [[ $prev == --browser ]] + then + COMPREPLY=($(compgen -W "$browsers" -- "$cur")) + elif [[ $cur == -* ]] + then + COMPREPLY=($(compgen -W "$opts" -- "$cur")) + else + COMPREPLY=($(compgen -W "$(planet -l)" -- "$cur")) + fi +} + +complete -F _planet planet diff --git a/.config/planet/config.yaml b/.config/planet/config.yaml new file mode 100644 index 0000000..8866675 --- /dev/null +++ b/.config/planet/config.yaml @@ -0,0 +1,25 @@ +--- +general: + # For the list of available browsers, consult + # https://docs.python.org/3/library/webbrowser.html?highlight=webbrowser#webbrowser.register + browser: w3m +sites: + shore: https://www.shore.co.il/blog/ + #openbsd: https://openbsdnow.org/ + openbsd: https://undeadly.org/ + fedora: http://fedoraplanet.org/ + debian: https://planet.debian.org/ + gnome: http://planet.gnome.org/ + kde: http://planetkde.org/ + python: http://planetpython.org/ + freedesktop: http://planet.freedesktop.org/ + freebsd: http://planet.freedesktop.org/ + kernel: http://planet.kernel.org/ + foss-il: http://planet.hamakor.org.il/ + sysadmin: http://planetsysadmin.com/ + fsf: http://www.fsf.org/blogs/recent-blog-posts + hn: https://news.ycombinator.com/ + phoronix: https://www.phoronix.com/scan.php?page=home + openbsd-current: http://www.openbsd.org/faq/current.html + debian-transitions: https://release.debian.org/transitions/index.html + lobsters: https://lobste.rs/ diff --git a/Documents/bin/planet b/Documents/bin/planet new file mode 100755 index 0000000..f83e5f9 --- /dev/null +++ b/Documents/bin/planet @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Planets and aggregators reader.""" + +import argparse +import pathlib +import sys +import webbrowser +import xdg.BaseDirectory # pylint: disable=import-error +import yaml + + +EXAMPLE_CONFIG = """--- +general: + # For the list of available browsers, consult + # https://docs.python.org/3/library/webbrowser.html?highlight=webbrowser#webbrowser.register + browser: w3m +sites: + shore: https://www.shore.co.il/blog/ +""" + + +def build_arg_parser(): + """Build and return the argument parser.""" + parser = argparse.ArgumentParser(description=__doc__) + + parser.add_argument( + "-C", + "--config", + type=pathlib.Path, + help="Use a different configuration file.", + ) + parser.add_argument( + "-l", "--list", action="store_true", help="List available sites." + ) + parser.add_argument("-b", "--browser", help="Use a specific browser.") + parser.add_argument("site", nargs="?", help="Name of the site to open.") + return parser + + +def get_config(path=None): + """Returns a configuration dictionary. + + If a path is passed, that file path is used. Otherwise, use the default + path. If path is None and the default doesn't exist, an example file is + created.""" + if path: + path = path.expanduser() + if not path.exists() and not path.is_file(): + arg_parser.error("Configuration file does not exist.") + + else: + base_dir = pathlib.Path(xdg.BaseDirectory.save_config_path("planet")) + path = base_dir / "config.yaml" + if not path.exists(): + with open(path, "w", encoding="utf-8") as configfile: + configfile.write(EXAMPLE_CONFIG) + arg_parser.error( + f"Missing config file, generated an example one at {path}." + ) + try: + with open(path, "r", encoding="utf-8") as configfile: + config = yaml.safe_load(configfile) + except Exception as exception: # pylint: disable=broad-except + arg_parser.error(str(exception)) + if "sites" not in config or not isinstance(config["sites"], dict): + arg_parser.error( + "Config file missing 'sites' key or 'sites' is not a dictionary." + ) + return config + + +def list_sites(config): + """Prints a list of sites from the config.""" + for site in config["sites"].keys(): + print(site) + + +def open_site(config, site, browser_name=None): + """Opens a site from the config in a webbrowser.""" + if browser_name == "": + browser_name = None + elif browser_name is not None: + pass + elif "general" in config and "browser" in config["general"]: + browser_name = config["general"]["browser"] + else: + browser_name = None + browser = webbrowser.get(using=browser_name) + if site not in config["sites"]: + arg_parser.error(f"Unknown site {site}.") + browser.open(config["sites"][site]) + + +if __name__ == "__main__": + arg_parser = build_arg_parser() + args = arg_parser.parse_args() + if not args.list and not args.site: + arg_parser.error( + "You must specify either site name or -l to list the available sites." # noqa: E501 + ) + conf = get_config(args.config) + if args.list: + list_sites(conf) + else: + open_site(conf, args.site, args.browser) + sys.exit() -- GitLab