Files
nadekobot/src/NadekoBot/Services/Impl/RedisCache.cs
Kwoth a1ef862382 - small bot.cs cleanup
- creds.yml now loads and reloads properly (from the current directory, like credentials.json)
- added empty creds.yml to repo, and added it to .gitignore
2021-09-06 21:34:49 +02:00

215 lines
7.1 KiB
C#

using NadekoBot.Extensions;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Services
{
public class RedisCache : IDataCache
{
public ConnectionMultiplexer Redis { get; }
public IImageCache LocalImages { get; }
public ILocalDataCache LocalData { get; }
private readonly string _redisKey;
private readonly EndPoint _redisEndpoint;
public RedisCache(ConnectionMultiplexer redis, IBotCredentials creds, DiscordSocketClient client)
{
Redis = redis;
_redisEndpoint = Redis.GetEndPoints().First();
LocalImages = new RedisImagesCache(Redis, creds);
LocalData = new RedisLocalDataCache(Redis, creds, client.ShardId);
_redisKey = creds.RedisKey();
}
// things here so far don't need the bot id
// because it's a good thing if different bots
// which are hosted on the same PC
// can re-use the same image/anime data
public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(Uri key)
{
var _db = Redis.GetDatabase();
byte[] x = await _db.StringGetAsync("image_" + key).ConfigureAwait(false);
return (x != null, x);
}
public Task SetImageDataAsync(Uri key, byte[] data)
{
var _db = Redis.GetDatabase();
return _db.StringSetAsync("image_" + key, data);
}
public async Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key)
{
var _db = Redis.GetDatabase();
string x = await _db.StringGetAsync("anime_" + key).ConfigureAwait(false);
return (x != null, x);
}
public Task SetAnimeDataAsync(string key, string data)
{
var _db = Redis.GetDatabase();
return _db.StringSetAsync("anime_" + key, data, expiry: TimeSpan.FromHours(3));
}
public async Task<(bool Success, string Data)> TryGetNovelDataAsync(string key)
{
var _db = Redis.GetDatabase();
string x = await _db.StringGetAsync("novel_" + key).ConfigureAwait(false);
return (x != null, x);
}
public Task SetNovelDataAsync(string key, string data)
{
var _db = Redis.GetDatabase();
return _db.StringSetAsync("novel_" + key, data, expiry: TimeSpan.FromHours(3));
}
private readonly object timelyLock = new object();
public TimeSpan? AddTimelyClaim(ulong id, int period)
{
if (period == 0)
return null;
lock (timelyLock)
{
var time = TimeSpan.FromHours(period);
var _db = Redis.GetDatabase();
if ((bool?)_db.StringGet($"{_redisKey}_timelyclaim_{id}") is null)
{
_db.StringSet($"{_redisKey}_timelyclaim_{id}", true, time);
return null;
}
return _db.KeyTimeToLive($"{_redisKey}_timelyclaim_{id}");
}
}
public void RemoveAllTimelyClaims()
{
var server = Redis.GetServer(_redisEndpoint);
var _db = Redis.GetDatabase();
foreach (var k in server.Keys(pattern: $"{_redisKey}_timelyclaim_*"))
{
_db.KeyDelete(k, CommandFlags.FireAndForget);
}
}
public bool TryAddAffinityCooldown(ulong userId, out TimeSpan? time)
{
var _db = Redis.GetDatabase();
time = _db.KeyTimeToLive($"{_redisKey}_affinity_{userId}");
if (time is null)
{
time = TimeSpan.FromMinutes(30);
_db.StringSet($"{_redisKey}_affinity_{userId}", true, time);
return true;
}
return false;
}
public bool TryAddDivorceCooldown(ulong userId, out TimeSpan? time)
{
var _db = Redis.GetDatabase();
time = _db.KeyTimeToLive($"{_redisKey}_divorce_{userId}");
if (time is null)
{
time = TimeSpan.FromHours(6);
_db.StringSet($"{_redisKey}_divorce_{userId}", true, time);
return true;
}
return false;
}
public Task SetStreamDataAsync(string url, string data)
{
var _db = Redis.GetDatabase();
return _db.StringSetAsync($"{_redisKey}_stream_{url}", data, expiry: TimeSpan.FromHours(6));
}
public bool TryGetStreamData(string url, out string dataStr)
{
var _db = Redis.GetDatabase();
dataStr = _db.StringGet($"{_redisKey}_stream_{url}");
return !string.IsNullOrWhiteSpace(dataStr);
}
public TimeSpan? TryAddRatelimit(ulong id, string name, int expireIn)
{
var _db = Redis.GetDatabase();
if (_db.StringSet($"{_redisKey}_ratelimit_{id}_{name}",
0, // i don't use the value
TimeSpan.FromSeconds(expireIn),
When.NotExists))
{
return null;
}
return _db.KeyTimeToLive($"{_redisKey}_ratelimit_{id}_{name}");
}
public bool TryGetEconomy(out string data)
{
var _db = Redis.GetDatabase();
if ((data = _db.StringGet($"{_redisKey}_economy")) != null)
{
return true;
}
return false;
}
public void SetEconomy(string data)
{
var _db = Redis.GetDatabase();
_db.StringSet($"{_redisKey}_economy",
data,
expiry: TimeSpan.FromMinutes(3));
}
public async Task<TOut> GetOrAddCachedDataAsync<TParam, TOut>(string key, Func<TParam, Task<TOut>> factory, TParam param, TimeSpan expiry) where TOut : class
{
var _db = Redis.GetDatabase();
RedisValue data = await _db.StringGetAsync(key).ConfigureAwait(false);
if (!data.HasValue)
{
var obj = await factory(param).ConfigureAwait(false);
if (obj is null)
return default(TOut);
await _db.StringSetAsync(key, JsonConvert.SerializeObject(obj),
expiry: expiry).ConfigureAwait(false);
return obj;
}
return (TOut)JsonConvert.DeserializeObject(data, typeof(TOut));
}
public DateTime GetLastCurrencyDecay()
{
var db = Redis.GetDatabase();
var str = (string)db.StringGet($"{_redisKey}_last_currency_decay");
if(string.IsNullOrEmpty(str))
return DateTime.MinValue;
return JsonConvert.DeserializeObject<DateTime>(str);
}
public void SetLastCurrencyDecay()
{
var db = Redis.GetDatabase();
db.StringSet($"{_redisKey}_last_currency_decay", JsonConvert.SerializeObject(DateTime.UtcNow));
}
}
}