当前位置:网站首页>Asp . Text of automatic reply to NETCORE wechat subscription number
Asp . Text of automatic reply to NETCORE wechat subscription number
2022-07-02 00:00:00 【Lin yiconger】
file
Getting started
I'm just a code Porter ~
https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html
effect
Environmental Science
- .Net Core 6.0
- Subscription number 【 personal 】
- domain name 【 Keep on record 】 It may not be accessible without filing
- The server 【 Keep on record 】 It may not be accessible without filing
- Docker
- Nginx
- Frp
Tools
- Visual Studio 2022
technological process
Server configuration
Docker install https://www.cnblogs.com/linyisonger/p/13964825.html
docker-compose install https://www.cnblogs.com/linyisonger/p/13964931.html
Nginx install https://blog.csdn.net/linyisonger/article/details/123569798
Frp To configure https://blog.csdn.net/linyisonger/article/details/123567529
Nginx Configure forwarding to Frp Domain name , Realization IP Forward domain name reverse proxy .
server {
listen 80;
...
location /api/ {
proxy_pass http://a.frp.1998.ink;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
# proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
...
}
Project structures,
New projects
This is how I built it here , You can also use it Visual Studio Visual creation in .
dotnet new webapi --name AspNetCoreWeChatOAMessage
Add dependency
edit .csproj
file .
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
Encapsulate the official account class
newly build OfficialAccount.cs file , Used to encapsulate the type and method used by official account .
At present, many have not been realized , It mainly realizes text reply …
using Newtonsoft.Json;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
namespace AspNetCoreWeChatOAMessage
{
/// <summary>
/// official account
/// </summary>
public class OfficialAccount
{
/// <summary>
/// official account appId
/// </summary>
public string appid {
get; set; }
/// <summary>
/// official account appSecret
/// </summary>
public string secret {
get; set; }
/// <summary>
/// Must be in English or number , The length is 3-32 character .
/// What is? Token?https://mp.weixin.qq.com/wiki
/// </summary>
public string token {
get; set; }
/// <summary>
/// The message encryption key is provided by 43 Bit characters make up , It can be modified randomly , The character range is A-Z,a-z,0-9.
/// What is? EncodingAESKey?https://mp.weixin.qq.com/wiki
/// </summary>
public string encodingAESKey {
get; set; }
public OfficialAccount(string appid, string secret, string token, string encodingAESKey)
{
this.appid = appid;
this.secret = secret;
this.token = token;
this.encodingAESKey = encodingAESKey;
}
/// <summary>
/// Check the signature
/// </summary>
/// <param name="timestamp"> Time stamp </param>
/// <param name="nonce"></param>
/// <param name="signature"> Signature </param>
/// <returns></returns>
public bool CheckSignature(string timestamp, string nonce, string signature)
{
var list = new List<string>() {
token, timestamp, nonce };
list.Sort();
return SHA1Encryption(string.Join("", list)) == signature.ToUpper();
}
/// <summary>
/// access_token The official account is the only global interface invoke credential. , Official account calls for interfaces. access_token. Developers need to keep it properly .access_token At least keep the storage of 512 Character space .access_token Is currently valid for 2 Hours , Need to refresh regularly , Duplicate acquisition will result in the last acquired access_token invalid .
/// </summary>
/// <param name="grant_type"> Fill in client_credential</param>
/// <returns></returns>
public async Task<GetAccessTokenResult?> GetAccessToken(string grant_type = "client_credential")
{
var result = await Get($"https://api.weixin.qq.com/cgi-bin/token?grant_type={
grant_type}&appid={
appid}&secret={
secret}");
return JsonConvert.DeserializeObject<GetAccessTokenResult>(result);
}
public class GetAccessTokenResult
{
/// <summary>
/// Acquired credentials
/// </summary>
public string? access_token {
get; set; }
/// <summary>
/// Voucher effective time , Company : second . At present, it is 7200 Value in seconds .
/// </summary>
public long expires_in {
get; set; }
/// <summary>
/// Error code
/// </summary>
public long errcode {
get; set; }
/// <summary>
/// error message
/// </summary>
public string? errmsg {
get; set; }
}
/// <summary>
/// Receive normal messages
/// </summary>
public class ReceivingStandardMessages
{
/// <summary>
/// Message base class
/// </summary>
public class Message
{
/// <summary>
/// Developer WeChat ID
/// </summary>
public string? ToUserName {
get; set; }
/// <summary>
/// Sender account ( One OpenID)
/// </summary>
public string? FromUserName {
get; set; }
/// <summary>
/// Message creation time
/// </summary>
public long CreateTime {
get; set; }
/// <summary>
/// Message type , Text is text
/// </summary>
public string? MsgType {
get; set; }
/// <summary>
/// news id
/// </summary>
public long MsgId {
get; set; }
/// <summary>
/// The data of the message ID( Only when the message comes from the article )
/// </summary>
public long MsgDataId {
get; set; }
/// <summary>
/// Several articles when there are many pictures and texts , from 1 Start ( Only when the message comes from the article )
/// </summary>
public long Idx {
get; set; }
}
public class TextMessage : Message
{
/// <summary>
/// Text message content
/// </summary>
public string? Content {
get; set; }
}
public class ImageMessage : Message
{
/// <summary>
/// PicUrl Picture links ( Generated by system )
/// </summary>
public string? PicUrl {
get; set; }
/// <summary>
/// Picture message media id, Can call to get temporary material interface to pull data .
/// </summary>
public string? MediaId {
get; set; }
}
public class VoiceMessage : Message
{
/// <summary>
/// Voice message media id, Can call to get temporary material interface to pull data .
/// </summary>
public string? MediaId {
get; set; }
/// <summary>
/// phonetic matrix , Such as amr,speex etc.
/// </summary>
public string? Format {
get; set; }
/// <summary>
/// Speech recognition results ,UTF8 code
/// Please note that , After opening speech recognition , Every time a user sends voice to the official account, , Voice message pushed by wechat XML In the packet , Add one more Recognition Field ( notes : Due to client cache , Developer turns voice recognition on or off , Effective immediately for new followers , For users who have paid attention to their needs 24 Effective hours . Developers can refocus on this account for testing ).
/// </summary>
public string? Recognition {
get; set; }
}
public class VideoMessage : Message
{
/// <summary>
/// Video message media id, Can call to get temporary material interface to pull data .
/// </summary>
public string? MediaId {
get; set; }
/// <summary>
/// Media for video message thumbnails id, You can call the multimedia file download interface to pull data .
/// </summary>
public string? ThumbMediaId {
get; set; }
}
public class ShortvideoMessage : Message
{
/// <summary>
/// Video message media id, Can call to get temporary material interface to pull data .
/// </summary>
public string? MediaId {
get; set; }
/// <summary>
/// Media for video message thumbnails id, Can call to get temporary material interface to pull data .
/// </summary>
public string? ThumbMediaId {
get; set; }
}
public class LocationMessage : Message
{
/// <summary>
/// Geographic location latitude
/// </summary>
public double Location_X {
get; set; }
/// <summary>
/// Geographic location longitude
/// </summary>
public double Location_Y {
get; set; }
/// <summary>
/// Map zoom size
/// </summary>
public double Scale {
get; set; }
/// <summary>
/// Geographic location information
/// </summary>
public string? Label {
get; set; }
}
public class LinkMessage : Message
{
/// <summary>
/// Message title
/// </summary>
public string? Title {
get; set; }
/// <summary>
/// Message description
/// </summary>
public string? Description {
get; set; }
/// <summary>
/// Message link
/// </summary>
public string? Url {
get; set; }
}
public static Message? Parse(string text)
{
var res = XmlDeSerialize<Message>(text);
return res?.MsgType switch
{
"text" => XmlDeSerialize<TextMessage>(text),
"image" => XmlDeSerialize<ImageMessage>(text),
"voice" => XmlDeSerialize<VoiceMessage>(text),
"video" => XmlDeSerialize<VideoMessage>(text),
"shortvideo" => XmlDeSerialize<ShortvideoMessage>(text),
"location" => XmlDeSerialize<LocationMessage>(text),
"link" => XmlDeSerialize<LinkMessage>(text),
_ => res,
};
}
}
/// <summary>
/// Passive reply to user messages
/// </summary>
public class PassiveUserReplyMessage
{
public class Media
{
/// <summary>
/// Upload multimedia files through the interface in material management , Got id
/// </summary>
public string? MediaId {
get; set; }
}
public class Image : Media {
}
public class Voice : Media {
}
public class Video : Media
{
/// <summary>
/// Title of video message
/// </summary>
public string? Title {
get; set; }
/// <summary>
/// Description of video message
/// </summary>
public string? Description {
get; set; }
}
public class Music
{
/// <summary>
/// Music title
/// </summary>
public string? Title {
get; set; }
/// <summary>
/// Music description
/// </summary>
public string? Description {
get; set; }
/// <summary>
/// Music links
/// </summary>
public string? MusicUrl {
get; set; }
/// <summary>
/// High quality music links ,WIFI Environment has priority to use this link to play music
/// </summary>
public string? HQMusicUrl {
get; set; }
/// <summary>
/// Media for thumbnails id, Upload multimedia files through the interface in material management , Got id
/// </summary>
public string? ThumbMediaId {
get; set; }
}
public class Article
{
/// <summary>
/// Graphic message title
/// </summary>
public string? Title {
get; set; }
/// <summary>
/// Text message description
/// </summary>
public string? Description {
get; set; }
/// <summary>
/// Picture links , Support JPG、PNG Format , The better effect is the big picture 360*200, Little picture 200*200
/// </summary>
public string? PicUrl {
get; set; }
/// <summary>
/// Click the graphic message jump link
/// </summary>
public string? Url {
get; set; }
}
public class Message
{
/// <summary>
/// Developer WeChat ID
/// </summary>
public string? ToUserName {
get; set; }
/// <summary>
/// Sender account ( One OpenID)
/// </summary>
public string? FromUserName {
get; set; }
/// <summary>
/// Message creation time
/// </summary>
public long CreateTime {
get; set; }
/// <summary>
/// Message type ,
/// </summary>
public string? MsgType {
get; set; }
}
public class TextMessage : Message
{
/// <summary>
/// The message content of the reply ( Line break : stay content Can wrap , The WeChat client supports a newline display )
/// </summary>
public string? Content {
get; set; }
}
public class ImageMessage : Message
{
public Image? Image {
get; set; }
}
public class VoiceMessage : Message
{
public Voice? Voice {
get; set; }
}
public class VideoMessage : Message
{
public Video? Video {
get; set; }
}
public class MusicMessage : Message
{
public Music? Music {
get; set; }
}
public class ArticleMessage : Message
{
/// <summary>
/// Number of text messages ; When the user sends text 、 picture 、 voice 、 video 、 Image & Text 、 Six kinds of news about geographical location , Developers can only reply 1 Text message ; Other scenes can be replied at most 8 Text message
/// </summary>
public long ArticleCount {
get; set; }
/// <summary>
/// Text message information , Be careful , If the number of pictures exceeds the limit , Then only the number within the limit will be sent
/// </summary>
public Article[]? Articles {
get; set; }
}
public static string Parse(Message message)
{
return $"<xml>${
Format(message)}</xml>";
}
private static string Format(object obj, string text = "")
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
Type propertyType = property.PropertyType;
Console.WriteLine(propertyType);
if (typeof(string).Equals(property.PropertyType))
text += $"<{
property.Name}><![CDATA[{
property.GetValue(obj)}]]></{
property.Name}>";
else
text += $"<{
property.Name}>{
property.GetValue(obj)}</{
property.Name}>";
}
return text;
}
}
/// <summary>
/// General internal use method
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
static async Task<string> Get(string url)
{
try
{
var httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(url);
return response.IsSuccessStatusCode ? await response.Content.ReadAsStringAsync() : ""; ;
}
catch (Exception ex)
{
throw new Exception("Get Request error :" + ex.Message);
}
}
/// <summary>
/// SHA1 encryption , Returns the uppercase string
/// </summary>
/// <param name="content"> Need to encrypt string </param>
/// <param name="encode"> Specify the encryption code </param>
/// <returns> return 40 Bit uppercase string </returns>
static string SHA1Encryption(string content, Encoding? encode = null)
{
try
{
if (encode == null) encode = Encoding.UTF8;
SHA1 sha1 = SHA1.Create();
byte[] bytes_in = encode.GetBytes(content);
byte[] bytes_out = sha1.ComputeHash(bytes_in);
sha1.Dispose();
string result = BitConverter.ToString(bytes_out);
result = result.Replace("-", "");
return result;
}
catch (Exception ex)
{
throw new Exception("SHA1Encryption Encryption error :" + ex.Message);
}
}
/// <summary>
/// Deserialization xml Characters are objects , The default is Utf-8 code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="text"></param>
/// <returns></returns>
static T? XmlDeSerialize<T>(string text) where T : new() => XmlDeSerialize<T>(text, Encoding.UTF8);
/// <summary>
/// Deserialization xml Characters are objects
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="text"></param>
/// <param name="encoding"></param>
/// <returns></returns>
static T? XmlDeSerialize<T>(string text, Encoding encoding) where T : new()
{
var xml = new XmlSerializer(typeof(T), new XmlRootAttribute("xml"));
using var ms = new MemoryStream(encoding.GetBytes(text));
using var sr = new StreamReader(ms, encoding);
return (T?)xml.Deserialize(sr);
}
}
}
Add single case injection
edit Program.cs file , Add a singleton injection , It is convenient for the use of various interfaces .
Some text replies may not be used , But for future expansion , Here we also add .
using AspNetCoreWeChatOAMessage;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<OfficialAccount>(new OfficialAccount(appid,secret,token,encodingAESKey));
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
app.Run();
New interface
newly build Controllers/HandleController.cs file , establish api/OfficialAccount Interface .
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using System.Security.Cryptography;
using System.Text;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace AspNetCoreWeChatOAMessage.Controllers
{
[Route("api/")]
[ApiController]
public class HandleController : ControllerBase
{
private readonly OfficialAccount oa;
public HandleController(OfficialAccount oa)
{
this.oa = oa;
}
[HttpGet("OfficialAccount")]
public string GetOfficialAccount()
{
Request.Query.TryGetValue("signature", out StringValues signature);
Request.Query.TryGetValue("timestamp", out StringValues timestamp);
Request.Query.TryGetValue("nonce", out StringValues nonce);
Request.Query.TryGetValue("echostr", out StringValues echostr);
if (oa.CheckSignature(timestamp, nonce, signature)) return echostr;
return "hello, this is handle view";
}
[HttpPost("OfficialAccount")]
public string PostOfficialAccount()
{
StreamReader stream = new StreamReader(Request.Body);
string messageStr = stream.ReadToEndAsync().GetAwaiter().GetResult();
var message = OfficialAccount.ReceivingStandardMessages.Parse(messageStr);
if (message is OfficialAccount.ReceivingStandardMessages.TextMessage)
{
var textMessage = (OfficialAccount.ReceivingStandardMessages.TextMessage)message;
Console.WriteLine(textMessage.Content);
var content = textMessage.Content;
var replyMessage = new OfficialAccount.PassiveUserReplyMessage.TextMessage
{
Content = " Hello , nice to meet you ~",
CreateTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
ToUserName = message.FromUserName,
FromUserName = message.ToUserName,
MsgType = message.MsgType,
};
if (content != null)
{
if (content.ToUpper().StartsWith("BMI"))
{
var bmiArr = content.Split(" ");
if (bmiArr.Length == 3 && double.TryParse(bmiArr[1], out double height) && double.TryParse(bmiArr[2], out double weight))
{
var bmi = (weight / ((height / 100) * (height / 100)));
var result = $" Your body mass index (BMI) by {
bmi:#.0},";
if (bmi < 18.5) result += " It's a little thin , Eat more 🧋~";
else if (bmi < 23.9) result += " In the normal range , Please keep it up ~";
else if (bmi < 26.9) result += " A little fat , You can exercise ~";
else if (bmi < 29.9) result += " In the stage of obesity , I'm going to exercise ~";
else result += " It's dangerous , Go to the hospital !";
replyMessage.Content = result;
}
}
}
return OfficialAccount.PassiveUserReplyMessage.Parse(replyMessage);
}
return "hello, this is handle view";
}
}
}
Debugging program
Run the program , start-up frpc Corresponding to the local service port and the server forwarding domain name prefix .
Wechat public platform configuration
Enter wechat public platform https://mp.weixin.qq.com
Server configuration
Configure your server content
If saved successfully , It means that the above configuration is correct .
Then you can develop your own official account to reply automatically .
Deploy
Deployment success , You need to modify the server nginx Forwarding configuration .
边栏推荐
- Which securities company is the best to open a stock account? Is there a security guarantee
- Timer和ScheduledThreadPoolExecutor的区别
- Jielizhi, production line assembly link [chapter]
- 【.Net Core】程序相关各种全局文件
- 【QT】对于Qt MSVC 2017无法编译的问题解决
- MySQL: the difference between insert ignore, insert and replace
- 下载在线视频 m3u8使用教程
- Using SqlCommand objects in code
- Shell process control
- ERP项目施行计划的目的是什么?
猜你喜欢
Selectively inhibiting learning bias for active sampling
【CMake】Qt creator 里面的 cmake 配置
Jielizhi, production line assembly link [chapter]
Selectively inhibiting learning bias for active sampling
2021 robocom world robot developer competition - preliminary competition of higher vocational group
Linux CentOS7安装Oracle11g的超完美新手教程
写给当前及未来博士研究生一些建议整理分享
Asp .NetCore 微信订阅号自动回复之文本篇
Graduation season is both a farewell and a new beginning
Openvino model performance evaluation tool DL workbench
随机推荐
牛客-练习赛101-推理小丑
kubernetes资源对象介绍及常用命令(三)
Redis AOF log
Depth first search and breadth first search of graph traversal
Reproduction process and problems of analog transformer (ICLR 2022 Spotlight)
13 MySQL-约束
[leetcode] length of the last word [58]
Similarities and differences between the defined identity execution function authid determiner and PostgreSQL in Oracle
Shell custom function
Digital transformation has a long way to go, so how to take the key first step
Chapter 6 data flow modeling
ADO. Net SqlCommand object
GaussDB(for MySQL) :Partial Result Cache,通过缓存中间结果对算子进行加速
关联性——组内相关系数
MySQL: the difference between insert ignore, insert and replace
Pytorch learning record
【QT】测试Qt是否能连接上数据库
Redis master-slave synchronization
. env. XXX file, with constant, but undefined
Algolia's search needs are almost closed