forked from golang/hotime
355 lines
9.3 KiB
Go
355 lines
9.3 KiB
Go
|
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
|
|||
|
}
|