自动打卡

1 minute read

背景

公司每天需要在 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 上重新弄前面的东西。不过这个时候就想到,既然都自动化提醒了,为啥不干脆自动化打卡呢,省得自己再打开浏览器了。于是,开始弄。

编写打卡脚本

基本上有两个操作思路:

  1. 脱离 UI,直接基于 GET/POST 等来和后端交互;
  2. 模拟前端交互。

考虑到:

  1. 打卡脚本没有性能(速度)的要求,因此 1 没有必要(反例是,之前写的刷课脚本 yusanshi/USTC-choose-course,需要“抢”,所以用的是思路 1);
  2. 打卡操作属于敏感行为,必然会有较多的安全性检测,用思路 1 较繁琐,思路 2 找几个 element 直接模拟操作即可,很方便;
  3. 如果网站改动,思路 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*,但是效果一直不理想,我主要碰到两个问题:

  1. GUI 难以显示出来;
  2. 在关机时,脚本难以被执行。

技术有限,折腾了大段时间也没有折腾出来好办法,最后只能使用自带的 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'"

效果如图。

Tags:

Updated:

Comments