├── .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 | 70 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | ![logo](./docs/logo.png) 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 | ![示范](docs/img1.png) 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 | ![示范](docs/img2.png) 59 | 60 | ## 启用脚本 61 | 1. 点击仓库中的 `Actions` 标签 62 | 2. 点击 `I understand my workflows, go ahead and enable them` 按钮 63 | 64 | ![示范](docs/img3.png) 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