Verified Commit 12e7a5fb authored by Cui's avatar Cui

add sender

parent a3a23bc5
......@@ -7,9 +7,15 @@ package utils
import (
"crypto/rand"
"math"
"math/big"
r "math/rand"
)
func RandInt() int {
return int(math.Round(r.Float64() * 99999))
}
func RandInt64(min, max int64) int64 {
maxBigInt := big.NewInt(max)
i, _ := rand.Int(rand.Reader, maxBigInt)
......
/*
* @Author: cuiweiqiang
* @Date: 2019-01-15 15:06
*/
package utils
import (
"encoding/json"
"github.com/sirupsen/logrus"
"strconv"
"time"
)
const (
CUSTOMURI = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
MASSURI = "https://api.weixin.qq.com/cgi-bin/message/mass/send"
MASSPREVIEWURI = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"
TEMPLATEURI = "https://api.weixin.qq.com/cgi-bin/message/template/send"
SUBSCRIBEURI = "https://api.weixin.qq.com/cgi-bin/message/template/subscribe"
NationCode = "86"
SMSSendUri = "https://yun.tim.qq.com/v5/tlssmssvr/sendsms"
)
type WxResponse struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
Msgid float32 `json:"msgid"`
}
func (wr *WxResponse) Tofields() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["errcode"] = wr.Errcode
fields["errmsg"] = wr.Errmsg
fields["msgid"] = wr.Msgid
return
}
type CustomerMessageInterface interface {
GetToUser() interface{}
ToFieldsCustomerMessage() logrus.Fields
}
func CustomerMessageSender(accessToken string, cmi CustomerMessageInterface, preview bool) (int, string) {
log := Logger.WithFields(logrus.Fields{
"position": "messageSender.go",
"func": "CustomerMessageSender",
"accessToken": accessToken,
})
var sendUri string
switch v := cmi.GetToUser().(type) {
case string:
sendUri = CUSTOMURI
case []string:
sendUri = MASSURI
default:
log.Print("type " + v.(string) + " is not in list")
}
if preview {
sendUri = MASSPREVIEWURI
}
uri := sendUri + "?access_token=" + accessToken
data, _ := json.Marshal(cmi)
body, _, err := FasthttpPost(uri, data)
var result WxResponse
if err != nil {
log.
WithFields(cmi.ToFieldsCustomerMessage()).
Warn("send message to user fail")
result.Errcode = -100001
result.Errmsg = err.Error()
} else {
_ = json.Unmarshal(body, &result)
log.
WithFields(result.Tofields()).
WithFields(cmi.ToFieldsCustomerMessage()).
Info("send message to user")
}
return result.Errcode, result.Errmsg
}
func TemplateMessageSender(accessToken string, tm TemplateMessage) (int, string) {
log := Logger.WithFields(logrus.Fields{
"position": "messageSender.go",
"func": "TemplateMessageSender",
"accessToken": accessToken,
})
uri := TEMPLATEURI + "?access_token=" + accessToken
data, _ := json.Marshal(tm)
body, _, err := FasthttpPost(uri, data)
var result WxResponse
if err != nil {
log.
WithFields(tm.ToFields()).
Warn("send TemplateMessage to user fail")
result.Errcode = -100001
result.Errmsg = err.Error()
} else {
_ = json.Unmarshal(body, &result)
log.
WithFields(result.Tofields()).
WithFields(tm.ToFields()).
Info("send TemplateMessage to user")
}
return result.Errcode, result.Errmsg
}
func SubscribeMessageSender(accessToken string, sm SubscribeMessage) (int, string) {
log := Logger.WithFields(logrus.Fields{
"position": "messageSender.go",
"func": "SubscribeMessageSender",
"accessToken": accessToken,
})
uri := SUBSCRIBEURI + "?access_token=" + accessToken
data, _ := json.Marshal(sm)
body, _, err := FasthttpPost(uri, data)
var result WxResponse
if err != nil {
log.
WithFields(sm.ToFields()).
Warn("send SubscribeMessage to user fail")
result.Errcode = -100001
result.Errmsg = err.Error()
} else {
_ = json.Unmarshal(body, &result)
log.
WithFields(result.Tofields()).
WithFields(sm.ToFields()).
Info("send SubscribeMessage to user")
}
return result.Errcode, result.Errmsg
}
func SmsMessageSender(sm SmsMessage, appId int, appKey string) (int, string) {
log := Logger.WithFields(logrus.Fields{
"position": "messageSender.go",
"func": "SmsMessageSender",
"accessToken": appId,
})
randInt := RandInt()
uri := SMSSendUri + "?sdkappid=" + strconv.Itoa(appId) + "&random=" + strconv.Itoa(randInt)
sm.Sign = ""
sm.Ext = ""
sm.Extend = ""
sm.Time = time.Now().Unix()
sm.Sig = SmsDoSign(appKey, []string{sm.Tel.Mobile}, sm.Time, randInt)
sm.Tel.Nationcode = NationCode
reqBody, _ := json.Marshal(sm)
body, statusCode, err := FasthttpPost(uri, reqBody)
if err != nil {
log.
WithField("error", err).
Error("SmsMessageSender Error!")
} else {
log.
WithFields(sm.ToFields()).
WithField("response", string(body)).
Info("SmsMessageSender send success !")
}
return statusCode, ""
}
/*
* @Author: cuiweiqiang
* @Date: 2019-01-15 15:06
*/
package utils
import (
"encoding/json"
"github.com/sirupsen/logrus"
)
type TemplateMessage struct {
Touser string `json:"touser" bson:"touser"` // 接收者openid
TemplateId string `json:"template_id" bson:"template_id"` // 模板ID
Url string `json:"url" bson:"url"` // 模板跳转链接(海外帐号没有跳转能力)
Miniprogram struct {
Appid string `json:"appid" bson:"appid"`
Pagepath string `json:"pagepath" bson:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),暂不支持小游戏
} `json:"miniprogram" bson:"miniprogram"` // 跳小程序所需数据,不需跳小程序可不用传该数据
Data map[string]struct {
Value string `json:"value" bson:"value"`
Color string `json:"color" bson:"color"` // 模板内容字体颜色,不填默认为黑色
} `json:"data" bson:"data"` // 模板数据
}
func (tm *TemplateMessage) ToFields() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = tm.Touser
fields["template_id"] = tm.TemplateId
fields["url"] = tm.Url
fields["miniprogram.Appid"] = tm.Miniprogram.Appid
fields["miniprogram.Pagepath"] = tm.Miniprogram.Pagepath
data, _ := json.Marshal(tm.Data)
fields["data"] = data
return
}
type CustomerMessage struct {
Touser interface{} `json:"touser" bson:"touser"` // 接收者openid or openids[]
MsgType string `json:"msgtype" bson:"msgtype"` // enum: ['text', 'image', 'voice', 'video', 'music', 'news', 'mpnews', 'wxcard', 'miniprogrampage']
}
func (cm *CustomerMessage) GetToUser() interface{} {
return cm.Touser
}
type TextCustomerMessage struct {
CustomerMessage
Text struct {
Content string `json:"content" bson:"content"` // 文本消息内容
} `json:"text" bson:"text"` // 发送文本消息
}
func (tcm *TextCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = tcm.Touser
fields["msgtype"] = tcm.MsgType
fields["text.content"] = tcm.Text.Content
return
}
type ImageCustomerMessage struct {
CustomerMessage
Image struct {
MediaId string `json:"media_id" bson:"media_id"` // 发送的图片/语音/视频/图文消息(点击跳转到图文消息页)的媒体ID
} `json:"image" bson:"image"` // 发送图片消息
}
func (icm *ImageCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = icm.Touser
fields["msgtype"] = icm.MsgType
fields["image.media_id"] = icm.Image.MediaId
return
}
type VoiceCustomerMessage struct {
CustomerMessage
Voice struct {
MediaId string `json:"media_id" bson:"media_id"` // 发送的图片/语音/视频/图文消息(点击跳转到图文消息页)的媒体ID
} `json:"voice" bson:"voice"` // 发送语音消息
}
func (vcm *VoiceCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = vcm.Touser
fields["msgtype"] = vcm.MsgType
fields["voice.media_id"] = vcm.Voice.MediaId
return
}
type VideoCustomerMessage struct {
CustomerMessage
Video struct {
MediaId string `json:"media_id" bson:"media_id"` // 发送的图片/语音/视频/图文消息(点击跳转到图文消息页)的媒体ID
ThumbMediaId string `json:"thumb_media_id" bson:"thumb_media_id"` // 缩略图/小程序卡片图片的媒体ID,小程序卡片图片建议大小为520*416
Title string `json:"title" bson:"title"` // 图文消息/视频消息/音乐消息/小程序卡片的标题
Description string `json:"description" bson:"description"` // 图文消息/视频消息/音乐消息的描述
} `json:"video" bson:"video"` // 发送视频消息
}
func (vcm *VideoCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = vcm.Touser
fields["msgtype"] = vcm.MsgType
fields["video.media_id"] = vcm.Video.MediaId
fields["video.thumb_media_id"] = vcm.Video.ThumbMediaId
fields["video.title"] = vcm.Video.Title
fields["video.description"] = vcm.Video.Description
return
}
type MusicCustomerMessage struct {
CustomerMessage
Music struct {
Title string `json:"title" bson:"title"` // 图文消息/视频消息/音乐消息/小程序卡片的标题
Description string `json:"description" bson:"description"` // 图文消息/视频消息/音乐消息的描述
Musicurl string `json:"musicurl" bson:"musicurl"` // 音乐链接
Hqmusicurl string `json:"hqmusicurl" bson:"hqmusicurl"` // 高品质音乐链接,wifi环境优先使用该链接播放音乐
ThumbMediaId string `json:"thumb_media_id" bson:"thumb_media_id"` // 缩略图/小程序卡片图片的媒体ID,小程序卡片图片建议大小为520*416
} `json:"music" bson:"music"` // 发送音乐消息
}
func (mcm *MusicCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = mcm.Touser
fields["msgtype"] = mcm.MsgType
fields["music.title"] = mcm.Music.Title
fields["music.description"] = mcm.Music.Description
fields["music.musicurl"] = mcm.Music.Musicurl
fields["music.hqmusicurl"] = mcm.Music.Hqmusicurl
fields["music.thumb_media_id"] = mcm.Music.ThumbMediaId
return
}
type NewsCustomerMessage struct {
CustomerMessage
News struct {
Articles []struct {
Title string `json:"title" bson:"title"` // 图文消息/视频消息/音乐消息/小程序卡片的标题
Description string `json:"description" bson:"description"` // 图文消息/视频消息/音乐消息的描述
Url string `json:"url" bson:"url"` // 图文消息被点击后跳转的链接
Picurl string `json:"picurl" bson:"picurl"` // 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80
} `json:"articles" bson:"articles"`
} `json:"news" bson:"news"` // 发送图文消息(点击跳转到外链) 图文消息条数限制在1条以内,注意,如果图文数超过1,则将会返回错误码45008
}
func (ncm *NewsCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = ncm.Touser
fields["msgtype"] = ncm.MsgType
return
}
type MpnewsCustomerMessage struct {
CustomerMessage
Mpnews struct {
MediaId string `json:"media_id" bson:"media_id"`
} `json:"mpnews" bson:"mpnews"` // 发送图文消息(点击跳转到图文消息页面) 图文消息条数限制在1条以内,注意,如果图文数超过1,则将会返回错误码45008
}
func (mcm *MpnewsCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = mcm.Touser
fields["msgtype"] = mcm.MsgType
fields["mpnews.media_id"] = mcm.Mpnews.MediaId
return
}
type WxcardCustomerMessage struct {
CustomerMessage
Wxcard struct {
CardId string `json:"card_id" bson:"card_id"`
} `json:"wxcard" bson:"wxcard"` // 发送卡券
}
func (wcm *WxcardCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = wcm.Touser
fields["msgtype"] = wcm.MsgType
fields["wxcard.card_id"] = wcm.Wxcard.CardId
return
}
type MiniprogrampageCustomerMessage struct {
CustomerMessage
Miniprogrampage struct {
Title string `json:"title" bson:"title"` // 图文消息/视频消息/音乐消息/小程序卡片的标题
Appid string `json:"appid" bson:"appid"` // 小程序的appid,要求小程序的appid需要与公众号有关联关系
Pagepath string `json:"pagepath" bson:"pagepath"` // 小程序的页面路径,跟app.json对齐,支持参数,比如pages/index/index?foo=bar
ThumbMediaId string `json:"thumb_media_id" bson:"thumb_media_id"` // 缩略图/小程序卡片图片的媒体ID,小程序卡片图片建议大小为520*416
} `json:"miniprogrampage" bson:"miniprogrampage"` // 发送小程序卡片
}
func (mcm *MiniprogrampageCustomerMessage) ToFieldsCustomerMessage() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["touser"] = mcm.Touser
fields["msgtype"] = mcm.MsgType
fields["miniprogrampage.title"] = mcm.Miniprogrampage.Title
fields["miniprogrampage.appid"] = mcm.Miniprogrampage.Appid
fields["miniprogrampage.pagepath"] = mcm.Miniprogrampage.Pagepath
fields["miniprogrampage.thumb_media_id"] = mcm.Miniprogrampage.ThumbMediaId
return
}
// 订阅消息
type SubscribeMessage struct {
Appid string `json:"appid" bson:"appid"` // 所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,并且小程序要求是已发布的)
TemplateId string `json:"template_id" bson:"template_id"` // 订阅消息模板ID
Url string `json:"url" bson:"url"` // 点击消息跳转的链接,需要有ICP备案
Miniprogram string `json:"miniprogram" bson:"miniprogram"` // 跳小程序所需数据,不需跳小程序可不用传该数据
Pagepath string `json:"pagepath" bson:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),暂不支持小游戏
Scene string `json:"scene" bson:"scene"` // 订阅场景值
Title string `json:"title" bson:"title"` // 消息标题,15字以内
Data struct {
Content struct {
Value string `json:"value" bson:"value"`
Color string `json:"color" bson:"color"`
} `json:"content" bson:"content"`
} `json:"data" bson:"data"` // 消息正文,value为消息内容文本(200字以内),没有固定格式,可用\n换行,color为整段消息内容的字体颜色(目前仅支持整段消息为一种颜色)
}
func (subM *SubscribeMessage) ToFields() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["appid"] = subM.Appid
fields["template_id"] = subM.TemplateId
fields["url"] = subM.Url
fields["miniprogram"] = subM.Miniprogram
fields["pagepath"] = subM.Pagepath
fields["scene"] = subM.Scene
fields["title"] = subM.Title
fields["data.content"] = subM.Data.Content
return
}
type Tel struct {
Mobile string `json:"mobile"`
Nationcode string `json:"nationcode"`
}
// 短信消息
type SmsMessage struct {
Ext string `json:"ext"`
Extend string `json:"extend"`
Sig string `json:"sig"`
Sign string `json:"sign"`
Tel Tel `json:"tel"`
TplId string `json:"tpl_id" bson:"tpl_id"` // 短信模板 ID
Params []string `json:"params" bson:"params"` // 短信参数
Time int64 `json:"time"`
}
func (sm *SmsMessage) ToFields() (fields logrus.Fields) {
fields = make(logrus.Fields)
fields["ext"] = sm.Ext
fields["extend"] = sm.Extend
fields["sig"] = sm.Sig
fields["sign"] = sm.Sign
fields["tel"] = sm.Tel
fields["tpl_id"] = sm.TplId
fields["params"] = sm.Params
fields["time"] = sm.Time
return
}
......@@ -6,11 +6,14 @@
package utils
import (
"crypto/sha256"
"database/sql"
"encoding/hex"
"errors"
"github.com/sirupsen/logrus"
"io"
"reflect"
"strconv"
)
func IOClose(fio io.ReadCloser) {
......@@ -21,10 +24,35 @@ func IOClose(fio io.ReadCloser) {
err := fio.Close()
if err != nil {
log.WithField("error", err).Error("io.ReadCloser close error")
log.
WithField("error", err).
Error("io.ReadCloser close error")
}
}
func SmsDoSign(appKey string, tels []string, time int64, rand int) string {
var err error
h := sha256.New()
if len(tels) != 0 {
var telStr string
for _, v := range tels {
telStr = telStr + v + ","
}
telStr = telStr[:len(telStr)-1]
_, err = h.Write([]byte("appkey=" + appKey + "&random=" + strconv.Itoa(rand) + "&time=" + strconv.FormatInt(time, 10) + "&mobile=" + telStr))
if err != nil {
return ""
}
} else {
_, err = h.Write([]byte("appkey=" + appKey + "&random=" + strconv.Itoa(rand) + "&time=" + strconv.FormatInt(time, 10)))
if err != nil {
return ""
}
}
return hex.EncodeToString(h.Sum(nil))
}
// Deep Copy
func Copy(toValue interface{}, fromValue interface{}) (err error) {
var (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment