2022-05-19 15:10:04 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
"""Bangs in DDG style without using DDG but just Rofi.
|
|
|
|
|
|
|
|
Load a JSON config file to have the bangs available. Rofi should return, using
|
|
|
|
its dmenu option, the handle followed by your query, e.g. "w burger" if you
|
|
|
|
want a Wikipedia article for "burger", or at least a much needed
|
|
|
|
disambiguation…
|
|
|
|
|
|
|
|
Config file example:
|
2022-08-28 15:46:14 +02:00
|
|
|
{
|
|
|
|
"bangs": [
|
|
|
|
{
|
|
|
|
"handle": "w",
|
|
|
|
"name": "Wikipedia",
|
|
|
|
"url": "https://en.wikipedia.org/wiki/Special:Search?search={}&go=Go"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
Optionally, a bang object can contain a "raw" key with a boolean value: if
|
|
|
|
true, arguments are passed without being URL-encoded.
|
2022-05-19 15:10:04 +02:00
|
|
|
|
|
|
|
By default this scripts attempts to load your config file from
|
2022-09-30 19:04:26 +02:00
|
|
|
`~/.config/bangs.json`, but you can specify the BANGS_CONFIG_PATH
|
2022-05-19 15:10:04 +02:00
|
|
|
environment variable or pass the path through the -c command-line option.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import urllib.parse
|
|
|
|
import webbrowser
|
|
|
|
|
|
|
|
|
|
|
|
def load_config(config_path=None):
|
|
|
|
if config_path is None:
|
2022-10-26 19:02:44 +02:00
|
|
|
config_path = (
|
|
|
|
os.environ.get("BANGS_CONFIG_PATH")
|
|
|
|
or os.path.expanduser("~/.config/bangs.json")
|
2022-05-19 15:10:04 +02:00
|
|
|
)
|
|
|
|
try:
|
|
|
|
with open(config_path, "rt") as bangs_file:
|
|
|
|
return json.load(bangs_file)
|
|
|
|
except OSError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def list_bangs(config):
|
|
|
|
for item in config["bangs"]:
|
|
|
|
name = item["name"]
|
|
|
|
handle = item["handle"]
|
2022-10-26 19:02:44 +02:00
|
|
|
print(f"- {handle}: {name}")
|
2022-05-19 15:10:04 +02:00
|
|
|
|
|
|
|
|
2022-05-20 11:39:34 +02:00
|
|
|
def run_rofi(config, input_text="", title="bang"):
|
|
|
|
rofi_path = config.get("rofi_path", "rofi")
|
|
|
|
completed_process = subprocess.run(
|
|
|
|
[rofi_path, "-dmenu", "-p", title],
|
|
|
|
text=True,
|
|
|
|
capture_output=True,
|
2022-08-18 11:10:44 +02:00
|
|
|
input=input_text,
|
2022-05-20 11:39:34 +02:00
|
|
|
)
|
|
|
|
output = completed_process.stdout
|
|
|
|
if not output:
|
|
|
|
exit("Empty Rofi output.")
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
2022-05-19 15:10:04 +02:00
|
|
|
def open_bang(config, handle, query):
|
2022-10-26 19:02:44 +02:00
|
|
|
try:
|
|
|
|
bang = next(bang for bang in config["bangs"] if handle == bang["handle"])
|
|
|
|
except StopIteration:
|
2022-05-19 15:10:04 +02:00
|
|
|
print("Unknown handle.")
|
|
|
|
return
|
2022-08-28 15:46:14 +02:00
|
|
|
query = query.strip()
|
|
|
|
if not bang.get("raw", False):
|
|
|
|
query = urllib.parse.quote(query)
|
|
|
|
url = bang["url"].format(query)
|
2022-05-19 15:10:04 +02:00
|
|
|
webbrowser.open_new_tab(url)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
ap = argparse.ArgumentParser()
|
2022-08-18 11:10:44 +02:00
|
|
|
ap.add_argument("-c", "--config", help="path to JSON config file")
|
|
|
|
ap.add_argument("-l", "--list", action="store_true", help="show available bangs")
|
2022-09-30 19:04:26 +02:00
|
|
|
ap.add_argument("-b", "--bang", nargs="+", help="launch with this bang already set")
|
2022-10-26 19:02:44 +02:00
|
|
|
ap.add_argument("-f", "--queries-file", help="file with one bang argument per line")
|
2022-05-19 15:10:04 +02:00
|
|
|
args = ap.parse_args()
|
|
|
|
|
|
|
|
config = load_config()
|
|
|
|
if config is None:
|
|
|
|
exit("Can't load config file.")
|
|
|
|
|
|
|
|
if args.list:
|
|
|
|
list_bangs(config)
|
|
|
|
return
|
|
|
|
|
2022-10-26 19:02:44 +02:00
|
|
|
queries = []
|
|
|
|
if listfile := args.queries_file:
|
|
|
|
try:
|
|
|
|
with open(listfile, "rt") as file:
|
|
|
|
queries = [line.rstrip() for line in file.readlines()]
|
|
|
|
except OSError:
|
|
|
|
exit("Can't load queries file.")
|
|
|
|
|
|
|
|
# If a bang is specified on the command line, use it, optionally with its args.
|
2022-09-30 19:04:26 +02:00
|
|
|
if bang_args := args.bang:
|
|
|
|
handle = bang_args[0]
|
|
|
|
if bang_args[1:]:
|
2022-10-26 19:02:44 +02:00
|
|
|
queries.append(" ".join(bang_args[1:]))
|
|
|
|
# Else show a Rofi with the list of available bangs.
|
2022-05-20 11:39:34 +02:00
|
|
|
else:
|
2022-08-18 11:10:44 +02:00
|
|
|
process_input = "\n".join(i["handle"] for i in config["bangs"]) + "\n"
|
|
|
|
output = run_rofi(config, input_text=process_input)
|
|
|
|
parts = output.split(maxsplit=1)
|
|
|
|
if len(parts) < 1:
|
|
|
|
exit("Bad Rofi output.")
|
2022-10-26 19:02:44 +02:00
|
|
|
handle = parts[0]
|
|
|
|
if len(parts) > 1:
|
|
|
|
queries.append(parts[1])
|
|
|
|
|
|
|
|
# If no queries were obtained during options parsing, show Rofi now to get a single query.
|
|
|
|
if not queries:
|
|
|
|
queries.append(run_rofi(config, title=handle))
|
|
|
|
|
|
|
|
for query in queries:
|
|
|
|
open_bang(config, handle, query)
|
2022-05-19 15:10:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|