当前位置:网站首页>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 .
边栏推荐
- 时间复杂度与空间复杂度
- Windows10 install WSL (I) (wslregisterdistribution error)
- 【ES实战】ES上的安全性运行方式
- Algolia's search needs are almost closed
- leetcode96不同的二叉搜索树
- vs2015 AdminDeployment.xml
- [must] bm41 output the right view of the binary tree [medium +]
- MySQL Replication中并行复制怎么实现
- Why does blocprovider feel similar to provider?
- PostgreSQL notes (10) dynamically execute syntax parsing process
猜你喜欢

LeetCode中等题题分享(5)

多表操作-一对一,一对多与多对多

Huawei HMS core joins hands with hypergraph to inject new momentum into 3D GIS

- Oui. Env. Fichier XXX, avec constante, mais non spécifié

GaussDB(for MySQL) :Partial Result Cache,通过缓存中间结果对算子进行加速

Algolia's search needs are almost closed

LDR6035智能蓝牙音响可对手机设备持续充放电方案

Asp .NetCore 微信订阅号自动回复之文本篇

起床困难综合症(按位贪心)
Linux CentOS7安装Oracle11g的超完美新手教程
随机推荐
UDS bootloader of s32kxxx bootloader
Use vb Net to convert PNG pictures into icon type icon files
openwrt 开启KV漫游
excel如何打开100万行以上的csv文件
哈工大《信息内容安全》课程知识要点和难点
Jielizhi Bluetooth headset quality control and production skills [chapter]
【QT】Qt 使用MSVC2017找不到编译器的解决办法
ADO. Net SqlCommand object
安全协议重点
Write some suggestions to current and future doctoral students to sort out and share
golang中的iota
SQL optimization
边缘计算概述
Shell process control
使用 pair 做 unordered_map 的键值
Operate database transactions with jpatractionmanager
多表操作-一对一,一对多与多对多
Windows installation WSL (II)
vs2015 AdminDeployment. xml
[es practice] safe operation mode on ES