예제 #1
0
        /// <summary>
        /// Standard BotBuilder adapter method to send a message from the bot to the messaging API.
        /// </summary>
        /// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
        /// <param name="activities">An array of outgoing activities to be sent back to the messaging API.</param>
        /// <param name="cancellationToken">A cancellation token for the task.</param>
        /// <returns>An array of <see cref="ResourceResponse"/> objects containing the IDs that Slack assigned to the sent messages.</returns>
        public override async Task <ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
        {
            if (turnContext == null)
            {
                throw new ArgumentNullException(nameof(turnContext));
            }

            if (activities == null)
            {
                throw new ArgumentNullException(nameof(activities));
            }

            var responses = new List <ResourceResponse>();

            foreach (var activity in activities)
            {
                if (activity.Type != ActivityTypes.Message)
                {
                    _logger.LogTrace($"Unsupported Activity Type: '{activity.Type}'. Only Activities of type 'Message' are supported.");
                }
                else
                {
                    var message = SlackHelper.ActivityToSlack(activity);

                    var slackResponse = await _slackClient.PostMessageAsync(message, cancellationToken)
                                        .ConfigureAwait(false);

                    if (slackResponse != null && slackResponse.Ok)
                    {
                        var resourceResponse = new ActivityResourceResponse()
                        {
                            Id           = slackResponse.Ts,
                            ActivityId   = slackResponse.Ts,
                            Conversation = new ConversationAccount()
                            {
                                Id = slackResponse.Channel,
                            },
                        };
                        responses.Add(resourceResponse);
                    }
                }
            }

            return(responses.ToArray());
        }
예제 #2
0
        /// <summary>
        /// Standard BotBuilder adapter method to update a previous message with new content.
        /// </summary>
        /// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
        /// <param name="activity">The updated activity in the form '{id: `id of activity to update`, ...}'.</param>
        /// <param name="cancellationToken">A cancellation token for the task.</param>
        /// <returns>A resource response with the Id of the updated activity.</returns>
        public override async Task <ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
        {
            if (turnContext == null)
            {
                throw new ArgumentNullException(nameof(turnContext));
            }

            if (activity == null)
            {
                throw new ArgumentNullException(nameof(activity));
            }

            if (activity.Id == null)
            {
                throw new ArgumentException(nameof(activity.Timestamp));
            }

            if (activity.Conversation == null)
            {
                throw new ArgumentException(nameof(activity.ChannelId));
            }

            var message = SlackHelper.ActivityToSlack(activity);

            var results = await _slackClient.UpdateAsync(message.Ts, message.Channel, message.Text, cancellationToken : cancellationToken).ConfigureAwait(false);

            if (!results.Ok)
            {
                throw new InvalidOperationException($"Error updating activity on Slack:{results}");
            }

            return(new ResourceResponse()
            {
                Id = activity.Id,
            });
        }
예제 #3
0
        /// <summary>
        /// Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic.
        /// </summary>
        /// <param name="request">The incoming HTTP request.</param>
        /// <param name="response">When this method completes, the HTTP response to send.</param>
        /// <param name="bot">The bot that will handle the incoming activity.</param>
        /// <param name="cancellationToken">A cancellation token for the task.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task ProcessAsync(HttpRequest request, HttpResponse response, IBot bot, CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            if (bot == null)
            {
                throw new ArgumentNullException(nameof(bot));
            }

            string body;

            Activity activity = null;

            using (var sr = new StreamReader(request.Body))
            {
                body = await sr.ReadToEndAsync().ConfigureAwait(false);
            }

            if (_options.VerifyIncomingRequests && !_slackClient.VerifySignature(request, body))
            {
                const string text = "Rejected due to mismatched header signature";
                await SlackHelper.WriteAsync(response, HttpStatusCode.Unauthorized, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);

                throw new AuthenticationException(text);
            }

            var requestContentType = request.Headers["Content-Type"].ToString();

            if (requestContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
            {
                var postValues = SlackHelper.QueryStringToDictionary(body);

                if (postValues.ContainsKey("payload"))
                {
                    var payload = JsonConvert.DeserializeObject <InteractionPayload>(postValues["payload"]);
                    activity = SlackHelper.PayloadToActivity(payload);
                }
                else if (postValues.ContainsKey("command"))
                {
                    var serializedPayload = JsonConvert.SerializeObject(postValues);
                    var payload           = JsonConvert.DeserializeObject <CommandPayload>(serializedPayload);
                    activity = SlackHelper.CommandToActivity(payload, _slackClient);
                }
            }
            else if (requestContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            {
                var bodyObject = JObject.Parse(body);

                if (bodyObject["type"]?.ToString() == "url_verification")
                {
                    var verificationEvent = bodyObject.ToObject <UrlVerificationEvent>();
                    await SlackHelper.WriteAsync(response, HttpStatusCode.OK, verificationEvent.Challenge, Encoding.UTF8, cancellationToken).ConfigureAwait(false);

                    return;
                }

                if (!string.IsNullOrWhiteSpace(_slackClient.Options.SlackVerificationToken) && bodyObject["token"]?.ToString() != _slackClient.Options.SlackVerificationToken)
                {
                    var text = $"Rejected due to mismatched verificationToken:{bodyObject["token"]}";
                    await SlackHelper.WriteAsync(response, HttpStatusCode.Forbidden, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);

                    throw new AuthenticationException(text);
                }

                if (bodyObject["type"]?.ToString() == "event_callback")
                {
                    // this is an event api post
                    var eventRequest = bodyObject.ToObject <EventRequest>();
                    activity = SlackHelper.EventToActivity(eventRequest, _slackClient);
                }
            }

            // As per official Slack API docs, some additional request types may be receieved that can be ignored
            // but we should respond with a 200 status code
            // https://api.slack.com/interactivity/slash-commands
            if (activity == null)
            {
                await SlackHelper.WriteAsync(response, HttpStatusCode.OK, "Unable to transform request / payload into Activity. Possible unrecognized request type", Encoding.UTF8, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                using (var context = new TurnContext(this, activity))
                {
                    context.TurnState.Add("httpStatus", ((int)HttpStatusCode.OK).ToString(System.Globalization.CultureInfo.InvariantCulture));

                    await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);

                    var code       = Convert.ToInt32(context.TurnState.Get <string>("httpStatus"), System.Globalization.CultureInfo.InvariantCulture);
                    var statusCode = (HttpStatusCode)code;
                    var text       = context.TurnState.Get <object>("httpBody") != null?context.TurnState.Get <object>("httpBody").ToString() : string.Empty;

                    await SlackHelper.WriteAsync(response, statusCode, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
                }
            }
        }