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
    }
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。