Example #1
0
        /// <summary>
        /// Causes the bot to appear as though it's typing in whichever hub you specify. Useful for custom interactions, but note that you
        /// don't need to set this while typical responders are being evaluated - the Bot class does that for you.
        /// </summary>
        /// <param name="chatHub">The hub in which the bot should appear to be typing.</param>
        /// <returns></returns>
        public async Task SendIsTyping(SlackChatHub chatHub)
        {
            if (!IsConnected)
            {
                throw new InvalidOperationException(@"Can't send the ""Bot typing"" indicator when the bot is disconnected.");
            }

            var message = new {
                type    = "typing",
                channel = chatHub.ID,
                user    = UserID
            };

            await WebSocket.Send(JsonConvert.SerializeObject(message));
        }
Example #2
0
        /// <summary>
        /// Connects this bot to Slack using the slack API key provided. Set yours up at https://yourteam.slack.com/apps/manage.
        /// </summary>
        /// <param name="slackKey">The API key the bot will use to identify itself to the Slack API.</param>
        public async Task Connect(string slackKey)
        {
            SlackKey = slackKey;

            // disconnect in case we're already connected like a crazy person
            Disconnect();

            // kill the regex for our bot's name - we'll rebuild it upon request with some of the info we get here
            BotNameRegex = string.Empty;

            // start session and get response
            var httpClient = new HttpClient();
            var json       = await httpClient.GetStringAsync($"https://slack.com/api/rtm.start?token={SlackKey}");

            var jData = JObject.Parse(json);

            // read various bot properties out of the response
            TeamID   = jData["team"]["id"].Value <string>();
            TeamName = jData["team"]["name"].Value <string>();
            UserID   = jData["self"]["id"].Value <string>();
            UserName = jData["self"]["name"].Value <string>();
            var webSocketUrl = jData["url"].Value <string>();

            // rebuild the username cache
            UserNameCache.Clear();
            foreach (JObject userObject in jData["users"])
            {
                UserNameCache.Add(userObject["id"].Value <string>(), userObject["name"].Value <string>());
            }

            // load the channels, groups, and DMs that margie's in
            Dictionary <string, SlackChatHub> hubs = new Dictionary <string, SlackChatHub>();

            ConnectedHubs = hubs;

            // channelz
            if (jData["channels"] != null)
            {
                foreach (JObject channelData in jData["channels"])
                {
                    if (!channelData["is_archived"].Value <bool>() && channelData["is_member"].Value <bool>())
                    {
                        var channel = new SlackChatHub()
                        {
                            ID   = channelData["id"].Value <string>(),
                            Name = "#" + channelData["name"].Value <string>(),
                            Type = SlackChatHubType.Channel
                        };
                        hubs.Add(channel.ID, channel);
                    }
                }
            }

            // groupz
            if (jData["groups"] != null)
            {
                foreach (JObject groupData in jData["groups"])
                {
                    if (!groupData["is_archived"].Value <bool>() && groupData["members"].Values <string>().Contains(UserID))
                    {
                        var group = new SlackChatHub()
                        {
                            ID   = groupData["id"].Value <string>(),
                            Name = groupData["name"].Value <string>(),
                            Type = SlackChatHubType.Group
                        };
                        hubs.Add(group.ID, group);
                    }
                }
            }

            // dmz
            if (jData["ims"] != null)
            {
                foreach (JObject dmData in jData["ims"])
                {
                    var userID = dmData["user"].Value <string>();
                    var dm     = new SlackChatHub()
                    {
                        ID   = dmData["id"].Value <string>(),
                        Name = "@" + (UserNameCache.ContainsKey(userID) ? UserNameCache[userID] : userID),
                        Type = SlackChatHubType.DM
                    };
                    hubs.Add(dm.ID, dm);
                }
            }

            // set up the websocket
            WebSocket         = new MargieBotWebSocket();
            WebSocket.OnOpen += (object sender, EventArgs e) =>
            {
                // set connection-related properties
                ConnectedSince = DateTime.Now;
            };
            WebSocket.OnMessage += async(object sender, string message) =>
            {
                await ListenTo(message);
            };
            WebSocket.OnClose += (object sender, EventArgs e) =>
            {
                // set connection-related properties
                ConnectedSince = null;
                TeamID         = null;
                TeamName       = null;
                UserID         = null;
                UserName       = null;
            };

            // connect
            await WebSocket.Connect(webSocketUrl);
        }
Example #3
0
        private async Task ListenTo(string json)
        {
            bool    isValidJson = true;
            JObject jObject     = null;

            try
            {
                jObject = JObject.Parse(json);
            }
            catch (JsonReaderException)
            {
                isValidJson = false;
#if DEBUG
                Console.WriteLine($"Illegal JSON message: {json}");
#endif
            }

            if (!isValidJson)
            {
                return;
            }

            if (jObject["type"].Value <string>() == "message")
            {
                var          channelID = jObject["channel"].Value <string>();
                SlackChatHub hub       = null;

                if (ConnectedHubs.ContainsKey(channelID))
                {
                    hub = ConnectedHubs[channelID];
                }
                else
                {
                    hub = SlackChatHub.FromID(channelID);
                    var hubs = new Dictionary <string, SlackChatHub>(ConnectedHubs.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
                    hubs.Add(hub.ID, hub);
                    ConnectedHubs = hubs;
                }

                // some messages may not have text or a user (like unfurled data from URLs)
                var messageText = (jObject["text"] != null ? jObject["text"].Value <string>() : null);

                SlackMessage message = new SlackMessage()
                {
                    ChatHub = hub,
                    // check to see if bot has been mentioned
                    MentionsBot = (messageText != null ? Regex.IsMatch(messageText, BotNameRegex, RegexOptions.IgnoreCase) : false),
                    RawData     = json,
                    Text        = messageText,
                    User        = (jObject["user"] != null ? new SlackUser()
                    {
                        ID = jObject["user"].Value <string>()
                    } : null)
                };

                ResponseContext context = new ResponseContext()
                {
                    BotHasResponded = false,
                    BotUserID       = UserID,
                    BotUserName     = UserName,
                    Message         = message,
                    TeamID          = TeamID,
                    UserNameCache   = new ReadOnlyDictionary <string, string>(UserNameCache)
                };

                // if the end dev has added any static entries to the ResponseContext collection of Bot, add them to the context being passed to the responders.
                if (ResponseContext != null)
                {
                    foreach (string key in ResponseContext.Keys)
                    {
                        context.Set(key, ResponseContext[key]);
                    }
                }

                // margie can never respond to herself and requires that the message have text and be from an actual person
                if (message.User != null && message.User.ID != UserID && message.Text != null)
                {
                    foreach (var responder in Responders ?? Enumerable.Empty <IResponder>())
                    {
                        if (responder != null && responder.CanRespond(context))
                        {
                            await SendIsTyping(message.ChatHub);
                            await Say(responder.GetResponse(context), context);

                            context.BotHasResponded = true;
                        }
                    }
                }
            }

            RaiseMessageReceived(json);
        }
Example #4
0
        private async Task ListenTo(string json)
        {
            JObject jObject = JObject.Parse(json);

            if (jObject["type"].Value <string>() == "message")
            {
                string       channelID = jObject["channel"].Value <string>();
                SlackChatHub hub       = null;

                if (ConnectedHubs.ContainsKey(channelID))
                {
                    hub = ConnectedHubs[channelID];
                }
                else
                {
                    hub = SlackChatHub.FromID(channelID);
                    List <SlackChatHub> hubs = new List <SlackChatHub>();
                    hubs.AddRange(ConnectedHubs.Values);
                    hubs.Add(hub);
                }

                string messageText = (jObject["text"] != null ? jObject["text"].Value <string>() : null);
                // check to see if bot has been mentioned
                SlackMessage message = new SlackMessage()
                {
                    ChatHub     = hub,
                    MentionsBot = (messageText != null ? Regex.IsMatch(messageText, BotNameRegex, RegexOptions.IgnoreCase) : false),
                    RawData     = json,
                    // some messages may not have text or a user (like unfurled data from URLs)
                    Text = messageText,
                    User = (jObject["user"] != null ? new SlackUser()
                    {
                        ID = jObject["user"].Value <string>()
                    } : null)
                };

                ResponseContext context = new ResponseContext()
                {
                    BotHasResponded = false,
                    BotUserID       = UserID,
                    BotUserName     = UserName,
                    Message         = message,
                    TeamID          = this.TeamID,
                    UserNameCache   = new ReadOnlyDictionary <string, string>(this.UserNameCache)
                };

                if (StaticResponseContextData != null)
                {
                    foreach (string key in StaticResponseContextData.Keys)
                    {
                        context.Set(key, StaticResponseContextData[key]);
                    }
                }

                // margie can never respond to herself and requires that the message have text and be from an actual person
                if (message.User != null && message.User.ID != UserID && message.Text != null)
                {
                    foreach (IResponseProcessor processor in ResponseProcessors)
                    {
                        if (processor.CanRespond(context))
                        {
                            await Say(processor.GetResponse(context), context);

                            context.BotHasResponded = true;
                        }
                    }
                }
            }

            RaiseMessageReceived(json);
        }
Example #5
0
        public async Task Connect()
        {
            // disconnect in case we're already connected like a crazy person
            Disconnect();

            // kill the regex for our bot's name - we'll rebuild it upon request with some of the info we get here
            BotNameRegex = string.Empty;

            NoobWebClient client = new NoobWebClient();
            string        json   = await client.GetResponse("https://slack.com/api/rtm.start", RequestType.Post, "token", this.SlackKey);

            JObject jData = JObject.Parse(json);

            TeamID   = jData["team"]["id"].Value <string>();
            UserID   = jData["self"]["id"].Value <string>();
            UserName = jData["self"]["name"].Value <string>();
            string webSocketUrl = jData["url"].Value <string>();

            UserNameCache.Clear();
            foreach (JObject userObject in jData["users"])
            {
                UserNameCache.Add(userObject["id"].Value <string>(), userObject["name"].Value <string>());
            }

            // load the channels, groups, and DMs that margie's in
            Dictionary <string, SlackChatHub> hubs = new Dictionary <string, SlackChatHub>();

            ConnectedHubs = hubs;

            // channelz
            if (jData["channels"] != null)
            {
                foreach (JObject channelData in jData["channels"])
                {
                    if (!channelData["is_archived"].Value <bool>() && channelData["is_member"].Value <bool>())
                    {
                        SlackChatHub channel = new SlackChatHub()
                        {
                            ID   = channelData["id"].Value <string>(),
                            Name = "#" + channelData["name"].Value <string>(),
                            Type = SlackChatHubType.Channel
                        };
                        hubs.Add(channel.ID, channel);
                    }
                }
            }

            // groupz
            if (jData["groups"] != null)
            {
                foreach (JObject groupData in jData["groups"])
                {
                    if (!groupData["is_archived"].Value <bool>() && groupData["members"].Values <string>().Contains(UserID))
                    {
                        SlackChatHub group = new SlackChatHub()
                        {
                            ID   = groupData["id"].Value <string>(),
                            Name = groupData["name"].Value <string>(),
                            Type = SlackChatHubType.Group
                        };
                        hubs.Add(group.ID, group);
                    }
                }
            }

            // dmz
            if (jData["ims"] != null)
            {
                foreach (JObject dmData in jData["ims"])
                {
                    string       userID = dmData["user"].Value <string>();
                    SlackChatHub dm     = new SlackChatHub()
                    {
                        ID   = dmData["id"].Value <string>(),
                        Name = "@" + (UserNameCache.ContainsKey(userID) ? UserNameCache[userID] : userID),
                        Type = SlackChatHubType.DM
                    };
                    hubs.Add(dm.ID, dm);
                }
            }

            // set up the websocket and connect
            WebSocket          = new WebSocket(webSocketUrl);
            WebSocket.OnClose += (object sender, CloseEventArgs e) => {
                IsConnected = false;
            };
            WebSocket.OnMessage += async(object sender, MessageEventArgs args) => {
                await ListenTo(args.Data);
            };
            WebSocket.OnOpen += (object sender, EventArgs e) => {
                IsConnected = true;
            };
            WebSocket.Connect();
        }
Example #6
0
        /// <summary>
        /// Connects this bot to Slack using the slack API key provided. Set yours up at https://yourteam.slack.com/apps/manage.
        /// </summary>
        /// <param name="slackKey">The API key the bot will use to identify itself to the Slack API.</param>
        public async Task Connect <R>(string slackKey) where R : T, new()
        {
            SlackKey = slackKey;

            // disconnect in case we're already connected like a crazy person
            Disconnect();

            // kill the regex for our bot's name - we'll rebuild it upon request with some of the info we get here
            BotNameRegex = string.Empty;

            // start session and get response
            var httpClient = new HttpClient();
            var json       = await httpClient.GetStringAsync($"https://slack.com/api/rtm.start?token={SlackKey}");

            var jData = JObject.Parse(json);

            // Handle exceptions.

            if (!jData["ok"].Value <bool>())
            {
                var errorMessage = jData["ok"].Value <string>();
                switch (errorMessage)
                {
                case "not_authed":
                case "account_inactive":
                case "invalid_auth":
                    InvalidCredentialException exIC = new InvalidCredentialException(errorMessage);
                    exIC.HelpLink = SlackRtmStartHelp;
                    throw exIC;

                case "invalid_arg_name":
                case "invalid_array_arg":
                case "invalid_charset":
                case "invalid_form_data":
                case "invalid_post_type":
                case "missing_post_type":
                    ArgumentException exAE = new ArgumentException(errorMessage);
                    exAE.HelpLink = SlackRtmStartHelp;
                    throw exAE;

                case "request_timeout":
                    TimeoutException exTE = new TimeoutException(errorMessage);
                    exTE.HelpLink = SlackRtmStartHelp;
                    throw exTE;

                default:
                    throw new Exception(errorMessage);
                }
            }

            // read various bot properties out of the response
            TeamID   = jData["team"]["id"].Value <string>();
            TeamName = jData["team"]["name"].Value <string>();
            UserID   = jData["self"]["id"].Value <string>();
            UserName = jData["self"]["name"].Value <string>();
            var webSocketUrl = jData["url"].Value <string>();

            // rebuild the username cache
            UserNameCache.Clear();
            foreach (JObject userObject in jData["users"])
            {
                UserNameCache.Add(userObject["id"].Value <string>(), userObject["name"].Value <string>());
            }

            // load the channels, groups, and DMs that margie's in
            Dictionary <string, SlackChatHub> hubs = new Dictionary <string, SlackChatHub>();

            ConnectedHubs = hubs;

            // channelz
            if (jData["channels"] != null)
            {
                foreach (JObject channelData in jData["channels"])
                {
                    if (!channelData["is_archived"].Value <bool>() && channelData["is_member"].Value <bool>())
                    {
                        var channel = new SlackChatHub()
                        {
                            ID   = channelData["id"].Value <string>(),
                            Name = "#" + channelData["name"].Value <string>(),
                            Type = SlackChatHubType.Channel
                        };
                        hubs.Add(channel.ID, channel);
                    }
                }
            }

            // groupz
            if (jData["groups"] != null)
            {
                foreach (JObject groupData in jData["groups"])
                {
                    if (!groupData["is_archived"].Value <bool>() && groupData["members"].Values <string>().Contains(UserID))
                    {
                        var group = new SlackChatHub()
                        {
                            ID   = groupData["id"].Value <string>(),
                            Name = groupData["name"].Value <string>(),
                            Type = SlackChatHubType.Group
                        };
                        hubs.Add(group.ID, group);
                    }
                }
            }

            // dmz
            if (jData["ims"] != null)
            {
                foreach (JObject dmData in jData["ims"])
                {
                    var userID = dmData["user"].Value <string>();
                    var dm     = new SlackChatHub()
                    {
                        ID   = dmData["id"].Value <string>(),
                        Name = "@" + (UserNameCache.ContainsKey(userID) ? UserNameCache[userID] : userID),
                        Type = SlackChatHubType.DM
                    };
                    hubs.Add(dm.ID, dm);
                }
            }

            // set up the websocket
            WebSocket         = new R();
            WebSocket.OnOpen += (object sender, EventArgs e) =>
            {
                // set connection-related properties
                ConnectedSince = DateTime.Now;
            };
            WebSocket.OnMessage += async(object sender, string message) =>
            {
                await ListenTo(message);
            };
            WebSocket.OnClose += (object sender, EventArgs e) =>
            {
                // set connection-related properties
                ConnectedSince = null;
                TeamID         = null;
                TeamName       = null;
                UserID         = null;
                UserName       = null;
            };

            // connect
            await WebSocket.Connect(webSocketUrl);
        }
Example #7
0
        private async Task ListenTo(string json)
        {
            JObject jObject = JObject.Parse(json);

            if (jObject["type"].Value <string>() == "message")
            {
                string       channelID = jObject["channel"].Value <string>();
                SlackChatHub hub       = null;

                if (ConnectedHubs.ContainsKey(channelID))
                {
                    hub = ConnectedHubs[channelID];
                }
                else
                {
                    hub = SlackChatHub.FromID(channelID);
                    Dictionary <string, SlackChatHub> hubs = new Dictionary <string, SlackChatHub>(ConnectedHubs.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
                    hubs.Add(hub.ID, hub);
                    ConnectedHubs = hubs;
                }

                string messageText = (jObject["text"] != null ? jObject["text"].Value <string>() : null);
                // check to see if bot has been mentioned
                SlackMessageContent message = new SlackMessageContent()
                {
                    ChatHub     = hub,
                    MentionsBot = (messageText != null ? Regex.IsMatch(messageText, BotNameRegex, RegexOptions.IgnoreCase) : false),
                    RawData     = json,
                    // some messages may not have text or a user (like unfurled data from URLs)
                    Text = messageText,
                    User = (jObject["user"] != null ? new SlackUser()
                    {
                        ID = jObject["user"].Value <string>()
                    } : null)
                };

                SlackMessage context = new SlackMessage()
                {
                    Creator         = this,
                    BotHasResponded = false,
                    BotUserID       = UserID,
                    BotUserName     = UserName,
                    Message         = message,
                    TeamID          = this.TeamID,
                    UserNameCache   = new ReadOnlyDictionary <string, string>(this.UserNameCache)
                };

                context.Answer = async(arg) =>
                {
                    await this.Say(arg, context);

                    context.BotHasResponded = true;
                };

                // if the end dev has added any static entries to the ResponseContext collection of Bot, add them to the context being passed to the responders.
                if (ResponseContext != null)
                {
                    foreach (string key in ResponseContext.Keys)
                    {
                        context.Set(key, ResponseContext[key]);
                    }
                }

                // margie can never respond to herself and requires that the message have text and be from an actual person
                if (message.User != null && message.User.ID != UserID && message.Text != null)
                {
                    foreach (var obs in observers)
                    {
                        obs.OnNext(context);
                    }
                }
            }

            RaiseMessageReceived(json);
        }
Example #8
0
        public async Task Connect(string slackKey)
        {
            this.SlackKey = slackKey;

            // disconnect in case we're already connected like a crazy person
            Disconnect();

            // kill the regex for our bot's name - we'll rebuild it upon request with some of the info we get here
            BotNameRegex = string.Empty;

            var client = new HttpClient();
            var cnt    = new FormUrlEncodedContent(new List <KeyValuePair <string, string> >()
            {
                MakeKeyValue("token", this.SlackKey)
            });
            var rsp = await client.PostAsync("https://slack.com/api/rtm.start", cnt);

            var json = await rsp.Content.ReadAsStringAsync();

            JObject jData = JObject.Parse(json);

            TeamID   = jData["team"]["id"].Value <string>();
            TeamName = jData["team"]["name"].Value <string>();
            UserID   = jData["self"]["id"].Value <string>();
            UserName = jData["self"]["name"].Value <string>();
            string webSocketUrl = jData["url"].Value <string>();

            UserNameCache.Clear();
            foreach (JObject userObject in jData["users"])
            {
                UserNameCache.Add(userObject["id"].Value <string>(), userObject["name"].Value <string>());
            }

            // load the channels, groups, and DMs that margie's in
            Dictionary <string, SlackChatHub> hubs = new Dictionary <string, SlackChatHub>();

            ConnectedHubs = hubs;

            // channelz
            if (jData["channels"] != null)
            {
                foreach (JObject channelData in jData["channels"])
                {
                    if (!channelData["is_archived"].Value <bool>() && channelData["is_member"].Value <bool>())
                    {
                        SlackChatHub channel = new SlackChatHub()
                        {
                            ID   = channelData["id"].Value <string>(),
                            Name = "#" + channelData["name"].Value <string>(),
                            Type = SlackChatHubType.Channel
                        };
                        hubs.Add(channel.ID, channel);
                    }
                }
            }

            // groupz
            if (jData["groups"] != null)
            {
                foreach (JObject groupData in jData["groups"])
                {
                    if (!groupData["is_archived"].Value <bool>() && groupData["members"].Values <string>().Contains(UserID))
                    {
                        SlackChatHub group = new SlackChatHub()
                        {
                            ID   = groupData["id"].Value <string>(),
                            Name = groupData["name"].Value <string>(),
                            Type = SlackChatHubType.Group
                        };
                        hubs.Add(group.ID, group);
                    }
                }
            }

            // dmz
            if (jData["ims"] != null)
            {
                foreach (JObject dmData in jData["ims"])
                {
                    string       userID = dmData["user"].Value <string>();
                    SlackChatHub dm     = new SlackChatHub()
                    {
                        ID   = dmData["id"].Value <string>(),
                        Name = "@" + (UserNameCache.ContainsKey(userID) ? UserNameCache[userID] : userID),
                        Type = SlackChatHubType.DM
                    };
                    hubs.Add(dm.ID, dm);
                }
            }

            // set up the websocket and connect
            WebSocket         = new WebSocket(webSocketUrl);
            WebSocket.OnOpen += (object sender, EventArgs e) => {
                // set connection-related properties
                ConnectedSince = DateTime.Now;
            };
            WebSocket.OnMessage += async(object sender, MessageEventArgs args) => {
                await ListenTo(args.Data);
            };
            WebSocket.OnClose += (object sender, CloseEventArgs e) => {
                // set connection-related properties
                ConnectedSince = null;
                TeamID         = null;
                TeamName       = null;
                UserID         = null;
                UserName       = null;
            };
            WebSocket.Connect();
            Tmr = new Timer(o => {
                if (!WebSocket.Ping())
                {
                    this.Tmr.Dispose();
                    this.Tmr = null;
                    this.Connect(this.SlackKey);
                }
            }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
        }