broadcast.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. Images *Image `json:"images,omitempty"`
  73. // 发送卡券
  74. WxCard map[string]interface{} `json:"wxcard,omitempty"`
  75. MsgType MsgType `json:"msgtype"`
  76. SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"`
  77. }
  78. // Image 发送图片
  79. type Image struct {
  80. MediaIDs []string `json:"media_ids"`
  81. Recommend string `json:"recommend"`
  82. NeedOpenComment int32 `json:"need_open_comment"`
  83. OnlyFansCanComment int32 `json:"only_fans_can_comment"`
  84. }
  85. // SendText 群发文本
  86. // user 为nil,表示全员发送
  87. // &User{TagID:2} 根据tag发送
  88. // &User{OpenID:[]string("xxx","xxx")} 根据openid发送
  89. func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
  90. ak, err := broadcast.GetAccessToken()
  91. if err != nil {
  92. return nil, err
  93. }
  94. req := &sendRequest{
  95. ToUser: nil,
  96. MsgType: MsgTypeText,
  97. }
  98. req.Text = map[string]interface{}{
  99. "content": content,
  100. }
  101. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  102. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  103. data, err := util.PostJSON(url, req)
  104. if err != nil {
  105. return nil, err
  106. }
  107. res := &Result{}
  108. err = util.DecodeWithError(data, res, "SendText")
  109. return res, err
  110. }
  111. // SendNews 发送图文
  112. func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
  113. ak, err := broadcast.GetAccessToken()
  114. if err != nil {
  115. return nil, err
  116. }
  117. req := &sendRequest{
  118. ToUser: nil,
  119. MsgType: MsgTypeNews,
  120. }
  121. if ignoreReprint {
  122. req.SendIgnoreReprint = 1
  123. }
  124. req.Mpnews = map[string]interface{}{
  125. "media_id": mediaID,
  126. }
  127. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  128. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  129. data, err := util.PostJSON(url, req)
  130. if err != nil {
  131. return nil, err
  132. }
  133. res := &Result{}
  134. err = util.DecodeWithError(data, res, "SendNews")
  135. return res, err
  136. }
  137. // SendVoice 发送语音
  138. func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
  139. ak, err := broadcast.GetAccessToken()
  140. if err != nil {
  141. return nil, err
  142. }
  143. req := &sendRequest{
  144. ToUser: nil,
  145. MsgType: MsgTypeVoice,
  146. }
  147. req.Voice = map[string]interface{}{
  148. "media_id": mediaID,
  149. }
  150. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  151. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  152. data, err := util.PostJSON(url, req)
  153. if err != nil {
  154. return nil, err
  155. }
  156. res := &Result{}
  157. err = util.DecodeWithError(data, res, "SendVoice")
  158. return res, err
  159. }
  160. // SendImage 发送图片
  161. func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
  162. ak, err := broadcast.GetAccessToken()
  163. if err != nil {
  164. return nil, err
  165. }
  166. req := &sendRequest{
  167. ToUser: nil,
  168. MsgType: MsgTypeImage,
  169. }
  170. req.Images = images
  171. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  172. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  173. data, err := util.PostJSON(url, req)
  174. if err != nil {
  175. return nil, err
  176. }
  177. res := &Result{}
  178. err = util.DecodeWithError(data, res, "SendImage")
  179. return res, err
  180. }
  181. // SendVideo 发送视频
  182. func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
  183. ak, err := broadcast.GetAccessToken()
  184. if err != nil {
  185. return nil, err
  186. }
  187. req := &sendRequest{
  188. ToUser: nil,
  189. MsgType: MsgTypeVideo,
  190. }
  191. req.Voice = map[string]interface{}{
  192. "media_id": mediaID,
  193. "title": title,
  194. "description": description,
  195. }
  196. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  197. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  198. data, err := util.PostJSON(url, req)
  199. if err != nil {
  200. return nil, err
  201. }
  202. res := &Result{}
  203. err = util.DecodeWithError(data, res, "SendVideo")
  204. return res, err
  205. }
  206. // SendWxCard 发送卡券
  207. func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
  208. ak, err := broadcast.GetAccessToken()
  209. if err != nil {
  210. return nil, err
  211. }
  212. req := &sendRequest{
  213. ToUser: nil,
  214. MsgType: MsgTypeWxCard,
  215. }
  216. req.WxCard = map[string]interface{}{
  217. "card_id": cardID,
  218. }
  219. req, sendURL := broadcast.chooseTagOrOpenID(user, req)
  220. url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
  221. data, err := util.PostJSON(url, req)
  222. if err != nil {
  223. return nil, err
  224. }
  225. res := &Result{}
  226. err = util.DecodeWithError(data, res, "SendWxCard")
  227. return res, err
  228. }
  229. // Delete 删除群发消息
  230. func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
  231. ak, err := broadcast.GetAccessToken()
  232. if err != nil {
  233. return err
  234. }
  235. req := map[string]interface{}{
  236. "msg_id": msgID,
  237. "article_idx": articleIDx,
  238. }
  239. url := fmt.Sprintf("%s?access_token=%s", deleteSendURL, ak)
  240. data, err := util.PostJSON(url, req)
  241. if err != nil {
  242. return err
  243. }
  244. return util.DecodeWithCommonError(data, "Delete")
  245. }
  246. // Preview 预览
  247. func (broadcast *Broadcast) Preview() *Broadcast {
  248. broadcast.preview = true
  249. return broadcast
  250. }
  251. // GetMassStatus 获取群发状态
  252. func (broadcast *Broadcast) GetMassStatus(msgID string) (*Result, error) {
  253. ak, err := broadcast.GetAccessToken()
  254. if err != nil {
  255. return nil, err
  256. }
  257. req := map[string]interface{}{
  258. "msg_id": msgID,
  259. }
  260. url := fmt.Sprintf("%s?access_token=%s", massStatusSendURL, ak)
  261. data, err := util.PostJSON(url, req)
  262. if err != nil {
  263. return nil, err
  264. }
  265. res := &Result{}
  266. err = util.DecodeWithError(data, res, "GetMassStatus")
  267. return res, err
  268. }
  269. // GetSpeed 获取群发速度
  270. func (broadcast *Broadcast) GetSpeed() (*SpeedResult, error) {
  271. ak, err := broadcast.GetAccessToken()
  272. if err != nil {
  273. return nil, err
  274. }
  275. req := map[string]interface{}{}
  276. url := fmt.Sprintf("%s?access_token=%s", getSpeedSendURL, ak)
  277. data, err := util.PostJSON(url, req)
  278. if err != nil {
  279. return nil, err
  280. }
  281. res := &SpeedResult{}
  282. err = util.DecodeWithError(data, res, "GetSpeed")
  283. return res, err
  284. }
  285. // SetSpeed 设置群发速度
  286. func (broadcast *Broadcast) SetSpeed(speed int) (*SpeedResult, error) {
  287. ak, err := broadcast.GetAccessToken()
  288. if err != nil {
  289. return nil, err
  290. }
  291. req := map[string]interface{}{
  292. "speed": speed,
  293. }
  294. url := fmt.Sprintf("%s?access_token=%s", setSpeedSendURL, ak)
  295. data, err := util.PostJSON(url, req)
  296. if err != nil {
  297. return nil, err
  298. }
  299. res := &SpeedResult{}
  300. err = util.DecodeWithError(data, res, "SetSpeed")
  301. return res, err
  302. }
  303. func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret *sendRequest, url string) {
  304. sendURL := ""
  305. if user == nil {
  306. req.Filter = map[string]interface{}{
  307. "is_to_all": true,
  308. }
  309. sendURL = sendURLByTag
  310. } else {
  311. if broadcast.preview {
  312. // 预览 默认发给第一个用户
  313. if len(user.OpenID) != 0 {
  314. req.ToUser = user.OpenID[0]
  315. sendURL = previewSendURL
  316. }
  317. } else {
  318. if user.TagID != 0 {
  319. req.Filter = map[string]interface{}{
  320. "is_to_all": false,
  321. "tag_id": user.TagID,
  322. }
  323. sendURL = sendURLByTag
  324. }
  325. if len(user.OpenID) != 0 {
  326. req.ToUser = user.OpenID
  327. sendURL = sendURLByOpenID
  328. }
  329. }
  330. }
  331. return req, sendURL
  332. }