feat(hammerspoon): setup URL handling to open different sites in different browsers

This commit is contained in:
2023-10-24 13:58:14 +01:00
parent b7ff042053
commit 407e02895d
5 changed files with 818 additions and 1 deletions

View File

@@ -0,0 +1,400 @@
[
{
"Command": [],
"Constant": [],
"Constructor": [],
"Deprecated": [],
"Field": [],
"Function": [],
"Method": [
{
"def": "URLDispatcher:dispatchURL(scheme, host, params, fullUrl, senderPid)",
"desc": "Dispatch a URL to an application according to the defined `url_patterns`.",
"doc": "Dispatch a URL to an application according to the defined `url_patterns`.\n\nParameters:\n * scheme - A string containing the URL scheme (i.e. \"http\")\n * host - A string containing the host requested (e.g. \"www.hammerspoon.org\")\n * params - A table containing the key/value pairs of all the URL parameters\n * fullURL - A string containing the full, original URL. This is the only parameter used in this implementation.\n * senderPID - An integer containing the PID of the application that opened the URL, if available (otherwise -1)\n\nNotes:\n * The parameters (follow to the [httpCallback](http://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback) specification)",
"examples": [],
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "188",
"name": "dispatchURL",
"notes": [
" * The parameters (follow to the [httpCallback](http://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback) specification)"
],
"parameters": [
" * scheme - A string containing the URL scheme (i.e. \"http\")",
" * host - A string containing the host requested (e.g. \"www.hammerspoon.org\")",
" * params - A table containing the key/value pairs of all the URL parameters",
" * fullURL - A string containing the full, original URL. This is the only parameter used in this implementation.",
" * senderPID - An integer containing the PID of the application that opened the URL, if available (otherwise -1)"
],
"returns": [],
"signature": "URLDispatcher:dispatchURL(scheme, host, params, fullUrl, senderPid)",
"stripped_doc": "",
"type": "Method"
},
{
"def": "URLDispatcher:start()",
"desc": "Start dispatching URLs according to the rules",
"doc": "Start dispatching URLs according to the rules\n\nParameters:\n * None",
"examples": [],
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "312",
"name": "start",
"notes": [],
"parameters": [
" * None"
],
"returns": [],
"signature": "URLDispatcher:start()",
"stripped_doc": "",
"type": "Method"
}
],
"Variable": [
{
"def": "URLDispatcher.decode_slack_redir_urls",
"desc": "If true, handle Slack-redir URLs to apply the rule on the destination URL. Defaults to `true`",
"doc": "If true, handle Slack-redir URLs to apply the rule on the destination URL. Defaults to `true`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "30",
"name": "decode_slack_redir_urls",
"signature": "URLDispatcher.decode_slack_redir_urls",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.default_handler",
"desc": "Default URL handler (Defaults to `\"com.apple.Safari\"`)",
"doc": "Default URL handler (Defaults to `\"com.apple.Safari\"`)\n\nNotes:\nCan be a string containing the Bundle ID of an application, or a function\nthat takes one argument, and which will be invoked with the URL to open.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "21",
"name": "default_handler",
"notes": [
"Can be a string containing the Bundle ID of an application, or a function",
"that takes one argument, and which will be invoked with the URL to open."
],
"signature": "URLDispatcher.default_handler",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.logger",
"desc": "Logger object used within the Spoon. Can be accessed to set the default log",
"doc": "Logger object used within the Spoon. Can be accessed to set the default log\nlevel for the messages coming from the Spoon.\n\nNotes:\nExample: `spoon.URLDispatcher.logger.setLogLevel(\"debug\")`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "99",
"name": "logger",
"notes": [
"Example: `spoon.URLDispatcher.logger.setLogLevel(\"debug\")`"
],
"signature": "URLDispatcher.logger",
"stripped_doc": "level for the messages coming from the Spoon.",
"type": "Variable"
},
{
"def": "URLDispatcher.pat_files",
"desc": "Internal variable containing a table where the pattern lists read from files are kept indexed by file name, and automatically updated.",
"doc": "Internal variable containing a table where the pattern lists read from files are kept indexed by file name, and automatically updated.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "114",
"name": "pat_files",
"signature": "URLDispatcher.pat_files",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.pat_watchers",
"desc": "Internal variable containing a table where the watchers for the pattern files are kept indexed by file name.",
"doc": "Internal variable containing a table where the watchers for the pattern files are kept indexed by file name.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "119",
"name": "pat_watchers",
"signature": "URLDispatcher.pat_watchers",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.set_system_handler",
"desc": "If true, URLDispatcher sets itself as system handler for http requests.",
"doc": "If true, URLDispatcher sets itself as system handler for http requests.\nDefaults to `true`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "108",
"name": "set_system_handler",
"signature": "URLDispatcher.set_system_handler",
"stripped_doc": "Defaults to `true`",
"type": "Variable"
},
{
"def": "URLDispatcher.url_patterns",
"desc": "URL dispatch rules.",
"doc": "URL dispatch rules.\n\nNotes:\n A table containing a list of dispatch rules. Rules are evaluated in the\n order they are declared. Each rule is a table with the following structure:\n `{ url-patterns, app-bundle-ID-or-function, function, app-patterns }`\n * `url-patterns` can be: (a) a single pattern as a string, (b) a table\n containing a list of strings, or (c) a string containing the path of a\n file from which the patterns will be read (if the string contains a valid\n filename it's used as a file, otherwise as a pattern). In case (c), a\n watcher will be set to automatically re-read the contents of the file\n when it changes. If a relative path is given (not starting with a \"/\"),\n then it is considered to be relative to the Hammerspoon configuration\n directory.\n * If `app-bundle-ID-or-function` is specified as a string, it is\n interpreted as a macOS application ID, and that application will be used\n to open matching URLs. If it is a function pointer, or not given but\n \"function\" is provided, it is expected to be a function that accepts a\n single argument, and it will be called with the URL.\n * If `app-patterns` is given, it should be a string or a table containing a\n pattern/list of patterns, and the rule will only be evaluated if the URL\n was opened from an application whose name matches one of those patterns.\n * Note that the patterns are [Lua patterns](https://www.lua.org/pil/20.2.html)\n and not regular expressions.\n * Defaults to an empty table, which has the effect of having all URLs\n dispatched to the `default_handler`.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "69",
"name": "url_patterns",
"notes": [
" A table containing a list of dispatch rules. Rules are evaluated in the",
" order they are declared. Each rule is a table with the following structure:",
" `{ url-patterns, app-bundle-ID-or-function, function, app-patterns }`",
" * `url-patterns` can be: (a) a single pattern as a string, (b) a table",
" containing a list of strings, or (c) a string containing the path of a",
" file from which the patterns will be read (if the string contains a valid",
" filename it's used as a file, otherwise as a pattern). In case (c), a",
" watcher will be set to automatically re-read the contents of the file",
" when it changes. If a relative path is given (not starting with a \"/\"),",
" then it is considered to be relative to the Hammerspoon configuration",
" directory.",
" * If `app-bundle-ID-or-function` is specified as a string, it is",
" interpreted as a macOS application ID, and that application will be used",
" to open matching URLs. If it is a function pointer, or not given but",
" \"function\" is provided, it is expected to be a function that accepts a",
" single argument, and it will be called with the URL.",
" * If `app-patterns` is given, it should be a string or a table containing a",
" pattern/list of patterns, and the rule will only be evaluated if the URL",
" was opened from an application whose name matches one of those patterns.",
" * Note that the patterns are [Lua patterns](https://www.lua.org/pil/20.2.html)",
" and not regular expressions.",
" * Defaults to an empty table, which has the effect of having all URLs",
" dispatched to the `default_handler`."
],
"signature": "URLDispatcher.url_patterns",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.url_redir_decoders",
"desc": "URL redirection decoders. Default value: empty list",
"doc": "URL redirection decoders. Default value: empty list\n\nNotes:\nList containing optional redirection decoders (other than the known Slack\ndecoder, which is enabled by `URLDispatcher.decode_slack_redir_urls` to\napply to URLs before dispatching them. Each list element must be a list\nitself with a maximum of five elements:\n * `decoder-name`: (String) a name to identify the decoder;\n * `decoder-pattern-or-function`: (String or Function) if a string is\n given, it is used as a [Lua pattern](https://www.lua.org/pil/20.2.html)\n to match against the URL. If a function is given, it will be called with\n arguments `scheme`, `host`, `params`, `fullUrl`, `senderPid` (the same\n arguments as passed to\n [hs.urlevent.httpCallback](https://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback)),\n and must return a string that contains the URL to be opened. The\n returned value will be URL-decoded according to the value of `skip-decode-url` (below).\n * `pattern-replacement`: (String) a replacement pattern to apply if a\n match is found when a decoder pattern (previous argument) is provided.\n If a decoder function is given, this argument is ignored.\n * `skip-decode-url`: (Boolean, optional) whether to skip URL-decoding of the\n resulting string (defaults to `false`, by default URLs are always decoded)\n * `source-application`: (String or Table, optional): a pattern or list of\n patterns to match against the name of the application from which the URL\n was opened. If this parameter is present, the decoder will only be\n applied when the application matches. Default is to apply the decoder\n regardless of the application.\nIf given as strings, `decoder-pattern-or-function` and `pattern-replacement`\nare passed as arguments to\n[string.gsub](https://www.lua.org/manual/5.3/manual.html#pdf-string.gsub)\napplied on the original URL.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "35",
"name": "url_redir_decoders",
"notes": [
"List containing optional redirection decoders (other than the known Slack",
"decoder, which is enabled by `URLDispatcher.decode_slack_redir_urls` to",
"apply to URLs before dispatching them. Each list element must be a list",
"itself with a maximum of five elements:",
" * `decoder-name`: (String) a name to identify the decoder;",
" * `decoder-pattern-or-function`: (String or Function) if a string is",
" given, it is used as a [Lua pattern](https://www.lua.org/pil/20.2.html)",
" to match against the URL. If a function is given, it will be called with",
" arguments `scheme`, `host`, `params`, `fullUrl`, `senderPid` (the same",
" arguments as passed to",
" [hs.urlevent.httpCallback](https://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback)),",
" and must return a string that contains the URL to be opened. The",
" returned value will be URL-decoded according to the value of `skip-decode-url` (below).",
" * `pattern-replacement`: (String) a replacement pattern to apply if a",
" match is found when a decoder pattern (previous argument) is provided.",
" If a decoder function is given, this argument is ignored.",
" * `skip-decode-url`: (Boolean, optional) whether to skip URL-decoding of the",
" resulting string (defaults to `false`, by default URLs are always decoded)",
" * `source-application`: (String or Table, optional): a pattern or list of",
" patterns to match against the name of the application from which the URL",
" was opened. If this parameter is present, the decoder will only be",
" applied when the application matches. Default is to apply the decoder",
" regardless of the application.",
"If given as strings, `decoder-pattern-or-function` and `pattern-replacement`",
"are passed as arguments to",
"[string.gsub](https://www.lua.org/manual/5.3/manual.html#pdf-string.gsub)",
"applied on the original URL."
],
"signature": "URLDispatcher.url_redir_decoders",
"stripped_doc": "",
"type": "Variable"
}
],
"desc": "Route URLs to different applications with pattern matching",
"doc": "Route URLs to different applications with pattern matching\n\nDownload: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip)\n\nSets Hammerspoon as the default browser for HTTP/HTTPS links, and\ndispatches them to different apps according to the patterns defined\nin the config. If no pattern matches, `default_handler` is used.",
"items": [
{
"def": "URLDispatcher.decode_slack_redir_urls",
"desc": "If true, handle Slack-redir URLs to apply the rule on the destination URL. Defaults to `true`",
"doc": "If true, handle Slack-redir URLs to apply the rule on the destination URL. Defaults to `true`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "30",
"name": "decode_slack_redir_urls",
"signature": "URLDispatcher.decode_slack_redir_urls",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.default_handler",
"desc": "Default URL handler (Defaults to `\"com.apple.Safari\"`)",
"doc": "Default URL handler (Defaults to `\"com.apple.Safari\"`)\n\nNotes:\nCan be a string containing the Bundle ID of an application, or a function\nthat takes one argument, and which will be invoked with the URL to open.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "21",
"name": "default_handler",
"notes": [
"Can be a string containing the Bundle ID of an application, or a function",
"that takes one argument, and which will be invoked with the URL to open."
],
"signature": "URLDispatcher.default_handler",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher:dispatchURL(scheme, host, params, fullUrl, senderPid)",
"desc": "Dispatch a URL to an application according to the defined `url_patterns`.",
"doc": "Dispatch a URL to an application according to the defined `url_patterns`.\n\nParameters:\n * scheme - A string containing the URL scheme (i.e. \"http\")\n * host - A string containing the host requested (e.g. \"www.hammerspoon.org\")\n * params - A table containing the key/value pairs of all the URL parameters\n * fullURL - A string containing the full, original URL. This is the only parameter used in this implementation.\n * senderPID - An integer containing the PID of the application that opened the URL, if available (otherwise -1)\n\nNotes:\n * The parameters (follow to the [httpCallback](http://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback) specification)",
"examples": [],
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "188",
"name": "dispatchURL",
"notes": [
" * The parameters (follow to the [httpCallback](http://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback) specification)"
],
"parameters": [
" * scheme - A string containing the URL scheme (i.e. \"http\")",
" * host - A string containing the host requested (e.g. \"www.hammerspoon.org\")",
" * params - A table containing the key/value pairs of all the URL parameters",
" * fullURL - A string containing the full, original URL. This is the only parameter used in this implementation.",
" * senderPID - An integer containing the PID of the application that opened the URL, if available (otherwise -1)"
],
"returns": [],
"signature": "URLDispatcher:dispatchURL(scheme, host, params, fullUrl, senderPid)",
"stripped_doc": "",
"type": "Method"
},
{
"def": "URLDispatcher.logger",
"desc": "Logger object used within the Spoon. Can be accessed to set the default log",
"doc": "Logger object used within the Spoon. Can be accessed to set the default log\nlevel for the messages coming from the Spoon.\n\nNotes:\nExample: `spoon.URLDispatcher.logger.setLogLevel(\"debug\")`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "99",
"name": "logger",
"notes": [
"Example: `spoon.URLDispatcher.logger.setLogLevel(\"debug\")`"
],
"signature": "URLDispatcher.logger",
"stripped_doc": "level for the messages coming from the Spoon.",
"type": "Variable"
},
{
"def": "URLDispatcher.pat_files",
"desc": "Internal variable containing a table where the pattern lists read from files are kept indexed by file name, and automatically updated.",
"doc": "Internal variable containing a table where the pattern lists read from files are kept indexed by file name, and automatically updated.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "114",
"name": "pat_files",
"signature": "URLDispatcher.pat_files",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.pat_watchers",
"desc": "Internal variable containing a table where the watchers for the pattern files are kept indexed by file name.",
"doc": "Internal variable containing a table where the watchers for the pattern files are kept indexed by file name.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "119",
"name": "pat_watchers",
"signature": "URLDispatcher.pat_watchers",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.set_system_handler",
"desc": "If true, URLDispatcher sets itself as system handler for http requests.",
"doc": "If true, URLDispatcher sets itself as system handler for http requests.\nDefaults to `true`",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "108",
"name": "set_system_handler",
"signature": "URLDispatcher.set_system_handler",
"stripped_doc": "Defaults to `true`",
"type": "Variable"
},
{
"def": "URLDispatcher:start()",
"desc": "Start dispatching URLs according to the rules",
"doc": "Start dispatching URLs according to the rules\n\nParameters:\n * None",
"examples": [],
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "312",
"name": "start",
"notes": [],
"parameters": [
" * None"
],
"returns": [],
"signature": "URLDispatcher:start()",
"stripped_doc": "",
"type": "Method"
},
{
"def": "URLDispatcher.url_patterns",
"desc": "URL dispatch rules.",
"doc": "URL dispatch rules.\n\nNotes:\n A table containing a list of dispatch rules. Rules are evaluated in the\n order they are declared. Each rule is a table with the following structure:\n `{ url-patterns, app-bundle-ID-or-function, function, app-patterns }`\n * `url-patterns` can be: (a) a single pattern as a string, (b) a table\n containing a list of strings, or (c) a string containing the path of a\n file from which the patterns will be read (if the string contains a valid\n filename it's used as a file, otherwise as a pattern). In case (c), a\n watcher will be set to automatically re-read the contents of the file\n when it changes. If a relative path is given (not starting with a \"/\"),\n then it is considered to be relative to the Hammerspoon configuration\n directory.\n * If `app-bundle-ID-or-function` is specified as a string, it is\n interpreted as a macOS application ID, and that application will be used\n to open matching URLs. If it is a function pointer, or not given but\n \"function\" is provided, it is expected to be a function that accepts a\n single argument, and it will be called with the URL.\n * If `app-patterns` is given, it should be a string or a table containing a\n pattern/list of patterns, and the rule will only be evaluated if the URL\n was opened from an application whose name matches one of those patterns.\n * Note that the patterns are [Lua patterns](https://www.lua.org/pil/20.2.html)\n and not regular expressions.\n * Defaults to an empty table, which has the effect of having all URLs\n dispatched to the `default_handler`.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "69",
"name": "url_patterns",
"notes": [
" A table containing a list of dispatch rules. Rules are evaluated in the",
" order they are declared. Each rule is a table with the following structure:",
" `{ url-patterns, app-bundle-ID-or-function, function, app-patterns }`",
" * `url-patterns` can be: (a) a single pattern as a string, (b) a table",
" containing a list of strings, or (c) a string containing the path of a",
" file from which the patterns will be read (if the string contains a valid",
" filename it's used as a file, otherwise as a pattern). In case (c), a",
" watcher will be set to automatically re-read the contents of the file",
" when it changes. If a relative path is given (not starting with a \"/\"),",
" then it is considered to be relative to the Hammerspoon configuration",
" directory.",
" * If `app-bundle-ID-or-function` is specified as a string, it is",
" interpreted as a macOS application ID, and that application will be used",
" to open matching URLs. If it is a function pointer, or not given but",
" \"function\" is provided, it is expected to be a function that accepts a",
" single argument, and it will be called with the URL.",
" * If `app-patterns` is given, it should be a string or a table containing a",
" pattern/list of patterns, and the rule will only be evaluated if the URL",
" was opened from an application whose name matches one of those patterns.",
" * Note that the patterns are [Lua patterns](https://www.lua.org/pil/20.2.html)",
" and not regular expressions.",
" * Defaults to an empty table, which has the effect of having all URLs",
" dispatched to the `default_handler`."
],
"signature": "URLDispatcher.url_patterns",
"stripped_doc": "",
"type": "Variable"
},
{
"def": "URLDispatcher.url_redir_decoders",
"desc": "URL redirection decoders. Default value: empty list",
"doc": "URL redirection decoders. Default value: empty list\n\nNotes:\nList containing optional redirection decoders (other than the known Slack\ndecoder, which is enabled by `URLDispatcher.decode_slack_redir_urls` to\napply to URLs before dispatching them. Each list element must be a list\nitself with a maximum of five elements:\n * `decoder-name`: (String) a name to identify the decoder;\n * `decoder-pattern-or-function`: (String or Function) if a string is\n given, it is used as a [Lua pattern](https://www.lua.org/pil/20.2.html)\n to match against the URL. If a function is given, it will be called with\n arguments `scheme`, `host`, `params`, `fullUrl`, `senderPid` (the same\n arguments as passed to\n [hs.urlevent.httpCallback](https://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback)),\n and must return a string that contains the URL to be opened. The\n returned value will be URL-decoded according to the value of `skip-decode-url` (below).\n * `pattern-replacement`: (String) a replacement pattern to apply if a\n match is found when a decoder pattern (previous argument) is provided.\n If a decoder function is given, this argument is ignored.\n * `skip-decode-url`: (Boolean, optional) whether to skip URL-decoding of the\n resulting string (defaults to `false`, by default URLs are always decoded)\n * `source-application`: (String or Table, optional): a pattern or list of\n patterns to match against the name of the application from which the URL\n was opened. If this parameter is present, the decoder will only be\n applied when the application matches. Default is to apply the decoder\n regardless of the application.\nIf given as strings, `decoder-pattern-or-function` and `pattern-replacement`\nare passed as arguments to\n[string.gsub](https://www.lua.org/manual/5.3/manual.html#pdf-string.gsub)\napplied on the original URL.",
"file": "Source/URLDispatcher.spoon//init.lua",
"lineno": "35",
"name": "url_redir_decoders",
"notes": [
"List containing optional redirection decoders (other than the known Slack",
"decoder, which is enabled by `URLDispatcher.decode_slack_redir_urls` to",
"apply to URLs before dispatching them. Each list element must be a list",
"itself with a maximum of five elements:",
" * `decoder-name`: (String) a name to identify the decoder;",
" * `decoder-pattern-or-function`: (String or Function) if a string is",
" given, it is used as a [Lua pattern](https://www.lua.org/pil/20.2.html)",
" to match against the URL. If a function is given, it will be called with",
" arguments `scheme`, `host`, `params`, `fullUrl`, `senderPid` (the same",
" arguments as passed to",
" [hs.urlevent.httpCallback](https://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback)),",
" and must return a string that contains the URL to be opened. The",
" returned value will be URL-decoded according to the value of `skip-decode-url` (below).",
" * `pattern-replacement`: (String) a replacement pattern to apply if a",
" match is found when a decoder pattern (previous argument) is provided.",
" If a decoder function is given, this argument is ignored.",
" * `skip-decode-url`: (Boolean, optional) whether to skip URL-decoding of the",
" resulting string (defaults to `false`, by default URLs are always decoded)",
" * `source-application`: (String or Table, optional): a pattern or list of",
" patterns to match against the name of the application from which the URL",
" was opened. If this parameter is present, the decoder will only be",
" applied when the application matches. Default is to apply the decoder",
" regardless of the application.",
"If given as strings, `decoder-pattern-or-function` and `pattern-replacement`",
"are passed as arguments to",
"[string.gsub](https://www.lua.org/manual/5.3/manual.html#pdf-string.gsub)",
"applied on the original URL."
],
"signature": "URLDispatcher.url_redir_decoders",
"stripped_doc": "",
"type": "Variable"
}
],
"name": "URLDispatcher",
"stripped_doc": "\nDownload: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip)\n\nSets Hammerspoon as the default browser for HTTP/HTTPS links, and\ndispatches them to different apps according to the patterns defined\nin the config. If no pattern matches, `default_handler` is used.",
"submodules": [],
"type": "Module"
}
]

View File

@@ -0,0 +1,330 @@
--- === URLDispatcher ===
---
--- Route URLs to different applications with pattern matching
---
--- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/URLDispatcher.spoon.zip)
---
--- Sets Hammerspoon as the default browser for HTTP/HTTPS links, and
--- dispatches them to different apps according to the patterns defined
--- in the config. If no pattern matches, `default_handler` is used.
local obj={}
obj.__index = obj
-- Metadata
obj.name = "URLDispatcher"
obj.version = "0.5"
obj.author = "Diego Zamboni <diego@zzamboni.org>"
obj.homepage = "https://github.com/Hammerspoon/Spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"
--- URLDispatcher.default_handler
--- Variable
--- Default URL handler (Defaults to `"com.apple.Safari"`)
---
--- Notes:
--- Can be a string containing the Bundle ID of an application, or a function
--- that takes one argument, and which will be invoked with the URL to open.
obj.default_handler = "com.apple.Safari"
--- URLDispatcher.decode_slack_redir_urls
--- Variable
--- If true, handle Slack-redir URLs to apply the rule on the destination URL. Defaults to `true`
obj.decode_slack_redir_urls = true
--- URLDispatcher.url_redir_decoders
--- Variable
--- URL redirection decoders. Default value: empty list
---
--- Notes:
--- List containing optional redirection decoders (other than the known Slack
--- decoder, which is enabled by `URLDispatcher.decode_slack_redir_urls` to
--- apply to URLs before dispatching them. Each list element must be a list
--- itself with a maximum of five elements:
--- * `decoder-name`: (String) a name to identify the decoder;
--- * `decoder-pattern-or-function`: (String or Function) if a string is
--- given, it is used as a [Lua pattern](https://www.lua.org/pil/20.2.html)
--- to match against the URL. If a function is given, it will be called with
--- arguments `scheme`, `host`, `params`, `fullUrl`, `senderPid` (the same
--- arguments as passed to
--- [hs.urlevent.httpCallback](https://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback)),
--- and must return a string that contains the URL to be opened. The
--- returned value will be URL-decoded according to the value of `skip-decode-url` (below).
--- * `pattern-replacement`: (String) a replacement pattern to apply if a
--- match is found when a decoder pattern (previous argument) is provided.
--- If a decoder function is given, this argument is ignored.
--- * `skip-decode-url`: (Boolean, optional) whether to skip URL-decoding of the
--- resulting string (defaults to `false`, by default URLs are always decoded)
--- * `source-application`: (String or Table, optional): a pattern or list of
--- patterns to match against the name of the application from which the URL
--- was opened. If this parameter is present, the decoder will only be
--- applied when the application matches. Default is to apply the decoder
--- regardless of the application.
--- If given as strings, `decoder-pattern-or-function` and `pattern-replacement`
--- are passed as arguments to
--- [string.gsub](https://www.lua.org/manual/5.3/manual.html#pdf-string.gsub)
--- applied on the original URL.
obj.url_redir_decoders = { }
--- URLDispatcher.url_patterns
--- Variable
--- URL dispatch rules.
---
--- Notes:
--- A table containing a list of dispatch rules. Rules are evaluated in the
--- order they are declared. Each rule is a table with the following structure:
--- `{ url-patterns, app-bundle-ID-or-function, function, app-patterns }`
--- * `url-patterns` can be: (a) a single pattern as a string, (b) a table
--- containing a list of strings, or (c) a string containing the path of a
--- file from which the patterns will be read (if the string contains a valid
--- filename it's used as a file, otherwise as a pattern). In case (c), a
--- watcher will be set to automatically re-read the contents of the file
--- when it changes. If a relative path is given (not starting with a "/"),
--- then it is considered to be relative to the Hammerspoon configuration
--- directory.
--- * If `app-bundle-ID-or-function` is specified as a string, it is
--- interpreted as a macOS application ID, and that application will be used
--- to open matching URLs. If it is a function pointer, or not given but
--- "function" is provided, it is expected to be a function that accepts a
--- single argument, and it will be called with the URL.
--- * If `app-patterns` is given, it should be a string or a table containing a
--- pattern/list of patterns, and the rule will only be evaluated if the URL
--- was opened from an application whose name matches one of those patterns.
--- * Note that the patterns are [Lua patterns](https://www.lua.org/pil/20.2.html)
--- and not regular expressions.
--- * Defaults to an empty table, which has the effect of having all URLs
--- dispatched to the `default_handler`.
obj.url_patterns = { }
--- URLDispatcher.logger
--- Variable
--- Logger object used within the Spoon. Can be accessed to set the default log
--- level for the messages coming from the Spoon.
---
--- Notes:
--- Example: `spoon.URLDispatcher.logger.setLogLevel("debug")`
obj.logger = hs.logger.new('URLDispatcher')
--- URLDispatcher.set_system_handler
--- Variable
--- If true, URLDispatcher sets itself as system handler for http requests.
--- Defaults to `true`
obj.set_system_handler = true
--- URLDispatcher.pat_files
--- Variable
--- Internal variable containing a table where the pattern lists read from files are kept indexed by file name, and automatically updated.
obj.pat_files = {}
--- URLDispatcher.pat_watchers
--- Variable
--- Internal variable containing a table where the watchers for the pattern files are kept indexed by file name.
obj.pat_watchers = {}
-- Local functions to decode URLs
function hex_to_char(x)
return string.char(tonumber(x, 16))
end
function obj.unescape(url)
return url:gsub("%%(%x%x)", hex_to_char)
end
-- Match a single pattern against an application name.
function obj.matchapp(app, pat)
obj.logger.df("Matching appname '%s' against pattern '%s'", app, pat)
return string.find(app, pat)
end
-- Match a pattern or a list of patterns against an application name.
-- The pattern can also be nil, in this case it's considered a success.
function obj.matchapps(app, pat)
local ismatch = (pat == nil) or
(type(pat) == 'string' and obj.matchapp(app, pat)) or
(type(pat) == 'table' and hs.fnutils.some(pat, hs.fnutils.partial(obj.matchapp, app)))
if ismatch then
obj.logger.df(" App pattern '%s' is nil or matches application name '%s' - evaluating rule.", pat, app)
else
obj.logger.df(" App pattern '%s' does not match application name '%s' - skipping rule.", pat, app)
end
return ismatch
end
function obj:read_and_store(patfile)
self.logger.df("Reading patterns from file '%s'", patfile)
local pats = {}
for line in io.lines(patfile) do
-- Skip empty lines and lines starting with "#" (comments)
if (line ~= '') and not (string.find(line, '^%s*#')) then
table.insert(pats, line)
end
end
self.pat_files[patfile] = hs.fnutils.copy(pats)
end
function obj:patfileWatcher(patfile, paths, flags)
-- Only trigger re-reading the file when the 'itemModified' flag is present,
-- otherwise the file gets read multiple times due to file manipulations done
-- by editors
if hs.fnutils.some(flags, function(f) return f['itemModified'] end) then
self:read_and_store(patfile)
end
end
function obj:setupPatfile(patfile)
-- If the file exists, read it and setup a watcher to update it.
if hs.fs.attributes(patfile) then
self.logger.df("File '%s' has not been loaded, reading it now.", patfile)
-- Read the file and set up the watcher to auto-update it.
self:read_and_store(patfile)
self.logger.df("Creating watcher for file '%s'", patfile)
self.pat_watchers[patfile] = hs.pathwatcher.new(patfile, hs.fnutils.partial(self.patfileWatcher, self, patfile)):start()
return self.pat_files[patfile]
else
return nil
end
end
--- URLDispatcher:dispatchURL(scheme, host, params, fullUrl, senderPid)
--- Method
--- Dispatch a URL to an application according to the defined `url_patterns`.
---
--- Parameters:
--- * scheme - A string containing the URL scheme (i.e. "http")
--- * host - A string containing the host requested (e.g. "www.hammerspoon.org")
--- * params - A table containing the key/value pairs of all the URL parameters
--- * fullURL - A string containing the full, original URL. This is the only parameter used in this implementation.
--- * senderPID - An integer containing the PID of the application that opened the URL, if available (otherwise -1)
---
--- Notes:
--- * The parameters (follow to the [httpCallback](http://www.hammerspoon.org/docs/hs.urlevent.html#httpCallback) specification)
function obj:dispatchURL(scheme, host, params, fullUrl, senderPid)
local url = fullUrl
local currentApp = ""
if senderPid ~= -1 then
currentApp = hs.application.applicationForPID(senderPid):name()
end
self.logger.df("Dispatching URL '%s' from application '%s'", url, currentApp)
if self.decode_slack_redir_urls then
local newUrl = string.match(url, 'https://slack.redir.net/.*url=(.*)')
if newUrl then
url = obj.unescape(newUrl)
self.logger.df(" Decoded Slack redirect. New URL: '%s'", url)
end
end
for i,dec in ipairs(self.url_redir_decoders) do
self.logger.df(" Testing decoder '%s'", dec[1])
local processed = false
if self.matchapps(currentApp, dec[5]) then
if type(dec[2]) == "string" then
if string.find(url, dec[2]) then
self.logger.df(" Applying pattern-based decoder '%s' to URL '%s'", dec[1], url)
url = string.gsub(url, dec[2], dec[3])
self.logger.df(" Decoded URL: '%s'", url)
processed = true
end
elseif type(dec[2]) == "function" then
self.logger.df(" Applying function-based decoder '%s' to URL '%s'", dec[1], url)
url = dec[2](scheme, host, params, fullUrl, senderPid)
self.logger.df(" Decoded URL: '%s'", url)
processed = true
else
self.logger.ef(" Decoder '%s' has an unknown second value of type '%s'", dec[1], dec[2])
end
if processed and (not dec[4]) then
self.logger.df(" Unescaping decoded URL '%s'", url)
url = obj.unescape(url)
self.logger.df(" Unescaped URL: '%s'", url)
end
end
end
self.logger.df("Final URL to open: '%s'", url)
for i,pair in ipairs(self.url_patterns) do
self.logger.df("Evaluating rule %s", hs.inspect(pair))
local pats = pair[1]
local app = pair[2]
local func = pair[3]
local app_pats = pair[4]
-- If app_pats is given, then first of all check whether the source app
-- matches, otherwise we skip the whole thing
if self.matchapps(currentApp, app_pats) then
-- First determine how to interpret the url-patterns
if type(pats) == "string" then
-- A string can be a single pattern, or a filename to load
if self.pat_files[pats] then
-- If it's already a known pattern file, use its content
self.logger.df(" File '%s' is already read, using its contents.", pats)
pats = self.pat_files[pats]
else
-- Else, try to load it as a file
local patsfile = self:setupPatfile(pats)
-- If this fails, we use it as a single pattern
if patsfile then
pats = patsfile
else
self.logger.df(" Single pattern given, converting to list for processing.")
pats = { pats }
end
end
end
for i,p in ipairs(pats) do
self.logger.df(" Testing URL with pattern '%s'", p)
if string.match(url, p) then
local id = nil
if type(app) == "string" then
id = app
elseif type(app) == "function" then
func = app
end
if id ~= nil then
self.logger.df(" Match found, opening with '%s'", id)
hs.application.launchOrFocusByBundleID(id)
hs.urlevent.openURLWithBundle(url, id)
return
end
if func ~= nil then
self.logger.df(" Match found, calling func '%s'", func)
func(url)
return
end
end
end
end
end
-- Fall through to the default handler
if type(self.default_handler) == "string" then
self.logger.df("No match found, opening with default handler '%s'", self.default_handler)
hs.application.launchOrFocusByBundleID(self.default_handler)
hs.urlevent.openURLWithBundle(url, self.default_handler)
elseif type(self.default_handler) == "function" then
self.logger.df("No match found, opening with default handler func '%s'", self.default_handler)
self.default_handler(url)
else
self.logger.ef("Unknown type '%s' for default_handler '%s', must be a string or a function.",
type(self.default_handler), self.default_handler)
end
end
--- URLDispatcher:start()
--- Method
--- Start dispatching URLs according to the rules
---
--- Parameters:
--- * None
function obj:start()
if hs.urlevent.httpCallback then
self.logger.w("An hs.urlevent.httpCallback was already set. I'm overriding it with my own but you should check if this breaks any other functionality")
end
hs.urlevent.httpCallback = function(...) self:dispatchURL(...) end
if self.set_system_handler then
hs.urlevent.setDefaultHandler('http')
end
-- hs.urlevent.setRestoreHandler('http', self.default_handler)
return self
end
return obj