Use Blog Park RSS and Quartz.NET to periodically push blog posts in Web Wechat applications

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.

Keywords: ASP.NET JSON Database xml

Added by dreamer on Wed, 17 Jul 2019 19:41:40 +0300