Exemple #1
0
        /// <summary>
        /// Called when the dialog is started and pushed onto the dialog stack.
        /// </summary>
        /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
        /// <param name="options">Optional, initial information to pass to the dialog.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options is CancellationToken)
            {
                throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
            }

            if (Disabled != null && Disabled.GetValue(dc.State) == true)
            {
                return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            string message = Message.GetValueOrNull(dc.State);

            if (string.IsNullOrEmpty(message))
            {
                throw new ArgumentException($"A {nameof(Message)} is required for {Kind}.");
            }

            var response = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type = MessagingExtensionResultResponseType.message.ToString(),
                    Text = message,
                },
                CacheInfo = GetCacheInfo(dc)
            };

            var invokeResponse            = CreateInvokeResponseActivity(response);
            ResourceResponse sendResponse = await dc.Context.SendActivityAsync(invokeResponse, cancellationToken : cancellationToken).ConfigureAwait(false);

            return(await dc.EndDialogAsync(sendResponse, cancellationToken : cancellationToken).ConfigureAwait(false));
        }
Exemple #2
0
        protected override Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
        {
            var title      = "";
            var titleParam = query.Parameters?.FirstOrDefault(p => p.Name == "cardTitle");

            if (titleParam != null)
            {
                title = titleParam.Value.ToString();
            }

            if (query == null || query.CommandId != "getRandomText")
            {
                // We only process the 'getRandomText' queries with this message extension
                throw new NotImplementedException($"Invalid CommandId: {query.CommandId}");
            }

            var attachments = new MessagingExtensionAttachment[5];

            for (int i = 0; i < 5; i++)
            {
                attachments[i] = GetAttachment(title);
            }

            var result = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    AttachmentLayout = "list",
                    Type             = "result",
                    Attachments      = attachments.ToList()
                },
            };

            return(Task.FromResult(result));
        }
        protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionSelectItemAsync(ITurnContext <IInvokeActivity> turnContext, JObject query, CancellationToken cancellationToken)
        {
            var searchQuery = query.ToObject <SearchQuery>();
            var bfLogo      = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU";
            var card        = new HeroCard
            {
                Title  = $"You selected a search result!",
                Text   = $"You searched for \"{searchQuery.Query}\"",
                Images = new List <CardImage>
                {
                    new CardImage {
                        Url = bfLogo
                    }
                }
            };

            var attachment = new MessagingExtensionAttachment
            {
                ContentType = HeroCard.ContentType,
                Content     = card
            };

            var messagingExtensionResponse = new MessagingExtensionResponse();

            messagingExtensionResponse.ComposeExtension = CreateMessagingExtensionResult(new List <MessagingExtensionAttachment> {
                attachment
            });
            return(messagingExtensionResponse);
        }
        /// <inheritdoc/>
        protected override Task <MessagingExtensionResponse> OnTeamsMessagingExtensionConfigurationQuerySettingUrlAsync(
            ITurnContext <IInvokeActivity> turnContext,
            MessagingExtensionQuery query,
            CancellationToken cancellationToken)
        {
            var openSettingAction = new CardAction
            {
                Type  = ActionTypes.OpenUrl,
                Value = $"{this.appSettings.BaseUrl}/mesettings",
            };

            // ME Result.
            var result = new MessagingExtensionResult
            {
                Type             = "config",
                SuggestedActions = new MessagingExtensionSuggestedAction
                {
                    Actions = new List <CardAction> {
                        openSettingAction
                    },
                },
            };

            // ME response.
            var response = new MessagingExtensionResponse()
            {
                ComposeExtension = result,
            };

            return(Task.FromResult(response));
        }
        /// <summary>
        /// Open the Messaging Extension Configuration Page
        /// </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 the settings / logout page </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
        /// </remarks>
        protected override Task <MessagingExtensionResponse> OnTeamsMessagingExtensionConfigurationQuerySettingUrlAsync(
            ITurnContext <IInvokeActivity> turnContext,
            MessagingExtensionQuery query,
            CancellationToken cancellationToken)
        {
            var messagingExtensionReponse = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type             = "config",
                    SuggestedActions = new MessagingExtensionSuggestedAction
                    {
                        Actions = new List <CardAction>
                        {
                            new CardAction
                            {
                                Type  = ActionTypes.OpenUrl,
                                Value = this.options.Value.SettingsPageUrl,
                            },
                        },
                    },
                },
            };

            return(Task.FromResult(messagingExtensionReponse));
        }
        public void MessagingExtensionResponseInitsWithNoArgs()
        {
            var msgExtResponse = new MessagingExtensionResponse();

            Assert.NotNull(msgExtResponse);
            Assert.IsType <MessagingExtensionResponse>(msgExtResponse);
        }
        /// <summary>
        /// Prepares a message extension response with sign-in action as compose extension.
        /// </summary>
        /// <param name="signInUrl">Sign-in url.</param>
        /// <returns>Messaging extension response.</returns>
        private MessagingExtensionResponse GetSignInResponse(string signInUrl)
        {
            // Sign-in action.
            var signInAction = new CardAction
            {
                Type  = ActionTypes.OpenUrl,
                Value = signInUrl,
                Title = "Sign in",
            };

            // ME Result.
            var result = new MessagingExtensionResult
            {
                Type             = "auth",
                SuggestedActions = new MessagingExtensionSuggestedAction
                {
                    Actions = new List <CardAction> {
                        signInAction
                    },
                },
            };

            // ME response.
            var response = new MessagingExtensionResponse()
            {
                ComposeExtension = result,
            };

            return(response);
        }
        public MessagingExtensionResponse GetComposeExtensionQueryResult(List <MessagingExtensionAttachment> composeExtensionAttachments)
        {
            MessagingExtensionResponse composeExtensionResponse = new MessagingExtensionResponse();
            MessagingExtensionResult   composeExtensionResult   = new MessagingExtensionResult();

            composeExtensionResult.Type               = "result";
            composeExtensionResult.Attachments        = composeExtensionAttachments;
            composeExtensionResult.AttachmentLayout   = "list";
            composeExtensionResponse.ComposeExtension = composeExtensionResult;

            return(composeExtensionResponse);
        }
        protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
        {
            var searchQuery = query.Parameters[0].Value as string;
            var messagingExtensionResponse = new MessagingExtensionResponse();

            messagingExtensionResponse.ComposeExtension = CreateMessagingExtensionResult(new List <MessagingExtensionAttachment>
            {
                CreateSearchResultAttachment(searchQuery),
                CreateDummySearchResultAttachment(),
                CreateSelectItemsResultAttachment(searchQuery)
            });

            return(messagingExtensionResponse);
        }
        public void MessagingExtensionResponseInits()
        {
            var composeExtension = new MessagingExtensionResult("list", "message", null, null, "happy dance");
            var cacheInfo        = new CacheInfo();

            var msgExtResponse = new MessagingExtensionResponse(composeExtension)
            {
                CacheInfo = cacheInfo
            };

            Assert.NotNull(msgExtResponse);
            Assert.IsType <MessagingExtensionResponse>(msgExtResponse);
            Assert.Equal(composeExtension, msgExtResponse.ComposeExtension);
            Assert.Equal(cacheInfo, msgExtResponse.CacheInfo);
        }
        /// <summary>
        /// Handle when the user is searching in the messaging extension query.
        /// Apps should handle user queries and return appropriate results.
        /// </summary>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="query">The messaging extension query.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A Task that resolves to the list of cards that matched the query.</returns>
        protected override Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(
            ITurnContext <IInvokeActivity> turnContext,
            MessagingExtensionQuery query,
            CancellationToken cancellationToken)
        {
            var attachment  = new HeroCard("Query not implemented", "App should handle query approperiately.");
            var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null /*url*/, attachment);
            var response    = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult(
                    AttachmentLayoutTypes.List,
                    "result" /*type*/,
                    new[] { attachments }),
            };

            return(Task.FromResult(response));
        }
Exemple #12
0
        /// <inheritdoc/>
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options is CancellationToken)
            {
                throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
            }

            if (Disabled != null && Disabled.GetValue(dc.State))
            {
                return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            if (Card == null)
            {
                throw new ArgumentException($"A valid card is required for {Kind}.");
            }

            var activity = await Card.BindAsync(dc, dc.State).ConfigureAwait(false);

            if (activity?.Attachments?.Any() != true)
            {
                throw new InvalidOperationException($"Invalid activity. An attachment is required for {Kind}.");
            }

            var attachment          = activity.Attachments[0];
            var extensionAttachment = new MessagingExtensionAttachment(attachment.ContentType, null, attachment.Content);

            var response = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type             = MEResultResponseType.result.ToString(),
                    AttachmentLayout = MEAttachmentLayoutResponseType.list.ToString(), // TODO: enum this
                    Attachments      = new List <MessagingExtensionAttachment> {
                        extensionAttachment
                    }
                },
                CacheInfo = GetCacheInfo(dc),
            };

            var invokeResponse            = CreateInvokeResponseActivity(response);
            ResourceResponse sendResponse = await dc.Context.SendActivityAsync(invokeResponse, cancellationToken : cancellationToken).ConfigureAwait(false);

            return(await dc.EndDialogAsync(sendResponse, cancellationToken : cancellationToken).ConfigureAwait(false));
        }
Exemple #13
0
        /// <inheritdoc/>
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options is CancellationToken)
            {
                throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
            }

            if (Disabled != null && Disabled.GetValue(dc.State))
            {
                return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            string configUrl = ConfigUrl.GetValueOrNull(dc.State);

            if (string.IsNullOrEmpty(configUrl))
            {
                throw new InvalidOperationException($"{nameof(ConfigUrl)} is required for {Kind}.");
            }

            var response = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type             = MEResultResponseType.config.ToString(),
                    SuggestedActions = new MessagingExtensionSuggestedAction
                    {
                        Actions = new List <CardAction>
                        {
                            new CardAction
                            {
                                Type  = ActionTypes.OpenUrl,
                                Value = configUrl,
                            },
                        },
                    },
                },
                CacheInfo = GetCacheInfo(dc)
            };

            var invokeResponse            = CreateInvokeResponseActivity(response);
            ResourceResponse sendResponse = await dc.Context.SendActivityAsync(invokeResponse, cancellationToken : cancellationToken).ConfigureAwait(false);

            return(await dc.EndDialogAsync(sendResponse, cancellationToken : cancellationToken).ConfigureAwait(false));
        }
        public MessagingExtensionResponse GetConfig()
        {
            string            configUrl   = ConfigurationManager.AppSettings["BaseUri"].ToString() + "/composeExtensionSettings.html";
            CardAction        configExp   = new CardAction(ActionTypes.OpenUrl, "Config", null, null, null, configUrl);
            List <CardAction> cardActions = new List <CardAction>();

            cardActions.Add(configExp);
            MessagingExtensionResponse composeExtensionResponse = new MessagingExtensionResponse();
            MessagingExtensionResult   composeExtensionResult   = new MessagingExtensionResult();

            MessagingExtensionSuggestedAction objSuggestedAction = new MessagingExtensionSuggestedAction();

            objSuggestedAction.Actions = cardActions;

            composeExtensionResult.SuggestedActions   = objSuggestedAction;
            composeExtensionResult.Type               = "config";
            composeExtensionResponse.ComposeExtension = composeExtensionResult;

            return(composeExtensionResponse);
        }
        protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionConfigurationQuerySettingUrlAsync(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
        {
            var messageExtensionResponse = new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type             = "config",
                    SuggestedActions = new MessagingExtensionSuggestedAction
                    {
                        Actions = new List <CardAction>
                        {
                            new CardAction
                            {
                                Type  = ActionTypes.OpenUrl,
                                Value = "https://teamssettingspagescenario.azurewebsites.net",
                            },
                        },
                    },
                },
            };

            return(messageExtensionResponse);
        }
        public async Task <MessagingExtensionResponse> GetComposeExtensionResponseAsync(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query)
        {
            MessagingExtensionResponse composeExtensionResponse = null;
            ImageResult imageResult = null;
            List <MessagingExtensionAttachment> composeExtensionAttachments = new List <MessagingExtensionAttachment>();
            var userData = await TemplateUtility.GetBotUserDataObject(_userState, turnContext, query);

            var  userPreferredCardType = userData.ComposeExtensionCardType;
            var  activity     = turnContext.Activity;
            bool isSettingUrl = false;

            if (string.Equals(activity.Name.ToLower(), Strings.ComposeExtensionQuerySettingUrl))
            {
                isSettingUrl = true;
            }

            if (query.CommandId == null || query.Parameters == null)
            {
                return(null);
            }
            var initialRunParameter = GetQueryParameterByName(query, Strings.manifestInitialRun);
            var queryParameter      = GetQueryParameterByName(query, Strings.manifestParameterName);

            if (userData == null)
            {
                composeExtensionResponse = new MessagingExtensionResponse();
                string message = Strings.ComposeExtensionNoUserData;
                composeExtensionResponse.ComposeExtension = GetMessageResponseResult(message);
                return(composeExtensionResponse);
            }

            /**
             * Below are the checks for various states that may occur
             * Note that the order of many of these blocks of code do matter
             */

            // situation where the incoming payload was received from the config popup

            if (!string.IsNullOrEmpty(query.State))
            {
                /**
                 * // need to keep going to return a response so do not return here
                 * // these variables are changed so if the word 'setting' kicked off the compose extension,
                 * // then the word setting will not retrigger the config experience
                 **/

                queryParameter      = "";
                initialRunParameter = "true";
            }

            // this is a sitaution where the user's preferences have not been set up yet
            if (userData.ComposeExtensionCardType == null)
            {
                composeExtensionResponse = GetConfig();
                return(composeExtensionResponse);
            }

            /**
             * // this is the situation where the user has entered the word 'reset' and wants
             * // to clear his/her settings
             * // resetKeyword for English is "reset"
             **/

            if (string.Equals(queryParameter.ToLower(), Strings.ComposeExtensionResetKeyword))
            {
                composeExtensionResponse = new MessagingExtensionResponse();
                composeExtensionResponse.ComposeExtension = GetMessageResponseResult(Strings.ComposeExtensionResetText);
                return(composeExtensionResponse);
            }

            /**
             * // this is the situation where the user has entered "setting" or "settings" in order
             * // to repromt the config experience
             * // keywords for English are "setting" and "settings"
             **/

            if (string.Equals(queryParameter.ToLower(), Strings.ComposeExtensionSettingKeyword) || string.Equals(queryParameter.ToLower(), Strings.ComposeExtensionSettingsKeyword) || (isSettingUrl))
            {
                composeExtensionResponse = GetConfig();
                return(composeExtensionResponse);
            }

            /**
             * // this is the situation where the user in on the initial run of the compose extension
             * // e.g. when the user first goes to the compose extension and the search bar is still blank
             * // in order to get the compose extension to run the initial run, the setting "initialRun": true
             * // must be set in the manifest for the compose extension
             **/

            if (initialRunParameter == "true")
            {
                // Signin Experience, please uncomment below code for Signin Experience
                // ComposeExtensionResponse = GetSignin(composeExtensionResponse);
                // Return composeExtensionResponse;

                composeExtensionResponse = new MessagingExtensionResponse();

                var historySearchWikiResult = userData.ComposeExtensionSelectedResults;
                if (historySearchWikiResult != null)
                {
                    foreach (var searchResult in historySearchWikiResult)
                    {
                        WikiHelperSearchResult wikiSearchResult = new WikiHelperSearchResult(searchResult.imageUrl, searchResult.highlightedTitle, searchResult.text);

                        // Create the card itself and the preview card based upon the information
                        var createdCardAttachment = TemplateUtility.CreateComposeExtensionCardsAttachments(wikiSearchResult, userPreferredCardType);
                        composeExtensionAttachments.Add(createdCardAttachment);
                    }

                    composeExtensionResponse = GetComposeExtensionQueryResult(composeExtensionAttachments);
                }
                else
                {
                    composeExtensionResponse.ComposeExtension = GetMessageResponseResult(Strings.ComposeExtensionInitialRunText);
                }
                return(composeExtensionResponse);
            }

            /**
             * Below here is simply the logic to call the Wikipedia API and create the response for
             * a query; the general flow is to call the Wikipedia API for the query and then call the
             * Wikipedia API for each entry for the query to see if that entry has an image; in order
             * to get the asynchronous sections handled, an array of Promises for cards is used; each
             * Promise is resolved when it is discovered if an image exists for that entry; once all
             * of the Promises are resolved, the response is sent back to Teams
             */

            WikiResult wikiResult = await SearchWiki(queryParameter, query);

            // enumerate search results and build Promises for cards for response
            foreach (var searchResult in wikiResult.query.search)
            {
                //Get the Image result on the basis of Image Title one by one
                imageResult = await SearchWikiImage(searchResult);

                //Get the Image Url from imageResult
                string imageUrl = GetImageURL(imageResult);

                string cardText = searchResult.snippet + " ...";

                WikiHelperSearchResult wikiSearchResult = new WikiHelperSearchResult(imageUrl, searchResult.title, cardText);

                // Create the card itself and the preview card based upon the information
                var createdCardAttachment = TemplateUtility.CreateComposeExtensionCardsAttachments(wikiSearchResult, userPreferredCardType);
                composeExtensionAttachments.Add(createdCardAttachment);
            }

            composeExtensionResponse = GetComposeExtensionQueryResult(composeExtensionAttachments);

            return(composeExtensionResponse);
        }
        protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
        {
            var text = query?.Parameters?[0]?.Value as string ?? string.Empty;

            switch (text)
            {
            case "adaptive card":
                MessagingExtensionResponse response = GetAdaptiveCard();
                return(response);

            case "connector card":
                MessagingExtensionResponse connectorCard = GetConnectorCard();
                return(connectorCard);

            case "result grid":
                MessagingExtensionResponse resultGrid = GetResultGrid();
                return(resultGrid);
            }

            var packages = await FindPackages(text);

            // We take every row of the results and wrap them in cards wrapped in MessagingExtensionAttachment objects.
            // The Preview is optional, if it includes a Tap, that will trigger the OnTeamsMessagingExtensionSelectItemAsync event back on this bot.
            var attachments = packages.Select(package =>
            {
                var previewCard = new ThumbnailCard {
                    Title = package.Item1, Tap = new CardAction {
                        Type = "invoke", Value = package
                    }
                };
                if (!string.IsNullOrEmpty(package.Item5))
                {
                    previewCard.Images = new List <CardImage>()
                    {
                        new CardImage(package.Item5, "Icon")
                    };
                }

                var attachment = new MessagingExtensionAttachment
                {
                    ContentType = HeroCard.ContentType,
                    Content     = new HeroCard {
                        Title = package.Item1
                    },
                    Preview = previewCard.ToAttachment()
                };

                return(attachment);
            }).ToList();

            // The list of MessagingExtensionAttachments must we wrapped in a MessagingExtensionResult wrapped in a MessagingExtensionResponse.
            return(new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type = "result",
                    AttachmentLayout = "list",
                    Attachments = attachments
                }
            });
        }
        /// <summary>
        /// Handle the callback received when the user selects an item from the results list
        /// </summary>
        /// <param name="activity"></param>
        /// <returns></returns>
        public async Task <MessagingExtensionResponse> HandleComposeExtensionSelectedItem(ITurnContext <IInvokeActivity> turnContext, MessagingExtensionQuery query)
        {
            // Keep a history of recently-selected items in bot user data. The history will be returned in response to the initialRun query
            var userData = await TemplateUtility.GetBotUserDataObject(_userState, turnContext, query);

            //Get the Max number of History items from config file
            int maxComposeExtensionHistoryCount = Convert.ToInt32(ConfigurationManager.AppSettings[MaxComposeExtensionHistoryCountKey]);

            WikiHelperSearchResult selectedItem = JsonConvert.DeserializeObject <WikiHelperSearchResult>(turnContext.Activity.Value.ToString());

            var historySearchWikiResult = userData.ComposeExtensionSelectedResults;

            //Removing other occurrences of the current selectedItem so there are not duplicates in the most recently used list
            if (historySearchWikiResult != null && historySearchWikiResult.Count > 0)
            {
                int index = 0;
                while (index < historySearchWikiResult.Count)
                {
                    if (string.Equals(historySearchWikiResult[index].highlightedTitle.ToLower(), selectedItem.highlightedTitle.ToLower()))
                    {
                        historySearchWikiResult.RemoveAt(index);
                    }
                    else
                    {
                        index++;
                    }
                }
            }
            else
            {
                historySearchWikiResult = new List <WikiHelperSearchResult>();
            }

            //Add new item in list
            historySearchWikiResult.Insert(0, selectedItem);

            //Restrict the transaction History with Max Items.
            if (historySearchWikiResult.Count > maxComposeExtensionHistoryCount)
            {
                historySearchWikiResult = historySearchWikiResult.GetRange(0, maxComposeExtensionHistoryCount);
            }

            //Save the history Items in user Data
            await TemplateUtility.SaveBotUserDataObject(_userState, turnContext, historySearchWikiResult);

            MessagingExtensionResponse          composeExtensionResponse   = new MessagingExtensionResponse();
            List <MessagingExtensionAttachment> composeExtensionAttachment = new List <MessagingExtensionAttachment>();

            if (selectedItem != null)
            {
                // create the card itself and the preview card based upon the information
                // check user preference for which type of card to create
                var userPreferredCardType = userData.ComposeExtensionCardType;
                var createdCardAttachment = TemplateUtility.CreateComposeExtensionCardsAttachmentsSelectedItem(selectedItem, userPreferredCardType);
                composeExtensionAttachment.Add(createdCardAttachment);

                composeExtensionResponse = GetComposeExtensionQueryResult(composeExtensionAttachment);
            }
            else
            {
                composeExtensionResponse.ComposeExtension = GetMessageResponseResult(Strings.ComposeExtensionInitialRunText);
            }

            return(composeExtensionResponse);
        }