wx-sdk 0.0.9

A Wechat SDK written in Rust
Documentation
//! Material Module (永久)素材文件模块
use crate::SdkResult;
use crate::{
    error::{CommonError, CommonResponse},
    wechat::WxApiRequestBuilder,
};
use serde::{Deserialize, Serialize};

use super::media::MediaId;

#[derive(Serialize, Deserialize, Debug)]
pub struct MediaRes {
    #[serde(rename = "type")]
    pub type_: String,
    pub media_id: String,
    pub created_at: i64,
}

// #[derive(Serialize, Deserialize, Debug)]
// pub enum MediaType {
//     image,
//     voice,
//     video,
//     thumb,
// }

// pub type MediaId = String;

#[derive(Serialize, Deserialize)]
struct MediaUrl {
    pub url: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Material {
    Video(Video),
    NewsItemList(NewsItemList),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Video {
    pub title: String,
    pub description: String,
    pub down_url: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NewsItemList {
    pub news_item: Vec<NewsItem>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NewsItem {
    pub title: String,
    pub thumb_media_id: String,
    /// 是否显示封面,0为false,即不显示,1为true,即显示
    pub show_cover_pic: i8,
    pub author: String,
    pub digest: String,
    pub content: String,
    pub url: String,
    pub content_source_url: String,
    #[serde(default)]
    pub thumb_url: String,
    #[serde(default)]
    pub need_open_comment: i8,
    #[serde(default)]
    pub only_fans_can_comment: i8,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Articles {
    pub title: String,
    pub thumb_media_id: String,
    pub author: String,
    pub digest: String,
    /// 是否显示封面,0为false,即不显示,1为true,即显示
    pub show_cover_pic: i8,
    pub content: String,
    pub content_source_url: String,
    pub need_open_comment: Option<i8>,
    pub only_fans_can_comment: Option<i8>,
}

#[derive(Debug, Serialize)]
pub struct VideoDesc {
    pub title: String,
    pub introduction: String,
}

pub struct FileStruct {
    pub filetype: String,
    pub file: Vec<u8>,
    pub filename: String,
    pub conten_type: String,
    pub video_desc: Option<VideoDesc>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AddMaterialRes {
    pub media_id: String,
    /// 新增的图片素材的图片URL(仅新增图片素材时会返回该字段)
    pub url: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateNews {
    pub media_id: String,
    /// 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义),第一篇为0
    pub index: i32,
    /// 在开发文档中这两个字段: `need_open_comment`, `only_fans_can_comment` 在更新时是没有提到的
    pub articles: Articles,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MaterialCount {
    pub voice_count: i32,
    pub video_count: i32,
    pub image_count: i32,
    pub news_count: i32,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Batch {
    #[serde(rename = "type")]
    pub type_: String,
    /// 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回
    pub offset: i32,
    /// 返回素材的数量,取值在1到20之间
    pub count: u8,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MaterialList {
    pub total_count: u32,
    pub item_count: u32,
    pub item: MaterialItemList,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum MaterialItemList {
    MediaInfo(Vec<MediaInfo>),
    News(Vec<NewsInfo>),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct NewsInfo {
    pub media_id: String,
    pub content: NewsItemList,
    pub update_time: i64,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MediaInfo {
    pub media_id: String,
    pub name: String,
    pub update_time: i64,
    pub url: Option<String>,
    pub description: Option<String>,
    pub tags: Option<Vec<String>>,
}
/// Material Module (永久)素材文件模块
pub struct MaterialModule<'a, T: WxApiRequestBuilder>(pub(crate) &'a T);
impl<'a, T: WxApiRequestBuilder> MaterialModule<'a, T> {
    /// 新增图文素材
    pub async fn add_news(&self, articles: &[Articles]) -> SdkResult<MediaId> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/add_news";
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;
        let res: CommonResponse<MediaId> = builder
            .json(&serde_json::json!({ "articles": articles }))
            .send()
            .await?
            .json()
            .await?;
        res.into()
    }

    /// 新增其他类型永久素材
    pub async fn add_material(&self, file: FileStruct) -> SdkResult<AddMaterialRes> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/add_material";
        let part = reqwest::multipart::Part::bytes(file.file)
            .file_name(file.filename)
            .mime_str(file.conten_type.as_ref());

        let mut form = reqwest::multipart::Form::new().part("media", part.unwrap());

        if file.filetype == "video" {
            form = form.text(
                "description",
                serde_json::to_string(&file.video_desc).unwrap(),
            );
        }
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;
        let builder = builder.query(&[("type", file.filetype)]);
        let res: CommonResponse<AddMaterialRes> =
            builder.multipart(form).send().await?.json().await?;
        res.into()
    }

    /// 获取永久素材
    pub async fn get_material(&self, media_id: &str) -> SdkResult<Material> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;
        let res: CommonResponse<Material> = builder
            .json(&serde_json::json!({ "media_id": media_id }))
            .send()
            .await?
            .json()
            .await?;

        res.into()
    }

    /// 获取除图文、视频外其它类型永久素材
    pub async fn get_material_other(&self, media_id: &str) -> SdkResult<Vec<u8>> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/get_material";
        let sdk = self.0;
        let res = sdk
            .wx_post(base_url)
            .await?
            .json(&serde_json::json!({ "media_id": media_id }))
            .send()
            .await?
            .bytes()
            .await?;

        Ok(res.to_vec())
    }

    /// 删除永久素材
    pub async fn del_material(&self, media_id: &str) -> SdkResult<()> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/del_material";
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;
        let res: CommonError = builder
            .json(&serde_json::json!({ "media_id": media_id }))
            .send()
            .await?
            .json()
            .await?;

        res.into()
    }

    /// 修改永久图文素材
    pub async fn update_news(&self, news: &UpdateNews) -> SdkResult<()> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/update_news";
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;

        let res: CommonError = builder.json(news).send().await?.json().await?;

        res.into()
    }

    /// 获取各类型素材文件数量
    pub async fn get_materialcount(&self) -> SdkResult<MaterialCount> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount";
        let sdk = self.0;
        let res: CommonResponse<MaterialCount> =
            sdk.wx_get(base_url).await?.send().await?.json().await?;
        res.into()
    }

    /// 批量获取素材
    pub async fn batchget_material(&self, batch: &Batch) -> SdkResult<MaterialList> {
        let base_url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material";
        let sdk = self.0;
        let builder = sdk.wx_post(base_url).await?;
        let res: CommonResponse<MaterialList> = builder.json(batch).send().await?.json().await?;

        res.into()
    }
}