package broadcast import ( "fmt" "github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/util" ) const ( sendURLByTag = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall" sendURLByOpenID = "https://api.weixin.qq.com/cgi-bin/message/mass/send" deleteSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/delete" previewSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/preview" massStatusSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/get" getSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get" setSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set" ) // MsgType 发送消息类型 type MsgType string const ( // MsgTypeNews 图文消息 MsgTypeNews MsgType = "mpnews" // MsgTypeText 文本 MsgTypeText MsgType = "text" // MsgTypeVoice 语音/音频 MsgTypeVoice MsgType = "voice" // MsgTypeImage 图片 MsgTypeImage MsgType = "image" // MsgTypeVideo 视频 MsgTypeVideo MsgType = "mpvideo" // MsgTypeWxCard 卡券 MsgTypeWxCard MsgType = "wxcard" ) // Broadcast 群发消息 type Broadcast struct { *context.Context preview bool } // NewBroadcast new func NewBroadcast(ctx *context.Context) *Broadcast { return &Broadcast{ctx, false} } // User 发送的用户 type User struct { TagID int64 OpenID []string } // Result 群发返回结果 type Result struct { util.CommonError MsgID int64 `json:"msg_id"` MsgDataID int64 `json:"msg_data_id"` MsgStatus string `json:"msg_status"` } // SpeedResult 群发速度返回结果 type SpeedResult struct { util.CommonError Speed int64 `json:"speed"` RealSpeed int64 `json:"realspeed"` } // sendRequest 发送请求的数据 type sendRequest struct { // 根据tag获全部发送 Filter map[string]interface{} `json:"filter,omitempty"` // 根据OpenID发送 ToUser interface{} `json:"touser,omitempty"` // 发送文本 Text map[string]interface{} `json:"text,omitempty"` // 发送图文消息 Mpnews map[string]interface{} `json:"mpnews,omitempty"` // 发送语音 Voice map[string]interface{} `json:"voice,omitempty"` // 发送图片 Images *Image `json:"images,omitempty"` // 发送卡券 WxCard map[string]interface{} `json:"wxcard,omitempty"` MsgType MsgType `json:"msgtype"` SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"` } // Image 发送图片 type Image struct { MediaIDs []string `json:"media_ids"` Recommend string `json:"recommend"` NeedOpenComment int32 `json:"need_open_comment"` OnlyFansCanComment int32 `json:"only_fans_can_comment"` } // SendText 群发文本 // user 为nil,表示全员发送 // &User{TagID:2} 根据tag发送 // &User{OpenID:[]string("xxx","xxx")} 根据openid发送 func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeText, } req.Text = map[string]interface{}{ "content": content, } req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendText") return res, err } // SendNews 发送图文 func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeNews, } if ignoreReprint { req.SendIgnoreReprint = 1 } req.Mpnews = map[string]interface{}{ "media_id": mediaID, } req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendNews") return res, err } // SendVoice 发送语音 func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeVoice, } req.Voice = map[string]interface{}{ "media_id": mediaID, } req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendVoice") return res, err } // SendImage 发送图片 func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeImage, } req.Images = images req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendImage") return res, err } // SendVideo 发送视频 func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeVideo, } req.Voice = map[string]interface{}{ "media_id": mediaID, "title": title, "description": description, } req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendVideo") return res, err } // SendWxCard 发送卡券 func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := &sendRequest{ ToUser: nil, MsgType: MsgTypeWxCard, } req.WxCard = map[string]interface{}{ "card_id": cardID, } req, sendURL := broadcast.chooseTagOrOpenID(user, req) url := fmt.Sprintf("%s?access_token=%s", sendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "SendWxCard") return res, err } // Delete 删除群发消息 func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error { ak, err := broadcast.GetAccessToken() if err != nil { return err } req := map[string]interface{}{ "msg_id": msgID, "article_idx": articleIDx, } url := fmt.Sprintf("%s?access_token=%s", deleteSendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return err } return util.DecodeWithCommonError(data, "Delete") } // Preview 预览 func (broadcast *Broadcast) Preview() *Broadcast { broadcast.preview = true return broadcast } // GetMassStatus 获取群发状态 func (broadcast *Broadcast) GetMassStatus(msgID string) (*Result, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := map[string]interface{}{ "msg_id": msgID, } url := fmt.Sprintf("%s?access_token=%s", massStatusSendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &Result{} err = util.DecodeWithError(data, res, "GetMassStatus") return res, err } // GetSpeed 获取群发速度 func (broadcast *Broadcast) GetSpeed() (*SpeedResult, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := map[string]interface{}{} url := fmt.Sprintf("%s?access_token=%s", getSpeedSendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &SpeedResult{} err = util.DecodeWithError(data, res, "GetSpeed") return res, err } // SetSpeed 设置群发速度 func (broadcast *Broadcast) SetSpeed(speed int) (*SpeedResult, error) { ak, err := broadcast.GetAccessToken() if err != nil { return nil, err } req := map[string]interface{}{ "speed": speed, } url := fmt.Sprintf("%s?access_token=%s", setSpeedSendURL, ak) data, err := util.PostJSON(url, req) if err != nil { return nil, err } res := &SpeedResult{} err = util.DecodeWithError(data, res, "SetSpeed") return res, err } func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret *sendRequest, url string) { sendURL := "" if user == nil { req.Filter = map[string]interface{}{ "is_to_all": true, } sendURL = sendURLByTag } else { if broadcast.preview { // 预览 默认发给第一个用户 if len(user.OpenID) != 0 { req.ToUser = user.OpenID[0] sendURL = previewSendURL } } else { if user.TagID != 0 { req.Filter = map[string]interface{}{ "is_to_all": false, "tag_id": user.TagID, } sendURL = sendURLByTag } if len(user.OpenID) != 0 { req.ToUser = user.OpenID sendURL = sendURLByOpenID } } } return req, sendURL }