当前位置:首页>python>用 Python 实现 WorkBuddy 每日自动领积分,踩了这些坑才搞定

用 Python 实现 WorkBuddy 每日自动领积分,踩了这些坑才搞定

  • 2026-10-11 06:38:44
用 Python 实现 WorkBuddy 每日自动领积分,踩了这些坑才搞定

用 Python 实现 WorkBuddy 每日自动领积分,踩了这些坑才搞定

本文记录了一次真实的 Windows 桌面自动化实战:用 Python ctypes 控制鼠标,自动完成 WorkBuddy 软件的每日签到领积分操作,并配置 Windows 任务计划程序实现每天定时自动执行。

起因

WorkBuddy 每天登录可以领取积分,但每天手动点几下感觉很麻烦。

能不能写个脚本,让电脑自动完成这几次点击?

答案是可以,但过程并没有想象中简单。

整体思路

签到流程只有两步:

  1. 点击左下角用户头像,展开菜单
  2. 点击菜单中的「立即领取」按钮

看起来很简单,实际上踩了不少坑。

环境准备

  • 系统
    :Windows 10
  • 分辨率
    :2560 × 1440,DPI 缩放 200%
  • Python
    :3.13(WorkBuddy 内置 Python 版本)

第一步:找到按钮的屏幕坐标

自动点击的前提是知道要点哪里。

写了一个坐标追踪工具,将鼠标移到目标位置,按 F9 记录坐标:

python📋 长按下文选中复制

import ctypes, ctypes.wintypes, timeuser32 = ctypes.windll.user32kernel32 = ctypes.windll.kernel32# 设置 DPI 感知,确保坐标是物理像素ctypes.windll.shcore.SetProcessDpiAwareness(2)# 按 F9 记录当前鼠标坐标,按 F10 退出print("移动鼠标到目标位置,按 F9 记录,按 F10 退出")coords = []while True:    if kernel32.GetAsyncKeyState(0x79) & 0x8000:  # F10 退出        break    if kernel32.GetAsyncKeyState(0x78) & 0x8000:  # F9 记录        pt = ctypes.wintypes.POINT()        user32.GetCursorPos(ctypes.byref(pt))        coords.append((pt.x, pt.y))        print(f"记录坐标: ({pt.x}, {pt.y})")        time.sleep(0.5)    time.sleep(0.05)print("所有坐标:", coords)

实测采集到的坐标:

  • 用户菜单按钮:`(86, 1293)`
  • 立即领取按钮:`(234, 637)`

第二步:写点击脚本

有了坐标,开始写自动点击。

⚠ 首次踩坑

最初尝试用 `SendInput`(Windows 官方推荐的模拟输入 API),结果在 DPI 200% 下坐标归一化不准,点偏了。

最终稳定下来的方案是:SetCursorPos 移动光标 + mouse_event 执行点击。

python📋 长按下文选中复制

import ctypes, ctypes.wintypes, timeuser32 = ctypes.windll.user32ctypes.windll.shcore.SetProcessDpiAwareness(2)COORD_USER_MENU   = (86, 1293)   # 用户菜单按钮坐标COORD_CHECKIN_BTN = (234, 637)   # 立即领取按钮坐标def click(x, y):    user32.SetCursorPos(x, y)      # 移动光标到目标位置    time.sleep(0.3)                # 等待光标稳定    user32.mouse_event(0x0002, 0, 0, 0, 0)  # 鼠标左键按下    time.sleep(0.08)    user32.mouse_event(0x0004, 0, 0, 0, 0)  # 鼠标左键释放# 激活 WorkBuddy 窗口hwnd = user32.FindWindowW(None, "WorkBuddy")user32.SetForegroundWindow(hwnd)user32.BringWindowToTop(hwnd)time.sleep(0.6)# 点击用户菜单click(*COORD_USER_MENU)time.sleep(2.5)   # 等待菜单展开# 点击领取积分click(*COORD_CHECKIN_BTN)print("签到完成!")

第三步:踩到最大的坑——UIPI 权限隔离

脚本写好了,鼠标能移动,却怎么也点不进去。WorkBuddy 完全没反应。

找了很久,查了 Windows 官方文档,才发现根本原因:

**Windows UIPI(用户界面特权隔离)**:从 Vista 开始,低权限进程无法向高权限进程发送模拟鼠标/键盘事件。

WorkBuddy 以管理员权限运行,而普通 CMD 里的 Python 脚本是普通权限——两者之间有一道隔离墙,mouse_event 发出的点击事件直接被系统底层丢弃,目标程序根本没收到。

💡 解决方法

**以管理员身份运行 Python 脚本。**右键开始菜单 → **Windows PowerShell(管理员)** → 运行脚本。

改完立刻成功,菜单展开,积分到账。

第四步:配置 Windows 定时任务

手动运行还不够,要实现每天 8:00 自动签到。

在管理员 PowerShell 中运行以下命令(一次性配置,永久生效):

powershell📋 长按下文选中复制

$action = New-ScheduledTaskAction `    -Execute "C:\Users\xyf\.workbuddy\binaries\python\versions\3.13.12\python.exe" `    -Argument "C:\Users\xyf\WorkBuddy\checkin\test_move.py"$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"$principal = New-ScheduledTaskPrincipal `    -UserId "$env:USERDOMAIN\$env:USERNAME" `    -LogonType Interactive `    -RunLevel Highest   # 以最高权限(管理员)运行,解决 UIPI 问题$settings = New-ScheduledTaskSettingsSet `    -StartWhenAvailable    # 如果 8:00 时锁屏,解锁后自动补跑Register-ScheduledTask `    -TaskName "WorkBuddy每日签到" `    -Action $action `    -Trigger $trigger `    -Principal $principal `    -Settings $settings `    -Force

关键参数说明

参数
作用
备注
`-RunLevel Highest`
以管理员权限运行
解决 UIPI 拦截问题,**必须设置**
`-StartWhenAvailable`
错过触发时间后自动补跑
锁屏/关机后解锁开机时自动执行
`-LogonType Interactive`
仅在用户已登录时运行
需要桌面环境才能执行鼠标点击
`-Daily -At "08:00"`
每天 8:00 触发
可根据需要修改时间

定时任务优化建议

创建任务后,打开任务计划程序(Win + R → taskschd.msc),找到 "WorkBuddy每日签到" 任务,右键 → 属性:

  1. 常规
    :勾选「使用最高权限运行」,保证脚本中点击功能生效
  2. 触发器
    :增加一条「解锁时触发」,确保锁屏状态下也能自动执行
  3. 操作
    :确认 Python 路径和 `test_move.py` 路径正确

踩坑汇总

坑
现象
原因
解决
**SendInput 坐标偏移**
鼠标点到错误位置
DPI 200% 下归一化不准
改用 `mouse_event`(无 ABSOLUTE 标志)
**点击无效**
鼠标移动正常但无响应
UIPI 权限隔离
以管理员身份运行脚本
**GetConsoleWindow 报错**
AttributeError
该函数在 kernel32 而非 user32
使用 `kernel32.GetConsoleWindow()`
**最小化后定位失败**
SetCursorPos 返回 0
窗口最小化影响定位
不要最小化 CMD,直接切到目标窗口
**坐标采集不准**
脚本点到空白处
复用了其他分辨率的坐标
必须在本机重新采集

总结

这次自动化开发的核心收获:

  1. Windows 桌面自动化首选 `SetCursorPos + mouse_event`
    ,`SendInput` 在高 DPI 环境下坐标换算容易出问题
  2. UIPI 权限隔离是最常见的点击失效原因
    ,遇到鼠标能移动但点击无反应,第一个想到的就是权限问题
  3. 坐标必须在目标机器上实时采集
    ,不同分辨率、不同缩放比例的坐标不能复用

整个开发过程大概花了 2 个小时,其中 80% 的时间都在排查 UIPI 这一个问题。希望这篇文章能帮你少走弯路。

💡 小贴士

如果你觉得配置过程比较复杂,可以把文档发给 WorkBuddy,让它学习一遍后帮助你一起完成。后续 WorkBuddy 软件更新导致按钮位置发生变化,可以重新定位一次坐标信息。

附录:完整 test_move.py 脚本

python📋 长按下文选中复制

# test_move.py - 鼠标移动诊断import ctypes, ctypes.wintypes, timeuser32 = ctypes.windll.user32kernel32 = ctypes.windll.kernel32# 尝试各种 DPI 感知设置print("=== DPI 感知测试 ===")try:    ctypes.windll.shcore.SetProcessDpiAwareness(2)    print("[OK] SetProcessDpiAwareness(2) 成功")except Exception as e:    print(f"[失败] SetProcessDpiAwareness: {e}")    try:        user32.SetProcessDPIAware()        print("[OK] SetProcessDPIAware() 成功")    except Exception as e2:        print(f"[失败] SetProcessDPIAware: {e2}")# 获取 DPI 值try:    from ctypes import wintypes    class POINT(ctypes.Structure):        _fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)]    # 获取主显示器 DPI    hdc = user32.GetDC(0)    dpi = ctypes.windll.gdi32.GetDeviceCaps(hdc, 88)  # LOGPIXELSX    print(f"当前 DPI: {dpi}")    ctypes.windll.gdi32.ReleaseDC(0, hdc)except Exception as e:    print(f"获取 DPI 失败: {e}")# 测试1:移动到屏幕中心sw = user32.GetSystemMetrics(0)  # SM_CXSCREENsh = user32.GetSystemMetrics(1)  # SM_CYSCREENprint(f"\n屏幕分辨率(GetSystemMetrics): {sw} x {sh}")cx = sw // 2cy = sh // 2print(f"尝试移动到屏幕中心: ({cx}, {cy})")ret = user32.SetCursorPos(cx, cy)print(f"SetCursorPos 返回值: {ret} (0=失败, 非0=成功)")time.sleep(2)# 验证实际位置pt = wintypes.POINT()user32.GetCursorPos(ctypes.byref(pt))print(f"GetCursorPos 实际位置: ({pt.x}, {pt.y})")if pt.x == cx and pt.y == cy:    print("\n✅ SetCursorPos 工作正常!")else:    print(f"\n❌ 坐标不匹配!目标({cx},{cy}) 实际({pt.x},{pt.y})")    print("   可能原因:DPI 缩放导致坐标被系统自动换算")# 测试2:移动到 (100, 1300) 附近print(f"\n尝试移动到 (100, 1300)...")user32.SetCursorPos(100, 1300)time.sleep(1)user32.GetCursorPos(ctypes.byref(pt))print(f"实际位置: ({pt.x}, {pt.y})")# ============================================================# 签到功能(对齐 debug_step3c.py 的成功方案:mouse_event 点击)# ============================================================COORD_USER_MENU   = (86, 1293)COORD_CHECKIN_BTN = (234, 637)MOUSEEVENTF_LEFTDOWN = 0x0002MOUSEEVENTF_LEFTUP   = 0x0004def send_click(x, y, label=""):    """SetCursorPos 定位 + mouse_event 点击(对齐 debug_step3c.py 的成功代码)"""    ret = user32.SetCursorPos(x, y)    time.sleep(0.3)    pt = wintypes.POINT()    user32.GetCursorPos(ctypes.byref(pt))    print(f"  [{label}] 目标({x},{y}) 实际({pt.x},{pt.y})  ret={ret}")    user32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)    time.sleep(0.08)    user32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)    print(f"  [{label}] 点击已发送")    time.sleep(0.15)# --- 查找并激活 WorkBuddy 窗口(对齐 debug_step3c.py)---print("\n=== 签到开始 ===")hwnd = user32.FindWindowW(None, "WorkBuddy")print(f"  FindWindowW 返回值: {hwnd} (0=未找到)")if hwnd == 0:    print("[错误] 找不到 WorkBuddy 窗口,请确认程序正在运行")    exit(1)print(f"  [OK] 找到窗口,句柄: 0x{hwnd:08X}")# 激活窗口(与 debug_step3c.py 完全相同的方式)print("  激活窗口...")try:    class WP(ctypes.Structure):        _fields_ = [            ("length", ctypes.c_uint),            ("flags", ctypes.c_uint),            ("showCmd", ctypes.c_uint),            ("ptMinPosition", ctypes.wintypes.POINT),            ("ptMaxPosition", ctypes.wintypes.POINT),            ("rcNormalPosition", ctypes.wintypes.RECT),        ]    wp = WP()    wp.length = ctypes.sizeof(wp)    user32.GetWindowPlacement(hwnd, ctypes.byref(wp))    print(f"  窗口状态: {wp.showCmd} (2=最小化, 1=正常, 3=最大化)")    if wp.showCmd == 2:        user32.ShowWindow(hwnd, 9)        print("  已恢复最小化窗口")        time.sleep(0.8)    user32.SetForegroundWindow(hwnd)    user32.BringWindowToTop(hwnd)    print("  [OK] 窗口已激活")    time.sleep(0.6)except Exception as e:    print(f"  [ERROR] 激活窗口失败: {e}")# --- 点击用户菜单 ---print(f"\n[1/2] 点击用户菜单 ({COORD_USER_MENU[0]}, {COORD_USER_MENU[1]})")send_click(*COORD_USER_MENU, label="用户菜单")print("  -> 等待菜单展开 (1s)...")time.sleep(1)# --- 点击签到按钮 ---print(f"\n[2/2] 点击签到按钮 ({COORD_CHECKIN_BTN[0]}, {COORD_CHECKIN_BTN[1]})")send_click(*COORD_CHECKIN_BTN, label="立即领取")time.sleep(0.5)print("\n[完成] 签到点击序列执行完毕。")

如果你也在用 WorkBuddy,欢迎留言交流

或者分享你遇到的其他自动化踩坑经验 🙌

最新文章

随机文章