#!/usr/bin/env python3 """ Behavior: A qutebrowser userscript that adds recipes to Nextcloud's Cookbook app. Requirements: requests userscript setup: Optionally create ~/.config/qutebrowser/add-nextcloud-cookbook.ini like: [nextcloud] HOST=https://nextcloud.example.com USER=username ;PASSWORD=lamepassword If settings aren't in the configuration file, the user will be prompted. If the user does not want to be prompted for a password, it is recommended to set up an 'app password' with 'Allow filesystem access' enabled. See the following for instructions: https://docs.nextcloud.com/server/latest/user_manual/en/session_management.html#managing-devices # noqa: E501 qutebrowser setup: add recipe via hints config.bind('X', 'hint links userscript add-nextcloud-cookbook') add recipe of current URL config.bind('X', 'spawn --userscript add-nextcloud-cookbook') troubleshooting: Errors detected within this userscript will have an exit of 231. All other exit codes will come from requests. """ import configparser from os import environ, path from sys import argv, exit from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit from requests import post from requests.auth import HTTPBasicAuth def get_text(name, info): """Get input from the user.""" _app = QApplication(argv) # noqa: F841 if name == "password": text, ok = QInputDialog.getText( None, "add-nextcloud-cookbook userscript", "Please enter {}".format(info), QLineEdit.Password, ) else: text, ok = QInputDialog.getText( None, "add-nextcloud-cookbook userscript", "Please enter {}".format(info) ) if not ok: message("info", "Dialog box canceled.") exit(0) return text def message(level, text): """display message""" with open(environ["QUTE_FIFO"], "w") as fifo: fifo.write( "message-{} 'add-nextcloud-cookbook userscript: {}'\n".format(level, text) ) fifo.flush() if "QUTE_FIFO" not in environ: print( "This script is designed to run as a qutebrowser userscript, " "not as a standalone script." ) exit(231) if "QUTE_CONFIG_DIR" not in environ: if "XDG_CONFIG_HOME" in environ: QUTE_CONFIG_DIR = environ["XDG_CONFIG_HOME"] + "/qutebrowser" else: QUTE_CONFIG_DIR = environ["HOME"] + "/.config/qutebrowser" else: QUTE_CONFIG_DIR = environ["QUTE_CONFIG_DIR"] config_file = QUTE_CONFIG_DIR + "/add-nextcloud-cookbook.ini" if path.isfile(config_file): config = configparser.ConfigParser() config.read(config_file) settings = dict(config.items("nextcloud")) else: settings = {} settings_info = [ ("host", "host information.", "required"), ("user", "username.", "required"), ("password", "password.", "required"), ] # check for settings that need user interaction for setting in settings_info: if setting[0] not in settings: userInput = get_text(setting[0], setting[1]) settings[setting[0]] = userInput api_url = settings["host"] + "/index.php/apps/cookbook/import" headers = {"Content-Type": "application/x-www-form-urlencoded"} auth = HTTPBasicAuth(settings["user"], settings["password"]) data = "url=" + environ["QUTE_URL"] message("info", "starting to process {}".format(environ["QUTE_URL"])) r = post(api_url, data=data, headers=headers, auth=auth, timeout=(3.05, 27)) if r.status_code == 200: message("info", "recipe from {} added.".format(environ["QUTE_URL"])) exit(0) elif r.status_code == 500: message("warning", "Cookbook app reports {}".format(r.text)) exit(0) else: message( "error", "Could not connect to {} with status code {}".format( settings["host"], r.status_code ), ) exit(r.status_code)