diff --git a/aw_watcher_window/__init__.py b/aw_watcher_window/__init__.py index 21db616..68e28ec 100644 --- a/aw_watcher_window/__init__.py +++ b/aw_watcher_window/__init__.py @@ -1,3 +1,6 @@ -from .main import main +def main(): + from .main import main as _main + + return _main() __all__ = ["main"] diff --git a/aw_watcher_window/macos.swift b/aw_watcher_window/macos.swift index 88d1047..5c0f22b 100644 --- a/aw_watcher_window/macos.swift +++ b/aw_watcher_window/macos.swift @@ -123,6 +123,8 @@ var baseurl = "http://localhost:5600" var clientHostname = ProcessInfo.processInfo.hostName var clientName = "aw-watcher-window" var bucketName = "\(clientName)_\(clientHostname)" +var excludeTitle = false +var excludeTitlePatterns: [NSRegularExpression] = [] let main = MainThing() var oldHeartbeat: Heartbeat? @@ -140,6 +142,49 @@ encoder.dateEncodingStrategy = .custom({ date, encoder in start() RunLoop.main.run() +func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression { + do { + return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) + } catch let regexError { + error("Invalid regex pattern: \(pattern) — \(regexError.localizedDescription)") + exit(1) + } +} + +func parseOptionalArguments(_ arguments: ArraySlice) { + var index = arguments.startIndex + while index < arguments.endIndex { + let argument = arguments[index] + + if argument == "--exclude-title" { + excludeTitle = true + index = arguments.index(after: index) + continue + } + + if argument == "--exclude-titles" { + let nextIndex = arguments.index(after: index) + guard nextIndex < arguments.endIndex else { + error("Missing value for --exclude-titles") + exit(1) + } + excludeTitlePatterns.append(compileExcludeTitlePattern(arguments[nextIndex])) + index = arguments.index(after: nextIndex) + continue + } + + error("Unknown argument: \(argument)") + exit(1) + } +} + +func titleShouldBeExcluded(_ title: String) -> Bool { + let range = NSRange(title.startIndex.. ") + // Check that we get the 4 required arguments plus any optional flags + if arguments.count < 5 { + print("Usage: aw-watcher-window [--exclude-title] [--exclude-titles ...]") exit(1) } @@ -158,6 +203,7 @@ func start() { bucketName = arguments[2] clientHostname = arguments[3] clientName = arguments[4] + parseOptionalArguments(arguments.dropFirst(5)) guard checkAccess() else { DispatchQueue.main.asyncAfter(deadline: .now() + 10) { @@ -388,6 +434,10 @@ class MainThing { } } + if excludeTitle || titleShouldBeExcluded(data.title) { + data.title = "excluded" + } + let heartbeat = Heartbeat(timestamp: nowTime, data: data) sendHeartbeat(heartbeat) } diff --git a/aw_watcher_window/macos_cli.py b/aw_watcher_window/macos_cli.py new file mode 100644 index 0000000..1ba4a3e --- /dev/null +++ b/aw_watcher_window/macos_cli.py @@ -0,0 +1,15 @@ +def build_swift_command( + binpath, + server_address, + bucket_id, + client_hostname, + client_name, + exclude_title=False, + exclude_titles=None, +): + command = [binpath, server_address, bucket_id, client_hostname, client_name] + if exclude_title: + command.append("--exclude-title") + for title in exclude_titles or []: + command.extend(["--exclude-titles", title]) + return command diff --git a/aw_watcher_window/main.py b/aw_watcher_window/main.py index b00f678..e271c70 100644 --- a/aw_watcher_window/main.py +++ b/aw_watcher_window/main.py @@ -14,6 +14,7 @@ from .config import parse_args from .exceptions import FatalError from .lib import get_current_window +from .macos_cli import build_swift_command from .macos_permissions import background_ensure_permissions logger = logging.getLogger(__name__) @@ -80,13 +81,15 @@ def main(): try: p = subprocess.Popen( - [ + build_swift_command( binpath, client.server_address, bucket_id, client.client_hostname, client.client_name, - ] + exclude_title=args.exclude_title, + exclude_titles=args.exclude_titles, + ) ) # terminate swift process when this process dies signal.signal(signal.SIGTERM, lambda *_: kill_process(p.pid)) diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..96e04b1 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,44 @@ +from aw_watcher_window.macos_cli import build_swift_command + + +def test_build_swift_command_omits_optional_filters(): + command = build_swift_command( + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + ) + + assert command == [ + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + ] + + +def test_build_swift_command_passes_title_filters(): + command = build_swift_command( + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + exclude_title=True, + exclude_titles=["Zoom", "Slack.*huddle"], + ) + + assert command == [ + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + "--exclude-title", + "--exclude-titles", + "Zoom", + "--exclude-titles", + "Slack.*huddle", + ]