mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
add: notify, minesweeper, migrations
dev: renames, refactors change: remind optimized wait
This commit is contained in:
@@ -46,7 +46,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ImageOnlyChannel(StoopidTime time = null)
|
||||
public async Task ImageOnlyChannel(ParsedTimespan timespan = null)
|
||||
{
|
||||
var newValue = await _somethingOnly.ToggleImageOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||
if (newValue)
|
||||
@@ -59,7 +59,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageGuild)]
|
||||
public async Task LinkOnlyChannel(StoopidTime time = null)
|
||||
public async Task LinkOnlyChannel(ParsedTimespan timespan = null)
|
||||
{
|
||||
var newValue = await _somethingOnly.ToggleLinkOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||
if (newValue)
|
||||
@@ -72,10 +72,10 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageChannels)]
|
||||
[BotPerm(ChannelPerm.ManageChannels)]
|
||||
public async Task Slowmode(StoopidTime time = null)
|
||||
public async Task Slowmode(ParsedTimespan timespan = null)
|
||||
{
|
||||
var seconds = (int?)time?.Time.TotalSeconds ?? 0;
|
||||
if (time is not null && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
|
||||
var seconds = (int?)timespan?.Time.TotalSeconds ?? 0;
|
||||
if (timespan is not null && (timespan.Time < TimeSpan.FromSeconds(0) || timespan.Time > TimeSpan.FromHours(6)))
|
||||
return;
|
||||
|
||||
await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
|
||||
@@ -298,18 +298,18 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
public Task Delete(ulong messageId, StoopidTime time = null)
|
||||
=> Delete((ITextChannel)ctx.Channel, messageId, time);
|
||||
public Task Delete(ulong messageId, ParsedTimespan timespan = null)
|
||||
=> Delete((ITextChannel)ctx.Channel, messageId, timespan);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Delete(ITextChannel channel, ulong messageId, StoopidTime time = null)
|
||||
=> await InternalMessageAction(channel, messageId, time, msg => msg.DeleteAsync());
|
||||
public async Task Delete(ITextChannel channel, ulong messageId, ParsedTimespan timespan = null)
|
||||
=> await InternalMessageAction(channel, messageId, timespan, msg => msg.DeleteAsync());
|
||||
|
||||
private async Task InternalMessageAction(
|
||||
ITextChannel channel,
|
||||
ulong messageId,
|
||||
StoopidTime time,
|
||||
ParsedTimespan timespan,
|
||||
Func<IMessage, Task> func)
|
||||
{
|
||||
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
|
||||
@@ -334,13 +334,13 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
return;
|
||||
}
|
||||
|
||||
if (time is null)
|
||||
if (timespan is null)
|
||||
await msg.DeleteAsync();
|
||||
else if (time.Time <= TimeSpan.FromDays(7))
|
||||
else if (timespan.Time <= TimeSpan.FromDays(7))
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(time.Time);
|
||||
await Task.Delay(timespan.Time);
|
||||
await msg.DeleteAsync();
|
||||
});
|
||||
}
|
||||
|
@@ -72,18 +72,18 @@ public partial class Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
||||
[Priority(1)]
|
||||
public async Task Mute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
||||
public async Task Mute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, reason: reason);
|
||||
await _service.TimedMute(user, ctx.User, timespan.Time, reason: reason);
|
||||
await Response().Confirm(strs.user_muted_time(Format.Bold(user.ToString()),
|
||||
(int)time.Time.TotalMinutes)).SendAsync();
|
||||
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -133,18 +133,18 @@ public partial class Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(1)]
|
||||
public async Task ChatMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
||||
public async Task ChatMute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason);
|
||||
await _service.TimedMute(user, ctx.User, timespan.Time, MuteType.Chat, reason);
|
||||
await Response().Confirm(strs.user_chat_mute_time(Format.Bold(user.ToString()),
|
||||
(int)time.Time.TotalMinutes)).SendAsync();
|
||||
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -193,18 +193,18 @@ public partial class Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.MuteMembers)]
|
||||
[Priority(1)]
|
||||
public async Task VoiceMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
||||
public async Task VoiceMute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason);
|
||||
await _service.TimedMute(user, ctx.User, timespan.Time, MuteType.Voice, reason);
|
||||
await Response().Confirm(strs.user_voice_mute_time(Format.Bold(user.ToString()),
|
||||
(int)time.Time.TotalMinutes)).SendAsync();
|
||||
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@@ -0,0 +1,92 @@
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
await _service.DisableAsync(ctx.Guild.Id, nEvent);
|
||||
await Response().Confirm(strs.notify_off(nEvent)).SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nEvent, message);
|
||||
await Response().Confirm(strs.notify_on(nEvent.ToString())).SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,17 +28,17 @@ public partial class Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task AntiAlt(
|
||||
StoopidTime minAge,
|
||||
ParsedTimespan minAge,
|
||||
PunishmentAction action,
|
||||
[Leftover] StoopidTime punishTime = null)
|
||||
[Leftover] ParsedTimespan punishTimespan = null)
|
||||
{
|
||||
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
||||
var punishTimeMinutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||
var punishTimeMinutes = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||
|
||||
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
||||
return;
|
||||
|
||||
var minutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||
var minutes = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||
if (action is PunishmentAction.TimeOut && minutes < 1)
|
||||
minutes = 1;
|
||||
|
||||
@@ -53,7 +53,7 @@ public partial class Administration
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover] IRole role)
|
||||
public async Task AntiAlt(ParsedTimespan minAge, PunishmentAction action, [Leftover] IRole role)
|
||||
{
|
||||
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
||||
|
||||
@@ -86,8 +86,8 @@ public partial class Administration
|
||||
int userThreshold,
|
||||
int seconds,
|
||||
PunishmentAction action,
|
||||
[Leftover] StoopidTime punishTime)
|
||||
=> InternalAntiRaid(userThreshold, seconds, action, punishTime);
|
||||
[Leftover] ParsedTimespan punishTimespan)
|
||||
=> InternalAntiRaid(userThreshold, seconds, action, punishTimespan);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@@ -100,7 +100,7 @@ public partial class Administration
|
||||
int userThreshold,
|
||||
int seconds = 10,
|
||||
PunishmentAction action = PunishmentAction.Mute,
|
||||
StoopidTime punishTime = null)
|
||||
ParsedTimespan punishTimespan = null)
|
||||
{
|
||||
if (action == PunishmentAction.AddRole)
|
||||
{
|
||||
@@ -120,13 +120,13 @@ public partial class Administration
|
||||
return;
|
||||
}
|
||||
|
||||
if (punishTime is not null)
|
||||
if (punishTimespan is not null)
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
||||
}
|
||||
|
||||
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||
var time = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||
if (time is < 0 or > 60 * 24)
|
||||
return;
|
||||
|
||||
@@ -170,8 +170,8 @@ public partial class Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(1)]
|
||||
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] StoopidTime punishTime)
|
||||
=> InternalAntiSpam(messageCount, action, punishTime);
|
||||
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] ParsedTimespan punishTimespan)
|
||||
=> InternalAntiSpam(messageCount, action, punishTimespan);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@@ -183,19 +183,19 @@ public partial class Administration
|
||||
private async Task InternalAntiSpam(
|
||||
int messageCount,
|
||||
PunishmentAction action,
|
||||
StoopidTime timeData = null,
|
||||
ParsedTimespan timespanData = null,
|
||||
IRole role = null)
|
||||
{
|
||||
if (messageCount is < 2 or > 10)
|
||||
return;
|
||||
|
||||
if (timeData is not null)
|
||||
if (timespanData is not null)
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
||||
}
|
||||
|
||||
var time = (int?)timeData?.Time.TotalMinutes ?? 0;
|
||||
var time = (int?)timespanData?.Time.TotalMinutes ?? 0;
|
||||
if (time is < 0 or > 60 * 24)
|
||||
return;
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#nullable disable
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using NadekoBot.Common.TypeReaders.Models;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = SixLabors.ImageSharp.Color;
|
||||
|
||||
@@ -13,13 +15,18 @@ public partial class Administration
|
||||
Excl
|
||||
}
|
||||
|
||||
private readonly TempRoleService _tempRoleService;
|
||||
private readonly IServiceProvider _services;
|
||||
private StickyRolesService _stickyRoleSvc;
|
||||
|
||||
public RoleCommands(IServiceProvider services, StickyRolesService stickyRoleSvc)
|
||||
public RoleCommands(
|
||||
IServiceProvider services,
|
||||
StickyRolesService stickyRoleSvc,
|
||||
TempRoleService tempRoleService)
|
||||
{
|
||||
_services = services;
|
||||
_stickyRoleSvc = stickyRoleSvc;
|
||||
_tempRoleService = tempRoleService;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -34,13 +41,16 @@ public partial class Administration
|
||||
return;
|
||||
try
|
||||
{
|
||||
await targetUser.AddRoleAsync(roleToAdd, new RequestOptions()
|
||||
{
|
||||
AuditLogReason = $"Added by [{ctx.User.Username}]"
|
||||
});
|
||||
await targetUser.AddRoleAsync(roleToAdd,
|
||||
new RequestOptions()
|
||||
{
|
||||
AuditLogReason = $"Added by [{ctx.User.Username}]"
|
||||
});
|
||||
|
||||
await Response().Confirm(strs.setrole(Format.Bold(roleToAdd.Name),
|
||||
Format.Bold(targetUser.ToString()))).SendAsync();
|
||||
await Response()
|
||||
.Confirm(strs.setrole(Format.Bold(roleToAdd.Name),
|
||||
Format.Bold(targetUser.ToString())))
|
||||
.SendAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -62,8 +72,10 @@ public partial class Administration
|
||||
try
|
||||
{
|
||||
await targetUser.RemoveRoleAsync(roleToRemove);
|
||||
await Response().Confirm(strs.remrole(Format.Bold(roleToRemove.Name),
|
||||
Format.Bold(targetUser.ToString()))).SendAsync();
|
||||
await Response()
|
||||
.Confirm(strs.remrole(Format.Bold(roleToRemove.Name),
|
||||
Format.Bold(targetUser.ToString())))
|
||||
.SendAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -204,5 +216,29 @@ public partial class Administration
|
||||
await Response().Confirm(strs.sticky_roles_disabled).SendAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task TempRole(ParsedTimespan timespan, IUser user, [Leftover] IRole role)
|
||||
{
|
||||
if (!await CheckRoleHierarchy(role))
|
||||
{
|
||||
await Response()
|
||||
.Error(strs.hierarchy)
|
||||
.SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _tempRoleService.AddTempRoleAsync(ctx.Guild.Id, role.Id, user.Id, timespan.Time);
|
||||
|
||||
|
||||
await Response()
|
||||
.Confirm(strs.temp_role_added(user.Mention,
|
||||
Format.Bold(role.Name),
|
||||
TimestampTag.FromDateTime(DateTime.UtcNow.Add(timespan.Time), TimestampTagStyles.Relative)))
|
||||
.SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
140
src/NadekoBot/Modules/Administration/Role/TempRoleService.cs
Normal file
140
src/NadekoBot/Modules/Administration/Role/TempRoleService.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public class TempRoleService : IReadyExecutor, INService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCreds _creds;
|
||||
|
||||
private TaskCompletionSource<bool> _tcs = new();
|
||||
|
||||
public TempRoleService(
|
||||
DbService db,
|
||||
DiscordSocketClient client,
|
||||
IBotCreds creds)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public async Task AddTempRoleAsync(
|
||||
ulong guildId,
|
||||
ulong roleId,
|
||||
ulong userId,
|
||||
TimeSpan duration)
|
||||
{
|
||||
if (duration == TimeSpan.Zero)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
await uow.GetTable<TempRole>()
|
||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||
.DeleteAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var until = DateTime.UtcNow.Add(duration);
|
||||
await using var ctx = _db.GetDbContext();
|
||||
await ctx.GetTable<TempRole>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
RoleId = roleId,
|
||||
UserId = userId,
|
||||
Remove = false,
|
||||
ExpiresAt = until
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
ExpiresAt = until,
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
UserId = userId,
|
||||
RoleId = roleId
|
||||
});
|
||||
|
||||
_tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
_tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var latest = await _db.GetDbContext()
|
||||
.GetTable<TempRole>()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
||||
_creds.TotalShards,
|
||||
_client.ShardId))
|
||||
.OrderBy(x => x.ExpiresAt)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
if (latest == default)
|
||||
{
|
||||
await _tcs.Task;
|
||||
continue;
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
if (latest.ExpiresAt > now)
|
||||
{
|
||||
await Task.WhenAny(Task.Delay(latest.ExpiresAt - now), _tcs.Task);
|
||||
continue;
|
||||
}
|
||||
|
||||
var deleted = await _db.GetDbContext()
|
||||
.GetTable<TempRole>()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
||||
_creds.TotalShards,
|
||||
_client.ShardId)
|
||||
&& x.ExpiresAt <= now)
|
||||
.DeleteWithOutputAsync();
|
||||
|
||||
foreach (var d in deleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RemoveRole(d);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Log.Warning("Unable to remove temp role {RoleId} from user {UserId}",
|
||||
d.RoleId,
|
||||
d.UserId);
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Unexpected error occurred in temprole loop");
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoveRole(TempRole tempRole)
|
||||
{
|
||||
var guild = _client.GetGuild(tempRole.GuildId);
|
||||
|
||||
var role = guild?.GetRole(tempRole.RoleId);
|
||||
if (role is null)
|
||||
return;
|
||||
|
||||
var user = guild?.GetUser(tempRole.UserId);
|
||||
if (user is null)
|
||||
return;
|
||||
|
||||
await user.RemoveRoleAsync(role);
|
||||
}
|
||||
}
|
@@ -313,7 +313,7 @@ public partial class Administration
|
||||
int number,
|
||||
AddRole _,
|
||||
IRole role,
|
||||
StoopidTime time = null)
|
||||
ParsedTimespan timespan = null)
|
||||
{
|
||||
var punish = PunishmentAction.AddRole;
|
||||
|
||||
@@ -324,12 +324,12 @@ public partial class Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
|
||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, timespan, role);
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
if (timespan is null)
|
||||
{
|
||||
await Response()
|
||||
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||
@@ -341,7 +341,7 @@ public partial class Administration
|
||||
await Response()
|
||||
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)))
|
||||
Format.Bold(timespan.Input)))
|
||||
.SendAsync();
|
||||
}
|
||||
}
|
||||
@@ -349,7 +349,7 @@ public partial class Administration
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
|
||||
public async Task WarnPunish(int number, PunishmentAction punish, ParsedTimespan timespan = null)
|
||||
{
|
||||
// this should never happen. Addrole has its own method with higher priority
|
||||
// also disallow warn punishment for getting warned
|
||||
@@ -357,15 +357,15 @@ public partial class Administration
|
||||
return;
|
||||
|
||||
// you must specify the time for timeout
|
||||
if (punish is PunishmentAction.TimeOut && time is null)
|
||||
if (punish is PunishmentAction.TimeOut && timespan is null)
|
||||
return;
|
||||
|
||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, timespan);
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
if (timespan is null)
|
||||
{
|
||||
await Response()
|
||||
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||
@@ -377,7 +377,7 @@ public partial class Administration
|
||||
await Response()
|
||||
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)))
|
||||
Format.Bold(timespan.Input)))
|
||||
.SendAsync();
|
||||
}
|
||||
}
|
||||
@@ -417,17 +417,17 @@ public partial class Administration
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(1)]
|
||||
public Task Ban(StoopidTime time, IUser user, [Leftover] string msg = null)
|
||||
=> Ban(time, user.Id, msg);
|
||||
public Task Ban(ParsedTimespan timespan, IUser user, [Leftover] string msg = null)
|
||||
=> Ban(timespan, user.Id, msg);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(0)]
|
||||
public async Task Ban(StoopidTime time, ulong userId, [Leftover] string msg = null)
|
||||
public async Task Ban(ParsedTimespan timespan, ulong userId, [Leftover] string msg = null)
|
||||
{
|
||||
if (time.Time > TimeSpan.FromDays(49))
|
||||
if (timespan.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
|
||||
var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
||||
@@ -444,7 +444,7 @@ public partial class Administration
|
||||
{
|
||||
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
|
||||
var smartText =
|
||||
await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
|
||||
await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, timespan.Time);
|
||||
if (smartText is not null)
|
||||
await Response().User(guildUser).Text(smartText).SendAsync();
|
||||
}
|
||||
@@ -456,14 +456,14 @@ public partial class Administration
|
||||
|
||||
var user = await ctx.Client.GetUserAsync(userId);
|
||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||
await _mute.TimedBan(ctx.Guild, userId, timespan.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
|
||||
.AddField("ID", userId.ToString(), true)
|
||||
.AddField(GetText(strs.duration),
|
||||
time.Time.ToPrettyStringHm(),
|
||||
timespan.Time.ToPrettyStringHm(),
|
||||
true);
|
||||
|
||||
if (dmFailed)
|
||||
@@ -601,7 +601,7 @@ public partial class Administration
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(1)]
|
||||
public Task BanMessageTest(StoopidTime duration, [Leftover] string reason = null)
|
||||
public Task BanMessageTest(ParsedTimespan duration, [Leftover] string reason = null)
|
||||
=> InternalBanMessageTest(reason, duration.Time);
|
||||
|
||||
private async Task InternalBanMessageTest(string reason, TimeSpan? duration)
|
||||
@@ -790,7 +790,7 @@ public partial class Administration
|
||||
[UserPerm(GuildPerm.ModerateMembers)]
|
||||
[BotPerm(GuildPerm.ModerateMembers)]
|
||||
[Priority(2)]
|
||||
public async Task Timeout(IUser globalUser, StoopidTime time, [Leftover] string msg = null)
|
||||
public async Task Timeout(IUser globalUser, ParsedTimespan timespan, [Leftover] string msg = null)
|
||||
{
|
||||
var user = await ctx.Guild.GetUserAsync(globalUser.Id);
|
||||
|
||||
@@ -816,7 +816,7 @@ public partial class Administration
|
||||
dmFailed = true;
|
||||
}
|
||||
|
||||
await user.SetTimeOutAsync(time.Time);
|
||||
await user.SetTimeOutAsync(timespan.Time);
|
||||
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
using System.Text;
|
||||
|
||||
namespace NadekoBot.Modules.Games;
|
||||
|
||||
@@ -38,10 +39,72 @@ public partial class Games : NadekoModule<GamesService>
|
||||
return;
|
||||
|
||||
var res = _service.GetEightballResponse(ctx.User.Id, question);
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(ctx.User.ToString())
|
||||
.AddField("❓ " + GetText(strs.question), question)
|
||||
.AddField("🎱 " + GetText(strs._8ball), res)).SendAsync();
|
||||
await Response()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(ctx.User.ToString())
|
||||
.AddField("❓ " + GetText(strs.question), question)
|
||||
.AddField("🎱 " + GetText(strs._8ball), res))
|
||||
.SendAsync();
|
||||
}
|
||||
|
||||
private readonly string[] _numberEmojis = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"];
|
||||
|
||||
[Cmd]
|
||||
public async Task Minesweeper(int numberOfMines = 12)
|
||||
{
|
||||
var boardSizeX = 9;
|
||||
var boardSizeY = 10;
|
||||
|
||||
if (numberOfMines < 1)
|
||||
{
|
||||
numberOfMines = 1;
|
||||
}
|
||||
else if (numberOfMines > boardSizeX * boardSizeY / 2)
|
||||
{
|
||||
numberOfMines = boardSizeX * boardSizeY / 2;
|
||||
}
|
||||
|
||||
var mineIndicies = Enumerable.Range(0, boardSizeX * boardSizeY)
|
||||
.ToArray()
|
||||
.Shuffle()
|
||||
.Take(numberOfMines)
|
||||
.ToHashSet();
|
||||
|
||||
string GetNumberOnCell(int x, int y)
|
||||
{
|
||||
var count = 0;
|
||||
for (var i = -1; i < 2; i++)
|
||||
{
|
||||
for (var j = -1; j < 2; j++)
|
||||
{
|
||||
if (y + j >= boardSizeY || y + j < 0)
|
||||
continue;
|
||||
if (x + i >= boardSizeX || x + i < 0)
|
||||
continue;
|
||||
|
||||
var boardIndex = (y + j) * boardSizeX + (x + i);
|
||||
if (mineIndicies.Contains(boardIndex))
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return _numberEmojis[count];
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"### Minesweeper [{numberOfMines}\\💣]");
|
||||
for (var i = 0; i < boardSizeY; i++)
|
||||
{
|
||||
for (var j = 0; j < boardSizeX; j++)
|
||||
{
|
||||
var emoji = mineIndicies.Contains((i * boardSizeX) + j) ? "💣" : GetNumberOnCell(j, i);
|
||||
sb.Append($"||{emoji}||");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
await Response().Text(sb.ToString()).SendAsync();
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
private readonly MessageSenderService _mss;
|
||||
|
||||
private static readonly TimeSpan _maxAfkDuration = 8.Hours();
|
||||
|
||||
public AfkService(IBotCache cache, DiscordSocketClient client, MessageSenderService mss)
|
||||
{
|
||||
_cache = cache;
|
||||
@@ -19,6 +20,9 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
private static TypedKey<string> GetKey(ulong userId)
|
||||
=> new($"afk:msg:{userId}");
|
||||
|
||||
private static TypedKey<bool> GetRecentlySentKey(ulong userId, ulong channelId)
|
||||
=> new($"afk:recent:{userId}:{channelId}");
|
||||
|
||||
public async Task<bool> SetAfkAsync(ulong userId, string text)
|
||||
{
|
||||
var added = await _cache.AddAsync(GetKey(userId), text, _maxAfkDuration, overwrite: true);
|
||||
@@ -43,9 +47,7 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
msg.DeleteAfter(5);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -61,7 +63,7 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
await Task.Delay(_maxAfkDuration);
|
||||
_client.MessageReceived -= StopAfk;
|
||||
});
|
||||
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
@@ -72,36 +74,29 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task TryTriggerAfkMessage(SocketMessage arg)
|
||||
private Task TryTriggerAfkMessage(SocketMessage sm)
|
||||
{
|
||||
if (arg.Author.IsBot || arg.Author.IsWebhook)
|
||||
if (sm.Author.IsBot || sm.Author.IsWebhook)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (arg is not IUserMessage uMsg || uMsg.Channel is not ITextChannel tc)
|
||||
if (sm is not IUserMessage uMsg || uMsg.Channel is not ITextChannel tc)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if ((arg.MentionedUsers.Count is 0 or > 3) && uMsg.ReferencedMessage is null)
|
||||
|
||||
if ((sm.MentionedUsers.Count is 0 or > 3) && uMsg.ReferencedMessage is null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var botUser = await tc.Guild.GetCurrentUserAsync();
|
||||
|
||||
var perms = botUser.GetPermissions(tc);
|
||||
|
||||
if (!perms.SendMessages)
|
||||
return;
|
||||
|
||||
ulong mentionedUserId = 0;
|
||||
|
||||
if (arg.MentionedUsers.Count <= 3)
|
||||
if (sm.MentionedUsers.Count <= 3)
|
||||
{
|
||||
foreach (var uid in uMsg.MentionedUserIds)
|
||||
{
|
||||
if (uid == arg.Author.Id)
|
||||
if (uid == sm.Author.Id)
|
||||
continue;
|
||||
|
||||
if (arg.Content.StartsWith($"<@{uid}>") || arg.Content.StartsWith($"<@!{uid}>"))
|
||||
if (sm.Content.StartsWith($"<@{uid}>") || sm.Content.StartsWith($"<@!{uid}>"))
|
||||
{
|
||||
mentionedUserId = uid;
|
||||
break;
|
||||
@@ -115,26 +110,46 @@ public sealed class AfkService : INService, IReadyExecutor
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mentionedUserId = repliedUserId;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _cache.GetAsync(GetKey(mentionedUserId));
|
||||
if (result.TryPickT0(out var msg, out _))
|
||||
{
|
||||
var st = SmartText.CreateFrom(msg);
|
||||
|
||||
|
||||
st = $"The user you've pinged (<#{mentionedUserId}>) is AFK: " + st;
|
||||
|
||||
var toDelete = await _mss.Response(arg.Channel)
|
||||
.User(arg.Author)
|
||||
|
||||
var toDelete = await _mss.Response(sm.Channel)
|
||||
.User(sm.Author)
|
||||
.Message(uMsg)
|
||||
.Text(st)
|
||||
.SendAsync();
|
||||
|
||||
toDelete.DeleteAfter(30);
|
||||
|
||||
var botUser = await tc.Guild.GetCurrentUserAsync();
|
||||
var perms = botUser.GetPermissions(tc);
|
||||
if (!perms.SendMessages)
|
||||
return;
|
||||
|
||||
var key = GetRecentlySentKey(mentionedUserId, sm.Channel.Id);
|
||||
var recent = await _cache.GetAsync(key);
|
||||
|
||||
if (!recent.TryPickT0(out _, out _))
|
||||
{
|
||||
var chMsg = await _mss.Response(sm.Channel)
|
||||
.Message(uMsg)
|
||||
.Pending(strs.user_afk($"<@{mentionedUserId}>"))
|
||||
.SendAsync();
|
||||
|
||||
chMsg.DeleteAfter(5);
|
||||
await _cache.AddAsync(key, true, expiry: TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
|
@@ -98,10 +98,10 @@ public partial class Utility
|
||||
return;
|
||||
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(guildId is not null
|
||||
? strs.reminder_server_list
|
||||
: strs.reminder_list));
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(guildId is not null
|
||||
? strs.reminder_server_list
|
||||
: strs.reminder_list));
|
||||
|
||||
List<Reminder> rems;
|
||||
if (guildId is { } gid)
|
||||
@@ -193,23 +193,14 @@ public partial class Utility
|
||||
message = message.SanitizeAllMentions();
|
||||
}
|
||||
|
||||
var rem = new Reminder
|
||||
{
|
||||
ChannelId = targetId,
|
||||
IsPrivate = isPrivate,
|
||||
When = time,
|
||||
Message = message,
|
||||
UserId = ctx.User.Id,
|
||||
ServerId = ctx.Guild?.Id ?? 0
|
||||
};
|
||||
await _service.AddReminderAsync(ctx.User.Id,
|
||||
targetId,
|
||||
ctx.Guild?.Id,
|
||||
isPrivate,
|
||||
time,
|
||||
message,
|
||||
ReminderType.User);
|
||||
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
uow.Set<Reminder>().Add(rem);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// var gTime = ctx.Guild is null ? time : TimeZoneInfo.ConvertTime(time, _tz.GetTimeZoneOrUtc(ctx.Guild.Id));
|
||||
await Response()
|
||||
.Confirm($"\u23f0 {GetText(strs.remind2(
|
||||
Format.Bold(!isPrivate ? $"<#{targetId}>" : ctx.User.Username),
|
||||
|
@@ -21,6 +21,8 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
private readonly IMessageSenderService _sender;
|
||||
private readonly CultureInfo _culture;
|
||||
|
||||
private TaskCompletionSource<bool> _tcs;
|
||||
|
||||
public RemindService(
|
||||
DiscordSocketClient client,
|
||||
DbService db,
|
||||
@@ -44,8 +46,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(15));
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
while (true)
|
||||
{
|
||||
await OnReminderLoopTickInternalAsync();
|
||||
}
|
||||
@@ -55,8 +56,7 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
{
|
||||
try
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var reminders = await GetRemindersBeforeAsync(now);
|
||||
var reminders = await GetRemindersBeforeAsync();
|
||||
if (reminders.Count == 0)
|
||||
return;
|
||||
|
||||
@@ -67,7 +67,6 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
{
|
||||
var executedReminders = group.ToList();
|
||||
await executedReminders.Select(ReminderTimerAction).WhenAll();
|
||||
await RemoveReminders(executedReminders.Select(x => x.Id));
|
||||
await Task.Delay(1500);
|
||||
}
|
||||
}
|
||||
@@ -80,21 +79,51 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
private async Task RemoveReminders(IEnumerable<int> reminders)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
await uow.Set<Reminder>()
|
||||
.ToLinqToDBTable()
|
||||
await uow.GetTable<Reminder>()
|
||||
.DeleteAsync(x => reminders.Contains(x.Id));
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task<List<Reminder>> GetRemindersBeforeAsync(DateTime now)
|
||||
private async Task<IReadOnlyList<Reminder>> GetRemindersBeforeAsync()
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
return await uow.Set<Reminder>()
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId, _creds.TotalShards, _client.ShardId)
|
||||
&& x.When < now)
|
||||
.ToListAsyncLinqToDB();
|
||||
while (true)
|
||||
{
|
||||
_tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
await using var uow = _db.GetDbContext();
|
||||
var earliest = await uow.Set<Reminder>()
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId,
|
||||
_creds.TotalShards,
|
||||
_client.ShardId))
|
||||
.OrderBy(x => x.When)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
if (earliest == default)
|
||||
{
|
||||
await _tcs.Task;
|
||||
continue;
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
if (earliest.When > now)
|
||||
{
|
||||
var diff = earliest.When - now;
|
||||
// Log.Information("Waiting for {Diff}", diff);
|
||||
await Task.WhenAny(Task.Delay(diff), _tcs.Task);
|
||||
continue;
|
||||
}
|
||||
|
||||
var reminders = await uow.Set<Reminder>()
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.ServerId,
|
||||
_creds.TotalShards,
|
||||
_client.ShardId))
|
||||
.Where(x => x.When <= now)
|
||||
.DeleteWithOutputAsync();
|
||||
|
||||
return reminders;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryParseRemindMessage(string input, out RemindObject obj)
|
||||
@@ -243,21 +272,24 @@ public class RemindService : INService, IReadyExecutor, IRemindService
|
||||
string message,
|
||||
ReminderType reminderType)
|
||||
{
|
||||
var rem = new Reminder
|
||||
await using (var ctx = _db.GetDbContext())
|
||||
{
|
||||
UserId = userId,
|
||||
ChannelId = targetId,
|
||||
ServerId = guildId ?? 0,
|
||||
IsPrivate = isPrivate,
|
||||
When = time,
|
||||
Message = message,
|
||||
Type = reminderType
|
||||
};
|
||||
await ctx.GetTable<Reminder>()
|
||||
.InsertAsync(() => new Reminder
|
||||
{
|
||||
UserId = userId,
|
||||
ChannelId = targetId,
|
||||
ServerId = guildId ?? 0,
|
||||
IsPrivate = isPrivate,
|
||||
When = time,
|
||||
Message = message,
|
||||
Type = reminderType,
|
||||
DateAdded = DateTime.UtcNow
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await using var ctx = _db.GetDbContext();
|
||||
await ctx.Set<Reminder>()
|
||||
.AddAsync(rem);
|
||||
await ctx.SaveChangesAsync();
|
||||
_tcs.SetResult(true);
|
||||
}
|
||||
|
||||
public async Task<List<Reminder>> GetServerReminders(int page, ulong guildId)
|
||||
|
@@ -110,14 +110,14 @@ public partial class Utility
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Repeat(StoopidTime interval, [Leftover] string message)
|
||||
public Task Repeat(ParsedTimespan interval, [Leftover] string message)
|
||||
=> Repeat(ctx.Channel, null, interval, message);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Repeat(ITextChannel channel, StoopidTime interval, [Leftover] string message)
|
||||
public Task Repeat(ITextChannel channel, ParsedTimespan interval, [Leftover] string message)
|
||||
=> Repeat(channel, null, interval, message);
|
||||
|
||||
[Cmd]
|
||||
@@ -138,14 +138,14 @@ public partial class Utility
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(2)]
|
||||
public Task Repeat(GuildDateTime? timeOfDay, StoopidTime? interval, [Leftover] string message)
|
||||
public Task Repeat(GuildDateTime? timeOfDay, ParsedTimespan? interval, [Leftover] string message)
|
||||
=> Repeat(ctx.Channel, timeOfDay, interval, message);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(3)]
|
||||
public async Task Repeat(IMessageChannel channel, GuildDateTime? timeOfDay, StoopidTime? interval,
|
||||
public async Task Repeat(IMessageChannel channel, GuildDateTime? timeOfDay, ParsedTimespan? interval,
|
||||
[Leftover] string message)
|
||||
{
|
||||
if (channel is not ITextChannel txtCh || txtCh.GuildId != ctx.Guild.Id)
|
||||
|
@@ -56,25 +56,18 @@ public partial class Xp : NadekoModule<XpService>
|
||||
public async Task XpNotify()
|
||||
{
|
||||
var globalSetting = _service.GetNotificationType(ctx.User);
|
||||
var serverSetting = _service.GetNotificationType(ctx.User.Id, ctx.Guild.Id);
|
||||
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField(GetText(strs.xpn_setting_global), GetNotifLocationString(globalSetting))
|
||||
.AddField(GetText(strs.xpn_setting_server), GetNotifLocationString(serverSetting));
|
||||
.AddField(GetText(strs.xpn_setting_global), GetNotifLocationString(globalSetting));
|
||||
|
||||
await Response().Embed(embed).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task XpNotify(NotifyPlace place, XpNotificationLocation type)
|
||||
public async Task XpNotify(XpNotificationLocation type)
|
||||
{
|
||||
if (place == NotifyPlace.Guild)
|
||||
await _service.ChangeNotificationType(ctx.User.Id, ctx.Guild.Id, type);
|
||||
else
|
||||
await _service.ChangeNotificationType(ctx.User, type);
|
||||
|
||||
await _service.ChangeNotificationType(ctx.User, type);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
|
@@ -159,14 +159,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MiniGuildXpStats
|
||||
{
|
||||
public long Xp { get; set; }
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
}
|
||||
|
||||
private async Task UpdateXp()
|
||||
{
|
||||
try
|
||||
@@ -261,7 +253,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
GuildId = guildId,
|
||||
Xp = group.Key,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
NotifyOnLevelUp = XpNotificationLocation.None
|
||||
},
|
||||
_ => new()
|
||||
{
|
||||
@@ -320,8 +311,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
du.UserId,
|
||||
true,
|
||||
oldLevel.Level,
|
||||
newLevel.Level,
|
||||
du.NotifyOnLevelUp));
|
||||
newLevel.Level));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +329,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
bool isServer,
|
||||
long oldLevel,
|
||||
long newLevel,
|
||||
XpNotificationLocation notifyLoc)
|
||||
XpNotificationLocation notifyLoc = XpNotificationLocation.None)
|
||||
=> async () =>
|
||||
{
|
||||
if (isServer)
|
||||
@@ -634,21 +624,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
.ToArrayAsyncLinqToDB();
|
||||
}
|
||||
|
||||
public async Task ChangeNotificationType(ulong userId, ulong guildId, XpNotificationLocation type)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var user = uow.GetOrCreateUserXpStats(guildId, userId);
|
||||
user.NotifyOnLevelUp = type;
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public XpNotificationLocation GetNotificationType(ulong userId, ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var user = uow.GetOrCreateUserXpStats(guildId, userId);
|
||||
return user.NotifyOnLevelUp;
|
||||
}
|
||||
|
||||
public XpNotificationLocation GetNotificationType(IUser user)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
@@ -1667,9 +1642,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
{
|
||||
GuildId = guildId,
|
||||
UserId = userId,
|
||||
AwardedXp = 0,
|
||||
Xp = lvlStats.TotalXp,
|
||||
NotifyOnLevelUp = XpNotificationLocation.None,
|
||||
DateAdded = DateTime.UtcNow
|
||||
}, (old) => new()
|
||||
{
|
||||
|
Reference in New Issue
Block a user