subscribe.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. package subscribe
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/silenceper/wechat/v2/miniprogram/context"
  6. "github.com/silenceper/wechat/v2/util"
  7. )
  8. const (
  9. // 发送订阅消息
  10. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
  11. subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"
  12. // 获取当前帐号下的个人模板列表
  13. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
  14. getTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
  15. // 添加订阅模板
  16. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
  17. addTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
  18. // 删除私有模板
  19. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.deleteTemplate.html
  20. delTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
  21. // 统一服务消息
  22. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
  23. uniformMessageSend = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"
  24. // getCategoryURL 获取类目
  25. getCategoryURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=%s"
  26. // getPubTemplateKeyWordsByIDURL 获取关键词列表
  27. getPubTemplateKeyWordsByIDURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=%s&tid=%s"
  28. // getPubTemplateTitleListURL 获取所属类目下的公共模板
  29. getPubTemplateTitleListURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=%s&ids=%s&start=%d&limit=%d"
  30. // setUserNotifyURL 激活与更新服务卡片
  31. setUserNotifyURL = "https://api.weixin.qq.com/wxa/set_user_notify?access_token=%s"
  32. // setUserNotifyExtURL 更新服务卡片扩展信息
  33. setUserNotifyExtURL = "https://api.weixin.qq.com/wxa/set_user_notifyext?access_token=%s"
  34. // getUserNotifyURL 查询服务卡片状态
  35. getUserNotifyURL = "https://api.weixin.qq.com/wxa/get_user_notify?access_token=%s"
  36. )
  37. // Subscribe 订阅消息
  38. type Subscribe struct {
  39. *context.Context
  40. }
  41. // NewSubscribe 实例化
  42. func NewSubscribe(ctx *context.Context) *Subscribe {
  43. return &Subscribe{Context: ctx}
  44. }
  45. // Message 订阅消息请求参数
  46. type Message struct {
  47. ToUser string `json:"touser"` // 必选,接收者(用户)的 openid
  48. TemplateID string `json:"template_id"` // 必选,所需下发的订阅模板id
  49. Page string `json:"page"` // 可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
  50. Data map[string]*DataItem `json:"data"` // 必选, 模板内容
  51. MiniprogramState string `json:"miniprogram_state"` // 可选,跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
  52. Lang string `json:"lang"` // 入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN
  53. }
  54. // DataItem 模版内某个 .DATA 的值
  55. type DataItem struct {
  56. Value interface{} `json:"value"`
  57. Color string `json:"color"`
  58. }
  59. // TemplateItem template item
  60. type TemplateItem struct {
  61. PriTmplID string `json:"priTmplId"`
  62. Title string `json:"title"`
  63. Content string `json:"content"`
  64. Example string `json:"example"`
  65. Type int64 `json:"type"`
  66. KeywordEnumValueList []KeywordEnumValue `json:"keywordEnumValueList"`
  67. }
  68. // KeywordEnumValue 枚举参数值范围
  69. type KeywordEnumValue struct {
  70. EnumValueList []string `json:"enumValueList"`
  71. KeywordCode string `json:"keywordCode"`
  72. }
  73. // TemplateList template list
  74. type TemplateList struct {
  75. util.CommonError
  76. Data []TemplateItem `json:"data"`
  77. }
  78. // resTemplateSend 发送获取 msg id
  79. type resTemplateSend struct {
  80. util.CommonError
  81. MsgID int64 `json:"msgid"`
  82. }
  83. // Send 发送订阅消息
  84. func (s *Subscribe) Send(msg *Message) (err error) {
  85. var accessToken string
  86. accessToken, err = s.GetAccessToken()
  87. if err != nil {
  88. return
  89. }
  90. uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
  91. response, err := util.PostJSON(uri, msg)
  92. if err != nil {
  93. return
  94. }
  95. return util.DecodeWithCommonError(response, "Send")
  96. }
  97. // SendGetMsgID 发送订阅消息返回 msgid
  98. func (s *Subscribe) SendGetMsgID(msg *Message) (msgID int64, err error) {
  99. var accessToken string
  100. accessToken, err = s.GetAccessToken()
  101. if err != nil {
  102. return
  103. }
  104. uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
  105. response, err := util.PostJSON(uri, msg)
  106. if err != nil {
  107. return
  108. }
  109. var result resTemplateSend
  110. if err = json.Unmarshal(response, &result); err != nil {
  111. return
  112. }
  113. if result.ErrCode != 0 {
  114. err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
  115. return
  116. }
  117. msgID = result.MsgID
  118. return
  119. }
  120. // ListTemplates 获取当前帐号下的个人模板列表
  121. // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
  122. func (s *Subscribe) ListTemplates() (*TemplateList, error) {
  123. accessToken, err := s.GetAccessToken()
  124. if err != nil {
  125. return nil, err
  126. }
  127. uri := fmt.Sprintf("%s?access_token=%s", getTemplateURL, accessToken)
  128. response, err := util.HTTPGet(uri)
  129. if err != nil {
  130. return nil, err
  131. }
  132. templateList := TemplateList{}
  133. err = util.DecodeWithError(response, &templateList, "ListTemplates")
  134. if err != nil {
  135. return nil, err
  136. }
  137. return &templateList, nil
  138. }
  139. // UniformMessage 统一服务消息
  140. type UniformMessage struct {
  141. ToUser string `json:"touser"`
  142. WeappTemplateMsg struct {
  143. TemplateID string `json:"template_id"`
  144. Page string `json:"page"`
  145. FormID string `json:"form_id"`
  146. Data map[string]*DataItem `json:"data"`
  147. EmphasisKeyword string `json:"emphasis_keyword"`
  148. } `json:"weapp_template_msg"`
  149. MpTemplateMsg struct {
  150. Appid string `json:"appid"`
  151. TemplateID string `json:"template_id"`
  152. URL string `json:"url"`
  153. Miniprogram struct {
  154. Appid string `json:"appid"`
  155. Pagepath string `json:"pagepath"`
  156. } `json:"miniprogram"`
  157. Data map[string]*DataItem `json:"data"`
  158. } `json:"mp_template_msg"`
  159. }
  160. // UniformSend 发送统一服务消息
  161. func (s *Subscribe) UniformSend(msg *UniformMessage) (err error) {
  162. var accessToken string
  163. accessToken, err = s.GetAccessToken()
  164. if err != nil {
  165. return
  166. }
  167. uri := fmt.Sprintf("%s?access_token=%s", uniformMessageSend, accessToken)
  168. response, err := util.PostJSON(uri, msg)
  169. if err != nil {
  170. return
  171. }
  172. return util.DecodeWithCommonError(response, "UniformSend")
  173. }
  174. type resSubscribeAdd struct {
  175. util.CommonError
  176. TemplateID string `json:"priTmplId"`
  177. }
  178. // Add 添加订阅消息模板
  179. func (s *Subscribe) Add(ShortID string, kidList []int, sceneDesc string) (templateID string, err error) {
  180. var accessToken string
  181. accessToken, err = s.GetAccessToken()
  182. if err != nil {
  183. return
  184. }
  185. var msg = struct {
  186. TemplateIDShort string `json:"tid"`
  187. SceneDesc string `json:"sceneDesc"`
  188. KidList []int `json:"kidList"`
  189. }{TemplateIDShort: ShortID, SceneDesc: sceneDesc, KidList: kidList}
  190. uri := fmt.Sprintf("%s?access_token=%s", addTemplateURL, accessToken)
  191. var response []byte
  192. response, err = util.PostJSON(uri, msg)
  193. if err != nil {
  194. return
  195. }
  196. var result resSubscribeAdd
  197. err = util.DecodeWithError(response, &result, "AddSubscribe")
  198. return result.TemplateID, err
  199. }
  200. // Delete 删除私有模板
  201. func (s *Subscribe) Delete(templateID string) (err error) {
  202. var accessToken string
  203. accessToken, err = s.GetAccessToken()
  204. if err != nil {
  205. return
  206. }
  207. var msg = struct {
  208. TemplateID string `json:"priTmplId"`
  209. }{TemplateID: templateID}
  210. uri := fmt.Sprintf("%s?access_token=%s", delTemplateURL, accessToken)
  211. var response []byte
  212. response, err = util.PostJSON(uri, msg)
  213. if err != nil {
  214. return
  215. }
  216. return util.DecodeWithCommonError(response, "DeleteSubscribe")
  217. }
  218. // GetCategoryResponse 获取类目响应
  219. type GetCategoryResponse struct {
  220. util.CommonError
  221. Data []Category `json:"data"`
  222. }
  223. // Category 类目
  224. type Category struct {
  225. ID int64 `json:"id"`
  226. Name string `json:"name"`
  227. }
  228. // GetCategory 获取类目
  229. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getCategory.html
  230. func (s *Subscribe) GetCategory() ([]Category, error) {
  231. var (
  232. accessToken string
  233. err error
  234. )
  235. if accessToken, err = s.GetAccessToken(); err != nil {
  236. return nil, err
  237. }
  238. var response []byte
  239. if response, err = util.HTTPGet(fmt.Sprintf(getCategoryURL, accessToken)); err != nil {
  240. return nil, err
  241. }
  242. result := &GetCategoryResponse{}
  243. err = util.DecodeWithError(response, result, "GetCategory")
  244. return result.Data, err
  245. }
  246. // GetPubTemplateKeywordsByIDResponse 获取关键词列表响应
  247. type GetPubTemplateKeywordsByIDResponse struct {
  248. util.CommonError
  249. Count int64 `json:"count"`
  250. Data []PubTemplateKeywords `json:"data"`
  251. }
  252. // PubTemplateKeywords 关键词
  253. type PubTemplateKeywords struct {
  254. KID int64 `json:"kid"`
  255. Name string `json:"name"`
  256. Example string `json:"example"`
  257. Rule string `json:"rule"`
  258. }
  259. // GetPubTemplateKeywordsByID 获取关键词列表
  260. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getPubTemplateKeyWordsById.html
  261. func (s *Subscribe) GetPubTemplateKeywordsByID(tid string) (*GetPubTemplateKeywordsByIDResponse, error) {
  262. var (
  263. accessToken string
  264. err error
  265. )
  266. if accessToken, err = s.GetAccessToken(); err != nil {
  267. return nil, err
  268. }
  269. var response []byte
  270. if response, err = util.HTTPGet(fmt.Sprintf(getPubTemplateKeyWordsByIDURL, accessToken, tid)); err != nil {
  271. return nil, err
  272. }
  273. result := &GetPubTemplateKeywordsByIDResponse{}
  274. err = util.DecodeWithError(response, result, "GetPubTemplateKeywordsByID")
  275. return result, err
  276. }
  277. // GetPubTemplateTitleListRequest 获取所属类目下的公共模板请求
  278. type GetPubTemplateTitleListRequest struct {
  279. Start int64
  280. Limit int64
  281. IDs string
  282. }
  283. // GetPubTemplateTitleListResponse 获取所属类目下的公共模板响应
  284. type GetPubTemplateTitleListResponse struct {
  285. util.CommonError
  286. Count int64 `json:"count"`
  287. Data []PubTemplateTitle `json:"data"`
  288. }
  289. // PubTemplateTitle 模板标题
  290. type PubTemplateTitle struct {
  291. Type int64 `json:"type"`
  292. TID string `json:"tid"`
  293. Title string `json:"title"`
  294. CategoryID string `json:"categoryId"`
  295. }
  296. // GetPubTemplateTitleList 获取所属类目下的公共模板
  297. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getPubTemplateTitleList.html
  298. func (s *Subscribe) GetPubTemplateTitleList(req *GetPubTemplateTitleListRequest) (*GetPubTemplateTitleListResponse, error) {
  299. var (
  300. accessToken string
  301. err error
  302. )
  303. if accessToken, err = s.GetAccessToken(); err != nil {
  304. return nil, err
  305. }
  306. var response []byte
  307. if response, err = util.HTTPGet(fmt.Sprintf(getPubTemplateTitleListURL, accessToken, req.IDs, req.Start, req.Limit)); err != nil {
  308. return nil, err
  309. }
  310. result := &GetPubTemplateTitleListResponse{}
  311. err = util.DecodeWithError(response, result, "GetPubTemplateTitleList")
  312. return result, err
  313. }
  314. // SetUserNotifyRequest 激活与更新服务卡片请求
  315. type SetUserNotifyRequest struct {
  316. OpenID string `json:"openid"`
  317. NotifyType int64 `json:"notify_type"`
  318. NotifyCode string `json:"notify_code"`
  319. ContentJSON string `json:"content_json"`
  320. CheckJSON string `json:"check_json,omitempty"`
  321. }
  322. // SetUserNotify 激活与更新服务卡片
  323. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/setUserNotify.html
  324. func (s *Subscribe) SetUserNotify(req *SetUserNotifyRequest) error {
  325. var (
  326. accessToken string
  327. err error
  328. )
  329. if accessToken, err = s.GetAccessToken(); err != nil {
  330. return err
  331. }
  332. var response []byte
  333. if response, err = util.PostJSON(fmt.Sprintf(setUserNotifyURL, accessToken), req); err != nil {
  334. return err
  335. }
  336. return util.DecodeWithCommonError(response, "SetUserNotify")
  337. }
  338. // SetUserNotifyExtRequest 更新服务卡片扩展信息请求
  339. type SetUserNotifyExtRequest struct {
  340. OpenID string `json:"openid"`
  341. NotifyType int64 `json:"notify_type"`
  342. NotifyCode string `json:"notify_code"`
  343. ExtJSON string `json:"ext_json"`
  344. }
  345. // SetUserNotifyExt 更新服务卡片扩展信息
  346. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/setUserNotifyExt.html
  347. func (s *Subscribe) SetUserNotifyExt(req *SetUserNotifyExtRequest) error {
  348. var (
  349. accessToken string
  350. err error
  351. )
  352. if accessToken, err = s.GetAccessToken(); err != nil {
  353. return err
  354. }
  355. var response []byte
  356. if response, err = util.PostJSON(fmt.Sprintf(setUserNotifyExtURL, accessToken), req); err != nil {
  357. return err
  358. }
  359. return util.DecodeWithCommonError(response, "SetUserNotifyExt")
  360. }
  361. // GetUserNotifyRequest 查询服务卡片状态请求
  362. type GetUserNotifyRequest struct {
  363. OpenID string `json:"openid"`
  364. NotifyType int64 `json:"notify_type"`
  365. NotifyCode string `json:"notify_code"`
  366. }
  367. // GetUserNotifyResponse 查询服务卡片状态响应
  368. type GetUserNotifyResponse struct {
  369. util.CommonError
  370. NotifyInfo NotifyInfo `json:"notify_info"`
  371. }
  372. // NotifyInfo 卡片状态
  373. type NotifyInfo struct {
  374. NotifyType int64 `json:"notify_type"`
  375. ContentJSON string `json:"content_json"`
  376. CodeState int64 `json:"code_state"`
  377. CodeExpireTime int64 `json:"code_expire_time"`
  378. }
  379. // GetUserNotify 查询服务卡片状态
  380. // see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getUserNotify.html
  381. func (s *Subscribe) GetUserNotify(req *GetUserNotifyRequest) (*GetUserNotifyResponse, error) {
  382. var (
  383. accessToken string
  384. err error
  385. )
  386. if accessToken, err = s.GetAccessToken(); err != nil {
  387. return nil, err
  388. }
  389. var response []byte
  390. if response, err = util.PostJSON(fmt.Sprintf(getUserNotifyURL, accessToken), req); err != nil {
  391. return nil, err
  392. }
  393. result := &GetUserNotifyResponse{}
  394. err = util.DecodeWithError(response, result, "GetUserNotify")
  395. return result, err
  396. }