public async Task Progress()
        {
            var author = Context.Message.Author;

            if (author.Id != Program.ApplicationSetting.Owner)
            {
                Context.Channel.SendMessageAsync("You aren't allowed to run this command", false);
                return;
            }

            (string basePath, string baseOutputPath) = MovieHelper.CleanAndCreateMovieFolders();

            SKRect rect = new SKRect(100, 100, 200, 200);

            var arcPaint = new SKPaint()
            {
                Color = new SKColor(255, 0, 0)
            };

            List <Task> tasks = new List <Task>();

            // draw empty rectabgle
            int frame = 0;

            // Spawn the border
            for (int i = 0; i < 10; i++)
            {
                var drawInfo = DrawingHelper.GetEmptyGraphics(505, 200);


                drawInfo.Canvas.DrawRect(70, 25, 365, 100, new SKPaint()
                {
                    Color = new SKColor(255, 0, 0, (byte)(20 * i + 30)), IsStroke = true, Style = SKPaintStyle.Stroke, StrokeWidth = 4
                });

                tasks.Add(MovieHelper.SaveToDisk(basePath, frame, drawInfo.Bitmap, drawInfo.Canvas));
                frame++;
            }

            // Standstill
            for (int i = 0; i < 5; i++)
            {
                var drawInfo = DrawingHelper.GetEmptyGraphics(505, 200);

                drawInfo.Canvas.DrawRect(70, 25, 365, 100, new SKPaint()
                {
                    Color = new SKColor(255, 0, 0, 255), IsStroke = true, Style = SKPaintStyle.Stroke, StrokeWidth = 4
                });

                tasks.Add(MovieHelper.SaveToDisk(basePath, frame, drawInfo.Bitmap, drawInfo.Canvas));
                frame++;
            }

            int dayOfTheYear = 60; Math.Min(DateTime.Now.DayOfYear, 365); // dont handle leap years for now TODO

            for (int i = 0; i < dayOfTheYear + 10; i++)
            {
                var drawInfo = DrawingHelper.GetEmptyGraphics(505, 200);

                for (int j = 0; j < Math.Min(i, 10); j++)
                {
                    drawInfo.Canvas.DrawRect(70, 23, Math.Min(i - j, dayOfTheYear), 100, new SKPaint()
                    {
                        Color = new SKColor(0, 255, 0, 25), Style = SKPaintStyle.Fill
                    });

                    if (j == i)
                    {
                        break;
                    }
                }

                // border
                drawInfo.Canvas.DrawRect(70, 25, 365, 100, new SKPaint()
                {
                    Color = new SKColor(255, 0, 0, 255), IsStroke = true, Style = SKPaintStyle.Stroke, StrokeWidth = 4
                });

                tasks.Add(MovieHelper.SaveToDisk(basePath, frame, drawInfo.Bitmap, drawInfo.Canvas));
                frame++;
            }


            /*            for (int i = 0; i < 180; i++)
             *  {
             *      var drawInfo = DrawingHelper.GetEmptyGraphics(500, 500);
             *
             *      using (SKPath path = new SKPath())
             *      {
             *          path.AddArc(rect, 0, i);
             *          drawInfo.Canvas.DrawPath(path, arcPaint);
             *
             *          tasks.Add(SaveToDisk(basePath, i, drawInfo.Bitmap, drawInfo.Canvas));
             *      }
             *  }*/

            int random = new Random().Next(1_000_000_000);

            var    files    = Directory.GetFiles(Path.Combine(basePath)).ToList().OrderBy(i => i);
            string fileName = Path.Combine(baseOutputPath, $"movie_{random}.gif");

            try
            {
                using (var collection = new MagickImageCollection())
                {
                    int i = 0;
                    foreach (var file in files)
                    {
                        collection.Add(file);
                        collection[i].AnimationDelay = 1; // in this example delay is 1000ms/1sec

                        if (i == 0)
                        {
                            collection[i].AnimationIterations = 0;
                        }

                        //collection[i].Flip();
                        i++;
                    }

                    // Optionally reduce colors
                    var settings = new QuantizeSettings();

                    //settings.Colors = 256;
                    //collection.Quantize(settings);

                    // Optionally optimize the images (images should have the same size).
                    //collection.Optimize();

                    // Save gif
                    collection.Write(fileName, MagickFormat.Gif);
                }
            }
            catch (Exception ex)
            {
            }

            await Context.Channel.SendFileAsync(fileName);
        }
        public async Task CreateMoviePlace(bool stacked, int groupByHour, int fps, bool drawDots)
        {
            var author = Context.Message.Author;

            if (author.Id != Program.ApplicationSetting.Owner)
            {
                Context.Channel.SendMessageAsync("You aren't allowed to run this command", false);
                return;
            }

            // Get users that havent pinged the role in the last 72h
            var sqlQuery = @"
    SELECT 
        HOUR(PlacedDateTime), 
        DATE(PlacedDateTime), 
        COUNT(*)
    FROM 
        PlaceBoardHistory 
    GROUP BY 
        HOUR(PlacedDateTime),
        DATE(PlacedDateTime)
    ORDER BY
        DATE(PlacedDateTime),
        HOUR(PlacedDateTime)";

            Stopwatch watch = new Stopwatch();

            watch.Start();


            List <GraphEntryInfo> messageTimes = new List <GraphEntryInfo>();



            var queryResult = await SQLHelper.GetQueryResults(null, sqlQuery, true, 10_000_000, true, true);


            Context.Channel.SendMessageAsync($"Retrieved data in {watch.ElapsedMilliseconds}ms");


            var parsedInfo = new ParsedGraphInfo()
            {
                ChannelId   = 1,
                Info        = new Dictionary <DateTimeOffset, int>(),
                ChannelName = "eth-place-bots",
                Color       = new SKColor(255, 0, 0)
            };

            var firstDateTime = DateTimeOffset.MaxValue;
            var lastDateTime  = DateTimeOffset.MinValue;


            foreach (var item in queryResult.Data)
            {
                int      hours    = Convert.ToInt32(item[0]);
                DateTime dateTime = Convert.ToDateTime(item[1]);
                int      count    = Convert.ToInt32(item[2]);

                var key = new DateTimeOffset(dateTime).AddHours(hours);
                parsedInfo.Info.Add(key, count);

                if (firstDateTime > key)
                {
                    firstDateTime = key;
                }
                if (lastDateTime < key)
                {
                    lastDateTime = key;
                }
            }


            double maxFrames = 600;

            int bound = (int)((lastDateTime - firstDateTime).TotalMinutes / maxFrames);

            Context.Channel.SendMessageAsync($"Group by {bound} minutes, Total mins {(lastDateTime - firstDateTime).TotalMinutes}");



            Context.Channel.SendMessageAsync($"Total frames {parsedInfo.Info.Count()}");

            //int val = 0;
            //foreach (var group in groups)
            //{
            //    val += group.Value;
            //    dataPointsSpam.Add(group.TimeStamp.DateTime, val);
            //}

            (string basePath, string baseOutputPath) = MovieHelper.CleanAndCreateMovieFolders();


            if (stacked)
            {
                var values = parsedInfo.Info.OrderBy(i => i.Key);
                int val    = 0;

                foreach (var info in values)
                {
                    val += info.Value;
                    parsedInfo.Info[info.Key] = val;
                }
            }

            try
            {
                List <Task> tasks = new List <Task>();

                for (int i = 2; i <= parsedInfo.Info.Count; i++)
                {
                    if (i % 250 == 0)
                    {
                        Context.Channel.SendMessageAsync($"Frame gen {i} out of {parsedInfo.Info.Count}");
                    }

                    var startTime = parsedInfo.Info.Keys.Take(i).Min();
                    var endTime   = parsedInfo.Info.Keys.Take(i).Max();

                    int maxY = 0;

                    // TODO calculate the rolling info


                    foreach (var info in parsedInfo.Info)
                    {
                        // only consider until this key
                        if (info.Key > endTime)
                        {
                            continue;
                        }

                        if (maxY < info.Value)
                        {
                            maxY = info.Value;
                        }
                    }


                    var drawInfo = DrawingHelper.GetEmptyGraphics();
                    var padding  = DrawingHelper.DefaultPadding;

                    padding.Left = 150; // large numbers

                    var labels = DrawingHelper.GetLabels(startTime.DateTime, endTime.DateTime, 0, maxY, 6, 10, " msg");

                    var gridSize = new GridSize(drawInfo.Bitmap, padding);

                    DrawingHelper.DrawGrid(drawInfo.Canvas, gridSize, padding, labels.XAxisLabels, labels.YAxisLabels, $"Messages count");

                    // TODO optimize some lines + move to draw helper
                    var dataPoints = parsedInfo.Info.Where(j => j.Key <= endTime).OrderBy(i => i.Key).ToDictionary(j => j.Key.DateTime, j => j.Value);

                    // todo add 2. y Axis on the right
                    var dataPointList = DrawingHelper.GetPoints(dataPoints, gridSize, true, startTime.DateTime, endTime.DateTime, false, maxY);

                    var highestPoint = -1f;

                    if (dataPoints.Count() > 0)
                    {
                        highestPoint = dataPointList.Min(i => i.Y);
                    }

                    DrawingHelper.DrawLine(drawInfo.Canvas, drawInfo.Bitmap, dataPointList, new SKPaint()
                    {
                        Color = parsedInfo.Color
                    }, 6, "#" + parsedInfo.ChannelName, 0, 0, drawDots, highestPoint);                                                                                                                  //new Pen(System.Drawing.Color.LightGreen)

                    tasks.Add(MovieHelper.SaveToDisk(basePath, i, drawInfo.Bitmap, drawInfo.Canvas));
                }

                await Context.Channel.SendMessageAsync("Finished processing, waiting for SaveToDisk");

                Task.WaitAll(tasks.ToArray());

                await Context.Channel.SendMessageAsync("Saving finished. Starting FFMpeg");

                int random = new Random().Next(1_000_000_000);
                //GlobalFFOptions.Configure(options => options.BinaryFolder = Program.Settings.FFMpegPath);
                Xabe.FFmpeg.FFmpeg.SetExecutablesPath(Program.ApplicationSetting.FFMpegPath);

                var    files    = Directory.GetFiles(Path.Combine(basePath)).ToList().OrderBy(i => i);
                string fileName = Path.Combine(baseOutputPath, $"movie_{random}.mp4");

                var conversion = new Conversion();
                conversion.SetInputFrameRate(fps);
                conversion.BuildVideoFromImages(files);
                conversion.SetFrameRate(fps);
                conversion.SetPixelFormat(PixelFormat.rgb24);
                conversion.SetOutput(fileName);
                conversion.OnProgress += Conversion_OnProgress;

                await conversion.Start();


                //var imageInfos = Directory.GetFiles(Path.Combine(basePath)).ToList().OrderBy(i => i).Select(i => ImageInfo.FromPath(i)).ToArray();
                //FFMpeg.JoinImageSequence(fileName, frameRate: 30, imageInfos);

                await Context.Channel.SendFileAsync(fileName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error while creating movie");
                await Context.Channel.SendMessageAsync(ex.ToString());
            }
        }