diff --git a/src/SlackConnector/Connections/Models/Channel.cs b/src/SlackConnector/Connections/Models/Channel.cs index 924ad1f..80aeddb 100644 --- a/src/SlackConnector/Connections/Models/Channel.cs +++ b/src/SlackConnector/Connections/Models/Channel.cs @@ -15,5 +15,7 @@ internal class Channel : Detail [JsonProperty("members")] public string[] Members { get; set; } + + public string Creator { get; set; } } } \ No newline at end of file diff --git a/src/SlackConnector/Connections/Sockets/Messages/Inbound/ChannelCreatedMessage.cs b/src/SlackConnector/Connections/Sockets/Messages/Inbound/ChannelCreatedMessage.cs new file mode 100644 index 0000000..b535438 --- /dev/null +++ b/src/SlackConnector/Connections/Sockets/Messages/Inbound/ChannelCreatedMessage.cs @@ -0,0 +1,14 @@ +using SlackConnector.Connections.Models; + +namespace SlackConnector.Connections.Sockets.Messages.Inbound +{ + internal class ChannelCreatedMessage : InboundMessage + { + public ChannelCreatedMessage() + { + MessageType = MessageType.Channel_Created; + } + + public Channel Channel { get; set; } + } +} \ No newline at end of file diff --git a/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageInterpreter.cs b/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageInterpreter.cs index bd2834b..9cbaf15 100644 --- a/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageInterpreter.cs +++ b/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageInterpreter.cs @@ -46,6 +46,9 @@ public InboundMessage InterpretMessage(string json) case MessageType.Reaction_Added: message = GetReactionMessage(json); break; + case MessageType.Channel_Created: + message = JsonConvert.DeserializeObject(json); + break; } } catch (Exception ex) diff --git a/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageType.cs b/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageType.cs index a4f2e67..4335fcf 100644 --- a/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageType.cs +++ b/src/SlackConnector/Connections/Sockets/Messages/Inbound/MessageType.cs @@ -9,6 +9,7 @@ internal enum MessageType Im_Created, Team_Join, Pong, - Reaction_Added + Reaction_Added, + Channel_Created } } diff --git a/src/SlackConnector/EventHandlers/ChannelCreatedHandler.cs b/src/SlackConnector/EventHandlers/ChannelCreatedHandler.cs new file mode 100644 index 0000000..a55ce2f --- /dev/null +++ b/src/SlackConnector/EventHandlers/ChannelCreatedHandler.cs @@ -0,0 +1,7 @@ +using System.Threading.Tasks; +using SlackConnector.Models; + +namespace SlackConnector.EventHandlers +{ + public delegate Task ChannelCreatedHandler(SlackChannelCreated chatHub); +} diff --git a/src/SlackConnector/ISlackConnection.cs b/src/SlackConnector/ISlackConnection.cs index 4ce17d0..bcf048f 100644 --- a/src/SlackConnector/ISlackConnection.cs +++ b/src/SlackConnector/ISlackConnection.cs @@ -9,13 +9,11 @@ namespace SlackConnector { public interface ISlackConnection { - #region Properties - /// /// All of the ChatHubs that are currently open. /// IReadOnlyDictionary ConnectedHubs { get; } - + /// /// UserId => User object. /// @@ -46,14 +44,6 @@ public interface ISlackConnection /// ContactDetails Self { get; } - #endregion - - /// - /// Disconnect from Slack. - /// - [Obsolete("Please use Close async method", true)] - void Disconnect(); - /// /// Close websocket connection to Slack /// @@ -63,7 +53,7 @@ public interface ISlackConnection /// Send message to Slack channel. /// Task Say(BotMessage message); - + /// /// Uploads a file from to a Slack channel /// @@ -166,5 +156,10 @@ public interface ISlackConnection /// Raised when SlackApi sends a pong to our ping /// event PongEventHandler OnPong; + + /// + /// Raised when a new channel is created + /// + event ChannelCreatedHandler OnChannelCreated; } } \ No newline at end of file diff --git a/src/SlackConnector/Models/SlackChannelCreated.cs b/src/SlackConnector/Models/SlackChannelCreated.cs new file mode 100644 index 0000000..657f686 --- /dev/null +++ b/src/SlackConnector/Models/SlackChannelCreated.cs @@ -0,0 +1,9 @@ +namespace SlackConnector.Models +{ + public class SlackChannelCreated + { + public string Id { get; internal set; } + public string Name { get; internal set; } + public SlackUser Creator { get; internal set; } + } +} \ No newline at end of file diff --git a/src/SlackConnector/SlackConnection.cs b/src/SlackConnector/SlackConnection.cs index 391349b..9a8f698 100644 --- a/src/SlackConnector/SlackConnection.cs +++ b/src/SlackConnector/SlackConnection.cs @@ -34,7 +34,7 @@ internal class SlackConnection : ISlackConnection private Dictionary _userCache { get; set; } public IReadOnlyDictionary UserCache => _userCache; - public bool IsConnected => _webSocketClient.IsAlive; + public bool IsConnected => _webSocketClient?.IsAlive ?? false; public DateTime? ConnectedSince { get; private set; } public string SlackKey { get; private set; } @@ -99,6 +99,7 @@ private Task ListenTo(InboundMessage inboundMessage) case MessageType.Team_Join: return HandleUserJoined((UserJoinedMessage)inboundMessage); case MessageType.Pong: return HandlePong((PongMessage)inboundMessage); case MessageType.Reaction_Added: return HandleReaction((ReactionMessage)inboundMessage); + case MessageType.Channel_Created: return HandleChannelCreated((ChannelCreatedMessage)inboundMessage); } return Task.CompletedTask; @@ -238,6 +239,23 @@ private Task HandlePong(PongMessage inboundMessage) return RaisePong(inboundMessage.Timestamp); } + private Task HandleChannelCreated(ChannelCreatedMessage inboundMessage) + { + string channelId = inboundMessage?.Channel?.Id; + if (channelId == null) return Task.CompletedTask; + + var hub = inboundMessage.Channel.ToChatHub(); + _connectedHubs[channelId] = hub; + + var slackChannelCreated = new SlackChannelCreated + { + Id = channelId, + Name = inboundMessage.Channel.Name, + Creator = GetMessageUser(inboundMessage.Channel.Creator) + }; + return RaiseOnChannelCreated(slackChannelCreated); + } + private SlackUser GetMessageUser(string userId) { if (string.IsNullOrEmpty(userId)) @@ -265,12 +283,6 @@ public async Task Close() } } - [Obsolete("Please use Close async method", true)] - public void Disconnect() - { - throw new NotImplementedException(); - } - public async Task Say(BotMessage message) { if (string.IsNullOrEmpty(message.ChatHub?.Id)) @@ -563,6 +575,21 @@ private async Task RaisePong(DateTime timestamp) } } + public event ChannelCreatedHandler OnChannelCreated; + private async Task RaiseOnChannelCreated(SlackChannelCreated chatHub) + { + var e = OnChannelCreated; + if (e != null) + { + try + { + await e(chatHub); + } + catch + { + } + } + } //TODO: USER JOINED EVENT HANDLING } } diff --git a/tests/SlackConnector.Tests.Unit/Connections/Sockets/Messages/MessageInterpreterTests.cs b/tests/SlackConnector.Tests.Unit/Connections/Sockets/Messages/MessageInterpreterTests.cs index a568011..0647d75 100644 --- a/tests/SlackConnector.Tests.Unit/Connections/Sockets/Messages/MessageInterpreterTests.cs +++ b/tests/SlackConnector.Tests.Unit/Connections/Sockets/Messages/MessageInterpreterTests.cs @@ -460,5 +460,40 @@ private void should_return_unknown_reaction_message(MessageInterpreter interpret result.ShouldLookLike(expected); } + + [Theory, AutoMoqData] + private void should_return_channel_created_message(MessageInterpreter interpreter) + { + // given + string json = @" + { + 'type': 'channel_created', + 'channel': { + 'id': 'C024BE91L', + 'name': 'fun', + 'created': 1360782804, + 'creator': 'U024BE7LH' + } + } + "; + + // when + var result = interpreter.InterpretMessage(json); + + // then + var expected = new ChannelCreatedMessage + { + MessageType = MessageType.Channel_Created, + Channel = new Channel + { + Id = "C024BE91L", + Name = "fun", + Creator = "U024BE7LH" + }, + RawData = json, + }; + + result.ShouldLookLike(expected); + } } } \ No newline at end of file diff --git a/tests/SlackConnector.Tests.Unit/SlackConnectionTests/InboundMessageTests/ChannelCreatedTests.cs b/tests/SlackConnector.Tests.Unit/SlackConnectionTests/InboundMessageTests/ChannelCreatedTests.cs new file mode 100644 index 0000000..8cd94d8 --- /dev/null +++ b/tests/SlackConnector.Tests.Unit/SlackConnectionTests/InboundMessageTests/ChannelCreatedTests.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Moq; +using Ploeh.AutoFixture; +using SlackConnector.Connections.Models; +using SlackConnector.Connections.Sockets; +using SlackConnector.Connections.Sockets.Messages.Inbound; +using SlackConnector.Models; +using Xunit; +using Shouldly; + +namespace SlackConnector.Tests.Unit.SlackConnectionTests.InboundMessageTests +{ + public class ChannelCreatedTests + { + [Theory, AutoMoqData] + private async Task should_raise_event( + Mock webSocket, + SlackConnection slackConnection, + SlackUser slackUser, + Fixture fixture) + { + // given + var connectionInfo = new ConnectionInformation + { + WebSocket = webSocket.Object, + Users = new Dictionary + { + {slackUser.Id , slackUser} + } + }; + await slackConnection.Initialise(connectionInfo); + + SlackChannelCreated channelCreated = null; + slackConnection.OnChannelCreated += channel => + { + channelCreated = channel; + return Task.CompletedTask; + }; + + var inboundMessage = new ChannelCreatedMessage + { + Channel = new Channel + { + Creator = slackUser.Id, + Id = fixture.Create(), + Name = fixture.Create() + } + }; + + // when + webSocket.Raise(x => x.OnMessage += null, null, inboundMessage); + + // then + channelCreated.Id.ShouldBe(inboundMessage.Channel.Id); + channelCreated.Name.ShouldBe(inboundMessage.Channel.Name); + channelCreated.Creator.ShouldBe(slackUser); + slackConnection.ConnectedHubs.ContainsKey(inboundMessage.Channel.Id).ShouldBeTrue(); + } + + [Theory, AutoMoqData] + private async Task should_not_raise_event_given_missing_data( + Mock webSocket, + SlackConnection slackConnection) + { + // given + var connectionInfo = new ConnectionInformation { WebSocket = webSocket.Object }; + await slackConnection.Initialise(connectionInfo); + + SlackChannelCreated channelCreated = null; + slackConnection.OnChannelCreated += channel => + { + channelCreated = channel; + return Task.CompletedTask; + }; + + var inboundMessage = new ChannelCreatedMessage { Channel = null }; + + // when + webSocket.Raise(x => x.OnMessage += null, null, inboundMessage); + + // then + channelCreated.ShouldBeNull(); + slackConnection.ConnectedHubs.ShouldBeEmpty(); + } + } +} \ No newline at end of file diff --git a/tests/SlackConnector.Tests.Unit/Stubs/SlackConnectionStub.cs b/tests/SlackConnector.Tests.Unit/Stubs/SlackConnectionStub.cs index 601fcc4..da81bf7 100644 --- a/tests/SlackConnector.Tests.Unit/Stubs/SlackConnectionStub.cs +++ b/tests/SlackConnector.Tests.Unit/Stubs/SlackConnectionStub.cs @@ -129,6 +129,8 @@ public void RaiseOnUserJoined() } public event PongEventHandler OnPong; + public event ChannelCreatedHandler OnChannelCreated; + public void RaiseOnPong() { OnPong?.Invoke(DateTime.MinValue);