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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! 实现 mirai 提供的 API 接口,如拉取群列表等
//!
pub mod common;
pub mod friend_list;
pub mod group_list;
pub mod member_list;
pub mod message_from_id;
pub mod recall;
pub mod send_friend_message;
pub mod send_group_message;

// 私有 mod,用 approve 方法隐藏实现
#[allow(non_snake_case)]
pub(crate) mod resp_botInvitedJoinGroupRequestEvent;

use serde::Serialize;

use crate::Result;

/// 所有发往 mirai 的请求都实现这个 trait
pub trait Api: ApiRequest {
    /// 请求返回的类型
    type Response: serde::de::DeserializeOwned;

    fn process_response(value: serde_json::Value) -> Result<Self::Response>;
}

/// 对应将请求序列化成 ws packet 的行为
pub trait ApiRequest: Send {
    fn command(&self) -> &'static str;

    fn sub_command(&self) -> Option<&'static str>;

    fn encode(&self, sync_id: i64) -> String;
}

#[derive(Serialize)]
struct ApiRequestData<T: Serialize> {
    #[serde(rename = "syncId")]
    sync_id: i64,

    command: &'static str,

    #[serde(rename = "subCommand")]
    sub_command: Option<&'static str>,

    content: T,
}

#[macro_export]
macro_rules! api {
    (
        command = $cmd:literal,
        $req:path,
        $rsp:path
    ) => {
        $crate::api!(
            command = $cmd,
            subcommand = None,
            field = "data",
            $req,
            $rsp
        );
    };
    (
        command = $cmd:literal,
        subcommand = $sub_cmd:expr,
        field = $field:tt,
        $req:path,
        $rsp:path
    ) => {
        impl $crate::api::ApiRequest for $req {
            fn command(&self) -> &'static str {
                $cmd
            }
            fn sub_command(&self) -> Option<&'static str> {
                $sub_cmd
            }
            fn encode(&self, sync_id: i64) -> String {
                let request = $crate::api::ApiRequestData {
                    command: self.command(),
                    sub_command: self.sub_command(),
                    sync_id,
                    content: &self,
                };
                serde_json::to_string(&request).unwrap()
            }
        }
        // 定义返回的类型
        crate::api!(@def_resp field = $field);

        impl $crate::api::Api for $req {
            type Response = $rsp;
            fn process_response(value: serde_json::Value) -> $crate::Result<Self::Response> {
                trace!("process value {:?} as response", value);
                let resp: ApiResponseData::<$rsp> = serde_json::from_value(value)?;
                if resp.code != 0 {
                    return Err($crate::Error::Request {
                        code: resp.code,
                        msg: resp.msg,
                    });
                }
                Ok(resp.data)
            }
        }
    };
    (@def_resp field = "data") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            data: T,
        }
    };
    (@def_resp field = "flatten") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            #[serde(flatten)]
            data: T,
        }
    };
    (@def_resp field = "default") => {
        #[derive(Deserialize)]
        struct ApiResponseData<T> {
            code: i32,
            msg: String,
            #[serde(default)]
            data: T,
        }
    }
}