永久停更
搬运不注出处或收费的自重
代码仅供逆向学习参考
2020.07.25 更新修复缺少的时间戳和加密信息
2020.07.05 更新修复缺少机型导致的信息错误
2020.05.31 更新修复使用特殊符号密码的报错
2020.05.25 更新修复新增newToken验证的报错

云函数自动签到视频教程bilibili在线播放
使用腾讯云函数,设置语言python3.6,模板空白函数
将index.py改成zjy.py,执行方法改成zjy.auto_signin
将下面代码的内容复制到云函数的zjy.py,修改网页中zjy.py的用户名密码
设置内存64MB,超时4s
设置环境变量TZ=Asia/Shanghai即可
添加触发方式,使用自定义触发

注意: 运行次数过多将产生费用参考云函数定价
免费额度:资源使用量40万GBs 调用次数100 万次
计费定价(超出免费额度部分):资源使用量0.00011108元/GBs 调用次数0.0133元/万次 外网出流量0.8元/GB
资源使用量 = (内存/1024) × (运行时间(毫秒)/1000)
因账单明细费用(计费)最多支持8位小数,而账户支付金额(扣费)最多支持2位小数,故在高精度计费和低精度扣费之间存在精度差异,系统会按高精度计费与按低精度扣费之间的差额进行自动精度差异调整。
以下面的实时签到为例
资源使用量 = (64/1024) × (700/1000) = 0.04375 GBs
调用次数= 20 × (19-7+1) x 60 x (5/60) = 18720次
资源使用量和调用次数都没有超出免费额度,所以只需要支付外网流量
每次调用的外网流量平均应该5K不到,实测定时签到在月度计费精度差异下可以免去费用(单个实时签到应该也行)

以下是触发参考,更多自定义触发参考cron文档
定时签到参考
9点50左右有一次签到(星期二到星期五的上午9点45-54的每五秒钟执行一次

*/5 45-54 9 * * TUE-FRI * 

13点40左右有一次签到(星期一和星期三的下午1点35-44的每五秒钟执行一次)

*/5 35-44 13 * * MON,WED * 

实时签到参考(频率高时,返回值是Unicode编码,Unicode转中文)
上午7点到19点不定时会有签到(星期一到星期五的上午7点到19点的每五秒执行一次)

*/5 * 7-19 * * MON-FRI * 

QQ截图20200324154657.png
支持常规、手势、二维码签到
代码如下,开启邮箱通知,修改mailConfig中Eanbled的值False为True,按照提示配置SMTP,开启邮箱记得调整超时。开启server酱,修改serverChan中Eanbled的值False为True,并配置SCKEY。

# -*- coding: utf8 -*-
import requests,json,time,smtplib,hashlib
from email.mime.text import MIMEText
from email.header import Header
def auto_signin(event, context):
    userName,userPwd="账号","密码"
    mailConfig={
            "enabled": False,"smtp_server": "SMTP地址","smtp_port": "SMTP端口","from_addr": "发件人邮箱","from_pwd": "发件人密码","to_addr": "收件人邮箱"
            #示例#"enabled": True,"smtp_server": "smtp.domain.com","smtp_port": "25","from_addr": "from@domain.com","from_pwd": "password","to_addr": "to@domain.com"
        }
    serverChan={
            "enabled": False,"SCKEY": "在这填写server酱的SCKEY"
            #示例#"enabled": True,"SCKEY": "SCU9086fd99b24f4d1665e79f4601b6b518T7e719eca372192c4ca"
        }
    url_api="https://zjyapp.icve.com.cn/newMobileAPI/"
    equipmentModel="Xiaomi Redmi K20 Pro"
    equipmentApiVersion=10
    try:
        equipmentAppVersion=getVersion()
    except:
        equipmentAppVersion="2.8.34"
    emit = str(int(time.time())) + "000"
    basicData={"equipmentAppVersion": equipmentAppVersion,"equipmentApiVersion":equipmentApiVersion,"equipmentModel":equipmentModel}
    headers = {
            "Content-Type": "application/x-www-form-urlencoded","Host": "zjyapp.icve.com.cn","Connection": "Keep-Alive","Accept-Encoding": "gzip","User-Agent": "okhttp/4.5.0","emit":emit,"device":getDevice(equipmentModel,str(equipmentApiVersion),equipmentAppVersion,emit)
        }
    userData = {"clientId": "bb702e1087904609a5de8dafe6241aa0", "sourceType": "2", "userPwd": userPwd, "userName": userName,
                 "appVersion": equipmentAppVersion}
    userData.update(basicData)
    session = requests.Session()
    login=session.post(url_api + "MobileLogin/newSignIn", data=userData,headers=headers)
    loginInfo=json.loads(login.text)
    if loginInfo["code"]!=1:
        return (loginInfo["msg"])
    stuId = loginInfo["userId"]
    faceDate = (time.strftime("%Y-%m-%d", time.localtime()))
    newToken=loginInfo["newToken"]
    todayClassData = {"stuId": stuId, "faceDate": faceDate,"newToken": newToken}
    todayClassData.update(basicData)
    todayClass=session.post(url_api + "faceteach/getStuFaceTeachList", data=todayClassData, headers=headers)
    todayClassInfo=json.loads(todayClass.text)["dataList"]
    result=""
    for i in range(len(todayClassInfo)):
        inClassData = {
            "activityId": todayClassInfo[i]["Id"], "stuId": stuId, "classState": todayClassInfo[i]["state"], "openClassId": todayClassInfo[i]["openClassId"],
            "newToken": newToken
        }
        inClassData.update(basicData)
        inClass=session.post(url_api + "faceteach/newGetStuFaceActivityList", data=inClassData, headers=headers)
        inClassInfo=json.loads(inClass.text)["dataList"]
        for n in range(len(inClassInfo)):
            if inClassInfo[n]["DataType"] == "签到" and inClassInfo[n]["State"] != 3:
                attendData = {
                    "activityId": todayClassInfo[i]["Id"], "openClassId": todayClassInfo[i]["openClassId"], "stuId": stuId, "typeId": inClassInfo[n]["Id"], "type": "1",
                    "newToken": newToken
                }
                attendData.update(basicData)
                attend=session.post(url_api + "faceteach/isJoinActivities", data=attendData,headers=headers)
                attendInfo=json.loads(attend.text)
                if attendInfo["isAttend"] != 1:
                    signInData = {
                    "signId": inClassInfo[n]["Id"], "stuId": stuId, "openClassId": todayClassInfo[i]["openClassId"],"sourceType": "2", "checkInCode": inClassInfo[n]["Gesture"],"activityId": todayClassInfo[i]["Id"],
                    "newToken": newToken
                    }
                    signInData.update(basicData)
                    signIn=session.post(url_api + "faceteach/saveStuSign", data=signInData,headers=headers)
                    signInInfo=json.loads(signIn.text)
                    signInTime=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                    result=result+(todayClassInfo[i]["courseName"] + " " + signInTime + " " + signInInfo["msg"])+"\r\n"
    if result == "":
        result = "当前不存在未签到"
    else:
        if mailConfig["enabled"]:
            sendMail(mailConfig["smtp_server"],mailConfig["smtp_port"],mailConfig["from_addr"],mailConfig["from_pwd"],mailConfig["to_addr"],result)
        if serverChan["enabled"]:
            sendWechat(serverChan["SCKEY"],result)
    return result.replace("\r\n", " ")
def getVersion():
    versionInfo=requests.get("https://zjy2.icve.com.cn/portal/AppVersion/getLatestVersionInfo").json()
    return versionInfo["appVersionInfo"]["VersionCode"]
def getMd5(str):
    md5=hashlib.md5()
    md5.update(str.encode("utf-8"))
    return md5.hexdigest()
def getDevice(equipmentModel,equipmentApiVersion,equipmentAppVersion,emit):
    tmp=getMd5(equipmentModel)+equipmentApiVersion
    tmp=getMd5(tmp)+equipmentAppVersion
    tmp=getMd5(tmp)+emit
    return getMd5(tmp)
def sendWechat(SCKEY,content):
    resultData={"text": "职教云签到结果", "desp": content}
    content.replace("\r\n", "\n\n")
    requests.post("https://sc.ftqq.com/" + SCKEY + ".send",data=resultData)
def sendMail(smtp_server,smtp_port,from_addr,from_pwd,to_addr,content):
    stmp=smtplib.SMTP_SSL(smtp_server,smtp_port)
    stmp.login(from_addr,from_pwd)
    message = MIMEText(content, "plain", "utf-8")
    message["From"] = Header("职教云自动签到系统", "utf-8")
    message["From"].append("<" + from_addr + ">", 'ascii')
    message["To"] = Header("用户", "utf-8")
    message["To"].append("<" + to_addr + ">", 'ascii')
    subject = "签到结果"
    message["Subject"] = Header(subject, 'utf-8')
    try:
        stmp.sendmail(from_addr, to_addr, message.as_string())
    except Exception as e:
        print ('邮件发送失败--' + str(e))