Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

News skill: adding trending articles, favorite topics, searching by market, searching by website #1660

Merged
merged 4 commits into from
Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,84 @@
- find me news on {topic}
- find news on {topic}
- news about {topic}
- news about {topic=microsoft} on {site=wsj.com}
- whats the latest news on {site=nytimes.com}
- news on {site=cnn.com}
- latest news on {site}
- find me news about {topic} on {site}
- find me news on {topic} on {site}
- whats the news on {site=cnn.com}
- find news on {topic=technology} on {site=wsj.com}
- whats the latest on {topic} on {site}
- find me news from {site}
- find me news about {topic} from {site}
- {topic} news from {site}
- {topic} news on {site}

## TrendingArticles
- what's trending now
- what's the latest trending news
- find trending news
- what's the top news on social media
- trending news
- top news on social media
- what news is popular right now
- popular news
- find trending articles
- trending articles
- news trending on social media
- articles popular on social media
- news trending now
- find news trending on social networks
- news popular on social networks
- articles trending now

## SetFavoriteTopics
- set favorite news topics
- set favorite news categories
- choose favorite news topics
- choose favorite news categories
- set news topics
- set news categories
- set favorites
- choose favorites
- choose my topics
- set my topics
- set news I'm interested in
- choose topics I'm interested in

## ShowFavoriteTopics
- favorite news
- favorite news topics
- my news categories
- my news topics
- my news interests
- news I'm interested in
- what's my favorite news
- topics I'm interested in
- my news
- my favorite news
- news for me
- my news suggestions
- news topics for me
- what are my favorites
- what are my favorite topics
- find favorites
- find favorite news
- find favorite topics

## None
- 1
- 2
- forward email
- find point of interest
- get directions
- add to my to do list
- how long do I have until my next calendar appointment
- hello world
- what's the weather today

> # Entity definitions

$topic:simple
$topic:simple
$site:simple
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using NewsSkill.Models;
using NewsSkill.Responses.FavoriteTopics;
using NewsSkill.Services;

namespace NewsSkill.Dialogs
{
public class FavoriteTopicsDialog : NewsDialogBase
{
private NewsClient _client;
private FavoriteTopicsResponses _responder = new FavoriteTopicsResponses();
private AzureMapsService _mapsService;

public FavoriteTopicsDialog(
BotSettings settings,
BotServices services,
ConversationState conversationState,
UserState userState,
AzureMapsService mapsService,
IBotTelemetryClient telemetryClient)
: base(nameof(FavoriteTopicsDialog), services, conversationState, userState, telemetryClient)
{
TelemetryClient = telemetryClient;

var newsKey = settings.Properties["BingNewsKey"] ?? throw new Exception("The BingNewsKey must be provided to use this dialog. Please provide this key in your Skill Configuration.");
var mapsKey = settings.Properties["AzureMapsKey"] ?? throw new Exception("The AzureMapsKey must be provided to use this dialog. Please provide this key in your Skill Configuration.");

_client = new NewsClient(newsKey);
_mapsService = mapsService;
_mapsService.InitKeyAsync(mapsKey);

var favoriteTopics = new WaterfallStep[]
{
GetMarket,
SetMarket,
SetFavorites,
ShowArticles,
};

AddDialog(new WaterfallDialog(nameof(FavoriteTopicsDialog), favoriteTopics));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
}

private async Task<DialogTurnResult> GetMarket(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

// Check if there's already a location
if (userState.Market != null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
if (userState.Market.Length > 0)
{
return await sc.NextAsync(userState.Market);
}
}

// Prompt user for location
return await sc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = await _responder.RenderTemplate(sc.Context, sc.Context.Activity.Locale, FavoriteTopicsResponses.MarketPrompt)
});
}

private async Task<DialogTurnResult> SetMarket(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

if (userState.Market == null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
string country = (string)sc.Result;

// use AzureMaps API to get country code from country input by user
userState.Market = await _mapsService.GetCountryCodeAsync(country);
litofish marked this conversation as resolved.
Show resolved Hide resolved
}

return await sc.NextAsync();
}

private async Task<DialogTurnResult> SetFavorites(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var convState = await ConvAccessor.GetAsync(sc.Context, () => new NewsSkillState());
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

// if intent is SetFavorites or not set in state yet
if (convState.LuisResult.TopIntent().intent == Luis.NewsLuis.Intent.SetFavoriteTopics || userState.Category == null)
{
// show card with categories the user can choose
var categories = new PromptOptions()
{
Choices = new List<Choice>(),
};

categories.Choices.Add(new Choice("Business"));
categories.Choices.Add(new Choice("Entertainment"));
categories.Choices.Add(new Choice("Health"));
categories.Choices.Add(new Choice("Politics"));
categories.Choices.Add(new Choice("World"));
categories.Choices.Add(new Choice("Sports"));

return await sc.PromptAsync(nameof(ChoicePrompt), new PromptOptions()
{
Prompt = await _responder.RenderTemplate(sc.Context, sc.Context.Activity.Locale, FavoriteTopicsResponses.FavoriteTopicPrompt),
Choices = categories.Choices
});
}

return await sc.NextAsync(userState.Category);
}

private async Task<DialogTurnResult> ShowArticles(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

userState.Category = (FoundChoice)sc.Result;

// show favorite articles
var articles = await _client.GetNewsByCategory(userState.Category.Value, userState.Market);
await _responder.ReplyWith(sc.Context, FavoriteTopicsResponses.ShowArticles, articles);

return await sc.EndDialogAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
Expand All @@ -13,41 +15,85 @@ public class FindArticlesDialog : NewsDialogBase
{
private NewsClient _client;
private FindArticlesResponses _responder = new FindArticlesResponses();
private AzureMapsService _mapsService;

public FindArticlesDialog(
BotSettings settings,
BotServices services,
ConversationState conversationState,
UserState userState,
AzureMapsService mapsService,
IBotTelemetryClient telemetryClient)
: base(nameof(FindArticlesDialog), services, conversationState, telemetryClient)
: base(nameof(FindArticlesDialog), services, conversationState, userState, telemetryClient)
{
TelemetryClient = telemetryClient;

var key = settings.Properties["BingNewsKey"] ?? throw new Exception("The BingNewsKey must be provided to use this dialog. Please provide this key in your Skill Configuration.");
var newsKey = settings.Properties["BingNewsKey"] ?? throw new Exception("The BingNewsKey must be provided to use this dialog. Please provide this key in your Skill Configuration.");
var mapsKey = settings.Properties["AzureMapsKey"] ?? throw new Exception("The AzureMapsKey must be provided to use this dialog. Please provide this key in your Skill Configuration.");

_client = new NewsClient(key);
_client = new NewsClient(newsKey);
_mapsService = mapsService;
_mapsService.InitKeyAsync(mapsKey);

var findArticles = new WaterfallStep[]
{
GetMarket,
SetMarket,
GetQuery,
GetSite,
ShowArticles,
};

AddDialog(new WaterfallDialog(nameof(FindArticlesDialog), findArticles));
AddDialog(new TextPrompt(nameof(TextPrompt)));
}

private async Task<DialogTurnResult> GetMarket(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

// Check if there's already a location
if (userState.Market != null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
if (userState.Market.Length > 0)
{
return await sc.NextAsync(userState.Market);
}
}

// Prompt user for location
return await sc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = await _responder.RenderTemplate(sc.Context, sc.Context.Activity.Locale, FindArticlesResponses.MarketPrompt)
});
}

private async Task<DialogTurnResult> SetMarket(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

if (userState.Market == null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
string country = (string)sc.Result;
litofish marked this conversation as resolved.
Show resolved Hide resolved

// use AzureMaps API to get country code from country input by user
userState.Market = await _mapsService.GetCountryCodeAsync(country);
}

return await sc.NextAsync();
}

private async Task<DialogTurnResult> GetQuery(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var state = await Accessor.GetAsync(sc.Context, () => new NewsSkillState());
var convState = await ConvAccessor.GetAsync(sc.Context, () => new NewsSkillState());

// Let's see if we have a topic
if (state.LuisResult.Entities.topic != null)
if (convState.LuisResult.Entities.topic != null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
// If we have a topic let's skip the topic prompt
if (state.LuisResult.Entities.topic.Length > 0)
if (convState.LuisResult.Entities.topic.Length > 0)
{
return await sc.NextAsync(state.LuisResult.Entities.topic[0]);
return await sc.NextAsync(convState.LuisResult.Entities.topic[0]);
}
}

Expand All @@ -57,10 +103,32 @@ private async Task<DialogTurnResult> GetQuery(WaterfallStepContext sc, Cancellat
});
}

private async Task<DialogTurnResult> GetSite(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var convState = await ConvAccessor.GetAsync(sc.Context, () => new NewsSkillState());

string query = (string)sc.Result;

// if site specified in luis, add to query
if (convState.LuisResult.Entities.site != null)
litofish marked this conversation as resolved.
Show resolved Hide resolved
{
if (convState.LuisResult.Entities.site.Length > 0)
{
string site = convState.LuisResult.Entities.site[0].Replace(" ", string.Empty);
query = string.Concat(query, $" site:{site}");
}
}

return await sc.NextAsync(query);
}

private async Task<DialogTurnResult> ShowArticles(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var userState = await UserAccessor.GetAsync(sc.Context, () => new NewsSkillUserState());

var query = (string)sc.Result;
var articles = await _client.GetNewsForTopic(query);

var articles = await _client.GetNewsForTopic(query, userState.Market);
await _responder.ReplyWith(sc.Context, FindArticlesResponses.ShowArticles, articles);

return await sc.EndDialogAsync();
Expand Down
19 changes: 19 additions & 0 deletions skills/src/csharp/experimental/newsskill/Dialogs/MainDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public MainDialog(
BotServices services,
ConversationState conversationState,
FindArticlesDialog findArticlesDialog,
TrendingArticlesDialog trendingArticlesDialog,
FavoriteTopicsDialog favoriteTopicsDialog,
IBotTelemetryClient telemetryClient)
: base(nameof(MainDialog), telemetryClient)
{
Expand All @@ -39,6 +41,8 @@ public MainDialog(
_stateAccessor = _conversationState.CreateProperty<NewsSkillState>(nameof(NewsSkillState));

AddDialog(findArticlesDialog ?? throw new ArgumentNullException(nameof(findArticlesDialog)));
AddDialog(trendingArticlesDialog ?? throw new ArgumentNullException(nameof(trendingArticlesDialog)));
AddDialog(favoriteTopicsDialog ?? throw new ArgumentNullException(nameof(favoriteTopicsDialog)));
}

protected override async Task OnStartAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
Expand Down Expand Up @@ -69,6 +73,21 @@ public MainDialog(
// switch on general intents
switch (intent)
{
case NewsLuis.Intent.TrendingArticles:
{
// send articles in response
turnResult = await dc.BeginDialogAsync(nameof(TrendingArticlesDialog));
break;
}

case NewsLuis.Intent.SetFavoriteTopics:
case NewsLuis.Intent.ShowFavoriteTopics:
{
// send favorite news categories
turnResult = await dc.BeginDialogAsync(nameof(FavoriteTopicsDialog));
break;
}

case NewsLuis.Intent.FindArticles:
{
// send greeting response
Expand Down
Loading