mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-06 13:42:26 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c4b28acee | ||
|
|
80ce4aefc4 | ||
|
|
0d915d203b | ||
|
|
172c4abde5 | ||
|
|
2f898f80f6 | ||
|
|
4721f7567b | ||
|
|
9cecda0469 |
@@ -132,6 +132,8 @@
|
|||||||
|
|
||||||
#### 群发任务管理
|
#### 群发任务管理
|
||||||
|
|
||||||
|
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/task-account/shopping-guide.addGuideMassendJob.html)
|
||||||
|
|
||||||
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
|
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
|
||||||
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
|
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
说明:「发表记录」包括群发和发布。
|
||||||
|
|
||||||
|
注意:该接口,只能处理 "发布" 相关的信息,无法操作和获取 "群发" 相关内容!
|
||||||
|
|
||||||
|
- 群发:主动推送给粉丝,历史消息可看,被搜一搜收录,可以限定部分的粉丝接收到。
|
||||||
|
- 发布:不会主动推给粉丝,历史消息列表看不到,但是是公开给所有人的文章。也不会占用群发的次数。每天可以发布多篇内容。可以用于自动回复、自定义菜单、页面模板和话题中,发布成功时会生成一个永久链接。
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | 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 |
|
||||||
|
|
||||||
|
|
||||||
## 图文消息留言管理
|
## 图文消息留言管理
|
||||||
|
|
||||||
## 用户管理
|
## 用户管理
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ const (
|
|||||||
code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
|
code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
|
||||||
|
|
||||||
checkEncryptedDataURL = "https://api.weixin.qq.com/wxa/business/checkencryptedmsg?access_token=%s"
|
checkEncryptedDataURL = "https://api.weixin.qq.com/wxa/business/checkencryptedmsg?access_token=%s"
|
||||||
|
|
||||||
|
getPhoneNumber = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auth 登录/用户信息
|
// Auth 登录/用户信息
|
||||||
@@ -90,3 +92,42 @@ func (auth *Auth) CheckEncryptedDataContext(ctx context2.Context, encryptedMsgHa
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPhoneNumberResponse 新版获取用户手机号响应结构体
|
||||||
|
type GetPhoneNumberResponse struct {
|
||||||
|
util.CommonError
|
||||||
|
|
||||||
|
PhoneInfo PhoneInfo `json:"phone_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PhoneInfo 获取用户手机号内容
|
||||||
|
type PhoneInfo struct {
|
||||||
|
PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号
|
||||||
|
PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号
|
||||||
|
CountryCode string `json:"countryCode"` // 区号
|
||||||
|
WaterMark struct {
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
AppID string `json:"appid"`
|
||||||
|
} `json:"watermark"` // 数据水印
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhoneNumber 小程序通过code获取用户手机号
|
||||||
|
func (auth *Auth) GetPhoneNumber(code string) (result GetPhoneNumberResponse, err error) {
|
||||||
|
var response []byte
|
||||||
|
var (
|
||||||
|
at string
|
||||||
|
)
|
||||||
|
if at, err = auth.GetAccessToken(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := map[string]interface{}{
|
||||||
|
"code": code,
|
||||||
|
}
|
||||||
|
if response, err = util.PostJSON(fmt.Sprintf(getPhoneNumber, at), body); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = util.DecodeWithError(response, &result, "phonenumber.getPhoneNumber"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
|
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/message"
|
"github.com/silenceper/wechat/v2/miniprogram/message"
|
||||||
|
"github.com/silenceper/wechat/v2/miniprogram/privacy"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/qrcode"
|
"github.com/silenceper/wechat/v2/miniprogram/qrcode"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/shortlink"
|
"github.com/silenceper/wechat/v2/miniprogram/shortlink"
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/subscribe"
|
"github.com/silenceper/wechat/v2/miniprogram/subscribe"
|
||||||
@@ -57,6 +58,11 @@ func (miniProgram *MiniProgram) GetAnalysis() *analysis.Analysis {
|
|||||||
return analysis.NewAnalysis(miniProgram.ctx)
|
return analysis.NewAnalysis(miniProgram.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPrivacy 小程序隐私协议相关API
|
||||||
|
func (miniProgram *MiniProgram) GetPrivacy() *privacy.Privacy {
|
||||||
|
return privacy.NewPrivacy(miniProgram.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// GetQRCode 小程序码相关API
|
// GetQRCode 小程序码相关API
|
||||||
func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode {
|
func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode {
|
||||||
return qrcode.NewQRCode(miniProgram.ctx)
|
return qrcode.NewQRCode(miniProgram.ctx)
|
||||||
|
|||||||
167
miniprogram/privacy/privacy.go
Normal file
167
miniprogram/privacy/privacy.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package privacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Privacy 小程序授权隐私设置
|
||||||
|
type Privacy struct {
|
||||||
|
*context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrivacy 实例化小程序隐私接口
|
||||||
|
// 文档地址 https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html
|
||||||
|
func NewPrivacy(context *context.Context) *Privacy {
|
||||||
|
if context == nil {
|
||||||
|
panic("NewPrivacy got a nil context")
|
||||||
|
}
|
||||||
|
return &Privacy{
|
||||||
|
context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerSetting 收集方(开发者)信息配置
|
||||||
|
type OwnerSetting struct {
|
||||||
|
ContactEmail string `json:"contact_email"`
|
||||||
|
ContactPhone string `json:"contact_phone"`
|
||||||
|
ContactQQ string `json:"contact_qq"`
|
||||||
|
ContactWeixin string `json:"contact_weixin"`
|
||||||
|
ExtFileMediaID string `json:"ext_file_media_id"`
|
||||||
|
NoticeMethod string `json:"notice_method"`
|
||||||
|
StoreExpireTimestamp string `json:"store_expire_timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingItem 收集权限的配置
|
||||||
|
type SettingItem struct {
|
||||||
|
PrivacyKey string `json:"privacy_key"`
|
||||||
|
PrivacyText string `json:"privacy_text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrivacySettingRequest 设置权限的请求参数
|
||||||
|
type SetPrivacySettingRequest struct {
|
||||||
|
PrivacyVer int `json:"privacy_ver"`
|
||||||
|
OwnerSetting OwnerSetting `json:"owner_setting"`
|
||||||
|
SettingList []SettingItem `json:"setting_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
setPrivacySettingURL = "https://api.weixin.qq.com/cgi-bin/component/setprivacysetting"
|
||||||
|
getPrivacySettingURL = "https://api.weixin.qq.com/cgi-bin/component/getprivacysetting"
|
||||||
|
uploadPrivacyExtFileURL = "https://api.weixin.qq.com/cgi-bin/component/uploadprivacyextfile"
|
||||||
|
|
||||||
|
// PrivacyV1 用户隐私保护指引的版本,1表示现网版本。
|
||||||
|
PrivacyV1 = 1
|
||||||
|
// PrivacyV2 2表示开发版。默认是2开发版。
|
||||||
|
PrivacyV2 = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetPrivacySettingResponse 获取权限配置的响应结果
|
||||||
|
type GetPrivacySettingResponse struct {
|
||||||
|
util.CommonError
|
||||||
|
CodeExist int `json:"code_exist"`
|
||||||
|
PrivacyList []string `json:"privacy_list"`
|
||||||
|
SettingList []SettingResponseItem `json:"setting_list"`
|
||||||
|
UpdateTime int64 `json:"update_time"`
|
||||||
|
OwnerSetting OwnerSetting `json:"owner_setting"`
|
||||||
|
PrivacyDesc DescList `json:"privacy_desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingResponseItem 获取权限设置的响应明细
|
||||||
|
type SettingResponseItem struct {
|
||||||
|
PrivacyKey string `json:"privacy_key"`
|
||||||
|
PrivacyText string `json:"privacy_text"`
|
||||||
|
PrivacyLabel string `json:"privacy_label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescList 权限列表(保持与官方一致)
|
||||||
|
type DescList struct {
|
||||||
|
PrivacyDescList []Desc `json:"privacy_desc_list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desc 权限列表明细(保持与官方一致)
|
||||||
|
type Desc struct {
|
||||||
|
PrivacyDesc string `json:"privacy_desc"`
|
||||||
|
PrivacyKey string `json:"privacy_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivacySetting 获取小程序权限配置
|
||||||
|
func (s *Privacy) GetPrivacySetting(privacyVer int) (GetPrivacySettingResponse, error) {
|
||||||
|
accessToken, err := s.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return GetPrivacySettingResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", getPrivacySettingURL, accessToken), map[string]int{
|
||||||
|
"privacy_ver": privacyVer,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return GetPrivacySettingResponse{}, err
|
||||||
|
}
|
||||||
|
// 返回错误信息
|
||||||
|
var result GetPrivacySettingResponse
|
||||||
|
if err = util.DecodeWithError(response, &result, "getprivacysetting"); err != nil {
|
||||||
|
return GetPrivacySettingResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrivacySetting 更新小程序权限配置
|
||||||
|
func (s *Privacy) SetPrivacySetting(privacyVer int, ownerSetting OwnerSetting, settingList []SettingItem) error {
|
||||||
|
if privacyVer == PrivacyV1 && len(settingList) > 0 {
|
||||||
|
return errors.New("当privacy_ver传2或者不传时,setting_list是必填;当privacy_ver传1时,该参数不可传")
|
||||||
|
}
|
||||||
|
accessToken, err := s.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", setPrivacySettingURL, accessToken), SetPrivacySettingRequest{
|
||||||
|
PrivacyVer: privacyVer,
|
||||||
|
OwnerSetting: ownerSetting,
|
||||||
|
SettingList: settingList,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回错误信息
|
||||||
|
if err = util.DecodeWithCommonError(response, "setprivacysetting"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadPrivacyExtFileResponse 上传权限定义模板响应参数
|
||||||
|
type UploadPrivacyExtFileResponse struct {
|
||||||
|
util.CommonError
|
||||||
|
ExtFileMediaID string `json:"ext_file_media_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadPrivacyExtFile 上传权限定义模板
|
||||||
|
func (s *Privacy) UploadPrivacyExtFile(fileData []byte) (UploadPrivacyExtFileResponse, error) {
|
||||||
|
accessToken, err := s.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return UploadPrivacyExtFileResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", uploadPrivacyExtFileURL, accessToken), map[string][]byte{
|
||||||
|
"file": fileData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return UploadPrivacyExtFileResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回错误信息
|
||||||
|
var result UploadPrivacyExtFileResponse
|
||||||
|
if err = util.DecodeWithError(response, &result, "setprivacysetting"); err != nil {
|
||||||
|
return UploadPrivacyExtFileResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
@@ -40,6 +40,8 @@ type QRCoder struct {
|
|||||||
Page string `json:"page,omitempty"`
|
Page string `json:"page,omitempty"`
|
||||||
// path 扫码进入的小程序页面路径
|
// path 扫码进入的小程序页面路径
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
|
// checkPath 检查page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错);为 false 时允许小程序未发布或者 page 不存在, 但page 有数量上限(60000个)请勿滥用
|
||||||
|
CheckPath bool `json:"check_path,omitempty"`
|
||||||
// width 图片宽度
|
// width 图片宽度
|
||||||
Width int `json:"width,omitempty"`
|
Width int `json:"width,omitempty"`
|
||||||
// scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
|
// scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
|
||||||
@@ -50,6 +52,8 @@ type QRCoder struct {
|
|||||||
LineColor Color `json:"line_color,omitempty"`
|
LineColor Color `json:"line_color,omitempty"`
|
||||||
// isHyaline 是否需要透明底色
|
// isHyaline 是否需要透明底色
|
||||||
IsHyaline bool `json:"is_hyaline,omitempty"`
|
IsHyaline bool `json:"is_hyaline,omitempty"`
|
||||||
|
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
|
||||||
|
EnvVersion string `json:"env_version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchCode 请求并返回二维码二进制数据
|
// fetchCode 请求并返回二维码二进制数据
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ const (
|
|||||||
// ULParams 请求参数
|
// ULParams 请求参数
|
||||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#请求参数
|
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#请求参数
|
||||||
type ULParams struct {
|
type ULParams struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
|
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
|
||||||
|
EnvVersion string `json:"env_version,omitempty"`
|
||||||
IsExpire bool `json:"is_expire"`
|
IsExpire bool `json:"is_expire"`
|
||||||
ExpireType TExpireType `json:"expire_type"`
|
ExpireType TExpireType `json:"expire_type"`
|
||||||
ExpireTime int64 `json:"expire_time"`
|
ExpireTime int64 `json:"expire_time"`
|
||||||
|
|||||||
228
officialaccount/draft/draft.go
Normal file
228
officialaccount/draft/draft.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
248
officialaccount/freepublish/freepublish.go
Normal file
248
officialaccount/freepublish/freepublish.go
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
package freepublish
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/officialaccount/context"
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
publishURL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit" // 发布接口
|
||||||
|
selectStateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/get" // 发布状态轮询接口
|
||||||
|
deleteURL = "https://api.weixin.qq.com/cgi-bin/freepublish/delete" // 删除发布
|
||||||
|
firstArticleURL = "https://api.weixin.qq.com/cgi-bin/freepublish/getarticle" // 通过 article_id 获取已发布文章
|
||||||
|
paginateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/batchget" // 获取成功发布列表
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublishStatus 发布状态
|
||||||
|
type PublishStatus uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PublishStatusSuccess 0:成功
|
||||||
|
PublishStatusSuccess PublishStatus = iota
|
||||||
|
// PublishStatusPublishing 1:发布中
|
||||||
|
PublishStatusPublishing
|
||||||
|
// PublishStatusOriginalFail 2:原创失败
|
||||||
|
PublishStatusOriginalFail
|
||||||
|
// PublishStatusFail 3:常规失败
|
||||||
|
PublishStatusFail
|
||||||
|
// PublishStatusAuditRefused 4:平台审核不通过
|
||||||
|
PublishStatusAuditRefused
|
||||||
|
// PublishStatusUserDeleted 5:成功后用户删除所有文章
|
||||||
|
PublishStatusUserDeleted
|
||||||
|
// PublishStatusSystemBanned 6:成功后系统封禁所有文章
|
||||||
|
PublishStatusSystemBanned
|
||||||
|
)
|
||||||
|
|
||||||
|
// FreePublish 发布能力
|
||||||
|
type FreePublish struct {
|
||||||
|
*context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFreePublish init
|
||||||
|
func NewFreePublish(ctx *context.Context) *FreePublish {
|
||||||
|
return &FreePublish{
|
||||||
|
Context: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish 发布接口。需要先将图文素材以草稿的形式保存(见“草稿箱/新建草稿”,
|
||||||
|
// 如需从已保存的草稿中选择,见“草稿箱/获取草稿列表”),选择要发布的草稿 media_id 进行发布
|
||||||
|
func (freePublish *FreePublish) Publish(mediaID string) (publishID int64, err error) {
|
||||||
|
var accessToken string
|
||||||
|
accessToken, err = freePublish.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", publishURL, accessToken)
|
||||||
|
response, err = util.PostJSON(uri, req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var res struct {
|
||||||
|
util.CommonError
|
||||||
|
PublishID int64 `json:"publish_id"`
|
||||||
|
}
|
||||||
|
err = util.DecodeWithError(response, &res, "SubmitFreePublish")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
publishID = res.PublishID
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishStatusList 发布任务状态列表
|
||||||
|
type PublishStatusList struct {
|
||||||
|
util.CommonError
|
||||||
|
PublishID int64 `json:"publish_id"` // 发布任务id
|
||||||
|
PublishStatus PublishStatus `json:"publish_status"` // 发布状态
|
||||||
|
ArticleID string `json:"article_id"` // 当发布状态为0时(即成功)时,返回图文的 article_id,可用于“客服消息”场景
|
||||||
|
ArticleDetail PublishArticleDetail `json:"article_detail"` // 发布任务文章成功状态详情
|
||||||
|
FailIndex []uint `json:"fail_idx"` // 当发布状态为2或4时,返回不通过的文章编号,第一篇为 1;其他发布状态则为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishArticleDetail 发布任务成功详情
|
||||||
|
type PublishArticleDetail struct {
|
||||||
|
Count uint `json:"count"` // 当发布状态为0时(即成功)时,返回文章数量
|
||||||
|
Items []PublishArticleItem `json:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishArticleItem 发布任务成功的文章内容
|
||||||
|
type PublishArticleItem struct {
|
||||||
|
Index uint `json:"idx"` // 当发布状态为0时(即成功)时,返回文章对应的编号
|
||||||
|
ArticleURL string `json:"article_url"` // 当发布状态为0时(即成功)时,返回图文的永久链接
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectStatus 发布状态轮询接口
|
||||||
|
func (freePublish *FreePublish) SelectStatus(publishID int64) (list PublishStatusList, err error) {
|
||||||
|
accessToken, err := freePublish.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
PublishID int64 `json:"publish_id"`
|
||||||
|
}
|
||||||
|
req.PublishID = publishID
|
||||||
|
|
||||||
|
var response []byte
|
||||||
|
uri := fmt.Sprintf("%s?access_token=%s", selectStateURL, accessToken)
|
||||||
|
response, err = util.PostJSON(uri, req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = util.DecodeWithError(response, &list, "SelectStatusFreePublish")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除发布。
|
||||||
|
// index 要删除的文章在图文消息中的位置,第一篇编号为1,该字段不填或填0会删除全部文章
|
||||||
|
// !!!此操作不可逆,请谨慎操作!!!删除后微信公众号后台仍然会有记录!!!
|
||||||
|
func (freePublish *FreePublish) Delete(articleID string, index uint) (err error) {
|
||||||
|
accessToken, err := freePublish.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
ArticleID string `json:"article_id"`
|
||||||
|
Index uint `json:"index"`
|
||||||
|
}
|
||||||
|
req.ArticleID = articleID
|
||||||
|
req.Index = index
|
||||||
|
|
||||||
|
var response []byte
|
||||||
|
uri := fmt.Sprintf("%s?access_token=%s", deleteURL, accessToken)
|
||||||
|
response, err = util.PostJSON(uri, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.DecodeWithCommonError(response, "DeleteFreePublish")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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粉丝才可评论
|
||||||
|
URL string `json:"url"` // 图文消息的URL
|
||||||
|
IsDeleted bool `json:"is_deleted"` // 该图文是否被删除
|
||||||
|
}
|
||||||
|
|
||||||
|
// First 通过 article_id 获取已发布文章
|
||||||
|
func (freePublish *FreePublish) First(articleID string) (list []Article, err error) {
|
||||||
|
accessToken, err := freePublish.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
ArticleID string `json:"article_id"`
|
||||||
|
}
|
||||||
|
req.ArticleID = articleID
|
||||||
|
|
||||||
|
var response []byte
|
||||||
|
uri := fmt.Sprintf("%s?access_token=%s", firstArticleURL, 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, "FirstFreePublish")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list = res.NewsItem
|
||||||
|
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 {
|
||||||
|
ArticleID string `json:"article_id"` // 成功发布的图文消息id
|
||||||
|
Content ArticleListContent `json:"content"` // 内容
|
||||||
|
UpdateTime int64 `json:"update_time"` // 这篇图文消息素材的最后更新时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArticleListContent 用于 ArticleListItem 的 content 节点
|
||||||
|
type ArticleListContent struct {
|
||||||
|
NewsItem []Article `json:"news_item"` // 这篇图文消息素材的内容
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginate 获取成功发布列表
|
||||||
|
func (freePublish *FreePublish) Paginate(offset, count int64, noReturnContent bool) (list ArticleList, err error) {
|
||||||
|
var accessToken string
|
||||||
|
accessToken, err = freePublish.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, "PaginateFreePublish")
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"github.com/silenceper/wechat/v2/officialaccount/device"
|
"github.com/silenceper/wechat/v2/officialaccount/device"
|
||||||
|
"github.com/silenceper/wechat/v2/officialaccount/freepublish"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgType 基本消息类型
|
// MsgType 基本消息类型
|
||||||
@@ -75,6 +76,8 @@ const (
|
|||||||
EventWxaMediaCheck EventType = "wxa_media_check"
|
EventWxaMediaCheck EventType = "wxa_media_check"
|
||||||
// EventSubscribeMsgPopupEvent 订阅通知事件推送
|
// EventSubscribeMsgPopupEvent 订阅通知事件推送
|
||||||
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
|
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
|
||||||
|
// EventPublishJobFinish 发布任务完成
|
||||||
|
EventPublishJobFinish EventType = "PUBLISHJOBFINISH"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -150,6 +153,21 @@ type MixMessage struct {
|
|||||||
List SubscribeMsgPopupEvent `xml:"List"`
|
List SubscribeMsgPopupEvent `xml:"List"`
|
||||||
} `xml:"SubscribeMsgPopupEvent"`
|
} `xml:"SubscribeMsgPopupEvent"`
|
||||||
|
|
||||||
|
// 事件相关:发布能力
|
||||||
|
PublishEventInfo struct {
|
||||||
|
PublishID int64 `xml:"publish_id"` // 发布任务id
|
||||||
|
PublishStatus freepublish.PublishStatus `xml:"publish_status"` // 发布状态
|
||||||
|
ArticleID string `xml:"article_id"` // 当发布状态为0时(即成功)时,返回图文的 article_id,可用于“客服消息”场景
|
||||||
|
ArticleDetail struct {
|
||||||
|
Count uint `xml:"count"` // 文章数量
|
||||||
|
Item []struct {
|
||||||
|
Index uint `xml:"idx"` // 文章对应的编号
|
||||||
|
ArticleURL string `xml:"article_url"` // 图文的永久链接
|
||||||
|
} `xml:"item"`
|
||||||
|
} `xml:"article_detail"` // 当发布状态为0时(即成功)时,返回内容
|
||||||
|
FailIndex []uint `xml:"fail_idx"` // 当发布状态为2或4时,返回不通过的文章编号,第一篇为 1;其他发布状态则为空
|
||||||
|
} `xml:"PublishEventInfo"`
|
||||||
|
|
||||||
// 第三方平台相关
|
// 第三方平台相关
|
||||||
InfoType InfoType `xml:"InfoType"`
|
InfoType InfoType `xml:"InfoType"`
|
||||||
AppID string `xml:"AppId"`
|
AppID string `xml:"AppId"`
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package officialaccount
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/officialaccount/draft"
|
||||||
|
"github.com/silenceper/wechat/v2/officialaccount/freepublish"
|
||||||
"github.com/silenceper/wechat/v2/officialaccount/ocr"
|
"github.com/silenceper/wechat/v2/officialaccount/ocr"
|
||||||
|
|
||||||
"github.com/silenceper/wechat/v2/officialaccount/datacube"
|
"github.com/silenceper/wechat/v2/officialaccount/datacube"
|
||||||
@@ -80,6 +82,16 @@ func (officialAccount *OfficialAccount) GetMaterial() *material.Material {
|
|||||||
return material.NewMaterial(officialAccount.ctx)
|
return material.NewMaterial(officialAccount.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDraft 草稿箱
|
||||||
|
func (officialAccount *OfficialAccount) GetDraft() *draft.Draft {
|
||||||
|
return draft.NewDraft(officialAccount.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFreePublish 发布能力
|
||||||
|
func (officialAccount *OfficialAccount) GetFreePublish() *freepublish.FreePublish {
|
||||||
|
return freepublish.NewFreePublish(officialAccount.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// GetJs js-sdk配置
|
// GetJs js-sdk配置
|
||||||
func (officialAccount *OfficialAccount) GetJs() *js.Js {
|
func (officialAccount *OfficialAccount) GetJs() *js.Js {
|
||||||
return js.NewJs(officialAccount.ctx)
|
return js.NewJs(officialAccount.ctx)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const (
|
|||||||
|
|
||||||
// ComponentAccessToken 第三方平台
|
// ComponentAccessToken 第三方平台
|
||||||
type ComponentAccessToken struct {
|
type ComponentAccessToken struct {
|
||||||
|
util.CommonError
|
||||||
AccessToken string `json:"component_access_token"`
|
AccessToken string `json:"component_access_token"`
|
||||||
ExpiresIn int64 `json:"expires_in"`
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
}
|
}
|
||||||
@@ -57,6 +58,10 @@ func (ctx *Context) SetComponentAccessToken(verifyTicket string) (*ComponentAcce
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if at.ErrCode != 0 {
|
||||||
|
return nil, fmt.Errorf("SetComponentAccessToken Error , errcode=%d , errmsg=%s", at.ErrCode, at.ErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
|
accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
|
||||||
expires := at.ExpiresIn - 1500
|
expires := at.ExpiresIn - 1500
|
||||||
if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
|
if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
|
||||||
|
|||||||
@@ -64,13 +64,14 @@ func main() {
|
|||||||
|
|
||||||
if chatInfo.Type == "image" {
|
if chatInfo.Type == "image" {
|
||||||
image, _ := chatInfo.GetImageMessage()
|
image, _ := chatInfo.GetImageMessage()
|
||||||
sdkfileid := image.Image.SdkFileId
|
sdkFileID := image.Image.SdkFileID
|
||||||
|
|
||||||
isFinish := false
|
isFinish := false
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
|
indexBuf := ""
|
||||||
for !isFinish {
|
for !isFinish {
|
||||||
//获取媒体数据
|
//获取媒体数据
|
||||||
mediaData, err := client.GetMediaData("", sdkfileid, "", "", 5)
|
mediaData, err := client.GetMediaData(indexBuf, sdkFileID, "", "", 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("媒体数据拉取失败:%v \n", err)
|
fmt.Printf("媒体数据拉取失败:%v \n", err)
|
||||||
return
|
return
|
||||||
@@ -79,6 +80,7 @@ func main() {
|
|||||||
if mediaData.IsFinish {
|
if mediaData.IsFinish {
|
||||||
isFinish = mediaData.IsFinish
|
isFinish = mediaData.IsFinish
|
||||||
}
|
}
|
||||||
|
indexBuf = mediaData.OutIndexBuf
|
||||||
}
|
}
|
||||||
filePath, _ := os.Getwd()
|
filePath, _ := os.Getwd()
|
||||||
filePath = path.Join(filePath, "test.png")
|
filePath = path.Join(filePath, "test.png")
|
||||||
|
|||||||
Reference in New Issue
Block a user