Skip to content

Rust

docs.rs/lnu-elytra

crates.io/lnu-elytra

安装Rust工具链 安装 Rust

sh
git clone https://github.com/mcitem/lnuElytra
cd examples
# 修改 examples/src/bin/best中的教学班、账号密码
cargo run --bin best

异步API(推荐使用)

安装

sh
cargo add tokio -F macros,rt-multi-thread
cargo add lnu-elytra

使用

rs
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = lnu_elytra::Client::new();
    client.login("账号", "密码").await?;
    client.init().await?;

    // 教学班示例:(2025-2026-2)-77101504-02
    // 使用精确的教学班查询能能减少教务系统返回的数据量,有利于加快抢课。
    let course = client.fetch_courses("教学班").await?;
    // 只有当使用精确教学班查询时,才适合直接调用 try_select_o
    course.try_select_0(&client).await?;
    Ok(())
}

最佳实践

在抢课开始前提前10分钟就登入系统,并等到抢课时间开始时在手动控制脚本继续运行(也可以通过断点调试、Cookie登录实现

示例:通过stdin控制脚本继续

rs
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = lnu_elytra::Client::new();
    client.login("账号", "密码").await?;

    println!("登录成功,按回车键继续...");
    std::io::stdin().read_line(&mut String::new())?;
    println!("正在初始化...");

    client.init().await?;
    let course = client.fetch_courses("教学班").await?;
    course.try_select_0(&client).await?;
    Ok(())
}

完整示例(可直接使用)

rs
use std::sync::Arc;

use lnu_elytra::{Client, Error, SelectCourseResponse};
use tracing_subscriber::{filter::LevelFilter, fmt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    fmt().with_max_level(LevelFilter::INFO).init();

    // 此示例非常适合直接用于单人抢课使用,含有基本的重试,多任务并行功能

    let mut client = Client::new();

    // 在 广州商学院 抢课(更换教务系统地址
    // let mut client = Client::new_with_base("http://jwxt.gcc.edu.cn".try_into()?);

    client.login("账号", "密码").await?;

    println!("登录成功,按回车键继续...");

    // 如果需要手动控制,取消注释此行
    // std::io::stdin().read_line(&mut String::new())?;

    println!("正在初始化...");

    // 登录成功后,永远不要引发任何的panic或直接上抛Result Err,否则会导致所有直接退出,无法继续抢课

    loop {
        if let Ok(()) = client.init().await {
            println!("初始化成功,正在准备选课...");
            break;
        };
        println!("初始化失败,正在重试...");
    }

    // 教学班列表
    // 并行运行多个选课任务,但可能会随机选到其中一个
    let tgs = vec!["教学班1", "教学班2", "教学班3"];

    let mut hds = Vec::new();

    // 使用std::sync::Arc来共享Client实例,以便在多个任务中使用

    let c = Arc::new(client);

    for i in tgs {
        hds.push(tokio::spawn(fetch(i, c.clone())));
    }

    for i in hds {
        if let Ok(status) = i.await {
            println!("选课结果: {:?}", status);
        }
    }

    println!("进程已结束,按回车键退出...");
    std::io::stdin().read_line(&mut String::new())?;

    Ok(())
}

async fn fetch(i: &str, client: Arc<Client>) -> Result<SelectCourseResponse, Error> {
    println!("正在选课 {}...", i);
    loop {
        if let Ok(course) = client.fetch_courses(i).await
            && let Ok(status) = course.try_select_0(&client).await
        {
            println!("选课结果 {}: {:?}", i, status);

            if let Some(msg) = status.msg()
                && (msg.contains("未开放") || msg.contains("频率过高"))
            {
                continue;
            }

            if status.is_success() {
                println!("选课成功 {}!", i);
            }

            return Ok(status);
        };
    }
}

阻塞API

不要在tokio的异步环境中使用阻塞客户端,这会引起panic

Cannot start a runtime from within a runtime. This happens because a function (like block_on) attempted to block the current thread while the thread is being used to drive asynchronous tasks.

安装

需要启用 blocking Feature

sh
cargo add lnu-elytra -F blocking

使用

rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = lnu_elytra::blocking::Client::new();
    client.login("账号", "密码")?;
    client.init()?;
    let course = client.fetch_course("教学班")?;
    course.try_select_0_blocking(&client)?;
    Ok(())
}

AGPL-3.0 License