#! /usr/bin/env python3 import sys from re import match from subprocess import Popen from typing import Any from typing import Callable from typing import List from typing import Iterable from typing import NamedTuple from urllib.parse import urlparse class Intent(NamedTuple): url: str referrer: str class BrowserRule(NamedTuple): match_func: Callable[[Intent], bool] command: str args: List[str] = [] def matched(self, intent: Intent) -> bool: """Checks if the intent matches the rule using match_func""" return self.match_func(intent) def launch(self, intent: Intent) -> None: """Launch the command""" Popen(["nohup", self.command, *self.args, intent.url], shell=False) def maybe_launch(self, intent: Intent) -> bool: if self.matched(intent): self.launch(intent) return True return False # Matching functions and factories MatchFunc = Callable[[Intent], bool] def match_regex(rules: List[str]) -> MatchFunc: """Returns a function to handle urls and match them using regex""" def match_func(intent: Intent) -> bool: for rule in rules: if match(rule, intent.url): return True return False return match_func def match_host_regex(rules: List[str]) -> MatchFunc: """Returns a function to handle urls and match them using regex""" def match_func(intent: Intent) -> bool: url = urlparse(intent.url) for rule in rules: if match(rule, url.hostname): return True return False return match_func def match_hostname(hostnames: List[str]) -> MatchFunc: """Returns a matching function that parses URLs and checks their hosts""" def match_func(intent: Intent) -> bool: url = urlparse(intent.url) for hostname in hostnames: if url.hostname == hostname: return True return False return match_func def any_match(rules: Iterable[MatchFunc]) -> MatchFunc: def match_func(intent: Intent) -> bool: return any(r(intent) for r in rules) return match_func def default(x: Any) -> bool: """Always returns True""" return True def noop(x: Any) -> bool: """Always returns False""" return False browser_rules = [ # Self-hosted sites BrowserRule( match_host_regex([ r".*\.iamthefij\.com$", r".*\.thefij\.rocks$", r".*\.thefij$", ]), "firefox", ), # Work domains BrowserRule( any_match(( match_hostname([ "app.signalfx.com", "lever.co", "work.grubhub.com", "y", "yelp.rimeto.io", "yelp.slack.com", "yelplove.appspot.com", ]), match_host_regex([ r".*\.lifesize\.com$", r".*\.myworkday\.com$", r".*\.salesforce\.com$", r".*\.yelpcorp\.com$", ]), )), "chromium-browser" ), # Other, generally Googly things BrowserRule( any_match(( match_hostname([ "google.com", "youtube.com", ]), match_host_regex([ r".*\.google\.com$", r".*\.youtube\.com$", ]), )), "chromium-browser", ), # Fall back to firefox as the default BrowserRule( default, "firefox", ), # Sample browsers # Firefox new tab BrowserRule( noop, "firefox", ), # Firefox new window BrowserRule( noop, "firefox", ["--new-window"], ), # Firefox private window BrowserRule( noop, "firefox", ["--private-window"], ), # Chromium new tab BrowserRule( noop, "chromium-browser", ), # Chromium new window BrowserRule( noop, "chromium-browser", ["--new-window"], ), # Chromium incongnito BrowserRule( noop, "chromium-browser", ["--incognito"], ), ] def open_url(url: str, referrer=None) -> bool: intent = Intent(url, referrer) return any(rule.maybe_launch(intent) for rule in browser_rules) def main(urls: List[str], referrer=None): failed_urls: List[str] = [] for url in urls: if not open_url(url): failed_urls.append(url) if failed_urls: raise ValueError(f"No rules present for {failed_urls}. Add a default rule") if __name__ == "__main__": main(sys.argv[1:])