/// <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)); }
/// <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); }
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); }
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); }
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(); }
/// <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); }
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); }
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)); }