/// <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 (dc.Context.Activity.ChannelId != Channels.Msteams)
            {
                throw new InvalidOperationException($"{Kind} works only on the Teams channel.");
            }

            IActivity activity = null;

            if (Activity != null)
            {
                activity = await Activity.BindAsync(dc, dc.State).ConfigureAwait(false);
            }

            string teamsChannelId = TeamsChannelId.GetValueOrNull(dc.State);

            if (string.IsNullOrEmpty(teamsChannelId))
            {
                teamsChannelId = dc.Context.Activity.TeamsGetChannelId();
            }

            if (!(dc.Context.Adapter is BotFrameworkAdapter))
            {
                throw new InvalidOperationException($"{Kind} is not supported by the current adapter.");
            }

            // TODO: this will NOT work with certificate app credentials

            // TeamsInfo.SendMessageToTeamsChannelAsync requires AppCredentials
            var credentials = dc.Context.TurnState.Get <IConnectorClient>()?.Credentials as MicrosoftAppCredentials;

            if (credentials == null)
            {
                throw new InvalidOperationException($"Missing credentials as {nameof(MicrosoftAppCredentials)} in {nameof(IConnectorClient)} from TurnState");
            }

            // The result comes back as a tuple, which is used to set the two properties (if present).
            var result = await TeamsInfo.SendMessageToTeamsChannelAsync(dc.Context, activity, teamsChannelId, credentials, cancellationToken : cancellationToken).ConfigureAwait(false);

            if (ConversationReferenceProperty != null)
            {
                dc.State.SetValue(ConversationReferenceProperty.GetValue(dc.State), result.Item1);
            }

            if (ActivityIdProperty != null)
            {
                dc.State.SetValue(ActivityIdProperty.GetValue(dc.State), result.Item2);
            }

            return(await dc.EndDialogAsync(result, cancellationToken : cancellationToken).ConfigureAwait(false));
        }
 /// <inheritdoc/>
 protected override string OnComputeId()
 {
     return($"{GetType().Name}[{TeamsChannelId?.ToString() ?? string.Empty},{ActivityIdProperty?.ToString() ?? string.Empty},{ConversationReferenceProperty?.ToString() ?? string.Empty}]");
 }
        /// <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 (dc.Context.Activity.ChannelId != Channels.Msteams)
            {
                throw new InvalidOperationException($"{Kind} works only on the Teams channel.");
            }

            IActivity activity = null;

            if (Activity != null)
            {
                activity = await Activity.BindAsync(dc, dc.State).ConfigureAwait(false);
            }

            string teamsChannelId = TeamsChannelId.GetValueOrNull(dc.State);

            if (string.IsNullOrEmpty(teamsChannelId))
            {
                teamsChannelId = dc.Context.Activity.TeamsGetChannelId();
            }

            Tuple <ConversationReference, string> result;

            // Check for legacy adapter
            if (dc.Context.Adapter is BotFrameworkAdapter)
            {
                // TeamsInfo.SendMessageToTeamsChannelAsync requires AppCredentials
                var credentials = dc.Context.TurnState.Get <IConnectorClient>()?.Credentials as MicrosoftAppCredentials;
                if (credentials == null)
                {
                    throw new InvalidOperationException($"Missing credentials as {nameof(MicrosoftAppCredentials)} in {nameof(IConnectorClient)} from TurnState");
                }

                // The result comes back as a tuple, which is used to set the two properties (if present).
                result = await TeamsInfo.SendMessageToTeamsChannelAsync(dc.Context, activity, teamsChannelId, credentials, cancellationToken : cancellationToken).ConfigureAwait(false);
            }
            else if (dc.Context.Adapter is CloudAdapterBase)
            {
                // Retrieve the bot appid from TurnState's ClaimsIdentity
                string appId;
                if (dc.Context.TurnState.Get <ClaimsIdentity>(BotAdapter.BotIdentityKey) is ClaimsIdentity botIdentity)
                {
                    // Apparently 'version' is sometimes empty, which will result in no id returned from GetAppIdFromClaims
                    appId = JwtTokenValidation.GetAppIdFromClaims(botIdentity.Claims);
                    if (string.IsNullOrEmpty(appId))
                    {
                        appId = botIdentity.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.AudienceClaim)?.Value;
                    }

                    if (string.IsNullOrEmpty(appId))
                    {
                        throw new InvalidOperationException($"Missing AppIdClaim in ClaimsIdentity.");
                    }
                }
                else
                {
                    throw new InvalidOperationException($"Missing {BotAdapter.BotIdentityKey} in {nameof(ITurnContext)} TurnState.");
                }

                // The result comes back as a tuple, which is used to set the two properties (if present).
                result = await TeamsInfo.SendMessageToTeamsChannelAsync(dc.Context, activity, teamsChannelId, appId, cancellationToken : cancellationToken).ConfigureAwait(false);
            }
            else
            {
                throw new InvalidOperationException($"The adapter does not support {nameof(SendMessageToTeamsChannel)}.");
            }

            if (ConversationReferenceProperty != null)
            {
                dc.State.SetValue(ConversationReferenceProperty.GetValue(dc.State), result.Item1);
            }

            if (ActivityIdProperty != null)
            {
                dc.State.SetValue(ActivityIdProperty.GetValue(dc.State), result.Item2);
            }

            return(await dc.EndDialogAsync(result, cancellationToken : cancellationToken).ConfigureAwait(false));
        }