mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
add: Added .notify and migrations, added levelup and protection events for notify, removed xpnotify completely
This commit is contained in:
23
src/NadekoBot/Modules/Administration/Notify/INotifyModel.cs
Normal file
23
src/NadekoBot/Modules/Administration/Notify/INotifyModel.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using NadekoBot.Db.Models;
|
||||
using System.Collections;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public interface INotifyModel
|
||||
{
|
||||
static abstract string KeyName { get; }
|
||||
static abstract NotifyType NotifyType { get; }
|
||||
IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements();
|
||||
|
||||
public virtual bool TryGetGuildId(out ulong guildId)
|
||||
{
|
||||
guildId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryGetUserId(out ulong userId)
|
||||
{
|
||||
userId = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public interface INotifySubscriber
|
||||
{
|
||||
Task NotifyAsync<T>(T data, bool isShardLocal = false)
|
||||
where T : struct, INotifyModel;
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public record struct LevelUpNotifyModel(
|
||||
ulong GuildId,
|
||||
ulong ChannelId,
|
||||
ulong UserId,
|
||||
long Level) : INotifyModel
|
||||
{
|
||||
public static string KeyName
|
||||
=> "notify.levelup";
|
||||
|
||||
public static NotifyType NotifyType
|
||||
=> NotifyType.LevelUp;
|
||||
|
||||
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||
{
|
||||
var data = this;
|
||||
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||
{
|
||||
{ "%event.level%", g => data.Level.ToString() },
|
||||
};
|
||||
}
|
||||
|
||||
public bool TryGetGuildId(out ulong guildId)
|
||||
{
|
||||
guildId = GuildId;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetUserId(out ulong userId)
|
||||
{
|
||||
userId = UserId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class INotifyModelExtensions
|
||||
{
|
||||
public static TypedKey<T> GetTypedKey<T>(this T model)
|
||||
where T : struct, INotifyModel
|
||||
=> new(T.KeyName);
|
||||
}
|
@@ -1,92 +1,24 @@
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public sealed class NotifyService : IReadyExecutor, INService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly IMessageSenderService _mss;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCreds _creds;
|
||||
|
||||
public NotifyService(
|
||||
DbService db,
|
||||
IMessageSenderService mss,
|
||||
DiscordSocketClient client,
|
||||
IBotCreds creds)
|
||||
{
|
||||
_db = db;
|
||||
_mss = mss;
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
// .Where(x => Linq2DbExpressions.GuildOnShard(guildId,
|
||||
// _creds.TotalShards,
|
||||
// _client.ShardId))
|
||||
}
|
||||
|
||||
public async Task EnableAsync(
|
||||
ulong guildId,
|
||||
ulong channelId,
|
||||
NotifyEvent nEvent,
|
||||
string message)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
await uow.GetTable<Notify>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
ChannelId = channelId,
|
||||
Event = nEvent,
|
||||
Message = message,
|
||||
},
|
||||
(_) => new()
|
||||
{
|
||||
Message = message,
|
||||
ChannelId = channelId
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Event = nEvent
|
||||
});
|
||||
}
|
||||
|
||||
public async Task DisableAsync(ulong guildId, NotifyEvent nEvent)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var deleted = await uow.GetTable<Notify>()
|
||||
.Where(x => x.GuildId == guildId && x.Event == nEvent)
|
||||
.DeleteAsync();
|
||||
|
||||
if (deleted > 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Administration
|
||||
{
|
||||
public class NotifyCommands : NadekoModule<NotifyService>
|
||||
{
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task Notify(NotifyEvent nEvent, [Leftover] string message = null)
|
||||
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
await _service.DisableAsync(ctx.Guild.Id, nEvent);
|
||||
await Response().Confirm(strs.notify_off(nEvent)).SendAsync();
|
||||
await _service.DisableAsync(ctx.Guild.Id, nType);
|
||||
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nEvent, message);
|
||||
await Response().Confirm(strs.notify_on(nEvent.ToString())).SendAsync();
|
||||
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
||||
await Response().Confirm(strs.notify_on(nType.ToString())).SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public static class NotifyKeys
|
||||
{
|
||||
public static TypedKey<LevelUpNotifyModel> LevelUp { get; } = new("notify:levelup");
|
||||
}
|
202
src/NadekoBot/Modules/Administration/Notify/NotifyService.cs
Normal file
202
src/NadekoBot/Modules/Administration/Notify/NotifyService.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public sealed class NotifyService : IReadyExecutor, INotifySubscriber, INService
|
||||
{
|
||||
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 _);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user