1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! 跟登录有关的请求,只实现了扫码登录
//!
//! 登录流程:先请求一个二维码,然后提示给用户,然后发起 [`CheckQrLogin`] 的轮询

use crate::requests::prelude::*;

/// 发起一次二维码登录请求
#[derive(Debug, Deserialize)]
pub struct QrLoginRequest {
    /// 二维码内容 url
    pub url: String,

    /// 扫码登录秘钥
    #[serde(rename = "oauthKey")]
    pub oauth_key: String,
}

impl Request for QrLoginRequest {
    type Args = ();
    fn request(client: &Client, _args: ()) -> RequestResponse<Self> {
        const URL: &str = "https://passport.bilibili.com/qrcode/getLoginUrl";
        let request = client.get(URL).send();

        Box::pin(async move { request.await?.bili_data().await })
    }
}

/// 检查二维码登录结果,需要轮询
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum CheckQrLogin {
    /// -1:密钥错误
    /// -2:密钥超时
    /// -4:未扫描
    /// -5:未确认
    Code(i32),
    Success {
        url: String,
    },
}

impl CheckQrLogin {
    pub fn is_success(&self) -> Option<bool> {
        match self {
            // 错误或者超时,不可重试
            CheckQrLogin::Code(-1 | -2) => Some(false),
            // 可以重试
            CheckQrLogin::Code(-4 | -5) => None,
            CheckQrLogin::Code(code) => {
                warn!("Unknown check qr login code = {}", code);
                Some(false)
            }
            Self::Success { .. } => Some(true),
        }
    }
}

impl Request for CheckQrLogin {
    // 扫码登录秘钥
    type Args = String;

    fn request(client: &Client, oauth_key: String) -> RequestResponse<Self> {
        const URL: &str = "https://passport.bilibili.com/qrcode/getLoginInfo";
        let request = client.post(URL).form(&[("oauthKey", oauth_key)]).send();

        /// 这玩意儿跟一般的返回结果不一样,特殊处理
        #[derive(Debug, Deserialize)]
        struct Response {
            status: bool,
            data: CheckQrLogin,
            #[serde(default)]
            message: String,
        }

        Box::pin(async move {
            let response: Response = request.await?.json().await?;
            Ok(response.data)
        })
    }
}

#[tokio::test]
async fn test_get_qr_login_request() -> Result<()> {
    let client = crate::connection::new_client()?;
    let r = QrLoginRequest::request(&client, ()).await?;
    dbg!(r);
    Ok(())
}