freepublish.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package freepublish
  2. import (
  3. "fmt"
  4. "github.com/silenceper/wechat/v2/officialaccount/context"
  5. "github.com/silenceper/wechat/v2/util"
  6. )
  7. const (
  8. publishURL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit" // 发布接口
  9. selectStateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/get" // 发布状态轮询接口
  10. deleteURL = "https://api.weixin.qq.com/cgi-bin/freepublish/delete" // 删除发布
  11. firstArticleURL = "https://api.weixin.qq.com/cgi-bin/freepublish/getarticle" // 通过 article_id 获取已发布文章
  12. paginateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/batchget" // 获取成功发布列表
  13. )
  14. // PublishStatus 发布状态
  15. type PublishStatus uint
  16. const (
  17. // PublishStatusSuccess 0:成功
  18. PublishStatusSuccess PublishStatus = iota
  19. // PublishStatusPublishing 1:发布中
  20. PublishStatusPublishing
  21. // PublishStatusOriginalFail 2:原创失败
  22. PublishStatusOriginalFail
  23. // PublishStatusFail 3:常规失败
  24. PublishStatusFail
  25. // PublishStatusAuditRefused 4:平台审核不通过
  26. PublishStatusAuditRefused
  27. // PublishStatusUserDeleted 5:成功后用户删除所有文章
  28. PublishStatusUserDeleted
  29. // PublishStatusSystemBanned 6:成功后系统封禁所有文章
  30. PublishStatusSystemBanned
  31. )
  32. // FreePublish 发布能力
  33. type FreePublish struct {
  34. *context.Context
  35. }
  36. // NewFreePublish init
  37. func NewFreePublish(ctx *context.Context) *FreePublish {
  38. return &FreePublish{
  39. Context: ctx,
  40. }
  41. }
  42. // Publish 发布接口。需要先将图文素材以草稿的形式保存(见“草稿箱/新建草稿”,
  43. // 如需从已保存的草稿中选择,见“草稿箱/获取草稿列表”),选择要发布的草稿 media_id 进行发布
  44. func (freePublish *FreePublish) Publish(mediaID string) (publishID int64, err error) {
  45. var accessToken string
  46. accessToken, err = freePublish.GetAccessToken()
  47. if err != nil {
  48. return
  49. }
  50. var req struct {
  51. MediaID string `json:"media_id"`
  52. }
  53. req.MediaID = mediaID
  54. var response []byte
  55. uri := fmt.Sprintf("%s?access_token=%s", publishURL, accessToken)
  56. response, err = util.PostJSON(uri, req)
  57. if err != nil {
  58. return
  59. }
  60. var res struct {
  61. util.CommonError
  62. PublishID int64 `json:"publish_id"`
  63. }
  64. err = util.DecodeWithError(response, &res, "SubmitFreePublish")
  65. if err != nil {
  66. return
  67. }
  68. publishID = res.PublishID
  69. return
  70. }
  71. // PublishStatusList 发布任务状态列表
  72. type PublishStatusList struct {
  73. util.CommonError
  74. PublishID int64 `json:"publish_id"` // 发布任务id
  75. PublishStatus PublishStatus `json:"publish_status"` // 发布状态
  76. ArticleID string `json:"article_id"` // 当发布状态为0时(即成功)时,返回图文的 article_id,可用于“客服消息”场景
  77. ArticleDetail PublishArticleDetail `json:"article_detail"` // 发布任务文章成功状态详情
  78. FailIndex []uint `json:"fail_idx"` // 当发布状态为2或4时,返回不通过的文章编号,第一篇为 1;其他发布状态则为空
  79. }
  80. // PublishArticleDetail 发布任务成功详情
  81. type PublishArticleDetail struct {
  82. Count uint `json:"count"` // 当发布状态为0时(即成功)时,返回文章数量
  83. Items []PublishArticleItem `json:"item"`
  84. }
  85. // PublishArticleItem 发布任务成功的文章内容
  86. type PublishArticleItem struct {
  87. Index uint `json:"idx"` // 当发布状态为0时(即成功)时,返回文章对应的编号
  88. ArticleURL string `json:"article_url"` // 当发布状态为0时(即成功)时,返回图文的永久链接
  89. }
  90. // SelectStatus 发布状态轮询接口
  91. func (freePublish *FreePublish) SelectStatus(publishID int64) (list PublishStatusList, err error) {
  92. accessToken, err := freePublish.GetAccessToken()
  93. if err != nil {
  94. return
  95. }
  96. var req struct {
  97. PublishID int64 `json:"publish_id"`
  98. }
  99. req.PublishID = publishID
  100. var response []byte
  101. uri := fmt.Sprintf("%s?access_token=%s", selectStateURL, accessToken)
  102. response, err = util.PostJSON(uri, req)
  103. if err != nil {
  104. return
  105. }
  106. err = util.DecodeWithError(response, &list, "SelectStatusFreePublish")
  107. return
  108. }
  109. // Delete 删除发布。
  110. // index 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章
  111. // !!!此操作不可逆,请谨慎操作!!!删除后微信公众号后台仍然会有记录!!!
  112. func (freePublish *FreePublish) Delete(articleID string, index uint) (err error) {
  113. accessToken, err := freePublish.GetAccessToken()
  114. if err != nil {
  115. return err
  116. }
  117. var req struct {
  118. ArticleID string `json:"article_id"`
  119. Index uint `json:"index"`
  120. }
  121. req.ArticleID = articleID
  122. req.Index = index
  123. var response []byte
  124. uri := fmt.Sprintf("%s?access_token=%s", deleteURL, accessToken)
  125. response, err = util.PostJSON(uri, req)
  126. if err != nil {
  127. return err
  128. }
  129. return util.DecodeWithCommonError(response, "DeleteFreePublish")
  130. }
  131. // Article 图文信息内容
  132. type Article struct {
  133. Title string `json:"title"` // 标题
  134. Author string `json:"author"` // 作者
  135. Digest string `json:"digest"` // 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
  136. Content string `json:"content"` // 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS
  137. ContentSourceURL string `json:"content_source_url"` // 图文消息的原文地址,即点击“阅读原文”后的URL
  138. ThumbMediaID string `json:"thumb_media_id"` // 图文消息的封面图片素材id(一定是永久MediaID)
  139. ShowCoverPic uint `json:"show_cover_pic"` // 是否显示封面,0为false,即不显示,1为true,即显示(默认)
  140. NeedOpenComment uint `json:"need_open_comment"` // 是否打开评论,0不打开(默认),1打开
  141. OnlyFansCanComment uint `json:"only_fans_can_comment"` // 是否粉丝才可评论,0所有人可评论(默认),1粉丝才可评论
  142. URL string `json:"url"` // 图文消息的URL
  143. IsDeleted bool `json:"is_deleted"` // 该图文是否被删除
  144. }
  145. // First 通过 article_id 获取已发布文章
  146. func (freePublish *FreePublish) First(articleID string) (list []Article, err error) {
  147. accessToken, err := freePublish.GetAccessToken()
  148. if err != nil {
  149. return
  150. }
  151. var req struct {
  152. ArticleID string `json:"article_id"`
  153. }
  154. req.ArticleID = articleID
  155. var response []byte
  156. uri := fmt.Sprintf("%s?access_token=%s", firstArticleURL, accessToken)
  157. response, err = util.PostJSON(uri, req)
  158. if err != nil {
  159. return
  160. }
  161. var res struct {
  162. util.CommonError
  163. NewsItem []Article `json:"news_item"`
  164. }
  165. err = util.DecodeWithError(response, &res, "FirstFreePublish")
  166. if err != nil {
  167. return
  168. }
  169. list = res.NewsItem
  170. return
  171. }
  172. // ArticleList 发布列表
  173. type ArticleList struct {
  174. util.CommonError
  175. TotalCount int64 `json:"total_count"` // 成功发布素材的总数
  176. ItemCount int64 `json:"item_count"` // 本次调用获取的素材的数量
  177. Item []ArticleListItem `json:"item"`
  178. }
  179. // ArticleListItem 用于 ArticleList 的 item 节点
  180. type ArticleListItem struct {
  181. ArticleID string `json:"article_id"` // 成功发布的图文消息id
  182. Content ArticleListContent `json:"content"` // 内容
  183. UpdateTime int64 `json:"update_time"` // 这篇图文消息素材的最后更新时间
  184. }
  185. // ArticleListContent 用于 ArticleListItem 的 content 节点
  186. type ArticleListContent struct {
  187. NewsItem []Article `json:"news_item"` // 这篇图文消息素材的内容
  188. }
  189. // Paginate 获取成功发布列表
  190. func (freePublish *FreePublish) Paginate(offset, count int64, noReturnContent bool) (list ArticleList, err error) {
  191. var accessToken string
  192. accessToken, err = freePublish.GetAccessToken()
  193. if err != nil {
  194. return
  195. }
  196. var req struct {
  197. Count int64 `json:"count"`
  198. Offset int64 `json:"offset"`
  199. NoReturnContent bool `json:"no_content"`
  200. }
  201. req.Count = count
  202. req.Offset = offset
  203. req.NoReturnContent = noReturnContent
  204. var response []byte
  205. uri := fmt.Sprintf("%s?access_token=%s", paginateURL, accessToken)
  206. response, err = util.PostJSON(uri, req)
  207. if err != nil {
  208. return
  209. }
  210. err = util.DecodeWithError(response, &list, "PaginateFreePublish")
  211. return
  212. }