当前位置:网站首页>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 .
边栏推荐
- 牛客-练习赛101-推理小丑
- USB-IF协会与各种接口的由来
- RPA tutorial 01: Excel automation from introduction to practice
- Concurrentskiplistmap -- principle of table skipping
- leetcode96不同的二叉搜索树
- Digital transformation has a long way to go, so how to take the key first step
- Is there a piece of code that makes you convinced by human wisdom
- cookie、session、tooken
- 安全协议重点
- Soft exam information system project manager_ Compiled abbreviations of the top ten management processes to help memory recitation - -- software test advanced information system project manager 054
猜你喜欢
![[es practice] safe operation mode on ES](/img/3f/fa28783770098ff10bffeccd64fe51.png)
[es practice] safe operation mode on ES

使用VB.net将PNG图片转成icon类型图标文件

Concurrentskiplistmap -- principle of table skipping

TS初次使用、ts类型

ConcurrentSkipListMap——跳表原理

Pytorch learning record

Shell process control

PostgreSQL source code (57) why is the performance gap so large in hot update?

华为HMS Core携手超图为三维GIS注入新动能
![[cmake] cmake configuration in QT Creator](/img/e3/1cf76f88eaddb5d32184523dfb049c.png)
[cmake] cmake configuration in QT Creator
随机推荐
E-commerce RPA robot helps brand e-commerce to achieve high traffic
vue 强制清理浏览器缓存
ConcurrentSkipListMap——跳表原理
SecurityUtils. getSubject(). How to solve the problem that getprincipal() is null
Relatively easy to understand PID understanding
边缘计算概述
哈工大《信息内容安全》课程知识要点和难点
LDR6035智能蓝牙音响可充可放(5.9.12.15.20V)快充快放设备充电
TS初次使用、ts类型
在长城证券上买基金安全吗?
Iota in golang
【模板】自适应辛普森积分
13 MySQL-约束
Which securities company is the best to open a stock account? Is there a security guarantee
Multi table operation - one to one, one to many and many to many
二叉搜索树的创建,查找,添加,删除操作
【CMake】Qt creator 里面的 cmake 配置
const // It is a const object... class nullptr_ t
Use the htaccess file to prohibit the script execution permission in the directory
How excel opens CSV files with more than one million lines