以前我用 AHK 实现过滴答清单的任务顺延,在这里 https://zhuanlan.zhihu.com/p/681570615。这个方案的缺点是需要保持滴清单在前台,同时我不能操作鼠标和键盘干扰 AHK 脚本运行。有一天突然想到,滴答清单有 web 版本,那么利用 tampermonkey 脚本 应该能达到相同效果。
愿景,要达到的效果是 每天的超期任务,顺延到今天。
需求分析,需要执行一系列的动作。包括点击某个控件,在新弹出的某些控制上再点击,诸如此类。
技术原型,需要能 找到特定控件、点击特定控件。此外,还要有入口。我尝试了裁入即触发脚本,失败了。时间久远,我已经想不起来到底是脚本没运行,还是脚本运行找不到控件。总之,我改成了在界面上加了个大按钮,每次手动点击这个按钮触发脚本运行。
动作顺列如下。
第1步 入口
第2步 把分组切换到按 时间,这样会出现 顺延 按钮。
第3步 把分组切换回按 清单。
定位控件的方法是根据 css 的 class 或者 xpath。写代码时,我打开 w3school | css 页面,当作忘记语法时的速查表。
实现过程 略,参见文末的代码。
这个方法和 AHK脚本 相比,优点是可以不必把滴答清单置于前台,甚至把滴答清单的页面或浏览器最小化也没有影响;不独占鼠标和键盘,因为代码调用 click 函数,而不是发送鼠标和键盘事件。
跑了两三个月以后,有一天突然不好使了。用手动完成了两三天,找时间看了一遍动作过程,有按钮不点击了。F12看源代码,发现滴答清单的 web 升级过。这时 AI 已经相当成熟,所以把那段 html 截下来问了豆包,请她给出代码。把这段代码插入到我原有的脚本里,一切又顺畅了。
表面上看,这是一段方便工作的脚本。实质上,在技术路线选择和技术手段上,这属于 软件工程的 自动化测试。有同学告诉我,这个现在叫作 RPA (rpa robotic process automation)。虽然核心的业务逻辑非常简单,但是仍然要花些时间,主要的精力消耗在如何定位控件上。遥想当年 Unix 系统通过 重定向和管道机制,仅解析文本,就能够方便地做各种自动化的工作。后来出现了人类用起来方便的 GUI 界面,机器的自动化变得比以前麻烦很多,最主要的原因就是找控件麻烦而琐碎。也许以后 AI 高度发展,自动化的动作又可以非常普及了吧。
此文也发布在以下站点。
----
知乎 https://www.zhihu.com/people/yang-gui-fu-52
微信公众号 杨贵福
----
以下是我曾经发布博客的站点,有些旧文。
----
豆瓣 - 因为审核"我的日记",不再更新。
https://www.douban.com/people/younggift/?_i=0098558fqLUL9h
CSDN – 因为要求我登记手机号码的原因是“为了您的安全”,不再更新。
https://blog.csdn.net/younggift?type=blog
blogsopt – 因为从我的机器不可达,无法更新
附 代码
// ==UserScript== // @name 滴答清单 顺延 // @namespace http://tampermonkey.net/ // @version 2025-01-18 // @description try to take over the world! // @author Young // @match https://dida365.com/webapp/ // @icon https://cncdn.dida365.com/static/img/favicon.ico // @grant none // ==/UserScript== $('body').append('<font color=red><input type="button" value="顺延" id="CP" border=1></input></font>') $("#CP").css("position", "fixed").css("top", 200).css("left", 150).css("color","white").css("background-color","#04AA6D").css("padding","15px 32px"); $('#CP').click(function(){ gogogo(); }); (function() { 'use strict'; // alert("here."); } )(); function sleep1(time) { time*=1000 return new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); } async function gogogo() { // 今天 $("p:contains('今天')")[0].click(); await sleep1(2); // 上下箭头 $("#group-order-option").click(); await sleep1(2); // 分组 $("div[data-key='groupBy']").click(); await sleep1(2); // 时间 //$("div[data-key='dueDate']").click(); // 20250321 问了豆包 $('li[data-menu-id*="dueDate"]').eq(0).click(); // 上下箭头,收起菜单 // $("#group-order-option").click(); $('div[id="group-order-option"]').click(); await sleep1(2); // 折叠 "今天" 如果是展开的 if($("span > svg.-rotate-90").length==0 && $("h6:contains('今天')").length!=0) $("h6:contains('今天')")[0].click(); await sleep1(2); // 顺延 如果顺延不存在,不会有影响 $("div:contains('顺延')[tabindex='0']").click(); await sleep1(2); // 确认"顺延" $("button:contains('顺延')").click(); await sleep1(2); // 上下箭头 $("#group-order-option").click(); await sleep1(2); // 分组 $("div[data-key='groupBy']").click(); await sleep1(2); // 清单 //$("div[data-key='project']").click(); // 20250321 问了豆包 $('li[data-menu-id*="project"]').eq(0).click(); await sleep1(4); // 上下箭头 //$("#group-order-option").click(); $('div[id="group-order-option"]').click(); await sleep1(2); alert("顺延完成。"); }