Skip to main content

u_sdk/server_chan/
mod.rs

1//! Server chan 3 sdk
2
3use bon::{Builder, bon};
4use reqwest::StatusCode;
5use serde::Serialize;
6
7#[derive(thiserror::Error, Debug)]
8pub enum Error {
9    #[error("request failed: code: {code}\nbody: {body}")]
10    API { code: StatusCode, body: String },
11    #[error("use reqwest error:\n {0}")]
12    Reqwest(#[from] reqwest::Error),
13}
14
15#[derive(Builder, Serialize)]
16pub struct SendMsg<'a> {
17    #[builder(start_fn)]
18    #[serde(skip_serializing)]
19    client: &'a Client,
20    /// 标签列表,多个标签使用竖线`|`分隔
21    // 注意这个#[builder(filed)字段有顺序要求,需要放在start_fn之后,finish_fn之前
22    #[builder(field)]
23    #[serde(
24        serialize_with = "serialize_tags",
25        skip_serializing_if = "Vec::is_empty"
26    )]
27    tags: Vec<&'a str>,
28    /// 推送的标题
29    title: &'a str,
30    #[serde(rename = "desp", skip_serializing_if = "Option::is_none")]
31    /// 推送的正文内容,则为必填,支持markdown
32    description: Option<&'a str>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    /// 推送消息的简短描述,用于指定消息卡片的内容部分,尤其是在推送markdown的时候
35    short: Option<&'a str>,
36}
37
38fn serialize_tags<S>(tags: &[&str], serializer: S) -> Result<S::Ok, S::Error>
39where
40    S: serde::Serializer,
41{
42    let tags_str = tags.join("|");
43    serializer.serialize_str(&tags_str)
44}
45
46impl<'a, S: send_msg_builder::State> SendMsgBuilder<'a, S> {
47    pub fn tag(mut self, tag: &'a str) -> Self {
48        self.tags.push(tag);
49        self
50    }
51
52    pub fn tags(mut self, tags: impl IntoIterator<Item = &'a str>) -> Self {
53        self.tags.extend(tags);
54        self
55    }
56}
57
58/// 使用Server酱3
59pub struct Client {
60    url: String,
61    http_client: reqwest::Client,
62}
63
64#[bon]
65impl Client {
66    #[builder]
67    pub fn new(uid: i32, key: &str) -> Self {
68        Self {
69            url: format!("https://{}.push.ft07.com/send/{}.send", uid, key),
70            http_client: reqwest::Client::new(),
71        }
72    }
73
74    pub fn send_msg(&self) -> SendMsgBuilder<'_> {
75        SendMsg::builder(self)
76    }
77}
78
79impl SendMsg<'_> {
80    pub async fn send(&self) -> Result<(), Error> {
81        let client = self.client;
82        let resp = client
83            .http_client
84            .post(&client.url)
85            .json(self)
86            .send()
87            .await?;
88        if !resp.status().is_success() {
89            return Err(Error::API {
90                code: resp.status(),
91                body: resp.text().await.unwrap_or_default(),
92            });
93        }
94
95        Ok(())
96    }
97}