Files
hammerspoon/Spoons/Seal.spoon/seal_vpn.lua
T
2026-05-14 18:59:23 -04:00

267 lines
7.9 KiB
Lua

local obj = {}
obj.__index = obj
obj.__name = "seal_vpn"
function obj:commands()
return {vpn = {
cmd = "vpn",
fn = obj.choicesVPNCommand,
name = "VPN",
description = "Manage VPN connections",
plugin = obj.__name
}
}
end
function obj:bare()
return nil
end
function obj.getVPNConnections()
connections = {}
code, output, descriptor = hs.osascript.applescript([[
set output to ""
on join_list(the_list, delimiter)
set the_string to ""
set old_delims to AppleScript's text item delimiters
repeat with the_item in the_list
if the_string is equal to "" then
set the_string to the_string & the_item
else
set the_string to the_string & delimiter & the_item
end if
end repeat
set AppleScript's text item delimiters to old_delims
return the_string
end join_list
set vpn_connections to {}
tell application "Viscosity"
repeat with the_connection in connections
set the end of vpn_connections to (name of the_connection) & tab & (state of the_connection) & tab & "Viscosity"
end repeat
end tell
tell application "System Events"
tell current location of network preferences
repeat with vpn in (every service whose (kind is greater than 10 and kind is less than 17))
set state to "Disconnected"
if connected of current configuration of vpn is equal to true then
set state to "Connected"
end if
set the end of vpn_connections to (name of vpn) & tab & state & tab & "macOS"
end repeat
end tell
end tell
return my join_list(vpn_connections, linefeed)
]])
if code == false or output == nil or output == "" then
return connections
end
for line in output:gmatch("[^\r\n]+") do
parts = {}
for part in line:gmatch("%S+") do
table.insert(parts, part)
end
kind = parts[#parts]
table.remove(parts, #parts)
state = parts[#parts]
table.remove(parts, #parts)
name = table.concat(parts, " ")
table.insert(connections, {name=name, state=state, kind=kind})
end
return connections
end
function obj.disconnectVPN(name, kind)
if kind == "Viscosity" then
obj.disconnectViscosity(name)
elseif kind == "macOS" then
obj.disconnectMacOS(name)
end
end
function obj.disconnectViscosity(name)
code, output, descriptor = hs.osascript.applescript(string.format([[
-- Return true if VPN is active
on vpn_is_active(vpn_name)
tell application "Viscosity"
repeat with the_connection in connections
if (name of the_connection) is equal to vpn_name then
if (state of the_connection) is equal to "Connected" then
return true
else
return false
end if
end if
end repeat
end tell
return false
end vpn_is_active
on run argv
set vpn_name to "%s"
if my vpn_is_active(vpn_name) then
tell application "Viscosity"
disconnect vpn_name
end tell
end if
end run
]], name))
end
function obj.disconnectMacOS(name)
code, output, descriptor = hs.osascript.applescript(string.format([[
-- Return true if VPN is active
on vpn_is_active(vpn_name)
tell application "System Events"
tell current location of network preferences
return connected of current configuration of service vpn_name
end tell
end tell
end vpn_is_active
on connect_vpn(vpn_name)
tell application "System Events"
tell current location of network preferences
connect service vpn_name
end tell
end tell
end connect_vpn
on run argv
set vpn_name to "%s"
if my vpn_is_active(vpn_name) then
tell application "System Events"
tell current location of network preferences
disconnect service vpn_name
end tell
end tell
end if
end run
]], name))
end
function obj.connectVPN(name, kind)
if kind == "Viscosity" then
obj.connectViscosity(name)
elseif kind == "macOS" then
obj.connectMacOS(name)
end
end
function obj.connectViscosity(name)
code, output, descriptor = hs.osascript.applescript(string.format([[
-- Return true if VPN is active
on vpn_is_active(vpn_name)
tell application "Viscosity"
repeat with the_connection in connections
if (name of the_connection) is equal to vpn_name then
if (state of the_connection) is equal to "Connected" then
return true
else
return false
end if
end if
end repeat
end tell
return false
end vpn_is_active
-- Connect to specified VPN
on connect_vpn(vpn_name)
tell application "Viscosity"
connect vpn_name
end tell
end connect_vpn
on run argv
set vpn_name to "%s"
if my vpn_is_active(vpn_name) then
log "VPN " & quote & vpn_name & quote & " is already active."
return
end if
connect_vpn(vpn_name)
end run
]], name))
end
function obj.connectMacOS(name)
code, output, descriptor = hs.osascript.applescript(string.format([[
-- Return true if VPN is active
on vpn_is_active(vpn_name)
tell application "System Events"
tell current location of network preferences
return connected of current configuration of service vpn_name
end tell
end tell
end vpn_is_active
-- Connect to specified VPN
on connect_vpn(vpn_name)
tell application "System Events"
tell current location of network preferences
connect service vpn_name
end tell
end tell
end connect_vpn
on run argv
set vpn_name to "%s"
if my vpn_is_active(vpn_name) then
log "VPN " & quote & vpn_name & quote & " is already active."
return
end if
connect_vpn(vpn_name)
end run
]], name))
end
function obj.choicesVPNCommand(query)
local choices = {}
local connections = obj.getVPNConnections()
local img_connected = hs.image.imageFromPath(obj.seal.spoonPath.."/viscosity_locked.png")
local img_disconnected = hs.image.imageFromPath(obj.seal.spoonPath.."/viscosity_unlocked.png")
for k,v in pairs(connections) do
name = v["name"]
if string.match(name:lower(), query:lower()) then
state = v["state"]
kind = v["kind"]
local choice = {}
choice["text"] = name
choice["subText"] = state .. " (" .. kind .. ")"
if state == "Connected" then
choice["image"] = img_connected
else
choice["image"] = img_disconnected
end
choice["name"] = name
choice["state"] = state
choice["kind"] = kind
choice["plugin"] = obj.__name
choice["type"] = "toggle"
table.insert(choices, choice)
end
end
return choices
end
function obj.completionCallback(rowInfo)
if rowInfo["type"] == "toggle" then
if rowInfo["state"] == "Connected" then
obj.disconnectVPN(rowInfo["name"], rowInfo["kind"])
else
obj.connectVPN(rowInfo["name"], rowInfo["kind"])
end
end
end
return obj