forked from EllieBotDevs/elliebot
202 lines
5.9 KiB
C#
202 lines
5.9 KiB
C#
using LinqToDB;
|
|
using LinqToDB.EntityFrameworkCore;
|
|
using EllieBot.Common.ModuleBehaviors;
|
|
using EllieBot.Db.Models;
|
|
|
|
namespace EllieBot.Modules.Administration;
|
|
|
|
public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
|
{
|
|
private readonly DbService _db;
|
|
private readonly IMessageSenderService _mss;
|
|
private readonly DiscordSocketClient _client;
|
|
private readonly IBotCreds _creds;
|
|
private readonly IReplacementService _repSvc;
|
|
private readonly IPubSub _pubSub;
|
|
private ConcurrentDictionary<NotifyType, ConcurrentDictionary<ulong, Notify>> _events = new();
|
|
|
|
public NotifyService(
|
|
DbService db,
|
|
IMessageSenderService mss,
|
|
DiscordSocketClient client,
|
|
IBotCreds creds,
|
|
IReplacementService repSvc,
|
|
IPubSub pubSub)
|
|
{
|
|
_db = db;
|
|
_mss = mss;
|
|
_client = client;
|
|
_creds = creds;
|
|
_repSvc = repSvc;
|
|
_pubSub = pubSub;
|
|
}
|
|
|
|
public async Task OnReadyAsync()
|
|
{
|
|
await using var uow = _db.GetDbContext();
|
|
_events = (await uow.GetTable<Notify>()
|
|
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
|
_creds.TotalShards,
|
|
_client.ShardId))
|
|
.ToListAsyncLinqToDB())
|
|
.GroupBy(x => x.Type)
|
|
.ToDictionary(x => x.Key, x => x.ToDictionary(x => x.GuildId).ToConcurrent())
|
|
.ToConcurrent();
|
|
|
|
|
|
await SubscribeToEvent<LevelUpNotifyModel>();
|
|
}
|
|
|
|
private async Task SubscribeToEvent<T>()
|
|
where T : struct, INotifyModel
|
|
{
|
|
await _pubSub.Sub(new TypedKey<T>(T.KeyName), async (model) => await OnEvent(model));
|
|
}
|
|
|
|
public async Task NotifyAsync<T>(T data, bool isShardLocal = false)
|
|
where T : struct, INotifyModel
|
|
{
|
|
try
|
|
{
|
|
if (isShardLocal)
|
|
{
|
|
await OnEvent(data);
|
|
return;
|
|
}
|
|
|
|
await _pubSub.Pub(data.GetTypedKey(), data);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Warning(ex,
|
|
"Unknown error occurred while trying to triger {NotifyEvent} for {NotifyModel}",
|
|
T.KeyName,
|
|
data);
|
|
}
|
|
}
|
|
|
|
private async Task OnEvent<T>(T model)
|
|
where T : struct, INotifyModel
|
|
{
|
|
if (_events.TryGetValue(T.NotifyType, out var subs))
|
|
{
|
|
if (model.TryGetGuildId(out var gid))
|
|
{
|
|
if (!subs.TryGetValue(gid, out var conf))
|
|
return;
|
|
|
|
await HandleNotifyEvent(conf, model);
|
|
return;
|
|
}
|
|
|
|
foreach (var key in subs.Keys.ToArray())
|
|
{
|
|
if (subs.TryGetValue(key, out var notif))
|
|
{
|
|
try
|
|
{
|
|
await HandleNotifyEvent(notif, model);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex,
|
|
"Error occured while sending notification {NotifyEvent} to guild {GuildId}: {ErrorMessage}",
|
|
T.NotifyType,
|
|
key,
|
|
ex.Message);
|
|
}
|
|
|
|
await Task.Delay(500);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task HandleNotifyEvent(Notify conf, INotifyModel model)
|
|
{
|
|
var guild = _client.GetGuild(conf.GuildId);
|
|
var channel = guild?.GetTextChannel(conf.ChannelId);
|
|
|
|
if (guild is null || channel is null)
|
|
return;
|
|
|
|
IUser? user = null;
|
|
if (model.TryGetUserId(out var userId))
|
|
{
|
|
user = guild.GetUser(userId) ?? _client.GetUser(userId);
|
|
}
|
|
|
|
var rctx = new ReplacementContext(guild: guild, channel: channel, user: user);
|
|
|
|
var st = SmartText.CreateFrom(conf.Message);
|
|
foreach (var modelRep in model.GetReplacements())
|
|
{
|
|
rctx.WithOverride(modelRep.Key, () => modelRep.Value(guild));
|
|
}
|
|
|
|
st = await _repSvc.ReplaceAsync(st, rctx);
|
|
if (st is SmartPlainText spt)
|
|
{
|
|
await _mss.Response(channel)
|
|
.Confirm(spt.Text)
|
|
.SendAsync();
|
|
return;
|
|
}
|
|
|
|
await _mss.Response(channel)
|
|
.Text(st)
|
|
.SendAsync();
|
|
}
|
|
|
|
public async Task EnableAsync(
|
|
ulong guildId,
|
|
ulong channelId,
|
|
NotifyType nType,
|
|
string message)
|
|
{
|
|
await using var uow = _db.GetDbContext();
|
|
await uow.GetTable<Notify>()
|
|
.InsertOrUpdateAsync(() => new()
|
|
{
|
|
GuildId = guildId,
|
|
ChannelId = channelId,
|
|
Type = nType,
|
|
Message = message,
|
|
},
|
|
(_) => new()
|
|
{
|
|
Message = message,
|
|
ChannelId = channelId
|
|
},
|
|
() => new()
|
|
{
|
|
GuildId = guildId,
|
|
Type = nType
|
|
});
|
|
|
|
var eventDict = _events.GetOrAdd(nType, _ => new());
|
|
eventDict[guildId] = new()
|
|
{
|
|
GuildId = guildId,
|
|
ChannelId = channelId,
|
|
Type = nType,
|
|
Message = message
|
|
};
|
|
}
|
|
|
|
public async Task DisableAsync(ulong guildId, NotifyType nType)
|
|
{
|
|
await using var uow = _db.GetDbContext();
|
|
var deleted = await uow.GetTable<Notify>()
|
|
.Where(x => x.GuildId == guildId && x.Type == nType)
|
|
.DeleteAsync();
|
|
|
|
if (deleted == 0)
|
|
return;
|
|
|
|
if (!_events.TryGetValue(nType, out var guildsDict))
|
|
return;
|
|
|
|
guildsDict.TryRemove(guildId, out _);
|
|
}
|
|
}
|