private PlotModel _createChart(SlackChannel channel)
        {
            PlotModel chart = new PlotModel {
                LegendPosition = LegendPosition.RightTop, LegendPlacement = LegendPlacement.Outside
            };

            chart.Axes.Add(new LinearAxis {
                Position = AxisPosition.Left, Minimum = 40, Maximum = 110
            });
            foreach (KeyValuePair <SlackUser, List <bool> > pair in _data[channel])
            {
                LineSeries series = new LineSeries {
                    MarkerType = MarkerType.Circle
                };
                int numberOfTimesThere = 0;
                for (int j = 0; j < pair.Value.Count; j++)
                {
                    numberOfTimesThere += pair.Value[j] ? 1 : 0;
                    DataPoint nextPoint = new DataPoint(j + 1, (float)numberOfTimesThere * 100 / (j + 1));
                    series.Points.Add(nextPoint);
                }
                series.Title = pair.Key.Name + " (" + Math.Round(series.Points.Last().Y, 2) + "%)";
                chart.Series.Add(series);
            }

            return(chart);
        }
        /// <summary>Sends a message to a given slack channel</summary>
        /// <param name="channel">Slack channel to send the message to</param>
        /// <param name="message">Message to send to the channel, this supports slack message formatting</param>
        public void SendMessage(SlackChannel channel, string message)
        {
            JObject parameters = new JObject
            {
                { "channel", channel.Id },
                { "text", message },
                { "username", "attendancebot" }
            };

            ApiCall("chat.postMessage", parameters);
        }
        private void helpCommand(SlackMessage message, SlackChannel channel)
        {
            string text = String.Format("The commands you need to know are:" +
                                        "\n        •`<@{0}|{1}> help` to show this message" +
                                        "\n        •`<@{0}|{1}> keep [users *not* attending]` to register attendance at a meeting" +
                                        "\n        •`<@{0}|{1}> reset` to reset all information about attendance in this channel. Be careful. This *can not* be undone" +
                                        "\n        •`<@{0}|{1}> revert` to remove the last registered meeting" +
                                        "\n        •`<@{0}|{1}> redo [meeting number]` to re-register a meeting (starts at 1!)" +
                                        "\n        •`<@{0}|{1}> image` to get a chart showing the latest attendance information", BotId, Name);

            SendMessage(channel, text);
        }
        private void _trackUsers(SlackMessage message, SlackChannel channel)
        {
            if (!_isTracking(channel))
            {
                _data.Add(channel, new AttendanceData());
            }

            List <SlackUser> usersFromMessage = _getUsersFromMessage(message, 2);
            int numberOfMeetings = _data[channel].Any() ? _data[channel].First().Value.Count : 0;

            _data[channel].Add(numberOfMeetings, usersFromMessage.ToArray());
            saveData();
        }
        private void _countAttendance(SlackMessage message, SlackChannel channel)
        {
            var    c        = _createChart(channel);
            string fileName = Path.GetTempPath() + Guid.NewGuid() + ".png";

            using (var stream = File.Create(fileName))
            {
                var svgExporter = new PngExporter {
                    Width = 600, Height = 400
                };
                svgExporter.Export(c, stream);
            }


            SlackSendFile(channel, fileName, "attendance");
        }
        private void _keepAttendance(SlackMessage message, SlackChannel channel)
        {
            List <SlackUser> usersNotThere = null;

            try
            {
                usersNotThere = _getUsersFromMessage(message, 2);
            }
            catch (ArgumentException e)
            {
                SendMessage(channel, e.Message);
                return;
            }
            _data[channel].Update(usersNotThere);
            _countAttendance(message, channel);
            saveData();
        }
        private void _parseMessage(object sender, MessageEventArgs e)
        {
            string  jsonString = e.Data;
            JObject message    = JObject.Parse(jsonString);

            if (_isMessage(message) && !_isFromSelf(message))
            {
                OnMessage?.Invoke(this, new SlackMessage((string)message["text"],
                                                         _getUserFromId((string)message["user"])),
                                  _getChannelFromId((string)message["channel"]));
            }
            else if (_isChannelJoined(message))
            {
                SlackChannel channel = _getChannelFromId((string)message["channel"]["id"]);
                OnChannelJoined?.Invoke(this, channel);
            }
        }
        private void _redoAttendance(SlackMessage message, SlackChannel channel)
        {
            List <SlackUser> usersInMessage = _getUsersFromMessage(message, 3);

            try
            {
                int meeting = int.Parse(message.Text.Split(' ')[2]) + 1;
                foreach (KeyValuePair <SlackUser, List <bool> > pair in _data[channel])
                {
                    pair.Value[meeting] = !usersInMessage.Contains(pair.Key);
                }
                _countAttendance(message, channel);
            }
            catch (InvalidOperationException)
            {
                SendMessage(channel, "That is not a meeting I know of");
            }
            catch (IndexOutOfRangeException)
            {
                SendMessage(channel, "You haven't had that meeting yet");
            }
        }
        private void _parseMessage(object sender, SlackMessage message, SlackChannel channel)
        {
            if (!message.Text.StartsWith($"<@{BotId}>"))
            {
                return;
            }
            if (!_isTracking(channel))
            {
                _data.Add(channel, new AttendanceData());
            }
            Dictionary <string, Delegate> commands = new Dictionary <string, Delegate>()
            {
                { "help", new Action <SlackMessage, SlackChannel>(helpCommand) },
                { "keep", new Action <SlackMessage, SlackChannel>(_keepAttendance) },
                { "track", new Action <SlackMessage, SlackChannel>(_trackUsers) },
                { "image", new Action <SlackMessage, SlackChannel>(_countAttendance) },
                { "reset", new Action <SlackMessage, SlackChannel>(_resetChannel) },
                { "revert", new Action <SlackMessage, SlackChannel>(_revertAttendance) },
                { "redo", new Action <SlackMessage, SlackChannel>(_redoAttendance) }
            };

            string[] msgParts = message.Text.Split(' ');

            try
            {
                commands[msgParts[1]].Method.Invoke(this, new object[] { message, channel });
            }
            catch (KeyNotFoundException)
            {
                SendMessage(channel, "I do not understand that command.");
            }
            catch (IndexOutOfRangeException)
            {
                SendMessage(channel, "I do not understand that command.");
            }
        }
 private bool _isTracking(SlackChannel channel)
 {
     return(_data.ContainsKey(channel));
 }
 private void _revertAttendance(SlackMessage message, SlackChannel channel)
 {
     _data[channel].RemoveMeeting(_data[channel].Count - 1);
 }
 private void _resetChannel(SlackMessage message, SlackChannel channel)
 {
     _data[channel] = new AttendanceData();
     saveData();
 }
        public override bool Equals(object obj)
        {
            SlackChannel slackChannel = obj as SlackChannel;

            return(slackChannel != null && Name.Equals(slackChannel.Name));
        }
 public List <SlackUser> GetUsersFromChannel(SlackChannel channel)
 {
     return(channel.Members.Select(_getUserFromId).ToList());
 }
        /// <summary>Uploads a file to a slack channel</summary><
        /// <param name="channel">Channel to upload the file in</param>
        /// <param name="path">Path to the file to upload</param>
        /// <param name="filename">Filename for the uploaded file on slack</param>
        public void SlackSendFile(SlackChannel channel, string path, string filename)
        {
            WebClient wc = new WebClient();

            wc.UploadFile("https://slack.com/api/files.upload?token=" + _token + "&filename=" + filename + "&channels=" + channel.Id, path);
        }