/// <summary> /// 回复单图文消息 /// </summary> /// <param name="access_token">调用接口凭证</param> /// <param name="touser">普通用户openid</param> /// <param name="news"></param> /// <param name="kf_account">完整客服账号,格式为:账号前缀@公众号微信号</param> /// <returns></returns> public static bool RepayNews(string access_token, string touser, WeixinNews news, string kf_account = null) { var builder = new StringBuilder(); builder.Append("{") .Append('"' + "touser" + '"' + ":").Append('"' + touser + '"').Append(",") .Append('"' + "msgtype" + '"' + ":").Append('"' + "news" + '"').Append(",") .Append('"' + "news" + '"' + ":") .Append("{") .Append('"' + "articles" + '"' + ":") .Append("[") .Append("{") .Append('"' + "title" + '"' + ":").Append('"' + news.title + '"').Append(",") .Append('"' + "description" + '"' + ":").Append('"' + news.description + '"').Append(",") .Append('"' + "url" + '"' + ":").Append('"' + news.url + '"').Append(",") .Append('"' + "picurl" + '"' + ":").Append('"' + news.picurl + '"') .Append("}") .Append("]") .Append("}"); if (!string.IsNullOrEmpty(kf_account)) { builder.Append(","); builder.Append('"' + "customservice" + '"' + ":") .Append("{") .Append('"' + "kfaccount" + '"' + ":").Append('"' + kf_account + '"') .Append("}"); } builder.Append("}"); var client = new HttpClient(); return(client.PostAsync(string.Format("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={0}", access_token), new StringContent(builder.ToString())).Result.IsSuccessStatusCode); }
//发送客服消息 public ActionResult SendMessage(int id) { var reply = _message.GetById(id); if (reply == null || string.IsNullOrEmpty(reply.Openid)) { return(Json("数据异常或者openid为空")); } var config = _config.GetConfig(reply.WeiXinId); var openId = reply.Openid; var result = false; switch (Convert.ToInt32(reply.MsgType)) { case (int)WeiXinMessageTypeEnum.text: result = ReplayActiveMessageAPI.RepayText(config.AccessToken, openId, reply.Content); break; case (int)WeiXinMessageTypeEnum.image: result = ReplayActiveMessageAPI.RepayImage(config.AccessToken, openId, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.video: result = ReplayActiveMessageAPI.RepayVedio(config.AccessToken, openId, reply.MediaId, reply.ThumbMediaId, reply.Title, reply.Description); break; case (int)WeiXinMessageTypeEnum.voice: result = ReplayActiveMessageAPI.RepayVoice(config.AccessToken, openId, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.music: result = ReplayActiveMessageAPI.RepayMusic(config.AccessToken, openId, reply.Title, reply.Description, reply.MusicURL, reply.HQMusicUrl, reply.ThumbMediaId); break; case (int)WeiXinMessageTypeEnum.news: var weiXinNewList = new List <WeixinNews>(); var titles = reply.Title.Split(';'); var descriptions = reply.Description.Split(';'); var picurls = reply.PicUrl.Split(';'); var urls = reply.Url.Split(';'); for (int i = 0; i < reply.ArticleCount; i++) { var weiXinNew = new WeixinNews { title = titles[i], description = descriptions[i], picurl = picurls[i], url = urls[i] }; weiXinNewList.Add(weiXinNew); } result = ReplayActiveMessageAPI.RepayNews(config.AccessToken, openId, weiXinNewList); break; } return(Json(result)); }
/// <summary> /// 响应消息/事件 /// </summary> /// <param name="reply"></param> public string Repay(WeiXinReplyMessage reply, string openId, string myUserName) { var result = ""; if (reply == null) { result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, "客官,小二无言以对!!"); } else { switch (Convert.ToInt32(reply.MsgType)) { case (int)WeiXinMessageTypeEnum.text: result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, reply.Content); break; case (int)WeiXinMessageTypeEnum.image: result = ReplayPassiveMessageAPI.ReplayImage(openId, myUserName, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.video: result = ReplayPassiveMessageAPI.ReplayVedio(openId, myUserName, reply.MediaId, reply.Title, reply.Description); break; case (int)WeiXinMessageTypeEnum.voice: result = ReplayPassiveMessageAPI.ReplayVoice(openId, myUserName, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.music: result = ReplayPassiveMessageAPI.ReplayMusic(openId, myUserName, reply.Title, reply.Description, reply.MusicURL, reply.HQMusicUrl, reply.ThumbMediaId); break; case (int)WeiXinMessageTypeEnum.news: var weiXinNewList = new List <WeixinNews>(); var titles = reply.Title.Split(';'); var descriptions = reply.Description.Split(';'); var picurls = reply.PicUrl.Split(';'); var urls = reply.Url.Split(';'); for (int i = 0; i < reply.ArticleCount; i++) { var weiXinNew = new WeixinNews { title = titles[i], description = descriptions[i], picurl = picurls[i], url = urls[i] }; weiXinNewList.Add(weiXinNew); } result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, weiXinNewList); break; } } return(result); }
/// <summary> /// 回复单图文消息 /// </summary> /// <param name="toUserName"></param> /// <param name="fromUserName"></param> /// <param name="news"></param> /// <returns></returns> public static string RepayNews(string toUserName, string fromUserName, WeixinNews news) { var builder = new StringBuilder(); builder.Append(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName>" + "<FromUserName><![CDATA[{1}]]></FromUserName>" + "<CreateTime>{2}</CreateTime>" + "<MsgType><![CDATA[news]]></MsgType>" + "<ArticleCount>{3}</ArticleCount><Articles>", toUserName, fromUserName, CommonHelper.CreateTimestamp(), 1 )); builder.Append(string.Format("<item><Title><![CDATA[{0}]]></Title>" + "<Description><![CDATA[{1}]]></Description>" + "<PicUrl><![CDATA[{2}]]></PicUrl>" + "<Url><![CDATA[{3}]]></Url>" + "</item>", news.title, news.description, news.picurl, news.url )); builder.Append("</Articles></xml>"); return(builder.ToString()); }
/// <summary> /// 说明:带TODO字眼的代码段,需要开发者自行按照自己的业务逻辑实现 /// </summary> /// <param name="message"></param> /// <returns>已经打包成xml的用于回复用户的消息包</returns> public string Execute(WeixinMessage message) { var result = ""; var openId = message.Body.FromUserName.Value; var myUserName = message.Body.ToUserName.Value; //这里需要调用TokenHelper获取Token的,省略了。 #region 记录接收信息 var receiveMessage = new WeiXinReceiveMessage() { ToUserName = myUserName, FromUserName = openId, CreateDate = DateTime.Now, CreateTime = message.Body.CreateTime.Value, MsgType = message.Body.MsgType.Value, MsgId = message.Type == WeixinMessageType.Event ? "" : message.Body.MsgId.Value //事件没有这个参数 }; #endregion switch (message.Type) { case WeixinMessageType.Text: //文字消息 #region 文字消息 string userMessage = message.Body.Content.Value; receiveMessage.Content = userMessage; //用于记录接收信息 var reply = _replyMessage.GetMessage(userMessage); //查找回复信息表 if (reply == null) { result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, "客官,小二无言以对!!"); } else { switch (Convert.ToInt32(reply.MsgType)) { case (int)WeiXinMessageTypeEnum.text: result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, reply.Content); break; case (int)WeiXinMessageTypeEnum.image: result = ReplayPassiveMessageAPI.ReplayImage(openId, myUserName, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.video: result = ReplayPassiveMessageAPI.ReplayVedio(openId, myUserName, reply.MediaId, reply.Title, reply.Description); break; case (int)WeiXinMessageTypeEnum.voice: result = ReplayPassiveMessageAPI.ReplayVoice(openId, myUserName, reply.MediaId); break; case (int)WeiXinMessageTypeEnum.music: result = ReplayPassiveMessageAPI.ReplayMusic(openId, myUserName, reply.Title, reply.Description, reply.MusicURL, reply.HQMusicUrl, reply.ThumbMediaId); break; case (int)WeiXinMessageTypeEnum.news: var weiXinNewList = new List <WeixinNews>(); var titles = reply.Title.Split(';'); var descriptions = reply.Description.Split(';'); var picurls = reply.PicUrl.Split(';'); var urls = reply.Url.Split(';'); for (int i = 0; i < reply.ArticleCount; i++) { var weiXinNew = new WeixinNews { title = titles[i], description = descriptions[i], picurl = picurls[i], url = urls[i] }; weiXinNewList.Add(weiXinNew); } result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, weiXinNewList); break; } } #endregion break; case WeixinMessageType.Image: //图片消息 #region 图片消息 string imageUrl = message.Body.PicUrl.Value; //图片地址 string mediaId = message.Body.MediaId.Value; //mediaId receiveMessage.PicUrl = imageUrl; receiveMessage.MediaId = mediaId; #endregion break; case WeixinMessageType.Video: //视频消息 #region 视频消息 { var media_id = message.Body.MediaId.Value.ToString(); var thumb_media_id = message.Body.ThumbMediaId.Value.ToString(); receiveMessage.MediaId = media_id; receiveMessage.ThumbMediaId = thumb_media_id; } #endregion break; case WeixinMessageType.Voice: //语音消息 #region 语音消息 { var media_id = message.Body.MediaId.Value.ToString(); var format = message.Body.Format.Value.ToString(); receiveMessage.MediaId = media_id; receiveMessage.Format = format; } #endregion break; case WeixinMessageType.Location: //地理位置消息 #region 地理位置消息 { var location_X = message.Body.Location_X.Value.ToString(); var location_Y = message.Body.Location_Y.Value.ToString(); var scale = message.Body.Scale.Value.ToString(); var Label = message.Body.Label.Value.ToString(); receiveMessage.Location_X = location_X; receiveMessage.Location_Y = location_Y; receiveMessage.Scale = scale; receiveMessage.Label = Label; } #endregion break; case WeixinMessageType.Link: //链接消息 #region 链接消息 { var title = message.Body.Title.Value.ToString(); var description = message.Body.Description.Value.ToString(); var url = message.Body.Url.Value.ToString(); receiveMessage.Title = title; receiveMessage.Description = description; receiveMessage.Url = url; } #endregion break; case WeixinMessageType.Event: #region 事件 string eventType = message.Body.Event.Value.ToLower(); string eventKey = string.Empty; try { eventKey = message.Body.EventKey.Value; } catch { } receiveMessage.EventType = eventType; receiveMessage.EventKey = eventKey; switch (eventType) { case "subscribe": //用户未关注时,进行关注后的事件推送 #region 首次关注 //TODO: 获取用户基本信息后,将用户信息存储在本地。 var weixinInfo = UserAdminAPI.GetInfo(AccountToken(), openId); //注意:订阅号没有此权限 _userTask.AddUser(EntityMapper.Map <dynamic, WeiXinUser>(weixinInfo)); //保存用户关注数据 if (!string.IsNullOrEmpty(eventKey)) { var qrscene = eventKey.Replace("qrscene_", ""); //此为场景二维码的场景值 result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new WeixinNews { title = "欢迎订阅,场景值:" + qrscene, description = "欢迎订阅,场景值:" + qrscene, picurl = string.Format("{0}/ad.jpg", domain), url = domain }); } else { result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new WeixinNews { title = "欢迎订阅", description = "欢迎订阅,点击此消息查看在线demo", picurl = string.Format("{0}/ad.jpg", domain), url = domain }); } #endregion break; case "unsubscribe": //取消关注 #region 取消关注 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, "欢迎再来"); #endregion break; case "scan": // 用户已关注时的事件推送 #region 已关注扫码事件 if (!string.IsNullOrEmpty(eventKey)) { var qrscene = eventKey.Replace("qrscene_", ""); //此为场景二维码的场景值 result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new WeixinNews { title = "欢迎使用,场景值:" + qrscene, description = "欢迎使用,场景值:" + qrscene, picurl = string.Format("{0}/ad.jpg", domain), url = domain }); } else { result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new WeixinNews { title = "欢迎使用", description = "欢迎订阅,点击此消息查看在线demo", picurl = string.Format("{0}/ad.jpg", domain), url = domain }); } #endregion break; case "masssendjobfinish": //事件推送群发结果, #region 事件推送群发结果 { var msgId = message.Body.MsgID.Value; var msgStatus = message.Body.Status.Value; //“send success”或“send fail”或“err(num)” //send success时,也有可能因用户拒收公众号的消息、系统错误等原因造成少量用户接收失败。 //err(num)是审核失败的具体原因,可能的情况如下:err(10001)涉嫌广告, err(20001)涉嫌政治, err(20004)涉嫌社会, err(20002)涉嫌色情, err(20006)涉嫌违法犯罪, //err(20008)涉嫌欺诈, err(20013)涉嫌版权, err(22000)涉嫌互推(互相宣传), err(21000)涉嫌其他 var totalCount = message.Body.TotalCount.Value; //group_id下粉丝数;或者openid_list中的粉丝数 var filterCount = message.Body.FilterCount.Value; //过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,FilterCount = SentCount + ErrorCount var sentCount = message.Body.SentCount.Value; //发送成功的粉丝数 var errorCount = message.Body.FilterCount.Value; //发送失败的粉丝数 //TODO:开发者自己的处理逻辑,这里用log4net记录日志 // LogWriter.Default.WriteInfo(string.Format("mass send job finishe,msgId:{0},msgStatus:{1},totalCount:{2},filterCount:{3},sentCount:{4},errorCount:{5}", msgId, msgStatus, totalCount, filterCount, sentCount, errorCount)); } #endregion break; case "templatesendjobfinish": //模版消息结果, #region 模版消息结果 { var msgId = message.Body.MsgID.Value; var msgStatus = message.Body.Status.Value; //发送状态为成功: success; 用户拒绝接收:failed:user block; 发送状态为发送失败(非用户拒绝):failed: system failed //TODO:开发者自己的处理逻辑,这里用log4net记录日志 //LogWriter.Default.WriteInfo(string.Format("template send job finish,msgId:{0},msgStatus:{1}", msgId, msgStatus)); } #endregion break; case "location": //上报地理位置事件 #region 报地理位置事件 var lat = message.Body.Latitude.Value.ToString(); var lng = message.Body.Longitude.Value.ToString(); var pcn = message.Body.Precision.Value.ToString(); //TODO:在此处将经纬度记录在数据库,这里用log4net记录日志 // LogWriter.Default.WriteInfo(string.Format("openid:{0} ,location,lat:{1},lng:{2},pcn:{3}", openId, lat, lng, pcn)); #endregion break; case "voice": //语音消息 #region 语音消息 //A:已开通语音识别权限的公众号 var userVoice = message.Body.Recognition.Value; //用户语音消息文字 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, "您说:" + userVoice); //B:未开通语音识别权限的公众号 var userVoiceMediaId = message.Body.MediaId.Value; //media_id //TODO:调用自定义的语音识别程序识别用户语义 #endregion break; case "image": //图片消息 #region 图片消息 var userImage = message.Body.PicUrl.Value; //用户语音消息文字 result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new WeixinNews { title = "您刚才发送了图片消息", picurl = string.Format("{0}/Images/ad.jpg", domain), description = "点击查看图片", url = userImage }); #endregion break; case "click": //自定义菜单事件 #region 自定义菜单事件 { switch (eventKey) { case "myaccount": //CLICK类型事件举例 #region 我的账户 result = ReplayPassiveMessageAPI.RepayNews(openId, myUserName, new List <WeixinNews>() { new WeixinNews { title = "我的帐户", url = string.Format("{0}/user?openId={1}", domain, openId), description = "点击查看帐户详情", picurl = string.Format("{0}/Images/ad.jpg", domain) }, }); #endregion break; case "www.weixinsdk.net": //VIEW类型事件举例,注意:点击菜单弹出子菜单,不会产生上报。 //TODO:后台处理逻辑 break; default: result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, "没有响应菜单事件"); break; } } #endregion break; case "view": //点击菜单跳转链接时的事件推送 #region 点击菜单跳转链接时的事件推送 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("您将跳转至:{0}", eventKey)); #endregion break; case "scancode_push": //扫码推事件的事件推送 { var scanType = message.Body.ScanCodeInfo.ScanType.Value; //扫描类型,一般是qrcode var scanResult = message.Body.ScanCodeInfo.ScanResult.Value; //扫描结果,即二维码对应的字符串信息 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("您扫描了二维码,scanType:{0},scanResult:{1},EventKey:{2}", scanType, scanResult, eventKey)); } break; case "scancode_waitmsg": //扫码推事件且弹出“消息接收中”提示框的事件推送 { var scanType = message.Body.ScanCodeInfo.ScanType.Value; //扫描类型,一般是qrcode var scanResult = message.Body.ScanCodeInfo.ScanResult.Value; //扫描结果,即二维码对应的字符串信息 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("您扫描了二维码,scanType:{0},scanResult:{1},EventKey:{2}", scanType, scanResult, eventKey)); } break; case "pic_sysphoto": //弹出系统拍照发图的事件推送 { var count = message.Body.SendPicsInfo.Count; //发送的图片数量 var picList = message.Body.PicList; //发送的图片信息 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("弹出系统拍照发图,count:{0},EventKey:{1}", count, eventKey)); } break; case "pic_photo_or_album": //弹出拍照或者相册发图的事件推送 { var count = message.Body.SendPicsInfo.Count.Value; //发送的图片数量 var picList = message.Body.PicList.Value; //发送的图片信息 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("弹出拍照或者相册发图,count:{0},EventKey:{1}", count, eventKey)); } break; case "pic_weixin": //弹出微信相册发图器的事件推送 { var count = message.Body.SendPicsInfo.Count.Value; //发送的图片数量 var picList = message.Body.PicList.Value; //发送的图片信息 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("弹出微信相册发图器,count:{0},EventKey:{1}", count, eventKey)); } break; case "location_select": //弹出地理位置选择器的事件推送 { var location_X = message.Body.SendLocationInfo.Location_X.Value; //X坐标信息 var location_Y = message.Body.SendLocationInfo.Location_Y.Value; //Y坐标信息 var scale = message.Body.SendLocationInfo.Scale.Value; //精度,可理解为精度或者比例尺、越精细的话 scale越高 var label = message.Body.SendLocationInfo.Label.Value; //地理位置的字符串信息 var poiname = message.Body.SendLocationInfo.Poiname.Value; //朋友圈POI的名字,可能为空 result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("弹出地理位置选择器,location_X:{0},location_Y:{1},scale:{2},label:{3},poiname:{4},eventKey:{5}", location_X, location_Y, scale, label, poiname, eventKey)); } break; case "card_pass_check": //生成的卡券通过审核时,微信会把这个事件推送到开发者填写的URL。 { var cardid = message.Body.CardId.Value; //CardId result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("您的卡券已经通过审核")); } break; case "card_not_pass_check": //生成的卡券未通过审核时,微信会把这个事件推送到开发者填写的URL。 { var cardid = message.Body.CardId.Value; //CardId } break; case "user_get_card": //用户在领取卡券时,微信会把这个事件推送到开发者填写的URL。 { var cardid = message.Body.CardId.Value; //CardId var isGiveByFriend = message.Body.IsGiveByFriend.Value; //是否为转赠,1代表是,0代表否。 var fromUserName = message.Body.FromUserName.Value; //领券方帐号(一个OpenID) var friendUserName = message.Body.FriendUserName.Value; //赠送方账号(一个OpenID),"IsGiveByFriend”为1时填写该参数。 var userCardCode = message.Body.UserCardCode.Value; //code序列号。自定义code及非自定义code的卡券被领取后都支持事件推送。 var outerId = message.Body.OuterId.Value; //领取场景值,用于领取渠道数据统计。可在生成二维码接口及添加JSAPI接口中自定义该字段的整型值。 } break; case "user_del_card": //用户在删除卡券时,微信会把这个事件推送到开发者填写的URL { var cardid = message.Body.CardId.Value; //CardId var userCardCode = message.Body.UserCardCode.Value; //商户自定义code值。非自定code推送为空 } break; case "merchant_order": //微信小店:订单付款通知:在用户在微信中付款成功后,微信服务器会将订单付款通知推送到开发者在公众平台网站中设置的回调URL(在开发模式中设置)中,如未设置回调URL,则获取不到该事件推送。 { var orderId = message.Body.OrderId.Value; //CardId var orderStatus = message.Body.OrderStatus.Value; //OrderStatus var productId = message.Body.ProductId.Value; //ProductId var skuInfo = message.Body.SkuInfo.Value; //SkuInfo } break; } #endregion break; default: result = ReplayPassiveMessageAPI.RepayText(openId, myUserName, string.Format("未处理消息类型:{0}", message.Type)); break; } _receiveMessage.AddMessage(receiveMessage); return(result); }