Allen_Jeo

Allen_Jeo

重邮课程表导入日历

虽然现在重邮课表查看可以通过掌上重邮 app 和 We 重邮微信小程序,但是这两个应用都不能有课程的时候提前通知。因此不如通过这两个应用的接口提供课表数据,然后转化为通用的 iCalendar格式导入日历。就可以体验到系统级的日历通知了。

此前已经有一个 Python 版本的 CQUPT-ics 是重邮的学长写的,但是目前这两个数据源的接口都已经发生改变,已经无法正常使用。

因此我就打算写一个新的项目来实现上述目标。按照先前学长项目的思路来一个重写 RIIR (Rewrite It In Rust)

新屎山地址:https://github.com/jizizr/cqupt-ics-rs 项目源码在这里

使用地址:https://cqupt.op.wiki/ 要用这个项目点这里

image

基本实现了先前的 Python 版本的功能,此外,也实现了调休的自动课程调节。避免了放假时候还收到课程的通知,和忘记今天调休要上课(重邮官方课表是不会根据放假去改的,需要自己手动查看调休安排照着去看)

当然,纯靠凭拼接订阅链接还是不够友好,当然要来一个前端的使用界面(AI 搓的),应该可以友好地使用本项目了。学校晚上会关闭课表查询,因此 大概 0-7 点查不了课表,属于正常现象

PS: 一键导入的实现只实现了 Android 和 apple 系列系统。其他系统自行在支持 ics 订阅的日历里复制链接添加

本来是打算使用 webcal:// 跳转日历添加,理论上 webcal:// 协议头跳转基本系统都实现了,但是在 apple (macos ios) 之外其他系统由于实际提供日历的软件不同,具体有着极大的差异,而且基本会出现奇奇怪怪的问题。因此只提供给了 apple 使用,然后 Android 使用 intent:// 去跳转系统日历。因此只实现了 apple 系列系统和 Android。

如果有更好的实现欢迎提 PR,有 bug 欢迎提 issue。

最后来看一下导入的效果图

image

在国庆节(休)去除了所有的课,在国庆节(班)的有对应调休的课。

Screenshot_2025-10-20-19-09-38-072_com.google.android.calendar-edit

手机上的效果

要增加新的 provider 源只要实现一个 trait 就行(非常简单

#[async_trait]
pub trait Provider: Send + Sync {
    /// Token type for this provider
    type Token: Send + Sync + Serialize + DeserializeOwned;
    type ContextType: Send + Sync;
    /// Provider 的名字
    fn name(&self) -> &str;

    /// Provider 的描述
    fn description(&self) -> &str;

    /// Get timezone for this provider
    ///
    /// Returns the timezone used by this provider for time calculations.
    /// This is used to ensure consistent timezone handling across all
    /// provider operations.
    /// provider 的时区
    fn timezone(&self) -> FixedOffset;

    /// Authenticate and get token
    /// 获取 token 的方法
    async fn authenticate<'a, 'b>(
        &'a self,
        context: ParamContext<'b, Self::ContextType>,
        request: &CourseRequest,
    ) -> Result<Self::Token>;

    /// Validate existing token
    /// 验证 token 是否有效
    async fn validate_token(&self, token: &Self::Token) -> Result<bool>;

    /// Refresh token
    /// 刷新 token 的方法(如果 provider 不存在则直接返回 Err 即可)
    async fn refresh_token(&self, token: &Self::Token) -> Result<Self::Token>;

    /// Get courses using token
    /// request.semester should be Some before calling this method
    /// If use crate::providers::Wrapper, it will ensure semester is Some
    /// 获取课程数据的方法
    async fn get_courses<'a, 'b>(
        &'a self,
        context: ParamContext<'b, Self::ContextType>,
        request: &mut CourseRequest,
        token: &Self::Token,
    ) -> Result<CourseResponse>;

    /// Get semester start date
    /// This is called if request.semester is None before get_courses
    /// If you use crate::providers::Wrapper, it will call this method automatically if request.semester is None
    /// You can use the context to store intermediate data if needed
    /// 获取学期开始日期的方法
    async fn get_semester_start<'a, 'b>(
        &'a self,
        context: ParamContext<'b, Self::ContextType>,
        request: &mut CourseRequest,
        token: &Self::Token,
    ) -> Result<chrono::DateTime<FixedOffset>>;

    /// Token TTL
    /// 返回 token 的有效期,用于控制缓存
    fn token_ttl(&self) -> Duration {
        Duration::from_secs(3600 * 24) // 24 hours default
    }
}
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。