Mac 睡眠之后微信不会退出附完美解决方案
z 2026-03-20
随笔
一直有个挺烦人的问题:
👉 Mac 上登录微信后,手机设置免提醒。但电脑睡眠之后手机基本就收不到通知了。
尤其是合上电脑盖子或睡眠之后,人已经走了,结果手机也不提醒,容易错过消息。
折腾了一下,搞了个比较稳定的自动方案:Mac 睡眠自动退出微信,唤醒自动恢复(稳定方案),效果还不错,记录一下。
# 问题本质
WeChat 的机制是:
PC 在线 → 手机不推送
PC 离线 → 手机恢复推送
1
2
2
所以核心思路其实很简单:
Mac 睡眠 → 让微信下线
Mac 唤醒 → 再自动打开微信
1
2
2
# 为什么不用 sleepwatcher
一开始试的是 sleepwatcher:
brew install sleepwatcher
1
确实能用,但有个问题:官方好久不更新了,就怕万一哪天系统升级或更新等就失效了。
# 最终方案:Hammerspoon(推荐)
用的是:Hammerspoon
它是基于 macOS 系统事件的自动化工具,比 sleepwatcher 稳很多。
# 实现效果
最终效果:
Mac 合盖 / 睡眠 → 自动退出微信 → 手机恢复通知 ✅
Mac 唤醒 → 自动打开微信(带重试)✅
1
2
2
基本无感使用。
如果你不想使用了,就把 Hammerspoon 这个软件退出就行。想用了,再重新打开即可。
# 安装
brew install --cask hammerspoon
1
启动后,编辑配置文件:
vim ~/.hammerspoon/init.lua
1
# 核心脚本
-- =============================================================
-- Mac睡眠 WeChat 自动控制(优雅退出 + 自动登录)
-- =============================================================
local APP_NAME = "WeChat"
local WECHAT_BUNDLE_ID = "com.tencent.xinWeChat"
local LOG_FILE = "/tmp/wechat_sleep.log"
-- ================================
-- 日志系统
-- ================================
local function logger(level, msg)
local f = io.open(LOG_FILE, "a")
if f then
f:write(os.date("%Y-%m-%d %H:%M:%S") .. " [" .. level .. "] " .. msg .. "\n")
f:close()
end
end
-- ================================
-- 工具方法
-- ================================
local function isWeChatRunning()
-- 方法 1: 通过 Hammerspoon 应用实例
local apps = hs.application.applicationsForBundleID(WECHAT_BUNDLE_ID)
if #apps > 0 then
return true
end
-- 方法 2: 通过系统进程
local ok, result, status = hs.execute("pgrep -x WeChat")
if ok and result and result:match("%d+") then
return true
end
return false
end
-- ================================
-- 方案 A: UI 菜单点击退出(优雅退出)
-- ================================
local function tryMenuQuit()
logger("INFO", "尝试方案 A: UI 菜单点击...")
local script = [[
tell application "System Events"
tell process "WeChat"
try
click menu item "退出微信" of menu "微信" of menu bar 1
return true
on error
try
click menu item "Quit WeChat" of menu "WeChat" of menu bar 1
return true
on error
return false
end try
end try
end tell
end tell
]]
local ok, result = hs.applescript.applescript(script)
if ok and result then
logger("ACTION", "方案 A 已触发菜单退出")
return true
else
logger("WARN", "方案 A 菜单点击失败")
return false
end
end
-- ================================
-- 方案 B: Cmd+Q 兜底
-- ================================
local function sendCmdQ()
local app = hs.application.get(APP_NAME)
if app then
app:activate()
app:unhide()
hs.timer.usleep(500000)
hs.eventtap.keyStroke({"cmd"}, "q", 0, app)
logger("ACTION", "方案 B 已发送 Cmd+Q")
else
logger("ERROR", "未获取到微信实例,Cmd+Q 失败")
end
end
-- ================================
-- 优雅退出流程
-- ================================
local function quitWeChatGracefully()
if not isWeChatRunning() then
return true
end
logger("ACTION", "开始优雅退出流程")
local app = hs.application.get(APP_NAME)
if app then
app:activate()
app:unhide()
hs.timer.usleep(500000)
end
local ok = tryMenuQuit()
if ok then
logger("ACTION", "方案 A 菜单点击已触发(退出成功)")
else
logger("WARN", "方案 A 菜单点击失败,尝试方案 B (Cmd+Q)...")
sendCmdQ()
end
-- 不再认为超时为 ERROR,改为 WARN 并保留实际状态
for i = 1, 10 do
if not isWeChatRunning() then
logger("INFO", "微信已退出")
return true
end
hs.timer.usleep(300000)
end
logger("WARN", "微信仍在运行,可能被后台拦截(无需强制退出)")
return false
end
-- ================================
-- 启动微信
-- ================================
local function startWeChat()
logger("INFO", "尝试启动微信...")
-- 尝试 launchOrFocus
hs.application.launchOrFocus(APP_NAME)
local maxWait = 5 -- 最多轮询 5 次
local i = 0
while i < maxWait do
if isWeChatRunning() then
logger("ACTION", "微信启动成功(launchOrFocus)")
return true
end
hs.timer.usleep(1000000) -- 每 0.5 秒检查一次
i = i + 1
end
-- fallback: open -a 命令
logger("WARN", "launchOrFocus 启动失败,尝试 open -a 命令")
hs.execute('open -a "WeChat"')
i = 0
while i < maxWait do
if isWeChatRunning() then
logger("ACTION", "微信启动成功(open 命令)")
return true
end
hs.timer.usleep(1000000)
i = i + 1
end
-- 最终仍未启动,才算失败
logger("ERROR", "微信启动失败")
return false
end
-- ================================
-- 网络检测
-- ================================
local function waitForNetwork(timeout)
local t = 0
while t < timeout do
local ok = hs.execute("ping -c 1 8.8.8.8 >/dev/null 2>&1")
if ok then
logger("INFO", "网络已恢复")
return true
end
hs.timer.usleep(1000000)
t = t + 1
end
logger("WARN", "网络检测超时")
return false
end
-- ================================
-- 停止微信流程(仅优雅退出,不强杀)
-- ================================
local function stopWeChat()
if not isWeChatRunning() then
logger("INFO", "微信未运行,无需处理")
return
end
logger("ACTION", "开始停止微信")
quitWeChatGracefully()
end
-- ================================
-- 系统睡眠/唤醒监听
-- ================================
local isSleeping = false
local watcher = hs.caffeinate.watcher.new(function(event)
if event == hs.caffeinate.watcher.systemWillSleep then
if isSleeping then return end
isSleeping = true
logger("SYSTEM", "=== 系统进入睡眠 ===")
stopWeChat()
elseif event == hs.caffeinate.watcher.systemDidWake then
logger("SYSTEM", "=== 系统已唤醒 ===")
hs.timer.doAfter(5, function()
waitForNetwork(10)
if not isWeChatRunning() then
logger("INFO", "尝试唤醒微信...")
startWeChat()
else
logger("INFO", "微信已在运行")
end
isSleeping = false
end)
end
end)
watcher:start()
logger("SYSTEM", "Hammerspoon: Mac睡眠 WeChat 自动控制插件加载成功(优雅退出 + 自动登录)")
-- ================================
-- 以下为测试脚本(注释状态,可按需启用)
-- ================================
--[[
local TEST_LOG_FILE = "/tmp/wechat_sleep.log"
local function testLogger(level, msg)
local f = io.open(TEST_LOG_FILE, "a")
if f then
f:write(os.date("%Y-%m-%d %H:%M:%S") .. " [" .. level .. "] " .. msg .. "\n")
f:close()
end
end
local function testGracefulQuit()
if not isWeChatRunning() then
testLogger("WARN", "微信未运行,请先手动开启微信再测试")
return
end
testLogger("ACTION", "--- 开始优雅退出测试 ---")
local aOk = tryMenuQuit()
hs.timer.doAfter(3, function()
if not isWeChatRunning() then
testLogger("ACTION", "方案 A 成功,微信已退出")
return
end
testLogger("WARN", "方案 A 未成功,尝试方案 B")
sendCmdQ()
hs.timer.doAfter(3, function()
if not isWeChatRunning() then
testLogger("ACTION", "方案 B 成功,微信已退出")
else
testLogger("ERROR", "方案 A/B 均未成功,微信仍在运行")
end
end)
end)
end
-- hs.hotkey.bind({"ctrl", "shift"}, "W", function()
-- testGracefulQuit()
-- end)
-- testLogger("SYSTEM", "微信退出测试脚本已加载。请按 Ctrl+Shift+W 开始测试")
--]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# 如何生效
修改完后:
打开 APP 应用:Hammerspoon → Reload Config
1
或者:
hs -c "hs.reload()"
1
# 效果验证
可以看日志:
tail -f /tmp/wechat_sleep.log
1
能看到:
========================================
2026-03-20 18:09:38 系统准备进入睡眠
========================================
2026-03-20 18:09:38 尝试优雅退出微信(osascript)
2026-03-20 18:09:39 优雅退出成功
2026-03-20 18:09:39 微信已完全退出
========================================
2026-03-20 18:09:53 系统已唤醒
========================================
2026-03-20 18:09:58 网络已恢复
2026-03-20 18:09:58 尝试启动微信,第 1 次
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 一些细节优化
# 1 为什么要强杀(pkill)
因为:osascript quit 不一定成功有时候微信会残留进程。
# 2 为什么要延迟 5 秒
因为刚唤醒时:
- Wi-Fi 可能还没连上
- 系统还没完全恢复
# 3 为什么要检测网络
避免:微信启动 → 没网 → 登录失败
# 适合人群
- Mac 常驻微信用户
- 经常合盖离开
- 依赖手机通知
# 总结
👉 用 Hammerspoon 接管系统事件,比“脚本 + 运气”靠谱得多。
这个方案的核心是:
监听系统睡眠/唤醒事件
+ 自动控制微信进程
+ 加上重试 网络检测
1
2
3
2
3
优点:
- 稳定
- 有日志(可排查)
- 基本无感
赞赏一下

「真诚赞赏,手留余香」
# 打赏记录
| 打赏者 | 打助金额 (元) | 支付方式 | 时间 | 备注 |
|---|---|---|---|---|
| John | 12 | 微信 | 2020-06-09 | |
| 艾斯 | 32 | 支付宝 | 2020-07-11 | nice |
| HickSalmon | 15 | 微信 | 2020-09-21 | 有赏交流 |