一、回复图文消息
表MessageReceive新增一条数据
INSERT INTO "MessageReceive" VALUES (4, 2, '图片', 'https://pic.cnblogs.com/avatar/668465/20210318093258.png');
IMessageService定义回复图文的接口
/// <summary>
/// 处理文字消息,包括回复图片
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveDbRequestAsync(RequestMessageText requestMessage);
实现接口,按理说图片和图文应该是分开来的,这里我仅仅是演示就直接把他们归为一类,实际肯定是要分开的。
public async Task<IResponseMessageBase> OnReceiveDbRequestAsync(RequestMessageText requestMessage)
{
var receives = await DbContext.Db.Queryable<MessageReceive>().ToListAsync();//获取列表
var receive = receives.Where(it => it.KeyWords == requestMessage.Content).FirstOrDefault();//查找关键字是否存在
if (receive != null)
{
switch (receive.ReceiveType)
{
case ReceiveType.文字:
var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseText.Content = receive.ReceiveString;
return responseText;
case ReceiveType.图片:
var responseImage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
responseImage.Articles.Add(new Article()
{
Title = "欢迎",
Description = "这是一张图片消息",
PicUrl = receive.ReceiveString,
Url = "https://www.cnblogs.com/huguodong/"
});
return responseImage;
default:
var responseOther = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseOther.Content = receive.ReceiveString;
return responseOther;
}
}
else
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
//如果关键字搜不到,列出关键字
var result = new StringBuilder();
result.AppendFormat("听不懂你再说什么,可以试试下面的关键字\r\n");
for (int i = 0; i < receives.Count; i++)
{
result.AppendFormat($"{i + 1}:{receives[i].KeyWords}\r\n");
}
responseMessage.Content = result.ToString();
return responseMessage;
}
}
修改CustomMessageHandler的OnTextRequestAsync
var result = await _messageService.OnReceiveDbRequestAsync(requestMessage);
发布到服务器,查看效果,没毛病
二、图片消息处理与回复
IMessageService定义接口
/// <summary>
/// 处理图片
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveImageRequestAsync(RequestMessageImage requestMessage);
实现接口,这里传什么图片我就返回什么图片
public async Task<IResponseMessageBase> OnReceiveImageRequestAsync(RequestMessageImage requestMessage)
{
var responseImage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageImage>(requestMessage);
responseImage.Image.MediaId = requestMessage.MediaId;
return responseImage;
}
CustomMessageHandler重写OnImageRequestAsync方法
/// <summary>
/// 处理图片
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnImageRequestAsync(RequestMessageImage requestMessage)
{
var result = await _messageService.OnReceiveImageRequestAsync(requestMessage);
return result;
}
发布到云服务器,看看效果,没毛病
三、语音消息的处理与回复
首先去公众号官网设置接口权限开通语音识别
IMessageService定义接口
/// <summary>
/// 处理语音消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveVoiceRequestAsync(RequestMessageVoice requestMessage);
实现接口
public async Task<IResponseMessageBase> OnReceiveVoiceRequestAsync(RequestMessageVoice requestMessage)
{
var recognition = requestMessage.Recognition;//文字识别结果
Console.WriteLine($"文字识别结果:{recognition}");
if (string.IsNullOrEmpty(recognition))
{
var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseText.Content = "抱歉,听不清你在说啥!";
return responseText;
}
else
{
var responseVoice = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVoice>(requestMessage);
responseVoice.Voice.MediaId = requestMessage.MediaId;
return responseVoice;
}
}
CustomMessageHandler重写OnVoiceRequestAsync
/// <summary>
/// 处理音频
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnVoiceRequestAsync(RequestMessageVoice requestMessage)
{
var result = await _messageService.OnReceiveVoiceRequestAsync(requestMessage);
return result;
}
上传服务器,测试,没毛病
四、视频消息的处理与回复
IMessageService定义接口
/// <summary>
/// 处理视频消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage);
实现接口
public async Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage)
{
var responseVideo = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVideo>(requestMessage);
responseVideo.Video.Title = "这是您刚才发送的视频";
responseVideo.Video.Description = "这是一条视频消息";
responseVideo.Video.MediaId = requestMessage.MediaId;
return responseVideo;
}
CustomMessageHandler重写OnVideoRequestAsync方法
/// <summary>
/// 处理视频消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnVideoRequestAsync(RequestMessageVideo requestMessage)
{
var result = await _messageService.OnReceiveVideoRequestAsync(requestMessage);
return result;
}
上传云服务器,查看效果
好像直接返回视频会报错,上网查了下,要通过接口上传到素材库再转发给用户才行,这是盛派SDK的Demo中写的,他是上传到素材库然后通过客服消息发给用户
修改MessageService里的代码
public async Task<IResponseMessageBase> OnReceiveVideoRequestAsync(RequestMessageVideo requestMessage)
{
var responseVideo = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageVideo>(requestMessage);
//responseVideo.Video.Title = "这是您刚才发送的视频";
//responseVideo.Video.Description = "这是一条视频消息";
//responseVideo.Video.MediaId = requestMessage.MediaId;
//上传素材
var dir = ServerUtility.ContentRootMapPath("~/App_Data/TempVideo/");
var file = await MediaApi.GetAsync(appId, requestMessage.MediaId, dir);
var uploadResult = await MediaApi.UploadTemporaryMediaAsync(appId, UploadMediaFileType.video, file, 5000);
responseVideo.Video.Title = "这是您刚才发送的视频";
responseVideo.Video.Description = "这是一条视频消息";
responseVideo.Video.MediaId = uploadResult.media_id;
return responseVideo;
}
上传云服务器,发现报错了,原来是没有access_token
我们在startup.cs中注册一下方法
继续发到服务器,发现又报错了
触发了weixin异常日志,之前我们没有打印日志消息,所以看不到错误内容,现在我们改一下
//当发生基于WeixinException的异常时触发
WeixinTrace.OnWeixinExceptionFunc = async ex =>
{
//加入每次触发WeixinExceptionLog后需要执行的代码
System.Console.WriteLine(ex.Message);
System.Console.WriteLine(ex.InnerException);
};
重新发布,看到报错是没有加到白名单,不给上传
设置白名单之后,发现报错了,原因可能是上传素材和回复视频不能同时操作,在demo中他是通过客服机器人回复的
这里因为我是用的个人公众号没有客服功能,所以我试着先上传临时素材,拿到MediaId之后在接口里写死。首先在WeiXinApi.Application->Services文件夹下新建Api接口MaterialService
这里考虑到MaterialService和WeiXinService都是动态API并且都要用到Appid等属性,所有就可以把通用的东西提取出来成为BaseService,然后service只要继承就行了,在services文件夹下新建BaseService
namespace WeiXinApi.Application.Services
{
public class BaseService : IDynamicApiController
{
public static readonly string Token = Config.SenparcWeixinSetting.MpSetting.Token;//与微信公众账号后台的Token设置保持一致,区分大小写。
public static readonly string EncodingAESKey = Config.SenparcWeixinSetting.MpSetting.EncodingAESKey;//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。
public static readonly string AppId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。
}
}
WeiXinService改成继承BaseService
MaterialService也继承BaseService,并新增加上传临时文件接口
namespace WeiXinApi.Application.Services
{
public class MaterialService : BaseService
{
/// <summary>
/// 上传临时素材
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/upload/temp")]
public async Task<dynamic> UploadTemp([FromForm] MaterialAddInput input)
{
var file = await GetPath(input.File);
var uploadResult = await MediaApi.UploadTemporaryMediaAsync(AppId, input.UploadMediaFileType.Value, file, 50000);
return uploadResult.media_id;
}
/// <summary>
/// 保存文件到本地
/// </summary>
/// <param name="file"></param>
/// <param name="isTemp"></param>
/// <returns></returns>
[NonAction]//不是API
private async Task<string> GetPath(IFormFile file, bool isTemp = true)
{
var dir = isTemp ? "temp" : "permanent";
// 保存到网站根目录下的 uploads 目录
var savePath = Path.Combine(App.HostEnvironment.ContentRootPath, "uploads", dir);
if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath);
// 避免文件名重复,采用 GUID 生成
var filePath = Path.Combine(savePath, Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName)); // 可以替代为你需要存储的真实路径
using (var stream = System.IO.File.Create(filePath))
{
await file.CopyToAsync(stream);
}
return filePath;
}
}
}
这里的MaterialAddInput是一个入参实体类,按照规则放在Material文件下的Dto文件夹中
namespace WeiXinApi.Application.Services
{
public class MaterialAddInput
{
/// <summary>
/// 文件类型
/// </summary>
[Required(ErrorMessage = "文件类型不能为空")]
public UploadMediaFileType? UploadMediaFileType { get; set; }
/// <summary>
/// 文件
/// </summary>
[Required(ErrorMessage = "文件不存在")]
public IFormFile File { get; set; }
}
}
通过swagger上传素材
将获取到的id放到视频处理里面
发布服务器,测试一下,这下回复正常了。
五、小视频消息的处理
IMessageService写接口
/// <summary>
/// 处理小视频消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveShortVideoRequestAsync(RequestMessageShortVideo requestMessage);
MessageService实现接口,这里我就简答的回复文字
public async Task<IResponseMessageBase> OnReceiveShortVideoRequestAsync(RequestMessageShortVideo requestMessage)
{
var responseText = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseText.Content = "您刚才发送的是小视频";
return responseText;
}
CustomMessageHandler重写OnShortVideoRequestAsync
/// <summary>
/// 小视频消息处理
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnShortVideoRequestAsync(RequestMessageShortVideo requestMessage)
{
var result = await _messageService.OnReceiveShortVideoRequestAsync(requestMessage);
return result;
}
发布服务器测试一下,发现好像没法发小视频给微信公众号,也就没法测试,所以这里就不演示测试结果了,反正应该没问题
六、位置消息的处理与回复
IMessageService定义接口
/// <summary>
/// 处理位置信息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveLocationRequestAsync(RequestMessageLocation requestMessage);
实现接口,这里我直接用的官方demo里的,返回图文消息,不得不吐槽官方demo,经纬度都搞反了。。。。。。
public async Task<IResponseMessageBase> OnReceiveLocationRequestAsync(RequestMessageLocation requestMessage)
{
var responseNews = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
var markersList = new List<BaiduMarkers>();
markersList.Add(new BaiduMarkers()
{
Longitude = requestMessage.Location_Y,
Latitude = requestMessage.Location_X,
Color = "red",
Label = "S",
Size = BaiduMarkerSize.m
});
var mapUrl = BaiduMapHelper.GetBaiduStaticMap(requestMessage.Location_Y, requestMessage.Location_X, 1, 6, markersList);
responseNews.Articles.Add(new Article()
{
Description = string.Format("【来自百度地图】您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}",
requestMessage.Location_X, requestMessage.Location_Y,
requestMessage.Scale, requestMessage.Label),
PicUrl = mapUrl,
Title = "定位地点周边地图",
Url = mapUrl
});
return responseNews;
}
CustomMessageHandler重写OnLocationRequestAsync
/// <summary>
/// 处理位置信息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnLocationRequestAsync(RequestMessageLocation requestMessage)
{
var result = await _messageService.OnReceiveLocationRequestAsync(requestMessage);
return result;
}
发布到服务器,查看下效果,没毛病
七、链接消息的处理与回复
IMessageService定义接口
/// <summary>
/// 处理链接消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnReceiveLinkRequestAsync(RequestMessageLink requestMessage);
实现接口,这里我直接返回图文消息
public async Task<IResponseMessageBase> OnReceiveLinkRequestAsync(RequestMessageLink requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageNews>(requestMessage);
responseMessage.Articles.Add(new Article
{
Title = requestMessage.Title,
Description = requestMessage.Description,
PicUrl = "https://pic.cnblogs.com/avatar/668465/20210318093258.png",
Url = requestMessage.Url
});
return responseMessage;
}
CustomMessageHandler重写OnLinkRequestAsync
/// <summary>
/// 处理链接消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnLinkRequestAsync(RequestMessageLink requestMessage)
{
var result = await _messageService.OnReceiveLinkRequestAsync(requestMessage);
return result;
}
上传到服务器,测试一下,没毛病
八、本章Gitee地址链接