From 286ddf86da08f366966bfb363207b5d4ff7c00ca Mon Sep 17 00:00:00 2001 From: Simon Oxtoby Date: Sun, 23 Jun 2019 21:38:07 +1000 Subject: [PATCH] Block interaction --- SlackNet.AspNetCore/AspNetCoreExtensions.cs | 2 + .../ResolvedBlockActionHandler.cs | 25 ++++ .../ResolvedBlockOptionProvider.cs | 34 +++++ .../ResolvedMessageActionHandler.cs | 21 +-- .../SlackBlockActionsService.cs | 22 ++++ .../SlackBlockOptionsService.cs | 20 +++ SlackNet.AspNetCore/SlackEventsMiddleware.cs | 48 +++++-- .../SlackMessageActionsService.cs | 8 +- .../SlackServiceConfiguration.cs | 77 ++++++++++- .../SpecificBlockActionHandler.cs | 25 ++++ .../SpecificMessageActionHandler.cs | 23 ++++ SlackNet.BotExample/PingHandler.cs | 2 +- SlackNet.BotExample/Program.cs | 4 +- SlackNet.EventsExample/BlockColorSelector.cs | 55 ++++++++ SlackNet.EventsExample/BlockCounter.cs | 85 +++++++++++++ SlackNet.EventsExample/BlockDialogDemo.cs | 39 ++++++ SlackNet.EventsExample/ColorSelector.cs | 12 +- SlackNet.EventsExample/DialogDemo.cs | 120 ------------------ SlackNet.EventsExample/DialogDemoBase.cs | 68 ++++++++++ SlackNet.EventsExample/DialogDemoHandler.cs | 54 ++++++++ .../{Counter.cs => LegacyCounter.cs} | 5 +- SlackNet.EventsExample/LegacyDialogDemo.cs | 34 +++++ SlackNet.EventsExample/MessageHandler.cs | 19 ++- SlackNet.EventsExample/Startup.cs | 23 +++- SlackNet.Tests/SerializationTests.cs | 40 ++++++ SlackNet/Blocks/BlockAction.cs | 15 +++ SlackNet/Blocks/ButtonAction.cs | 9 ++ SlackNet/Blocks/ChannelSelectAction.cs | 8 ++ SlackNet/Blocks/ConversationSelectAction.cs | 8 ++ SlackNet/Blocks/DatePickerAction.cs | 10 ++ SlackNet/Blocks/ExternalSelectAction.cs | 9 ++ SlackNet/Blocks/OverflowAction.cs | 8 ++ SlackNet/Blocks/StaticSelectAction.cs | 8 ++ SlackNet/Blocks/UserSelectAction.cs | 8 ++ SlackNet/Interaction/BlockActionRequest.cs | 19 +++ SlackNet/Interaction/BlockOptionsRequest.cs | 14 ++ SlackNet/Interaction/BlockOptionsResponse.cs | 10 ++ SlackNet/Interaction/Container.cs | 12 ++ SlackNet/Interaction/DialogCancellation.cs | 1 + SlackNet/Interaction/DialogElement.cs | 4 +- SlackNet/Interaction/DialogElementType.cs | 11 -- SlackNet/Interaction/DialogSubmission.cs | 1 + SlackNet/Interaction/IBlockActionHandler.cs | 13 ++ SlackNet/Interaction/IBlockOptionProvider.cs | 9 ++ SlackNet/Interaction/IMessageActionHandler.cs | 2 +- SlackNet/Interaction/InteractionRequest.cs | 1 - SlackNet/Interaction/InteractiveMessage.cs | 1 + SlackNet/Interaction/MessageAction.cs | 1 + SlackNet/Interaction/MessageActionResponse.cs | 11 -- SlackNet/Interaction/OptionsRequest.cs | 8 +- SlackNet/Interaction/OptionsRequestBase.cs | 12 ++ SlackNet/Interaction/SelectElement.cs | 2 +- SlackNet/Interaction/SlackBlockActions.cs | 30 +++++ SlackNet/Interaction/SlackBlockOptions.cs | 23 ++++ SlackNet/Interaction/SlackMessageActions.cs | 14 +- SlackNet/Interaction/TextArea.cs | 2 +- SlackNet/Interaction/TextElement.cs | 2 +- SlackNet/Interaction/TextElementBase.cs | 2 +- 58 files changed, 936 insertions(+), 217 deletions(-) create mode 100644 SlackNet.AspNetCore/ResolvedBlockActionHandler.cs create mode 100644 SlackNet.AspNetCore/ResolvedBlockOptionProvider.cs create mode 100644 SlackNet.AspNetCore/SlackBlockActionsService.cs create mode 100644 SlackNet.AspNetCore/SlackBlockOptionsService.cs create mode 100644 SlackNet.AspNetCore/SpecificBlockActionHandler.cs create mode 100644 SlackNet.AspNetCore/SpecificMessageActionHandler.cs create mode 100644 SlackNet.EventsExample/BlockColorSelector.cs create mode 100644 SlackNet.EventsExample/BlockCounter.cs create mode 100644 SlackNet.EventsExample/BlockDialogDemo.cs delete mode 100644 SlackNet.EventsExample/DialogDemo.cs create mode 100644 SlackNet.EventsExample/DialogDemoBase.cs create mode 100644 SlackNet.EventsExample/DialogDemoHandler.cs rename SlackNet.EventsExample/{Counter.cs => LegacyCounter.cs} (91%) create mode 100644 SlackNet.EventsExample/LegacyDialogDemo.cs create mode 100644 SlackNet/Blocks/BlockAction.cs create mode 100644 SlackNet/Blocks/ButtonAction.cs create mode 100644 SlackNet/Blocks/ChannelSelectAction.cs create mode 100644 SlackNet/Blocks/ConversationSelectAction.cs create mode 100644 SlackNet/Blocks/DatePickerAction.cs create mode 100644 SlackNet/Blocks/ExternalSelectAction.cs create mode 100644 SlackNet/Blocks/OverflowAction.cs create mode 100644 SlackNet/Blocks/StaticSelectAction.cs create mode 100644 SlackNet/Blocks/UserSelectAction.cs create mode 100644 SlackNet/Interaction/BlockActionRequest.cs create mode 100644 SlackNet/Interaction/BlockOptionsRequest.cs create mode 100644 SlackNet/Interaction/BlockOptionsResponse.cs create mode 100644 SlackNet/Interaction/Container.cs delete mode 100644 SlackNet/Interaction/DialogElementType.cs create mode 100644 SlackNet/Interaction/IBlockActionHandler.cs create mode 100644 SlackNet/Interaction/IBlockOptionProvider.cs delete mode 100644 SlackNet/Interaction/MessageActionResponse.cs create mode 100644 SlackNet/Interaction/OptionsRequestBase.cs create mode 100644 SlackNet/Interaction/SlackBlockActions.cs create mode 100644 SlackNet/Interaction/SlackBlockOptions.cs diff --git a/SlackNet.AspNetCore/AspNetCoreExtensions.cs b/SlackNet.AspNetCore/AspNetCoreExtensions.cs index 5cd4923..f0d25fb 100644 --- a/SlackNet.AspNetCore/AspNetCoreExtensions.cs +++ b/SlackNet.AspNetCore/AspNetCoreExtensions.cs @@ -14,6 +14,8 @@ public static IServiceCollection AddSlackNet(this IServiceCollection serviceColl configure(configuration); Default.RegisterServices((serviceType, createService) => serviceCollection.AddTransient(serviceType, c => createService(c.GetService))); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/SlackNet.AspNetCore/ResolvedBlockActionHandler.cs b/SlackNet.AspNetCore/ResolvedBlockActionHandler.cs new file mode 100644 index 0000000..96de744 --- /dev/null +++ b/SlackNet.AspNetCore/ResolvedBlockActionHandler.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using SlackNet.Blocks; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + class ResolvedBlockActionHandler : IBlockActionHandler + where TAction : BlockAction + where THandler : IBlockActionHandler + { + private readonly IServiceProvider _serviceProvider; + public ResolvedBlockActionHandler(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public async Task Handle(TAction action, BlockActionRequest request) + { + using (var scope = _serviceProvider.CreateScope()) + { + var handler = scope.ServiceProvider.GetRequiredService(); + await handler.Handle(action, request).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/SlackNet.AspNetCore/ResolvedBlockOptionProvider.cs b/SlackNet.AspNetCore/ResolvedBlockOptionProvider.cs new file mode 100644 index 0000000..37d7ce9 --- /dev/null +++ b/SlackNet.AspNetCore/ResolvedBlockOptionProvider.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + abstract class ResolvedBlockOptionProvider : IBlockOptionProvider + { + protected ResolvedBlockOptionProvider(string actionId) => ActionName = actionId; + + public string ActionName { get; } + + public abstract Task GetOptions(BlockOptionsRequest request); + } + + class ResolvedBlockOptionProvider : ResolvedBlockOptionProvider + where T : IBlockOptionProvider + { + private readonly IServiceProvider _serviceProvider; + + public ResolvedBlockOptionProvider(IServiceProvider serviceProvider, string actionId) + : base(actionId) + { + _serviceProvider = serviceProvider; + } + + public override Task GetOptions(BlockOptionsRequest request) + { + var handler = _serviceProvider.GetRequiredService(); + return handler.GetOptions(request); + } + } +} \ No newline at end of file diff --git a/SlackNet.AspNetCore/ResolvedMessageActionHandler.cs b/SlackNet.AspNetCore/ResolvedMessageActionHandler.cs index 6f79e74..b76370b 100644 --- a/SlackNet.AspNetCore/ResolvedMessageActionHandler.cs +++ b/SlackNet.AspNetCore/ResolvedMessageActionHandler.cs @@ -1,36 +1,27 @@ -using System; + +using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using SlackNet.Interaction; namespace SlackNet.AspNetCore { - abstract class ResolvedMessageActionHandler : IMessageActionHandler - { - protected ResolvedMessageActionHandler(string callbackId) => CallbackId = callbackId; - - public string CallbackId { get; } - - public abstract Task Handle(MessageAction request); - } - - class ResolvedMessageActionHandler : ResolvedMessageActionHandler + class ResolvedMessageActionHandler : IMessageActionHandler where T : IMessageActionHandler { private readonly IServiceProvider _serviceProvider; - public ResolvedMessageActionHandler(IServiceProvider serviceProvider, string callbackId) - : base(callbackId) + public ResolvedMessageActionHandler(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } - public override async Task Handle(MessageAction request) + public async Task Handle(MessageAction request) { using (var scope = _serviceProvider.CreateScope()) { var handler = scope.ServiceProvider.GetRequiredService(); - return await handler.Handle(request).ConfigureAwait(false); + await handler.Handle(request).ConfigureAwait(false); } } } diff --git a/SlackNet.AspNetCore/SlackBlockActionsService.cs b/SlackNet.AspNetCore/SlackBlockActionsService.cs new file mode 100644 index 0000000..15cba27 --- /dev/null +++ b/SlackNet.AspNetCore/SlackBlockActionsService.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SlackNet.Blocks; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + class SlackBlockActionsService : ISlackBlockActions + { + private readonly SlackBlockActions _slackBlockActions = new SlackBlockActions(); + + public SlackBlockActionsService(IEnumerable handlers) + { + foreach (var handler in handlers) + AddHandler((dynamic)handler); + } + + public Task Handle(BlockActionRequest request) => _slackBlockActions.Handle(request); + + public void AddHandler(IBlockActionHandler handler) where TAction : BlockAction => _slackBlockActions.AddHandler(handler); + } +} \ No newline at end of file diff --git a/SlackNet.AspNetCore/SlackBlockOptionsService.cs b/SlackNet.AspNetCore/SlackBlockOptionsService.cs new file mode 100644 index 0000000..ed20930 --- /dev/null +++ b/SlackNet.AspNetCore/SlackBlockOptionsService.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + class SlackBlockOptionsService : ISlackBlockOptions + { + private readonly ISlackBlockOptions _options = new SlackBlockOptions(); + + public SlackBlockOptionsService(IEnumerable providers) + { + foreach (var provider in providers) + _options.SetProvider(provider.ActionName, provider); + } + + public Task Handle(BlockOptionsRequest request) => _options.Handle(request); + public void SetProvider(string actionId, IBlockOptionProvider handler) => _options.SetProvider(actionId, handler); + } +} \ No newline at end of file diff --git a/SlackNet.AspNetCore/SlackEventsMiddleware.cs b/SlackNet.AspNetCore/SlackEventsMiddleware.cs index ce112e3..3f5c8d5 100644 --- a/SlackNet.AspNetCore/SlackEventsMiddleware.cs +++ b/SlackNet.AspNetCore/SlackEventsMiddleware.cs @@ -15,6 +15,8 @@ class SlackEventsMiddleware private readonly RequestDelegate _next; private readonly SlackEndpointConfiguration _configuration; private readonly ISlackEvents _slackEvents; + private readonly ISlackBlockActions _slackBlockActions; + private readonly ISlackBlockOptions _slackBlockOptions; private readonly ISlackInteractiveMessages _slackInteractiveMessages; private readonly ISlackMessageActions _slackMessageActions; private readonly ISlackOptions _slackOptions; @@ -25,15 +27,19 @@ public SlackEventsMiddleware( RequestDelegate next, SlackEndpointConfiguration configuration, ISlackEvents slackEvents, + ISlackBlockActions slackBlockActions, + ISlackBlockOptions slackBlockOptions, ISlackInteractiveMessages slackInteractiveMessages, ISlackMessageActions slackMessageActions, - ISlackOptions slackOptions, + ISlackOptions slackOptions, IDialogSubmissionHandler dialogSubmissionHandler, SlackJsonSettings jsonSettings) { _next = next; _configuration = configuration; _slackEvents = slackEvents; + _slackBlockActions = slackBlockActions; + _slackBlockOptions = slackBlockOptions; _slackInteractiveMessages = slackInteractiveMessages; _slackMessageActions = slackMessageActions; _slackOptions = slackOptions; @@ -87,6 +93,8 @@ private async Task HandleSlackAction(HttpContext context) { switch (interactionRequest) { + case BlockActionRequest blockActions: + return await HandleBlockActions(context, blockActions).ConfigureAwait(false); case InteractiveMessage interactiveMessage: return await HandleInteractiveMessage(context, interactiveMessage).ConfigureAwait(false); case DialogSubmission dialogSubmission: @@ -101,6 +109,12 @@ private async Task HandleSlackAction(HttpContext context) return await context.Respond(HttpStatusCode.BadRequest, body: "Invalid token or unrecognized content").ConfigureAwait(false); } + private async Task HandleBlockActions(HttpContext context, BlockActionRequest blockActionRequest) + { + await _slackBlockActions.Handle(blockActionRequest).ConfigureAwait(false); + return await context.Respond(HttpStatusCode.OK).ConfigureAwait(false); + } + private async Task HandleInteractiveMessage(HttpContext context, InteractiveMessage interactiveMessage) { var response = await _slackInteractiveMessages.Handle(interactiveMessage).ConfigureAwait(false); @@ -130,13 +144,8 @@ private async Task HandleDialogCancellation(HttpContext context, D private async Task HandleMessageAction(HttpContext context, MessageAction messageAction) { - var response = await _slackMessageActions.Handle(messageAction).ConfigureAwait(false); - - var responseJson = response == null - ? null - : Serialize(response); - - return await context.Respond(HttpStatusCode.OK, "application/json", responseJson).ConfigureAwait(false); + await _slackMessageActions.Handle(messageAction).ConfigureAwait(false); + return await context.Respond(HttpStatusCode.OK).ConfigureAwait(false); } private async Task HandleSlackOptions(HttpContext context) @@ -144,17 +153,34 @@ private async Task HandleSlackOptions(HttpContext context) if (context.Request.Method != "POST") return await context.Respond(HttpStatusCode.MethodNotAllowed).ConfigureAwait(false); - var optionsRequest = await DeserializePayload(context).ConfigureAwait(false); + var optionsRequest = await DeserializePayload(context).ConfigureAwait(false); if (optionsRequest != null && IsValidToken(optionsRequest.Token)) { - var response = await _slackOptions.Handle(optionsRequest).ConfigureAwait(false); - return await context.Respond(HttpStatusCode.OK, "application/json", Serialize(response)).ConfigureAwait(false); + switch (optionsRequest) + { + case OptionsRequest legacyOptionsRequest: + return await HandleLegacyOptionsRequest(context, legacyOptionsRequest).ConfigureAwait(false); + case BlockOptionsRequest blockOptionsRequest: + return await HandleBlockOptionsRequest(context, blockOptionsRequest).ConfigureAwait(false); + } } return await context.Respond(HttpStatusCode.BadRequest, body: "Invalid token or unrecognized content").ConfigureAwait(false); } + private async Task HandleLegacyOptionsRequest(HttpContext context, OptionsRequest optionsRequest) + { + var response = await _slackOptions.Handle(optionsRequest).ConfigureAwait(false); + return await context.Respond(HttpStatusCode.OK, "application/json", Serialize(response)).ConfigureAwait(false); + } + + private async Task HandleBlockOptionsRequest(HttpContext context, BlockOptionsRequest blockOptionsRequest) + { + var response = await _slackBlockOptions.Handle(blockOptionsRequest).ConfigureAwait(false); + return await context.Respond(HttpStatusCode.OK, "application/json", Serialize(response)).ConfigureAwait(false); + } + private async Task DeserializePayload(HttpContext context) { var form = await context.Request.ReadFormAsync().ConfigureAwait(false); diff --git a/SlackNet.AspNetCore/SlackMessageActionsService.cs b/SlackNet.AspNetCore/SlackMessageActionsService.cs index 388bd79..70ba53e 100644 --- a/SlackNet.AspNetCore/SlackMessageActionsService.cs +++ b/SlackNet.AspNetCore/SlackMessageActionsService.cs @@ -8,13 +8,13 @@ class SlackMessageActionsService : ISlackMessageActions { private readonly ISlackMessageActions _actions = new SlackMessageActions(); - public SlackMessageActionsService(IEnumerable handlers) + public SlackMessageActionsService(IEnumerable handlers) { foreach (var handler in handlers) - _actions.SetHandler(handler.CallbackId, handler); + AddHandler(handler); } - public Task Handle(MessageAction request) => _actions.Handle(request); - public void SetHandler(string callbackId, IMessageActionHandler handler) => _actions.SetHandler(callbackId, handler); + public Task Handle(MessageAction request) => _actions.Handle(request); + public void AddHandler(IMessageActionHandler handler) => _actions.AddHandler(handler); } } \ No newline at end of file diff --git a/SlackNet.AspNetCore/SlackServiceConfiguration.cs b/SlackNet.AspNetCore/SlackServiceConfiguration.cs index c2292cd..da19504 100644 --- a/SlackNet.AspNetCore/SlackServiceConfiguration.cs +++ b/SlackNet.AspNetCore/SlackServiceConfiguration.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; +using SlackNet.Blocks; using SlackNet.Events; using SlackNet.Interaction; @@ -24,7 +26,48 @@ public SlackServiceConfiguration RegisterEventHandler() where THandler : class, IEventHandler { _serviceCollection.AddTransient(); - _serviceCollection.AddSingleton>(); + return RegisterEventHandler(c => new ResolvedEventHandler(c)); + } + + public SlackServiceConfiguration RegisterEventHandler(IEventHandler handler) + where TEvent : Event + { + return RegisterEventHandler(c => handler); + } + + public SlackServiceConfiguration RegisterEventHandler(Func> handlerFactory) + where TEvent : Event + { + _serviceCollection.AddSingleton(handlerFactory); + return this; + } + + public SlackServiceConfiguration RegisterBlockActionHandler() + where TAction : BlockAction + where THandler : class, IBlockActionHandler + { + _serviceCollection.AddTransient(); + return RegisterBlockActionHandler(c => new ResolvedBlockActionHandler(c)); + } + + public SlackServiceConfiguration RegisterBlockActionHandler(string actionId) + where TAction : BlockAction + where THandler : class, IBlockActionHandler + { + _serviceCollection.AddTransient(); + return RegisterBlockActionHandler(c => new SpecificBlockActionHandler(actionId, new ResolvedBlockActionHandler(c))); + } + + public SlackServiceConfiguration RegisterBlockActionHandler(IBlockActionHandler handler) + where TAction : BlockAction + { + return RegisterBlockActionHandler(c => handler); + } + + public SlackServiceConfiguration RegisterBlockActionHandler(Func> handlerFactory) + where TAction : BlockAction + { + _serviceCollection.AddSingleton(handlerFactory); return this; } @@ -36,11 +79,28 @@ public SlackServiceConfiguration RegisterInteractiveMessageHandler(str return this; } + public SlackServiceConfiguration RegisterMessageActionHandler() + where THandler : class, IMessageActionHandler + { + _serviceCollection.AddTransient(); + return RegisterMessageActionHandler(c => new ResolvedMessageActionHandler(c)); + } + public SlackServiceConfiguration RegisterMessageActionHandler(string callbackId) where THandler : class, IMessageActionHandler { _serviceCollection.AddTransient(); - _serviceCollection.AddSingleton(c => new ResolvedMessageActionHandler(c, callbackId)); + return RegisterMessageActionHandler(c => new SpecificMessageActionHandler(callbackId, new ResolvedMessageActionHandler(c))); + } + + public SlackServiceConfiguration RegisterMessageActionHandler(IMessageActionHandler handler) + { + return RegisterMessageActionHandler(c => handler); + } + + public SlackServiceConfiguration RegisterMessageActionHandler(Func handlerFactory) + { + _serviceCollection.AddSingleton(handlerFactory); return this; } @@ -52,9 +112,18 @@ public SlackServiceConfiguration RegisterOptionProvider(string action return this; } + public SlackServiceConfiguration RegisterBlockOptionProvider(string actionId) + where TProvider : class, IBlockOptionProvider + { + _serviceCollection.AddTransient(); + _serviceCollection.AddSingleton(c => new ResolvedBlockOptionProvider(c, actionId)); + return this; + } + public SlackServiceConfiguration RegisterDialogSubmissionHandler() - where THandler : IDialogSubmissionHandler + where THandler : class, IDialogSubmissionHandler { + _serviceCollection.AddTransient(); _serviceCollection.AddSingleton(c => new ResolvedDialogSubmissionHandler(c)); return this; } diff --git a/SlackNet.AspNetCore/SpecificBlockActionHandler.cs b/SlackNet.AspNetCore/SpecificBlockActionHandler.cs new file mode 100644 index 0000000..814f79a --- /dev/null +++ b/SlackNet.AspNetCore/SpecificBlockActionHandler.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using SlackNet.Blocks; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + class SpecificBlockActionHandler : IBlockActionHandler + where TAction : BlockAction + { + private readonly string _actionId; + private readonly IBlockActionHandler _handler; + + public SpecificBlockActionHandler(string actionId, IBlockActionHandler handler) + { + _actionId = actionId; + _handler = handler; + } + + public async Task Handle(TAction action, BlockActionRequest request) + { + if (request.Action.ActionId == _actionId) + await _handler.Handle(action, request).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/SlackNet.AspNetCore/SpecificMessageActionHandler.cs b/SlackNet.AspNetCore/SpecificMessageActionHandler.cs new file mode 100644 index 0000000..90293f9 --- /dev/null +++ b/SlackNet.AspNetCore/SpecificMessageActionHandler.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using SlackNet.Interaction; + +namespace SlackNet.AspNetCore +{ + public class SpecificMessageActionHandler : IMessageActionHandler + { + private readonly string _callbackId; + private readonly IMessageActionHandler _handler; + + public SpecificMessageActionHandler(string callbackId, IMessageActionHandler handler) + { + _callbackId = callbackId; + _handler = handler; + } + + public async Task Handle(MessageAction request) + { + if (request.CallbackId == _callbackId) + await _handler.Handle(request).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/SlackNet.BotExample/PingHandler.cs b/SlackNet.BotExample/PingHandler.cs index d42c87e..b2c5a99 100644 --- a/SlackNet.BotExample/PingHandler.cs +++ b/SlackNet.BotExample/PingHandler.cs @@ -18,7 +18,7 @@ await message.ReplyWith(new BotMessage { Text = "pong", Attachments = { new Attachment { Text = $"Count: {++_count}" } } - }); + }).ConfigureAwait(false); } } } diff --git a/SlackNet.BotExample/Program.cs b/SlackNet.BotExample/Program.cs index 296bef7..80cc768 100644 --- a/SlackNet.BotExample/Program.cs +++ b/SlackNet.BotExample/Program.cs @@ -20,10 +20,10 @@ private static async Task Run(string token) { bot.AddHandler(new PingHandler()); - await bot.Connect(); + await bot.Connect().ConfigureAwait(false); Console.WriteLine("Connected"); - await WaitForKeyPress(); + await WaitForKeyPress().ConfigureAwait(false); } } diff --git a/SlackNet.EventsExample/BlockColorSelector.cs b/SlackNet.EventsExample/BlockColorSelector.cs new file mode 100644 index 0000000..6c64bcb --- /dev/null +++ b/SlackNet.EventsExample/BlockColorSelector.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Threading.Tasks; +using SlackNet.Blocks; +using SlackNet.Interaction; +using SlackNet.WebApi; + +namespace SlackNet.EventsExample +{ + public class BlockColorSelector : IBlockActionHandler, IBlockOptionProvider + { + private readonly ISlackApiClient _slack; + public static readonly string ActionId = "color_select"; + + public BlockColorSelector(ISlackApiClient slack) + { + _slack = slack; + } + + public async Task Handle(ExternalSelectAction select, BlockActionRequest request) + { + await _slack.Chat.PostMessage(new Message + { + Text = $"Selected color: {select.SelectedOption.Text.Text} ({select.SelectedOption.Value})", + Channel = request.Channel.Id + }).ConfigureAwait(false); + } + + public async Task GetOptions(BlockOptionsRequest request) => new BlockOptionsResponse { Options = GetBlockOptions(request.Value) }; + + private static List GetBlockOptions(string search) => + FindColors(search) + .Select(c => new Blocks.Option { Text = c.Name, Value = $"#{c.R:X2}{c.G:X2}{c.B:X2}" }) + .ToList(); + + private static IEnumerable FindColors(string search) => + new ColorConverter().GetStandardValues() + .Cast() + .Where(c => c.Name.ToUpperInvariant().Contains(search.ToUpperInvariant())); + + public static IEnumerable Blocks => new Block[] + { + new SectionBlock + { + Text = "Choose a color", + Accessory = new ExternalSelectMenu + { + ActionId = ActionId, + Placeholder = new PlainText("Select a color") + } + } + }; + } +} \ No newline at end of file diff --git a/SlackNet.EventsExample/BlockCounter.cs b/SlackNet.EventsExample/BlockCounter.cs new file mode 100644 index 0000000..84d77c8 --- /dev/null +++ b/SlackNet.EventsExample/BlockCounter.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using SlackNet.Blocks; +using SlackNet.Interaction; +using SlackNet.WebApi; + +namespace SlackNet.EventsExample +{ + public class BlockCounter : IBlockActionHandler + { + private readonly ISlackApiClient _slack; + private static readonly string ActionPrefix = "add"; + public static readonly string Add1 = ActionPrefix + "1"; + public static readonly string Add5 = ActionPrefix + "5"; + public static readonly string Add10 = ActionPrefix + "10"; + + private static readonly Regex _counterPattern = new Regex("Counter: (\\d+)"); + + public BlockCounter(ISlackApiClient slack) + { + _slack = slack; + } + + public async Task Handle(ButtonAction button, BlockActionRequest request) + { + var counter = SectionBeforeAddButtons(button, request); + if (counter != null) + { + var counterText = _counterPattern.Match(counter.Text.Text ?? string.Empty); + if (counterText.Success) + { + var count = int.Parse(counterText.Groups[1].Value); + var increment = int.Parse(((ButtonAction)request.Action).Value); + counter.Text = $"Counter: {count + increment}"; + await _slack.Chat.Update(new MessageUpdate + { + Ts = request.Message.Ts, + Text = request.Message.Text, + Blocks = request.Message.Blocks, + Attachments = request.Message.Attachments, + ChannelId = request.Channel.Id + }).ConfigureAwait(false); + } + } + } + + private static SectionBlock SectionBeforeAddButtons(ButtonAction button, BlockActionRequest request) + { + return request.Message.Blocks + .TakeWhile(b => b.BlockId != button.BlockId) + .LastOrDefault() as SectionBlock; + } + + public static IEnumerable Blocks => new Block[] + { + new SectionBlock { Text = "Counter: 0" }, + new ActionsBlock + { + Elements = + { + new Blocks.Button + { + ActionId = Add1, + Value = "1", + Text = new PlainText("Add 1") + }, + new Blocks.Button + { + ActionId = Add5, + Value = "5", + Text = new PlainText("Add 5") + }, + new Blocks.Button + { + ActionId = Add10, + Value = "10", + Text = new PlainText("Add 10") + } + } + } + }; + } +} \ No newline at end of file diff --git a/SlackNet.EventsExample/BlockDialogDemo.cs b/SlackNet.EventsExample/BlockDialogDemo.cs new file mode 100644 index 0000000..d3cc45e --- /dev/null +++ b/SlackNet.EventsExample/BlockDialogDemo.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SlackNet.Blocks; +using SlackNet.Interaction; +using Button = SlackNet.Blocks.Button; + +namespace SlackNet.EventsExample +{ + public class BlockDialogDemo : DialogDemoBase, IBlockActionHandler + { + public BlockDialogDemo(ISlackApiClient slack) : base(slack) { } + + public async Task Handle(ButtonAction action, BlockActionRequest request) + { + await OpenDialog(action.ActionId, request.TriggerId).ConfigureAwait(false); + } + + public static IEnumerable Blocks => new Block[] + { + new SectionBlock { Text = "Dialogs" }, + new ActionsBlock + { + Elements = + { + new Button + { + Text = "Echo dialog", + ActionId = EchoDialog + }, + new Button + { + Text = "Error dialog", + ActionId = ErrorDialog + } + } + } + }; + } +} \ No newline at end of file diff --git a/SlackNet.EventsExample/ColorSelector.cs b/SlackNet.EventsExample/ColorSelector.cs index ad189dc..81b563c 100644 --- a/SlackNet.EventsExample/ColorSelector.cs +++ b/SlackNet.EventsExample/ColorSelector.cs @@ -3,10 +3,11 @@ using System.Linq; using System.Threading.Tasks; using SlackNet.Interaction; +using ActionElement = SlackNet.Interaction.ActionElement; namespace SlackNet.EventsExample { - public class ColorSelector : IInteractiveMessageHandler, IOptionProvider + public class LegacyColorSelector : IInteractiveMessageHandler, IOptionProvider { public static readonly string ActionName = "color_select"; @@ -31,12 +32,15 @@ public async Task Handle(InteractiveMessage message) public async Task GetOptions(OptionsRequest request) => new OptionsResponse { Options = GetOptions(request.Value) }; private static List