Monash Study Kit 原来只读 Moodle 和 Ed。邮箱和考勤我一直在自己的私人部署上用,10 月 31 日发布的 1.5.0 把它们搬进了公开版。两部分都是可选的,接上之前什么都不会多。
这次和以前最大的不同是:它第一次能改东西了。起草邮件、发草稿、归档、签到,做错了都收不回来。所以这篇主要讲这些操作是怎么管住的,还有几处为了让同学用得下去做的取舍。
会改东西的,都分两步
AI 不能一步就执行写操作。它先调用 prepare,拿到一份完整预览:收件人、密送、正文、附件,或者要改的标签,或者是哪节课、哪个签到码。这份预览连同它的 SHA-256 存成一张凭据:
body = json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
digest = hashlib.sha256(body.encode()).hexdigest()
执行时要带上凭据编号、这个哈希,还要 user_confirmed=true。服务器给 AI 的说明里写着:预览要原样给用户看,不能替用户确认。凭据 10 分钟过期,只能用一次。执行前先把它标成“正在执行”再去联系 Gmail,所以同时点两次也不会发出两封。发送草稿前还会再核对一次草稿内容,预览之后被改过就拒绝。
出错也分两种。执行前的检查没过,记成“已拒绝”,可以确定什么都没发出去;真正发请求时出错,记成“结果未知”,不会自动重试,要先去邮箱里看看到底发没发。签到只有你说自己实际到课了才会准备,而且不能给还没开始或已经签过的课提交。
一开始我让这些写操作默认关着,要用户自己去设置里打开。后来发现这样很别扭:同学登录完问 AI“帮我把这封归档”,AI 说做不到,看起来像坏了。真正起保护作用的是上面那个两步流程,不是开关。所以 1.5.0 改成登录以后就打开,开关留着给不想要的人关。这个决定写进了 ADR 0004。Moodle 和 Ed 还是只读,没有变。
远程的每个软件自己选
1.4.0 的远程模式只有一个 monash 权限,所以之前的版本干脆整组拒绝邮箱和考勤。直接放开的话,已经配对过的 ChatGPT 会在你不知道的情况下突然能读整个邮箱。
现在配对页上,服务器开了的功能各有一个勾选框:只读的默认勾上,写操作默认不勾。勾了什么就存进授权码和令牌,刷新令牌时一起带过去。每次调用时,再和服务器当前的设置取一次交集,服务器上关掉某项,所有软件马上就用不了。1.5.0 之前配对的令牌在数据库升级时补的是 monash,所以旧软件不会多出任何权限。凭据和后台任务也按客户端分开,一个软件准备的操作,另一个软件执行不了。
考勤不开浏览器
考勤门户是一个 ASP.NET 页面,日期用 #daySel 下拉框切换。我的 Chrome 插件一直是让浏览器打开页面、切到那天、再跑一段 JavaScript 解析。放到后台定时跑,每次都要起一个无头浏览器,一天一天地切,很重。
我注意到切日期时页面并不会重新请求,只是把对应的 dayPanel_<日期> 显示出来。那么所有日期很可能本来就在 HTML 里。于是写了一个 Python 版的解析器,只在当天那个区块里找,用登录时保存的 cookie 直接请求一次页面。
光凭猜不能就这么上线,所以做了两层对照。测试里用几种不同结构的页面,让浏览器跑原来的 JS、Python 跑新的解析,逐条比较,连显示文字都要一样。另外加了一个 monash attendance probe,在真实门户上把两种读法各跑一遍,只输出数量和对不对得上,不输出课程内容。在马来西亚校区的门户上,门户提供的 15 天全部一致,页面只有 5 KB。所以快速读取默认打开了,页面结构对不上时会自动退回浏览器。
第一次在 Mac 上跑 probe 时报了 CERTIFICATE_VERIFY_FAILED。门户服务器大概没把中间证书发全,Chrome 会自己去补,Python 自带的校验不会。最后用 truststore 让 Python 走系统的证书校验,macOS 会像 Chrome 一样补上。校验一直是开着的,没有为了能跑通去关掉它。
读的时间也换了思路。不按钟点每小时跑,而是按课表:工作日早上 8 点读一次,每节还没签到的课开始 15 分钟后再读一次。晚上、周末和已经签过的课都不跑。登录能维持多久还不知道,所以程序会记下每次登录、每次成功和第一次过期的时间,等有了数据再决定要不要续期。
不让同学开终端
功能做完后有一个问题:接 Gmail 要自己去 Google Cloud 建项目、开 API、下载一个 JSON,再在终端里输命令。这对大多数同学来说太难了,难到不如直接用 Claude 自带的 Gmail 连接器。
我做了两件事。第一,安装包里带了一个公开版专用的桌面应用客户端。按 Google 的设计,桌面应用的这个文件本来就随程序公开,它只告诉 Google 是哪个应用在请求授权,拿不到任何人的邮件;GitHub 的推送保护还是会拦一下,要手动放行。代价是授权页会显示“尚未验证此应用”,而且没审核的应用最多 100 个用户,对朋友圈子够用。
第二,加了一个工具 start_school_service_setup。同学在 AI 里说“帮我接上学校邮箱”,它在后台把缺的组件装进现在的环境,然后在电脑上打开登录窗口,同学只要登录。接好以后通知 AI 软件刷新工具列表。这个工具每次对话多占约 145 个 token,所以两样都接好以后它会自己隐藏。原来 17 个工具的参数在测试里是用哈希锁住的,这次一个都没改。
小结
- 写操作的安全靠流程,不靠开关:完整预览、绑定内容的一次性凭据、用户确认、出错不重试。
- 给已有的远程授权加新能力时,旧的授权不能跟着变大,要让用户重新选。
- 想换掉一个验证过的解析器,先在真实数据上把新旧两个对照一遍,再默认打开。
- 证书校验不过时,去找为什么不过,不要关掉校验。
- 功能做好了还不够,接上它的步骤也要是普通同学能走完的。