由于疫情原因,幼儿园要求每天健康码打卡,每天都需要花时间重复这个动作,作为程序员,这肯定是不能容忍的,必须脚本安排起!
抓包分析API
首先我们需要做的就是抓包,提取API接口,有两种方式,一种是PC微信 + Fiddler
,还有一种就是直接手机抓取,手机需要安装Http Canary
,这是付费软件,当然也有破解版。
我这里就采用第一种方式了,Fiddler
使用的是果壳破解版,下面就讲一下过程中遇到的问题,其实这个小程序在验证方面做的还是比较简单的,简单一个Authorization
就完了,没有到期时长,不受机器限制;并且所有接口提交都不会验证客户端设备session等,大大有利于自动化脚本。
-
首先
Fiddler
抓取微信小程序,刚开始无法抓取,在网上搜了很多文章,发现问题出在微信小程序上,微信修改了框架导致的。参考上一篇文章:fiddler抓包PC微信小程序失败解决方案 -
然后是上传图片到OBS的问题:
- 虽然上在options中指定了host,但在headers中还是必须再加上Host
- 需要手动指定
boundary
和Content-Length
{ host: "obs.jielong.co", headers: { Host: "obs.jielong.co", "Content-Length": await replaceCb2promise(form.getLength.bind(form), [])[1], 'Content-Type': 'multipart/form-data; boundary=' + form.getBoundary(), }, body: form, }
- 发现一个问题,打卡项目的发布与否与图片提交地址有关系,没有Publisher的在本地,Publisher了的在华为云,相应的参数也会不同,有点小坑。
-
接着就是提交打卡记录,需要注意的是:
- 提交时需要添加
Authorization
Content-Type
需要修改为application/json
- 提交时需要添加
接口分析其实是很简单的,结合以上需要规避的问题,基本上写自动化脚本就没啥问题了。
自动生成每日打卡截图
提交接口没问题了,打卡需要健康码截图,这里我就稍微偷了个懒,没有直接使用每日的健康码截图,而是将健康码截图后使用ps抹去了日期相关的信息,然后通过程序对图片写入响应日期信息。
整个脚本程序我使用tinyhttp
写的,TS在处理图片方面还是比较麻烦,只能借用第三方库来处理,这里推荐两个库jimp
和images
,这里我采用的是jimp
,需要说明的是在图片上生成日期的时候,jimp
不支持丰富的样式,只能简单的字体。设置稍微复杂一点的文字就不行,比如字体颜色,不同字体大小等。这里我借助了其他人写的脚本来实现的。
先说下这个脚本的思路:
-
先创建透明图片,在图片上写入字体
-
用需要的颜色替换掉图片中字体的颜色
-
再将图片叠加到需要做水印的图片上
-
我在脚本源代码的基础上做了些许修改以适应通用场景
// @ts-nocheck import Jimp from "jimp"; import {Font} from "@jimp/plugin-print"; const FONT_PATH = process.env.TMS_KOA_JIMP_FONT_PATH || Jimp.FONT_SANS_32_BLACK const FONT_SIZE = process.env.TMS_KOA_JIMP_FONT_SIZE || 32 export default class { image: Jimp; font: Font; fontSize: number; // saver = null constructor(image: Jimp, font: Font, fontSize: number) { this.image = image this.font = font this.fontSize = fontSize // this.saver = new Saver() } /** * * @param {*} text * @param {*} x * @param {*} y * @param {*} color * @param {*} bgColor * @param {*} width * @param {*} fontSize * @param {*} align */ addText(text: string, x: number, y: number, color: string, bgColor: string | null = null, width: number | null = null, fontSize: number = this.fontSize, align: "left" | "center" | "right" = "center") { // 大小和位置 let textRawWidth = Jimp.measureText(this.font, text) + 2 let textRawHeight = Jimp.measureTextHeight(this.font, text) + 2 // x = parseInt(x) // y = parseInt(y) let bgWidth = width // 背景宽度 let textSize = fontSize > 0 ? fontSize : this.fontSize let textScale = textSize / this.fontSize // 颜色 let textRGB = color ? Jimp.intToRGBA(Jimp.cssColorToHex(color)) : null let textBgHex = bgColor ? Jimp.cssColorToHex(bgColor) : null // 将文字放在透明的图片上 let textImage = new Jimp(textRawWidth, this.fontSize + 2, 0x00000000) textImage.print(this.font, 1, 1, text) if (textRGB) { textImage.scan(0, 0, textRawWidth, textRawHeight, function (x: number, y: number, idx: number) { let bitmap = textImage.bitmap let red = bitmap.data[idx + 0] let green = bitmap.data[idx + 1] let blue = bitmap.data[idx + 2] let alpha = bitmap.data[idx + 3] if (x > 0 && y > 0 && x < bitmap.width && y < bitmap.height) if (red === 0 && green === 0 && blue === 0 && alpha > 0) { textImage.setPixelColor(Jimp.rgbaToInt(textRGB.r, textRGB.g, textRGB.b, alpha), x, y) } }) } // 缩放 if (textScale !== 1) textImage.scale(textScale) if (textBgHex) { /* 生成背景 */ let textScaleWidth = textRawWidth * textScale bgWidth = bgWidth || textScaleWidth let bgImage = new Jimp(bgWidth, textSize + 2, textBgHex) let textX = 0 switch (align) { case 'center': textX = (bgWidth - textScaleWidth) / 2 break case 'right': textX = bgWidth - textScaleWidth break } bgImage.composite(textImage, textX, 0) this.image.composite(bgImage, x, y) } else { this.image.composite(textImage, x, y) } return this.image } getBase64Async() { return this.image.getBase64Async(Jimp.AUTO) } }
-
调用
serviceGenImage(path.join(__dirname, "images/owner.png"))
定时框架
脚本有了,健康码截图也有了,然后就是解放每日双手,程序自动运行,定时打卡了,ts下也有定时任务框架:cron
、silkyTimer
,相关对而言silkyTimer
更符合我的口味,但是该框架还在测试阶段,稳定性不够。所以就选择了已经很成熟、稳定的corn
,配置也很简单,就是个java下的corn一模一样,直接移植过来的,就不多介绍了。