文字描述比较少,有遇到问题可以V本人。
公司wifi需要登录认证网页,手动输入账号和密码,验证完成后,才能开始上网。如果电脑合盖,或者睡眠。再次打开需要重新在打开网页,输入账号密码,连接。就和我们在KFC或者是机场、酒店,连接了wifi会弹出一个认证的窗口。
首先想到的是把url的请求地址拿出来,通过curl的请求去完成认证。
这个有一个问题是当Captive Network Assistant
弹出的时候,网络会被拦截,这个英语翻译成中文的意思强制网络助理
。只要有这个弹窗在,网络都会被终止(没有实际性的原理,亲测是网络被拦截)最早是使用sleepwatcher来执行,当mac唤醒的时候,执行脚本。
有个问题就是当切换网络的时候,就没办法执行脚本了。只能在唤起的时候使用
解决方案
使用hammerspoon+lua+applescript来解决
思路
- 监听网络切换和电脑的唤醒、解锁
- 监听到事件,查找是否有目标wifi在,如果有,强制连接目标wifi
- 监听
Captive Network Assistant
的弹窗,如果弹窗存在,执行applescript的脚本,自动输入账号密码
安装hammerspoon
- 使用brew安装
没有安装过brew的话可以先执行
/usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install)"
然后安装
brew install hammerspoon --cask
- 下载app,自行百度或者官网下载。这边就不做更多的说明
- 安装完成后,顶部会有一个锤子的图标,点击红框部分给到权限。可以开机的时候就启动hammerspoon
-
小技巧,可以把图标也隐藏。通过command + 空格,唤起搜索,搜索ha开头,可以再打开设置界面。
-
在~目录下可以看到有这个文件。hammerspoon会先执行init.lua
开发环境
init.lua 判断了如果是自己的mac,才会执行自动更新脚本。这样只要有修改,hammerspoon的控制台就会有log,方便做调试
require "wifi.wifi"
if (hs.host.localizedName() == 'Radish') then
local pathwatcher = require "hs.pathwatcher"
local alert = require "hs.alert"
-- 开启自动重载配置文件
function autoReload()
function reloadConfig(files)
doReload = false
for _, file in pairs(files) do
if file:sub(-4) == ".lua" then
doReload = true
end
end
if doReload then
hs.reload()
end
end
pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon", reloadConfig):start()
end
autoReload()
end
wifi
wifi的脚本思路和上面所述的一样。主要监听了
监听电脑休眠和唤醒
和wifi发生改变
和创建应用程序监控器
。唤醒的时候,去扫描后台有没有目标WIFINAME
,有的话,就连接目标wifi,执行applescript脚本
local WIFINAME = "Dianei";
-- local USERNAME = "1111"
-- local PASSWORD = "2222"
-- 异步执行shell
function asyncSheel(cmd, callback)
hs.task.new("/bin/sh", function(exitCode, stdOut, stdErr)
if exitCode ~= 0 then
print("Error executing command: " .. stdErr)
callback()
else
print("Command output: " .. stdOut)
callback(stdOut)
end
end, {"-c", cmd}):start()
end
-- 执行shell脚本
function shell(cmd)
local success,obj,descriptor = hs.osascript.applescript(string.format('do shell script "%s"', cmd))
return success,obj,descriptor
end
-- 判断网络是否连接
function isNet()
local success,obj,descriptor = shell("ping -o -t 1 baidu.com")
if (success and obj ~= nil and string.find(descriptor, '1 packets received')~= nil) then
return true
else
return false
end
end
function wifiCallback(watcher, message, interface)
local ssid = hs.wifi.currentNetwork(interface)
if (ssid ~= nil and ssid == WIFINAME) then
print("当前wifi列表中存在"..ssid)
-- scan()
end
end
function caffeinateCallback(event)
if event == hs.caffeinate.watcher.screensDidSleep then
-- 屏幕休眠(屏保开启)
print("screensDidSleep")
elseif event == hs.caffeinate.watcher.screensDidWake then
-- 屏幕唤醒
print("screensDidWake")
elseif event == hs.caffeinate.watcher.screensDidLock then
print("screensDidLock")
elseif event == hs.caffeinate.watcher.screensDidUnlock then
-- 屏幕解锁
print("screensDidUnlock")
-- 强制开启wifi的按钮
asyncSheel("networksetup -setairportpower en0 on", function()
print('强制开启了wifi按钮')
-- 后台扫描WiFi热点
hs.wifi.backgroundScan(function(networks)
-- 处理扫描结果
print("wifi 扫描完成")
for i, network in ipairs(networks) do
if (network.ssid == WIFINAME or network.bssid == WIFINAME) then
print("找到 wifi名字了")
-- 连接wifi 等待cna的弹窗
hs.wifi.associate(WIFINAME, "")
break
end
end
end)
end)
elseif event == hs.caffeinate.watcher.systemWillSleep then
print("systemWillSleep")
elseif event == hs.caffeinate.watcher.systemDidWake then
print("systemDidWake")
elseif event == hs.caffeinate.watcher.systemWillPowerOff then
print("systemWillPowerOff")
elseif event == hs.caffeinate.watcher.systemDidPowerOn then
print("systemDidPowerOn")
end
end
-- 应用程序激活事件处理
function applicationCallback(name, event, app)
-- and event == hs.application.watcher.launched
if name == "Captive Network Assistant" or name == "强制网络助理" then
-- 启动
-- print(hs.application.watcher.launched)
-- -- 激活
-- print(hs.application.watcher.activated)
-- 在 Captive Network Assistant 打开时自动输入账号密码
if event == hs.application.watcher.activated then
print("激活cna了 ~ ")
asyncSheel("osascript ~/.hammerspoon/wifi/cna.applescript", function(e)
print("执行完毕")
local haveNet = isNet()
if (haveNet == true) then
hs.alert.show(WIFINAME.."网络已连接")
end
end)
end
end
end
-- 监听电脑休眠和唤醒
hs.caffeinate.watcher.new(caffeinateCallback):start()
-- wifi发生改变
hs.wifi.watcher.new(wifiCallback):start()
-- 创建应用程序监控器
hs.application.watcher.new(applicationCallback):start()
function getDianneiWifi()
return hs.window.filter.new(false):setAppFilter("Captive Network Assistant", { currentSpace = true, allowTitles = "加入“Dianei”" }):getWindows()
end
-- hs.application:getWindow(title)
-- function launchOrNextWindow(name, showName)
-- local findName = showName or name
-- local appName = hs.application.frontmostApplication():name()
-- print("appName")
-- print(appName)
-- if findName ~= appName then
-- hs.application.launchOrFocus(name)
-- else
-- -- local wlist = getWinList(findName)
-- -- local wcount = #wlist
-- -- if wcount > 1 then
-- -- hs.eventtap.keyStroke({'cmd'}, '`')
-- -- else
-- -- local win = wlist[1]
-- -- if win:isMinimized() then win:unminimize() else win:minimize() end
-- -- end
-- end
-- end
-- function mapLaunch(key, name, showName)
-- hs.hotkey.bind({}, key, function()
-- launchOrNextWindow(name, showName)
-- end)
-- end
-- mapLaunch('f1', 'Google Chrome')
-- launchOrNextWindow('iTerm', 'iTerm2')
applescript
需要注意的是,vscode中默认是UTF-8的编码,applescript要设置为GB2312。不然有中文的会识别不了
global WIFINAME
global USERNAME
global USERPASSWORD
global ROOPNUM
global DELAYTIME
global NOTWIFI
set WIFINAME to "xxxx"
set USERNAME to "xxx"
set USERPASSWORD to "xxxx"
set ROOPNUM to 5
set DELAYTIME to 0.5
set roopCount to 0
repeat while roopCount < ROOPNUM
if isNet() = true then
log "net connct success"
my closeDianei()
exit repeat
else
if my getCNAAlert() = 1 then
log "find cna"
-- begin connect
my connectWIFI()
end if
set roopCount to roopCount + 1
end if
end repeat
on closeDianei()
tell application "System Events"
-- 是否存在 CNA 弹窗
if exists (process "Captive Network Assistant") then
-- 进入CNA的进程
tell process "Captive Network Assistant"
-- 是否存在dianei的wifi连接弹窗
if exists (window "加入“Dianei”") then
-- 进入dianeiwif连接的弹窗
tell window "加入“Dianei”"
-- 进入UI界面
if exists (button "完成") then
click button "完成"
end if
if exists (button "取消") then
click button "取消"
end if
end tell
end if
end tell
end if
end tell
end closeDianei
on getCNAAlert()
-- 系统事件
tell application "System Events"
if exists (process "Captive Network Assistant") then
return 1
else
return 2
end if
end tell
end getCNAAlert
on connectWIFI()
-- 系统事件
tell application "System Events"
-- 是否存在 CNA 弹窗
if exists (process "Captive Network Assistant") then
-- 进入CNA的进程
tell process "Captive Network Assistant"
-- 是否存在dianei的wifi连接弹窗
if exists (window "加入“Dianei”") then
-- 进入dianeiwif连接的弹窗
tell window "加入“Dianei”"
-- 进入UI层级 从这个命令去找 entire contents 找到对应想要的名称。比如用户名 密码 注销等等
if exists (UI element 1 of scroll area 1 of group 1 of group 2) then
-- UI层架
tell UI element 1 of scroll area 1 of group 1 of group 2
set groupTag to 0
repeat while groupTag < 50
try
if my isNet() = true then
exit repeat
else
if exists (text field 1 of group 1 of group groupTag) then
log "开始自动输入用户名和密码"
-- 用户名
tell group 1 of group groupTag
set value of attribute "AXFocused" of text field 1 to true
set value of text field 1 to USERNAME
end tell
-- 密码
tell group 2 of group groupTag
set value of attribute "AXFocused" of text field 1 to true
set value of text field 1 to USERPASSWORD
end tell
-- 勾选框
tell group (groupTag + 1)
click checkbox 1
end tell
-- 登录按钮
tell group (groupTag + 2)
click button "登录"
end tell
exit repeat
else
set groupTag to groupTag + 1
end if
end if
end try
end repeat
end tell
end if
end tell
end if
end tell
end if
end tell
end connectWIFI
on isNet()
try
set pingResult to do shell script "ping -o -t 1 baidu.com"
if pingResult contains "1 packets received" then
return true
else
return false
end if
on error
return false
end try
end isNet