public void AppBasedLinkQueryInitsWithNoArgs()
        {
            var appBasedLinkQuery = new AppBasedLinkQuery();

            Assert.NotNull(appBasedLinkQuery);
            Assert.IsType <AppBasedLinkQuery>(appBasedLinkQuery);
        }
 protected virtual Task <InvokeResponse> OnAppBasedLinkQueryAsync(
     ITurnContext <IInvokeActivity> turnContext,
     AppBasedLinkQuery query,
     CancellationToken cancellationToken)
 {
     return(Task.FromResult <InvokeResponse>(null));
 }
        /// <summary>
        /// Unfurl / Get a preview for the a link pasted inside the Teams' compose box.
        /// </summary>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="query">The matched url.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A Task resolving to either a login card or the adaptive card of the Reddit post.</returns>
        /// <remarks>
        /// For more information on link unfurling in Teams, see the documentation
        /// https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/link-unfurling?tabs=dotnet .
        /// </remarks>
        protected override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(
            ITurnContext <IInvokeActivity> turnContext,
            AppBasedLinkQuery query,
            CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
            query       = query ?? throw new ArgumentNullException(nameof(query));

            this.RecordEvent(nameof(this.OnTeamsAppBasedLinkQueryAsync), turnContext);

            return(this.GetRedditPostAsync(query.Url));
        }
        public void AppBasedLinkQueryInits()
        {
            var url   = "http://example.com";
            var state = "magicCode";

            var appBasedLinkQuery = new AppBasedLinkQuery(url)
            {
                State = state
            };

            Assert.NotNull(appBasedLinkQuery);
            Assert.IsType <AppBasedLinkQuery>(appBasedLinkQuery);
            Assert.Equal(url, appBasedLinkQuery.Url);
            Assert.Equal(state, appBasedLinkQuery.State);
        }
Пример #5
0
        public async Task <InvokeResponse> HandleAppBasedLinkQueryAsync(
            ITurnContext <IInvokeActivity> turnContext,
            AppBasedLinkQuery query,
            CancellationToken cancellationToken)
        {
            var url = query?.Url?.Trim() ?? string.Empty;

            var result = new InvokeResponse {
                Status = (int)HttpStatusCode.OK
            };

            if (url.Contains("OpenPositionsPersonalTab"))
            {
                var positionIdParam = HttpUtility.ParseQueryString(new Uri(url).Query).Get("positionId");
                if (int.TryParse(positionIdParam, out var positionId))
                {
                    result = await PositionMessagingExtensionResponse(turnContext, positionId);
                }
            }

            return(result);
        }
Пример #6
0
        protected async override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var state         = query.State; // Check the state value
            var tokenResponse = await GetTokenResponse(turnContext, state, cancellationToken);

            if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.Token))
            {
                // There is no token, so the user has not signed in yet.

                // Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions
                var signInLink = await GetSignInLinkAsync(turnContext, cancellationToken).ConfigureAwait(false);

                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "auth",
                        SuggestedActions = new MessagingExtensionSuggestedAction
                        {
                            Actions = new List <CardAction>
                            {
                                new CardAction
                                {
                                    Type = ActionTypes.OpenUrl,
                                    Value = signInLink,
                                    Title = "Bot Service OAuth",
                                },
                            },
                        },
                    },
                });
            }

            var client  = new SimpleGraphClient(tokenResponse.Token);
            var profile = await client.GetMyProfile();

            var heroCard = new ThumbnailCard
            {
                Title  = "Thumbnail Card",
                Text   = $"Hello, {profile.DisplayName}",
                Images = new List <CardImage> {
                    new CardImage("https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png")
                },
            };

            var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
            var result      = new MessagingExtensionResult("list", "result", new[] { attachments });

            return(new MessagingExtensionResponse(result));
        }
Пример #7
0
        protected override async Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var projectLinkQuery = new TestLinkQuery();

            return(await projectLinkQuery.HandleInvokeActivityAsync(turnContext, query).ConfigureAwait(false));
        }
 /// <summary>
 /// Invoked when an app based link query activity is received from the connector.
 /// </summary>
 /// <param name="turnContext">A strongly-typed context object for this turn.</param>
 /// <param name="query">The invoke request body type for app-based link query.</param>
 /// <param name="cancellationToken">A cancellation token that can be used by other objects
 /// or threads to receive notice of cancellation.</param>
 /// <returns>The Messaging Extension Response for the query.</returns>
 protected virtual Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
 {
     throw new InvokeResponseException(HttpStatusCode.NotImplemented);
 }
Пример #9
0
 /// <summary>
 /// Handles app-based link query asynchronously.
 /// </summary>
 /// <param name="turnContext">The turn context.</param>
 /// <param name="query">The app-based link query.</param>
 /// <returns>Task tracking operation.</returns>
 public virtual Task <InvokeResponse> HandleAppBasedLinkQueryAsync(ITurnContext turnContext, AppBasedLinkQuery query)
 {
     return(Task.FromResult <InvokeResponse>(null));
 }
Пример #10
0
        protected override async Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var heroCard = new ThumbnailCard
            {
                Title  = "Thumbnail Card",
                Text   = query.Url,
                Images = new List <CardImage> {
                    new CardImage("https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png")
                },
            };

            var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
            var result      = new MessagingExtensionResult(AttachmentLayoutTypes.List, "result", new[] { attachments }, null, "test unfurl");

            return(new MessagingExtensionResponse(result));
        }
        /// <summary>
        /// Invoked when an app based link query activity is received from the connector.
        /// </summary>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="query">The invoke request body type for app-based link query</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        /// <returns>The Messaging Extension Response for the query.</returns>
        protected async override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var  state = query.State;
            bool isSignedIn;

            _isSignedIn.TryGetValue("isSignedIn", out isSignedIn);
            var previewCard = new ThumbnailCard();
            var attachment  = new MessagingExtensionAttachment();

            if (string.IsNullOrEmpty(state))
            {
                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "config",
                        SuggestedActions = new MessagingExtensionSuggestedAction
                        {
                            Actions = new List <CardAction>
                            {
                                new CardAction
                                {
                                    Type = ActionTypes.OpenUrl,
                                    Value = $"{_applicationBaseUrl}/config",
                                    Title = "Select login option",
                                },
                            },
                        },
                    },
                });
            }
            else if (state == "sso" || isSignedIn)
            {
                var tokenResponse = await GetTokenResponse(turnContext, _botConnectionName, state, cancellationToken);

                if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.Token))
                {
                    isSignedIn = true;
                    _isSignedIn.AddOrUpdate("isSignedIn", true, (key, newValue) => true);
                    var signInLink = await GetSignInLinkAsync(turnContext, _botConnectionName, cancellationToken).ConfigureAwait(false);

                    return(new MessagingExtensionResponse
                    {
                        ComposeExtension = new MessagingExtensionResult
                        {
                            Type = "auth",
                            SuggestedActions = new MessagingExtensionSuggestedAction
                            {
                                Actions = new List <CardAction>
                                {
                                    new CardAction
                                    {
                                        Type = ActionTypes.OpenUrl,
                                        Value = signInLink,
                                        Title = "Bot Service OAuth",
                                    },
                                },
                            },
                        },
                    });
                }

                var client  = new SimpleGraphClient(tokenResponse.Token);
                var profile = await client.GetMeAsync();

                var photo = await client.GetPhotoAsync();

                var title = !string.IsNullOrEmpty(profile.JobTitle) ? profile.JobTitle : "Unknown";
                previewCard = new ThumbnailCard
                {
                    Title    = $"Hello! {profile.DisplayName}",
                    Text     = $"Job title: {title}",
                    Subtitle = $"Email: {profile.UserPrincipalName}",
                    Images   = new List <CardImage> {
                        new CardImage {
                            Url = photo
                        }
                    }
                };
                attachment = new MessagingExtensionAttachment
                {
                    ContentType = ThumbnailCard.ContentType,
                    Content     = previewCard,
                    Preview     = previewCard.ToAttachment()
                };

                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "result",
                        AttachmentLayout = "list",
                        Attachments = new List <MessagingExtensionAttachment> {
                            attachment
                        }
                    },
                });
            }
            else if (state == "usercredentials" || state.Contains("userName") || isSignedIn)
            {
                if (state.Contains("userName"))
                {
                    JObject asJobject = JObject.Parse(state);
                    var     userName  = (string)asJobject.ToObject <CardTaskFetchValue <string> >()?.UserName;
                    var     password  = (string)asJobject.ToObject <CardTaskFetchValue <string> >()?.Password;

                    if (userName == Constant.UserName && password == Constant.Password)
                    {
                        previewCard = new ThumbnailCard
                        {
                            Title    = $"Hello! Test user",
                            Text     = "Job title: Data Scientist",
                            Subtitle = "Email: [email protected]",
                            Images   = new List <CardImage> {
                                new CardImage {
                                    Url = "https://media.istockphoto.com/vectors/profile-placeholder-image-gray-silhouette-no-photo-vector-id1016744034?k=20&m=1016744034&s=612x612&w=0&h=kjCAwH5GOC3n3YRTHBaLDsLIuF8P3kkAJc9RvfiYWBY="
                                }
                            }
                        };
                        attachment = new MessagingExtensionAttachment
                        {
                            ContentType = ThumbnailCard.ContentType,
                            Content     = previewCard,
                            Preview     = previewCard.ToAttachment()
                        };

                        return(new MessagingExtensionResponse
                        {
                            ComposeExtension = new MessagingExtensionResult
                            {
                                Type = "result",
                                AttachmentLayout = "list",
                                Attachments = new List <MessagingExtensionAttachment> {
                                    attachment
                                }
                            },
                        });
                    }
                    else
                    {
                        previewCard = new ThumbnailCard
                        {
                            Title = "Invalid credentials",
                        };
                        attachment = new MessagingExtensionAttachment
                        {
                            ContentType = ThumbnailCard.ContentType,
                            Content     = previewCard,
                            Preview     = previewCard.ToAttachment()
                        };

                        return(new MessagingExtensionResponse
                        {
                            ComposeExtension = new MessagingExtensionResult
                            {
                                Type = "result",
                                AttachmentLayout = "list",
                                Attachments = new List <MessagingExtensionAttachment> {
                                    attachment
                                }
                            },
                        });
                    }
                }
                else
                {
                    return(new MessagingExtensionResponse
                    {
                        ComposeExtension = new MessagingExtensionResult
                        {
                            Type = "auth",
                            SuggestedActions = new MessagingExtensionSuggestedAction
                            {
                                Actions = new List <CardAction>
                                {
                                    new CardAction
                                    {
                                        Type = ActionTypes.OpenUrl,
                                        Value = $"{_applicationBaseUrl}/popUpSignin?from=msgext",
                                        Title = "Using username/password",
                                    },
                                },
                            },
                        },
                    });
                }
            }
            else
            {
                var facebooktokenResponse = await GetTokenResponse(turnContext, _facebookConnectionName, state, cancellationToken);

                if (facebooktokenResponse == null || string.IsNullOrEmpty(facebooktokenResponse.Token))
                {
                    _isSignedIn.AddOrUpdate("isSignedIn", false, (key, newValue) => false);
                    var fbsignInLink = await GetSignInLinkAsync(turnContext, _facebookConnectionName, cancellationToken).ConfigureAwait(false);

                    return(new MessagingExtensionResponse
                    {
                        ComposeExtension = new MessagingExtensionResult
                        {
                            Type = "auth",
                            SuggestedActions = new MessagingExtensionSuggestedAction
                            {
                                Actions = new List <CardAction>
                                {
                                    new CardAction
                                    {
                                        Type = ActionTypes.OpenUrl,
                                        Value = fbsignInLink,
                                        Title = "Facebook auth",
                                    },
                                },
                            },
                        },
                    });
                }

                FacebookProfile fbProfile = await FacebookHelper.GetFacebookProfileName(facebooktokenResponse.Token);

                previewCard = new ThumbnailCard
                {
                    Title  = $"Hello! {fbProfile.Name}",
                    Images = new List <CardImage> {
                        new CardImage {
                            Url = fbProfile.ProfilePicture.data.url
                        }
                    }
                };
                attachment = new MessagingExtensionAttachment
                {
                    ContentType = ThumbnailCard.ContentType,
                    Content     = previewCard,
                    Preview     = previewCard.ToAttachment()
                };

                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "result",
                        AttachmentLayout = "list",
                        Attachments = new List <MessagingExtensionAttachment> {
                            attachment
                        }
                    },
                });
            }
        }
Пример #12
0
        //protected override Task<MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext<IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        //{
        //    var heroCard = new ThumbnailCard
        //    {
        //        Title = "Thumbnail Card",
        //        Text = query.Url,
        //        Images = new List<CardImage> { new CardImage("https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png") },
        //    };

        //    var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
        //    var result = new MessagingExtensionResult("list", "result", new[] { attachments });

        //    return Task.FromResult(new MessagingExtensionResponse(result));
        //}


        protected override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var heroCard = new ThumbnailCard
            {
                Title  = "Thumbnail Card",
                Text   = query.Url,
                Images = new List <CardImage> {
                    new CardImage("https://www.leaders-in-law.com/wp-content/uploads/2020/03/COVID-19.png")
                },
            };
            var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
            var result      = new MessagingExtensionResult("list", "result", new[] { attachments });

            return(Task.FromResult(new MessagingExtensionResponse(result)));
        }
Пример #13
0
 protected override Task <InvokeResponse> OnAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
 => _invokeActivityHandler.HandleAppBasedLinkQueryAsync(turnContext, query, cancellationToken);
        /// <summary>
        /// Handles app-based link query asynchronously.
        /// </summary>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="query">The app-based link query.</param>
        /// <returns>Task tracking operation.</returns>
        public override async Task <InvokeResponse> HandleAppBasedLinkQueryAsync(ITurnContext turnContext, AppBasedLinkQuery query)
        {
            var previewImg = new List <CardImage>
            {
                new CardImage("https://assets.pokemon.com/assets/cms2/img/pokedex/full/025.png", "Pokemon"),
            };
            var preview     = new ThumbnailCard("Preview Card", null, $"Your query URL: {query.Url}", previewImg).ToAttachment();
            var heroCard    = new HeroCard("Preview Card", null, $"Your query URL: <pre>{query.Url}</pre>", previewImg).ToAttachment();
            var resultCards = new List <MessagingExtensionAttachment> {
                heroCard.ToMessagingExtensionAttachment(preview)
            };

            return(new InvokeResponse
            {
                Status = 200,
                Body = new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult("list", "result", resultCards),
                },
            });
        }
        /// <summary>
        /// Get the login or preview response for the given link.
        /// </summary>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="query">The matched url.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A Task resolving to either a login card or </returns>
        /// <remarks>
        /// For more information on Link Unfurling see the documentation
        /// https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/link-unfurling?tabs=dotnet
        ///
        /// This method also implements messaging extension authentication to get the reddit API token for the user.
        /// https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/add-authentication
        /// </remarks>
        protected override async Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(
            ITurnContext <IInvokeActivity> turnContext,
            AppBasedLinkQuery query,
            CancellationToken cancellationToken = default)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
            query       = query ?? throw new ArgumentNullException(nameof(query));

            IUserTokenProvider tokenProvider = turnContext.Adapter as IUserTokenProvider;

            // Get the magic code out of the request for when the login flow is completed.
            // https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-authentication?view=azure-bot-service-4.0#securing-the-sign-in-url
            string magicCode = (turnContext.Activity?.Value as JObject)?.Value <string>("state");

            // Get the token from the Azure Bot Framework Token Service to handle token storage
            // https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-authentication?view=azure-bot-service-4.0#about-the-bot-framework-token-service
            var tokenResponse = await tokenProvider
                                ?.GetUserTokenAsync(
                turnContext : turnContext,
                connectionName : this.options.Value.BotFrameworkConnectionName,
                magicCode : magicCode,
                cancellationToken : cancellationToken);

            try
            {
                // Execute the domain logic to get the reddit link
                RedditLinkModel redditLink = await this.redditHttpClient.GetLinkAsync(tokenResponse?.Token, query.Url);

                var preview = new MessagingExtensionAttachment(
                    contentType: HeroCard.ContentType,
                    contentUrl: null,
                    content: this.RenderLinkHeroCard(redditLink));

                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "result",
                        AttachmentLayout = AttachmentLayoutTypes.List,
                        Attachments = new List <MessagingExtensionAttachment>()
                        {
                            new MessagingExtensionAttachment
                            {
                                ContentType = AdaptiveCard.ContentType,
                                Content = this.RenderLinkAdaptiveCard(redditLink),
                                Preview = preview,
                            },
                        },
                    },
                });
            }
            catch (RedditUnauthorizedException)
            {
                this.logger.LogInformation("Attempt to fetch post resulted in unauthorized, triggering log-in flow");

                // "log out" the user, so log-in gets a new token.
                await tokenProvider.SignOutUserAsync(
                    turnContext : turnContext,
                    connectionName : this.options.Value.BotFrameworkConnectionName,
                    cancellationToken : cancellationToken);

                return(await this
                       .GetAuthenticationMessagingExtensionResponseAsync(turnContext, cancellationToken)
                       .ConfigureAwait(false));
            }
#pragma warning disable CA1031
            catch (Exception ex)
#pragma warning restore CA1031
            {
                this.logger.LogError(ex, "Failed to get reddit post");
                return(null);
            }
        }
Пример #16
0
 protected override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
 {
     Record.Add(MethodBase.GetCurrentMethod().Name);
     return(Task.FromResult(new MessagingExtensionResponse()));
 }
        /// <summary>
        /// This method is called when user enters a url in compose box from a domain that this app unfurls.
        ///
        /// Application authenticates the user to read resource and insert a card with resource information and option to review it in a meeting.
        /// </summary>
        /// <param name="turnContext">Turn context.</param>
        /// <param name="query">Query - this contains the url that user entered.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>ME response.</returns>
        protected async override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            this.logger.LogInformation($"OnTeamsAppBasedLinkQueryAsync:  {query.Url}");

            // Make sure its a valid url.
            if (!this.urlParser.IsValidResourceUrl(query.Url))
            {
                return(null);
            }

            // Make sure the user is signed-in.
            var isUserSignedIn = await turnContext.IsUserSignedInAsync(this.appSettings.GraphConnectionName, query.State, cancellationToken);

            if (!isUserSignedIn)
            {
                this.logger.LogInformation("User is not signed-in.");
                var signInUrl = await turnContext.GetOAuthSignInUrlAsync(this.appSettings.GraphConnectionName, cancellationToken);

                var signInResponse = this.GetSignInResponse(signInUrl);
                return(signInResponse);
            }

            // If the user is signed-in:
            // 1. Check if user is authorized to access the resource. (Skipped in this sample).
            // 2. Create a card with resource information/preview image and option to review it in a meeting.
            var resourceId = this.urlParser.GetResourceId(query.Url);
            var resource   = await this.resourceProvider.GetResourceAsync(resourceId);

            return(this.GetContentCardResponse(resource));
        }
Пример #18
0
        protected async override Task <MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext <IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
        {
            var tokenResponse = await GetTokenResponse(turnContext, query.State, cancellationToken);

            if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.Token))
            {
                // There is no token, so the user has not signed in yet.
                // Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions
                var signInLink = await(turnContext.Adapter as IUserTokenProvider).GetOauthSignInLinkAsync(turnContext, _connectionName, cancellationToken);
                return(new MessagingExtensionResponse
                {
                    ComposeExtension = new MessagingExtensionResult
                    {
                        Type = "auth",
                        SuggestedActions = new MessagingExtensionSuggestedAction
                        {
                            Actions = new List <CardAction>
                            {
                                new CardAction
                                {
                                    Type = ActionTypes.OpenUrl,
                                    Value = signInLink,
                                    Title = "Bot Service OAuth",
                                },
                            },
                        },
                    },
                });
            }

            var client  = new SimpleGraphClient(tokenResponse.Token);
            var profile = await client.GetMyProfile();

            var imagelink = await client.GetPhotoAsync();

            var heroCard = new ThumbnailCard
            {
                Title  = "Thumbnail Card",
                Text   = $"Hello {profile.DisplayName}",
                Images = new List <CardImage> {
                    new CardImage(imagelink)
                }
            };
            var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
            var result      = new MessagingExtensionResult("list", "result", new[] { attachments });

            return(new MessagingExtensionResponse(result));
        }