diff --git a/.github/vpn/config.ovpn b/.github/vpn/config.ovpn index 3e8aa53..1a46136 100644 --- a/.github/vpn/config.ovpn +++ b/.github/vpn/config.ovpn @@ -2,10 +2,24 @@ client dev tun proto tcp +# ROUTING CONFIG + +remote openvpn.shu.edu.cn 443 #not exist, just for display +remote openvpn1.shu.edu.cn 443 +remote openvpn2.shu.edu.cn 443 remote openvpn3.shu.edu.cn 443 +remote openvpn4.shu.edu.cn 443 +remote openvpn5.shu.edu.cn 443 +remote openvpn6.shu.edu.cn 443 +remote openvpn7.shu.edu.cn 443 +remote openvpn8.shu.edu.cn 443 +remote openvpn9.shu.edu.cn 443 +remote-random +resolv-retry 60 + comp-lzo resolv-retry infinite nobind @@ -49,4 +63,6 @@ ox+1WKF/6d5gXOZOTf0GuJi4JHRojDzb5p/Z6/8gxE9krVsQmfiR3HnaegyTVMle KzpCQRuEx5rn/uDvV6joYCwy4q0BkQTKGBeUofR2ETlTeC7t6l+GKRPwCQ8BeBvL e0+RSP6QWl/+flS0qdmtSswuZF98OqAuu6wW2PHXB3auUn77nw== -----END CERTIFICATE----- - \ No newline at end of file + + +auth-user-pass secret.txt diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 77c22df..2fc5266 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -2,7 +2,7 @@ name: Auto Report on: schedule: - - cron: "0 0,12 * * *" + - cron: "0 19 * * *" workflow_dispatch: jobs: @@ -12,13 +12,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up chromedriver - uses: nanasess/setup-chromedriver@v1 - - run: | - export DISPLAY=:99 - chromedriver --url-base=/wd/hub & - sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional - - name: Set up Python 3.8 uses: actions/setup-python@v2 with: @@ -29,30 +22,83 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt + - name: Pull Remote + env: + DISABLE_PULL_REMOTE: ${{ secrets.DISABLE_PULL_REMOTE }} + if: env.DISABLE_PULL_REMOTE == '' + run: | + wget -O remote.zip https://github.com/BlueFisher/SHU-selfreport/archive/refs/heads/master.zip + unzip remote.zip + + - name: Report Without OpenVPN + if: always() + env: + USERS: ${{ secrets.USERS }} + HTTPS_PROXY: ${{ secrets.HTTPS_PROXY }} + DISABLE_PULL_REMOTE: ${{ secrets.DISABLE_PULL_REMOTE }} + IMG: ${{ secrets.IMG }} + run: | + if [[ $DISABLE_PULL_REMOTE == '' ]]; then + echo "Report from remote"; + if python -u SHU-selfreport-master/main.py; then + echo "Report from remote success"; + else + echo "Report from remote failed, try using OpenVPN"; + echo "USE_OVPN=1" >> $GITHUB_ENV; + fi + else + echo "Report from local"; + if python -u main.py; then + echo "Report from local success"; + else + echo "Report from local failed, try using OpenVPN"; + echo "USE_OVPN=1" >> $GITHUB_ENV; + fi + fi + - name: Configure OpenVPN env: - users: ${{ secrets.USERS }} + USERS: ${{ secrets.USERS }} + if: env.USE_OVPN != '' run: | sudo apt update - sudo apt install -y openvpn openvpn-systemd-resolved - echo "::add-mask::$(python main.py gh-vu)" - echo "::add-mask::$(python main.py gh-vp)" - echo "ovpn_username=$(python main.py gh-vu)" >> $GITHUB_ENV - echo "ovpn_password=$(python main.py gh-vp)" >> $GITHUB_ENV - - - name: Connect to VPN - uses: "kota65535/github-openvpn-connect-action@v1" - with: - config_file: .github/vpn/config.ovpn - username: ${{ env.ovpn_username }} - password: ${{ env.ovpn_password }} + sudo apt-get --assume-yes --no-install-recommends install openvpn + python3 ovpn.py + touch vpn.log - - name: Test a VPN connection - timeout-minutes: 1 - run: until ping -c1 xk.autoisp.shu.edu.cn; do sleep 2; done + - name: Connect and Test OpenVPN Connection + if: env.USE_OVPN != '' + run: sudo ./ovpn.sh - - name: Report - if: always() + - name: Report With OpenVPN + if: env.USE_OVPN != '' + env: + USERS: ${{ secrets.USERS }} + HTTPS_PROXY: ${{ secrets.HTTPS_PROXY }} + DISABLE_PULL_REMOTE: ${{ secrets.DISABLE_PULL_REMOTE }} + IMG: ${{ secrets.IMG }} + run: | + if [[ $DISABLE_PULL_REMOTE == '' ]]; then + echo "Report from remote"; + python -u SHU-selfreport-master/main.py; + else + echo "Report from local"; + python -u main.py; + fi + + - name: Kill OpenVPN Connection env: - users: ${{ secrets.USERS }} - run: "python -u main.py" + HTTPS_PROXY: ${{ secrets.HTTPS_PROXY }} + if: env.USE_OVPN != '' + run: | + sudo chmod 777 vpn.log + sudo killall openvpn + + - name: Upload VPN logs + uses: actions/upload-artifact@v2 + env: + HTTPS_PROXY: ${{ secrets.HTTPS_PROXY }} + if: env.USE_OVPN != '' + with: + name: VPN logs + path: vpn.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7c56535 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:20.04 + +WORKDIR /selfreport + +COPY . . + +RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list &&\ + apt-get update &&\ + apt-get install -y python3.8 python3-pip &&\ + pip3 install -i https://mirrors.ustc.edu.cn/pypi/web/simple pip -U &&\ + pip3 config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple &&\ + pip3 install -r requirements.txt && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +CMD ["python3", "/selfreport/main.py"] diff --git a/README.md b/README.md index f7567ac..6699cfe 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,63 @@ -# 上海大学健康之路每日一报/每日两报自动打卡 +# 上海大学健康之路每日一报自动打卡 -**适配2022-02-28版本,GitHub Actions 新增连接学校VPN再填报功能** +***!!!本项目不支持进行虚假填报!!!仅根据前一天填报内容自动填报,以免遗忘漏报*** -**使用selenium提交表单(测试中),自动生成行程码(目前发现行程码只需要图片中有日期文字,且图片长宽比与手机差不多就行)** +程序为python脚本文件,修改配置文件相关信息,设置后台运行脚本,脚本会根据配置文件信息自动进行每日一报。 -**适配2022-01-15版,增加随申码。请真实提交随申码与行程码。不信谣不传谣!** +支持github actions服务器。 -**适配2021-11-05版,DangSZS** +支持一键补报功能。 -**适配2021-11-04版,修复了ShiFSH字段 [issue 51](https://github.com/BlueFisher/SHU-selfreport/issues/51),去除随申码,丰富日志记录,若出现错误,可以当天真实提交一次,等第二天再观察** +支持每日一报。目前在 4:00AM(report_remote)/3:00AM(report) 自动填报一次。 -**适配2021-10-08版,获取当天的随申码、行程码。请真实地提交一次随申码、行程码,后续会自动采用之前上传的随申码、行程码** +为了避免过多的配置,感谢[@Lanszhang131](https://github.com/Lanszhang131/DailyReport_SHU)的思路,**利用前一天的报送信息进行填报,所以如果你真实地提交过一次,那每天会重复填报。由于默认是 4:00AM(report_remote)/3:00AM(report) 填报,所以如果你想用真实信息的话第一次在早上6点填报后修改下信息。** -**适配2021-06-28版,更新github action的自动打卡时间** -**适配2021-04-24版,暂不支持党史知识天天学** +## 更新日志 -程序为python脚本文件,修改配置文件相关信息,设置后台运行脚本,脚本会根据配置文件信息自动进行每日一报/每日两报。 +**目前实现模拟随机IP功能,以规避IP屏蔽;连接学校VPN功能依然保留,若出现提交失败则会尝试连接后再次填报** -支持github actions服务器。 +**由于OpenVPN同一时间登录登陆人数过多,现增加失败重连功能,每次连接之间会随机等待一定时间;现默认每次从主项目拉取最新代码填报;增加自定义 `HTTP_PROXY` 与 `DISABLE_PULL_REMOTE` 设置选项,具体使用说明见`用法`** -支持一键补报功能。 +**~~由于OpenVPN同一时间登录登陆人数过多,现增加失败重连功能,每次连接之间会随机等待一定时间;增加自定义HTTP_PROXY的环境变量与DISABLE_OPVPN的选项~~** -支持每日一报/每日两报。目前在早9晚9各自动填报一次。 +**~~由于OpenVPN同一时间登录登陆人数过多,现随机等待600-1440秒再连接~~** -为了避免过多的配置,感谢[@Lanszhang131](https://github.com/Lanszhang131/DailyReport_SHU)的思路,**利用前一天的报送信息进行填报,所以如果你真实地提交过一次,那每天会重复填报。由于默认是早9晚9填报,所以如果你想用真实信息的话第一次在晚上9点填报后修改下信息。** +**新增 Auto Report from Remote,可以拉取远程最新的代码,不再需要每次更新,每次拉取** -## 免责申明 +**适配2022-03-22版本,修改字段(早日解封)** -本项目仅做免费的学术交流使用。 +**适配2022-03-20版本,增加校区(早日解封)** -## 用法 +**适配2022-02-28版本,GitHub Actions 新增连接学校VPN再填报功能** -### 0. ⭐Star⭐项目😉 +~~使用selenium提交表单(测试中),自动生成行程码(目前发现行程码只需要图片中有日期文字,且图片长宽比与手机差不多就行)~~ -如果你想获取最新的更新,记得右上角的`watch` +~~适配2022-01-15版,增加随申码。请真实提交随申码与行程码。不信谣不传谣!~~ -### 1. 你有服务器,只在自己服务器上进行自动打卡 +**适配2021-11-05版,DangSZS** -在 `config.yaml` 中设置所有需要打卡的学号密码 +**适配2021-11-04版,修复了ShiFSH字段 [issue 51](https://github.com/BlueFisher/SHU-selfreport/issues/51),去除随申码,丰富日志记录,若出现错误,可以当天真实提交一次,等第二天再观察** -针对Ubuntu,编辑定时执行程序cron +**适配2021-10-08版,获取当天的随申码、行程码。请真实地提交一次随申码、行程码,后续会自动采用之前上传的随申码、行程码** -```bash -crontab -e -``` +**适配2021-06-28版,更新github action的自动打卡时间** -加入以下命令: +**适配2021-04-24版,暂不支持党史知识天天学** -```bash -# 程序每天4点执行一次,并将结果输出至shu_report.log -# 注意python的路径,main.py与输出日志shu_report.log的绝对路径 -0 4 * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log -``` -如果你服务器是UTC时区,则为 +## 免责申明 -```bash -0 20 * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log -``` +本项目仅做免费的学术交流使用。 -如果你不确定你服务器的时区,也可以每小时运行一次: +## 用法 -```bash -0 * * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log -``` +### 0. ⭐Star⭐项目😉 -### 2. 你没有服务器,使用 github actions(推荐) +如果你想获取最新的更新,记得右上角的`watch` + + +### 1. 如果你没有服务器,使用 github actions(推荐) #### 你第一次Fork @@ -78,15 +69,19 @@ crontab -e `VALUE` 设置为 `学号1,密码1;学号2,密码2` 的格式,注意逗号与分号的区分,学号密码之间用逗号,每两个学号之间用分号,必须是英文半角符号,如果只有一个学号密码则不需要加分号 +*如果你有http代理服务器,则建议添加 `HTTPS_PROXY`为你的代理服务器地址,一旦设置 `HTTPS_PROXY` ,则会自动关闭 OpenVPN 连接服务。* + +*如果你不想通过拉取主项目的代码来实现自动填报,则建议添加 `DISABLE_PULL_REMOTE` 并设置为 `true` ,这样会禁用拉取主项目代码的模式。* + ![](images/secrets.png) -4. 定位到你仓库下的 `Actions` 选项卡,点击 `Enable workflow` +4. 定位到你仓库下的 `Actions` 选项卡中的 `Auto Report`,点击 `Enable workflow`,即可开启自动填报系统。 -![](images/enable_actions.png) +![](images/enable_workflow.png) -5. 此时Actions 已经启动完成,每天上午八点(UTC+8)和晚上八点各执行一次,每执行一次会在 `Actions` 选项卡下生成一个报告。 +5. 此时 Actions 已经启动完成,每执行一次会在 `Actions` 选项卡下生成一个报告。 - 如果需要对报送功能进行测试,可以点击 `run workflow` 按钮,立即进行一次运行。 + 如果需要对报送功能进行测试,可以点击 `Run workflow` 按钮,立即进行一次运行。 ![](images/run_workflow.png) @@ -104,6 +99,52 @@ crontab -e ![](images/fetch_upstream_02.png) +### 2. 如果你有服务器,只在自己服务器上进行自动打卡 + +在 `config.yaml` 中设置所有需要打卡的学号密码 + +针对Ubuntu,编辑定时执行程序cron + +```bash +crontab -e +``` + +加入以下命令: + +```bash +# 程序每天4点执行一次,并将结果输出至shu_report.log +# 注意python的路径,main.py与输出日志shu_report.log的绝对路径 +0 4 * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log +``` + +如果你服务器是UTC时区,则为 + +```bash +0 20 * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log +``` + +如果你不确定你服务器的时区,也可以每小时运行一次: + +```bash +0 * * * * python -u /xxx/main.py 2>&1 >> /xxx/shu_report.log +``` + +### 3. 使用Docker部署自动打卡 + +在 `config.yaml` 中设置所有需要打卡的学号密码 + +```bash +docker build -t selfreport:release . +docker run --name selfreport selfreport:release +``` + +之后在宿主机编辑Crontab即可 + +```bash +# crontab -e +0 4 * * * docker start selfreport +``` + ## 依赖 - python3 @@ -111,8 +152,8 @@ crontab -e - pyyaml - beautifulsoup4 - requests - - selenium - pillow + - rsa ## 感谢 diff --git a/fstate_day.json b/fstate_day.json new file mode 100644 index 0000000..8bfbc77 --- /dev/null +++ b/fstate_day.json @@ -0,0 +1,2434 @@ +{ + "p1_ChengNuo": { + "Checked": true + }, + "p1_pnlDangSZS_DangSZS": { + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_pnlDangSZS": { + "IFrameAttributes": {} + }, + "p1_BaoSRQ": { + "Text": "2022-09-18" + }, + "p1_CengFWSS": { + "F_Items": [ + [ + "是", + "是", + 1 + ], + [ + "否", + "否", + 1 + ] + ], + "SelectedValue": "否" + }, + "p1_DangQSTZK": { + "F_Items": [ + [ + "良好", + "良好(体温不高于37.3)", + 1 + ], + [ + "不适", + "不适", + 1 + ] + ], + "SelectedValue": "良好" + }, + "p1_ZhengZhuang": { + "Hidden": true, + "F_Items": [ + [ + "感冒", + "感冒", + 1 + ], + [ + "咳嗽", + "咳嗽", + 1 + ], + [ + "发热", + "发热", + 1 + ] + ], + "SelectedValueArray": [] + }, + "p1_GuoNei": { + "F_Items": [ + [ + "国内", + "国内", + 1 + ], + [ + "国外", + "国外", + 1 + ] + ], + "SelectedValue": "国内" + }, + "p1_P_GuoNei_ShiFSH": { + "Required": true, + "Hidden": false, + "SelectedValue": "在上海(校内)", + "F_Items": [ + [ + "在上海(校内)", + "在上海(进学校)", + 1 + ], + [ + "在上海(不在校内)", + "在上海(不进学校)", + 1 + ], + [ + "不在上海", + "不在上海", + 1 + ] + ] + }, + "p1_P_GuoNei_JinXXQ": { + "Required": true, + "Hidden": false, + "SelectedValueArray": [ + "宝山" + ], + "F_Items": [ + [ + "宝山", + "宝山", + 1 + ], + [ + "延长", + "延长", + 1 + ], + [ + "嘉定", + "嘉定", + 1 + ], + [ + "新闸路", + "新闸路", + 1 + ] + ] + }, + "p1_P_GuoNei_ShiFZX": { + "Required": true, + "Hidden": false, + "SelectedValue": "是", + "F_Items": [ + [ + "是", + "住校", + 1 + ], + [ + "否", + "不住校", + 1 + ] + ] + }, + "p1_P_GuoNei_XiaoQu": { + "Hidden": false, + "SelectedValue": "宝山", + "F_Items": [ + [ + "宝山", + "宝山", + 1 + ], + [ + "延长", + "延长", + 1 + ], + [ + "嘉定", + "嘉定", + 1 + ], + [ + "新闸路", + "新闸路", + 1 + ] + ] + }, + "p1_P_GuoNei_pImages_HFimgXingCM": { + "Text": "cYskH72v3ZA=" + }, + "p1_P_GuoNei_pImages_imgXingCM": { + "ImageUrl": "" + }, + "p1_P_GuoNei_pImages": { + "Hidden": false, + "IFrameAttributes": {} + }, + "p1_P_GuoNei": { + "Hidden": false, + "IFrameAttributes": {} + }, + "p1_JinChuSQ": { + "F_Items": [ + [ + "0", + "不需要进出校,不申请", + 1 + ], + [ + "1", + "需要申请进校", + 1 + ], + [ + "2", + "需要申请出校", + 1 + ] + ], + "SelectedValue": null + }, + "p1_QiuZZT": { + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_JiuYKN": { + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_JiuYYX": { + "Required": false, + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_JiuYZD": { + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_JiuYZL": { + "F_Items": [], + "SelectedValueArray": [] + }, + "p1_ddlGuoJia": { + "DataTextField": "ZhongWen", + "DataValueField": "ZhongWen", + "F_Items": [ + [ + "-1", + "选择国家", + 1, + "", + "" + ], + [ + "阿尔巴尼亚", + "阿尔巴尼亚", + 1, + "", + "" + ], + [ + "阿尔及利亚", + "阿尔及利亚", + 1, + "", + "" + ], + [ + "阿富汗", + "阿富汗", + 1, + "", + "" + ], + [ + "阿根廷", + "阿根廷", + 1, + "", + "" + ], + [ + "阿拉伯联合酋长国", + "阿拉伯联合酋长国", + 1, + "", + "" + ], + [ + "阿鲁巴", + "阿鲁巴", + 1, + "", + "" + ], + [ + "阿曼", + "阿曼", + 1, + "", + "" + ], + [ + "阿塞拜疆", + "阿塞拜疆", + 1, + "", + "" + ], + [ + "埃及", + "埃及", + 1, + "", + "" + ], + [ + "埃塞俄比亚", + "埃塞俄比亚", + 1, + "", + "" + ], + [ + "爱尔兰", + "爱尔兰", + 1, + "", + "" + ], + [ + "爱沙尼亚", + "爱沙尼亚", + 1, + "", + "" + ], + [ + "安道尔", + "安道尔", + 1, + "", + "" + ], + [ + "安哥拉", + "安哥拉", + 1, + "", + "" + ], + [ + "安圭拉", + "安圭拉", + 1, + "", + "" + ], + [ + "安提瓜和巴布达", + "安提瓜和巴布达", + 1, + "", + "" + ], + [ + "奥地利", + "奥地利", + 1, + "", + "" + ], + [ + "奥兰群岛", + "奥兰群岛", + 1, + "", + "" + ], + [ + "澳大利亚", + "澳大利亚", + 1, + "", + "" + ], + [ + "巴巴多斯", + "巴巴多斯", + 1, + "", + "" + ], + [ + "巴布亚新几内亚", + "巴布亚新几内亚", + 1, + "", + "" + ], + [ + "巴哈马", + "巴哈马", + 1, + "", + "" + ], + [ + "巴基斯坦", + "巴基斯坦", + 1, + "", + "" + ], + [ + "巴勒斯坦", + "巴勒斯坦", + 1, + "", + "" + ], + [ + "巴林", + "巴林", + 1, + "", + "" + ], + [ + "巴拿马", + "巴拿马", + 1, + "", + "" + ], + [ + "巴西", + "巴西", + 1, + "", + "" + ], + [ + "白俄罗斯", + "白俄罗斯", + 1, + "", + "" + ], + [ + "百慕大", + "百慕大", + 1, + "", + "" + ], + [ + "保加利亚", + "保加利亚", + 1, + "", + "" + ], + [ + "贝宁", + "贝宁", + 1, + "", + "" + ], + [ + "比利时", + "比利时", + 1, + "", + "" + ], + [ + "冰岛", + "冰岛", + 1, + "", + "" + ], + [ + "波多黎各", + "波多黎各", + 1, + "", + "" + ], + [ + "波兰", + "波兰", + 1, + "", + "" + ], + [ + "波斯尼亚和黑塞哥维那", + "波斯尼亚和黑塞哥维那", + 1, + "", + "" + ], + [ + "玻利维亚", + "玻利维亚", + 1, + "", + "" + ], + [ + "伯利兹", + "伯利兹", + 1, + "", + "" + ], + [ + "博茨瓦纳", + "博茨瓦纳", + 1, + "", + "" + ], + [ + "不丹", + "不丹", + 1, + "", + "" + ], + [ + "布基纳法索", + "布基纳法索", + 1, + "", + "" + ], + [ + "布隆迪", + "布隆迪", + 1, + "", + "" + ], + [ + "布维岛", + "布维岛", + 1, + "", + "" + ], + [ + "朝鲜", + "朝鲜", + 1, + "", + "" + ], + [ + "赤道几内亚", + "赤道几内亚", + 1, + "", + "" + ], + [ + "丹麦", + "丹麦", + 1, + "", + "" + ], + [ + "德国", + "德国", + 1, + "", + "" + ], + [ + "东帝汶", + "东帝汶", + 1, + "", + "" + ], + [ + "东帝汶", + "东帝汶", + 1, + "", + "" + ], + [ + "多哥", + "多哥", + 1, + "", + "" + ], + [ + "多米尼加", + "多米尼加", + 1, + "", + "" + ], + [ + "俄罗斯联邦", + "俄罗斯联邦", + 1, + "", + "" + ], + [ + "厄瓜多尔", + "厄瓜多尔", + 1, + "", + "" + ], + [ + "厄立特里亚", + "厄立特里亚", + 1, + "", + "" + ], + [ + "法国", + "法国", + 1, + "", + "" + ], + [ + "法国大都会", + "法国大都会", + 1, + "", + "" + ], + [ + "法罗群岛", + "法罗群岛", + 1, + "", + "" + ], + [ + "法属波利尼西亚", + "法属波利尼西亚", + 1, + "", + "" + ], + [ + "法属圭亚那", + "法属圭亚那", + 1, + "", + "" + ], + [ + "梵蒂冈", + "梵蒂冈", + 1, + "", + "" + ], + [ + "菲律宾", + "菲律宾", + 1, + "", + "" + ], + [ + "斐济", + "斐济", + 1, + "", + "" + ], + [ + "芬兰", + "芬兰", + 1, + "", + "" + ], + [ + "佛得角", + "佛得角", + 1, + "", + "" + ], + [ + "冈比亚", + "冈比亚", + 1, + "", + "" + ], + [ + "刚果", + "刚果", + 1, + "", + "" + ], + [ + "刚果(金)", + "刚果(金)", + 1, + "", + "" + ], + [ + "哥伦比亚", + "哥伦比亚", + 1, + "", + "" + ], + [ + "哥斯达黎加", + "哥斯达黎加", + 1, + "", + "" + ], + [ + "格林纳达", + "格林纳达", + 1, + "", + "" + ], + [ + "格鲁吉亚", + "格鲁吉亚", + 1, + "", + "" + ], + [ + "根西岛", + "根西岛", + 1, + "", + "" + ], + [ + "古巴", + "古巴", + 1, + "", + "" + ], + [ + "瓜德罗普岛", + "瓜德罗普岛", + 1, + "", + "" + ], + [ + "关岛", + "关岛", + 1, + "", + "" + ], + [ + "圭亚那", + "圭亚那", + 1, + "", + "" + ], + [ + "哈萨克斯坦", + "哈萨克斯坦", + 1, + "", + "" + ], + [ + "海地", + "海地", + 1, + "", + "" + ], + [ + "韩国", + "韩国", + 1, + "", + "" + ], + [ + "荷兰", + "荷兰", + 1, + "", + "" + ], + [ + "黑山", + "黑山", + 1, + "", + "" + ], + [ + "洪都拉斯", + "洪都拉斯", + 1, + "", + "" + ], + [ + "基里巴斯", + "基里巴斯", + 1, + "", + "" + ], + [ + "吉布提", + "吉布提", + 1, + "", + "" + ], + [ + "吉尔吉斯斯坦", + "吉尔吉斯斯坦", + 1, + "", + "" + ], + [ + "几内亚", + "几内亚", + 1, + "", + "" + ], + [ + "几内亚比绍", + "几内亚比绍", + 1, + "", + "" + ], + [ + "加拿大", + "加拿大", + 1, + "", + "" + ], + [ + "加纳", + "加纳", + 1, + "", + "" + ], + [ + "加蓬", + "加蓬", + 1, + "", + "" + ], + [ + "柬埔寨", + "柬埔寨", + 1, + "", + "" + ], + [ + "捷克", + "捷克", + 1, + "", + "" + ], + [ + "津巴布韦", + "津巴布韦", + 1, + "", + "" + ], + [ + "喀麦隆", + "喀麦隆", + 1, + "", + "" + ], + [ + "卡塔尔", + "卡塔尔", + 1, + "", + "" + ], + [ + "科科斯(基林)群岛", + "科科斯(基林)群岛", + 1, + "", + "" + ], + [ + "科摩罗", + "科摩罗", + 1, + "", + "" + ], + [ + "科特迪瓦", + "科特迪瓦", + 1, + "", + "" + ], + [ + "科威特", + "科威特", + 1, + "", + "" + ], + [ + "克罗地亚", + "克罗地亚", + 1, + "", + "" + ], + [ + "肯尼亚", + "肯尼亚", + 1, + "", + "" + ], + [ + "库克群岛", + "库克群岛", + 1, + "", + "" + ], + [ + "拉脱维亚", + "拉脱维亚", + 1, + "", + "" + ], + [ + "莱索托", + "莱索托", + 1, + "", + "" + ], + [ + "老挝", + "老挝", + 1, + "", + "" + ], + [ + "黎巴嫩", + "黎巴嫩", + 1, + "", + "" + ], + [ + "立陶宛", + "立陶宛", + 1, + "", + "" + ], + [ + "利比里亚", + "利比里亚", + 1, + "", + "" + ], + [ + "利比亚", + "利比亚", + 1, + "", + "" + ], + [ + "列支敦士登", + "列支敦士登", + 1, + "", + "" + ], + [ + "留尼汪岛", + "留尼汪岛", + 1, + "", + "" + ], + [ + "卢森堡", + "卢森堡", + 1, + "", + "" + ], + [ + "卢旺达", + "卢旺达", + 1, + "", + "" + ], + [ + "罗马尼亚", + "罗马尼亚", + 1, + "", + "" + ], + [ + "马达加斯加", + "马达加斯加", + 1, + "", + "" + ], + [ + "马恩岛", + "马恩岛", + 1, + "", + "" + ], + [ + "马尔代夫", + "马尔代夫", + 1, + "", + "" + ], + [ + "马耳他", + "马耳他", + 1, + "", + "" + ], + [ + "马拉维", + "马拉维", + 1, + "", + "" + ], + [ + "马来西亚", + "马来西亚", + 1, + "", + "" + ], + [ + "马里", + "马里", + 1, + "", + "" + ], + [ + "马其顿", + "马其顿", + 1, + "", + "" + ], + [ + "马绍尔群岛", + "马绍尔群岛", + 1, + "", + "" + ], + [ + "马提尼克岛", + "马提尼克岛", + 1, + "", + "" + ], + [ + "马约特", + "马约特", + 1, + "", + "" + ], + [ + "毛里求斯", + "毛里求斯", + 1, + "", + "" + ], + [ + "毛里塔尼亚", + "毛里塔尼亚", + 1, + "", + "" + ], + [ + "美国", + "美国", + 1, + "", + "" + ], + [ + "美属萨摩亚", + "美属萨摩亚", + 1, + "", + "" + ], + [ + "蒙古", + "蒙古", + 1, + "", + "" + ], + [ + "蒙特塞拉特", + "蒙特塞拉特", + 1, + "", + "" + ], + [ + "孟加拉", + "孟加拉", + 1, + "", + "" + ], + [ + "秘鲁", + "秘鲁", + 1, + "", + "" + ], + [ + "密克罗尼西亚", + "密克罗尼西亚", + 1, + "", + "" + ], + [ + "缅甸", + "缅甸", + 1, + "", + "" + ], + [ + "摩尔多瓦", + "摩尔多瓦", + 1, + "", + "" + ], + [ + "摩洛哥", + "摩洛哥", + 1, + "", + "" + ], + [ + "摩纳哥", + "摩纳哥", + 1, + "", + "" + ], + [ + "莫桑比克", + "莫桑比克", + 1, + "", + "" + ], + [ + "墨西哥", + "墨西哥", + 1, + "", + "" + ], + [ + "纳米比亚", + "纳米比亚", + 1, + "", + "" + ], + [ + "南非", + "南非", + 1, + "", + "" + ], + [ + "南斯拉夫", + "南斯拉夫", + 1, + "", + "" + ], + [ + "瑙鲁", + "瑙鲁", + 1, + "", + "" + ], + [ + "尼泊尔", + "尼泊尔", + 1, + "", + "" + ], + [ + "尼加拉瓜", + "尼加拉瓜", + 1, + "", + "" + ], + [ + "尼日尔", + "尼日尔", + 1, + "", + "" + ], + [ + "尼日利亚", + "尼日利亚", + 1, + "", + "" + ], + [ + "纽埃", + "纽埃", + 1, + "", + "" + ], + [ + "挪威", + "挪威", + 1, + "", + "" + ], + [ + "诺福克岛", + "诺福克岛", + 1, + "", + "" + ], + [ + "帕劳", + "帕劳", + 1, + "", + "" + ], + [ + "皮特凯恩群岛", + "皮特凯恩群岛", + 1, + "", + "" + ], + [ + "葡萄牙", + "葡萄牙", + 1, + "", + "" + ], + [ + "日本", + "日本", + 1, + "", + "" + ], + [ + "瑞典", + "瑞典", + 1, + "", + "" + ], + [ + "瑞士", + "瑞士", + 1, + "", + "" + ], + [ + "萨尔瓦多", + "萨尔瓦多", + 1, + "", + "" + ], + [ + "萨摩亚", + "萨摩亚", + 1, + "", + "" + ], + [ + "塞尔维亚", + "塞尔维亚", + 1, + "", + "" + ], + [ + "塞拉利昂", + "塞拉利昂", + 1, + "", + "" + ], + [ + "塞内加尔", + "塞内加尔", + 1, + "", + "" + ], + [ + "塞浦路斯", + "塞浦路斯", + 1, + "", + "" + ], + [ + "塞舌尔", + "塞舌尔", + 1, + "", + "" + ], + [ + "沙特阿拉伯", + "沙特阿拉伯", + 1, + "", + "" + ], + [ + "圣诞岛", + "圣诞岛", + 1, + "", + "" + ], + [ + "圣多美和普林西比", + "圣多美和普林西比", + 1, + "", + "" + ], + [ + "圣赫勒拿", + "圣赫勒拿", + 1, + "", + "" + ], + [ + "圣基茨和尼维斯", + "圣基茨和尼维斯", + 1, + "", + "" + ], + [ + "圣卢西亚", + "圣卢西亚", + 1, + "", + "" + ], + [ + "圣马力诺", + "圣马力诺", + 1, + "", + "" + ], + [ + "圣文森特和格林纳丁斯", + "圣文森特和格林纳丁斯", + 1, + "", + "" + ], + [ + "斯里兰卡", + "斯里兰卡", + 1, + "", + "" + ], + [ + "斯洛伐克", + "斯洛伐克", + 1, + "", + "" + ], + [ + "斯洛文尼亚", + "斯洛文尼亚", + 1, + "", + "" + ], + [ + "斯威士兰", + "斯威士兰", + 1, + "", + "" + ], + [ + "苏丹", + "苏丹", + 1, + "", + "" + ], + [ + "苏里南", + "苏里南", + 1, + "", + "" + ], + [ + "所罗门群岛", + "所罗门群岛", + 1, + "", + "" + ], + [ + "索马里", + "索马里", + 1, + "", + "" + ], + [ + "塔吉克斯坦", + "塔吉克斯坦", + 1, + "", + "" + ], + [ + "泰国", + "泰国", + 1, + "", + "" + ], + [ + "坦桑尼亚", + "坦桑尼亚", + 1, + "", + "" + ], + [ + "汤加", + "汤加", + 1, + "", + "" + ], + [ + "特立尼达和多巴哥", + "特立尼达和多巴哥", + 1, + "", + "" + ], + [ + "突尼斯", + "突尼斯", + 1, + "", + "" + ], + [ + "图瓦卢", + "图瓦卢", + 1, + "", + "" + ], + [ + "土耳其", + "土耳其", + 1, + "", + "" + ], + [ + "土库曼斯坦", + "土库曼斯坦", + 1, + "", + "" + ], + [ + "托克劳", + "托克劳", + 1, + "", + "" + ], + [ + "瓦利斯群岛和富图纳群岛", + "瓦利斯群岛和富图纳群岛", + 1, + "", + "" + ], + [ + "瓦努阿图", + "瓦努阿图", + 1, + "", + "" + ], + [ + "危地马拉", + "危地马拉", + 1, + "", + "" + ], + [ + "委内瑞拉", + "委内瑞拉", + 1, + "", + "" + ], + [ + "文莱", + "文莱", + 1, + "", + "" + ], + [ + "乌干达", + "乌干达", + 1, + "", + "" + ], + [ + "乌克兰", + "乌克兰", + 1, + "", + "" + ], + [ + "乌拉圭", + "乌拉圭", + 1, + "", + "" + ], + [ + "乌兹别克斯坦", + "乌兹别克斯坦", + 1, + "", + "" + ], + [ + "西班牙", + "西班牙", + 1, + "", + "" + ], + [ + "西撒哈拉", + "西撒哈拉", + 1, + "", + "" + ], + [ + "希腊", + "希腊", + 1, + "", + "" + ], + [ + "新加坡", + "新加坡", + 1, + "", + "" + ], + [ + "新喀里多尼亚", + "新喀里多尼亚", + 1, + "", + "" + ], + [ + "新西兰", + "新西兰", + 1, + "", + "" + ], + [ + "匈牙利", + "匈牙利", + 1, + "", + "" + ], + [ + "叙利亚", + "叙利亚", + 1, + "", + "" + ], + [ + "牙买加", + "牙买加", + 1, + "", + "" + ], + [ + "亚美尼亚", + "亚美尼亚", + 1, + "", + "" + ], + [ + "也门", + "也门", + 1, + "", + "" + ], + [ + "伊拉克", + "伊拉克", + 1, + "", + "" + ], + [ + "伊朗", + "伊朗", + 1, + "", + "" + ], + [ + "以色列", + "以色列", + 1, + "", + "" + ], + [ + "意大利", + "意大利", + 1, + "", + "" + ], + [ + "印度", + "印度", + 1, + "", + "" + ], + [ + "印度尼西亚", + "印度尼西亚", + 1, + "", + "" + ], + [ + "英国", + "英国", + 1, + "", + "" + ], + [ + "约旦", + "约旦", + 1, + "", + "" + ], + [ + "越南", + "越南", + 1, + "", + "" + ], + [ + "赞比亚", + "赞比亚", + 1, + "", + "" + ], + [ + "泽西岛", + "泽西岛", + 1, + "", + "" + ], + [ + "乍得", + "乍得", + 1, + "", + "" + ], + [ + "直布罗陀", + "直布罗陀", + 1, + "", + "" + ], + [ + "智利", + "智利", + 1, + "", + "" + ], + [ + "中非", + "中非", + 1, + "", + "" + ] + ], + "SelectedValueArray": [ + "-1" + ] + }, + "p1_ddlSheng": { + "Hidden": false, + "Readonly": true, + "F_Items": [ + [ + "-1", + "选择省份", + 1, + "", + "" + ], + [ + "北京", + "北京", + 1, + "", + "" + ], + [ + "天津", + "天津", + 1, + "", + "" + ], + [ + "上海", + "上海", + 1, + "", + "" + ], + [ + "重庆", + "重庆", + 1, + "", + "" + ], + [ + "河北", + "河北", + 1, + "", + "" + ], + [ + "山西", + "山西", + 1, + "", + "" + ], + [ + "辽宁", + "辽宁", + 1, + "", + "" + ], + [ + "吉林", + "吉林", + 1, + "", + "" + ], + [ + "黑龙江", + "黑龙江", + 1, + "", + "" + ], + [ + "江苏", + "江苏", + 1, + "", + "" + ], + [ + "浙江", + "浙江", + 1, + "", + "" + ], + [ + "安徽", + "安徽", + 1, + "", + "" + ], + [ + "福建", + "福建", + 1, + "", + "" + ], + [ + "江西", + "江西", + 1, + "", + "" + ], + [ + "山东", + "山东", + 1, + "", + "" + ], + [ + "河南", + "河南", + 1, + "", + "" + ], + [ + "湖北", + "湖北", + 1, + "", + "" + ], + [ + "湖南", + "湖南", + 1, + "", + "" + ], + [ + "广东", + "广东", + 1, + "", + "" + ], + [ + "海南", + "海南", + 1, + "", + "" + ], + [ + "四川", + "四川", + 1, + "", + "" + ], + [ + "贵州", + "贵州", + 1, + "", + "" + ], + [ + "云南", + "云南", + 1, + "", + "" + ], + [ + "陕西", + "陕西", + 1, + "", + "" + ], + [ + "甘肃", + "甘肃", + 1, + "", + "" + ], + [ + "青海", + "青海", + 1, + "", + "" + ], + [ + "内蒙古", + "内蒙古", + 1, + "", + "" + ], + [ + "广西", + "广西", + 1, + "", + "" + ], + [ + "西藏", + "西藏", + 1, + "", + "" + ], + [ + "宁夏", + "宁夏", + 1, + "", + "" + ], + [ + "新疆", + "新疆", + 1, + "", + "" + ], + [ + "香港", + "香港", + 1, + "", + "" + ], + [ + "澳门", + "澳门", + 1, + "", + "" + ], + [ + "台湾", + "台湾", + 1, + "", + "" + ] + ], + "SelectedValueArray": [ + "上海" + ] + }, + "p1_ddlShi": { + "Hidden": false, + "Enabled": true, + "Readonly": true, + "F_Items": [ + [ + "-1", + "选择市", + 1, + "", + "" + ], + [ + "上海市", + "上海市", + 1, + "", + "" + ] + ], + "SelectedValueArray": [ + "上海市" + ] + }, + "p1_ddlXian": { + "Hidden": false, + "Enabled": true, + "F_Items": [ + [ + "-1", + "选择县区", + 1, + "", + "" + ], + [ + "黄浦区", + "黄浦区", + 1, + "", + "" + ], + [ + "徐汇区", + "徐汇区", + 1, + "", + "" + ], + [ + "长宁区", + "长宁区", + 1, + "", + "" + ], + [ + "静安区", + "静安区", + 1, + "", + "" + ], + [ + "普陀区", + "普陀区", + 1, + "", + "" + ], + [ + "虹口区", + "虹口区", + 1, + "", + "" + ], + [ + "杨浦区", + "杨浦区", + 1, + "", + "" + ], + [ + "闵行区", + "闵行区", + 1, + "", + "" + ], + [ + "宝山区", + "宝山区", + 1, + "", + "" + ], + [ + "嘉定区", + "嘉定区", + 1, + "", + "" + ], + [ + "浦东新区", + "浦东新区", + 1, + "", + "" + ], + [ + "金山区", + "金山区", + 1, + "", + "" + ], + [ + "松江区", + "松江区", + 1, + "", + "" + ], + [ + "青浦区", + "青浦区", + 1, + "", + "" + ], + [ + "奉贤区", + "奉贤区", + 1, + "", + "" + ], + [ + "崇明区", + "崇明区", + 1, + "", + "" + ] + ], + "SelectedValueArray": [ + "宝山区" + ] + }, + "p1_ddlJieDao": { + "Hidden": false, + "F_Items": [ + [ + "-1", + "选择街道", + 1, + "", + "" + ], + [ + "大场镇", + "大场镇", + 1, + "大场镇", + "" + ], + [ + "高境镇", + "高境镇", + 1, + "高境镇", + "" + ], + [ + "顾村镇", + "顾村镇", + 1, + "顾村镇", + "" + ], + [ + "罗店镇", + "罗店镇", + 1, + "罗店镇", + "" + ], + [ + "罗泾镇", + "罗泾镇", + 1, + "罗泾镇", + "" + ], + [ + "庙行镇", + "庙行镇", + 1, + "庙行镇", + "" + ], + [ + "淞南镇", + "淞南镇", + 1, + "淞南镇", + "" + ], + [ + "吴淞街道", + "吴淞街道", + 1, + "吴淞街道", + "" + ], + [ + "杨行镇", + "杨行镇", + 1, + "杨行镇", + "" + ], + [ + "友谊路街道", + "友谊路街道", + 1, + "友谊路街道", + "" + ], + [ + "月浦镇", + "月浦镇", + 1, + "月浦镇", + "" + ], + [ + "张庙街道", + "张庙街道", + 1, + "张庙街道", + "" + ] + ], + "SelectedValueArray": [ + "大场镇" + ] + }, + "p1_XiangXDZ": { + "Hidden": false, + "Label": "国内详细地址(省市区县无需重复填写)", + "Text": "" + }, + "p1_ShiFZJ": { + "Required": true, + "Hidden": false, + "F_Items": [ + [ + "是", + "家庭地址", + 1 + ], + [ + "否", + "不是家庭地址", + 1 + ] + ], + "SelectedValue": null + }, + "p1_GaoZDFXLJS": { + "F_Items": [ + [ + "无", + "无", + 1 + ], + [ + "高", + "有高风险地区旅居史", + 1 + ], + [ + "中", + "有中风险地区旅居史", + 1 + ], + [ + "低", + "有低风险地区旅居史", + 1 + ] + ], + "SelectedValue": null + }, + "p1_QueZHZJC": { + "F_Items": [ + [ + "是", + "是", + 1 + ], + [ + "否", + "否", + 1 + ] + ], + "SelectedValue": null + }, + "p1_DangRGL": { + "SelectedValue": "否", + "F_Items": [ + [ + "是", + "是", + 1 + ], + [ + "否", + "否", + 1 + ] + ] + }, + "p1_GeLSM": { + "Hidden": true, + "IFrameAttributes": {} + }, + "p1_GeLFS": { + "Required": false, + "Hidden": true, + "F_Items": [ + [ + "居家隔离", + "居家隔离", + 1 + ], + [ + "集中隔离", + "集中隔离", + 1 + ] + ], + "SelectedValue": null + }, + "p1_GeLDZ": { + "Hidden": true + }, + "p1_ctl01_btnReturn": { + "OnClientClick": "document.location.href='/Default.aspx';return;" + }, + "p1": { + "Title": "", + "IFrameAttributes": {} + } +} \ No newline at end of file diff --git a/fstate_generator.py b/fstate_generator.py new file mode 100644 index 0000000..7bcb580 --- /dev/null +++ b/fstate_generator.py @@ -0,0 +1,206 @@ +import base64 +import datetime as dt +import json +import re +from pathlib import Path + +from bs4 import BeautifulSoup +from PIL import Image, ImageDraw, ImageFont + + +def _generate_fstate_base64(fstate): + fstate_json = json.dumps(fstate, ensure_ascii=False) + fstate_bytes = fstate_json.encode("utf-8") + return base64.b64encode(fstate_bytes).decode() + + +def generate_fstate_day(BaoSRQ, ShiFSH, JinXXQ, ShiFZX, XiaoQu, + ddlSheng, ddlShi, ddlXian, ddlJieDao, XiangXDZ, ShiFZJ, + XingCM): + with open(Path(__file__).resolve().parent.joinpath('fstate_day.json'), encoding='utf8') as f: + fstate = json.loads(f.read()) + + fstate['p1_BaoSRQ']['Text'] = BaoSRQ + fstate['p1_P_GuoNei_ShiFSH']['SelectedValue'] = ShiFSH + fstate['p1_P_GuoNei_JinXXQ']['SelectedValueArray'][0] = JinXXQ + fstate['p1_P_GuoNei_ShiFZX']['SelectedValue'] = ShiFZX + fstate['p1_P_GuoNei_XiaoQu']['SelectedValue'] = XiaoQu + fstate['p1_ddlSheng']['F_Items'] = [[ddlSheng, ddlSheng, 1, '', '']] + fstate['p1_ddlSheng']['SelectedValueArray'] = [ddlSheng] + fstate['p1_ddlShi']['F_Items'] = [[ddlShi, ddlShi, 1, '', '']] + fstate['p1_ddlShi']['SelectedValueArray'] = [ddlShi] + fstate['p1_ddlXian']['F_Items'] = [[ddlXian, ddlXian, 1, '', '']] + fstate['p1_ddlXian']['SelectedValueArray'] = [ddlXian] + fstate['p1_ddlJieDao']['F_Items'] = [[ddlJieDao, ddlJieDao, 1, '', '']] + fstate['p1_ddlJieDao']['SelectedValueArray'] = [ddlJieDao] + fstate['p1_XiangXDZ']['Text'] = XiangXDZ + fstate['p1_ShiFZJ']['SelectedValue'] = ShiFZJ + fstate['p1_P_GuoNei_pImages_HFimgXingCM']['Text'] = XingCM + + fstate_base64 = _generate_fstate_base64(fstate) + t = len(fstate_base64) // 2 + fstate_base64 = fstate_base64[:t] + 'F_STATE' + fstate_base64[t:] + + return fstate_base64 + + +def _html_to_json(html): + return json.loads(html[html.find('=') + 1:]) + + +def get_ShouJHM(sess): + print('#正在获取个人信息...') + ShouJHM = '111111111' + + r = sess.get(f'https://selfreport.shu.edu.cn/PersonInfo.aspx') + t = re.findall(r'^.*//\]', r.text, re.MULTILINE)[0] + htmls = t.split(';var ') + for i, h in enumerate(htmls): + try: + if 'ShouJHM' in h: + print('-ShouJHM-') + ShouJHM = _html_to_json(htmls[i - 1])['Text'] + except: + print('获取个人信息有错误', htmls[i - 1]) + + return ShouJHM + + +def get_last_report(sess, t): + print('#正在获取前一天的填报信息...') + ShiFSH = '在上海(校内)' + JinXXQ = '宝山' + ShiFZX = '是' + XiaoQu = '宝山' + ddlSheng = '上海' + ddlShi = '上海市' + ddlXian = '宝山区' + ddlJieDao = '大场镇' + XiangXDZ = '上海大学' + ShiFZJ = '是' + + t = t - dt.timedelta(days=1) + r = sess.get(f'https://selfreport.shu.edu.cn/ViewDayReport.aspx?day={t.year}-{t.month}-{t.day}') + t = re.findall(r'^.*//\]', r.text, re.MULTILINE)[0] + htmls = t.split(';var ') + for i, h in enumerate(htmls): + try: + if 'ShiFSH' in h: + print('-ShiFSH-') + ShiFSH = _html_to_json(htmls[i - 1])['Text'] + print(ShiFSH) + if 'JinXXQ' in h: + print('-JinXXQ-') + JinXXQ = "" if "不在上海" in ShiFSH else _html_to_json(htmls[i - 1])['Text'] + print(JinXXQ) + if 'ShiFZX' in h: + print('-ShiFZX-') + ShiFZX = _html_to_json(htmls[i - 1])['SelectedValue'] + print(ShiFZX) + if 'XiaoQu' in h: + print('-XiaoQu-') + XiaoQu = "" if "不在上海" in ShiFSH or ShiFZX == "否" else _html_to_json(htmls[i - 1])['Text'] + print(XiaoQu) + if 'ddlSheng' in h: + print('-ddlSheng-') + ddlSheng = _html_to_json(htmls[i - 1])['SelectedValueArray'][0] + print(ddlSheng) + if 'ddlShi' in h: + print('-ddlShi-') + ddlShi = _html_to_json(htmls[i - 1])['SelectedValueArray'][0] + print(ddlShi) + if 'ddlXian' in h: + print('-ddlXian-') + ddlXian = _html_to_json(htmls[i - 1])['SelectedValueArray'][0] + print(ddlXian) + if 'ddlJieDao' in h: + print('-ddlJieDao-') + if "不在上海" in ShiFSH: + ddlJieDao = "" + else: + ddlJieDao = _html_to_json(htmls[i - 1])['SelectedValueArray'][0] + if ddlJieDao == '-1': + ddlJieDao = '大场镇' + print(ddlJieDao) + if 'XiangXDZ' in h: + print('-XiangXDZ-') + XiangXDZ = _html_to_json(htmls[i - 1])['Text'] + print(f'###{XiangXDZ[-2:]}') + if 'ShiFZJ' in h: + print('-ShiFZJ-') + ShiFZJ = _html_to_json(htmls[i - 1])['SelectedValue'] + print(ShiFZJ) + except: + print('获取前一天日报有错误', htmls[i - 1], htmls[i]) + + return ShiFSH, JinXXQ, ShiFZX, XiaoQu, ddlSheng, ddlShi, ddlXian, ddlJieDao, XiangXDZ, ShiFZJ + + +def _draw_XingCM(ShouJHM: str, t): + + work_path = Path(__file__).resolve().parent + image = Image.open(str(work_path / 'xingcm.jpg')) + + font1 = ImageFont.truetype(str(work_path / 'yahei.ttf'), 30) + font2 = ImageFont.truetype(str(work_path / 'yahei.ttf'), 36) + + draw = ImageDraw.Draw(image) + draw.text((414, 380), f'{ShouJHM[:3]}****{ShouJHM[-4:]}的动态行程卡', font=font1, fill=(39, 39, 39), anchor='mm') + draw.text((414, 460), '更新于:' + t.strftime('%Y-%m-%d %H:%M:%S'), font=font2, fill=(143, 142, 147), anchor='mm') + img_path = str(Path(__file__).resolve().parent.joinpath('xingcm_a.jpg')) + image.save(img_path, 'jpeg') + + return img_path + + +def upload_img(sess, view_state, ShouJHM, t): + img_path = _draw_XingCM(ShouJHM, t) + + target = 'p1$P_GuoNei$pImages$fileXingCM' + with open(img_path, 'rb') as f: + r = sess.post('https://selfreport.shu.edu.cn/DayReport.aspx', data={ + '__EVENTTARGET': target, + '__EVENTARGUMENT': '', + '__VIEWSTATE': view_state, + 'p1$BaoSRQ': t.strftime('%Y-%m-%d') + }, files={ + target: f + }, headers={ + 'X-Requested-With': 'XMLHttpRequest', + 'X-FineUI-Ajax': 'true' + }, allow_redirects=False) + + ret = re.search(r'Text":"(.*?)"}\)', r.text) + if ret is None: + print(r.text) + return None + else: + return ret.group(1) + + +def get_img_value(sess, ShouJHM, t): + print('#正在获取随申码、行程码信息...') + + XingCM = 'cYskH72v3ZA=' + + r = sess.get(f'https://selfreport.shu.edu.cn/DayReport.aspx') + + soup = BeautifulSoup(r.text, 'html.parser') + view_state = soup.find('input', attrs={'name': '__VIEWSTATE'}) + view_state = view_state['value'] + + ret = re.findall(r'^.*//\]', r.text, re.MULTILINE)[0] + htmls = ret.split(';var ') + for i, h in enumerate(htmls): + if 'p1_P_GuoNei_pImages_HFimgXingCM' in h: + try: + XingCM = _html_to_json(htmls[i - 1])['Text'] + except: + print('没有获取到已提交行程码,开始自动上传') + code = upload_img(sess, view_state, ShouJHM, t) + if code is None: + print('上传行程码失败,使用默认行程码') + else: + XingCM = code + + return XingCM diff --git a/images/disable_workflow.png b/images/disable_workflow.png new file mode 100644 index 0000000..6006b24 Binary files /dev/null and b/images/disable_workflow.png differ diff --git a/images/enable_actions.png b/images/enable_actions.png deleted file mode 100644 index 4a74bc6..0000000 Binary files a/images/enable_actions.png and /dev/null differ diff --git a/images/enable_workflow.png b/images/enable_workflow.png new file mode 100644 index 0000000..ad433a2 Binary files /dev/null and b/images/enable_workflow.png differ diff --git a/images/run_workflow.png b/images/run_workflow.png index ec78272..8f74baf 100644 Binary files a/images/run_workflow.png and b/images/run_workflow.png differ diff --git a/login.py b/login.py index c54ac69..f319340 100644 --- a/login.py +++ b/login.py @@ -1,40 +1,69 @@ +import base64 import time -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC +import requests +import rsa +from bs4 import BeautifulSoup -RETRY = 5 -RETRY_TIMEOUT = 120 +RETRY = 1 +RETRY_TIMEOUT = 1 -def login(browser, username, password): - for retry in range(RETRY): - print(f'第{retry}次尝试登陆') +# 2021.04.17 更新密码加密 +def encryptPass(password): + key_str = '''-----BEGIN PUBLIC KEY----- + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDl/aCgRl9f/4ON9MewoVnV58OL + OU2ALBi2FKc5yIsfSpivKxe7A6FitJjHva3WpM7gvVOinMehp6if2UNIkbaN+plW + f5IwqEVxsNZpeixc4GsbY9dXEk3WtRjwGSyDLySzEESH/kpJVoxO7ijRYqU+2oSR + wTBNePOk1H+LRQokgQIDAQAB + -----END PUBLIC KEY-----''' + pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(key_str.encode('utf-8')) + crypto = base64.b64encode(rsa.encrypt(password.encode('utf-8'), pub_key)).decode() + return crypto + + +def login(username, password): + sess = requests.Session() + for _ in range(RETRY): try: - browser.get('https://selfreport.shu.edu.cn/Default.aspx') - WebDriverWait(browser, 10).until( - EC.presence_of_element_located((By.ID, 'username')) - ) - browser.find_element(By.ID, 'username').send_keys(username) - browser.find_element(By.ID, 'password').send_keys(password) - browser.find_element(By.ID, 'submit-button').click() + r = sess.get('https://selfreport.shu.edu.cn/Default.aspx', timeout=5) + code = r.url.split('/')[-1] + url_param = eval(base64.b64decode(code).decode("utf-8")) + state = url_param['state'] + sess.post(r.url, data={ + 'username': username, + 'password': encryptPass(password) + }, allow_redirects=False) + sess.get(f'https://newsso.shu.edu.cn/oauth/authorize?response_type=code&client_id=WUHWfrntnWYHZfzQ5QvXUCVy&redirect_uri=https%3a%2f%2fselfreport.shu.edu.cn%2fLoginSSO.aspx%3fReturnUrl%3d%252fDefault.aspx&scope=1&state={state}') + except Exception as e: print(e) + time.sleep(RETRY_TIMEOUT) + continue + break + else: + print('登录超时') + return - browser.get('https://selfreport.shu.edu.cn/DayReport.aspx') - time.sleep(1) - if browser.current_url == 'https://selfreport.shu.edu.cn/DayReport.aspx': - return True - else: - if 'HSJC' in browser.current_url: - print('请立即手动填报核酸检测信息') - print(browser.current_url) - return False + url = f'https://selfreport.shu.edu.cn/DayReport.aspx' + for _ in range(RETRY_TIMEOUT): + try: + r = sess.get(url) + except Exception as e: + print(e) + time.sleep(RETRY_TIMEOUT) + continue + break + else: + print('登录后验证超时') + return - print(browser.current_url) + soup = BeautifulSoup(r.text, 'html.parser') + view_state = soup.find('input', attrs={'name': '__VIEWSTATE'}) - time.sleep(RETRY_TIMEOUT) + if view_state is None or 'invalid_grant' in r.text: + print(r.text) + return - return False + return sess \ No newline at end of file diff --git a/main.py b/main.py index 4c7bdda..cf42d19 100644 --- a/main.py +++ b/main.py @@ -1,55 +1,24 @@ -# -*- coding: utf-8 -*- import datetime as dt import json import os +import random import re import sys import time -import traceback from pathlib import Path -import requests import yaml -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -from PIL import Image, ImageDraw, ImageFont +from bs4 import BeautifulSoup +from fstate_generator import * from login import login NEED_BEFORE = False # 如需补报则置为True,否则False -START_DT = dt.datetime(2022, 1, 14) # 需要补报的起始日期 +START_DT = dt.datetime(2021, 4, 20) # 需要补报的起始日期 RETRY = 5 RETRY_TIMEOUT = 120 -class element_has_value(): - def __init__(self, locator): - self.locator = locator - - def __call__(self, driver): - element = driver.find_element(*self.locator) # Finding the referenced element - if element.get_attribute('value') != '': - return element - else: - return False - - -class element_has_no_value(): - def __init__(self, locator): - self.locator = locator - - def __call__(self, driver): - element = driver.find_element(*self.locator) # Finding the referenced element - if element.get_attribute('value') == '': - return element - else: - return False - - # 获取东八区时间 def get_time(): # 获取0时区时间,变换为东八区时间 @@ -68,234 +37,149 @@ def get_time(): return t -def get_last_report(browser: webdriver.Chrome, t): - print('正在获取手机号...') - browser.get('https://selfreport.shu.edu.cn/PersonInfo.aspx') - time.sleep(1) +def report_day(sess, t): + url = f'https://selfreport.shu.edu.cn/DayReport.aspx?day={t.year}-{t.month}-{t.day}' - # 手机号 - ShouJHM = browser.find_element(By.ID, 'persinfo_ctl00_ShouJHM-inputEl').get_attribute('value') - if NEED_BEFORE: - print('开始补报,正在获取补报日期前一天的填报信息...') - t = START_DT - dt.timedelta(days=1) - else: - print('正在获取前一天的填报信息...') - t = t - dt.timedelta(days=1) - browser.get(f'https://selfreport.shu.edu.cn/ViewDayReport.aspx?day={t.year}-{t.month}-{t.day}') - time.sleep(1) - - # 是否在上海,在上海(校内),在上海(不在校内),不在上海 - ShiFSH = browser.find_element(By.CSS_SELECTOR, '#ctl03_ShiFSH #ctl03_ShiFSH-inputEl').text - # 是否住校 - ShiFZX = 'f-checked' in browser.find_element(By.CSS_SELECTOR, '#ctl03_ShiFZX .f-field-checkbox-icon').get_attribute('class') - # 省 - ddlSheng = browser.find_element(By.CSS_SELECTOR, '#ctl03_ddlSheng #ctl03_ddlSheng-inputEl').get_attribute('value') - # 市 - ddlShi = browser.find_element(By.CSS_SELECTOR, '#ctl03_ddlShi #ctl03_ddlShi-inputEl').get_attribute('value') - # 县 - ddlXian = browser.find_element(By.CSS_SELECTOR, '#ctl03_ddlXian #ctl03_ddlXian-inputEl').get_attribute('value') - # 详细地址 - XiangXDZ = browser.find_element(By.CSS_SELECTOR, '#ctl03_XiangXDZ #ctl03_XiangXDZ-inputEl').get_attribute('value') - # 是否家庭地址 - ShiFZJ = 'f-checked' in browser.find_element(By.CSS_SELECTOR, '#ctl03_ShiFZJ .f-field-checkbox-icon').get_attribute('class') - - return ShouJHM, ShiFSH, ShiFZX, ddlSheng, ddlShi, ddlXian, XiangXDZ, ShiFZJ - - -def draw_XingCM(ShouJHM: str, t): - image = Image.open('xingcm.jpg') - - font1 = ImageFont.truetype('yahei.ttf', 30) - font2 = ImageFont.truetype('yahei.ttf', 36) - draw = ImageDraw.Draw(image) - draw.text((414, 380), f'{ShouJHM[:3]}****{ShouJHM[-4:]}的动态行程卡', font=font1, fill=(39, 39, 39), anchor='mm') - draw.text((414, 460), '更新于:' + t.strftime('%Y-%m-%d %H:%M:%S'), font=font2, fill=(143, 142, 147), anchor='mm') - image.save('xingcm_a.jpg', 'jpeg') - - return os.path.dirname(os.path.abspath(__file__)) + os.path.sep + 'xingcm_a.jpg' - - -def report_day(browser: webdriver.Chrome, - ShouJHM, ShiFSH, ShiFZX, ddlSheng, ddlShi, ddlXian, XiangXDZ, ShiFZJ, - t: dt.datetime): - print(f'正在填报{t.year}-{t.month}-{t.day}') - browser.get(f'https://selfreport.shu.edu.cn/DayReport.aspx?day={t.year}-{t.month}-{t.day}') - time.sleep(1) - - print('承诺') - browser.find_element(By.ID, 'p1_ChengNuo-inputEl-icon').click() - - checkboxes = browser.find_elements(By.CSS_SELECTOR, '#p1_pnlDangSZS .f-field-checkbox-icon') - if len(checkboxes) > 0: # 有的人没有答题 - print('答题') - checkboxes[0].click() - - print('是否在上海', ShiFSH) - # 在上海(校内),在上海(不在校内),不在上海 - checkboxes = browser.find_elements(By.CSS_SELECTOR, '#p1_ShiFSH .f-field-checkbox-icon') - if ShiFSH == '在上海(不在校内)': - checkboxes[1].click() - elif ShiFSH == '不在上海': - checkboxes[2].click() + for _ in range(RETRY): + try: + r = sess.get(url, allow_redirects=False) + except Exception as e: + print(e) + time.sleep(RETRY_TIMEOUT) + continue + break else: - checkboxes[0].click() - time.sleep(1) - - print('是否住校', ShiFZX) - try: - checkboxes = browser.find_elements(By.CSS_SELECTOR, '#p1_ShiFZX .f-field-checkbox-icon') - checkboxes[0 if ShiFZX else 1].click() - except Exception as e: - print('是否住校提交失败') - - print('省市县详细地址', ddlSheng, ddlShi, ddlXian, XiangXDZ[:2]) - elem = browser.find_element(By.CSS_SELECTOR, "#p1_ddlSheng input[name='p1$ddlSheng$Value']") - browser.execute_script(''' - var elem = arguments[0]; - var value = arguments[1]; - elem.value = value; - ''', elem, ddlSheng) - - elem = browser.find_element(By.CSS_SELECTOR, "#p1_ddlShi input[name='p1$ddlShi$Value']") - browser.execute_script(''' - var elem = arguments[0]; - var value = arguments[1]; - elem.value = value; - ''', elem, ddlShi) - - elem = browser.find_element(By.CSS_SELECTOR, "#p1_ddlXian input[name='p1$ddlXian$Value']") - browser.execute_script(''' - var elem = arguments[0]; - var value = arguments[1]; - elem.value = value; - ''', elem, ddlXian) - - elem = browser.find_element(By.CSS_SELECTOR, "#p1_XiangXDZ #p1_XiangXDZ-inputEl") - browser.execute_script(''' - var elem = arguments[0]; - var value = arguments[1]; - elem.value = value; - ''', elem, XiangXDZ) - - print('是否家庭地址', ShiFZJ) - checkboxes = browser.find_elements(By.CSS_SELECTOR, '#p1_ShiFZJ .f-field-checkbox-icon') - checkboxes[0 if ShiFZJ else 1].click() - time.sleep(0.5) - - # 随申码 - try: - SuiSM = browser.find_element(By.ID, 'p1_pImages_HFimgSuiSM-inputEl') - if SuiSM.get_attribute('value') == '': - print('未检测到已提交随申码') - upload = browser.find_element(By.NAME, 'p1$pImages$fileSuiSM') - upload.send_keys(draw_XingCM(ShouJHM, t)) - WebDriverWait(browser, 10).until( - element_has_no_value((By.NAME, 'p1$pImages$fileSuiSM')) - ) - - browser.find_element(By.CSS_SELECTOR, '#p1_pImages_fileSuiSM a.f-btn').click() - WebDriverWait(browser, 10).until( - element_has_value((By.ID, 'p1_pImages_HFimgSuiSM-inputEl')) - ) - - print(SuiSM.get_attribute('value')) - else: - print(f'已提交随申码') - except Exception as e: - print(e) - print('随申码提交失败') - - # 行程码 - try: - XingCM = browser.find_element(By.ID, 'p1_pImages_HFimgXingCM-inputEl') - if XingCM.get_attribute('value') == '': - print('未检测到已提交行程码') - upload = browser.find_element(By.NAME, 'p1$pImages$fileXingCM') - upload.send_keys(draw_XingCM(ShouJHM, t)) - WebDriverWait(browser, 10).until( - element_has_no_value((By.NAME, 'p1$pImages$fileXingCM')) - ) - - browser.find_element(By.CSS_SELECTOR, '#p1_pImages_fileXingCM a').click() - WebDriverWait(browser, 10).until( - element_has_value((By.ID, 'p1_pImages_HFimgXingCM-inputEl')) - ) - - print(XingCM.get_attribute('value')) + print('获取每日一报起始页超时') + return False + + soup = BeautifulSoup(r.text, 'html.parser') + view_state = soup.find('input', attrs={'name': '__VIEWSTATE'}) + + if view_state is None: + if '上海大学统一身份认证' in r.text: + print('登录信息过期') else: - print(f'已提交行程码') - except Exception as e: - print(e) - print('行程码提交失败') - - # 确认提交 - browser.find_element(By.ID, 'p1_ctl02_btnSubmit').click() - - messagebox = WebDriverWait(browser, 10).until( - EC.presence_of_element_located((By.CLASS_NAME, 'f-messagebox')) - ) - - if '确定' in messagebox.text: - for a in messagebox.find_elements(By.TAG_NAME, 'a'): - if a.text == '确定': - a.click() - break - time.sleep(2) - - messagebox = WebDriverWait(browser, 10).until( - EC.presence_of_element_located((By.CLASS_NAME, 'f-messagebox')) - ) - if '提交成功' in messagebox.text: + print(r.text) + return False + else: + view_state = view_state['value'] + + print('#正在获取历史信息...#') + + BaoSRQ = t.strftime('%Y-%m-%d') + ShiFSH, JinXXQ, ShiFZX, XiaoQu, ddlSheng, ddlShi, ddlXian, ddlJieDao, XiangXDZ, ShiFZJ = get_last_report(sess, t) + XingCM = '' + if 'IMG' in os.environ and os.environ['IMG'] != '': + ShouJHM = get_ShouJHM(sess) + XingCM = get_img_value(sess, ShouJHM, t) + + print('#信息获取完成#') + print(f'是否在上海:{ShiFSH}') + print(f'进校校区:{JinXXQ}') + print(f'是否在校:{ShiFZX}') + print(f'校区:{XiaoQu}') + print(ddlSheng, ddlShi, ddlXian, ddlJieDao, f'***{XiangXDZ[-2:]}') + print(f'是否为家庭地址:{ShiFZJ}') + print(f'行程码:{XingCM}') + + for _ in range(RETRY): + try: + r = sess.post(url, data={ + "__EVENTTARGET": "p1$ctl01$btnSubmit", + "__EVENTARGUMENT": "", + "__VIEWSTATE": view_state, + "__VIEWSTATEGENERATOR": "7AD7E509", + "p1$ChengNuo": "p1_ChengNuo", + "p1$BaoSRQ": BaoSRQ, + "p1$CengFWSS": "否", + "p1$DangQSTZK": "良好", + "p1$TiWen": "", + "p1$GuoNei": "国内", + "p1$P_GuoNei$ShiFSH": ShiFSH, + "p1$P_GuoNei$JinXXQ": JinXXQ, + "p1$P_GuoNei$ShiFZX": ShiFZX, + "p1$P_GuoNei$XiaoQu": XiaoQu, + "p1$P_GuoNei$pImages$HFimgXingCM": XingCM, + "p1$JiuYe_ShouJHM": "", + "p1$JiuYe_Email": "", + "p1$JiuYe_Wechat": "", + "p1$QiuZZT": "", + "p1$JiuYKN": "", + "p1$JiuYSJ": "", + "p1$ddlGuoJia$Value": "-1", + "p1$ddlGuoJia": "选择国家", + "p1$ddlSheng$Value": ddlSheng, + "p1$ddlSheng": ddlSheng, + "p1$ddlShi$Value": ddlShi, + "p1$ddlShi": ddlShi, + "p1$ddlXian$Value": ddlXian, + "p1$ddlXian": ddlXian, + "p1$ddlJieDao$Value": ddlJieDao, + "p1$ddlJieDao": ddlJieDao, + "p1$XiangXDZ": XiangXDZ, + "p1$ShiFZJ": ShiFZJ, + "p1$GaoZDFXLJS": "无", + "p1$QueZHZJC": "否", + "p1$DangRGL": "否", + "p1$GeLDZ": "", + "p1$Address2": "", + "F_TARGET": "p1_ctl01_btnSubmit", + "p1_pnlDangSZS_Collapsed": "false", + "p1_P_GuoNei_pImages_Collapsed": "false", + "p1_P_GuoNei_Collapsed": "false", + "p1_GeLSM_Collapsed": "false", + "p1_Collapsed": "false", + "F_STATE": generate_fstate_day(BaoSRQ, ShiFSH, JinXXQ, ShiFZX, XiaoQu, + ddlSheng, ddlShi, ddlXian, ddlJieDao, XiangXDZ, ShiFZJ, + XingCM) + }, headers={ + 'X-Requested-With': 'XMLHttpRequest', + 'X-FineUI-Ajax': 'true' + }, allow_redirects=False) + except Exception as e: + print(e) + time.sleep(RETRY_TIMEOUT) + continue + + if any(i in r.text for i in ['提交成功', '历史信息不能修改', '现在还没到晚报时间', '只能填报当天或补填以前的信息']): return True + elif '数据库有点忙' in r.text: + print('数据库有点忙,重试') + time.sleep(RETRY_TIMEOUT) + continue else: - print(messagebox.text) + print(r.text) return False + else: - print(messagebox.text) + print('每日一报填报超时') return False def view_messages(sess): - try: - r = sess.get('https://selfreport.shu.edu.cn/MyMessages.aspx') - t = re.findall(r'^.*//\]', r.text, re.MULTILINE)[0] - htmls = t.split(';var ') - for h in htmls: - if '未读' in h: - f_items = json.loads(h[h.find('=') + 1:])['F_Items'] - for item in f_items: - if '未读' in item[1]: - sess.get(f'https://selfreport.shu.edu.cn{item[4]}') - print('已读', item[4]) - break - except Exception as e: - print(e) - print('view_messages 失败,已忽略') + r = sess.get('https://selfreport.shu.edu.cn/MyMessages.aspx') + t = re.findall(r'^.*//\]', r.text, re.MULTILINE)[0] + htmls = t.split(';var ') + for h in htmls: + if '未读' in h: + f_items = json.loads(h[h.find('=') + 1:])['F_Items'] + for item in f_items: + if '未读' in item[1]: + sess.get(f'https://selfreport.shu.edu.cn{item[4]}', allow_redirects=False) + print('已读', item[4]) + break def notice(sess): - try: - sess.post('https://selfreport.shu.edu.cn/DayReportNotice.aspx') - except Exception as e: - print(e) - print('notice 失败,已忽略') + sess.post('https://selfreport.shu.edu.cn/DayReportNotice.aspx') if __name__ == "__main__": with open(Path(__file__).resolve().parent.joinpath('config.yaml'), encoding='utf8') as f: config = yaml.load(f, Loader=yaml.FullLoader) - if 'users' in os.environ: - os_users = os.environ['users'].split(';') - if len(sys.argv) == 2: - if sys.argv[1] == 'gh-vu': - print(os_users[0].split(',')[0]) - exit(0) - elif sys.argv[1] == 'gh-vp': - print(os_users[0].split(',')[1]) - exit(0) - - for user_password in os_users: + if 'USERS' in os.environ: + for user_password in os.environ['USERS'].split(';'): user, password = user_password.split(',') config[user] = { 'pwd': password @@ -309,65 +193,42 @@ def notice(sess): user_abbr = user[-4:] print(f'====={user_abbr}=====') + sess = login(user, config[user]['pwd']) - chrome_options = webdriver.ChromeOptions() - - chrome_options.add_argument('--headless') - chrome_options.add_argument('--disable-gpu') - chrome_options.add_argument("window-size=428,843") - chrome_options.add_argument("--no-sandbox") - - s = Service() - browser = webdriver.Chrome(options=chrome_options, service=s) - browser.implicitly_wait(10) - - login_result = login(browser, user, config[user]['pwd']) - - if login_result: + if sess: print('登录成功') - else: - print('登录失败') - failed_users.append(user_abbr) - continue - sess = requests.Session() - for cookie in browser.get_cookies(): - sess.cookies.set(cookie['name'], cookie['value']) - - notice(sess) - view_messages(sess) - - now = get_time() - for retry in range(RETRY): - print(f'第{retry}次尝试填报') - - try: - infos = get_last_report(browser, now) - if NEED_BEFORE: - t = START_DT - while t < now: - report_result = report_day(browser, - *infos, - t) - if report_result: - print(f'{now} 每日一报补报成功') - else: - print(f'{now} 每日一报补报失败') - t = t + dt.timedelta(days=1) - report_result = report_day(browser, - *infos, - now) - - break - except: - print(traceback.format_exc()) - report_result = False - - if report_result: - print(f'{now} 每日一报提交成功') - succeeded_users.append(user_abbr) + fake_ip = '59.79.' + '.'.join(str(random.randint(0, 255)) for _ in range(2)) + print('生成了随机IP: %s' % fake_ip) + headers = { + 'X-Forwarded-For': fake_ip, + } + sess.headers.update(headers) + + # notice(sess) + view_messages(sess) + + now = get_time() + + if NEED_BEFORE: + t = START_DT + while t < now: + if report_day(sess, t): + print(f'{t} 每日一报补报成功') + else: + print(f'{t} 每日一报补报失败') + + t = t + dt.timedelta(days=1) + + now = get_time() + if report_day(sess, now): + print(f'{now} 每日一报提交成功') + succeeded_users.append(user_abbr) + else: + print(f'{now} 每日一报提交失败') + failed_users.append(user_abbr) else: - print(f'{now} 每日一报提交失败') + print('登录失败') failed_users.append(user_abbr) if i < len(config) - 1: diff --git a/ovpn.py b/ovpn.py new file mode 100644 index 0000000..cc397c2 --- /dev/null +++ b/ovpn.py @@ -0,0 +1,57 @@ +""" +This code is maintained by @panghaibin and is distributed under the GPL-2.0 license. +Original source: https://github.com/panghaibin/shuasr/blob/master/ovpn.py +""" +import os +import re +SHU_DOMAINS = [ + 'speedtest.shu.edu.cn', + 'selfreport.shu.edu.cn', + 'newsso.shu.edu.cn', + 'oauth.shu.edu.cn', + 'services.shu.edu.cn', + 'xk.autoisp.shu.edu.cn', + 'xk.shu.edu.cn', +] +SHU_IPS = [] + + +def get_ip(domain): + ip = os.popen("dig +short {}".format(domain)).read().strip() + ip = re.findall(r'\d+\.\d+\.\d+\.\d+', ip)[0] + return ip + + +def get_route_config(): + global SHU_IPS + routing_config = "route-nopull\nroute-metric 150\nmax-routes 1000" + for domain in SHU_DOMAINS: + ip = get_ip(domain) + SHU_IPS.append(ip) + SHU_IPS = list(set(SHU_IPS)) + for ip in SHU_IPS: + routing_config += "\n" + routing_config += "route %s 255.255.255.255" % ip + print(routing_config) + return routing_config + + +def config_ovpn(): + global SHU_IPS + users = os.environ['USERS'].split(';') + user = users[0].split(',') + secret = user[0] + "\n" + user[1] + with open('secret.txt', 'w') as f: + f.write(secret) + + routing_config = get_route_config() + + with open('.github/vpn/config.ovpn', 'r') as f: + content = f.read() + content = content.replace('# ROUTING CONFIG', routing_config) + with open('.github/vpn/config.ovpn', 'w') as fa: + fa.write(content) + + +if __name__ == '__main__': + config_ovpn() diff --git a/ovpn.sh b/ovpn.sh new file mode 100755 index 0000000..f8714cb --- /dev/null +++ b/ovpn.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# This code is maintained by @panghaibin and is distributed under the GPL-2.0 license. +# Original source: https://github.com/panghaibin/shuasr/blob/master/ovpn.sh + +connect_times=0 +while true; do + openvpn --config ".github/vpn/config.ovpn" --log "vpn.log" --daemon + connect_times=$((connect_times + 1)) + sleep 5 + test_times=0 + while true; do + if ping -c1 xk.autoisp.shu.edu.cn; then + echo "OpenVPN 已成功连接" + exit 0 + else + test_times=$((test_times + 1)) + if [ $test_times -gt 15 ]; then + echo "测试OpenVPN连接失败15次,将尝试重新连接......" + if killall openvpn; then + echo "已停止OpenVPN进程" + else + echo "停止OpenVPN进程失败" + fi + break + fi + sleep 2 + fi + done + + if [ $connect_times -gt 5 ]; then + echo "OpenVPN连接失败5次,将退出" + exit 1 + fi + + if [ $connect_times -gt 1 ]; then + sleep_time=$((RANDOM % 240 + 60)) + else + sleep_time=$((RANDOM % 900 + 300)) + fi + echo "开始休眠 $sleep_time 秒" + for ((i = 0; i < sleep_time; i = i + 5)); do + echo -ne "休眠中,剩余秒数:$((sleep_time - i)) \r" + sleep 5 + done +done diff --git a/requirements.txt b/requirements.txt index f5a2e79..bd27e3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ pyyaml>=5.0.0 beautifulsoup4>=4.0.0 requests>=2.0.0 rsa>=4.0.0 -selenium>=4.0.0 pillow>=8.0.0 \ No newline at end of file diff --git a/xingcm.jpg b/xingcm.jpg index edc5f80..94c8c38 100644 Binary files a/xingcm.jpg and b/xingcm.jpg differ