This article introduces how to use Blog Garden RSS and Quartz.NET to push blog post content regularly in the application of Web Wechat. First, do a simple introduction and code analysis to Quartz.NET, master the processing of job scheduling, then how to get the RSS content of Blog Garden and combine it with the mass distribution of Wechat messages.Interfaces send content, which builds a business model for message sending in Web applications using job scheduling.
Quartz.NET is an open source job scheduling framework that is well suited for routine work, periodic polling for database synchronization, periodic mail notifications, periodic processing of data, and so on.Quartz.NET allows developers to schedule jobs based on time intervals (or days).It implements a many-to-many relationship between jobs and triggers and associates multiple jobs with different triggers.Applications that integrate Quartz.NET can reuse jobs from different events or combine multiple jobs for one event.
1. Use of Quartz.NET
Some basic conceptual explanations of the Quartz framework:
Scheduler Job Scheduler.
Job interface, inherits and implements Execute, and writes specific job logic for execution.
JobBuilder) Generate a JobDetail based on the settings.
TriggerBuilder produces the corresponding Trigger according to the rules
The official use case code is as follows
private void button1_Click(object sender, EventArgs e) { try { Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info }; // Grab the Scheduler instance from the Factory IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); // and start it off scheduler.Start(); // define the job and tie it to our HelloJob class IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("job1", "group1") .Build(); // Trigger the job to run now, and then repeat every 10 seconds ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); // Tell quartz to schedule the job using our trigger scheduler.ScheduleJob(job, trigger); // some sleep to show what's happening Thread.Sleep(TimeSpan.FromSeconds(60)); // and last shut down the scheduler when you are ready to close your program scheduler.Shutdown(); } catch (SchedulerException se) { Console.WriteLine(se); } Console.WriteLine("Finished"); }
Start an object that defines a HelloJOb, as shown in the code below
public class HelloJob : IJob { public void Execute(IJobExecutionContext context) { Console.WriteLine("Greetings from HelloJob!"); } }
2. Quartz's cron expression
cron expressions are easy to understand as a whole, but only one thing to note is the usage of the'?'sign, which you can see below?It can be used in day of month and day of week, and it mainly solves the following scenarios: 31 minutes per hour on the 1st of each month, the correct expression is: * 31 * 1 *?And not: * 31 * 1 * * because it represents any day of the week.
Composed of 7 segments: seconds, minutes, hours, days, months, weeks and years (optional)
"-": range MON-WED Monday to Wednesday
",": for listing MONs, WEB for Monday and Wednesday
"*": Table is "Every", Monthly, Daily, Weekly, Annual, etc.
"/": Indicates increment: 0/15 (within minutes) every 15 minutes, starting after 0 minutes, 3/20 every 20 minutes, starting after 3 minutes
"?": Can only appear on a day, within a week period, indicating that no specific value is specified
"L": Can only appear on Sundays, within Sundays, is the abbreviation of Last, the last day of a month, the last day of a week (Saturday)
"W": Represents a working day, the closest working day to a given value
"#": Represents the day of the week of a month, e.g. "6#3" means the third Friday of a month (1=SUN...6=FRI,7=SAT)
Official cron expression instance
Expression | Representational Meaning |
---|---|
0 0 12 * * ? | Trigger every day at 12 noon |
0 15 10 ? * * | Trigger every day at 10:15 a.m. |
0 15 10 * * ? | Trigger every day at 10:15 a.m. |
0 15 10 * * ? * | Trigger every day at 10:15 a.m. |
0 15 10 * * ? 2005 | Triggered at 10:15 a.m. every day in 2005 |
0 * 14 * * ? | Triggered every minute between 2 p.m. and 2:59 p.m. each day |
0 0/5 14 * * ? | Triggered every 5 minutes between 2 p.m. and 2:55 p.m. each day |
0 0/5 14,18 * * ? | Triggers every 5 minutes between 2 p.m. and 2:55 p.m. and between 6 p.m. and 6:55 p.m. each day |
0 0-5 14 * * ? | Triggered every minute between 2 p.m. and 2:05 p.m. each day |
0 10,44 14 ? 3 WED | Triggered every Wednesday, March at 2:10 and 2:44 p.m. |
0 15 10 ? * MON-FRI | Triggered at 10:15 a.m. Monday to Friday |
0 15 10 15 * ? | Triggered at 10:15 a.m. on 15th of each month |
0 15 10 L * ? | Triggered at 10:15 on the last day of each month |
0 15 10 L-2 * ? | Fire at 10:15am on the 2nd-to-last last day of every month |
0 15 10 ? * 6L | Triggered at 10:15 a.m. on the last Friday of the month |
0 15 10 ? * 6L | Fire at 10:15am on the last Friday of every month |
0 15 10 ? * 6L 2002-2005 | Triggered at 10:15 a.m. on the last Friday of each month, 2002-2005 |
0 15 10 ? * 6#3 | Triggered at 10:15 a.m. on the third Friday of each month |
0 0 12 1/5 * ? | Fire at 12pm (noon) every 5 days every month, starting on the first day of the month. |
0 11 11 11 11 ? | Fire every November 11th at 11:11am. |
3. Application cases of Quartz.NET
I used to use this Quartz.NET in the Web API background of Unified Interface to synchronize site information. This synchronizes interface data provided by other vendors to local locations and speeds up data retrieval and processing.
The code is shown below.
First, synchronize code within the background code of Global.asax.
public class WebApiApplication : System.Web.HttpApplication { IScheduler scheduler = null; protected void Application_Start() { GlobalConfiguration.Configuration.EnableCors(); GlobalConfiguration.Configure(WebApiConfig.Register); //Create a process that performs synchronization ISchedulerFactory sf = new StdSchedulerFactory(); scheduler = sf.GetScheduler(); CalendarTask(); CreateOnceJob(); //Start all tasks scheduler.Start(); } protected void Application_End(object sender, EventArgs e) { if(scheduler != null) { scheduler.Shutdown(true); } } /// <summary> /// Create synchronization tasks /// </summary> private void CalendarTask() { IJobDetail job = JobBuilder.Create<StationSyncJob>() .WithIdentity("StationSyncJob", "group1") .Build(); //Run once a day at 1 a.m. 0:01 * * ? ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") //"0 34,36,38,40 * * * ?" .WithCronSchedule("0 0 1 * * ?")//"0 0 1 * * ?" .Build(); DateTimeOffset ft = scheduler.ScheduleJob(job, trigger); LogTextHelper.Info(string.Format("You are in {0} Time to create Quartz task", DateTime.Now)); } private void CreateOnceJob() { IJobDetail onceJob = JobBuilder.Create<StationSyncJob>() .WithIdentity("onceJob", "group1") .Build(); //Run once at startup DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 30); ISimpleTrigger simpleTrigger = (ISimpleTrigger)TriggerBuilder.Create() .WithIdentity("simpleOnce", "group1") .StartAt(startTime) .Build(); DateTimeOffset ft = scheduler.ScheduleJob(onceJob, simpleTrigger); } }
The Job implementation for synchronizing site information is shown below (here, data is obtained by calling a third-party interface and then saved locally, this timer service is set to execute at a point in time each day, such as at 1 a.m.).
/// <summary> /// Synchronize site information /// </summary> public class StationSyncJob : IJob { public void Execute(IJobExecutionContext context) { LogTextHelper.Info(string.Format("You are in {0} Time Call [Synchronize Site Information] Once", DateTime.Now)); StationDetailResult result = new StationDetailResult(); try { QueryStationJson json = new QueryStationJson();//Empty query, query all at once BaseDataAgent agent = new BaseDataAgent(); result = agent.QueryStationDetail(json); if(result != null && result.success) { foreach(StationDetailJson detail in result.data) { StationInfo info = detail.ConvertInfo(); try { BLLFactory<Station>.Instance.InsertIfNew(info); } catch(Exception ex) { LogTextHelper.Error(ex); LogTextHelper.Info(info.ToJson()); } } } } catch (Exception ex) { result.errmsg = ex.Message; result.success = false; LogTextHelper.Error(ex); } } }
4. RSS of Blog
In principle, we can use any RSS source to get blog content that responds. Here I use my own blog site's RSS source. Each blog site's account has the following connection to provide our latest blog list information.
Open the connection and you can see that its content is the latest blog content, as shown below
It is very convenient for us to process RSS content using the built-in SyndicationFeed object.
string url = "http://feed.cnblogs.com/blog/u/12391/rss"; XmlReader reader = XmlReader.Create(url); SyndicationFeed feed = SyndicationFeed.Load(reader); reader.Close();
The above code simply takes the corresponding RSS content and converts it to XMLReader for parsing.
Then you can get the contents of each XML node through a traversal process, which is very convenient.
foreach (SyndicationItem item in feed.Items) { var id = item.Id; string subject = item.Title.Text; string summary = item.Summary.Text; }
5. Send blog content in WeChat applications
Through the RSS read operation above, we can get the corresponding blog content, and if we need to send some content to our customers every week, these can be processed and sent through the RSS source above.
For the processing of sending text messages, you can refer to my essays, " C#Develop WeChat Portal and Application(3) --Response to Text Message and Text Message>
Here I will directly apply the above interface to process and send content, and the logic of the specific interface will not be listed.
/// <summary> /// Get blog Park articles ( RSS)And send text to the specified user /// </summary> private void GetCnblogsArticles() { string url = "http://feed.cnblogs.com/blog/u/12391/rss"; XmlReader reader = XmlReader.Create(url); SyndicationFeed feed = SyndicationFeed.Load(reader); reader.Close(); ICustomerApi api = new CustomerApi(); foreach (SyndicationItem item in feed.Items) { Console.WriteLine(item.ToJson()); var id = item.Id; string subject = item.Title.Text; string summary = item.Summary.Text; var content = string.Format("<a href='{0}'>{1}</a>", id, subject); CommonResult result = api.SendText(token, openId, content); Console.WriteLine("Send content:" + (result.Success ? "Success" : "fail:" + result.ErrorMessage)); } }
The resulting interface effect is shown below.
However, the effect is still somewhat unsatisfactory. We know that WeChat has an interface for text messages. It is more beautiful to send messages using the interface for text messages.
The adjusted code is shown below.
/// <summary> /// Send blog text messages to specified users /// </summary> private void SendBlogsNews() { List<ArticleEntity> list = new List<ArticleEntity>(); string url = "http://feed.cnblogs.com/blog/u/12391/rss"; XmlReader reader = XmlReader.Create(url); SyndicationFeed feed = SyndicationFeed.Load(reader); reader.Close(); int i = 0; foreach (SyndicationItem item in feed.Items) { list.Add( new ArticleEntity { Title = item.Title.Text, Description = item.Summary.Text, PicUrl = i == 0 ? "http://www.iqidi.com/Content/Images/cnblogs_whc.png" : "http://www.iqidi.com/Content/Images/frame_web.png", Url = item.Id }); if(i >= 8) { break; } i++; } ICustomerApi customerApi = new CustomerApi(); var result = customerApi.SendNews(token, openId, list); }
This is the code that sends the text message, which needs to rebuild a collection of entity classes for sending, and the effect is shown below.
The overall interface effect is what we need, but if we need to send in bulk to subscribers, then we need to use the mass messaging interface, which encapsulates messages. For more information, refer to the article " C#Develop WeChat Portal and Application (30) --Mass processing and preview of messages>.
The logic code of the whole mass message is as follows. The main logic is to get blog posts, upload pictures of the posts, then upload graphic message resources that need to be mass distributed, and finally call the mass interface to send messages.
private void BatchSendBlogNews() { List<NewsUploadJson> list = new List<NewsUploadJson>(); string url = "http://feed.cnblogs.com/blog/u/12391/rss"; XmlReader reader = XmlReader.Create(url); SyndicationFeed feed = SyndicationFeed.Load(reader); reader.Close(); //Upload Picture Acquisition MediaId IMediaApi mediaApi = new MediaApi(); var result1 = mediaApi.UploadTempMedia(token, UploadMediaFileType.image, @"E:\My Site Profile\iqidiSoftware\content\images\cnblogs_whc.png");//"http://www.iqidi.com/Content/Images/cnblogs_whc.png"); var result2 = mediaApi.UploadTempMedia(token, UploadMediaFileType.image, @"E:\My Site Profile\iqidiSoftware\content\images\frame_web.png");//"http://www.iqidi.com/Content/Images/frame_web.png"); if (result1 != null && result2 != null) { int i = 0; foreach (SyndicationItem item in feed.Items) { list.Add( new NewsUploadJson { author = "Wu Huacong", title = item.Title.Text, content = item.Summary.Text, //digest = item.Summary.Text, thumb_media_id = i == 0 ? result1.media_id : result2.media_id, content_source_url = item.Id, }); if (i >= 8) { break; } i++; } } if (list.Count > 0) { UploadJsonResult resultNews = mediaApi.UploadNews(token, list); if (resultNews != null) { IMassSendApi massApi = new MassSendApi(); var result = massApi.SendByGroup(token, MassMessageType.mpnews, resultNews.media_id, "0", true); } else { Console.WriteLine("Failed to upload text message"); } } }
Mass messages on WeChat see the same content as before, but clicking doesn't jump the link directly. Instead, it goes into a page with more details and only clicks to read the original text to jump the URL, as shown below.
6. Push blog posts regularly with Quartz.NET
Using blog RSS and Quartz.NET to push articles regularly in Web Wechat applications, we need to combine the job scheduling processing of Quartz.NET, the content sending of Wechat interface, and the content acquisition and processing of blog RSS to achieve the whole function.
First, we designed the Job content for scheduling based on the code above, as shown below.
Then, in the background code of Global.asa for Web applications, write code to start job scheduling.
As explained in the previous Corn expression, we send a rule periodically once a week, as shown below.
Weekly Sunday at 1 AM: 0 01? * L
So our final Globa.asa background code is shown below.
public class Global : HttpApplication { private IScheduler scheduler = null; void Application_Start(object sender, EventArgs e) { // Code that runs at application startup AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //Construct a dispatch object and create a corresponding dispatch task scheduler = StdSchedulerFactory.GetDefaultScheduler(); CalendarTask(); //Start all tasks scheduler.Start(); } protected void Application_End(object sender, EventArgs e) { if (scheduler != null) { scheduler.Shutdown(true); } } private void CalendarTask() { IJobDetail job = JobBuilder.Create<BlogArticleSendJob>() .WithIdentity("BlogArticleSendJob", "group1") .Build(); //Once a week at 1 a.m. on Sundays: 0.01 ? * L ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .WithCronSchedule("0 0 1 ? * L")//0 0 1 ? * L .Build(); DateTimeOffset ft = scheduler.ScheduleJob(job, trigger); LogTextHelper.Info(string.Format("You are in {0} Time to create Quartz task", DateTime.Now)); }
Taken together, we can use Quartz.NET to schedule more data synchronization tasks. In addition, in Wechat applications, we can integrate many components or controls to achieve more flexible business support, such as message distribution, guest summary, content synchronization and so on.
These are some of my component code application ideas, in fact, we only need to explore a wider range of things, many things can be used to fetch doctrine, after their own integration and optimization, can serve us better.