This article describes the methods of receiving event push and message scheduling in C# WeChat public account development. Share it for your reference. The specific analysis is as follows:
If the WeChat server fails to receive a response within 5 seconds, it will disconnect and re-initiate a request, and retry three times in total. In this way, the problem arises. There is a scenario where when a user follows a WeChat account, he obtains the current user information and then writes the information to the database. Similar to registration of PC websites. Perhaps because of this concern event, the business logic we need to deal with is relatively complex. If you give points, write user logs, and assign user groups. Wait... A series of logic needs to be executed, or the network environment is relatively complex and cannot guarantee the response to the current user's operations within 5 seconds. If the operation has not been completed and the WeChat server pushes the same attention event to our server, we will execute our logic again, which may cause duplicate data in the database (some children's shoes will say that I will first determine whether it already exists before inserting the data. If it exists, the insertion operation will not be performed. What I want to say is that I thought the same way at the beginning, but there is still a gap between the real running environment and our debugging environment. It was not until I found that there were a lot of duplicate user information in the database that I discovered the importance of message deduplication.).
There is a difference between ordinary messages and event messages when deduplication of messages. Normal messages use msgid, while event messages use FromUserName + CreateTime. My thoughts are:
Create a new class BaseMsg, and there are three properties: FromUser, MsgFlag, and CreateTime. The code is as follows:
{
/// <summary>
/// Sender ID
/// </summary>
public string FromUser { get; set; }
/// <summary>
/// Message indicates. When a normal message is msgid, when an event message is, the creation time of the event is
/// </summary>
public string MsgFlag { get; set; }
/// <summary>
/// Time added to the queue
/// </summary>
public DateTime CreateTime { get; set; }
}
Create a static list_queue to store the message list. The type of the list is List<BaseMsg>.
Before processing the WeChat message body, first determine whether the list is instantiated. If it is not instantiated, it is instantiated. Otherwise, determine whether the length of the list is greater than or equal to 50 (this can be customized, and its use is the number of messages concurrently sent by WeChat). If it is greater than or equal to 50, the unresponsive messages within 20 seconds are retained (retry once in 5 seconds, retry 3 times in total, which is 15 seconds, and write 20 seconds here to be safe).
Get the message type of the current message body and determine whether the current message has been requested based on _queue. If it is an event, save FromUser and creation time. If it is a normal message, save MsgFlag. Here is the code:
{
_queue = new List<BaseMsg>();
}
else if(_queue.Count>=50)
{
_queue = _queue.Where(q => { return (20) > ; }).ToList();//Retain unresponsive messages within 20 seconds
}
XElement xdoc = (xml);
var msgtype = ("MsgType").();
var FromUserName = ("FromUserName").Value;
var MsgId = ("MsgId").Value;
var CreateTime = ("CreateTime").Value;
MsgType type = (MsgType)(typeof(MsgType), msgtype);
if (type!=)
{
if (_queue.FirstOrDefault(m => { return == MsgId; }) == null)
{
_queue.Add(new BaseMsg
{
CreateTime = ,
FromUser = FromUserName,
MsgFlag = MsgId
});
}
else
{
return null;
}
}
else
{
if (_queue.FirstOrDefault(m => { return == CreateTime; }) == null)
{
_queue.Add(new BaseMsg
{
CreateTime = ,
FromUser = FromUserName,
MsgFlag = CreateTime
});
}
else
{
return null;
}
}
When the message is already in the queue, the current message is not converted to an entity, and null is directly returned. When calling, no processing is done when null is returned.
The following is the explanation of event messages. Continue with the previous article. All messages inherit BaseMessage, and all event types contain an Event attribute. Here, for the convenience of calling, the message is
/// Event type enumeration
/// </summary>
public enum Event
{
/// <summary>
/// Non-event type
/// </summary>
NOEVENT,
/// <summary>
/// Subscribe
/// </summary>
SUBSCRIBE,
/// <summary>
/// Unsubscribe
/// </summary>
UNSUBSCRIBE,
/// <summary>
/// Scan the QR code with parameters
/// </summary>
SCAN,
/// <summary>
/// Geographical location
/// </summary>
LOCATION,
/// <summary>
/// Click the button
/// </summary>
CLICK,
/// <summary>
/// Link button
/// </summary>
VIEW,
/// <summary>
/// Scan the QR code to push the event
/// </summary>
SCANCODE_PUSH,
/// <summary>
/// Scan the code to push the event and the "Message Receive" prompt box pops up
/// </summary>
SCANCODE_WAITMSG,
/// <summary>
/// Pop up the system to take photos and post pictures
/// </summary>
PIC_SYSPHOTO,
/// <summary>
/// Pop up to take photos or post pictures
/// </summary>
PIC_PHOTO_OR_ALBUM,
/// <summary>
/// Pop up WeChat photo album sender
/// </summary>
PIC_WEIXIN,
/// <summary>
/// Popup geolocation selector
/// </summary>
LOCATION_SELECT,
/// <summary>
/// Template message push
/// </summary>
TEMPLATESENDJOBFINISH
}
After defining the enumeration, the message entity is defined.
Follow/Unfollow events
The xml data packet is as follows:
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>
Corresponding entities:
/// Subscribe/Unsubscribe events
/// </summary>
public class SubEventMessage : EventMessage
{
private string _eventkey;
/// <summary>
/// Event KEY value, qrscene_ is prefixed, followed by the parameter value of the QR code (the prefix has been removed, can be used directly)
/// </summary>
public string EventKey
{
get { return _eventkey; }
set { _eventkey = ("qrscene_", ""); }
}
/// <summary>
/// The QR code ticket can be exchanged for QR code pictures
/// </summary>
public string Ticket { get; set; }
}
It should be noted here that when the user scans the QR code with parameters, if the user does not follow the current official account, when the user follows, he will bring the qrscene_ parameter and Ticket in the message body, so two attributes are defined here: EventKey and Ticket. When assigning a value to EventKey, replace qrscene_, because what we really need is the subsequent parameters.
Scan the QR code event with parameters
When a user scans a QR code with scene value, he may push two kinds of events:
If the user has not followed the official account yet, the user can follow the official account. After following, WeChat will push the event with scene value to the developer.
If the user has followed the official account, WeChat will push the scene value scan event to the developer. ,
The first type has been mentioned above, and here we will only explain the second type.
Event push when the user has been following
The xml package is as follows:
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
The corresponding entities are as follows:
/// Scan the QR code entity with parameters
/// </summary>
public class ScanEventMessage : EventMessage
{
/// <summary>
/// The event KEY value is a 32-bit unsigned integer, that is, the QR code scene_id when creating the QR code
/// </summary>
public string EventKey { get; set; }
/// <summary>
/// The QR code ticket can be exchanged for QR code pictures
/// </summary>
public string Ticket { get; set; }
}
Report a geographical location event
When the official account enables the function of reporting the geographical location, every time you enter the official account session, the user agrees to report the geographical location, and will report the geographical location every 5 seconds after entering the reply. The official account can modify the settings in the backend of the public platform. When reporting a geographical location, WeChat will push the reported geographical location event to the URL filled in by the developer.
The xml data packet is as follows:
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>23.137466</Latitude>
<Longitude>113.352425</Longitude>
<Precision>119.385040</Precision>
</xml>
The corresponding entities are as follows:
/// Report the geographical location entity
/// </summary>
public class LocationEventMessage : EventMessage
{
/// <summary>
/// Geographical location
/// </summary>
public string Latitude { get; set; }
/// <summary>
/// Geographical location longitude
/// </summary>
public string Longitude { get; set; }
/// <summary>
/// Geographical location accuracy
/// </summary>
public string Precision { get; set; }
}
Commonly used events for custom menu events are: click, view, scancode_puth, scancode_waitmsg, location_select. There are three other types of pictures posted. Since they are not commonly used, the author did not expect the use scenarios, so I won’t tell you one by one again. If you are interested, you can study it yourself or communicate with me.
XML packet pushed by click event:
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>
The XML packet pushed by the view event is the same format as the click, so just define a class, as follows:
/// Normal menu events, including click and view
/// </summary>
public class NormalMenuEventMessage : EventMessage
{
/// <summary>
/// Event KEY value, set jump URL
/// </summary>
public string EventKey { get; set; }
}
The xml packet of the scancode event is as follows:
<FromUserName><![CDATA[FromUserName]]></FromUserName>
<CreateTime>1419265698</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[scancode_push]]></Event>
<EventKey><![CDATA[EventKey]]></EventKey>
<ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType>
<ScanResult><![CDATA[/r/JEy5oRLE0U_urVbC9xk2]]></ScanResult>
</ScanCodeInfo>
</xml>
The corresponding entities are as follows:
/// Menu scan event
/// </summary>
public class ScanMenuEventMessage : EventMessage
{
/// <summary>
/// Event KEY value
/// </summary>
public string EventKey { get; set; }
/// <summary>
/// Scan the code type. qrcode is a QR code, the others are barcodes
/// </summary>
public string ScanType { get; set; }
/// <summary>
/// Scan results
/// </summary>
public string ScanResult { get; set; }
}
At this point, the currently commonly used event type messages have been defined. In combination with the previous article, the complete code for converting the xml packet into an object is as follows:
{
private static List<BaseMsg> _queue;
public static BaseMessage CreateMessage(string xml)
{
if (_queue == null)
{
_queue = new List<BaseMsg>();
}
else if(_queue.Count>=50)
{
_queue = _queue.Where(q => { return (20) > ; }).ToList();//Retain unresponsive messages within 20 seconds
}
XElement xdoc = (xml);
var msgtype = ("MsgType").();
var FromUserName = ("FromUserName").Value;
var MsgId = ("MsgId").Value;
var CreateTime = ("CreateTime").Value;
MsgType type = (MsgType)(typeof(MsgType), msgtype);
if (type!=)
{
if (_queue.FirstOrDefault(m => { return == MsgId; }) == null)
{
_queue.Add(new BaseMsg
{
CreateTime = ,
FromUser = FromUserName,
MsgFlag = MsgId
});
}
else
{
return null;
}
}
else
{
if (_queue.FirstOrDefault(m => { return == CreateTime; }) == null)
{
_queue.Add(new BaseMsg
{
CreateTime = ,
FromUser = FromUserName,
MsgFlag = CreateTime
});
}
else
{
return null;
}
}
switch (type)
{
case : return <TextMessage>(xml);
case : return <ImgMessage>(xml);
case : return <VideoMessage>(xml);
case : return <VoiceMessage>(xml);
case :
return <LinkMessage>(xml);
case :
return <LocationMessage>(xml);
case ://event type
{
var eventtype = (Event)(typeof(Event), ("Event").());
switch (eventtype)
{
case :
return <NormalMenuEventMessage>(xml);
case : return <NormalMenuEventMessage>(xml);
case : return <LocationEventMessage>(xml);
case Event.LOCATION_SELECT: return <LocationMenuEventMessage>(xml);
case : return <ScanEventMessage>(xml);
case : return <SubEventMessage>(xml);
case : return <SubEventMessage>(xml);
case Event.SCANCODE_WAITMSG: return <ScanMenuEventMessage>(xml);
default:
return <EventMessage>(xml);
}
} break;
default:
return <BaseMessage>(xml);
}
}
}
I hope this article will be helpful to everyone's development of WeChat based on C#.