209 lines
5.0 KiB
Python
Executable File
209 lines
5.0 KiB
Python
Executable File
#! /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):
|
|
"""Intent contains a url requested to be opened"""
|
|
url: str
|
|
# referrer isn't possible to be captured at this time
|
|
referrer: str
|
|
|
|
|
|
class BrowserRule(NamedTuple):
|
|
"""BrowserRule is matches a url and launches it in a browser"""
|
|
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"""
|
|
with Popen(["nohup", self.command, *self.args, intent.url], shell=False):
|
|
pass
|
|
|
|
def maybe_launch(self, intent: Intent) -> bool:
|
|
"""Launch URL in a browser if it matches the rules"""
|
|
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:
|
|
return any(match(rule, intent.url) for rule in rules)
|
|
|
|
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)
|
|
if not url.hostname:
|
|
return False
|
|
|
|
return any(match(rule, url.hostname) for rule in rules)
|
|
|
|
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:
|
|
"""Returns a functino that will check against any match rules
|
|
|
|
Equivalent to using `any(...)`"""
|
|
def match_func(intent: Intent) -> bool:
|
|
return any(r(intent) for r in rules)
|
|
|
|
return match_func
|
|
|
|
|
|
def default(_: Any) -> bool:
|
|
"""Always returns True"""
|
|
return True
|
|
|
|
|
|
def noop(_: 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:
|
|
"""Open a given url based on rule set"""
|
|
intent = Intent(url, referrer)
|
|
return any(rule.maybe_launch(intent) for rule in browser_rules)
|
|
|
|
|
|
def main(urls: List[str]):
|
|
"""Recieves list of urls and opens them each with provided rule sets"""
|
|
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:])
|