diff --git a/src/NadekoBot/DataStructures/DisposableImutableList.cs b/src/NadekoBot/DataStructures/DisposableImutableList.cs new file mode 100644 index 00000000..71bda6de --- /dev/null +++ b/src/NadekoBot/DataStructures/DisposableImutableList.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.DataStructures +{ + + public static class DisposableReadOnlyListExtensions + { + public static IDisposableReadOnlyList AsDisposable(this IReadOnlyList arr) where T : IDisposable + => new DisposableReadOnlyList(arr); + + public static IDisposableReadOnlyList> AsDisposable(this IReadOnlyList> arr) where TValue : IDisposable + => new DisposableReadOnlyList(arr); + } + + public interface IDisposableReadOnlyList : IReadOnlyList, IDisposable + { + } + + public class DisposableReadOnlyList : IDisposableReadOnlyList + where T : IDisposable + { + private readonly IReadOnlyList _arr; + + public int Count => _arr.Count; + + public T this[int index] => _arr[index]; + + public DisposableReadOnlyList(IReadOnlyList arr) + { + this._arr = arr; + } + + public IEnumerator GetEnumerator() + => _arr.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _arr.GetEnumerator(); + + public void Dispose() + { + foreach (var item in _arr) + { + item.Dispose(); + } + } + } + + public class DisposableReadOnlyList : IDisposableReadOnlyList> + where U : IDisposable + { + private readonly IReadOnlyList> _arr; + + public int Count => _arr.Count; + + KeyValuePair IReadOnlyList>.this[int index] => _arr[index]; + + public DisposableReadOnlyList(IReadOnlyList> arr) + { + this._arr = arr; + } + + public IEnumerator> GetEnumerator() => + _arr.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + _arr.GetEnumerator(); + + public void Dispose() + { + foreach (var item in _arr) + { + item.Value.Dispose(); + } + } + } +} diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 137fa10c..80356f73 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -173,10 +173,8 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task ReloadImages() { - var sw = Stopwatch.StartNew(); - await NadekoBot.Images.Reload().ConfigureAwait(false); - sw.Stop(); - await Context.Channel.SendConfirmAsync("Images Reloaded").ConfigureAwait(false); + var time = await NadekoBot.Images.Reload().ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Images loaded after {time.TotalSeconds:F3}s!").ConfigureAwait(false); } private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 01283c83..4616aa91 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -34,15 +34,11 @@ namespace NadekoBot.Modules.Gambling var num2 = gen % 10; var imageStream = await Task.Run(() => { - try - { - var ms = new MemoryStream(); - new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); - ms.Position = 0; - return ms; - } - catch { return new MemoryStream(); } - }); + var ms = new MemoryStream(); + new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); + ms.Position = 0; + return ms; + }).ConfigureAwait(false); await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false); } @@ -82,7 +78,7 @@ namespace NadekoBot.Modules.Gambling await InternallDndRoll(arg, false).ConfigureAwait(false); } - private async Task InternalRoll( int num, bool ordered) + private async Task InternalRoll(int num, bool ordered) { if (num < 1 || num > 30) { @@ -209,20 +205,24 @@ namespace NadekoBot.Modules.Gambling private Image GetDice(int num) { - const string pathToImage = "data/images/dice"; - if (num != 10) + if (num < 0 || num > 10) + throw new ArgumentOutOfRangeException(nameof(num)); + + if (num == 10) { - using (var stream = File.OpenRead(Path.Combine(pathToImage, $"{num}.png"))) - return new Image(stream); + var images = NadekoBot.Images.Dice; + using (var imgOneStream = images[1].Value.ToStream()) + using (var imgZeroStream = images[0].Value.ToStream()) + { + Image imgOne = new Image(imgOneStream); + Image imgZero = new Image(imgZeroStream); + + return new[] { imgOne, imgZero }.Merge(); + } } - - using (var one = File.OpenRead(Path.Combine(pathToImage, "1.png"))) - using (var zero = File.OpenRead(Path.Combine(pathToImage, "0.png"))) + using (var die = NadekoBot.Images.Dice[num].Value.ToStream()) { - Image imgOne = new Image(one); - Image imgZero = new Image(zero); - - return new[] { imgOne, imgZero }.Merge(); + return new Image(die); } } } diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 74e201c0..6ccfe786 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -5,6 +5,7 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Image = ImageSharp.Image; @@ -32,9 +33,19 @@ namespace NadekoBot.Modules.Gambling if (count == 1) { if (rng.Next(0, 2) == 1) - await Context.Channel.SendFileAsync(_images.Heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + { + using (var heads = _images.Heads.ToStream()) + { + await Context.Channel.SendFileAsync(heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + } + } else - await Context.Channel.SendFileAsync(_images.Tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + { + using (var tails = _images.Tails.ToStream()) + { + await Context.Channel.SendFileAsync(tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + } + } return; } if (count > 10 || count < 1) @@ -43,13 +54,17 @@ namespace NadekoBot.Modules.Gambling return; } var imgs = new Image[count]; - for (var i = 0; i < count; i++) + using (var heads = _images.Heads.ToStream()) + using(var tails = _images.Tails.ToStream()) { - imgs[i] = rng.Next(0, 10) < 5 ? - new Image(_images.Heads) : - new Image(_images.Tails); + for (var i = 0; i < count; i++) + { + imgs[i] = rng.Next(0, 10) < 5 ? + new Image(heads) : + new Image(tails); + } + await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } - await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -74,9 +89,10 @@ namespace NadekoBot.Modules.Gambling //heads = true //tails = false + //todo this seems stinky, no time to look at it right now var isHeads = guessStr == "HEADS" || guessStr == "H"; bool result = false; - Stream imageToSend; + IEnumerable imageToSend; if (rng.Next(0, 2) == 1) { imageToSend = _images.Heads; @@ -98,8 +114,10 @@ namespace NadekoBot.Modules.Gambling { str = $"{Context.User.Mention}`Better luck next time.`"; } - - await Context.Channel.SendFileAsync(imageToSend, "result.png", str).ConfigureAwait(false); + using (var toSend = imageToSend.ToStream()) + { + await Context.Channel.SendFileAsync(toSend, "result.png", str).ConfigureAwait(false); + } } } } diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index e1671a6d..445d6c48 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -21,52 +21,19 @@ namespace NadekoBot.Modules.Gambling private static int totalBet = 0; private static int totalPaidOut = 0; - private const string backgroundPath = "data/slots/background.png"; - - private static readonly byte[] backgroundBuffer; - private static readonly byte[][] numbersBuffer = new byte[10][]; - private static readonly byte[][] emojiBuffer; - const int alphaCutOut = byte.MaxValue / 3; - static Slots() - { - backgroundBuffer = File.ReadAllBytes(backgroundPath); - - for (int i = 0; i < 10; i++) - { - numbersBuffer[i] = File.ReadAllBytes("data/slots/" + i + ".png"); - } - int throwaway; - var emojiFiles = Directory.GetFiles("data/slots/emojis/", "*.png") - .Where(f => int.TryParse(Path.GetFileNameWithoutExtension(f), out throwaway)) - .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) - .ToArray(); - - emojiBuffer = new byte[emojiFiles.Length][]; - for (int i = 0; i < emojiFiles.Length; i++) - { - emojiBuffer[i] = File.ReadAllBytes(emojiFiles[i]); - } - } - - - private static MemoryStream InternalGetStream(string path) - { - var ms = new MemoryStream(); - using (var fs = File.Open(path, FileMode.Open)) - { - fs.CopyTo(ms); - fs.Flush(); - } - ms.Position = 0; - return ms; - } - //here is a payout chart //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg //thanks to judge for helping me with this + private readonly IImagesService _images; + + public Slots() + { + this._images = NadekoBot.Images; + } + public class SlotMachine { public const int MaxValue = 5; @@ -154,7 +121,7 @@ namespace NadekoBot.Modules.Gambling var sb = new StringBuilder(); const int bet = 1; int payout = 0; - foreach (var key in dict.Keys.OrderByDescending(x=>x)) + foreach (var key in dict.Keys.OrderByDescending(x => x)) { sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%"); payout += key * dict[key]; @@ -164,6 +131,7 @@ namespace NadekoBot.Modules.Gambling } static HashSet runningUsers = new HashSet(); + [NadekoCommand, Usage, Description, Aliases] public async Task Slot(int amount = 0) { @@ -189,7 +157,7 @@ namespace NadekoBot.Modules.Gambling return; } Interlocked.Add(ref totalBet, amount); - using (var bgFileStream = new MemoryStream(backgroundBuffer)) + using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream()) { var bgImage = new ImageSharp.Image(bgFileStream); @@ -199,7 +167,7 @@ namespace NadekoBot.Modules.Gambling { for (int i = 0; i < 3; i++) { - using (var file = new MemoryStream(emojiBuffer[numbers[i]])) + using (var file = _images.SlotEmojis[numbers[i]].ToStream()) { var randomImage = new ImageSharp.Image(file); using (var toAdd = randomImage.Lock()) @@ -226,7 +194,7 @@ namespace NadekoBot.Modules.Gambling do { var digit = printWon % 10; - using (var fs = new MemoryStream(numbersBuffer[digit])) + using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream()) { var img = new ImageSharp.Image(fs); using (var pixels = img.Lock()) @@ -251,7 +219,7 @@ namespace NadekoBot.Modules.Gambling do { var digit = printAmount % 10; - using (var fs = new MemoryStream(numbersBuffer[digit])) + using (var fs = _images.SlotNumbers[digit].ToStream()) { var img = new ImageSharp.Image(fs); using (var pixels = img.Lock()) @@ -301,4 +269,4 @@ namespace NadekoBot.Modules.Gambling } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 0ae16be6..1c8671f8 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -10,6 +10,7 @@ using NLog; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -97,13 +98,16 @@ namespace NadekoBot.Modules.Games firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; } var file = GetRandomCurrencyImage(); - var sent = await channel.SendFileAsync( - file.Item2, - file.Item1, - $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") - .ConfigureAwait(false); + using (var fileStream = file.Value.ToStream()) + { + var sent = await channel.SendFileAsync( + fileStream, + file.Key, + $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") + .ConfigureAwait(false); - msgs[0] = sent; + msgs[0] = sent; + } plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); } @@ -167,18 +171,15 @@ namespace NadekoBot.Modules.Games return; } - var file = GetRandomCurrencyImage(); - IUserMessage msg; + var imgData = GetRandomCurrencyImage(); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; - if (file == null) + + IUserMessage msg; + using (var toSend = imgData.Value.ToStream()) { - msg = await Context.Channel.SendConfirmAsync(NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); - } - else - { - msg = await Context.Channel.SendFileAsync(file.Item2, file.Item1, msgToSend).ConfigureAwait(false); + msg = await Context.Channel.SendFileAsync(toSend, imgData.Key, msgToSend).ConfigureAwait(false); } var msgs = new IUserMessage[amount]; @@ -228,12 +229,12 @@ namespace NadekoBot.Modules.Games } } - private static Tuple GetRandomCurrencyImage() + private static KeyValuePair> GetRandomCurrencyImage() { var rng = new NadekoRandom(); var images = NadekoBot.Images.Currency; - return images[rng.Next(0, images.Count)]; + return images[rng.Next(0, images.Length)]; } int GetRandomNumber() diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index 7d98cd06..81e21907 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -1,4 +1,5 @@ -using System; +using NadekoBot.DataStructures; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -10,12 +11,16 @@ namespace NadekoBot.Services { public interface IImagesService { - Stream Heads { get; } - Stream Tails { get; } + ImmutableArray Heads { get; } + ImmutableArray Tails { get; } - IImmutableList> Currency { get; } - IImmutableList> Dice { get; } + ImmutableArray>> Currency { get; } + ImmutableArray>> Dice { get; } - Task Reload(); + ImmutableArray SlotBackground { get; } + ImmutableArray> SlotEmojis { get; } + ImmutableArray> SlotNumbers { get; } + + Task Reload(); } } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 80898dc1..02bcb1cd 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -1,4 +1,5 @@ -using NLog; +using NadekoBot.DataStructures; +using NLog; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -15,27 +16,30 @@ namespace NadekoBot.Services.Impl { private readonly Logger _log; - private const string headsPath = "data/images/coins/heads.png"; - private const string tailsPath = "data/images/coins/tails.png"; + private const string basePath = "data/images/"; - private const string currencyImagesPath = "data/currency_images"; - private const string diceImagesPath = "data/images/dice"; + private const string headsPath = basePath + "coins/heads.png"; + private const string tailsPath = basePath + "coins/tails.png"; - private byte[] heads; - public Stream Heads => new MemoryStream(heads, false); + private const string currencyImagesPath = basePath + "currency"; + private const string diceImagesPath = basePath + "dice"; + + private const string slotBackgroundPath = basePath + "slots/background.png"; + private const string slotNumbersPath = basePath + "slots/numbers/"; + private const string slotEmojisPath = basePath + "slots/emojis/"; + + + public ImmutableArray Heads { get; private set; } + public ImmutableArray Tails { get; private set; } - private byte[] tails; - public Stream Tails => new MemoryStream(tails, false); //todo C#7 - private IReadOnlyDictionary currency; - public IImmutableList> Currency => - currency.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) - .ToImmutableArray(); + public ImmutableArray>> Currency { get; private set; } - private IReadOnlyDictionary dice; - public IImmutableList> Dice => - dice.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) - .ToImmutableArray(); + public ImmutableArray>> Dice { get; private set; } + + public ImmutableArray SlotBackground { get; private set; } + public ImmutableArray> SlotNumbers { get; private set; } + public ImmutableArray> SlotEmojis { get; private set; } private ImagesService() { @@ -49,18 +53,41 @@ namespace NadekoBot.Services.Impl return srvc; } - public Task Reload() => Task.Run(() => + public Task Reload() => Task.Run(() => { try { _log.Info("Loading images..."); var sw = Stopwatch.StartNew(); - heads = File.ReadAllBytes(headsPath); - tails = File.ReadAllBytes(tailsPath); + Heads = File.ReadAllBytes(headsPath).ToImmutableArray(); + Tails = File.ReadAllBytes(tailsPath).ToImmutableArray(); - currency = Directory.GetFiles(currencyImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); - dice = Directory.GetFiles(diceImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); + Currency = Directory.GetFiles(currencyImagesPath) + .Select(x => new KeyValuePair>( + Path.GetFileName(x), + File.ReadAllBytes(x).ToImmutableArray())) + .ToImmutableArray(); + + Dice = Directory.GetFiles(diceImagesPath) + .OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x))) + .Select(x => new KeyValuePair>(x, + File.ReadAllBytes(x).ToImmutableArray())) + .ToImmutableArray(); + + SlotBackground = File.ReadAllBytes(slotBackgroundPath).ToImmutableArray(); + + SlotNumbers = Directory.GetFiles(slotNumbersPath) + .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) + .Select(x => File.ReadAllBytes(x).ToImmutableArray()) + .ToImmutableArray(); + + SlotEmojis = Directory.GetFiles(slotEmojisPath) + .Select(x => File.ReadAllBytes(x).ToImmutableArray()) + .ToImmutableArray(); + + sw.Stop(); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); + return sw.Elapsed; } catch (Exception ex) { diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 56479a6f..4c32a542 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -20,7 +20,10 @@ namespace NadekoBot.Extensions { private const string arrow_left = "⬅"; private const string arrow_right = "➡"; - + + public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) + => new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); + /// /// danny kamisama /// diff --git a/src/NadekoBot/data/currency_images/img1.jpg b/src/NadekoBot/data/images/currency/img1.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img1.jpg rename to src/NadekoBot/data/images/currency/img1.jpg diff --git a/src/NadekoBot/data/currency_images/img2.jpg b/src/NadekoBot/data/images/currency/img2.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img2.jpg rename to src/NadekoBot/data/images/currency/img2.jpg diff --git a/src/NadekoBot/data/currency_images/img3.jpg b/src/NadekoBot/data/images/currency/img3.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img3.jpg rename to src/NadekoBot/data/images/currency/img3.jpg diff --git a/src/NadekoBot/data/slots/background.png b/src/NadekoBot/data/images/slots/background.png similarity index 100% rename from src/NadekoBot/data/slots/background.png rename to src/NadekoBot/data/images/slots/background.png diff --git a/src/NadekoBot/data/slots/emojis/0.png b/src/NadekoBot/data/images/slots/emojis/0.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/0.png rename to src/NadekoBot/data/images/slots/emojis/0.png diff --git a/src/NadekoBot/data/slots/emojis/1.png b/src/NadekoBot/data/images/slots/emojis/1.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/1.png rename to src/NadekoBot/data/images/slots/emojis/1.png diff --git a/src/NadekoBot/data/slots/emojis/2.png b/src/NadekoBot/data/images/slots/emojis/2.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/2.png rename to src/NadekoBot/data/images/slots/emojis/2.png diff --git a/src/NadekoBot/data/slots/emojis/3.png b/src/NadekoBot/data/images/slots/emojis/3.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/3.png rename to src/NadekoBot/data/images/slots/emojis/3.png diff --git a/src/NadekoBot/data/slots/emojis/4.png b/src/NadekoBot/data/images/slots/emojis/4.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/4.png rename to src/NadekoBot/data/images/slots/emojis/4.png diff --git a/src/NadekoBot/data/slots/emojis/5.png b/src/NadekoBot/data/images/slots/emojis/5.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/5.png rename to src/NadekoBot/data/images/slots/emojis/5.png diff --git a/src/NadekoBot/data/slots/0.png b/src/NadekoBot/data/images/slots/numbers/0.png similarity index 100% rename from src/NadekoBot/data/slots/0.png rename to src/NadekoBot/data/images/slots/numbers/0.png diff --git a/src/NadekoBot/data/slots/1.png b/src/NadekoBot/data/images/slots/numbers/1.png similarity index 100% rename from src/NadekoBot/data/slots/1.png rename to src/NadekoBot/data/images/slots/numbers/1.png diff --git a/src/NadekoBot/data/slots/2.png b/src/NadekoBot/data/images/slots/numbers/2.png similarity index 100% rename from src/NadekoBot/data/slots/2.png rename to src/NadekoBot/data/images/slots/numbers/2.png diff --git a/src/NadekoBot/data/slots/3.png b/src/NadekoBot/data/images/slots/numbers/3.png similarity index 100% rename from src/NadekoBot/data/slots/3.png rename to src/NadekoBot/data/images/slots/numbers/3.png diff --git a/src/NadekoBot/data/slots/4.png b/src/NadekoBot/data/images/slots/numbers/4.png similarity index 100% rename from src/NadekoBot/data/slots/4.png rename to src/NadekoBot/data/images/slots/numbers/4.png diff --git a/src/NadekoBot/data/slots/5.png b/src/NadekoBot/data/images/slots/numbers/5.png similarity index 100% rename from src/NadekoBot/data/slots/5.png rename to src/NadekoBot/data/images/slots/numbers/5.png diff --git a/src/NadekoBot/data/slots/6.png b/src/NadekoBot/data/images/slots/numbers/6.png similarity index 100% rename from src/NadekoBot/data/slots/6.png rename to src/NadekoBot/data/images/slots/numbers/6.png diff --git a/src/NadekoBot/data/slots/7.png b/src/NadekoBot/data/images/slots/numbers/7.png similarity index 100% rename from src/NadekoBot/data/slots/7.png rename to src/NadekoBot/data/images/slots/numbers/7.png diff --git a/src/NadekoBot/data/slots/8.png b/src/NadekoBot/data/images/slots/numbers/8.png similarity index 100% rename from src/NadekoBot/data/slots/8.png rename to src/NadekoBot/data/images/slots/numbers/8.png diff --git a/src/NadekoBot/data/slots/9.png b/src/NadekoBot/data/images/slots/numbers/9.png similarity index 100% rename from src/NadekoBot/data/slots/9.png rename to src/NadekoBot/data/images/slots/numbers/9.png