自动打卡
背景
公司每天需要在 ERP 系统上打卡:上午进公司一次,晚上离开一次。但是常常会忘记,忘记之后就得给 leader 提打卡异常申请,比较麻烦。
因此希望能自动提醒打卡。
经过
Reminders App
IOS 自带的 Reminders App 可以添加根据地点的提醒,于是我添加了两个 reminders:接近公司时和离开公司时。但是这个 App 没法限制时间范围,导致任何时候接近或离开公司都会有提醒,于是平时中午、晚上吃个饭也会有提醒,很乱;提醒多了,提醒的效力就下降,有时候还是会被忽略。
Reminders on Windows
最初我在公司用的主力设备是公司提供的 ThinkPad 笔记本,就打算在它开机和关机的时候提醒打卡。
创建提醒文件
创建 clock-in.vbs
文件:
MsgBox "Clock in!", 0, "Clock in!"
直接运行它,能弹出一个提醒打卡的提示窗。
在开关机时运行
按照 https://stackoverflow.com/questions/12434863/executing-a-batch-script-on-windows-shutdown 的提示,把 Startup 和 Shutdown 都添加上 clock-in.vbs
。
不过,接着搜索,又发现在 User Configuration 下有类似的 Logon/Logoff(前面的 Startup/Shutdown 是在 Computer Configuration 下)。实验对比发现,用 Logon/Logoff 更好一些。
于是,在开机的时候会有个弹窗,再关机的时候,Windows 会闪来闪去(在桌面和纯蓝背景之间),然后弹出来弹窗——体验不太好,但是能用。
Auto Clock-in on Ubuntu
后来我把工作主力换成了自己带过来的游戏本,用 Ubuntu 20.04,于是我在 Ubuntu 上重新弄前面的东西。不过这个时候就想到,既然都自动化提醒了,为啥不干脆自动化打卡呢,省得自己再打开浏览器了。于是,开始弄。
编写打卡脚本
基本上有两个操作思路:
- 脱离 UI,直接基于 GET/POST 等来和后端交互;
- 模拟前端交互。
考虑到:
- 打卡脚本没有性能(速度)的要求,因此 1 没有必要(反例是,之前写的刷课脚本 yusanshi/USTC-choose-course,需要“抢”,所以用的是思路 1);
- 打卡操作属于敏感行为,必然会有较多的安全性检测,用思路 1 较繁琐,思路 2 找几个 element 直接模拟操作即可,很方便;
- 如果网站改动,思路 1 很有可能需要大改,但思路 2 改动很方便。
选择了思路 2,使用的是 Selenium 和 Chromium Driver。
编写 clock-in.py
如下(模拟 UI 交互的部分涉密,故略去):
##!/usr/bin/env python3
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.utils import ChromeType
from time import sleep
from datetime import datetime
import requests
import base64
import os
import logging
def clock_in():
options = webdriver.ChromeOptions()
options.add_argument(
'user-data-dir=/home/yu/snap/chromium/common/chromium')
driver = webdriver.Chrome(
ChromeDriverManager(chrome_type=ChromeType.CHROMIUM).install(),
options=options)
# Do the trick
pass
if __name__ == '__main__':
logging.basicConfig(
level=logging.INFO,
handlers=[
logging.FileHandler(
os.path.join(os.path.dirname(os.path.realpath(__file__)),
'log', f'{datetime.now().isoformat()}.txt')),
logging.StreamHandler()
],
)
logging.info('Begin')
for _ in range(20):
try:
clock_in()
logging.info('Success')
break
except Exception as e:
logging.error(e)
logging.info('Retrying')
sleep(30)
else:
logging.info('Failed')
注意这里指定了 user-data-dir
,这是因为使用新浏览器登录 ERP 网站需要通过手机验证,如果不手动指定 user-data-dir
,那么每次运行脚本都会使用新的 profile,会被 ERP 网站认为是新浏览器,所以还需要重新手机验证。user-data-dir
的使用参考自 https://stackoverflow.com/a/48665557/8418049。
在开关机时运行
这个过程被我弄得很繁琐,尝试过 Systemd Service、/etc/rc*
,但是效果一直不理想,我主要碰到两个问题:
- GUI 难以显示出来;
- 在关机时,脚本难以被执行。
技术有限,折腾了大段时间也没有折腾出来好办法,最后只能使用自带的 Startup Applications,但是它没法关机时运行。
没办法,退而求其次,把程序改成这样的逻辑:
## Run on startup
run()
while True:
sleep(600)
if current_time in evening:
# Run in evening
run()
即开机打卡一次后,每隔一段时间检测一次,如果是在晚上就打卡。
很繁琐,毕竟晚上的时候脚本会时不时跳出来。
不爽了两天后,想把它改一改,决定做个自定义的关机按钮:点了按钮后,先打卡,再执行真正的关机操作。
调研的时候,先是决定写一个 GNOME Shell Extension,看文档有点繁琐。
然后发现了 argos 这个神器,可以分分钟实现 button-and-dropdown 的功能,但是在 Ubuntu 20.04 上,dropdown 没法显示,找 issue 发现是 GNOME 版本过高,于是使用 https://github.com/rammie/argos/tree/gnome-3.36 解决之。
仿照给的 demo 编写 clock-in-then.sh
文件如下:
##!/usr/bin/env bash
echo "Clock-in Then | iconName=appointment-new-symbolic"
echo "---"
echo "Log Out | iconName=system-log-out bash='/home/yu/clock-in/clock-in.py && gnome-session-quit --logout --no-prompt'"
echo "Shutdown | iconName=system-shutdown bash='/home/yu/clock-in/clock-in.py && shutdown -h now'"
echo "Reboot | iconName=system-reboot bash='/home/yu/clock-in/clock-in.py && reboot'"
效果如图。
Comments