├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .idea
├── .gitignore
├── HDU_AUTO_BOOK-public.iml
├── deployment.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── config
├── basic_config.yml
└── seat_config.yml
├── docs
├── img1.png
├── img2.png
├── img3.png
├── logo.png
├── tutorial.mov
└── tutorial.mp4
├── main.py
├── requirements.txt
└── user_config.yml
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: HDU Auto Book
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '57 11 * * *'
7 | - cron: '57 12 * * *'
8 |
9 | jobs:
10 | bot:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout codes
14 | uses: actions/checkout@v2
15 | - name: Set up Python
16 | uses: actions/setup-python@v5
17 | with:
18 | python-version: 3.12
19 | - name: Install dependencies
20 | run: |
21 | python -m pip install --upgrade pip
22 | pip install -r requirements.txt
23 | - name: Install ChromeDriver
24 | run: |
25 | CHROME_VERSION=$(google-chrome --version | cut -f 3 -d ' ' | cut -d '.' -f 1)
26 | echo "Chrome version is ${CHROME_VERSION}"
27 | DRIVER_URL=$(curl -s 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json' | jq -r --arg version $CHROME_VERSION '.versions[] | select(.version | startswith($version)) | .downloads.chromedriver[] | select(.platform == "linux64") | .url' | tail -1)
28 | curl --silent --show-error --location --fail --retry 3 --output /tmp/chromedriver_linux64.zip $DRIVER_URL
29 | cd /tmp
30 | unzip chromedriver_linux64.zip
31 | rm -rf chromedriver_linux64.zip
32 | sudo mv chromedriver-linux64/chromedriver /usr/local/bin/chromedriver
33 | sudo chmod +x /usr/local/bin/chromedriver
34 | chromedriver --version
35 | - name: Auto Book
36 | env:
37 | SCHOOL_ID: ${{ secrets.SCHOOL_ID }}
38 | PASSWORD: ${{ secrets.PASSWORD }}
39 | SCKEY: ${{ secrets.SCKEY }}
40 | run: python main.py
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | venv
2 | chromedriver.exe
3 | chromedriver
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/HDU_AUTO_BOOK-public.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/deployment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
20 |
21 |
22 |
36 |
37 |
38 |
39 |
58 |
59 |
60 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # HDU Auto Book 杭州电子科技大学图书馆自动预约脚本
3 |
4 | *
“当知识织就的茧房温柔剥落,你在图书馆沉淀的岁月自会羽化成风,托起你向青空而去的锋利轨迹。” *
5 |
6 | > [!IMPORTANT]
7 | > 请遵守以下使用协议,若不同意此协议,请移步其它项目
8 | > 使用协议
9 | >
10 | > - 请合理使用此脚本,切勿占用公共资源,做出诸如预约但不去签到使用的行为。
11 | > - 本项目仅供学术交流使用,作者不对任何因使用本脚本造成的后果负责,包括但不仅限于由滥用脚本导致的封号,账号被锁定等。
12 | > - 本项目将停止维护并将被移除,当发生以下情况之一:
13 | > - 本项目被杭电图书馆或校方要求删除
14 | > - 作者发现本项目影响到了杭电图书馆正常的预约服务
15 | > - 作者发现本项目被滥用或有其他不妥之处
16 | > - 当本项目被移除后,请各位使用者自觉停止使用fork的代码,以免造成不必要的麻烦。
17 | >
18 |
19 |
20 | > [!NOTE]
21 | > 本脚本于`2025.4.4`进行了最后一次更新(新增自习室楼层),但本项目应该可以在很长一段时间内可以正常工作,大家可以自行配置下试试是否可用,若有人愿意接手本项目,可以邮件联系我。
22 |
23 | - [配置](#配置)
24 | - [设置预约时间](#设置预约时间设置预约时间)
25 | - [启用脚本](#启用脚本)
26 |
27 | ## 视频教学
28 |
29 | [https://github.com/HaleyCH/HDU_AUTO_BOOK-public/blob/V2/docs/tutorial.mp4](https://github.com/user-attachments/assets/03444438-6150-4b71-8f39-7d5f4eda3992)
30 |
31 | ## 配置
32 | 1. fork本仓库
33 | 2. 点击仓库中的 `setting` 标签, 选中 `Secrets` 选项卡
34 | 3. 点击 `New repository secret` 按钮,新建环境变量。
35 |
36 | | 环境变量名| 说明 | 用途 |
37 | |:----------|:------------------------------------------------|:--------------|
38 | | `SCHOOL_ID` | 你的学号 | 用于登录[杭电智慧图书馆](https://hdu.huitu.zhishulib.com/) |
39 | | `PASSWORD` | 杭电智慧图书馆密码 | 用于登录[杭电智慧图书馆](https://hdu.huitu.zhishulib.com/) |
40 | | `SCKEY`(选填) | 微信推送服务 | 详见 [Sever酱](https://sct.ftqq.com/) 配置微信推送打卡结果 |
41 |
42 | 
43 |
44 | ## 设置预约时间
45 | 1. 点开 `user_config.yml` 文件
46 | 2. 修改其中日期所对应的设置。
47 |
48 | | 名称 | 值 | 功能 | 示范 |
49 | |---------|----------------------------------|-----------|--------------------------|
50 | | `启用` | `true`/`false` | 是否在该天预定座位 | 启用: true |
51 | | `name` | `二楼东`/`二楼西`/`四楼`/`三楼大厅`/`自定义` | 预定座位位置 | name:二楼自习室 |
52 | | `开始时间` | 24小时对应数字 | 使用座位开始时间 | 开始时间: 14 (代表预约下午两点开始的座位) |
53 | | `持续小时数` | 数字 | 使用座位小时数 | 持续小时数: 2 |
54 | | `自定义` | yaml格式列表,内容为你喜欢的座位号(需设置type:自定义) | 使用自定义座位号 | 自定义:
-10001 |
55 |
56 | 3. 保存文件并点击 `Commit changes` 按钮
57 |
58 | 
59 |
60 | ## 启用脚本
61 | 1. 点击仓库中的 `Actions` 标签
62 | 2. 点击 `I understand my workflows, go ahead and enable them` 按钮
63 |
64 | 
65 |
66 | ### 完成后它将于每天晚上 19:57 与 20:57 自动运行,开始预约。你也可以在 `Actions` 标签中手动运行。
67 |
68 | ### ❗Notice:由于GitHub不能保证cron完全准时,故该项目不适合用作抢座脚本。
69 |
70 | # 致谢
71 | - [杭州电子科技大学健康打卡脚本](https://github.com/YeQiuO/HDU_AUTO_PUNCH)
72 |
73 | # 写在最后
74 | 这个简单的脚本伴我度过了在图书馆的日日夜夜,而如今终于抵达告别的码头。本来只是为了我能抢到座位而写的脚本,没想到在这几年间也多多少少帮了一些同学的忙,对此我深表荣幸。如今我即将离开校园,无法再更新本项目,对此本人感到非常抱歉。也在此祝愿各位热爱在图书馆学习的同学,*当知识织就的茧房温柔剥落,你在图书馆沉淀的岁月自会羽化成风,托起你向青空而去的锋利轨迹。*
--------------------------------------------------------------------------------
/config/basic_config.yml:
--------------------------------------------------------------------------------
1 | SeatAutoBooker:
2 | timezone: 8
3 | target: https://hdu.huitu.zhishulib.com/Seat/Index/bookSeats?LAB_JSON=1
4 | start-time: 1970-01-01 08:00:00
5 | headers:
6 | Host: hdu.huitu.zhishulib.com
7 | Accept: application/json, text/plain, */*
8 | User-Agent: Mozilla/5.0 (Linux; Android 11; SM-G9910 Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.99 XWEB/4375 MMWEBSDK/20221012 Mobile Safari/537.36 MMWEBID/5551 MicroMessenger/8.0.30.2260(0x28001E55) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 miniProgram/wx5e72fc4ec15ca4e8
9 | Content-Type: application/x-www-form-urlencoded;charset=UTF-8
10 | Origin: https://hdu.huitu.zhishulib.com
11 | X-Requested-With: com.tencent.mm
12 | Sec-Fetch-Site: same-origin
13 | Sec-Fetch-Mode: cors
14 | Sec-Fetch-Dest: empty
15 | Referer: https://hdu.huitu.zhishulib.com/content/index/startUp/openid/okOhQt-6kvozwqD9JhY_A4_8nXEQ/
16 | Accept-Encoding: gzip, deflate
17 | Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
18 | Connection: close
19 | cron-delta-minutes: 5
20 | max-retry: 20
--------------------------------------------------------------------------------
/config/seat_config.yml:
--------------------------------------------------------------------------------
1 | # 如果你不知道这是什么,请不要修改这个文件
2 |
3 | # 二楼东/二楼西/四楼/三楼大厅/自定义
4 | 二楼东:
5 | begin: 10002
6 | end: 10469
7 | type: 自习室
8 | 二楼西:
9 | begin: 60710
10 | end: 61129
11 | type: 自习室
12 | 四楼:
13 | begin: 28756
14 | end: 29231
15 | type: 自习室
16 | 三楼大厅:
17 | begin: 58841
18 | end: 58899
19 | type: 自习室
20 | 十二楼:
21 | begin: 60212
22 | end: 60656
23 | type: 自习室
24 | 守正书院:
25 | begin: 42710
26 | end: 42842
27 | type: 生活区
28 | 求新书院:
29 | begin: 42887
30 | end: 42978
31 | type: 生活区
32 | 自定义:
33 | begin: 0
34 | end: 0
35 | type: 阅览室
36 |
--------------------------------------------------------------------------------
/docs/img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/img1.png
--------------------------------------------------------------------------------
/docs/img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/img2.png
--------------------------------------------------------------------------------
/docs/img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/img3.png
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/logo.png
--------------------------------------------------------------------------------
/docs/tutorial.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/tutorial.mov
--------------------------------------------------------------------------------
/docs/tutorial.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZRui-C/HDU_AUTO_BOOK-public/780d47bea74f13a420ee22818ccac2e16bf2feeb/docs/tutorial.mp4
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import yaml
3 | import random
4 | from datetime import datetime, timedelta
5 | import json
6 | import os
7 | import logging
8 |
9 | from selenium import webdriver
10 | from selenium.webdriver.chrome.options import Options
11 | from selenium.webdriver.chrome.service import Service
12 | from selenium.webdriver.common.by import By
13 | from selenium.webdriver.support import expected_conditions as EC
14 | from selenium.webdriver.support.wait import WebDriverWait
15 | import time
16 |
17 |
18 | logging.basicConfig(
19 | format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
20 | datefmt='%H:%M:%S',
21 | level=logging.DEBUG)
22 |
23 | time_zone = 8 # 时区
24 |
25 | # 两天后日期
26 |
27 | def get_seats_with_config(user_config, date_config, seat_config):
28 | # 二楼东/二楼西/四楼/三楼大厅/守正书院/求新书院/自定义
29 | seat_name = date_config['name']
30 | if seat_name == "自定义":
31 | return user_config['自定义']
32 | return list(range(seat_config[seat_name]['begin'], seat_config[seat_name]['end']))
33 |
34 |
35 | class SeatAutoBooker:
36 | def __init__(self, booker_config):
37 | self.json = None
38 | self.resp = None
39 | self.user_data = None
40 |
41 | logging.info('Creating SeatAutoBooker object')
42 |
43 | self.un = os.environ["SCHOOL_ID"].strip() # 学号
44 | print("使用用户:{}".format(self.un))
45 | self.pd = os.environ["PASSWORD"].strip() # 密码
46 | self.SCKey = None
47 | try:
48 | self.SCKey = os.environ["SCKEY"]
49 | except KeyError:
50 | print("没有Server酱的key,将不会推送消息")
51 |
52 | chrome_options = Options()
53 | chrome_options.add_argument('--headless')
54 | chrome_options.add_argument('--no-sandbox')
55 | chrome_options.add_argument('--disable-dev-shm-usage')
56 | self.driver = webdriver.Chrome(service=Service('/usr/local/bin/chromedriver'), options=chrome_options)
57 | self.wait = WebDriverWait(self.driver, 10, 0.5)
58 | self.cookie = None
59 |
60 | self.cfg = booker_config
61 |
62 | def book_favorite_seat(self, user_config, seat_config):
63 | #判断是否到了预约时间
64 | # 阅览室晚上9点开始预约,自习室晚上8点半开始预约
65 | the_day_after_tomorrow = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'][(datetime.now().weekday() + 2) % 7]
66 | seat_type = seat_config[user_config[the_day_after_tomorrow]['name']]["type"]
67 | if seat_type == "自习室":
68 | start_time = datetime.now().replace(hour=20-time_zone, minute=0, second=0, microsecond=0)
69 | end_time = datetime.now().replace(hour=20-time_zone, minute=15, second=0, microsecond=0)
70 | else:
71 | start_time = datetime.now().replace(hour=21-time_zone, minute=0, second=0, microsecond=0)
72 | end_time = datetime.now().replace(hour=21-time_zone, minute=15, second=0, microsecond=0)
73 | start_time = start_time - timedelta(minutes=self.cfg["cron-delta-minutes"])
74 | if datetime.now() < start_time or datetime.now() > end_time:
75 | return -1, "未到预约时间"
76 | logging.info('Booking favorite seat')
77 | retry_sleep_time = timedelta(minutes=self.cfg["cron-delta-minutes"]).seconds*2/(self.cfg["max-retry"]-2) - 10
78 | for tried_times in range(self.cfg["max-retry"]):
79 | try:
80 | return self._book_favorite_seat(user_config, seat_config, tried_times)
81 | except Exception as e:
82 | logging.exception(e)
83 | print(e.__class__, "尝试第{}次".format(tried_times))
84 | time.sleep(retry_sleep_time)
85 |
86 | def _book_favorite_seat(self, user_config, seat_config, tried_times=0):
87 | logging.info('Entering _book_favorite_seat method')
88 | the_day_after_tomorrow = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'][(datetime.now().weekday() + 2) % 7]
89 | date_config = user_config[the_day_after_tomorrow]
90 | seats = get_seats_with_config(user_config, date_config, seat_config)
91 | today_0_clock = datetime.strptime(datetime.now().strftime("%Y-%m-%d 00:00:00"), "%Y-%m-%d %H:%M:%S")
92 | book_time = today_0_clock + timedelta(days=2) + timedelta(hours=date_config['开始时间'])
93 | delta = book_time - self.cfg["start-time"]
94 | total_seconds = delta.days * 24 * 3600 + delta.seconds
95 | if date_config['name'] == '自定义' and tried_times