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
|
||
}
|