broadcast.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package broadcast
  2. import (
  3. "fmt"
  4. "github.com/silenceper/wechat/v2/officialaccount/context"
  5. "github.com/silenceper/wechat/v2/util"
  6. )
  7. const (
  8. sendURLByTag = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"
  9. sendURLByOpenID = "https://api.weixin.qq.com/cgi-bin/message/mass/send"
  10. deleteSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/delete"
  11. previewSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"
  12. massStatusSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/get"
  13. getSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get"
  14. setSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set"
  15. )
  16. // MsgType 发送消息类型
  17. type MsgType string
  18. const (
  19. // MsgTypeNews 图文消息
  20. MsgTypeNews MsgType = "mpnews"
  21. // MsgTypeText 文本
  22. MsgTypeText MsgType = "text"
  23. // MsgTypeVoice 语音/音频
  24. MsgTypeVoice MsgType = "voice"
  25. // MsgTypeImage 图片
  26. MsgTypeImage MsgType = "image"
  27. // MsgTypeVideo 视频
  28. MsgTypeVideo MsgType = "mpvideo"
  29. // MsgTypeWxCard 卡券
  30. MsgTypeWxCard MsgType = "wxcard"
  31. )
  32. // Broadcast 群发消息
  33. type Broadcast struct {
  34. *context.Context
  35. preview bool
  36. }
  37. // NewBroadcast new
  38. func NewBroadcast(ctx *context.Context) *Broadcast {
  39. return &Broadcast{ctx, false}
  40. }
  41. // User 发送的用户
  42. type User struct {
  43. TagID int64
  44. OpenID []string
  45. }
  46. // Result 群发返回结果
  47. type Result struct {
  48. util.CommonError
  49. MsgID int64 `json:"msg_id"`
  50. MsgDataID int64 `json:"msg_data_id"`
  51. MsgStatus string `json:"msg_status"`
  52. }
  53. // SpeedResult 群发速度返回结果
  54. type SpeedResult struct {
  55. util.CommonError
  56. Speed int64 `json:"speed"`
  57. RealSpeed int64 `json:"realspeed"`
  58. }
  59. // sendRequest 发送请求的数据
  60. type sendRequest struct {
  61. // 根据tag获全部发送
  62. Filter map[string]interface{} `json:"filter,omitempty"`
  63. // 根据OpenID发送
  64. ToUser interface{} `json:"touser,omitempty"`
  65. // 发送文本
  66. Text map[string]interface{} `json:"text,omitempty"`
  67. // 发送图文消息
  68. Mpnews map[string]interface{} `json:"mpnews,omitempty"`
  69. // 发送语音
  70. Voice map[string]interface{} `json:"voice,omitempty"`
  71. // 发送视频
  72. Mpvideo map[string]interface{} `json:"mpvideo,omitempty"`
  73. // 发送图片-预览使用
  74. Image map[string]interface{} `json:"image,omitempty"`
  75. // 发送图片
  76. Images *Image `json:"images,omitempty"`
  77. // 发送卡券
  78. WxCard map[string]interface{} `json:"wxcard,omitempty"`
  79. MsgType MsgType `json:"msgtype"`
  80. SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"`
  81. }
  82. // Image 发送图片
  83. type Image struct {
  84. MediaIDs []string `json:"media_ids"`
  85. Recommend string `json:"recommend"`
  86. NeedOpenComment int32 `json:"need_open_comment"`
  87. OnlyFansCanComment int32 `json:"only_fans_can_comment"`
  88. }
  89. // SendText 群发文本
  90. // user 为nil,表示全员发送
  91. // &User{TagID:2} 根据tag发送
  92. // &User{OpenID:[]string("xxx","xxx")} 根据openid发送
  93. func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
  94. ak, err := broadcast.GetAccessToken()
  95. if err != nil {
  96. return nil, err
  97. }
  98. req := &sendRequest{
  99. ToUser: nil,
  100. MsgType: MsgTypeText,
  101. }
  102. req.Text = map[string]interface{}{
  103. "content": content,
  104. }
  105. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  106. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  107. data, err := util.PostJSON(url, req)
  108. if err != nil {
  109. return nil, err
  110. }
  111. res := &Result{}
  112. err = util.DecodeWithError(data, res, "SendText")
  113. return res, err
  114. }
  115. // SendNews 发送图文
  116. func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
  117. ak, err := broadcast.GetAccessToken()
  118. if err != nil {
  119. return nil, err
  120. }
  121. req := &sendRequest{
  122. ToUser: nil,
  123. MsgType: MsgTypeNews,
  124. }
  125. if ignoreReprint {
  126. req.SendIgnoreReprint = 1
  127. }
  128. req.Mpnews = map[string]interface{}{
  129. "media_id": mediaID,
  130. }
  131. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  132. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  133. data, err := util.PostJSON(url, req)
  134. if err != nil {
  135. return nil, err
  136. }
  137. res := &Result{}
  138. err = util.DecodeWithError(data, res, "SendNews")
  139. return res, err
  140. }
  141. // SendVoice 发送语音
  142. func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
  143. ak, err := broadcast.GetAccessToken()
  144. if err != nil {
  145. return nil, err
  146. }
  147. req := &sendRequest{
  148. ToUser: nil,
  149. MsgType: MsgTypeVoice,
  150. }
  151. req.Voice = map[string]interface{}{
  152. "media_id": mediaID,
  153. }
  154. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  155. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  156. data, err := util.PostJSON(url, req)
  157. if err != nil {
  158. return nil, err
  159. }
  160. res := &Result{}
  161. err = util.DecodeWithError(data, res, "SendVoice")
  162. return res, err
  163. }
  164. // SendImage 发送图片
  165. func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
  166. ak, err := broadcast.GetAccessToken()
  167. if err != nil {
  168. return nil, err
  169. }
  170. req := &sendRequest{
  171. ToUser: nil,
  172. MsgType: MsgTypeImage,
  173. }
  174. if broadcast.preview {
  175. req.Image = map[string]interface{}{
  176. "media_id": images.MediaIDs[0],
  177. }
  178. } else {
  179. req.Images = images
  180. }
  181. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  182. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  183. data, err := util.PostJSON(url, req)
  184. if err != nil {
  185. return nil, err
  186. }
  187. res := &Result{}
  188. err = util.DecodeWithError(data, res, "SendImage")
  189. return res, err
  190. }
  191. // SendVideo 发送视频
  192. func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
  193. ak, err := broadcast.GetAccessToken()
  194. if err != nil {
  195. return nil, err
  196. }
  197. req := &sendRequest{
  198. ToUser: nil,
  199. MsgType: MsgTypeVideo,
  200. }
  201. req.Mpvideo = map[string]interface{}{
  202. "media_id": mediaID,
  203. "title": title,
  204. "description": description,
  205. }
  206. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  207. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  208. data, err := util.PostJSON(url, req)
  209. if err != nil {
  210. return nil, err
  211. }
  212. res := &Result{}
  213. err = util.DecodeWithError(data, res, "SendVideo")
  214. return res, err
  215. }
  216. // SendWxCard 发送卡券
  217. func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
  218. ak, err := broadcast.GetAccessToken()
  219. if err != nil {
  220. return nil, err
  221. }
  222. req := &sendRequest{
  223. ToUser: nil,
  224. MsgType: MsgTypeWxCard,
  225. }
  226. req.WxCard = map[string]interface{}{
  227. "card_id": cardID,
  228. }
  229. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  230. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  231. data, err := util.PostJSON(url, req)
  232. if err != nil {
  233. return nil, err
  234. }
  235. res := &Result{}
  236. err = util.DecodeWithError(data, res, "SendWxCard")
  237. return res, err
  238. }
  239. // Delete 删除群发消息
  240. func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
  241. ak, err := broadcast.GetAccessToken()
  242. if err != nil {
  243. return err
  244. }
  245. req := map[string]interface{}{
  246. "msg_id": msgID,
  247. "article_idx": articleIDx,
  248. }
  249. url := fmt.Sprintf("%s?access_token=%s", deleteSendURL, ak)
  250. data, err := util.PostJSON(url, req)
  251. if err != nil {
  252. return err
  253. }
  254. return util.DecodeWithCommonError(data, "Delete")
  255. }
  256. // Preview 预览
  257. func (broadcast *Broadcast) Preview() *Broadcast {
  258. broadcast.preview = true
  259. return broadcast
  260. }
  261. // GetMassStatus 获取群发状态
  262. func (broadcast *Broadcast) GetMassStatus(msgID string) (*Result, error) {
  263. ak, err := broadcast.GetAccessToken()
  264. if err != nil {
  265. return nil, err
  266. }
  267. req := map[string]interface{}{
  268. "msg_id": msgID,
  269. }
  270. url := fmt.Sprintf("%s?access_token=%s", massStatusSendURL, ak)
  271. data, err := util.PostJSON(url, req)
  272. if err != nil {
  273. return nil, err
  274. }
  275. res := &Result{}
  276. err = util.DecodeWithError(data, res, "GetMassStatus")
  277. return res, err
  278. }
  279. // GetSpeed 获取群发速度
  280. func (broadcast *Broadcast) GetSpeed() (*SpeedResult, error) {
  281. ak, err := broadcast.GetAccessToken()
  282. if err != nil {
  283. return nil, err
  284. }
  285. req := map[string]interface{}{}
  286. url := fmt.Sprintf("%s?access_token=%s", getSpeedSendURL, ak)
  287. data, err := util.PostJSON(url, req)
  288. if err != nil {
  289. return nil, err
  290. }
  291. res := &SpeedResult{}
  292. err = util.DecodeWithError(data, res, "GetSpeed")
  293. return res, err
  294. }
  295. // SetSpeed 设置群发速度
  296. func (broadcast *Broadcast) SetSpeed(speed int) (*SpeedResult, error) {
  297. ak, err := broadcast.GetAccessToken()
  298. if err != nil {
  299. return nil, err
  300. }
  301. req := map[string]interface{}{
  302. "speed": speed,
  303. }
  304. url := fmt.Sprintf("%s?access_token=%s", setSpeedSendURL, ak)
  305. data, err := util.PostJSON(url, req)
  306. if err != nil {
  307. return nil, err
  308. }
  309. res := &SpeedResult{}
  310. err = util.DecodeWithError(data, res, "SetSpeed")
  311. return res, err
  312. }
  313. func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret *sendRequest, url string) {
  314. sendURL := ""
  315. if user == nil {
  316. req.Filter = map[string]interface{}{
  317. "is_to_all": true,
  318. }
  319. sendURL = sendURLByTag
  320. } else {
  321. if broadcast.preview {
  322. // 预览 默认发给第一个用户
  323. if len(user.OpenID) != 0 {
  324. req.ToUser = user.OpenID[0]
  325. sendURL = previewSendURL
  326. }
  327. } else {
  328. if user.TagID != 0 {
  329. req.Filter = map[string]interface{}{
  330. "is_to_all": false,
  331. "tag_id": user.TagID,
  332. }
  333. sendURL = sendURLByTag
  334. }
  335. if len(user.OpenID) != 0 {
  336. req.ToUser = user.OpenID
  337. sendURL = sendURLByOpenID
  338. }
  339. }
  340. }
  341. return req, sendURL
  342. }