From ad43aff9c70acaa6082fb94ce9919c427bbdb3ee Mon Sep 17 00:00:00 2001 From: biss Date: Mon, 27 Jul 2026 21:07:41 +0800 Subject: [PATCH] gitea --- .gitea/workflows/check-scores.yml | 204 +++++++++++++++++++++++++++ .gitea/workflows/delete_old_runs.yml | 106 ++++++++++++++ .github/workflows/main.yml | 1 + README.md | 1 + scripts/get_grade.py | 34 ++++- 5 files changed, 342 insertions(+), 4 deletions(-) create mode 100644 .gitea/workflows/check-scores.yml create mode 100644 .gitea/workflows/delete_old_runs.yml diff --git a/.gitea/workflows/check-scores.yml b/.gitea/workflows/check-scores.yml new file mode 100644 index 0000000..a1e0d31 --- /dev/null +++ b/.gitea/workflows/check-scores.yml @@ -0,0 +1,204 @@ +name: CheckScores + +run-name: ${{ gitea.workflow }} ${{ gitea.sha }} + +on: + schedule: + # 每小时的第 0、30 分钟运行,即每 30 分钟一次 + - cron: "*/30 * * * *" + + workflow_dispatch: + inputs: + force_push_message: + description: "Whether or not to force a push message?" + required: true + default: "False" + type: choice + options: + - "True" + - "False" + +# 允许内置 GITEA_TOKEN 向当前仓库提交代码 +permissions: + contents: write + +jobs: + CheckScores: + name: CheckScores + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: https://github.com/actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITEA_TOKEN }} + + - name: Configure Git + run: | + git config --global user.email "actions@gitea.local" + git config --global user.name "Gitea Actions" + + - name: Set up Python + uses: https://github.com/actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install requests rsa pyquery + + - name: Get Connection + run: | + url="https://ifconfig.me/all" + response="$(curl -fsS "$url" || true)" + echo "$response" + + - name: Run main program + id: run_main_program + env: + # 定时触发时此值为空,下面的脚本会自动改为 False + FORCE_PUSH_MESSAGE: ${{ gitea.event.inputs.force_push_message }} + + URL: ${{ secrets.URL }} + USERNAME: ${{ secrets.USERNAME }} + PASSWORD: ${{ secrets.PASSWORD }} + TOKEN: ${{ secrets.TOKEN }} + + GITEA_REF_NAME: ${{ gitea.ref_name }} + GITEA_EVENT_NAME: ${{ gitea.event_name }} + GITEA_ACTOR: ${{ gitea.actor }} + GITEA_TRIGGERING_ACTOR: ${{ gitea.triggering_actor }} + REPOSITORY_NAME: ${{ gitea.repository }} + GITEA_SHA: ${{ gitea.sha }} + GITEA_WORKFLOW: ${{ gitea.workflow }} + GITEA_RUN_NUMBER: ${{ gitea.run_number }} + GITEA_RUN_ID: ${{ gitea.run_id }} + + run: | + set -euo pipefail + + secret_is_empty=false + + if [ -z "${URL:-}" ]; then + echo "错误:URL Secret 为空。" + secret_is_empty=true + fi + + if [ -z "${USERNAME:-}" ]; then + echo "错误:USERNAME Secret 为空。" + secret_is_empty=true + fi + + if [ -z "${PASSWORD:-}" ]; then + echo "错误:PASSWORD Secret 为空。" + secret_is_empty=true + fi + + if [ -z "${TOKEN:-}" ]; then + echo "错误:TOKEN Secret 为空。" + secret_is_empty=true + fi + + if [ "$secret_is_empty" = true ]; then + echo "一个或多个 Secrets 未配置。" + exit 1 + fi + + # 定时任务没有 workflow_dispatch 输入,默认使用 False + export FORCE_PUSH_MESSAGE="${FORCE_PUSH_MESSAGE:-False}" + + export BEIJING_TIME="$( + TZ='Asia/Shanghai' date +'%Y-%m-%d %H:%M:%S:%3N' + )" + + echo "BEIJING_TIME=$BEIJING_TIME" >> "$GITEA_OUTPUT" + + mask_middle() { + local input="$1" + local length="${#input}" + + if [ "$length" -le 3 ]; then + printf '%*s\n' "$length" '' | tr ' ' '*' + else + local first="${input:0:1}" + local last="${input: -1}" + local middle_length=$((length - 2)) + local middle + + middle="$(printf '%*s' "$middle_length" '' | tr ' ' '*')" + echo "${first}${middle}${last}" + fi + } + + anonymize_domain() { + local url="$1" + local protocol + local rest + local domain + local path + local anonymized_domain="" + + protocol="$(echo "$url" | sed -n 's#^\(.*://\).*#\1#p')" + rest="${url#*://}" + domain="$(echo "$rest" | sed 's#/.*##')" + path="$(echo "$rest" | sed 's#^[^/]*##')" + + IFS='.' read -r -a domain_parts <<< "$domain" + + for part in "${domain_parts[@]}"; do + anonymized_domain+="$(printf '%*s' "${#part}" '' | tr ' ' '*')." + done + + anonymized_domain="${anonymized_domain%.}" + echo "${protocol}${anonymized_domain}${path}" + } + + LOGINURL="xtgl/login_slogin.html" + + echo "URL: $(anonymize_domain "$URL")${LOGINURL}" + echo "Username: $(mask_middle "$USERNAME")" + echo "Password: $(mask_middle "$PASSWORD")" + echo "Force Push Message: $FORCE_PUSH_MESSAGE" + echo "Branch Name: $GITEA_REF_NAME" + echo "Triggered By: $GITEA_EVENT_NAME" + echo "Initial Run By: $GITEA_ACTOR" + echo "Initiated Run By: $GITEA_TRIGGERING_ACTOR" + echo "Repository Name: $REPOSITORY_NAME" + echo "Commit SHA: $GITEA_SHA" + echo "Workflow Name: $GITEA_WORKFLOW" + echo "Workflow Number: $GITEA_RUN_NUMBER" + echo "Workflow ID: $GITEA_RUN_ID" + echo "Beijing Time: $BEIJING_TIME" + + echo "------" + + python main.py + + - name: Delete __pycache__ folders + run: | + find . \ + -type d \ + -name "__pycache__" \ + -prune \ + -exec rm -rf {} + + + - name: Commit and push changes + env: + BRANCH_NAME: ${{ gitea.ref_name }} + run: | + set -euo pipefail + + git add -A + + if git diff --cached --quiet; then + echo "没有需要提交的更改。" + exit 0 + fi + + git commit -m "Update from Gitea Actions" + + # 避免使用 --force 覆盖远端提交 + git pull --rebase origin "$BRANCH_NAME" + git push origin "HEAD:$BRANCH_NAME" \ No newline at end of file diff --git a/.gitea/workflows/delete_old_runs.yml b/.gitea/workflows/delete_old_runs.yml new file mode 100644 index 0000000..e9d9302 --- /dev/null +++ b/.gitea/workflows/delete_old_runs.yml @@ -0,0 +1,106 @@ +name: DeleteOldRuns + +run-name: DeleteOldRuns + +on: + schedule: + # Gitea 1.23+ 按 UTC 解释: + # UTC 16:00 = 北京时间次日 00:00 + - cron: "0 16 * * *" + + workflow_dispatch: + inputs: + hour_count: + description: "删除多少小时前的运行记录?" + required: true + default: "168" + type: string + +# 读取仓库代码,并允许删除 Actions 运行记录 +permissions: + contents: read + actions: write + +jobs: + DeleteOldRuns: + name: DeleteOldRuns + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: https://github.com/actions/checkout@v4 + + - name: Set up Python + uses: https://github.com/actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install requests + + - name: Get Connection + run: | + curl -fsS https://ifconfig.me/all || true + + - name: Delete old Gitea Actions runs + id: run_main_program + env: + # 手动运行时来自输入;定时运行时为空 + HOUR_COUNT: ${{ gitea.event.inputs.hour_count }} + + # Gitea 自动提供的当前仓库令牌 + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + + # 当前 Gitea 实例及运行信息 + GITEA_API_URL: ${{ gitea.api_url }} + GITEA_REPOSITORY: ${{ gitea.repository }} + GITEA_RUN_ID: ${{ gitea.run_id }} + GITEA_REF_NAME: ${{ gitea.ref_name }} + GITEA_EVENT_NAME: ${{ gitea.event_name }} + GITEA_ACTOR: ${{ gitea.actor }} + GITEA_TRIGGERING_ACTOR: ${{ gitea.triggering_actor }} + GITEA_SHA: ${{ gitea.sha }} + GITEA_WORKFLOW: ${{ gitea.workflow }} + GITEA_RUN_NUMBER: ${{ gitea.run_number }} + + run: | + set -euo pipefail + + # 定时触发时没有 workflow_dispatch 输入,默认保留 168 小时 + if ! [[ "${HOUR_COUNT:-}" =~ ^[0-9]+$ ]]; then + export HOUR_COUNT="168" + fi + + export BEIJING_TIME="$( + TZ='Asia/Shanghai' date +'%Y-%m-%d %H:%M:%S:%3N' + )" + + echo "BEIJING_TIME=$BEIJING_TIME" >> "$GITEA_OUTPUT" + + echo "Hour Count: $HOUR_COUNT" + echo "Branch Name: $GITEA_REF_NAME" + echo "Triggered By: $GITEA_EVENT_NAME" + echo "Initial Run By: $GITEA_ACTOR" + echo "Initiated Run By: $GITEA_TRIGGERING_ACTOR" + echo "Repository Name: $GITEA_REPOSITORY" + echo "Commit SHA: $GITEA_SHA" + echo "Workflow Name: $GITEA_WORKFLOW" + echo "Workflow Number: $GITEA_RUN_NUMBER" + echo "Workflow ID: $GITEA_RUN_ID" + echo "Gitea API URL: $GITEA_API_URL" + echo "Beijing Time: $BEIJING_TIME" + + echo "------" + + python scripts/delete_old_runs.py + + - name: Delete __pycache__ folders + if: always() + run: | + find . \ + -type d \ + -name "__pycache__" \ + -prune \ + -exec rm -rf {} + \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5d441c6..6da4654 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -63,6 +63,7 @@ jobs: FORCE_PUSH_MESSAGE: ${{ github.event.inputs.force_push_message }} GITHUB_ACTIONS: ${{github.actions}} URL: ${{ secrets.URL }} + GRADE_API_MODE: ${{ secrets.GRADE_API_MODE }} USERNAME: ${{ secrets.USERNAME }} PASSWORD: ${{ secrets.PASSWORD }} TOKEN: ${{ secrets.TOKEN }} diff --git a/README.md b/README.md index c6aa491..80c0b62 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ | Name | 例子 | 说明 | | -------- | ---------------------- | ------------------------------------------------------------------------- | | URL | https://www.klaio.top/ | 教务系统地址 | +| GRADE_API_MODE | auto | 可选;成绩接口兼容模式:`auto`、`dg` 或 `xsgr`,未配置时默认为 `auto` | | USERNAME | 2971802058 | 教务系统用户名 | | PASSWORD | Y3xhaCkb5PZ4 | 教务系统密码 | | TOKEN | J65KWMBfyDh3YPLpcvm8 | [Showdoc 的 token](https://push.showdoc.com.cn/#/push "Showdoc 的 token") | diff --git a/scripts/get_grade.py b/scripts/get_grade.py index b740728..cbfa403 100644 --- a/scripts/get_grade.py +++ b/scripts/get_grade.py @@ -1,8 +1,30 @@ +import os import time import traceback +def get_grade_endpoint_modes(): + """从环境变量读取成绩接口模式,并返回接口尝试顺序。""" + grade_api_mode = (os.environ.get("GRADE_API_MODE") or "auto").strip().lower() + aliases = { + "dgxscj": "dg", + "xsgrcj": "xsgr", + } + grade_api_mode = aliases.get(grade_api_mode, grade_api_mode) + endpoint_modes = { + "auto": (True, False), + "dg": (True,), + "xsgr": (False,), + } + if grade_api_mode not in endpoint_modes: + raise ValueError( + "GRADE_API_MODE 配置无效,仅支持 auto、dg 或 xsgr。" + ) + return endpoint_modes[grade_api_mode] + + def get_grade(student_client, output_type="none"): + grade_endpoint_modes = get_grade_endpoint_modes() try: # 定义重试次数上限为5次 attempts = 5 @@ -19,10 +41,14 @@ def get_grade(student_client, output_type="none"): # 使用while循环最多重试5次获取成绩信息 while attempts > 0: - # 调用student_client的get_grade方法获取成绩信息 - # 学生成绩查询目前使用 cjcx_cxDgXscj 接口 - grade_data = student_client.get_grade(use_personal_info=True).get("data", {}) - grade = grade_data.get("courses", []) + # auto 模式优先使用 DgXscj,内容为空或接口不兼容时回退 Xsgrcj + for use_personal_info in grade_endpoint_modes: + grade_data = student_client.get_grade( + use_personal_info=use_personal_info + ).get("data", {}) + grade = grade_data.get("courses", []) + if grade: + break # 如果grade不为空,跳出循环 if grade: