Create a Daily News Bot using RSS Feeds

Spread the love

In this tutorial, we are going to create a News Bot that uses RSS Feeds to deliver news content to the user. The bot on a new conversation asks the user what news category he/she wants to see articles in. Based on the category the bot fetches the RSS Feed and displays the top 5 articles to the user.

Our bot will return data for the following Azure RSS Feeds.

  • Announcements: https://azurecomcdn.azureedge.net/en-us/blog/topics/announcements/feed/
  • Big Data: https://azurecomcdn.azureedge.net/en-us/blog/topics/big-data/feed/
  • Cloud Strategy: https://azurecomcdn.azureedge.net/en-us/blog/topics/cloud-strategy/feed/
  • Developer: https://azurecomcdn.azureedge.net/en-us/blog/topics/developer/feed/
  • All: https://azurecomcdn.azureedge.net/en-us/blog/feed/

Prerequisites

  1. To run the sample code you must have Visual Studio 2017 installed
  2. You will need basic bot knowledge to follow along with this tutorial. You can read this introductory tutorial to familiarize yourself with basic bot concepts and the solution structure.
  3. To run and test the bot you will need the Bot Framework Emulator
  4. The .NET Core SDK version 2.1

Get Started

This tutorial was based on this Bot Builder Sample. You can download the sample and start from scratch or download the ready bot code at the end of this tutorial. The sample uses adaptive cards to show results to the user. We will use the Hero Card type to show articles to the user.

Open the MainDialog.cs. Make sure your consructor looks like the following. Our Dialog has 2 steps, 1. Prompts the user to choose a category and 2. Show the Results of the user choice.

public MainDialog(ILogger<MainDialog> logger)
            : base(nameof(MainDialog))
        {
            _logger = logger;

            // Define the main dialog and its related components.
            AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
            {
                ChoiceCardStepAsync,
                ShowCardStepAsync,
            }));

            // The initial child Dialog to run.
            InitialDialogId = nameof(WaterfallDialog);
        }

For the first step, the ChoiceCardStepAsync function is already implemented. Substitute it with the following:

private async Task<DialogTurnResult> ChoiceCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            _logger.LogInformation("MainDialog.ChoiceCardStepAsync");

            // Create the PromptOptions which contain the prompt and re-prompt messages.
            // PromptOptions also contains the list of choices available to the user.
            var options = new PromptOptions()
            {
                Prompt = MessageFactory.Text("What Azure Category would you like to see?"),
                RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a card or number from 1 to 4."),
                Choices = GetChoices(),
            };

            // Prompt the user with the configured PromptOptions.
            return await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken);
        }

Now we need to adjust the user choices. Go to GetChoices function and substitute with the following snippet. Our choices will be the following 4 Azure RSS categories.

private IList<Choice> GetChoices()
       {
           var cardOptions = new List<Choice>()
           {

               new Choice() { Value = "Announcements", Synonyms = new List<string>() { "news" } },
               new Choice() { Value = "Big Data", Synonyms = new List<string>() { "data", "big" } },
               new Choice() { Value = "Cloud Strategy", Synonyms = new List<string>() { "cloud", "strategy" } },
               new Choice() { Value = "Developer", Synonyms = new List<string>() { "dev" } }
           };
           return cardOptions;
       }

You can try running the emulator at this point and see the results:

Fetch RSS Feeds

Now before we go on with the Bot Dialog, we need to go and implement the functions that will read the RSS Feeds and return the articles. Go back to the solution and create a Folder named RSSFeeds. Inside the folder create the RSS class. Add the following code to the class.

public class RSS
    {
        string _url;
        public RSS(string URL)
        {
            _url = URL;
        }

        public SyndicationFeed Get()
        {
            try
            {           
                XmlReader reader = XmlReader.Create(_url);
                SyndicationFeed feed = SyndicationFeed.Load(reader);
                reader.Close();
                return feed;
            }
            catch (Exception)
            {
                return null;
            }
        }

    }

This class get an RSS Feed and returns the items. Now open the Cards.cs file. This file has examples of all the cards you can use to show the user data. You can go ahead and delete the contents of this file and replace it with the following code. Here you can see that we have a generic function called GetResults which uses our class to get the top 5 items of an RSS Feed and 5 functions that return those results for different categories. There is also an All category, that displays data for all Azure Categories in case something goes wrong with the Choices given. Our RSS Feed does not have images, but if you choose an RSS that does, you can add it to the Hero card as well.

  public static class Cards
    {    

        public static List<HeroCard> GetAnnouncements()
        {
            List<HeroCard> CardList = new List<HeroCard>();

           CardList = GetResults("https://azurecomcdn.azureedge.net/en-us/blog/topics/announcements/feed/");

            return CardList;
        }

        public static List<HeroCard> GetBigData()
        {
            List<HeroCard> CardList = new List<HeroCard>();

            CardList = GetResults("https://azurecomcdn.azureedge.net/en-us/blog/topics/big-data/feed/");

            return CardList;
        }

        public static List<HeroCard> GetCloudStrategy()
        {
            List<HeroCard> CardList = new List<HeroCard>();

            CardList = GetResults("https://azurecomcdn.azureedge.net/en-us/blog/topics/cloud-strategy/feed/");

            return CardList;
        }

        public static List<HeroCard> GetDeveloper()
        {
            List<HeroCard> CardList = new List<HeroCard>();

            CardList = GetResults("https://azurecomcdn.azureedge.net/en-us/blog/topics/developer/feed/");

            return CardList;
        }

        public static List<HeroCard> GetAll()
        {
            List<HeroCard> CardList = new List<HeroCard>();

            CardList = GetResults("https://azurecomcdn.azureedge.net/en-us/blog/feed/");

            return CardList;
        }

        public static List<HeroCard> GetResults(string url)
        {
            List<HeroCard> CardList = new List<HeroCard>();

            RSS rss = new RSS(url);
            SyndicationFeed Feed = rss.Get();

            if (Feed!=null)
            {
                foreach (var item in Feed.Items.Take(5))
                {
                    var heroCard = new HeroCard
                    {
                        Title = item.Title.Text,
                        Subtitle = item.Categories.FirstOrDefault().Name,
                        Text = item.Summary.Text,
                        Buttons = new List<CardAction> { new CardAction(ActionTypes.OpenUrl, "Link", value: item.Links.FirstOrDefault().Uri.ToString()) }
                    };
                    CardList.Add(heroCard);
                }
            }
                       
            return CardList;
        }



    }
}

Finish the Dialog

Now you can go back and open the MainDialog.cs file. Replace the ShowCardStepAsync function contents with the following code. This function will fetch the RSS Feed based on each Category.

private async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
       {
           _logger.LogInformation("MainDialog.ShowCardStepAsync");
           
           // Cards are sent as Attachments in the Bot Framework.
           // So we need to create a list of attachments for the reply activity.
           var attachments = new List<Attachment>();
           
           // Reply to the activity we received with an activity.
           var reply = MessageFactory.Attachment(attachments);
           
           // Decide which type of card(s) we are going to show the user
           switch (((FoundChoice)stepContext.Result).Value)
           {
               case "Announcements":                   
                   foreach (var item in Cards.GetAnnouncements()) 
                   {
                       reply.Attachments.Add(item.ToAttachment());
                   }
                   break;
               case "Big Data":
                   foreach (var item in Cards.GetBigData())
                   {
                       reply.Attachments.Add(item.ToAttachment());
                   }
                   break;
               case "Cloud Strategy":
                   foreach (var item in Cards.GetCloudStrategy())
                   {
                       reply.Attachments.Add(item.ToAttachment());
                   }
                   break;
               case "Developer":
                   foreach (var item in Cards.GetDeveloper())
                   {
                       reply.Attachments.Add(item.ToAttachment());
                   }
                   break;
               default:
                   // Display a carousel of all the rich card types.                    
                   foreach (var item in Cards.GetAll())
                   {
                       reply.Attachments.Add(item.ToAttachment());
                   }
                   break;
           }

           // Send the card(s) to the user as an attachment to the activity
           await stepContext.Context.SendActivityAsync(reply, cancellationToken);

           // Give the user instructions about what to do next
           await stepContext.Context.SendActivityAsync(MessageFactory.Text("Type anything to see another category."), cancellationToken);

           return await stepContext.EndDialogAsync();
       }

Our bot is ready. Let’s test it in the bot emulator.

You can find the complete code for this tutorial on this Github Repository.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *