Skip to content

Commit

Permalink
add: 增加公众号「发布能力」接口(silenceper#530) (silenceper#531)
Browse files Browse the repository at this point in the history
* add: 增加公众号「发布能力」接口(silenceper#530)

* fix: go lint

* fix: 修复响应结构体及解析方式

* add: 增加公众号「草稿箱」接口(silenceper#530)

* fix: text

* mod: 增加 发布任务完成 消息事件

Co-authored-by: luoyu <[email protected]>
  • Loading branch information
save95 and luoyu authored Feb 14, 2022
1 parent 80ce4ae commit 5c4b28a
Show file tree
Hide file tree
Showing 5 changed files with 545 additions and 0 deletions.
39 changes: 39 additions & 0 deletions doc/api/officialaccount.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@

#### 群发任务管理

[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/task-account/shopping-guide.addGuideMassendJob.html)

| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
Expand All @@ -156,6 +158,43 @@

## 素材管理

## 草稿箱

[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Add_draft.html)

| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------------- | -------- | ------------------------------------------------------------ | ---------- | ---------------------------- |
| 新建草稿 | POST | /cgi-bin/draft/add | YES | (draft *Draft) AddDraft |
| 获取草稿 | POST | /cgi-bin/draft/get | YES | (draft *Draft) GetDraft |
| 删除草稿 | POST | /cgi-bin/draft/delete | YES | (draft *Draft) DeleteDraft |
| 修改草稿 | POST | /cgi-bin/draft/update | YES | (draft *Draft) UpdateDraft |
| 获取草稿总数 | GET | /cgi-bin/draft/count | YES | (draft *Draft) CountDraft |
| 获取草稿列表 | POST | /cgi-bin/draft/batchget | YES | (draft *Draft) PaginateDraft |
| MP端开关(仅内测期间使用) | POST | /cgi-bin/draft/switch<br />/cgi-bin/draft/switch?checkonly=1 | NO | |



## 发布能力

[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Publish/Publish.html)

说明:「发表记录」包括群发和发布。

注意:该接口,只能处理 "发布" 相关的信息,无法操作和获取 "群发" 相关内容!![官方回复](https://developers.weixin.qq.com/community/develop/doc/0002a4fb2109d8f7a91d421c556c00)

- 群发:主动推送给粉丝,历史消息可看,被搜一搜收录,可以限定部分的粉丝接收到。
- 发布:不会主动推给粉丝,历史消息列表看不到,但是是公开给所有人的文章。也不会占用群发的次数。每天可以发布多篇内容。可以用于自动回复、自定义菜单、页面模板和话题中,发布成功时会生成一个永久链接。

| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------ | -------- | ------------------------------- | ---------- | --------------------------------------- |
| 发布接口 | POST | /cgi-bin/freepublish/submit | YES | (freePublish *FreePublish) Publish |
| 发布状态轮询接口 | POST | /cgi-bin/freepublish/get | YES | (freePublish *FreePublish) SelectStatus |
| 事件推送发布结果 | | | YES | EventPublishJobFinish |
| 删除发布 | POST | /cgi-bin/freepublish/delete | YES | (freePublish *FreePublish) Delete |
| 通过 article_id 获取已发布文章 | POST | /cgi-bin/freepublish/getarticle | YES | (freePublish *FreePublish) First |
| 获取成功发布列表 | POST | /cgi-bin/freepublish/batchget | YES | (freePublish *FreePublish) Paginate |


## 图文消息留言管理

## 用户管理
Expand Down
228 changes: 228 additions & 0 deletions officialaccount/draft/draft.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package draft

import (
"fmt"

"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)

const (
addURL = "https://api.weixin.qq.com/cgi-bin/draft/add" // 新建草稿
getURL = "https://api.weixin.qq.com/cgi-bin/draft/get" // 获取草稿
deleteURL = "https://api.weixin.qq.com/cgi-bin/draft/delete" // 删除草稿
updateURL = "https://api.weixin.qq.com/cgi-bin/draft/update" // 修改草稿
countURL = "https://api.weixin.qq.com/cgi-bin/draft/count" // 获取草稿总数
paginateURL = "https://api.weixin.qq.com/cgi-bin/draft/batchget" // 获取草稿列表
)

// Draft 草稿箱
type Draft struct {
*context.Context
}

// NewDraft init
func NewDraft(ctx *context.Context) *Draft {
return &Draft{
Context: ctx,
}
}

// Article 草稿
type Article struct {
Title string `json:"title"` // 标题
Author string `json:"author"` // 作者
Digest string `json:"digest"` // 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空。
Content string `json:"content"` // 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且去除JS
ContentSourceURL string `json:"content_source_url"` // 图文消息的原文地址,即点击“阅读原文”后的URL
ThumbMediaID string `json:"thumb_media_id"` // 图文消息的封面图片素材id(必须是永久MediaID)
ShowCoverPic uint `json:"show_cover_pic"` // 是否显示封面,0为false,即不显示,1为true,即显示(默认)
NeedOpenComment uint `json:"need_open_comment"` // 是否打开评论,0不打开(默认),1打开
OnlyFansCanComment uint `json:"only_fans_can_comment"` // 是否粉丝才可评论,0所有人可评论(默认),1粉丝才可评论
}

// AddDraft 新建草稿
func (draft *Draft) AddDraft(articles []*Article) (mediaID string, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var req struct {
Articles []*Article `json:"articles"`
}
req.Articles = articles

uri := fmt.Sprintf("%s?access_token=%s", addURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}

var res struct {
util.CommonError
MediaID string `json:"media_id"`
}
err = util.DecodeWithError(response, &res, "AddDraft")
if err != nil {
return
}
mediaID = res.MediaID
return
}

// GetDraft 获取草稿
func (draft *Draft) GetDraft(mediaID string) (articles []*Article, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var req struct {
MediaID string `json:"media_id"`
}
req.MediaID = mediaID

uri := fmt.Sprintf("%s?access_token=%s", getURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}

var res struct {
util.CommonError
NewsItem []*Article `json:"news_item"`
}
err = util.DecodeWithError(response, &res, "GetDraft")
if err != nil {
return
}

articles = res.NewsItem
return
}

// DeleteDraft 删除草稿
func (draft *Draft) DeleteDraft(mediaID string) (err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var req struct {
MediaID string `json:"media_id"`
}
req.MediaID = mediaID

var response []byte
uri := fmt.Sprintf("%s?access_token=%s", deleteURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}

err = util.DecodeWithCommonError(response, "DeleteDraft")
return
}

// UpdateDraft 修改草稿
// index 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义),第一篇为0
func (draft *Draft) UpdateDraft(article *Article, mediaID string, index uint) (err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var req struct {
MediaID string `json:"media_id"`
Index uint `json:"index"`
Article *Article `json:"articles"`
}
req.MediaID = mediaID
req.Index = index
req.Article = article

uri := fmt.Sprintf("%s?access_token=%s", updateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, req)
if err != nil {
return
}

err = util.DecodeWithCommonError(response, "UpdateDraft")
return
}

// CountDraft 获取草稿总数
func (draft *Draft) CountDraft() (total uint, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var response []byte
uri := fmt.Sprintf("%s?access_token=%s", countURL, accessToken)
response, err = util.HTTPGet(uri)
if err != nil {
return
}

var res struct {
util.CommonError
Total uint `json:"total_count"`
}
err = util.DecodeWithError(response, &res, "CountDraft")
if nil != err {
return
}

total = res.Total
return
}

// 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"` // 图文消息的id
Content ArticleListContent `json:"content"` // 内容
UpdateTime int64 `json:"update_time"` // 这篇图文消息素材的最后更新时间
}

// ArticleListContent 用于 ArticleListItem 的 content 节点
type ArticleListContent struct {
NewsItem []Article `json:"news_item"` // 这篇图文消息素材的内容
}

// PaginateDraft 获取草稿列表
func (draft *Draft) PaginateDraft(offset, count int64, noReturnContent bool) (list ArticleList, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}

var req struct {
Count int64 `json:"count"`
Offset int64 `json:"offset"`
NoReturnContent bool `json:"no_content"`
}
req.Count = count
req.Offset = offset
req.NoReturnContent = noReturnContent

var response []byte
uri := fmt.Sprintf("%s?access_token=%s", paginateURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}

err = util.DecodeWithError(response, &list, "PaginateDraft")
return
}
Loading

0 comments on commit 5c4b28a

Please sign in to comment.