package material import ( "encoding/json" "errors" "fmt" "github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/util" ) const ( addNewsURL = "https://api.weixin.qq.com/cgi-bin/material/add_news" updateNewsURL = "https://api.weixin.qq.com/cgi-bin/material/update_news" addMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/add_material" delMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/del_material" getMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/get_material" getMaterialCountURL = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount" batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material" ) // PermanentMaterialType 永久素材类型 type PermanentMaterialType string const ( // PermanentMaterialTypeImage 永久素材图片类型(image) PermanentMaterialTypeImage PermanentMaterialType = "image" // PermanentMaterialTypeVideo 永久素材视频类型(video) PermanentMaterialTypeVideo PermanentMaterialType = "video" // PermanentMaterialTypeVoice 永久素材语音类型 (voice) PermanentMaterialTypeVoice PermanentMaterialType = "voice" // PermanentMaterialTypeNews 永久素材图文类型(news) PermanentMaterialTypeNews PermanentMaterialType = "news" ) // Material 素材管理 type Material struct { *context.Context } // NewMaterial init func NewMaterial(context *context.Context) *Material { material := new(Material) material.Context = context return material } // Article 永久图文素材 type Article struct { Title string `json:"title"` ThumbMediaID string `json:"thumb_media_id"` ThumbURL string `json:"thumb_url"` Author string `json:"author"` Digest string `json:"digest"` ShowCoverPic int `json:"show_cover_pic"` Content string `json:"content"` ContentSourceURL string `json:"content_source_url"` URL string `json:"url"` DownURL string `json:"down_url"` } // GetNews 获取/下载永久素材 func (material *Material) GetNews(id string) ([]*Article, error) { accessToken, err := material.GetAccessToken() if err != nil { return nil, err } uri := fmt.Sprintf("%s?access_token=%s", getMaterialURL, accessToken) var req struct { MediaID string `json:"media_id"` } req.MediaID = id responseBytes, err := util.PostJSON(uri, req) if err != nil { return nil, err } var res struct { NewsItem []*Article `json:"news_item"` } err = json.Unmarshal(responseBytes, &res) if err != nil { return nil, err } return res.NewsItem, nil } // reqArticles 永久性图文素材请求信息 type reqArticles struct { Articles []*Article `json:"articles"` } // resArticles 永久性图文素材返回结果 type resArticles struct { util.CommonError MediaID string `json:"media_id"` } // AddNews 新增永久图文素材 func (material *Material) AddNews(articles []*Article) (mediaID string, err error) { req := &reqArticles{articles} var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", addNewsURL, accessToken) responseBytes, err := util.PostJSON(uri, req) if err != nil { return } var res resArticles err = json.Unmarshal(responseBytes, &res) if err != nil { return } if res.ErrCode != 0 { return "", fmt.Errorf("errcode=%d,errmsg=%s", res.ErrCode, res.ErrMsg) } mediaID = res.MediaID return } // reqUpdateArticle 更新永久性图文素材请求信息 type reqUpdateArticle struct { MediaID string `json:"media_id"` Index int64 `json:"index"` Articles *Article `json:"articles"` } // UpdateNews 更新永久图文素材 func (material *Material) UpdateNews(article *Article, mediaID string, index int64) (err error) { req := &reqUpdateArticle{mediaID, index, article} var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", updateNewsURL, accessToken) var response []byte response, err = util.PostJSON(uri, req) if err != nil { return } return util.DecodeWithCommonError(response, "UpdateNews") } // resAddMaterial 永久性素材上传返回的结果 type resAddMaterial struct { util.CommonError MediaID string `json:"media_id"` URL string `json:"url"` } // AddMaterial 上传永久性素材(处理视频需要单独上传) func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) { if mediaType == MediaTypeVideo { err = errors.New("永久视频素材上传使用 AddVideo 方法") return } var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s&type=%s", addMaterialURL, accessToken, mediaType) var response []byte response, err = util.PostFile("media", filename, uri) if err != nil { return } var resMaterial resAddMaterial err = json.Unmarshal(response, &resMaterial) if err != nil { return } if resMaterial.ErrCode != 0 { err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg) return } mediaID = resMaterial.MediaID url = resMaterial.URL return } type reqVideo struct { Title string `json:"title"` Introduction string `json:"introduction"` } // AddVideo 永久视频素材文件上传 func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) { var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s&type=video", addMaterialURL, accessToken) videoDesc := &reqVideo{ Title: title, Introduction: introduction, } var fieldValue []byte fieldValue, err = json.Marshal(videoDesc) if err != nil { return } fields := []util.MultipartFormField{ { IsFile: true, Fieldname: "media", Filename: filename, }, { IsFile: false, Fieldname: "description", Value: fieldValue, }, } var response []byte response, err = util.PostMultipartForm(fields, uri) if err != nil { return } var resMaterial resAddMaterial err = json.Unmarshal(response, &resMaterial) if err != nil { return } if resMaterial.ErrCode != 0 { err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg) return } mediaID = resMaterial.MediaID url = resMaterial.URL return } type reqDeleteMaterial struct { MediaID string `json:"media_id"` } // DeleteMaterial 删除永久素材 func (material *Material) DeleteMaterial(mediaID string) error { accessToken, err := material.GetAccessToken() if err != nil { return err } uri := fmt.Sprintf("%s?access_token=%s", delMaterialURL, accessToken) response, err := util.PostJSON(uri, reqDeleteMaterial{mediaID}) if err != nil { return err } return util.DecodeWithCommonError(response, "DeleteMaterial") } // ArticleList 永久素材列表 type ArticleList struct { util.CommonError TotalCount int64 `json:"total_count"` ItemCount int64 `json:"item_count"` Item []ArticleListItem `json:"item"` } // ArticleListItem 用于ArticleList的item节点 type ArticleListItem struct { MediaID string `json:"media_id"` Content ArticleListContent `json:"content"` Name string `json:"name"` URL string `json:"url"` UpdateTime int64 `json:"update_time"` } // ArticleListContent 用于ArticleListItem的content节点 type ArticleListContent struct { NewsItem []Article `json:"news_item"` UpdateTime int64 `json:"update_time"` CreateTime int64 `json:"create_time"` } // reqBatchGetMaterial BatchGetMaterial请求参数 type reqBatchGetMaterial struct { Type PermanentMaterialType `json:"type"` Count int64 `json:"count"` Offset int64 `json:"offset"` } // BatchGetMaterial 批量获取永久素材 //reference:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html func (material *Material) BatchGetMaterial(permanentMaterialType PermanentMaterialType, offset, count int64) (list ArticleList, err error) { var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", batchGetMaterialURL, accessToken) req := reqBatchGetMaterial{ Type: permanentMaterialType, Offset: offset, Count: count, } var response []byte response, err = util.PostJSON(uri, req) if err != nil { return } err = util.DecodeWithError(response, &list, "BatchGetMaterial") return } // ResMaterialCount 素材总数 type ResMaterialCount struct { util.CommonError VoiceCount int64 `json:"voice_count"` // 语音总数量 VideoCount int64 `json:"video_count"` // 视频总数量 ImageCount int64 `json:"image_count"` // 图片总数量 NewsCount int64 `json:"news_count"` // 图文总数量 } // GetMaterialCount 获取素材总数. func (material *Material) GetMaterialCount() (res ResMaterialCount, err error) { var accessToken string accessToken, err = material.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", getMaterialCountURL, accessToken) var response []byte response, err = util.HTTPGet(uri) if err != nil { return } err = util.DecodeWithError(response, &res, "GetMaterialCount") return }