using NadekoBot.Common; using NadekoBot.Services.Common; using NadekoBot.Extensions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Serilog; namespace NadekoBot.Services { public sealed class RedisImagesCache : IImageCache { private readonly ConnectionMultiplexer _con; private readonly IBotCredentials _creds; private readonly HttpClient _http; private IDatabase _db => _con.GetDatabase(); private const string _basePath = "data/"; private const string _cardsPath = "data/images/cards"; public ImageUrls ImageUrls { get; private set; } public IReadOnlyList Heads => GetByteArrayData(ImageKey.Coins_Heads); public IReadOnlyList Tails => GetByteArrayData(ImageKey.Coins_Tails); public IReadOnlyList Dice => GetByteArrayData(ImageKey.Dice); public IReadOnlyList SlotEmojis => GetByteArrayData(ImageKey.Slots_Emojis); public IReadOnlyList SlotNumbers => GetByteArrayData(ImageKey.Slots_Numbers); public IReadOnlyList Currency => GetByteArrayData(ImageKey.Currency); public byte[] SlotBackground => GetByteData(ImageKey.Slots_Bg); public byte[] RategirlMatrix => GetByteData(ImageKey.Rategirl_Matrix); public byte[] RategirlDot => GetByteData(ImageKey.Rategirl_Dot); public byte[] XpBackground => GetByteData(ImageKey.Xp_Bg); public byte[] Rip => GetByteData(ImageKey.Rip_Bg); public byte[] RipOverlay => GetByteData(ImageKey.Rip_Overlay); public byte[] GetCard(string key) { return _con.GetDatabase().StringGet(GetKey("card_" + key)); } public enum ImageKey { Coins_Heads, Coins_Tails, Dice, Slots_Bg, Slots_Numbers, Slots_Emojis, Rategirl_Matrix, Rategirl_Dot, Xp_Bg, Rip_Bg, Rip_Overlay, Currency, } public RedisImagesCache(ConnectionMultiplexer con, IBotCredentials creds) { _con = con; _creds = creds; _http = new HttpClient(); ImageUrls = JsonConvert.DeserializeObject( File.ReadAllText(Path.Combine(_basePath, "images.json"))); } public async Task AllKeysExist() { try { var results = await Task.WhenAll(Enum.GetNames(typeof(ImageKey)) .Select(x => x.ToLowerInvariant()) .Select(x => _db.KeyExistsAsync(GetKey(x)))) .ConfigureAwait(false); var cardsExist = await Task.WhenAll(GetAllCardNames() .Select(x => "card_" + x) .Select(x => _db.KeyExistsAsync(GetKey(x)))) .ConfigureAwait(false); var num = results.Where(x => !x).Count(); return results.All(x => x) && cardsExist.All(x => x); } catch (Exception ex) { Log.Warning(ex, "Error checking for Image keys"); return false; } } public async Task Reload() { try { var sw = Stopwatch.StartNew(); var obj = JObject.Parse( File.ReadAllText(Path.Combine(_basePath, "images.json"))); ImageUrls = obj.ToObject(); var t = new ImageLoader(_http, _con, GetKey) .LoadAsync(obj); var loadCards = Task.Run(async () => { await _db.StringSetAsync(Directory.GetFiles(_cardsPath) .ToDictionary( x => GetKey("card_" + Path.GetFileNameWithoutExtension(x)), x => (RedisValue)File.ReadAllBytes(x)) // loads them and creates pairs to store in redis .ToArray()) .ConfigureAwait(false); }); await Task.WhenAll(t, loadCards).ConfigureAwait(false); sw.Stop(); Log.Information($"Images reloaded in {sw.Elapsed.TotalSeconds:F2}s"); } catch (Exception ex) { Log.Error(ex, "Error reloading image service"); throw; } } private IEnumerable GetAllCardNames(bool showExtension = false) { return Directory.GetFiles(_cardsPath) // gets all cards from the cards folder .Select(x => showExtension ? Path.GetFileName(x) : Path.GetFileNameWithoutExtension(x)); // gets their names } public RedisKey GetKey(string key) { return $"{_creds.RedisKey()}_localimg_{key.ToLowerInvariant()}"; } public byte[] GetByteData(string key) { return _db.StringGet(GetKey(key)); } public byte[] GetByteData(ImageKey key) => GetByteData(key.ToString()); public RedisImageArray GetByteArrayData(string key) { return new RedisImageArray(GetKey(key), _con); } public RedisImageArray GetByteArrayData(ImageKey key) => GetByteArrayData(key.ToString()); } }