diff --git a/.github/trigger b/.github/trigger new file mode 100644 index 0000000..cfb931e --- /dev/null +++ b/.github/trigger @@ -0,0 +1 @@ +off diff --git a/.github/workflows/Step.PNG b/.github/workflows/Step.PNG new file mode 100644 index 0000000..f3964e4 Binary files /dev/null and b/.github/workflows/Step.PNG differ diff --git a/.github/workflows/Step2.PNG b/.github/workflows/Step2.PNG new file mode 100644 index 0000000..c81a3dd Binary files /dev/null and b/.github/workflows/Step2.PNG differ diff --git a/.github/workflows/auto-sign-action.yml b/.github/workflows/auto-sign-action.yml index fabb0ff..d8d31cd 100644 --- a/.github/workflows/auto-sign-action.yml +++ b/.github/workflows/auto-sign-action.yml @@ -7,7 +7,7 @@ on: push: branches: [ master ] schedule: - - cron: 30,0,57,27 16 * * * + - cron: 30,0 */4 * * * # watch: # types: started @@ -34,7 +34,6 @@ jobs: id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - - name: pip cache uses: actions/cache@v2 with: @@ -42,11 +41,21 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - - name: Prepare the jobs + - name: Prepare the jobs + env: + IMAGE: ${{ secrets.IMAGE }} + CONFIG: ${{ secrets.CONFIG }} run: | sudo apt install tesseract-ocr tesseract-ocr-chi-sim sudo apt install tesseract-ocr-eng - curl -L -v -o temp.jar https://github.com/ZimoLoveShuang/wisedu-unified-login-api/releases/download/v1.0.8/wisedu-unified-login-api-v1.0.jar + + rm config.yml default.png + + curl -L -v -o temp.jar https://github.com/ZimoLoveShuang/wisedu-unified-login-api/releases/download/v1.0.11/wisedu-unified-login-api-v1.0.jar + + echo ${IMAGE} | base64 -d > default.png + echo ${CONFIG} | base64 -d > config.yml + ls pwd java -version @@ -63,3 +72,6 @@ jobs: - name: Run with schedule. run: | python3 index.py + - name: Clean + run: | + rm config.yml default.png diff --git a/README.md b/README.md index 036ae94..b2e285d 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ -# auto-sign +
+

+ 今日校园辅导猫自动签到 +

+
-# 禁止任何人使用此项目提供付费的代挂服务 +## 这是什么 ? -受人之托,写的今日校园自动签到脚本,支持图片,定位,额外选项等,**可能已经通用了在今日校园签到的所有学校了**。 +受人之托,写的今日校园自动签到脚本,可能已经通用了在今日校园签到的所有学校了 +* [x] 图片签到 +* [x] 位置签到 +* [x] 额外选项 -# 设计思路 + +## 设计思路 1. 模拟登陆 2. 获取每日未签到任务 @@ -12,20 +20,49 @@ 4. 根据配置,自动填写表单 5. 提交未签到任务 -# 使用 -## 请参考[auto-submit项目](https://github.com/ZimoLoveShuang/auto-submit) +## Actions 方式 -#### 如果你不会配置表单组默认选项配置,请先配置好user信息之后本地执行generate.py然后将分割线下的内容复制到配置文件中对应位置 +1. 先 Fork 本项目 +2. 配置好自己的config.yml和default.png[**如不需要图片,请自行修改actions脚本**] +3. 将上面两个文件分别转为Base64,可使用[这个网站](https://www.hitoy.org/tool/file_base64.php) +4. 进入项目 **Settings -> Secrets** 后 点击**New repository secret**按钮 依次添加以下 2 个 Secrets。 -#### 如遇到依赖问题,请去[`auto-sumit`](https://github.com/ZimoLoveShuang/auto-submit)项目下载`dependency.zip`,然后参考`auto-submit`项目的说明将函数依赖层添加到腾讯云函数 +| Name | Value | +| ---------- | ------------------ | +| CONFIG | config.yml的Base64 | +| IMAGE | default.png的Base64 | + +5. 在你 Folk 的项目主页依次点击以下按钮 + +![图示](.github/workflows/Step.PNG) + +6. 继续以下操作开启Actions + +![图示](.github/workflows/Step2.PNG) + +7. 修改.github/trigger内的内容为on(随便创建一个文件也可以) +8. 检查能否**正常**签到 -#### 如果不知道怎么配置经纬度信息,可以访问[这里](http://zuobiao.ay800.com/s/27/index.php),将经纬度四舍五入保留六位小数之后的放入配置文件对应位置即可 -#### 如果一天签到多次,除了问题不一样之外,其他都一样,你又不想配置多个云函数的话,配置文件设置不检查就行了 -# 其他 -1. 项目依赖于我的开源项目[模拟登陆 金智教务统一登陆系统 的API](https://github.com/ZimoLoveShuang/wisedu-unified-login-api) -2. `Cpdaily-Extension`本质上就是对一个json对象进行了des加密,然后编码为了Base64字符串,加密解密实现可以参考[Java版](https://github.com/ZimoLoveShuang/yibinu-score-crawler/blob/master/src/main/java/wiki/zimo/scorecrawler/helper/DESHelper.java) [python版](https://github.com/ZimoLoveShuang/auto-submit/blob/master/currency/encrypt.py) -3. 也欢迎其他学校学子在此提交适用于自己学校的配置,命名规则为`config_xxxx.yml`,`xxxx`为学校英文简称 \ No newline at end of file +## 云函数方式 + 请参考[auto-submit项目](https://github.com/ZimoLoveShuang/auto-submit)如遇到依赖问题,请去[`auto-sumit`](https://github.com/ZimoLoveShuang/auto-submit)项目下载`dependency.zip`,然后参考`auto-submit`项目的说明将函数依赖层添加到腾讯云函数, 如果一天签到多次,除了问题不一样之外,其他都一样,你又不想配置多个云函数的话,配置文件设置不检查就行了 + + + + +## Config.yml的 配置方式 + 如果你不会配置表单组默认选项配置,请下载本项目到本地输入`pip install -r requirements.txt`后配置好Config.yml内的user部分的信息之后本地执行`generate.py`然后将分割线下的内容复制到配置文件中对应位置,如果不知道怎么配置经纬度信息可以访问[这里](http://zuobiao.ay800.com/s/27/index.php),将经纬度**四舍五入保留六位小数**之后的放入配置文件对应位置即可 + + + + +# 其他 +1. 禁止任何人使用此项目提供付费的代挂服务 +2. 项目依赖于我的开源项目[模拟登陆 金智教务统一登陆系统 的API](https://github.com/ZimoLoveShuang/wisedu-unified-login-api) +3. `Cpdaily-Extension`本质上就是对一个json对象进行了des加密,然后编码为了Base64字符串,加密解密实现可以参考[Java版](https://github.com/ZimoLoveShuang/yibinu-score-crawler/blob/master/src/main/java/wiki/zimo/scorecrawler/helper/DESHelper.java) [python版](https://github.com/ZimoLoveShuang/auto-submit/blob/master/currency/encrypt.py) +4. 也欢迎其他学校学子在此提交适用于自己学校的配置,命名规则为`config_xxxx.yml`,`xxxx`为学校英文简称 +5. 如果需要针对特定签到任务的黑名单(如出入校等),可自行修改代码 +6. 如果cookies一直为null请手动获取cookies并修改代码相应位置(参考[`auto-sumit`](https://github.com/ZimoLoveShuang/auto-submit)项目) diff --git a/index.py b/index.py index 9d5b645..763bb9a 100644 --- a/index.py +++ b/index.py @@ -10,12 +10,21 @@ from datetime import datetime, timedelta, timezone from urllib.parse import urlparse from urllib3.exceptions import InsecureRequestWarning +from decimal import Decimal + +# Key For ServerChan +key='' # debug模式 debug = True if debug: requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +class DecimalEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, Decimal): + return float(o) + super(DecimalEncoder, self).default(o) # 读取yml配置 def getYmlConfig(yaml_file='config.yml'): @@ -47,7 +56,7 @@ def log(content): def getCpdailyApis(user): apis = {} user = user['user'] - schools = requests.get(url='https://www.cpdaily.com/v6/config/guest/tenant/list', verify=not debug).json()['data'] + schools = requests.get(url='https://mobile.campushoy.com/v6/config/guest/tenant/list', verify=not debug).json()['data'] flag = True for one in schools: if one['name'] == user['school']: @@ -58,7 +67,7 @@ def getCpdailyApis(user): params = { 'ids': one['id'] } - res = requests.get(url='https://www.cpdaily.com/v6/config/guest/tenant/info', params=params, + res = requests.get(url='https://mobile.campushoy.com/v6/config/guest/tenant/info', params=params, verify=not debug) data = res.json()['data'][0] joinType = data['joinType'] @@ -86,7 +95,7 @@ def getCpdailyApis(user): if flag: log(user['school'] + ' 未找到该院校信息,请检查是否是学校全称错误') exit(-1) - log(apis) + # log(apis) return apis @@ -151,6 +160,7 @@ def getUnSignedTasksAndSign(session, apis, user): exit(-1) # log(res.json()) for i in range(0, len(res.json()['datas']['unSignedTasks'])): + # 如果需要过滤特定关键字,请在下面这里加判断 #if '出校' in res.json()['datas']['unSignedTasks'][i]['taskName'] == False: # if '入校' in res.json()['datas']['unSignedTasks'][i]['taskName'] == False: latestTask = res.json()['datas']['unSignedTasks'][i] @@ -215,32 +225,47 @@ def fillForm(task, session, user, apis): form['extraFieldItems'] = extraFieldItemValues # form['signInstanceWid'] = params['signInstanceWid'] form['signInstanceWid'] = task['signInstanceWid'] - form['longitude'] = user['lon'] - form['latitude'] = user['lat'] + form['longitude'] = Decimal(user['lon']) + form['latitude'] = Decimal(user['lat']) form['isMalposition'] = task['isMalposition'] form['abnormalReason'] = user['abnormalReason'] form['position'] = user['address'] return form - -# 上传图片到阿里云oss +# 11/2020 的 新上传API credit to @mrjesen def uploadPicture(session, image, apis): - url = 'https://{host}/wec-counselor-sign-apps/stu/sign/getStsAccess'.format(host=apis['host']) - res = session.post(url=url, headers={'content-type': 'application/json'}, data=json.dumps({}), verify=not debug) + url = 'https://{host}/wec-counselor-sign-apps/stu/oss/getUploadPolicy'.format(host=apis['host']) + res = session.post(url=url, headers={'content-type': 'application/json'}, data=json.dumps({'fileType':1}), verify=not debug) datas = res.json().get('datas') - fileName = datas.get('fileName') - accessKeyId = datas.get('accessKeyId') - accessSecret = datas.get('accessKeySecret') - securityToken = datas.get('securityToken') - endPoint = datas.get('endPoint') - bucket = datas.get('bucket') - bucket = oss2.Bucket(oss2.Auth(access_key_id=accessKeyId, access_key_secret=accessSecret), endPoint, bucket) - with open(image, "rb") as f: - data = f.read() - bucket.put_object(key=fileName, headers={'x-oss-security-token': securityToken}, data=data) - res = bucket.sign_url('PUT', fileName, 60) + #log(datas) + #new_api_upload + fileName = datas.get('fileName') + '.png' + accessKeyId = datas.get('accessid') + xhost = datas.get('host') + xdir = datas.get('dir') + xpolicy = datas.get('policy') + signature = datas.get('signature') + #new_api_upload + #new_api_upload2 + url = xhost + '/' + data={ + 'key':fileName, + 'policy':xpolicy, + 'OSSAccessKeyId':accessKeyId, + 'success_action_status':'200', + 'signature':signature + } + data_file = { + 'file':('blob',open(image,'rb'),'image/jpg') + } + res = session.post(url=url,data=data,files=data_file) + if(res.status_code == requests.codes.ok): + log('图片上传成功! ') + return fileName + + #new_api_upload2 # log(res) - return fileName + return '' # 获取图片上传位置 @@ -268,29 +293,29 @@ def submitForm(session, user, form, apis): user = user['user'] # Cpdaily-Extension extension = { - "lon": user['lon'], - "model": "OPPO R11 Plus", - "appVersion": "8.1.14", - "systemVersion": "8.0", + "lon": Decimal(user['lon']), + "model": "NOH-AN00", + "appVersion": "8.2.12", + "systemVersion": "10.0.0", "userId": user['username'], "systemName": "android", - "lat": user['lat'], + "lat": Decimal(user['lat']), "deviceId": str(uuid.uuid1()) } headers = { # 'tenantId': '1019318364515869', - 'User-Agent': 'Mozilla/5.0 (Linux; Android 4.4.4; OPPO R11 Plus Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 okhttp/3.12.4', + 'User-Agent': 'Mozilla/5.0 (Linux; Android 10.0.0; NOH-AN00 Build/NZH54D) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.117 Safari/537.36 cpdaily/8.2.12 wisedu/8.2.12', 'CpdailyStandAlone': '0', 'extension': '1', - 'Cpdaily-Extension': DESEncrypt(json.dumps(extension)), + 'Cpdaily-Extension': DESEncrypt(json.dumps(extension, cls=DecimalEncoder)), 'Content-Type': 'application/json; charset=utf-8', - 'Accept-Encoding': 'gzip', + 'Accept-Encoding': 'gzip, deflate', # 'Host': 'swu.cpdaily.com', 'Connection': 'Keep-Alive' } res = session.post(url='https://{host}/wec-counselor-sign-apps/stu/sign/completeSignIn'.format(host=apis['host']), - headers=headers, data=json.dumps(form), verify=not debug) + headers=headers, data=json.dumps(form, cls=DecimalEncoder), verify=not debug) message = res.json()['message'] if message == 'SUCCESS': log('自动签到成功') @@ -298,7 +323,8 @@ def submitForm(session, user, form, apis): else: log('自动签到失败,原因是:' + message) # sendMessage('自动签到失败,原因是:' + message, user['email']) - exit(-1) + # 这里先注释掉,因为碰到时间没到的会断签 + # exit(-1) # 发送邮件通知 @@ -310,8 +336,8 @@ def sendMessage(msg, email): if send != '': log('正在发送邮件通知。。。') log(getTimeStr()) - # sendMessageWeChat(msg + getTimeStr(), '今日校园自动签到结果通知') - + sendMessageWeChat(msg + getTimeStr(), '今日校园自动签到结果通知') + res = requests.post(url='http://www.zimo.wiki:8080/mail-sender/sendMail', data={'title': '今日校园自动签到结果通知' + getTimeStr(), 'content': msg, 'to': send}, verify=not debug) code = res.json()['code'] @@ -323,7 +349,11 @@ def sendMessage(msg, email): except Exception as e: log("send failed") - +def sendMessageWeChat(msg, desp): + if len(key) < 1: + return + url = 'https://sc.ftqq.com/' + key + '.send?text=' + msg + '&desp=' + desp; + requests.get(url, verify=False) # 主函数 def main(): diff --git a/requirements.txt b/requirements.txt index eeff23f..24b655a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ pyDes==2.0.1 -aliyun-python-sdk-core==2.13.20 -aliyun-python-sdk-core-v3==2.13.11 -aliyun-python-sdk-kms==2.10.1 -oss2==2.12.1 +#aliyun-python-sdk-core==2.13.20 +#aliyun-python-sdk-core-v3==2.13.11 +#aliyun-python-sdk-kms==2.10.1 +#oss2==2.12.1 pytz==2019.3 PyYAML==5.3.1 requests==2.23.0