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
use reqwest;
use serde::{Deserialize, Serialize};
use std::error::Error;

#[derive(Debug, Clone)]
pub struct WecomAgent {
    corpid: String,
    secret: String,
    access_token: Option<String>,
    client: reqwest::Client,
}

#[derive(Deserialize)]
struct AccessTokenResponse {
    errcode: i64,
    errmsg: String,
    access_token: String,
    expires_in: usize,
}

// 应用消息发送结果
#[derive(Deserialize)]
pub struct MsgSendResponse {
    errcode: i64,
    errmsg: String,
    invaliduser: Option<String>,
    invalidparty: Option<String>,
    invalidtag: Option<String>,
    unlicenseduser: Option<String>,
    msgid: String,
    response_code: Option<String>,
}

impl MsgSendResponse {
    pub fn is_error(&self) -> bool {
        self.errcode != 0
    }

    pub fn error_code(&self) -> i64 {
        self.errcode
    }

    pub fn error_msg(&self) -> &str {
        &self.errmsg
    }
}

// 文本消息
#[derive(Debug, Serialize, PartialEq)]
pub struct TextMsgContent {
    pub content: String,
}

// 文本消息结构体
#[derive(Debug, Serialize, PartialEq)]
pub struct TextMsg {
    pub touser: String,
    pub toparty: String,
    pub totag: String,
    pub msgtype: String,
    pub agentid: usize,
    pub text: TextMsgContent,
    pub safe: i64,
    pub enable_id_trans: i64,
    pub enable_duplicate_check: i64,
    pub duplicate_check_interval: usize,
}

impl WecomAgent {
    /// 创建一个Agent。注意默认access_token为空,需要更新后使用。
    pub fn new(corpid: &str, secret: &str) -> Self {
        Self {
            corpid: String::from(corpid),
            secret: String::from(secret),
            access_token: None,
            client: reqwest::Client::new(),
        }
    }

    /// 检查access_token是否初始化。
    pub fn token_is_some(&self) -> bool {
        self.access_token.is_some()
    }

    /// 更新access_token。
    pub async fn update_token(&mut self) -> Result<(), Box<dyn Error>> {
        let url = format!(
            "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={}&corpsecret={}",
            &self.corpid, &self.secret
        );
        let response = self
            .client
            .get(url)
            .send()
            .await?
            .json::<AccessTokenResponse>()
            .await?;
        if response.errcode != 0 {
            return Err(format!(
                "Failed to fetch access token. Error code: {}, {}",
                response.errcode, response.errmsg,
            )
            .into());
        }
        self.access_token = Some(response.access_token);
        Ok(())
    }

    /// 发送文本消息
    pub async fn send_text(&self, msg: &TextMsg) -> Result<MsgSendResponse, Box<dyn Error>> {
        if self.access_token.is_none() {
            return Err("Can not send message. Access token is None.".into());
        }
        let url = format!(
            "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={}",
            self.access_token.as_ref().unwrap()
        );
        let response = self
            .client
            .post(&url)
            .json(msg)
            .send()
            .await?
            .json::<MsgSendResponse>()
            .await?;
        Ok(response)
    }
}