Applied codestyle to all .cs files

This commit is contained in:
Kwoth
2021-12-29 06:07:16 +01:00
parent 723447c7d4
commit 82000c97a4
543 changed files with 13221 additions and 14059 deletions

View File

@@ -6,10 +6,13 @@ namespace NadekoBot.Modules.Administration;
public partial class Administration : NadekoModule<AdministrationService>
{
private readonly ImageOnlyChannelService _imageOnly;
public Administration(ImageOnlyChannelService imageOnly)
=> _imageOnly = imageOnly;
public enum Channel
{
Channel,
Ch,
Chnl,
Chan
}
public enum List
{
@@ -17,7 +20,25 @@ public partial class Administration : NadekoModule<AdministrationService>
Ls = 0
}
[NadekoCommand, Aliases]
public enum Server
{
Server
}
public enum State
{
Enable,
Disable,
Inherit
}
private readonly ImageOnlyChannelService _imageOnly;
public Administration(ImageOnlyChannelService imageOnly)
=> _imageOnly = imageOnly;
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.Administrator)]
@@ -30,7 +51,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyPendingLocalizedAsync(strs.imageonly_disable);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageChannels)]
[BotPerm(ChannelPerm.ManageChannels)]
@@ -39,9 +61,9 @@ public partial class Administration : NadekoModule<AdministrationService>
var seconds = (int?)time?.Time.TotalSeconds ?? 0;
if (time is not null && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
return;
await ((ITextChannel) ctx.Channel).ModifyAsync(tcp =>
await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
{
tcp.SlowModeInterval = seconds;
});
@@ -49,26 +71,26 @@ public partial class Administration : NadekoModule<AdministrationService>
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageMessages)]
[Priority(2)]
public async Task Delmsgoncmd(List _)
{
var guild = (SocketGuild) ctx.Guild;
var guild = (SocketGuild)ctx.Guild;
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
var embed = _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.server_delmsgoncmd))
.WithDescription(enabled ? "✅" : "❌");
.WithOkColor()
.WithTitle(GetText(strs.server_delmsgoncmd))
.WithDescription(enabled ? "✅" : "❌");
var str = string.Join("\n", channels
.Select(x =>
var str = string.Join("\n",
channels.Select(x =>
{
var ch = guild.GetChannel(x.ChannelId)?.ToString()
?? x.ChannelId.ToString();
var ch = guild.GetChannel(x.ChannelId)?.ToString() ?? x.ChannelId.ToString();
var prefix = x.State ? "✅ " : "❌ ";
return prefix + ch;
}));
@@ -81,12 +103,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ctx.Channel.EmbedAsync(embed);
}
public enum Server
{
Server
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageMessages)]
@@ -105,22 +123,8 @@ public partial class Administration : NadekoModule<AdministrationService>
}
}
public enum Channel
{
Channel,
Ch,
Chnl,
Chan
}
public enum State
{
Enable,
Disable,
Inherit
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageMessages)]
@@ -128,7 +132,8 @@ public partial class Administration : NadekoModule<AdministrationService>
public Task Delmsgoncmd(Channel _, State s, ITextChannel ch)
=> Delmsgoncmd(_, s, ch.Id);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageMessages)]
@@ -139,20 +144,15 @@ public partial class Administration : NadekoModule<AdministrationService>
await _service.SetDelMsgOnCmdState(ctx.Guild.Id, actualChId, s);
if (s == State.Disable)
{
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_off);
}
else if (s == State.Enable)
{
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_on);
}
else
{
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_inherit);
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.DeafenMembers)]
[BotPerm(GuildPerm.DeafenMembers)]
@@ -162,7 +162,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.deafen);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.DeafenMembers)]
[BotPerm(GuildPerm.DeafenMembers)]
@@ -172,7 +173,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.undeafen);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
@@ -182,7 +184,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.delvoich(Format.Bold(voiceChannel.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
@@ -192,7 +195,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.createvoich(Format.Bold(ch.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
@@ -202,7 +206,8 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.deltextchan(Format.Bold(toDelete.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
@@ -212,36 +217,39 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.createtextchan(Format.Bold(txtCh.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
public async Task SetTopic([Leftover] string topic = null)
{
var channel = (ITextChannel) ctx.Channel;
var channel = (ITextChannel)ctx.Channel;
topic ??= "";
await channel.ModifyAsync(c => c.Topic = topic);
await ReplyConfirmLocalizedAsync(strs.set_topic);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
public async Task SetChanlName([Leftover] string name)
{
var channel = (ITextChannel) ctx.Channel;
var channel = (ITextChannel)ctx.Channel;
await channel.ModifyAsync(c => c.Name = name);
await ReplyConfirmLocalizedAsync(strs.set_channel_name);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageChannels)]
[BotPerm(GuildPerm.ManageChannels)]
public async Task NsfwToggle()
{
var channel = (ITextChannel) ctx.Channel;
var channel = (ITextChannel)ctx.Channel;
var isEnabled = channel.IsNsfw;
await channel.ModifyAsync(c => c.IsNsfw = !isEnabled);
@@ -252,20 +260,22 @@ public partial class Administration : NadekoModule<AdministrationService>
await ReplyConfirmLocalizedAsync(strs.nsfw_set_true);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageMessages)]
[Priority(0)]
public Task Edit(ulong messageId, [Leftover] string text)
=> Edit((ITextChannel) ctx.Channel, messageId, text);
=> Edit((ITextChannel)ctx.Channel, messageId, text);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task Edit(ITextChannel channel, ulong messageId, [Leftover] string text)
{
var userPerms = ((SocketGuildUser) ctx.User).GetPermissions(channel);
var botPerms = ((SocketGuild) ctx.Guild).CurrentUser.GetPermissions(channel);
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
if (!userPerms.Has(ChannelPermission.ManageMessages))
{
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
@@ -281,23 +291,28 @@ public partial class Administration : NadekoModule<AdministrationService>
await _service.EditMessage(ctx, channel, messageId, text);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageMessages)]
[BotPerm(ChannelPerm.ManageMessages)]
public Task Delete(ulong messageId, StoopidTime time = null)
=> Delete((ITextChannel) ctx.Channel, messageId, time);
=> Delete((ITextChannel)ctx.Channel, messageId, time);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Delete(ITextChannel channel, ulong messageId, StoopidTime time = null)
=> await InternalMessageAction(channel, messageId, time, msg => msg.DeleteAsync());
private async Task InternalMessageAction(ITextChannel channel, ulong messageId, StoopidTime time,
private async Task InternalMessageAction(
ITextChannel channel,
ulong messageId,
StoopidTime time,
Func<IMessage, Task> func)
{
var userPerms = ((SocketGuildUser) ctx.User).GetPermissions(channel);
var botPerms = ((SocketGuild) ctx.Guild).CurrentUser.GetPermissions(channel);
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
if (!userPerms.Has(ChannelPermission.ManageMessages))
{
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
@@ -338,4 +353,4 @@ public partial class Administration : NadekoModule<AdministrationService>
await ctx.OkAsync();
}
}
}

View File

@@ -8,13 +8,14 @@ public partial class Administration
[Group]
public class AutoAssignRoleCommands : NadekoSubmodule<AutoAssignRoleService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task AutoAssignRole([Leftover] IRole role)
{
var guser = (IGuildUser) ctx.User;
var guser = (IGuildUser)ctx.User;
if (role.Id == ctx.Guild.EveryoneRole.Id)
return;
@@ -27,20 +28,15 @@ public partial class Administration
var roles = await _service.ToggleAarAsync(ctx.Guild.Id, role.Id);
if (roles.Count == 0)
{
await ReplyConfirmLocalizedAsync(strs.aar_disabled);
}
else if (roles.Contains(role.Id))
{
await AutoAssignRole();
}
else
{
await ReplyConfirmLocalizedAsync(strs.aar_role_removed(Format.Bold(role.ToString())));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -51,18 +47,14 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.aar_none);
return;
}
var existing = roles.Select(rid => ctx.Guild.GetRole(rid)).Where(r => r is not null)
.ToList();
var existing = roles.Select(rid => ctx.Guild.GetRole(rid)).Where(r => r is not null).ToList();
if (existing.Count != roles.Count)
{
await _service.SetAarRolesAsync(ctx.Guild.Id, existing.Select(x => x.Id));
}
await ReplyConfirmLocalizedAsync(strs.aar_roles(
'\n' + existing.Select(x => Format.Bold(x.ToString()))
.Join(",\n")));
'\n' + existing.Select(x => Format.Bold(x.ToString())).Join(",\n")));
}
}
}
}

View File

@@ -18,4 +18,4 @@ public enum LogType
VoicePresence,
VoicePresenceTTS,
UserMuted
}
}

View File

@@ -25,18 +25,28 @@ public class AntiSpamStats
public class AntiAltStats
{
public PunishmentAction Action
=> _setting.Action;
public int ActionDurationMinutes
=> _setting.ActionDurationMinutes;
public ulong? RoleId
=> _setting.RoleId;
public TimeSpan MinAge
=> _setting.MinAge;
public int Counter
=> _counter;
private readonly AntiAltSetting _setting;
public PunishmentAction Action => _setting.Action;
public int ActionDurationMinutes => _setting.ActionDurationMinutes;
public ulong? RoleId => _setting.RoleId;
public TimeSpan MinAge => _setting.MinAge;
private int _counter = 0;
public int Counter => _counter;
private int _counter;
public AntiAltStats(AntiAltSetting setting)
=> _setting = setting;
public void Increment() => Interlocked.Increment(ref _counter);
}
public void Increment()
=> Interlocked.Increment(ref _counter);
}

View File

@@ -10,4 +10,4 @@ public class PunishQueueItem
public int MuteTime { get; set; }
public ulong? RoleId { get; set; }
public IGuildUser User { get; set; }
}
}

View File

@@ -3,11 +3,15 @@ namespace NadekoBot.Modules.Administration.Common;
public sealed class UserSpamStats : IDisposable
{
public int Count => timers.Count;
public int Count
=> timers.Count;
public string LastMessage { get; set; }
private ConcurrentQueue<Timer> timers { get; }
private readonly object applyLock = new();
public UserSpamStats(IUserMessage msg)
{
LastMessage = msg.Content.ToUpperInvariant();
@@ -16,7 +20,6 @@ public sealed class UserSpamStats : IDisposable
ApplyNextMessage(msg);
}
private readonly object applyLock = new();
public void ApplyNextMessage(IUserMessage message)
{
lock (applyLock)
@@ -28,10 +31,15 @@ public sealed class UserSpamStats : IDisposable
while (timers.TryDequeue(out var old))
old.Change(Timeout.Infinite, Timeout.Infinite);
}
var t = new Timer(_ => {
if (timers.TryDequeue(out var old))
old.Change(Timeout.Infinite, Timeout.Infinite);
}, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
var t = new Timer(_ =>
{
if (timers.TryDequeue(out var old))
old.Change(Timeout.Infinite, Timeout.Infinite);
},
null,
TimeSpan.FromMinutes(30),
TimeSpan.FromMinutes(30));
timers.Enqueue(t);
}
}
@@ -41,4 +49,4 @@ public sealed class UserSpamStats : IDisposable
while (timers.TryDequeue(out var old))
old.Change(Timeout.Infinite, Timeout.Infinite);
}
}
}

View File

@@ -10,20 +10,17 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly]
public class DangerousCommands : NadekoSubmodule<DangerousCommandsService>
{
private async Task InternalExecSql(string sql, params object[] reps)
{
sql = string.Format(sql, reps);
try
{
var embed = _eb.Create()
.WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql));
.WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql));
if (!await PromptUserConfirmAsync(embed))
{
return;
}
var res = await _service.ExecuteSql(sql);
await SendConfirmAsync(res.ToString());
@@ -34,87 +31,91 @@ namespace NadekoBot.Modules.Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task SqlSelect([Leftover]string sql)
public Task SqlSelect([Leftover] string sql)
{
var result = _service.SelectSql(sql);
return ctx.SendPaginatedConfirmAsync(0, cur =>
{
var items = result.Results.Skip(cur * 20).Take(20);
if (!items.Any())
return ctx.SendPaginatedConfirmAsync(0,
cur =>
{
var items = result.Results.Skip(cur * 20).Take(20).ToList();
if (!items.Any())
return _eb.Create().WithErrorColor().WithFooter(sql).WithDescription("-");
return _eb.Create()
.WithErrorColor()
.WithFooter(sql)
.WithDescription("-");
}
return _eb.Create()
.WithOkColor()
.WithFooter(sql)
.WithTitle(string.Join(" ║ ", result.ColumnNames))
.WithDescription(string.Join('\n', items.Select(x => string.Join(" ║ ", x))));
}, result.Results.Count, 20);
.WithOkColor()
.WithFooter(sql)
.WithTitle(string.Join(" ║ ", result.ColumnNames))
.WithDescription(string.Join('\n', items.Select(x => string.Join(" ║ ", x))));
},
result.Results.Count,
20);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task SqlExec([Leftover]string sql) =>
InternalExecSql(sql);
public Task SqlExec([Leftover] string sql)
=> InternalExecSql(sql);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeleteWaifus() =>
SqlExec(DangerousCommandsService.WaifusDeleteSql);
public Task DeleteWaifus()
=> SqlExec(DangerousCommandsService.WaifusDeleteSql);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeleteWaifu(IUser user) =>
DeleteWaifu(user.Id);
public Task DeleteWaifu(IUser user)
=> DeleteWaifu(user.Id);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeleteWaifu(ulong userId) =>
InternalExecSql(DangerousCommandsService.WaifuDeleteSql, userId);
public Task DeleteWaifu(ulong userId)
=> InternalExecSql(DangerousCommandsService.WaifuDeleteSql, userId);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeleteCurrency() =>
SqlExec(DangerousCommandsService.CurrencyDeleteSql);
public Task DeleteCurrency()
=> SqlExec(DangerousCommandsService.CurrencyDeleteSql);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeletePlaylists() =>
SqlExec(DangerousCommandsService.MusicPlaylistDeleteSql);
public Task DeletePlaylists()
=> SqlExec(DangerousCommandsService.MusicPlaylistDeleteSql);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task DeleteXp() =>
SqlExec(DangerousCommandsService.XpDeleteSql);
public Task DeleteXp()
=> SqlExec(DangerousCommandsService.XpDeleteSql);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task PurgeUser(ulong userId)
{
var embed = _eb.Create()
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
if (!await PromptUserConfirmAsync(embed)) return;
if (!await PromptUserConfirmAsync(embed))
{
return;
}
await _service.PurgeUserAsync(userId);
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task PurgeUser([Leftover]IUser user)
public Task PurgeUser([Leftover] IUser user)
=> PurgeUser(user.Id);
//[NadekoCommand, Usage, Description, Aliases]
//[OwnerOnly]
@@ -123,4 +124,4 @@ namespace NadekoBot.Modules.Administration
}
}
}
#endif
#endif

View File

@@ -11,7 +11,8 @@ public partial class Administration
{
// override stats, it should require that the user has managessages guild permission
// .po 'stats' add user guild managemessages
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task DiscordPermOverride(CommandOrCrInfo cmd, params GuildPerm[] perms)
@@ -26,20 +27,20 @@ public partial class Administration
var aggregatePerms = perms.Aggregate((acc, seed) => seed | acc);
await _service.AddOverride(ctx.Guild.Id, cmd.Name, aggregatePerms);
await ReplyConfirmLocalizedAsync(strs.perm_override(
Format.Bold(aggregatePerms.ToString()),
await ReplyConfirmLocalizedAsync(strs.perm_override(Format.Bold(aggregatePerms.ToString()),
Format.Code(cmd.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task DiscordPermOverrideReset()
{
var result = await PromptUserConfirmAsync(_eb.Create()
.WithOkColor()
.WithDescription(GetText(strs.perm_override_all_confirm)));
.WithOkColor()
.WithDescription(GetText(strs.perm_override_all_confirm)));
if (!result)
return;
await _service.ClearAllOverrides(ctx.Guild.Id);
@@ -47,35 +48,34 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.perm_override_all);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task DiscordPermOverrideList(int page = 1)
{
if (--page < 0)
return;
var overrides = await _service.GetAllOverrides(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var eb = _eb.Create()
.WithTitle(GetText(strs.perm_overrides))
.WithOkColor();
await ctx.SendPaginatedConfirmAsync(page,
curPage =>
{
var eb = _eb.Create().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
var thisPageOverrides = overrides
.Skip(9 * curPage)
.Take(9)
.ToList();
var thisPageOverrides = overrides.Skip(9 * curPage).Take(9).ToList();
if (thisPageOverrides.Count == 0)
eb.WithDescription(GetText(strs.perm_override_page_none));
else
eb.WithDescription(string.Join("\n",
thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")));
if (thisPageOverrides.Count == 0)
eb.WithDescription(GetText(strs.perm_override_page_none));
else
eb.WithDescription(string.Join("\n",
thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")));
return eb;
}, overrides.Count, 9, true);
return eb;
},
overrides.Count,
9);
}
}
}
}

View File

@@ -8,7 +8,8 @@ public partial class Administration
[Group]
public class GameChannelCommands : NadekoSubmodule<GameVoiceChannelService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.MoveMembers)]
@@ -21,6 +22,7 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.not_in_voice);
return;
}
var id = _service.ToggleGameVoiceChannel(ctx.Guild.Id, vch.Id);
if (id is null)
@@ -34,4 +36,4 @@ public partial class Administration
}
}
}
}
}

View File

@@ -10,7 +10,7 @@ public class GreetGrouper<T>
/// <summary>
/// Creates a group, if group already exists, adds the specified user
/// Creates a group, if group already exists, adds the specified user
/// </summary>
/// <param name="guildId">Id of the server for which to create group for</param>
/// <param name="toAddIfExists">User to add if group already exists</param>
@@ -31,7 +31,7 @@ public class GreetGrouper<T>
}
/// <summary>
/// Remove the specified amount of items from the group. If all items are removed, group will be removed.
/// Remove the specified amount of items from the group. If all items are removed, group will be removed.
/// </summary>
/// <param name="guildId">Id of the group</param>
/// <param name="count">Maximum number of items to retrieve</param>
@@ -54,8 +54,7 @@ public class GreetGrouper<T>
// if there are more in the group than what's needed
// take the requested number, remove them from the set
// and return them
var toReturn = set.TakeWhile(_ => count-- != 0)
.ToList();
var toReturn = set.TakeWhile(_ => count-- != 0).ToList();
foreach (var item in toReturn)
set.Remove(item);
@@ -69,4 +68,4 @@ public class GreetGrouper<T>
return true;
}
}
}
}

View File

@@ -1,4 +1,3 @@
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Services;
@@ -43,4 +42,4 @@ public class GreetSettings
BoostMessageDeleteAfter = g.BoostMessageDeleteAfter,
BoostMessageChannelId = g.BoostMessageChannelId
};
}
}

View File

@@ -1,10 +1,13 @@
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Services;
public class GreetSettingsService : INService
{
public bool GroupGreets
=> _bss.Data.GroupGreets;
private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, GreetSettings> _guildConfigsCache;
@@ -14,9 +17,6 @@ public class GreetSettingsService : INService
private readonly GreetGrouper<IUser> _byes = new();
private readonly BotConfigService _bss;
public bool GroupGreets
=> _bss.Data.GroupGreets;
public GreetSettingsService(
DiscordSocketClient client,
Bot bot,
@@ -42,8 +42,10 @@ public class GreetSettingsService : INService
{
// if user is a new booster
// or boosted again the same server
if ((optOldUser.Value is { PremiumSince: null } && newUser is { PremiumSince: not null }) ||
(optOldUser.Value?.PremiumSince is { } oldDate && newUser.PremiumSince is { } newDate && newDate > oldDate))
if ((optOldUser.Value is { PremiumSince: null } && newUser is { PremiumSince: not null })
|| (optOldUser.Value?.PremiumSince is { } oldDate
&& newUser.PremiumSince is { } newDate
&& newDate > oldDate))
{
var conf = GetOrAddSettingsForGuild(newUser.Guild.Id);
if (!conf.SendBoostMessage) return Task.CompletedTask;
@@ -65,20 +67,12 @@ public class GreetSettingsService : INService
return;
var toSend = SmartText.CreateFrom(conf.BoostMessage);
var rep = new ReplacementBuilder().WithDefault(user,
channel,
user.Guild,
_client
)
.Build();
var rep = new ReplacementBuilder().WithDefault(user, channel, user.Guild, _client).Build();
try
{
var toDelete = await channel.SendAsync(rep.Replace(toSend));
if (conf.BoostMessageDeleteAfter > 0)
{
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
}
if (conf.BoostMessageDeleteAfter > 0) toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
}
catch (Exception ex)
{
@@ -96,76 +90,71 @@ public class GreetSettingsService : INService
{
_guildConfigsCache.AddOrUpdate(gc.GuildId,
GreetSettings.Create(gc),
delegate { return GreetSettings.Create(gc); }
);
delegate { return GreetSettings.Create(gc); });
return Task.CompletedTask;
}
private Task UserLeft(SocketGuild guild, SocketUser user)
{
var _ = Task.Run(async () =>
{
try
{
try
var conf = GetOrAddSettingsForGuild(guild.Id);
if (!conf.SendChannelByeMessage) return;
var channel = guild.TextChannels.FirstOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel is null) //maybe warn the server owner that the channel is missing
return;
if (GroupGreets)
{
var conf = GetOrAddSettingsForGuild(guild.Id);
if (!conf.SendChannelByeMessage) return;
var channel = guild.TextChannels.FirstOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel is null) //maybe warn the server owner that the channel is missing
return;
if (GroupGreets)
// if group is newly created, greet that user right away,
// but any user which joins in the next 5 seconds will
// be greeted in a group greet
if (_byes.CreateOrAdd(guild.Id, user))
{
// if group is newly created, greet that user right away,
// but any user which joins in the next 5 seconds will
// be greeted in a group greet
if (_byes.CreateOrAdd(guild.Id, user))
// greet single user
await ByeUsers(conf, channel, new[] { user });
var groupClear = false;
while (!groupClear)
{
// greet single user
await ByeUsers(conf, channel, new[] { user });
var groupClear = false;
while (!groupClear)
{
await Task.Delay(5000);
groupClear = _byes.ClearGroup(guild.Id, 5, out var toBye);
await ByeUsers(conf, channel, toBye);
}
await Task.Delay(5000);
groupClear = _byes.ClearGroup(guild.Id, 5, out var toBye);
await ByeUsers(conf, channel, toBye);
}
}
else
{
await ByeUsers(conf, channel, new[] { user });
}
}
catch
else
{
// ignored
await ByeUsers(conf, channel, new[] { user });
}
}
);
catch
{
// ignored
}
});
return Task.CompletedTask;
}
public string? GetDmGreetMsg(ulong id)
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(id, set => set)
?.DmGreetMessageText;
return uow.GuildConfigsForId(id, set => set)?.DmGreetMessageText;
}
public string? GetGreetMsg(ulong gid)
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(gid, set => set)
.ChannelGreetMessageText;
return uow.GuildConfigsForId(gid, set => set).ChannelGreetMessageText;
}
public string? GetBoostMessage(ulong gid)
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(gid, set => set)
.BoostMessage;
return uow.GuildConfigsForId(gid, set => set).BoostMessage;
}
private Task ByeUsers(GreetSettings conf, ITextChannel channel, IUser user)
@@ -177,20 +166,17 @@ public class GreetSettingsService : INService
return;
var rep = new ReplacementBuilder().WithChannel(channel)
.WithClient(_client)
.WithServer(_client, (SocketGuild)channel.Guild)
.WithManyUsers(users)
.Build();
.WithClient(_client)
.WithServer(_client, (SocketGuild)channel.Guild)
.WithManyUsers(users)
.Build();
var text = SmartText.CreateFrom(conf.ChannelByeMessageText);
text = rep.Replace(text);
try
{
var toDelete = await channel.SendAsync(text);
if (conf.AutoDeleteByeMessagesTimer > 0)
{
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
if (conf.AutoDeleteByeMessagesTimer > 0) toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
catch (Exception ex)
{
@@ -207,20 +193,17 @@ public class GreetSettingsService : INService
return;
var rep = new ReplacementBuilder().WithChannel(channel)
.WithClient(_client)
.WithServer(_client, (SocketGuild)channel.Guild)
.WithManyUsers(users)
.Build();
.WithClient(_client)
.WithServer(_client, (SocketGuild)channel.Guild)
.WithManyUsers(users)
.Build();
var text = SmartText.CreateFrom(conf.ChannelGreetMessageText);
text = rep.Replace(text);
try
{
var toDelete = await channel.SendAsync(text);
if (conf.AutoDeleteGreetMessagesTimer > 0)
{
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
if (conf.AutoDeleteGreetMessagesTimer > 0) toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
catch (Exception ex)
{
@@ -230,12 +213,7 @@ public class GreetSettingsService : INService
private async Task<bool> GreetDmUser(GreetSettings conf, IDMChannel channel, IGuildUser user)
{
var rep = new ReplacementBuilder().WithDefault(user,
channel,
(SocketGuild)user.Guild,
_client
)
.Build();
var rep = new ReplacementBuilder().WithDefault(user, channel, (SocketGuild)user.Guild, _client).Build();
var text = SmartText.CreateFrom(conf.DmGreetMessageText);
rep.Replace(text);
@@ -254,65 +232,60 @@ public class GreetSettingsService : INService
private Task UserJoined(IGuildUser user)
{
var _ = Task.Run(async () =>
{
try
{
try
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
var conf = GetOrAddSettingsForGuild(user.GuildId);
if (conf.SendChannelGreetMessage)
if (conf.SendChannelGreetMessage)
{
var channel = await user.Guild.GetTextChannelAsync(conf.GreetMessageChannelId);
if (channel != null)
{
var channel = await user.Guild.GetTextChannelAsync(conf.GreetMessageChannelId);
if (channel != null)
if (GroupGreets)
{
if (GroupGreets)
// if group is newly created, greet that user right away,
// but any user which joins in the next 5 seconds will
// be greeted in a group greet
if (_greets.CreateOrAdd(user.GuildId, user))
{
// if group is newly created, greet that user right away,
// but any user which joins in the next 5 seconds will
// be greeted in a group greet
if (_greets.CreateOrAdd(user.GuildId, user))
// greet single user
await GreetUsers(conf, channel, new[] { user });
var groupClear = false;
while (!groupClear)
{
// greet single user
await GreetUsers(conf, channel, new[] { user });
var groupClear = false;
while (!groupClear)
{
await Task.Delay(5000);
groupClear = _greets.ClearGroup(user.GuildId, 5, out var toGreet);
await GreetUsers(conf, channel, toGreet);
}
await Task.Delay(5000);
groupClear = _greets.ClearGroup(user.GuildId, 5, out var toGreet);
await GreetUsers(conf, channel, toGreet);
}
}
else
{
await GreetUsers(conf, channel, new[] { user });
}
}
}
if (conf.SendDmGreetMessage)
{
var channel = await user.CreateDMChannelAsync();
if (channel is not null)
else
{
await GreetDmUser(conf, channel, user);
await GreetUsers(conf, channel, new[] { user });
}
}
}
catch
if (conf.SendDmGreetMessage)
{
// ignored
var channel = await user.CreateDMChannelAsync();
if (channel is not null) await GreetDmUser(conf, channel, user);
}
}
);
catch
{
// ignored
}
});
return Task.CompletedTask;
}
public string? GetByeMessage(ulong gid)
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(gid, set => set)
.ChannelByeMessageText;
return uow.GuildConfigsForId(gid, set => set).ChannelByeMessageText;
}
public GreetSettings GetOrAddSettingsForGuild(ulong guildId)
@@ -332,11 +305,9 @@ public class GreetSettingsService : INService
public async Task<bool> SetSettings(ulong guildId, GreetSettings settings)
{
if (settings.AutoDeleteByeMessagesTimer is > 600 or < 0 ||
settings.AutoDeleteGreetMessagesTimer is > 600 or < 0)
{
if (settings.AutoDeleteByeMessagesTimer is > 600 or < 0
|| settings.AutoDeleteGreetMessagesTimer is > 600 or < 0)
return false;
}
await using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
@@ -411,53 +382,6 @@ public class GreetSettingsService : INService
return enabled;
}
#region Get Enabled Status
public bool GetGreetDmEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendDmGreetMessage;
}
public bool GetGreetEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendChannelGreetMessage;
}
public bool GetByeEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendChannelByeMessage;
}
#endregion
#region Test Messages
public Task ByeTest(ITextChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return ByeUsers(conf, channel, user);
}
public Task GreetTest(ITextChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return GreetUsers(conf, channel, user);
}
public Task<bool> GreetDmTest(IDMChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return GreetDmUser(conf, channel, user);
}
#endregion
public bool SetGreetDmMessage(ulong guildId, ref string? message)
{
message = message?.SanitizeMentions();
@@ -580,4 +504,51 @@ public class GreetSettingsService : INService
_guildConfigsCache.AddOrUpdate(guildId, toAdd, (_, _) => toAdd);
return conf.SendBoostMessage;
}
#region Get Enabled Status
public bool GetGreetDmEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendDmGreetMessage;
}
public bool GetGreetEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendChannelGreetMessage;
}
public bool GetByeEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendChannelByeMessage;
}
#endregion
#region Test Messages
public Task ByeTest(ITextChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return ByeUsers(conf, channel, user);
}
public Task GreetTest(ITextChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return GreetUsers(conf, channel, user);
}
public Task<bool> GreetDmTest(IDMChannel channel, IGuildUser user)
{
var conf = GetOrAddSettingsForGuild(user.GuildId);
return GreetDmUser(conf, channel, user);
}
#endregion
}

View File

@@ -5,7 +5,8 @@ public partial class Administration
[Group]
public class ServerGreetCommands : NadekoSubmodule<GreetSettingsService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task Boost()
@@ -18,7 +19,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.boost_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task BoostDel(int timer = 30)
@@ -34,7 +36,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.boostdel_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task BoostMsg([Leftover] string? text = null)
@@ -53,7 +56,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.boostmsg_enable($"`{Prefix}boost`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task GreetDel(int timer = 30)
@@ -69,7 +73,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.greetdel_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task Greet()
@@ -82,7 +87,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.greet_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task GreetMsg([Leftover] string? text = null)
@@ -97,12 +103,13 @@ public partial class Administration
var sendGreetEnabled = _service.SetGreetMessage(ctx.Guild.Id, ref text);
await ReplyConfirmLocalizedAsync(strs.greetmsg_new);
if (!sendGreetEnabled)
await ReplyPendingLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task GreetDm()
@@ -115,7 +122,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.greetdm_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task GreetDmMsg([Leftover] string? text = null)
@@ -134,7 +142,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task Bye()
@@ -147,7 +156,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.bye_off);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task ByeMsg([Leftover] string? text = null)
@@ -166,7 +176,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task ByeDel(int timer = 30)
@@ -180,7 +191,8 @@ public partial class Administration
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
[Ratelimit(5)]
@@ -190,13 +202,11 @@ public partial class Administration
await _service.ByeTest((ITextChannel)ctx.Channel, user);
var enabled = _service.GetByeEnabled(ctx.Guild.Id);
if (!enabled)
{
await ReplyPendingLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
}
if (!enabled) await ReplyPendingLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
[Ratelimit(5)]
@@ -206,13 +216,11 @@ public partial class Administration
await _service.GreetTest((ITextChannel)ctx.Channel, user);
var enabled = _service.GetGreetEnabled(ctx.Guild.Id);
if (!enabled)
{
await ReplyPendingLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
}
if (!enabled) await ReplyPendingLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
[Ratelimit(5)]
@@ -231,4 +239,4 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
}
}
}
}

View File

@@ -8,46 +8,46 @@ public partial class Administration
[Group]
public class LocalizationCommands : NadekoSubmodule
{
private static readonly IReadOnlyDictionary<string, string> supportedLocales =
new Dictionary<string, string>()
{
{"ar", "العربية"},
{"zh-TW", "繁體中文, 台灣"},
{"zh-CN", "简体中文, 中华人民共和国"},
{"nl-NL", "Nederlands, Nederland"},
{"en-US", "English, United States"},
{"fr-FR", "Français, France"},
{"cs-CZ", "Čeština, Česká republika"},
{"da-DK", "Dansk, Danmark"},
{"de-DE", "Deutsch, Deutschland"},
{"he-IL", "עברית, ישראל"},
{"hu-HU", "Magyar, Magyarország"},
{"id-ID", "Bahasa Indonesia, Indonesia"},
{"it-IT", "Italiano, Italia"},
{"ja-JP", "日本語, 日本"},
{"ko-KR", "한국어, 대한민국"},
{"nb-NO", "Norsk, Norge"},
{"pl-PL", "Polski, Polska"},
{"pt-BR", "Português Brasileiro, Brasil"},
{"ro-RO", "Română, România"},
{"ru-RU", "Русский, Россия"},
{"sr-Cyrl-RS", "Српски, Србија"},
{"es-ES", "Español, España"},
{"sv-SE", "Svenska, Sverige"},
{"tr-TR", "Türkçe, Türkiye"},
{"ts-TS", "Tsundere, You Baka"},
{"uk-UA", "Українська, Україна"}
};
private static readonly IReadOnlyDictionary<string, string> supportedLocales = new Dictionary<string, string>
{
{ "ar", "العربية" },
{ "zh-TW", "繁體中文, 台灣" },
{ "zh-CN", "简体中文, 中华人民共和国" },
{ "nl-NL", "Nederlands, Nederland" },
{ "en-US", "English, United States" },
{ "fr-FR", "Français, France" },
{ "cs-CZ", "Čeština, Česká republika" },
{ "da-DK", "Dansk, Danmark" },
{ "de-DE", "Deutsch, Deutschland" },
{ "he-IL", "עברית, ישראל" },
{ "hu-HU", "Magyar, Magyarország" },
{ "id-ID", "Bahasa Indonesia, Indonesia" },
{ "it-IT", "Italiano, Italia" },
{ "ja-JP", "日本語, 日本" },
{ "ko-KR", "한국어, 대한민국" },
{ "nb-NO", "Norsk, Norge" },
{ "pl-PL", "Polski, Polska" },
{ "pt-BR", "Português Brasileiro, Brasil" },
{ "ro-RO", "Română, România" },
{ "ru-RU", "Русский, Россия" },
{ "sr-Cyrl-RS", "Српски, Србија" },
{ "es-ES", "Español, España" },
{ "sv-SE", "Svenska, Sverige" },
{ "tr-TR", "Türkçe, Türkiye" },
{ "ts-TS", "Tsundere, You Baka" },
{ "uk-UA", "Українська, Україна" }
};
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[Priority(0)]
public async Task LanguageSet()
=> await ReplyConfirmLocalizedAsync(strs.lang_set_show(
Format.Bold(Culture.ToString()),
=> await ReplyConfirmLocalizedAsync(strs.lang_set_show(Format.Bold(Culture.ToString()),
Format.Bold(Culture.NativeName)));
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(1)]
@@ -67,8 +67,7 @@ public partial class Administration
Localization.SetGuildCulture(ctx.Guild, ci);
}
await ReplyConfirmLocalizedAsync(strs.lang_set(Format.Bold(ci.ToString()),
Format.Bold(ci.NativeName)));
await ReplyConfirmLocalizedAsync(strs.lang_set(Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)));
}
catch (Exception)
{
@@ -76,14 +75,16 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
public async Task LanguageSetDefault()
{
var cul = Localization.DefaultCultureInfo;
await ReplyErrorLocalizedAsync(strs.lang_set_bot_show(cul, cul.NativeName));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task LanguageSetDefault(string name)
{
@@ -110,12 +111,15 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
public async Task LanguagesList()
=> await ctx.Channel.EmbedAsync(_eb.Create().WithOkColor()
.WithTitle(GetText(strs.lang_list))
.WithDescription(string.Join("\n",
supportedLocales.Select(x => $"{Format.Code(x.Key),-10} => {x.Value}"))));
=> await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.lang_list))
.WithDescription(string.Join("\n",
supportedLocales.Select(
x => $"{Format.Code(x.Key),-10} => {x.Value}"))));
}
}
/* list of language codes for reference.
@@ -248,4 +252,4 @@ public partial class Administration
{ "YE", "ar-YE" },
{ "ZA", "af-ZA" },
{ "ZW", "en-ZW" }
*/
*/

View File

@@ -1,7 +1,7 @@
#nullable disable
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration;
@@ -11,7 +11,8 @@ public partial class Administration
[NoPublicBot]
public class LogCommands : NadekoSubmodule<ILogCommandService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -24,7 +25,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.log_disabled);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -38,63 +40,74 @@ public partial class Administration
?? new List<IgnoredLogItem>();
var eb = _eb.Create(ctx)
.WithOkColor()
.AddField(GetText(strs.log_ignored_channels),
chs.Count == 0 ? "-" : string.Join('\n', chs.Select(x => $"{x.LogItemId} | <#{x.LogItemId}>")))
.AddField(GetText(strs.log_ignored_users),
usrs.Count == 0 ? "-" : string.Join('\n', usrs.Select(x => $"{x.LogItemId} | <@{x.LogItemId}>")));
.WithOkColor()
.AddField(GetText(strs.log_ignored_channels),
chs.Count == 0
? "-"
: string.Join('\n', chs.Select(x => $"{x.LogItemId} | <#{x.LogItemId}>")))
.AddField(GetText(strs.log_ignored_users),
usrs.Count == 0
? "-"
: string.Join('\n', usrs.Select(x => $"{x.LogItemId} | <@{x.LogItemId}>")));
await ctx.Channel.EmbedAsync(eb);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task LogIgnore([Leftover]ITextChannel target)
public async Task LogIgnore([Leftover] ITextChannel target)
{
target ??= (ITextChannel)ctx.Channel;
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.Channel);
if (!removed)
await ReplyConfirmLocalizedAsync(strs.log_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
await ReplyConfirmLocalizedAsync(
strs.log_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
else
await ReplyConfirmLocalizedAsync(strs.log_not_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
await ReplyConfirmLocalizedAsync(
strs.log_not_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task LogIgnore([Leftover]IUser target)
public async Task LogIgnore([Leftover] IUser target)
{
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.User);
if (!removed)
await ReplyConfirmLocalizedAsync(strs.log_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
await ReplyConfirmLocalizedAsync(
strs.log_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
else
await ReplyConfirmLocalizedAsync(strs.log_not_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
await ReplyConfirmLocalizedAsync(
strs.log_not_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task LogEvents()
{
var logSetting = _service.GetGuildLogSettings(ctx.Guild.Id);
var str = string.Join("\n", Enum.GetNames(typeof(LogType))
.Select(x =>
{
var val = logSetting is null ? null : GetLogProperty(logSetting, Enum.Parse<LogType>(x));
if (val != null)
return $"{Format.Bold(x)} <#{val}>";
return Format.Bold(x);
}));
var str = string.Join("\n",
Enum.GetNames(typeof(LogType))
.Select(x =>
{
var val = logSetting is null ? null : GetLogProperty(logSetting, Enum.Parse<LogType>(x));
if (val != null)
return $"{Format.Bold(x)} <#{val}>";
return Format.Bold(x);
}));
await SendConfirmAsync(Format.Bold(GetText(strs.log_events)) + "\n" +
str);
await SendConfirmAsync(Format.Bold(GetText(strs.log_events)) + "\n" + str);
}
private static ulong? GetLogProperty(LogSetting l, LogType type)
@@ -136,7 +149,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -150,4 +164,4 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.log_stop(Format.Bold(type.ToString())));
}
}
}
}

View File

@@ -13,8 +13,8 @@ public partial class Administration
{
var runnerUserRoles = runnerUser.GetRoles();
var targetUserRoles = targetUser.GetRoles();
if (runnerUser.Id != ctx.Guild.OwnerId &&
runnerUserRoles.Max(x => x.Position) <= targetUserRoles.Max(x => x.Position))
if (runnerUser.Id != ctx.Guild.OwnerId
&& runnerUserRoles.Max(x => x.Position) <= targetUserRoles.Max(x => x.Position))
{
await ReplyErrorLocalizedAsync(strs.mute_perms);
return false;
@@ -23,7 +23,8 @@ public partial class Administration
return true;
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
public async Task MuteRole([Leftover] IRole role = null)
@@ -34,20 +35,21 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.mute_role(Format.Code(muteRole.Name)));
return;
}
if (ctx.User.Id != ctx.Guild.OwnerId &&
role.Position >= ((SocketGuildUser) ctx.User).Roles.Max(x => x.Position))
if (ctx.User.Id != ctx.Guild.OwnerId
&& role.Position >= ((SocketGuildUser)ctx.User).Roles.Max(x => x.Position))
{
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
return;
}
await _service.SetMuteRoleAsync(ctx.Guild.Id, role.Name);
await ReplyConfirmLocalizedAsync(strs.mute_role_set);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
[Priority(0)]
@@ -68,7 +70,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
[Priority(1)]
@@ -82,7 +85,8 @@ public partial class Administration
return;
await _service.TimedMute(user, ctx.User, time.Time, reason: reason);
await ReplyConfirmLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await ReplyConfirmLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()),
(int)time.Time.TotalMinutes));
}
catch (Exception ex)
{
@@ -91,7 +95,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
public async Task Unmute(IGuildUser user, [Leftover] string reason = "")
@@ -107,7 +112,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[Priority(0)]
@@ -118,7 +124,7 @@ public partial class Administration
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
return;
await _service.MuteUser(user, ctx.User, MuteType.Chat, reason: reason);
await _service.MuteUser(user, ctx.User, MuteType.Chat, reason);
await ReplyConfirmLocalizedAsync(strs.user_chat_mute(Format.Bold(user.ToString())));
}
catch (Exception ex)
@@ -128,7 +134,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[Priority(1)]
@@ -141,8 +148,9 @@ public partial class Administration
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
return;
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason);
await ReplyConfirmLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason);
await ReplyConfirmLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()),
(int)time.Time.TotalMinutes));
}
catch (Exception ex)
{
@@ -151,14 +159,15 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
public async Task ChatUnmute(IGuildUser user, [Leftover] string reason = "")
{
try
{
await _service.UnmuteUser(user.Guild.Id, user.Id, ctx.User, MuteType.Chat, reason: reason);
await _service.UnmuteUser(user.Guild.Id, user.Id, ctx.User, MuteType.Chat, reason);
await ReplyConfirmLocalizedAsync(strs.user_chat_unmute(Format.Bold(user.ToString())));
}
catch
@@ -167,7 +176,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.MuteMembers)]
[Priority(0)]
@@ -178,7 +188,7 @@ public partial class Administration
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
return;
await _service.MuteUser(user, ctx.User, MuteType.Voice, reason: reason);
await _service.MuteUser(user, ctx.User, MuteType.Voice, reason);
await ReplyConfirmLocalizedAsync(strs.user_voice_mute(Format.Bold(user.ToString())));
}
catch
@@ -187,11 +197,12 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.MuteMembers)]
[Priority(1)]
public async Task VoiceMute(StoopidTime time,IGuildUser user, [Leftover] string reason = "")
public async Task VoiceMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
{
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
return;
@@ -200,8 +211,9 @@ public partial class Administration
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
return;
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason);
await ReplyConfirmLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason);
await ReplyConfirmLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()),
(int)time.Time.TotalMinutes));
}
catch
{
@@ -209,14 +221,15 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.MuteMembers)]
public async Task VoiceUnmute(IGuildUser user, [Leftover] string reason = "")
{
try
{
await _service.UnmuteUser(user.GuildId, user.Id, ctx.User, MuteType.Voice, reason: reason);
await _service.UnmuteUser(user.GuildId, user.Id, ctx.User, MuteType.Voice, reason);
await ReplyConfirmLocalizedAsync(strs.user_voice_unmute(Format.Bold(user.ToString())));
}
catch
@@ -225,4 +238,4 @@ public partial class Administration
}
}
}
}
}

View File

@@ -8,7 +8,8 @@ public partial class Administration
[Group]
public class PlayingRotateCommands : NadekoSubmodule<PlayingRotateService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task RotatePlaying()
{
@@ -18,7 +19,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.ropl_disabled);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task AddPlaying(ActivityType t, [Leftover] string status)
{
@@ -27,7 +29,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.ropl_added);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task ListPlaying()
{
@@ -40,13 +43,13 @@ public partial class Administration
else
{
var i = 1;
await ReplyConfirmLocalizedAsync(strs.ropl_list(
string.Join("\n\t", statuses.Select(rs => $"`{i++}.` *{rs.Type}* {rs.Status}"))));
await ReplyConfirmLocalizedAsync(strs.ropl_list(string.Join("\n\t",
statuses.Select(rs => $"`{i++}.` *{rs.Type}* {rs.Status}"))));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task RemovePlaying(int index)
{
@@ -60,4 +63,4 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.reprm(msg));
}
}
}
}

View File

@@ -6,41 +6,45 @@ public partial class Administration
[Group]
public class PrefixCommands : NadekoSubmodule
{
[NadekoCommand, Aliases]
[Priority(1)]
public async Task PrefixCommand()
=> await ReplyConfirmLocalizedAsync(strs.prefix_current(Format.Code(CmdHandler.GetPrefix(ctx.Guild))));
public enum Set
{
Set
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[Priority(1)]
public async Task PrefixCommand()
=> await ReplyConfirmLocalizedAsync(strs.prefix_current(Format.Code(CmdHandler.GetPrefix(ctx.Guild))));
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(0)]
public Task PrefixCommand(Set _, [Leftover] string prefix)
=> PrefixCommand(prefix);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(0)]
public async Task PrefixCommand([Leftover]string prefix)
public async Task PrefixCommand([Leftover] string prefix)
{
if (string.IsNullOrWhiteSpace(prefix))
return;
var oldPrefix = base.Prefix;
var oldPrefix = Prefix;
var newPrefix = CmdHandler.SetPrefix(ctx.Guild, prefix);
await ReplyConfirmLocalizedAsync(strs.prefix_new(Format.Code(oldPrefix), Format.Code(newPrefix)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task DefPrefix([Leftover]string prefix = null)
public async Task DefPrefix([Leftover] string prefix = null)
{
if (string.IsNullOrWhiteSpace(prefix))
{
@@ -54,4 +58,4 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.defprefix_new(Format.Code(oldPrefix), Format.Code(newPrefix)));
}
}
}
}

View File

@@ -1,8 +1,8 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Modules.Administration.Common;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration;
@@ -11,7 +11,8 @@ public partial class Administration
[Group]
public class ProtectionCommands : NadekoSubmodule<ProtectionService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task AntiAlt()
@@ -25,26 +26,31 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.protection_not_running("Anti-Alt"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover] StoopidTime punishTime = null)
{
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
var punishTimeMinutes = (int?) punishTime?.Time.TotalMinutes ?? 0;
var punishTimeMinutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
return;
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, (int?)punishTime?.Time.TotalMinutes ?? 0);
await _service.StartAntiAltAsync(ctx.Guild.Id,
minAgeMinutes,
action,
(int?)punishTime?.Time.TotalMinutes ?? 0);
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover]IRole role)
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover] IRole role)
{
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
@@ -55,46 +61,50 @@ public partial class Administration
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public Task AntiRaid()
{
if (_service.TryStopAntiRaid(ctx.Guild.Id))
{
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Raid"));
}
else
{
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Raid"));
}
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Raid"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(1)]
public Task AntiRaid(int userThreshold, int seconds,
PunishmentAction action, [Leftover] StoopidTime punishTime)
=> InternalAntiRaid(userThreshold, seconds, action, punishTime: punishTime);
public Task AntiRaid(
int userThreshold,
int seconds,
PunishmentAction action,
[Leftover] StoopidTime punishTime)
=> InternalAntiRaid(userThreshold, seconds, action, punishTime);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(2)]
public Task AntiRaid(int userThreshold, int seconds, PunishmentAction action)
=> InternalAntiRaid(userThreshold, seconds, action);
private async Task InternalAntiRaid(int userThreshold, int seconds = 10,
PunishmentAction action = PunishmentAction.Mute, StoopidTime punishTime = null)
private async Task InternalAntiRaid(
int userThreshold,
int seconds = 10,
PunishmentAction action = PunishmentAction.Mute,
StoopidTime punishTime = null)
{
if (action == PunishmentAction.AddRole)
{
await ReplyErrorLocalizedAsync(strs.punishment_unsupported(action));
return;
}
if (userThreshold is < 2 or > 30)
{
await ReplyErrorLocalizedAsync(strs.raid_cnt(2, 30));
@@ -106,47 +116,36 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.raid_time(2, 300));
return;
}
if (punishTime is not null)
{
if (!_service.IsDurationAllowed(action))
{
await ReplyErrorLocalizedAsync(strs.prot_cant_use_time);
}
}
var time = (int?) punishTime?.Time.TotalMinutes ?? 0;
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
if (time is < 0 or > 60 * 24)
return;
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds,
action, time);
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds, action, time);
if (stats is null)
{
return;
}
if (stats is null) return;
await SendConfirmAsync(GetText(strs.prot_enable("Anti-Raid")),
$"{ctx.User.Mention} {GetAntiRaidString(stats)}");
$"{ctx.User.Mention} {GetAntiRaidString(stats)}");
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public Task AntiSpam()
{
if (_service.TryStopAntiSpam(ctx.Guild.Id))
{
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Spam"));
}
else
{
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Spam"));
}
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Spam"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(0)]
@@ -158,35 +157,36 @@ public partial class Administration
return InternalAntiSpam(messageCount, action, null, role);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(1)]
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] StoopidTime punishTime)
=> InternalAntiSpam(messageCount, action, punishTime, null);
=> InternalAntiSpam(messageCount, action, punishTime);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[Priority(2)]
public Task AntiSpam(int messageCount, PunishmentAction action)
=> InternalAntiSpam(messageCount, action);
public async Task InternalAntiSpam(int messageCount, PunishmentAction action,
StoopidTime timeData = null, IRole role = null)
public async Task InternalAntiSpam(
int messageCount,
PunishmentAction action,
StoopidTime timeData = null,
IRole role = null)
{
if (messageCount is < 2 or > 10)
return;
if (timeData is not null)
{
if (!_service.IsDurationAllowed(action))
{
await ReplyErrorLocalizedAsync(strs.prot_cant_use_time);
}
}
var time = (int?) timeData?.Time.TotalMinutes ?? 0;
var time = (int?)timeData?.Time.TotalMinutes ?? 0;
if (time is < 0 or > 60 * 24)
return;
@@ -196,14 +196,15 @@ public partial class Administration
$"{ctx.User.Mention} {GetAntiSpamString(stats)}");
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task AntispamIgnore()
{
var added = await _service.AntiSpamIgnoreAsync(ctx.Guild.Id, ctx.Channel.Id);
if(added is null)
if (added is null)
{
await ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Spam"));
return;
@@ -215,7 +216,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.spam_not_ignore("Anti-Spam"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task AntiList()
{
@@ -227,8 +229,7 @@ public partial class Administration
return;
}
var embed = _eb.Create().WithOkColor()
.WithTitle(GetText(strs.prot_active));
var embed = _eb.Create().WithOkColor().WithTitle(GetText(strs.prot_active));
if (spam != null)
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
@@ -242,9 +243,8 @@ public partial class Administration
await ctx.Channel.EmbedAsync(embed);
}
private string GetAntiAltString(AntiAltStats alt)
=> GetText(strs.anti_alt_status(
Format.Bold(alt.MinAge.ToString(@"dd\d\ hh\h\ mm\m\ ")),
private string GetAntiAltString(AntiAltStats alt)
=> GetText(strs.anti_alt_status(Format.Bold(alt.MinAge.ToString(@"dd\d\ hh\h\ mm\m\ ")),
Format.Bold(alt.Action.ToString()),
Format.Bold(alt.Counter.ToString())));
@@ -257,13 +257,9 @@ public partial class Administration
ignoredString = "none";
var add = string.Empty;
if (settings.MuteTime > 0)
{
add = $" ({TimeSpan.FromMinutes(settings.MuteTime):hh\\hmm\\m})";
}
if (settings.MuteTime > 0) add = $" ({TimeSpan.FromMinutes(settings.MuteTime):hh\\hmm\\m})";
return GetText(strs.spam_stats(
Format.Bold(settings.MessageThreshold.ToString()),
return GetText(strs.spam_stats(Format.Bold(settings.MessageThreshold.ToString()),
Format.Bold(settings.Action + add),
ignoredString));
}
@@ -273,14 +269,11 @@ public partial class Administration
var actionString = Format.Bold(stats.AntiRaidSettings.Action.ToString());
if (stats.AntiRaidSettings.PunishDuration > 0)
{
actionString += $" **({TimeSpan.FromMinutes(stats.AntiRaidSettings.PunishDuration):hh\\hmm\\m})**";
}
return GetText(strs.raid_stats(
Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
return GetText(strs.raid_stats(Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
Format.Bold(stats.AntiRaidSettings.Seconds.ToString()),
actionString));
}
}
}
}

View File

@@ -1,6 +1,5 @@
#nullable disable
using NadekoBot.Modules.Administration.Services;
using ITextChannel = Discord.ITextChannel;
namespace NadekoBot.Modules.Administration;
@@ -12,7 +11,8 @@ public partial class Administration
private static readonly TimeSpan twoWeeks = TimeSpan.FromDays(14);
//delets her own messages, no perm required
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Prune(string parameter = null)
{
@@ -24,8 +24,10 @@ public partial class Administration
await _service.PruneWhere((ITextChannel)ctx.Channel, 100, x => x.Author.Id == user.Id);
ctx.Message.DeleteAfter(3);
}
// prune x
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageMessages)]
[BotPerm(ChannelPerm.ManageMessages)]
@@ -45,7 +47,8 @@ public partial class Administration
}
//prune @user [x]
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageMessages)]
[BotPerm(ChannelPerm.ManageMessages)]
@@ -54,7 +57,8 @@ public partial class Administration
=> Prune(user.Id, count, parameter);
//prune userid [x]
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPerm.ManageMessages)]
[BotPerm(ChannelPerm.ManageMessages)]
@@ -71,9 +75,13 @@ public partial class Administration
count = 1000;
if (parameter is "-s" or "--safe")
await _service.PruneWhere((ITextChannel)ctx.Channel, count, m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks && !m.IsPinned);
await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks && !m.IsPinned);
else
await _service.PruneWhere((ITextChannel)ctx.Channel, count, m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks);
await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks);
}
}
}
}

View File

@@ -1,8 +1,9 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Services.Database.Models;
using SixLabors.ImageSharp.PixelFormats;
using System.Net;
using Color = SixLabors.ImageSharp.Color;
namespace NadekoBot.Modules.Administration;
@@ -10,9 +11,10 @@ public partial class Administration
{
public class RoleCommands : NadekoSubmodule<RoleCommandsService>
{
private IServiceProvider _services;
public enum Exclude { Excl }
private IServiceProvider _services;
public RoleCommands(IServiceProvider services)
=> _services = services;
@@ -20,34 +22,32 @@ public partial class Administration
{
var target = messageId is { } msgId
? await ctx.Channel.GetMessageAsync(msgId)
: (await ctx.Channel.GetMessagesAsync(2).FlattenAsync())
.Skip(1)
.FirstOrDefault();
: (await ctx.Channel.GetMessagesAsync(2).FlattenAsync()).Skip(1).FirstOrDefault();
if (input.Length % 2 != 0)
return;
var all = await input
.Chunk(input.Length / 2)
.Select(async x =>
{
var inputRoleStr = x.First();
var roleReader = new RoleTypeReader<SocketRole>();
var roleResult = await roleReader.ReadAsync(ctx, inputRoleStr, _services);
if (!roleResult.IsSuccess)
{
Log.Warning("Role {0} not found.", inputRoleStr);
return null;
}
var role = (IRole)roleResult.BestMatch;
if (role.Position > ((IGuildUser)ctx.User).GetRoles().Select(r => r.Position).Max()
&& ctx.User.Id != ctx.Guild.OwnerId)
return null;
var emote = x.Last().ToIEmote();
return new { role, emote };
})
.Where(x => x != null)
.WhenAll();
var all = await input.Chunk(input.Length / 2)
.Select(async x =>
{
var inputRoleStr = x.First();
var roleReader = new RoleTypeReader<SocketRole>();
var roleResult = await roleReader.ReadAsync(ctx, inputRoleStr, _services);
if (!roleResult.IsSuccess)
{
Log.Warning("Role {0} not found.", inputRoleStr);
return null;
}
var role = (IRole)roleResult.BestMatch;
if (role.Position > ((IGuildUser)ctx.User).GetRoles().Select(r => r.Position).Max()
&& ctx.User.Id != ctx.Guild.OwnerId)
return null;
var emote = x.Last().ToIEmote();
return new { role, emote };
})
.Where(x => x != null)
.WhenAll();
if (!all.Any())
return;
@@ -56,12 +56,10 @@ public partial class Administration
{
try
{
await target.AddReactionAsync(x.emote, new()
{
RetryMode = RetryMode.Retry502 | RetryMode.RetryRatelimit
});
await target.AddReactionAsync(x.emote,
new() { RetryMode = RetryMode.Retry502 | RetryMode.RetryRatelimit });
}
catch (Discord.Net.HttpException ex) when(ex.HttpCode == HttpStatusCode.BadRequest)
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.BadRequest)
{
await ReplyErrorLocalizedAsync(strs.reaction_cant_access(Format.Code(x.emote.ToString())));
return;
@@ -70,75 +68,75 @@ public partial class Administration
await Task.Delay(500);
}
if (_service.Add(ctx.Guild.Id, new()
{
Exclusive = exclusive,
MessageId = target.Id,
ChannelId = target.Channel.Id,
ReactionRoles = all.Select(x =>
if (_service.Add(ctx.Guild.Id,
new()
{
return new ReactionRole()
{
EmoteName = x.emote.ToString(),
RoleId = x.role.Id,
};
}).ToList(),
}))
{
Exclusive = exclusive,
MessageId = target.Id,
ChannelId = target.Channel.Id,
ReactionRoles = all.Select(x =>
{
return new ReactionRole
{
EmoteName = x.emote.ToString(), RoleId = x.role.Id
};
})
.ToList()
}))
await ctx.OkAsync();
}
else
{
await ReplyErrorLocalizedAsync(strs.reaction_roles_full);
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(0)]
public Task ReactionRoles(ulong messageId, params string[] input) =>
InternalReactionRoles(false, messageId, input);
[NadekoCommand, Aliases]
public Task ReactionRoles(ulong messageId, params string[] input)
=> InternalReactionRoles(false, messageId, input);
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(1)]
public Task ReactionRoles(ulong messageId, Exclude _, params string[] input) =>
InternalReactionRoles(true, messageId, input);
[NadekoCommand, Aliases]
public Task ReactionRoles(ulong messageId, Exclude _, params string[] input)
=> InternalReactionRoles(true, messageId, input);
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(0)]
public Task ReactionRoles(params string[] input) =>
InternalReactionRoles(false, null, input);
public Task ReactionRoles(params string[] input)
=> InternalReactionRoles(false, null, input);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(1)]
public Task ReactionRoles(Exclude _, params string[] input) =>
InternalReactionRoles(true, null, input);
public Task ReactionRoles(Exclude _, params string[] input)
=> InternalReactionRoles(true, null, input);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
public async Task ReactionRolesList()
{
var embed = _eb.Create()
.WithOkColor();
if (!_service.Get(ctx.Guild.Id, out var rrs) ||
!rrs.Any())
var embed = _eb.Create().WithOkColor();
if (!_service.Get(ctx.Guild.Id, out var rrs) || !rrs.Any())
{
embed.WithDescription(GetText(strs.no_reaction_roles));
}
@@ -149,37 +147,33 @@ public partial class Administration
{
var ch = g.GetTextChannel(rr.ChannelId);
IUserMessage msg = null;
if (ch is not null)
{
msg = await ch.GetMessageAsync(rr.MessageId) as IUserMessage;
}
if (ch is not null) msg = await ch.GetMessageAsync(rr.MessageId) as IUserMessage;
var content = msg?.Content.TrimTo(30) ?? "DELETED!";
embed.AddField($"**{rr.Index + 1}.** {ch?.Name ?? "DELETED!"}",
GetText(strs.reaction_roles_message(rr.ReactionRoles?.Count ?? 0, content)));
}
}
await ctx.Channel.EmbedAsync(embed);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
public async Task ReactionRolesRemove(int index)
{
if (index < 1 ||
!_service.Get(ctx.Guild.Id, out var rrs) ||
!rrs.Any() || rrs.Count < index)
{
if (index < 1 || !_service.Get(ctx.Guild.Id, out var rrs) || !rrs.Any() || rrs.Count < index)
return;
}
index--;
var rr = rrs[index];
_service.Remove(ctx.Guild.Id, index);
await ReplyConfirmLocalizedAsync(strs.reaction_role_removed(index + 1));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -193,9 +187,8 @@ public partial class Administration
{
await targetUser.AddRoleAsync(roleToAdd);
await ReplyConfirmLocalizedAsync(
strs.setrole(Format.Bold(roleToAdd.Name), Format.Bold(targetUser.ToString()))
);
await ReplyConfirmLocalizedAsync(strs.setrole(Format.Bold(roleToAdd.Name),
Format.Bold(targetUser.ToString())));
}
catch (Exception ex)
{
@@ -204,19 +197,22 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task RemoveRole(IGuildUser targetUser, [Leftover] IRole roleToRemove)
{
var runnerUser = (IGuildUser)ctx.User;
if (ctx.User.Id != runnerUser.Guild.OwnerId && runnerUser.GetRoles().Max(x => x.Position) <= roleToRemove.Position)
if (ctx.User.Id != runnerUser.Guild.OwnerId
&& runnerUser.GetRoles().Max(x => x.Position) <= roleToRemove.Position)
return;
try
{
await targetUser.RemoveRoleAsync(roleToRemove);
await ReplyConfirmLocalizedAsync(strs.remrole(Format.Bold(roleToRemove.Name), Format.Bold(targetUser.ToString())));
await ReplyConfirmLocalizedAsync(strs.remrole(Format.Bold(roleToRemove.Name),
Format.Bold(targetUser.ToString())));
}
catch
{
@@ -224,11 +220,12 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task RenameRole(IRole roleToEdit, [Leftover]string newname)
public async Task RenameRole(IRole roleToEdit, [Leftover] string newname)
{
var guser = (IGuildUser)ctx.User;
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= roleToEdit.Position)
@@ -240,6 +237,7 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.renrole_perms);
return;
}
await roleToEdit.ModifyAsync(g => g.Name = newname);
await ReplyConfirmLocalizedAsync(strs.renrole);
}
@@ -249,7 +247,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -257,11 +256,11 @@ public partial class Administration
{
var guser = (IGuildUser)ctx.User;
var userRoles = user.GetRoles()
.Where(x => !x.IsManaged && x != x.Guild.EveryoneRole)
.ToList();
if (user.Id == ctx.Guild.OwnerId || (ctx.User.Id != ctx.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
var userRoles = user.GetRoles().Where(x => !x.IsManaged && x != x.Guild.EveryoneRole).ToList();
if (user.Id == ctx.Guild.OwnerId
|| (ctx.User.Id != ctx.Guild.OwnerId
&& guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
return;
try
{
@@ -274,7 +273,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -287,22 +287,23 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.cr(Format.Bold(r.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task DeleteRole([Leftover] IRole role)
{
var guser = (IGuildUser)ctx.User;
if (ctx.User.Id != guser.Guild.OwnerId
&& guser.GetRoles().Max(x => x.Position) <= role.Position)
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
return;
await role.DeleteAsync();
await ReplyConfirmLocalizedAsync(strs.dr(Format.Bold(role.Name)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -311,32 +312,30 @@ public partial class Administration
var newHoisted = !role.IsHoisted;
await role.ModifyAsync(r => r.Hoist = newHoisted);
if (newHoisted)
{
await ReplyConfirmLocalizedAsync(strs.rolehoist_enabled(Format.Bold(role.Name)));
}
else
{
await ReplyConfirmLocalizedAsync(strs.rolehoist_disabled(Format.Bold(role.Name)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task RoleColor([Leftover] IRole role)
=> await SendConfirmAsync("Role Color", role.Color.RawValue.ToString("x6"));
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(0)]
public async Task RoleColor(SixLabors.ImageSharp.Color color, [Leftover]IRole role)
public async Task RoleColor(Color color, [Leftover] IRole role)
{
try
{
var rgba32 = color.ToPixel<Rgba32>();
await role.ModifyAsync(r => r.Color = new Color(rgba32.R, rgba32.G, rgba32.B));
await role.ModifyAsync(r => r.Color = new Discord.Color(rgba32.R, rgba32.G, rgba32.B));
await ReplyConfirmLocalizedAsync(strs.rc(Format.Bold(role.Name)));
}
catch (Exception)
@@ -345,4 +344,4 @@ public partial class Administration
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
#nullable disable
using System.Text;
using NadekoBot.Modules.Administration.Services;
using System.Text;
namespace NadekoBot.Modules.Administration;
@@ -9,7 +9,8 @@ public partial class Administration
[Group]
public class SelfAssignedRolesCommands : NadekoSubmodule<SelfAssignedRolesService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
[BotPerm(GuildPerm.ManageMessages)]
@@ -18,24 +19,22 @@ public partial class Administration
var newVal = _service.ToggleAdSarm(ctx.Guild.Id);
if (newVal)
{
await ReplyConfirmLocalizedAsync(strs.adsarm_enable(Prefix));
}
else
{
await ReplyConfirmLocalizedAsync(strs.adsarm_disable(Prefix));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(1)]
public Task Asar([Leftover] IRole role) =>
Asar(0, role);
public Task Asar([Leftover] IRole role)
=> Asar(0, role);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -49,16 +48,14 @@ public partial class Administration
var succ = _service.AddNew(ctx.Guild.Id, role, group);
if (succ)
{
await ReplyConfirmLocalizedAsync(strs.role_added(Format.Bold(role.Name), Format.Bold(group.ToString())));
}
await ReplyConfirmLocalizedAsync(strs.role_added(Format.Bold(role.Name),
Format.Bold(group.ToString())));
else
{
await ReplyErrorLocalizedAsync(strs.role_in_list(Format.Bold(role.Name)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -70,16 +67,14 @@ public partial class Administration
var set = await _service.SetNameAsync(ctx.Guild.Id, group, name);
if (set)
{
await ReplyConfirmLocalizedAsync(strs.group_name_added(Format.Bold(group.ToString()), Format.Bold(name.ToString())));
}
await ReplyConfirmLocalizedAsync(
strs.group_name_added(Format.Bold(group.ToString()), Format.Bold(name)));
else
{
await ReplyConfirmLocalizedAsync(strs.group_name_removed(Format.Bold(group.ToString())));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
public async Task Rsar([Leftover] IRole role)
@@ -90,16 +85,13 @@ public partial class Administration
var success = _service.RemoveSar(role.Guild.Id, role.Id);
if (!success)
{
await ReplyErrorLocalizedAsync(strs.self_assign_not);
}
else
{
await ReplyConfirmLocalizedAsync(strs.self_assign_rem(Format.Bold(role.Name)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Lsar(int page = 1)
{
@@ -108,57 +100,55 @@ public partial class Administration
var (exclusive, roles, groups) = _service.GetRoles(ctx.Guild);
await ctx.SendPaginatedConfirmAsync(page, cur =>
{
var rolesStr = new StringBuilder();
var roleGroups = roles
.OrderBy(x => x.Model.Group)
.Skip(cur * 20)
.Take(20)
.GroupBy(x => x.Model.Group)
.OrderBy(x => x.Key);
foreach (var kvp in roleGroups)
await ctx.SendPaginatedConfirmAsync(page,
cur =>
{
var groupNameText = string.Empty;
if (!groups.TryGetValue(kvp.Key, out var name))
{
groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key)));
}
else
{
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
}
var rolesStr = new StringBuilder();
var roleGroups = roles.OrderBy(x => x.Model.Group)
.Skip(cur * 20)
.Take(20)
.GroupBy(x => x.Model.Group)
.OrderBy(x => x.Key);
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
foreach (var (model, role) in kvp.AsEnumerable())
foreach (var kvp in roleGroups)
{
if (role is null)
{
continue;
}
var groupNameText = string.Empty;
if (!groups.TryGetValue(kvp.Key, out var name))
groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key)));
else
{
// first character is invisible space
if (model.LevelRequirement == 0)
rolesStr.AppendLine(" " + role.Name);
else
rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)");
}
}
rolesStr.AppendLine();
}
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
return _eb.Create().WithOkColor()
.WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count()))))
.WithDescription(rolesStr.ToString())
.WithFooter(exclusive
? GetText(strs.self_assign_are_exclusive)
: GetText(strs.self_assign_are_not_exclusive));
}, roles.Count(), 20);
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
foreach (var (model, role) in kvp.AsEnumerable())
if (role is null)
{
}
else
{
// first character is invisible space
if (model.LevelRequirement == 0)
rolesStr.AppendLine(" " + role.Name);
else
rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)");
}
rolesStr.AppendLine();
}
return _eb.Create()
.WithOkColor()
.WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count()))))
.WithDescription(rolesStr.ToString())
.WithFooter(exclusive
? GetText(strs.self_assign_are_exclusive)
: GetText(strs.self_assign_are_not_exclusive));
},
roles.Count(),
20);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -171,7 +161,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.self_assign_no_excl);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
@@ -188,12 +179,12 @@ public partial class Administration
return;
}
await ReplyConfirmLocalizedAsync(strs.self_assign_level_req(
Format.Bold(role.Name),
await ReplyConfirmLocalizedAsync(strs.self_assign_level_req(Format.Bold(role.Name),
Format.Bold(level.ToString())));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Iam([Leftover] IRole role)
{
@@ -203,25 +194,15 @@ public partial class Administration
IUserMessage msg;
if (result == SelfAssignedRolesService.AssignResult.Err_Not_Assignable)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_not);
}
else if (result == SelfAssignedRolesService.AssignResult.Err_Lvl_Req)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_not_level(Format.Bold(extra.ToString())));
}
else if (result == SelfAssignedRolesService.AssignResult.Err_Already_Have)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_already(Format.Bold(role.Name)));
}
else if (result == SelfAssignedRolesService.AssignResult.Err_Not_Perms)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_perms);
}
else
{
msg = await ReplyConfirmLocalizedAsync(strs.self_assign_success(Format.Bold(role.Name)));
}
if (autoDelete)
{
@@ -230,7 +211,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Iamnot([Leftover] IRole role)
{
@@ -240,21 +222,13 @@ public partial class Administration
IUserMessage msg;
if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Assignable)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_not);
}
else if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Have)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_not_have(Format.Bold(role.Name)));
}
else if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Perms)
{
msg = await ReplyErrorLocalizedAsync(strs.self_assign_perms);
}
else
{
msg = await ReplyConfirmLocalizedAsync(strs.self_assign_remove(Format.Bold(role.Name)));
}
if (autoDelete)
{
@@ -263,4 +237,4 @@ public partial class Administration
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration;
@@ -9,6 +9,14 @@ public partial class Administration
[Group]
public class SelfCommands : NadekoSubmodule<SelfService>
{
public enum SettableUserStatus
{
Online,
Invisible,
Idle,
Dnd
}
private readonly DiscordSocketClient _client;
private readonly IBotStrings _strings;
private readonly ICoordinator _coord;
@@ -20,7 +28,8 @@ public partial class Administration
_coord = coord;
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -30,7 +39,7 @@ public partial class Administration
return;
var guser = (IGuildUser)ctx.User;
var cmd = new AutoCommand()
var cmd = new AutoCommand
{
CommandText = cmdText,
ChannelId = ctx.Channel.Id,
@@ -39,18 +48,22 @@ public partial class Administration
GuildName = ctx.Guild?.Name,
VoiceChannelId = guser.VoiceChannel?.Id,
VoiceChannelName = guser.VoiceChannel?.Name,
Interval = 0,
Interval = 0
};
_service.AddNewAutoCommand(cmd);
await ctx.Channel.EmbedAsync(_eb.Create().WithOkColor()
.WithTitle(GetText(strs.scadd))
.AddField(GetText(strs.server), cmd.GuildId is null ? $"-" : $"{cmd.GuildName}/{cmd.GuildId}", true)
.AddField(GetText(strs.channel), $"{cmd.ChannelName}/{cmd.ChannelId}", true)
.AddField(GetText(strs.command_text), cmdText, false));
await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.scadd))
.AddField(GetText(strs.server),
cmd.GuildId is null ? "-" : $"{cmd.GuildName}/{cmd.GuildId}",
true)
.AddField(GetText(strs.channel), $"{cmd.ChannelName}/{cmd.ChannelId}", true)
.AddField(GetText(strs.command_text), cmdText));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -63,7 +76,7 @@ public partial class Administration
return;
var guser = (IGuildUser)ctx.User;
var cmd = new AutoCommand()
var cmd = new AutoCommand
{
CommandText = cmdText,
ChannelId = ctx.Channel.Id,
@@ -72,14 +85,15 @@ public partial class Administration
GuildName = ctx.Guild?.Name,
VoiceChannelId = guser.VoiceChannel?.Id,
VoiceChannelName = guser.VoiceChannel?.Name,
Interval = interval,
Interval = interval
};
_service.AddNewAutoCommand(cmd);
await ReplyConfirmLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartupCommandsList(int page = 1)
@@ -87,11 +101,8 @@ public partial class Administration
if (page-- < 1)
return;
var scmds = _service.GetStartupCommands()
.Skip(page * 5)
.Take(5)
.ToList();
var scmds = _service.GetStartupCommands().Skip(page * 5).Take(5).ToList();
if (scmds.Count == 0)
{
await ReplyErrorLocalizedAsync(strs.startcmdlist_none);
@@ -99,19 +110,19 @@ public partial class Administration
else
{
var i = 0;
await SendConfirmAsync(
text: string.Join("\n", scmds
.Select(x => $@"```css
await SendConfirmAsync(text: string.Join("\n",
scmds.Select(x => $@"```css
#{++i + (page * 5)}
[{GetText(strs.server)}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
[{GetText(strs.channel)}]: {x.ChannelName} #{x.ChannelId}
[{GetText(strs.command_text)}]: {x.CommandText}```")),
title: string.Empty,
footer: GetText(strs.page(page + 1)));
title: string.Empty,
footer: GetText(strs.page(page + 1)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task AutoCommandsList(int page = 1)
@@ -119,10 +130,7 @@ public partial class Administration
if (page-- < 1)
return;
var scmds = _service.GetAutoCommands()
.Skip(page * 5)
.Take(5)
.ToList();
var scmds = _service.GetAutoCommands().Skip(page * 5).Take(5).ToList();
if (!scmds.Any())
{
await ReplyErrorLocalizedAsync(strs.autocmdlist_none);
@@ -130,23 +138,23 @@ public partial class Administration
else
{
var i = 0;
await SendConfirmAsync(
text: string.Join("\n", scmds
.Select(x => $@"```css
await SendConfirmAsync(text: string.Join("\n",
scmds.Select(x => $@"```css
#{++i + (page * 5)}
[{GetText(strs.server)}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
[{GetText(strs.channel)}]: {x.ChannelName} #{x.ChannelId}
{GetIntervalText(x.Interval)}
[{GetText(strs.command_text)}]: {x.CommandText}```")),
title: string.Empty,
footer: GetText(strs.page(page + 1)));
title: string.Empty,
footer: GetText(strs.page(page + 1)));
}
}
private string GetIntervalText(int interval)
=> $"[{GetText(strs.interval)}]: {interval}";
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task Wait(int miliseconds)
{
@@ -162,8 +170,9 @@ public partial class Administration
await Task.Delay(miliseconds);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -174,11 +183,12 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.acrm_fail);
return;
}
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartupCommandRemove([Leftover] int index)
@@ -189,7 +199,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.scrm);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
@@ -200,7 +211,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.startcmds_cleared);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task ForwardMessages()
{
@@ -212,7 +224,8 @@ public partial class Administration
await ReplyPendingLocalizedAsync(strs.fwdm_stop);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task ForwardToAll()
{
@@ -222,10 +235,10 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.fwall_start);
else
await ReplyPendingLocalizedAsync(strs.fwall_stop);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
public async Task ShardStats(int page = 1)
{
if (--page < 0)
@@ -233,35 +246,36 @@ public partial class Administration
var statuses = _coord.GetAllShardStatuses();
var status = string.Join(" : ", statuses
.Select(x => (ConnectionStateToEmoji(x), x))
.GroupBy(x => x.Item1)
.Select(x => $"`{x.Count()} {x.Key}`")
.ToArray());
var status = string.Join(" : ",
statuses.Select(x => (ConnectionStateToEmoji(x), x))
.GroupBy(x => x.Item1)
.Select(x => $"`{x.Count()} {x.Key}`")
.ToArray());
var allShardStrings = statuses
.Select(st =>
var allShardStrings = statuses.Select(st =>
{
var stateStr = ConnectionStateToEmoji(st);
var timeDiff = DateTime.UtcNow - st.LastUpdate;
var maxGuildCountLength =
statuses.Max(x => x.GuildCount).ToString().Length;
return $"`{stateStr} "
+ $"| #{st.ShardId.ToString().PadBoth(3)} "
+ $"| {timeDiff:mm\\:ss} "
+ $"| {st.GuildCount.ToString().PadBoth(maxGuildCountLength)} `";
})
.ToArray();
await ctx.SendPaginatedConfirmAsync(page,
curPage =>
{
var stateStr = ConnectionStateToEmoji(st);
var timeDiff = DateTime.UtcNow - st.LastUpdate;
var maxGuildCountLength = statuses.Max(x => x.GuildCount).ToString().Length;
return $"`{stateStr} " +
$"| #{st.ShardId.ToString().PadBoth(3)} " +
$"| {timeDiff:mm\\:ss} " +
$"| {st.GuildCount.ToString().PadBoth(maxGuildCountLength)} `";
})
.ToArray();
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25));
var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25));
if (string.IsNullOrWhiteSpace(str))
str = GetText(strs.no_shards_on_page);
if (string.IsNullOrWhiteSpace(str))
str = GetText(strs.no_shards_on_page);
return _eb.Create()
.WithOkColor()
.WithDescription($"{status}\n\n{str}");
}, allShardStrings.Length, 25);
return _eb.Create().WithOkColor().WithDescription($"{status}\n\n{str}");
},
allShardStrings.Length,
25);
}
private static string ConnectionStateToEmoji(ShardStatus status)
@@ -276,28 +290,27 @@ public partial class Administration
};
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task RestartShard(int shardId)
{
var success = _coord.RestartShard(shardId);
if (success)
{
await ReplyConfirmLocalizedAsync(strs.shard_reconnecting(Format.Bold("#" + shardId)));
}
else
{
await ReplyErrorLocalizedAsync(strs.no_shard_id);
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public Task Leave([Leftover] string guildStr)
=> _service.LeaveGuild(guildStr);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task Die(bool graceful = false)
{
@@ -309,11 +322,13 @@ public partial class Administration
{
// ignored
}
await Task.Delay(2000);
_coord.Die(graceful);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task Restart()
{
@@ -324,10 +339,12 @@ public partial class Administration
return;
}
try { await ReplyConfirmLocalizedAsync(strs.restarting); } catch { }
try { await ReplyConfirmLocalizedAsync(strs.restarting); }
catch { }
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task SetName([Leftover] string newName)
{
@@ -346,7 +363,8 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.bot_name(Format.Bold(newName)));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[UserPerm(GuildPerm.ManageNicknames)]
[BotPerm(GuildPerm.ChangeNickname)]
[Priority(0)]
@@ -360,26 +378,28 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[BotPerm(GuildPerm.ManageNicknames)]
[UserPerm(GuildPerm.ManageNicknames)]
[Priority(1)]
public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null)
{
var sg = (SocketGuild) ctx.Guild;
if (sg.OwnerId == gu.Id ||
gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
var sg = (SocketGuild)ctx.Guild;
if (sg.OwnerId == gu.Id
|| gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
{
await ReplyErrorLocalizedAsync(strs.insuf_perms_i);
return;
}
await gu.ModifyAsync(u => u.Nickname = newNick);
await ReplyConfirmLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task SetStatus([Leftover] SettableUserStatus status)
{
@@ -388,32 +408,30 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.bot_status(Format.Bold(status.ToString())));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task SetAvatar([Leftover] string img = null)
{
var success = await _service.SetAvatar(img);
if (success)
{
await ReplyConfirmLocalizedAsync(strs.set_avatar);
}
if (success) await ReplyConfirmLocalizedAsync(strs.set_avatar);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task SetGame(ActivityType type, [Leftover] string game = null)
{
var rep = new ReplacementBuilder()
.WithDefault(Context)
.Build();
var rep = new ReplacementBuilder().WithDefault(Context).Build();
await _service.SetGameAsync(game is null ? game : rep.Replace(game), type);
await ReplyConfirmLocalizedAsync(strs.set_game);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task SetStream(string url, [Leftover] string name = null)
{
@@ -424,23 +442,22 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.set_stream);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task Send(string where, [Leftover] SmartText text = null)
{
var ids = where.Split('|');
if (ids.Length != 2)
return;
var sid = ulong.Parse(ids[0]);
var server = _client.Guilds.FirstOrDefault(s => s.Id == sid);
if (server is null)
return;
var rep = new ReplacementBuilder()
.WithDefault(Context)
.Build();
var rep = new ReplacementBuilder().WithDefault(Context).Build();
if (ids[1].ToUpperInvariant().StartsWith("C:", StringComparison.InvariantCulture))
{
@@ -450,7 +467,7 @@ public partial class Administration
return;
text = rep.Replace(text);
await ch.SendAsync(text, sanitizeAll: false);
await ch.SendAsync(text);
}
else if (ids[1].ToUpperInvariant().StartsWith("U:", StringComparison.InvariantCulture))
{
@@ -468,27 +485,30 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.invalid_format);
return;
}
await ReplyConfirmLocalizedAsync(strs.message_sent);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task ImagesReload()
{
await _service.ReloadImagesAsync();
await ReplyConfirmLocalizedAsync(strs.images_loading);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task StringsReload()
{
_strings.Reload();
await ReplyConfirmLocalizedAsync(strs.bot_strings_reloaded);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[OwnerOnly]
public async Task CoordReload()
{
@@ -512,13 +532,5 @@ public partial class Administration
return UserStatus.Online;
}
public enum SettableUserStatus
{
Online,
Invisible,
Idle,
Dnd
}
}
}
}

View File

@@ -1,7 +1,7 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -13,19 +13,20 @@ public class AdministrationService : INService
private readonly DbService _db;
private readonly ILogCommandService _logService;
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, ILogCommandService logService)
public AdministrationService(
Bot bot,
CommandHandler cmdHandler,
DbService db,
ILogCommandService logService)
{
_db = db;
_logService = logService;
DeleteMessagesOnCommand = new(bot.AllGuildConfigs
.Where(g => g.DeleteMessageOnCommand)
.Select(g => g.GuildId));
DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
DeleteMessagesOnCommandChannels = new(bot.AllGuildConfigs
.SelectMany(x => x.DelMsgOnCmdChannels)
.ToDictionary(x => x.ChannelId, x => x.State)
.ToConcurrent());
DeleteMessagesOnCommandChannels = new(bot.AllGuildConfigs.SelectMany(x => x.DelMsgOnCmdChannels)
.ToDictionary(x => x.ChannelId, x => x.State)
.ToConcurrent());
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
}
@@ -33,8 +34,7 @@ public class AdministrationService : INService
public (bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels) GetDelMsgOnCmdData(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.DelMsgOnCmdChannels));
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
return (conf.DeleteMessageOnCommand, conf.DelMsgOnCmdChannels);
}
@@ -52,14 +52,16 @@ public class AdministrationService : INService
if (state && cmd.Name != "prune" && cmd.Name != "pick")
{
_logService.AddDeleteIgnore(msg.Id);
try { await msg.DeleteAsync(); } catch { }
try { await msg.DeleteAsync(); }
catch { }
}
//if state is false, that means do not do it
}
else if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
{
_logService.AddDeleteIgnore(msg.Id);
try { await msg.DeleteAsync(); } catch { }
try { await msg.DeleteAsync(); }
catch { }
}
});
return Task.CompletedTask;
@@ -80,8 +82,7 @@ public class AdministrationService : INService
{
await using (var uow = _db.GetDbContext())
{
var conf = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.DelMsgOnCmdChannels));
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
var old = conf.DelMsgOnCmdChannels.FirstOrDefault(x => x.ChannelId == chId);
if (newState == Administration.State.Inherit)
@@ -125,7 +126,6 @@ public class AdministrationService : INService
if (!users.Any())
return;
foreach (var u in users)
{
try
{
await u.ModifyAsync(usr => usr.Deaf = value);
@@ -134,23 +134,24 @@ public class AdministrationService : INService
{
// ignored
}
}
}
public async Task EditMessage(ICommandContext context, ITextChannel chanl, ulong messageId, string input)
public async Task EditMessage(
ICommandContext context,
ITextChannel chanl,
ulong messageId,
string input)
{
var msg = await chanl.GetMessageAsync(messageId);
if (msg is not IUserMessage umsg || msg.Author.Id != context.Client.CurrentUser.Id)
return;
var rep = new ReplacementBuilder()
.WithDefault(context)
.Build();
var rep = new ReplacementBuilder().WithDefault(context).Build();
var text = SmartText.CreateFrom(input);
text = rep.Replace(text);
await umsg.EditAsync(text);
}
}
}

View File

@@ -1,9 +1,10 @@
#nullable disable
using System.Threading.Channels;
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
using System.Net;
using System.Threading.Channels;
namespace NadekoBot.Modules.Administration.Services;
@@ -18,9 +19,7 @@ public sealed class AutoAssignRoleService : INService
private readonly Channel<SocketGuildUser> _assignQueue = Channel.CreateBounded<SocketGuildUser>(
new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false,
FullMode = BoundedChannelFullMode.DropOldest, SingleReader = true, SingleWriter = false
});
public AutoAssignRoleService(DiscordSocketClient client, Bot bot, DbService db)
@@ -28,10 +27,10 @@ public sealed class AutoAssignRoleService : INService
_client = client;
_db = db;
_autoAssignableRoles = bot.AllGuildConfigs
.Where(x => !string.IsNullOrWhiteSpace(x.AutoAssignRoleIds))
.ToDictionary<GuildConfig, ulong, IReadOnlyList<ulong>>(k => k.GuildId, v => v.GetAutoAssignableRoles())
.ToConcurrent();
_autoAssignableRoles = bot.AllGuildConfigs.Where(x => !string.IsNullOrWhiteSpace(x.AutoAssignRoleIds))
.ToDictionary<GuildConfig, ulong, IReadOnlyList<ulong>>(k => k.GuildId,
v => v.GetAutoAssignableRoles())
.ToConcurrent();
_ = Task.Run(async () =>
{
@@ -40,14 +39,13 @@ public sealed class AutoAssignRoleService : INService
var user = await _assignQueue.Reader.ReadAsync();
if (!_autoAssignableRoles.TryGetValue(user.Guild.Id, out var savedRoleIds))
continue;
try
{
var roleIds = savedRoleIds
.Select(roleId => user.Guild.GetRole(roleId))
.Where(x => x is not null)
.ToList();
var roleIds = savedRoleIds.Select(roleId => user.Guild.GetRole(roleId))
.Where(x => x is not null)
.ToList();
if (roleIds.Any())
{
await user.AddRolesAsync(roleIds);
@@ -59,16 +57,17 @@ public sealed class AutoAssignRoleService : INService
"Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server the roles dont exist",
user.Guild.Name,
user.Guild.Id);
await DisableAarAsync(user.Guild.Id);
}
}
catch (Discord.Net.HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
{
Log.Warning("Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server because I don't have role management permissions",
Log.Warning(
"Disabled 'Auto assign role' feature on {GuildName} [{GuildId}] server because I don't have role management permissions",
user.Guild.Name,
user.Guild.Id);
await DisableAarAsync(user.Guild.Id);
}
catch (Exception ex)
@@ -84,16 +83,13 @@ public sealed class AutoAssignRoleService : INService
private async Task OnClientRoleDeleted(SocketRole role)
{
if (_autoAssignableRoles.TryGetValue(role.Guild.Id, out var roles)
&& roles.Contains(role.Id))
{
if (_autoAssignableRoles.TryGetValue(role.Guild.Id, out var roles) && roles.Contains(role.Id))
await ToggleAarAsync(role.Guild.Id, role.Id);
}
}
private async Task OnClientOnUserJoined(SocketGuildUser user)
{
if (_autoAssignableRoles.TryGetValue(user.Guild.Id, out _))
if (_autoAssignableRoles.TryGetValue(user.Guild.Id, out _))
await _assignQueue.Writer.WriteAsync(user);
}
@@ -102,42 +98,40 @@ public sealed class AutoAssignRoleService : INService
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
var roles = gc.GetAutoAssignableRoles();
if(!roles.Remove(roleId) && roles.Count < 3)
if (!roles.Remove(roleId) && roles.Count < 3)
roles.Add(roleId);
gc.SetAutoAssignableRoles(roles);
await uow.SaveChangesAsync();
if (roles.Count > 0)
_autoAssignableRoles[guildId] = roles;
else
_autoAssignableRoles.TryRemove(guildId, out _);
return roles;
}
public async Task DisableAarAsync(ulong guildId)
{
await using var uow = _db.GetDbContext();
await uow
.GuildConfigs
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.UpdateAsync(_ => new(){ AutoAssignRoleIds = null});
await uow.GuildConfigs.AsNoTracking()
.Where(x => x.GuildId == guildId)
.UpdateAsync(_ => new() { AutoAssignRoleIds = null });
_autoAssignableRoles.TryRemove(guildId, out _);
await uow.SaveChangesAsync();
}
public async Task SetAarRolesAsync(ulong guildId, IEnumerable<ulong> newRoles)
{
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
gc.SetAutoAssignableRoles(newRoles);
await uow.SaveChangesAsync();
}
@@ -157,4 +151,4 @@ public static class GuildConfigExtensions
public static void SetAutoAssignableRoles(this GuildConfig gc, IEnumerable<ulong> roles)
=> gc.AutoAssignRoleIds = roles.Join(',');
}
}

View File

@@ -1,7 +1,7 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -11,12 +11,18 @@ public class DangerousCommandsService : INService
public const string WaifusDeleteSql = @"DELETE FROM WaifuUpdates;
DELETE FROM WaifuItem;
DELETE FROM WaifuInfo;";
public const string WaifuDeleteSql = @"DELETE FROM WaifuUpdates WHERE UserId=(SELECT Id FROM DiscordUser WHERE UserId={0});
public const string WaifuDeleteSql =
@"DELETE FROM WaifuUpdates WHERE UserId=(SELECT Id FROM DiscordUser WHERE UserId={0});
DELETE FROM WaifuItem WHERE WaifuInfoId=(SELECT Id FROM WaifuInfo WHERE WaifuId=(SELECT Id FROM DiscordUser WHERE UserId={0}));
UPDATE WaifuInfo SET ClaimerId=NULL WHERE ClaimerId=(SELECT Id FROM DiscordUser WHERE UserId={0});
DELETE FROM WaifuInfo WHERE WaifuId=(SELECT Id FROM DiscordUser WHERE UserId={0});";
public const string CurrencyDeleteSql = "UPDATE DiscordUser SET CurrencyAmount=0; DELETE FROM CurrencyTransactions; DELETE FROM PlantedCurrency;";
public const string CurrencyDeleteSql =
"UPDATE DiscordUser SET CurrencyAmount=0; DELETE FROM CurrencyTransactions; DELETE FROM PlantedCurrency;";
public const string MusicPlaylistDeleteSql = "DELETE FROM MusicPlaylists;";
public const string XpDeleteSql = @"DELETE FROM UserXpStats;
UPDATE DiscordUser
SET ClubId=NULL,
@@ -44,19 +50,9 @@ DELETE FROM Clubs;";
return res;
}
public class SelectResult
{
public List<string> ColumnNames { get; set; }
public List<string[]> Results { get; set; }
}
public SelectResult SelectSql(string sql)
{
var result = new SelectResult()
{
ColumnNames = new(),
Results = new(),
};
var result = new SelectResult { ColumnNames = new(), Results = new() };
using var uow = _db.GetDbContext();
var conn = uow.Database.GetDbConnection();
@@ -65,10 +61,7 @@ DELETE FROM Clubs;";
using var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
for (var i = 0; i < reader.FieldCount; i++)
{
result.ColumnNames.Add(reader.GetName(i));
}
for (var i = 0; i < reader.FieldCount; i++) result.ColumnNames.Add(reader.GetName(i));
while (reader.Read())
{
var obj = new object[reader.FieldCount];
@@ -83,50 +76,45 @@ DELETE FROM Clubs;";
public async Task PurgeUserAsync(ulong userId)
{
await using var uow = _db.GetDbContext();
// get waifu info
var wi = await uow.Set<WaifuInfo>()
.FirstOrDefaultAsyncEF(x => x.Waifu.UserId == userId);
var wi = await uow.Set<WaifuInfo>().FirstOrDefaultAsyncEF(x => x.Waifu.UserId == userId);
// if it exists, delete waifu related things
if (wi is not null)
{
// remove updates which have new or old as this waifu
await uow
.WaifuUpdates
.DeleteAsync(wu => wu.New.UserId == userId || wu.Old.UserId == userId);
await uow.WaifuUpdates.DeleteAsync(wu => wu.New.UserId == userId || wu.Old.UserId == userId);
// delete all items this waifu owns
await uow
.Set<WaifuItem>()
.DeleteAsync(x => x.WaifuInfoId == wi.Id);
await uow.Set<WaifuItem>().DeleteAsync(x => x.WaifuInfoId == wi.Id);
// all waifus this waifu claims are released
await uow
.Set<WaifuInfo>()
.AsQueryable()
.Where(x => x.Claimer.UserId == userId)
.UpdateAsync(x => new() {ClaimerId = null});
await uow.Set<WaifuInfo>()
.AsQueryable()
.Where(x => x.Claimer.UserId == userId)
.UpdateAsync(x => new() { ClaimerId = null });
// all affinities set to this waifu are reset
await uow
.Set<WaifuInfo>()
.AsQueryable()
.Where(x => x.Affinity.UserId == userId)
.UpdateAsync(x => new() {AffinityId = null});
await uow.Set<WaifuInfo>()
.AsQueryable()
.Where(x => x.Affinity.UserId == userId)
.UpdateAsync(x => new() { AffinityId = null });
}
// delete guild xp
await uow
.UserXpStats
.DeleteAsync(x => x.UserId == userId);
await uow.UserXpStats.DeleteAsync(x => x.UserId == userId);
// delete currency transactions
await uow.Set<CurrencyTransaction>()
.DeleteAsync(x => x.UserId == userId);
await uow.Set<CurrencyTransaction>().DeleteAsync(x => x.UserId == userId);
// delete user, currency, and clubs go away with it
await uow.DiscordUser
.DeleteAsync(u => u.UserId == userId);
await uow.DiscordUser.DeleteAsync(u => u.UserId == userId);
}
}
public class SelectResult
{
public List<string> ColumnNames { get; set; }
public List<string[]> Results { get; set; }
}
}

View File

@@ -7,11 +7,10 @@ namespace NadekoBot.Modules.Administration.Services;
public class DiscordPermOverrideService : INService, ILateBlocker
{
public int Priority { get; } = int.MaxValue;
private readonly DbService _db;
private readonly IServiceProvider _services;
public int Priority { get; } = int.MaxValue;
private readonly ConcurrentDictionary<(ulong, string), DiscordPermOverride> _overrides;
public DiscordPermOverrideService(DbService db, IServiceProvider services)
@@ -19,11 +18,10 @@ public class DiscordPermOverrideService : INService, ILateBlocker
_db = db;
_services = services;
using var uow = _db.GetDbContext();
_overrides = uow.DiscordPermOverrides
.AsNoTracking()
.AsEnumerable()
.ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o)
.ToConcurrent();
_overrides = uow.DiscordPermOverrides.AsNoTracking()
.AsEnumerable()
.ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o)
.ToConcurrent();
}
public bool TryGetOverrides(ulong guildId, string commandName, out GuildPerm? perm)
@@ -39,8 +37,11 @@ public class DiscordPermOverrideService : INService, ILateBlocker
return false;
}
public Task<PreconditionResult> ExecuteOverrides(ICommandContext ctx, CommandInfo command,
GuildPerm perms, IServiceProvider services)
public Task<PreconditionResult> ExecuteOverrides(
ICommandContext ctx,
CommandInfo command,
GuildPerm perms,
IServiceProvider services)
{
var rupa = new RequireUserPermissionAttribute(perms);
return rupa.CheckPermissionsAsync(ctx, command, services);
@@ -50,20 +51,14 @@ public class DiscordPermOverrideService : INService, ILateBlocker
{
commandName = commandName.ToLowerInvariant();
await using var uow = _db.GetDbContext();
var over = await uow
.Set<DiscordPermOverride>()
.AsQueryable()
.FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command);
var over = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command);
if (over is null)
{
uow.Set<DiscordPermOverride>()
.Add(over = new() { Command = commandName, Perm = perm, GuildId = guildId, });
}
uow.Set<DiscordPermOverride>().Add(over = new() { Command = commandName, Perm = perm, GuildId = guildId });
else
{
over.Perm = perm;
}
_overrides[(guildId, commandName)] = over;
@@ -73,20 +68,16 @@ public class DiscordPermOverrideService : INService, ILateBlocker
public async Task ClearAllOverrides(ulong guildId)
{
await using var uow = _db.GetDbContext();
var overrides = await uow
.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
var overrides = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
uow.RemoveRange(overrides);
await uow.SaveChangesAsync();
foreach (var over in overrides)
{
_overrides.TryRemove((guildId, over.Command), out _);
}
foreach (var over in overrides) _overrides.TryRemove((guildId, over.Command), out _);
}
public async Task RemoveOverride(ulong guildId, string commandName)
@@ -94,11 +85,10 @@ public class DiscordPermOverrideService : INService, ILateBlocker
commandName = commandName.ToLowerInvariant();
await using var uow = _db.GetDbContext();
var over = await uow
.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName);
var over = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName);
if (over is null)
return;
@@ -112,24 +102,25 @@ public class DiscordPermOverrideService : INService, ILateBlocker
public async Task<List<DiscordPermOverride>> GetAllOverrides(ulong guildId)
{
await using var uow = _db.GetDbContext();
return await uow
.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
return await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
}
public async Task<bool> TryBlockLate(ICommandContext context, string moduleName, CommandInfo command)
{
if (TryGetOverrides(context.Guild?.Id ?? 0, command.Name, out var perm) && perm is not null)
{
var result = await new RequireUserPermissionAttribute((GuildPermission)perm)
.CheckPermissionsAsync(context, command, _services);
var result =
await new RequireUserPermissionAttribute((GuildPermission)perm).CheckPermissionsAsync(context,
command,
_services);
return !result.IsSuccess;
}
return false;
}
}
}

View File

@@ -16,9 +16,8 @@ public class GameVoiceChannelService : INService
_db = db;
_client = client;
GameVoiceChannels = new(
bot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
.Select(gc => gc.GameVoiceChannel.Value));
GameVoiceChannels = new(bot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
.Select(gc => gc.GameVoiceChannel.Value));
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
_client.GuildMemberUpdated += _client_GuildMemberUpdated;
@@ -39,11 +38,8 @@ public class GameVoiceChannelService : INService
var oldActivity = before.Value.Activities.FirstOrDefault();
var newActivity = after.Activities.FirstOrDefault();
if (oldActivity != newActivity && newActivity is { Type: ActivityType.Playing })
{
//trigger gvc
await TriggerGvc(after, newActivity.Name);
}
}
catch (Exception ex)
{
@@ -87,12 +83,10 @@ public class GameVoiceChannelService : INService
var game = gUser.Activities.FirstOrDefault()?.Name;
if (oldState.VoiceChannel == newState.VoiceChannel ||
newState.VoiceChannel is null)
if (oldState.VoiceChannel == newState.VoiceChannel || newState.VoiceChannel is null)
return;
if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
string.IsNullOrWhiteSpace(game))
if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id) || string.IsNullOrWhiteSpace(game))
return;
await TriggerGvc(gUser, game);
@@ -112,8 +106,7 @@ public class GameVoiceChannelService : INService
return;
game = game.TrimTo(50).ToLowerInvariant();
var vch = gUser.Guild.VoiceChannels
.FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
var vch = gUser.Guild.VoiceChannels.FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
if (vch is null)
return;
@@ -121,4 +114,4 @@ public class GameVoiceChannelService : INService
await Task.Delay(1000);
await gUser.ModifyAsync(gu => gu.Channel = vch);
}
}
}

View File

@@ -1,6 +1,6 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -12,11 +12,10 @@ public class GuildTimezoneService : INService
public GuildTimezoneService(DiscordSocketClient client, Bot bot, DbService db)
{
_timezones = bot.AllGuildConfigs
.Select(GetTimzezoneTuple)
.Where(x => x.Timezone != null)
.ToDictionary(x => x.GuildId, x => x.Timezone)
.ToConcurrent();
_timezones = bot.AllGuildConfigs.Select(GetTimzezoneTuple)
.Where(x => x.Timezone != null)
.ToDictionary(x => x.GuildId, x => x.Timezone)
.ToConcurrent();
var curUser = client.CurrentUser;
if (curUser != null)
@@ -48,6 +47,7 @@ public class GuildTimezoneService : INService
{
tz = null;
}
return (x.GuildId, Timezone: tz);
}
@@ -74,4 +74,4 @@ public class GuildTimezoneService : INService
public TimeZoneInfo GetTimeZoneOrUtc(ulong guildId)
=> GetTimeZoneOrDefault(guildId) ?? TimeZoneInfo.Utc;
}
}

View File

@@ -1,25 +1,25 @@
#nullable disable
using System.Net;
using System.Threading.Channels;
using LinqToDB;
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Common.ModuleBehaviors;
using System.Net;
using System.Threading.Channels;
namespace NadekoBot.Modules.Administration.Services;
public sealed class ImageOnlyChannelService : IEarlyBehavior
{
public int Priority { get; } = 0;
private readonly IMemoryCache _ticketCache;
private readonly DiscordSocketClient _client;
private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _enabledOn;
private readonly Channel<IUserMessage> _deleteQueue = Channel.CreateBounded<IUserMessage>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false,
});
private readonly Channel<IUserMessage> _deleteQueue = Channel.CreateBounded<IUserMessage>(
new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.DropOldest, SingleReader = true, SingleWriter = false
});
public ImageOnlyChannelService(IMemoryCache ticketCache, DiscordSocketClient client, DbService db)
@@ -29,14 +29,13 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
_db = db;
var uow = _db.GetDbContext();
_enabledOn = uow.ImageOnlyChannels
.ToList()
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(x => x.ChannelId)))
.ToConcurrent();
_enabledOn = uow.ImageOnlyChannels.ToList()
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(x => x.ChannelId)))
.ToConcurrent();
_ = Task.Run(DeleteQueueRunner);
_client.ChannelDestroyed += ClientOnChannelDestroyed;
}
@@ -73,25 +72,19 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
{
var newState = false;
using var uow = _db.GetDbContext();
if (forceDisable
|| (_enabledOn.TryGetValue(guildId, out var channels)
&& channels.TryRemove(channelId)))
if (forceDisable || (_enabledOn.TryGetValue(guildId, out var channels) && channels.TryRemove(channelId)))
{
uow.ImageOnlyChannels.Delete(x => x.ChannelId == channelId);
}
else
{
uow.ImageOnlyChannels.Add(new()
{
GuildId = guildId,
ChannelId = channelId
});
uow.ImageOnlyChannels.Add(new() { GuildId = guildId, ChannelId = channelId });
channels = _enabledOn.GetOrAdd(guildId, new ConcurrentHashSet<ulong>());
channels.Add(channelId);
newState = true;
}
uow.SaveChanges();
return newState;
}
@@ -100,20 +93,19 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
{
if (msg.Channel is not ITextChannel tch)
return false;
if (msg.Attachments.Any(x => x is { Height: > 0, Width: > 0 }))
return false;
if (!_enabledOn.TryGetValue(tch.GuildId, out var chs)
|| !chs.Contains(msg.Channel.Id))
if (!_enabledOn.TryGetValue(tch.GuildId, out var chs) || !chs.Contains(msg.Channel.Id))
return false;
var user = await tch.Guild.GetUserAsync(msg.Author.Id)
?? await _client.Rest.GetGuildUserAsync(tch.GuildId, msg.Author.Id);
if (user is null)
return false;
// ignore owner and admin
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
{
@@ -129,7 +121,8 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
// can't modify channel perms if not admin apparently
if (!botUser.GuildPermissions.ManageGuild)
{
ToggleImageOnlyChannel( tch.GuildId, tch.Id, true);;
ToggleImageOnlyChannel(tch.GuildId, tch.Id, true);
;
return false;
}
@@ -142,16 +135,14 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
msg.Author.Id,
msg.Channel.Id);
}
try
{
await _deleteQueue.Writer.WriteAsync(msg);
}
catch (Exception ex)
{
Log.Error(ex, "Error deleting message {MessageId} in image-only channel {ChannelId}.",
msg.Id,
tch.Id);
Log.Error(ex, "Error deleting message {MessageId} in image-only channel {ChannelId}.", msg.Id, tch.Id);
}
return true;
@@ -159,18 +150,17 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
private bool AddUserTicket(ulong guildId, ulong userId)
{
var old = _ticketCache.GetOrCreate($"{guildId}_{userId}", entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);
return 0;
});
var old = _ticketCache.GetOrCreate($"{guildId}_{userId}",
entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);
return 0;
});
_ticketCache.Set($"{guildId}_{userId}", ++old);
// if this is the third time that the user posts a
// non image in an image-only channel on this server
return old > 2;
}
public int Priority { get; } = 0;
}
}

View File

@@ -1,9 +1,9 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Modules.Administration.Common;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -15,7 +15,7 @@ public interface ILogCommandService
LogSetting GetGuildLogSettings(ulong guildId);
bool Log(ulong guildId, ulong? channelId, LogType type);
}
public sealed class DummyLogCommandService : ILogCommandService
{
public void AddDeleteIgnore(ulong xId)
@@ -34,14 +34,13 @@ public sealed class DummyLogCommandService : ILogCommandService
public bool Log(ulong guildId, ulong? channelId, LogType type)
=> false;
}
public sealed class LogCommandService : ILogCommandService
{
private readonly DiscordSocketClient _client;
public ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
private ConcurrentDictionary<ITextChannel, List<string>> PresenceUpdates { get; } = new();
private readonly DiscordSocketClient _client;
private readonly Timer _timerReference;
private readonly IBotStrings _strings;
@@ -51,13 +50,19 @@ public sealed class LogCommandService : ILogCommandService
private readonly GuildTimezoneService _tz;
private readonly IEmbedBuilderService _eb;
private readonly IMemoryCache _memoryCache;
private readonly Timer _clearTimer;
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
public LogCommandService(DiscordSocketClient client, IBotStrings strings,
DbService db, MuteService mute, ProtectionService prot, GuildTimezoneService tz,
IMemoryCache memoryCache, IEmbedBuilderService eb)
public LogCommandService(
DiscordSocketClient client,
IBotStrings strings,
DbService db,
MuteService mute,
ProtectionService prot,
GuildTimezoneService tz,
IMemoryCache memoryCache,
IEmbedBuilderService eb)
{
_client = client;
_memoryCache = memoryCache;
@@ -69,41 +74,41 @@ public sealed class LogCommandService : ILogCommandService
_tz = tz;
#if !GLOBAL_NADEKO
using (var uow = db.GetDbContext())
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow
.LogSettings
.AsQueryable()
.AsNoTracking()
.Where(x => guildIds.Contains(x.GuildId))
.Include(ls => ls.LogIgnores)
.ToList();
var configs = uow.LogSettings.AsQueryable()
.AsNoTracking()
.Where(x => guildIds.Contains(x.GuildId))
.Include(ls => ls.LogIgnores)
.ToList();
GuildLogSettings = configs
.ToDictionary(ls => ls.GuildId)
.ToConcurrent();
GuildLogSettings = configs.ToDictionary(ls => ls.GuildId).ToConcurrent();
}
_timerReference = new(async state =>
{
var keys = PresenceUpdates.Keys.ToList();
await keys.Select(key =>
{
if (!((SocketGuild) key.Guild).CurrentUser.GetPermissions(key).SendMessages)
return Task.CompletedTask;
if (PresenceUpdates.TryRemove(key, out var msgs))
{
var title = GetText(key.Guild, strs.presence_updates);
var desc = string.Join(Environment.NewLine, msgs);
return key.SendConfirmAsync(_eb, title, desc.TrimTo(2048));
}
var keys = PresenceUpdates.Keys.ToList();
return Task.CompletedTask;
}).WhenAll();
}, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
await keys.Select(key =>
{
if (!((SocketGuild)key.Guild).CurrentUser.GetPermissions(key).SendMessages)
return Task.CompletedTask;
if (PresenceUpdates.TryRemove(key, out var msgs))
{
var title = GetText(key.Guild, strs.presence_updates);
var desc = string.Join(Environment.NewLine, msgs);
return key.SendConfirmAsync(_eb, title, desc.TrimTo(2048));
}
return Task.CompletedTask;
})
.WhenAll();
},
null,
TimeSpan.FromSeconds(15),
TimeSpan.FromSeconds(15));
//_client.MessageReceived += _client_MessageReceived;
_client.MessageUpdated += _client_MessageUpdated;
@@ -128,10 +133,13 @@ public sealed class LogCommandService : ILogCommandService
_prot.OnAntiProtectionTriggered += TriggeredAntiProtection;
_clearTimer = new(_ =>
{
_ignoreMessageIds.Clear();
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
{
_ignoreMessageIds.Clear();
},
null,
TimeSpan.FromHours(1),
TimeSpan.FromHours(1));
#endif
}
@@ -150,12 +158,11 @@ public sealed class LogCommandService : ILogCommandService
using (var uow = _db.GetDbContext())
{
var logSetting = uow.LogSettingsFor(gid);
removed = logSetting.LogIgnores
.RemoveAll(x => x.ItemType == itemType && itemId == x.LogItemId);
removed = logSetting.LogIgnores.RemoveAll(x => x.ItemType == itemType && itemId == x.LogItemId);
if (removed == 0)
{
var toAdd = new IgnoredLogItem { LogItemId = itemId, ItemType = itemType};
var toAdd = new IgnoredLogItem { LogItemId = itemId, ItemType = itemType };
logSetting.LogIgnores.Add(toAdd);
}
@@ -165,9 +172,9 @@ public sealed class LogCommandService : ILogCommandService
return removed > 0;
}
private string GetText(IGuild guild, LocStr str) =>
_strings.GetText(str, guild.Id);
private string GetText(IGuild guild, LocStr str)
=> _strings.GetText(str, guild.Id);
private string PrettyCurrentTime(IGuild g)
{
@@ -190,23 +197,12 @@ public sealed class LogCommandService : ILogCommandService
{
await using var uow = _db.GetDbContext();
var logSetting = uow.LogSettingsFor(guildId);
logSetting.LogOtherId =
logSetting.MessageUpdatedId =
logSetting.MessageDeletedId =
logSetting.UserJoinedId =
logSetting.UserLeftId =
logSetting.UserBannedId =
logSetting.UserUnbannedId =
logSetting.UserUpdatedId =
logSetting.ChannelCreatedId =
logSetting.ChannelDestroyedId =
logSetting.ChannelUpdatedId =
logSetting.LogUserPresenceId =
logSetting.LogVoicePresenceId =
logSetting.UserMutedId =
logSetting.LogVoicePresenceTTSId =
value ? channelId : null;
logSetting.LogOtherId = logSetting.MessageUpdatedId = logSetting.MessageDeletedId = logSetting.UserJoinedId =
logSetting.UserLeftId = logSetting.UserBannedId = logSetting.UserUnbannedId = logSetting.UserUpdatedId =
logSetting.ChannelCreatedId = logSetting.ChannelDestroyedId = logSetting.ChannelUpdatedId =
logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = logSetting.UserMutedId =
logSetting.LogVoicePresenceTTSId = value ? channelId : null;
;
await uow.SaveChangesAsync();
GuildLogSettings.AddOrUpdate(guildId, id => logSetting, (id, old) => logSetting);
@@ -223,13 +219,11 @@ public sealed class LogCommandService : ILogCommandService
var g = after.Guild;
if (!GuildLogSettings.TryGetValue(g.Id, out var logSetting)
|| logSetting.UserUpdatedId is null)
if (!GuildLogSettings.TryGetValue(g.Id, out var logSetting) || logSetting.UserUpdatedId is null)
return;
ITextChannel logChannel;
if ((logChannel =
await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) is null)
if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) is null)
return;
var embed = _eb.Create();
@@ -237,18 +231,18 @@ public sealed class LogCommandService : ILogCommandService
if (before.Username != after.Username)
{
embed.WithTitle("👥 " + GetText(g, strs.username_changed))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.AddField("Old Name", $"{before.Username}", true)
.AddField("New Name", $"{after.Username}", true)
.WithFooter(CurrentTime(g))
.WithOkColor();
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.AddField("Old Name", $"{before.Username}", true)
.AddField("New Name", $"{after.Username}", true)
.WithFooter(CurrentTime(g))
.WithOkColor();
}
else if (before.AvatarId != after.AvatarId)
{
embed.WithTitle("👥" + GetText(g, strs.avatar_changed))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithFooter(CurrentTime(g))
.WithOkColor();
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithFooter(CurrentTime(g))
.WithOkColor();
var bav = before.RealAvatarUrl();
if (bav != null && bav.IsAbsoluteUri)
@@ -273,7 +267,7 @@ public sealed class LogCommandService : ILogCommandService
return Task.CompletedTask;
}
public bool Log(ulong gid, ulong? cid, LogType type/*, string options*/)
public bool Log(ulong gid, ulong? cid, LogType type /*, string options*/)
{
ulong? channelId = null;
using (var uow = _db.GetDbContext())
@@ -314,19 +308,16 @@ public sealed class LogCommandService : ILogCommandService
channelId = logSetting.ChannelCreatedId = logSetting.ChannelCreatedId is null ? cid : default;
break;
case LogType.ChannelDestroyed:
channelId = logSetting.ChannelDestroyedId =
logSetting.ChannelDestroyedId is null ? cid : default;
channelId = logSetting.ChannelDestroyedId = logSetting.ChannelDestroyedId is null ? cid : default;
break;
case LogType.ChannelUpdated:
channelId = logSetting.ChannelUpdatedId = logSetting.ChannelUpdatedId is null ? cid : default;
break;
case LogType.UserPresence:
channelId = logSetting.LogUserPresenceId =
logSetting.LogUserPresenceId is null ? cid : default;
channelId = logSetting.LogUserPresenceId = logSetting.LogUserPresenceId is null ? cid : default;
break;
case LogType.VoicePresence:
channelId = logSetting.LogVoicePresenceId =
logSetting.LogVoicePresenceId is null ? cid : default;
channelId = logSetting.LogVoicePresenceId = logSetting.LogVoicePresenceId is null ? cid : default;
break;
case LogType.VoicePresenceTTS:
channelId = logSetting.LogVoicePresenceTTSId =
@@ -365,17 +356,11 @@ public sealed class LogCommandService : ILogCommandService
var str = string.Empty;
if (beforeVch?.Guild == afterVch?.Guild)
{
str = GetText(logChannel.Guild, strs.log_vc_moved(usr.Username, beforeVch?.Name, afterVch?.Name));
}
else if (beforeVch is null)
{
str = GetText(logChannel.Guild, strs.log_vc_joined(usr.Username, afterVch.Name));
}
else if (afterVch is null)
{
str = GetText(logChannel.Guild, strs.log_vc_left(usr.Username, beforeVch.Name));
}
var toDelete = await logChannel.SendMessageAsync(str, true);
toDelete.DeleteAfter(5);
@@ -388,14 +373,17 @@ public sealed class LogCommandService : ILogCommandService
return Task.CompletedTask;
}
private void MuteCommands_UserMuted(IGuildUser usr, IUser mod, MuteType muteType, string reason)
private void MuteCommands_UserMuted(
IGuildUser usr,
IUser mod,
MuteType muteType,
string reason)
{
var _ = Task.Run(async () =>
{
try
{
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting)
|| logSetting.UserMutedId is null)
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting) || logSetting.UserMutedId is null)
return;
ITextChannel logChannel;
@@ -412,15 +400,16 @@ public sealed class LogCommandService : ILogCommandService
mutes = "🔇 " + GetText(logChannel.Guild, strs.xmuted_text(mutedLocalized, mod.ToString()));
break;
case MuteType.All:
mutes = "🔇 " + GetText(logChannel.Guild, strs.xmuted_text_and_voice(mutedLocalized,
mod.ToString()));
mutes = "🔇 "
+ GetText(logChannel.Guild, strs.xmuted_text_and_voice(mutedLocalized, mod.ToString()));
break;
}
var embed = _eb.Create().WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(CurrentTime(usr.Guild))
.WithOkColor();
var embed = _eb.Create()
.WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(CurrentTime(usr.Guild))
.WithOkColor();
await logChannel.EmbedAsync(embed);
}
@@ -431,14 +420,17 @@ public sealed class LogCommandService : ILogCommandService
});
}
private void MuteCommands_UserUnmuted(IGuildUser usr, IUser mod, MuteType muteType, string reason)
private void MuteCommands_UserUnmuted(
IGuildUser usr,
IUser mod,
MuteType muteType,
string reason)
{
var _ = Task.Run(async () =>
{
try
{
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting)
|| logSetting.UserMutedId is null)
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting) || logSetting.UserMutedId is null)
return;
ITextChannel logChannel;
@@ -456,15 +448,17 @@ public sealed class LogCommandService : ILogCommandService
mutes = "🔊 " + GetText(logChannel.Guild, strs.xmuted_text(unmutedLocalized, mod.ToString()));
break;
case MuteType.All:
mutes = "🔊 " + GetText(logChannel.Guild, strs.xmuted_text_and_voice(unmutedLocalized,
mod.ToString()));
mutes = "🔊 "
+ GetText(logChannel.Guild,
strs.xmuted_text_and_voice(unmutedLocalized, mod.ToString()));
break;
}
var embed = _eb.Create().WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter($"{CurrentTime(usr.Guild)}")
.WithOkColor();
var embed = _eb.Create()
.WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter($"{CurrentTime(usr.Guild)}")
.WithOkColor();
if (!string.IsNullOrWhiteSpace(reason))
embed.WithDescription(reason);
@@ -478,8 +472,7 @@ public sealed class LogCommandService : ILogCommandService
});
}
public Task TriggeredAntiProtection(PunishmentAction action, ProtectionType protection,
params IGuildUser[] users)
public Task TriggeredAntiProtection(PunishmentAction action, ProtectionType protection, params IGuildUser[] users)
{
var _ = Task.Run(async () =>
{
@@ -515,11 +508,12 @@ public sealed class LogCommandService : ILogCommandService
break;
}
var embed = _eb.Create().WithAuthor($"🛡 Anti-{protection}")
.WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment)
.WithDescription(string.Join("\n", users.Select(u => u.ToString())))
.WithFooter(CurrentTime(logChannel.Guild))
.WithOkColor();
var embed = _eb.Create()
.WithAuthor($"🛡 Anti-{protection}")
.WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment)
.WithDescription(string.Join("\n", users.Select(u => u.ToString())))
.WithFooter(CurrentTime(logChannel.Guild))
.WithOkColor();
await logChannel.EmbedAsync(embed);
}
@@ -533,13 +527,11 @@ public sealed class LogCommandService : ILogCommandService
private string GetRoleDeletedKey(ulong roleId)
=> $"role_deleted_{roleId}";
private Task _client_RoleDeleted(SocketRole socketRole)
{
Serilog.Log.Information("Role deleted {RoleId}", socketRole.Id);
_memoryCache.Set(GetRoleDeletedKey(socketRole.Id),
true,
TimeSpan.FromMinutes(5));
_memoryCache.Set(GetRoleDeletedKey(socketRole.Id), true, TimeSpan.FromMinutes(5));
return Task.CompletedTask;
}
@@ -559,25 +551,27 @@ public sealed class LogCommandService : ILogCommandService
if (before is null)
return;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out var logSetting)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.User))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
if (logSetting.UserUpdatedId != null &&
(logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) != null)
if (logSetting.UserUpdatedId != null
&& (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) != null)
{
var embed = _eb.Create().WithOkColor()
.WithFooter(CurrentTime(before.Guild))
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
var embed = _eb.Create()
.WithOkColor()
.WithFooter(CurrentTime(before.Guild))
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
if (before.Nickname != after.Nickname)
{
embed.WithAuthor("👥 " + GetText(logChannel.Guild, strs.nick_change))
.AddField(GetText(logChannel.Guild, strs.old_nick)
, $"{before.Nickname}#{before.Discriminator}")
.AddField(GetText(logChannel.Guild, strs.new_nick)
, $"{after.Nickname}#{after.Discriminator}");
.AddField(GetText(logChannel.Guild, strs.old_nick),
$"{before.Nickname}#{before.Discriminator}")
.AddField(GetText(logChannel.Guild, strs.new_nick),
$"{after.Nickname}#{after.Discriminator}");
await logChannel.EmbedAsync(embed);
}
@@ -587,22 +581,21 @@ public sealed class LogCommandService : ILogCommandService
{
var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => r.Name);
embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_add))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
await logChannel.EmbedAsync(embed);
}
else if (before.Roles.Count > after.Roles.Count)
{
await Task.Delay(1000);
var diffRoles = before.Roles
.Where(r => !after.Roles.Contains(r) && !IsRoleDeleted(r.Id))
.Select(r => r.Name)
.ToList();
var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r) && !IsRoleDeleted(r.Id))
.Select(r => r.Name)
.ToList();
if (diffRoles.Any())
{
embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_rem))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
await logChannel.EmbedAsync(embed);
}
@@ -611,17 +604,20 @@ public sealed class LogCommandService : ILogCommandService
}
logChannel = null;
if (!before.IsBot && logSetting.LogUserPresenceId != null && (logChannel =
await TryGetLogChannel(before.Guild, logSetting, LogType.UserPresence)) != null)
if (!before.IsBot
&& logSetting.LogUserPresenceId != null
&& (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserPresence)) != null)
{
if (before.Status != after.Status)
{
var str = "🎭" + Format.Code(PrettyCurrentTime(after.Guild)) +
GetText(logChannel.Guild, strs.user_status_change(
"👤" + Format.Bold(after.Username),
Format.Bold(after.Status.ToString())));
var str = "🎭"
+ Format.Code(PrettyCurrentTime(after.Guild))
+ GetText(logChannel.Guild,
strs.user_status_change("👤" + Format.Bold(after.Username),
Format.Bold(after.Status.ToString())));
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>() {str}, (id, list) =>
new List<string> { str },
(id, list) =>
{
list.Add(str);
return list;
@@ -632,7 +628,8 @@ public sealed class LogCommandService : ILogCommandService
var str =
$"👾`{PrettyCurrentTime(after.Guild)}`👤__**{after.Username}**__ is now playing **{after.Activities.FirstOrDefault()?.Name ?? "-"}**.";
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>() {str}, (id, list) =>
new List<string> { str },
(id, list) =>
{
list.Add(str);
return list;
@@ -657,36 +654,32 @@ public sealed class LogCommandService : ILogCommandService
if (cbefore is not IGuildChannel before)
return;
var after = (IGuildChannel) cafter;
var after = (IGuildChannel)cafter;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out var logSetting)
|| logSetting.ChannelUpdatedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.Channel))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) is null)
return;
var embed = _eb.Create().WithOkColor()
.WithFooter(CurrentTime(before.Guild));
var embed = _eb.Create().WithOkColor().WithFooter(CurrentTime(before.Guild));
var beforeTextChannel = cbefore as ITextChannel;
var afterTextChannel = cafter as ITextChannel;
if (before.Name != after.Name)
{
embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_name_change))
.WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.ch_old_name), before.Name);
}
.WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.ch_old_name), before.Name);
else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
{
embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_topic_change))
.WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.old_topic) , beforeTextChannel?.Topic ?? "-")
.AddField(GetText(logChannel.Guild, strs.new_topic), afterTextChannel?.Topic ?? "-");
}
.WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.old_topic), beforeTextChannel?.Topic ?? "-")
.AddField(GetText(logChannel.Guild, strs.new_topic), afterTextChannel?.Topic ?? "-");
else
return;
@@ -711,7 +704,8 @@ public sealed class LogCommandService : ILogCommandService
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out var logSetting)
|| logSetting.ChannelDestroyedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == ch.Id && ilc.ItemType == IgnoredItemType.Channel))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == ch.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
@@ -719,17 +713,15 @@ public sealed class LogCommandService : ILogCommandService
return;
string title;
if (ch is IVoiceChannel)
{
title = GetText(logChannel.Guild, strs.voice_chan_destroyed);
}
else
title = GetText(logChannel.Guild, strs.text_chan_destroyed);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch
{
@@ -757,17 +749,15 @@ public sealed class LogCommandService : ILogCommandService
return;
string title;
if (ch is IVoiceChannel)
{
title = GetText(logChannel.Guild, strs.voice_chan_created);
}
else
title = GetText(logChannel.Guild, strs.text_chan_created);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch (Exception)
{
@@ -794,7 +784,8 @@ public sealed class LogCommandService : ILogCommandService
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting)
|| logSetting.LogVoicePresenceId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == iusr.Id && ilc.ItemType == IgnoredItemType.User))
|| logSetting.LogIgnores.Any(
ilc => ilc.LogItemId == iusr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -803,32 +794,33 @@ public sealed class LogCommandService : ILogCommandService
string str = null;
if (beforeVch?.Guild == afterVch?.Guild)
{
str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild,
strs.user_vmoved(
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? "")));
}
str = "🎙"
+ Format.Code(PrettyCurrentTime(usr.Guild))
+ GetText(logChannel.Guild,
strs.user_vmoved("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch?.Name ?? ""),
Format.Bold(afterVch?.Name ?? "")));
else if (beforeVch is null)
{
str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild,
strs.user_vjoined(
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(afterVch.Name ?? "")));
}
str = "🎙"
+ Format.Code(PrettyCurrentTime(usr.Guild))
+ GetText(logChannel.Guild,
strs.user_vjoined("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(afterVch.Name ?? "")));
else if (afterVch is null)
{
str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild,
strs.user_vleft("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch.Name ?? "")));
}
str = "🎙"
+ Format.Code(PrettyCurrentTime(usr.Guild))
+ GetText(logChannel.Guild,
strs.user_vleft("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch.Name ?? "")));
if (!string.IsNullOrWhiteSpace(str))
PresenceUpdates.AddOrUpdate(logChannel, new List<string>() {str}, (id, list) =>
{
list.Add(str);
return list;
});
PresenceUpdates.AddOrUpdate(logChannel,
new List<string> { str },
(id, list) =>
{
list.Add(str);
return list;
});
}
catch
{
@@ -846,18 +838,19 @@ public sealed class LogCommandService : ILogCommandService
{
if (!GuildLogSettings.TryGetValue(guild.Id, out var logSetting)
|| logSetting.UserLeftId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserLeft)) is null)
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
.WithOkColor()
.WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -878,8 +871,7 @@ public sealed class LogCommandService : ILogCommandService
{
try
{
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting)
|| logSetting.UserJoinedId is null)
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out var logSetting) || logSetting.UserJoinedId is null)
return;
ITextChannel logChannel;
@@ -887,17 +879,17 @@ public sealed class LogCommandService : ILogCommandService
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined))
.WithDescription($"{usr.Mention} `{usr}`")
.AddField("Id", usr.Id.ToString())
.AddField(GetText(logChannel.Guild, strs.joined_server),
$"{usr.JoinedAt?.ToString("dd.MM.yyyy HH:mm" ?? "?")}",
true)
.AddField(GetText(logChannel.Guild, strs.joined_discord),
$"{usr.CreatedAt:dd.MM.yyyy HH:mm}",
true)
.WithFooter(CurrentTime(usr.Guild));
.WithOkColor()
.WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined))
.WithDescription($"{usr.Mention} `{usr}`")
.AddField("Id", usr.Id.ToString())
.AddField(GetText(logChannel.Guild, strs.joined_server),
$"{usr.JoinedAt?.ToString("dd.MM.yyyy HH:mm" ?? "?")}",
true)
.AddField(GetText(logChannel.Guild, strs.joined_discord),
$"{usr.CreatedAt:dd.MM.yyyy HH:mm}",
true)
.WithFooter(CurrentTime(usr.Guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -920,18 +912,19 @@ public sealed class LogCommandService : ILogCommandService
{
if (!GuildLogSettings.TryGetValue(guild.Id, out var logSetting)
|| logSetting.UserUnbannedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) is null)
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
.WithOkColor()
.WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -954,20 +947,19 @@ public sealed class LogCommandService : ILogCommandService
{
if (!GuildLogSettings.TryGetValue(guild.Id, out var logSetting)
|| logSetting.UserBannedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
if ((logChannel =
await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) ==
null)
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null)
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
.WithOkColor()
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
.WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild));
var avatarUrl = usr.GetAvatarUrl();
@@ -1002,27 +994,28 @@ public sealed class LogCommandService : ILogCommandService
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out var logSetting)
|| logSetting.MessageDeletedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) is null || logChannel.Id == msg.Id)
if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) is null
|| logChannel.Id == msg.Id)
return;
var resolvedMessage = msg.Resolve(userHandling: TagHandling.FullName);
var resolvedMessage = msg.Resolve(TagHandling.FullName);
var embed = _eb.Create()
.WithOkColor()
.WithTitle("🗑 " + GetText(logChannel.Guild, strs.msg_del(((ITextChannel) msg.Channel).Name)))
.WithDescription(msg.Author.ToString())
.AddField(GetText(logChannel.Guild, strs.content),
string.IsNullOrWhiteSpace(resolvedMessage) ? "-" : resolvedMessage,
false)
.AddField("Id", msg.Id.ToString(), false)
.WithFooter(CurrentTime(channel.Guild));
.WithOkColor()
.WithTitle("🗑 "
+ GetText(logChannel.Guild, strs.msg_del(((ITextChannel)msg.Channel).Name)))
.WithDescription(msg.Author.ToString())
.AddField(GetText(logChannel.Guild, strs.content),
string.IsNullOrWhiteSpace(resolvedMessage) ? "-" : resolvedMessage)
.AddField("Id", msg.Id.ToString())
.WithFooter(CurrentTime(channel.Guild));
if (msg.Attachments.Any())
embed.AddField(GetText(logChannel.Guild, strs.attachments),
string.Join(", ", msg.Attachments.Select(a => a.Url)),
false);
string.Join(", ", msg.Attachments.Select(a => a.Url)));
await logChannel.EmbedAsync(embed);
}
@@ -1034,7 +1027,9 @@ public sealed class LogCommandService : ILogCommandService
return Task.CompletedTask;
}
private Task _client_MessageUpdated(Cacheable<IMessage, ulong> optmsg, SocketMessage imsg2,
private Task _client_MessageUpdated(
Cacheable<IMessage, ulong> optmsg,
SocketMessage imsg2,
ISocketMessageChannel ch)
{
var _ = Task.Run(async () =>
@@ -1058,30 +1053,29 @@ public sealed class LogCommandService : ILogCommandService
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out var logSetting)
|| logSetting.MessageUpdatedId is null
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
|| logSetting.LogIgnores.Any(ilc
=> ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) is null || logChannel.Id == after.Channel.Id)
if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) is null
|| logChannel.Id == after.Channel.Id)
return;
var embed = _eb.Create()
.WithOkColor()
.WithTitle("📝 " + GetText(logChannel.Guild, strs.msg_update(((ITextChannel)after.Channel).Name)))
.WithDescription(after.Author.ToString())
.AddField(GetText(logChannel.Guild, strs.old_msg),
string.IsNullOrWhiteSpace(before.Content)
? "-"
: before.Resolve(userHandling: TagHandling.FullName),
false)
.AddField(
GetText(logChannel.Guild, strs.new_msg),
string.IsNullOrWhiteSpace(after.Content)
? "-"
: after.Resolve(userHandling: TagHandling.FullName),
false)
.AddField("Id", after.Id.ToString(), false)
.WithFooter(CurrentTime(channel.Guild));
.WithOkColor()
.WithTitle("📝 "
+ GetText(logChannel.Guild,
strs.msg_update(((ITextChannel)after.Channel).Name)))
.WithDescription(after.Author.ToString())
.AddField(GetText(logChannel.Guild, strs.old_msg),
string.IsNullOrWhiteSpace(before.Content)
? "-"
: before.Resolve(TagHandling.FullName))
.AddField(GetText(logChannel.Guild, strs.new_msg),
string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(TagHandling.FullName))
.AddField("Id", after.Id.ToString())
.WithFooter(CurrentTime(channel.Guild));
await logChannel.EmbedAsync(embed);
}
@@ -1158,8 +1152,8 @@ public sealed class LogCommandService : ILogCommandService
UnsetLogSetting(guild.Id, logChannelType);
return null;
}
else
return channel;
return channel;
}
private void UnsetLogSetting(ulong guildId, LogType logChannelType)
@@ -1218,4 +1212,4 @@ public sealed class LogCommandService : ILogCommandService
GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting);
uow.SaveChanges();
}
}
}

View File

@@ -1,7 +1,7 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -14,17 +14,19 @@ public enum MuteType
public class MuteService : INService
{
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
public enum TimerType { Mute, Ban, AddRole }
public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> Un_Timers { get; } = new();
private static readonly OverwritePermissions denyOverwrite = new(addReactions: PermValue.Deny,
sendMessages: PermValue.Deny,
attachFiles: PermValue.Deny);
public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { };
public event Action<IGuildUser, IUser, MuteType, string> UserUnmuted = delegate { };
private static readonly OverwritePermissions denyOverwrite =
new(addReactions: PermValue.Deny, sendMessages: PermValue.Deny,
attachFiles: PermValue.Deny);
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> Un_Timers { get; } = new();
private readonly DiscordSocketClient _client;
private readonly DbService _db;
@@ -39,24 +41,21 @@ public class MuteService : INService
using (var uow = db.GetDbContext())
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow.Set<GuildConfig>().AsQueryable()
.Include(x => x.MutedUsers)
.Include(x => x.UnbanTimer)
.Include(x => x.UnmuteTimers)
.Include(x => x.UnroleTimer)
.Where(x => guildIds.Contains(x.GuildId))
.ToList();
var configs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.MutedUsers)
.Include(x => x.UnbanTimer)
.Include(x => x.UnmuteTimers)
.Include(x => x.UnroleTimer)
.Where(x => guildIds.Contains(x.GuildId))
.ToList();
GuildMuteRoles = configs
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
GuildMuteRoles = configs.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)
.ToConcurrent();
MutedUsers = new(configs
.ToDictionary(
k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
));
MutedUsers = new(configs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))));
var max = TimeSpan.FromDays(49);
@@ -118,30 +117,40 @@ public class MuteService : INService
UserUnmuted += OnUserUnmuted;
}
private void OnUserMuted(IGuildUser user, IUser mod, MuteType type, string reason)
private void OnUserMuted(
IGuildUser user,
IUser mod,
MuteType type,
string reason)
{
if (string.IsNullOrWhiteSpace(reason))
return;
var _ = Task.Run(() => user.SendMessageAsync(embed: _eb.Create()
.WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason)
.Build()));
.WithDescription(
$"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason)
.Build()));
}
private void OnUserUnmuted(IGuildUser user, IUser mod, MuteType type, string reason)
private void OnUserUnmuted(
IGuildUser user,
IUser mod,
MuteType type,
string reason)
{
if (string.IsNullOrWhiteSpace(reason))
return;
var _ = Task.Run(() => user.SendMessageAsync(embed: _eb.Create()
.WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason)
.Build()));
.WithDescription(
$"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString())
.AddField("Reason", reason)
.Build()));
}
private Task Client_UserJoined(IGuildUser usr)
@@ -158,6 +167,7 @@ public class MuteService : INService
{
Log.Warning(ex, "Error in MuteService UserJoined event");
}
return Task.CompletedTask;
}
@@ -170,11 +180,17 @@ public class MuteService : INService
await uow.SaveChangesAsync();
}
public async Task MuteUser(IGuildUser usr, IUser mod, MuteType type = MuteType.All, string reason = "")
public async Task MuteUser(
IGuildUser usr,
IUser mod,
MuteType type = MuteType.All,
string reason = "")
{
if (type == MuteType.All)
{
try { await usr.ModifyAsync(x => x.Mute = true); } catch { }
try { await usr.ModifyAsync(x => x.Mute = true); }
catch { }
var muteRole = await GetMuteRole(usr.Guild);
if (!usr.RoleIds.Contains(muteRole.Id))
await usr.AddRoleAsync(muteRole);
@@ -182,12 +198,8 @@ public class MuteService : INService
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(usr.Guild.Id,
set => set.Include(gc => gc.MutedUsers)
.Include(gc => gc.UnmuteTimers));
config.MutedUsers.Add(new()
{
UserId = usr.Id
});
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers));
config.MutedUsers.Add(new() { UserId = usr.Id });
if (MutedUsers.TryGetValue(usr.Guild.Id, out var muted))
muted.Add(usr.Id);
@@ -195,6 +207,7 @@ public class MuteService : INService
await uow.SaveChangesAsync();
}
UserMuted(usr, mod, MuteType.All, reason);
}
else if (type == MuteType.Voice)
@@ -213,7 +226,12 @@ public class MuteService : INService
}
}
public async Task UnmuteUser(ulong guildId, ulong usrId, IUser mod, MuteType type = MuteType.All, string reason = "")
public async Task UnmuteUser(
ulong guildId,
ulong usrId,
IUser mod,
MuteType type = MuteType.All,
string reason = "")
{
var usr = _client.GetGuild(guildId)?.GetUser(usrId);
if (type == MuteType.All)
@@ -221,17 +239,11 @@ public class MuteService : INService
StopTimer(guildId, usrId, TimerType.Mute);
await using (var uow = _db.GetDbContext())
{
var config = uow.GuildConfigsForId(guildId, set => set.Include(gc => gc.MutedUsers)
.Include(gc => gc.UnmuteTimers));
var match = new MutedUserId()
{
UserId = usrId
};
var config = uow.GuildConfigsForId(guildId,
set => set.Include(gc => gc.MutedUsers).Include(gc => gc.UnmuteTimers));
var match = new MutedUserId { UserId = usrId };
var toRemove = config.MutedUsers.FirstOrDefault(x => x.Equals(match));
if (toRemove != null)
{
uow.Remove(toRemove);
}
if (toRemove != null) uow.Remove(toRemove);
if (MutedUsers.TryGetValue(guildId, out var muted))
muted.TryRemove(usrId);
@@ -239,10 +251,18 @@ public class MuteService : INService
await uow.SaveChangesAsync();
}
if (usr != null)
{
try { await usr.ModifyAsync(x => x.Mute = false); } catch { }
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); } catch { /*ignore*/ }
try { await usr.ModifyAsync(x => x.Mute = false); }
catch { }
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)); }
catch
{
/*ignore*/
}
UserUnmuted(usr, mod, MuteType.All, reason);
}
}
@@ -277,20 +297,16 @@ public class MuteService : INService
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
if (muteRole is null)
{
//if it doesn't exist, create it
try { muteRole = await guild.CreateRoleAsync(muteRoleName, isMentionable: false); }
catch
{
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ??
await guild.CreateRoleAsync(defaultMuteRoleName, isMentionable: false);
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName)
?? await guild.CreateRoleAsync(defaultMuteRoleName, isMentionable: false);
}
}
foreach (var toOverwrite in await guild.GetTextChannelsAsync())
{
try
{
if (!toOverwrite.PermissionOverwrites.Any(x => x.TargetId == muteRole.Id
@@ -305,12 +321,16 @@ public class MuteService : INService
{
// ignored
}
}
return muteRole;
}
public async Task TimedMute(IGuildUser user, IUser mod, TimeSpan after, MuteType muteType = MuteType.All, string reason = "")
public async Task TimedMute(
IGuildUser user,
IUser mod,
TimeSpan after,
MuteType muteType = MuteType.All,
string reason = "")
{
await MuteUser(user, mod, muteType, reason); // mute the user. This will also remove any previous unmute timers
await using (var uow = _db.GetDbContext())
@@ -318,8 +338,7 @@ public class MuteService : INService
var config = uow.GuildConfigsForId(user.GuildId, set => set.Include(x => x.UnmuteTimers));
config.UnmuteTimers.Add(new()
{
UserId = user.Id,
UnmuteAt = DateTime.UtcNow + after,
UserId = user.Id, UnmuteAt = DateTime.UtcNow + after
}); // add teh unmute timer to the database
uow.SaveChanges();
}
@@ -327,7 +346,11 @@ public class MuteService : INService
StartUn_Timer(user.GuildId, user.Id, after, TimerType.Mute); // start the timer
}
public async Task TimedBan(IGuild guild, IUser user, TimeSpan after, string reason)
public async Task TimedBan(
IGuild guild,
IUser user,
TimeSpan after,
string reason)
{
await guild.AddBanAsync(user.Id, 0, reason);
await using (var uow = _db.GetDbContext())
@@ -335,8 +358,7 @@ public class MuteService : INService
var config = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.UnbanTimer));
config.UnbanTimer.Add(new()
{
UserId = user.Id,
UnbanAt = DateTime.UtcNow + after,
UserId = user.Id, UnbanAt = DateTime.UtcNow + after
}); // add teh unmute timer to the database
uow.SaveChanges();
}
@@ -344,7 +366,11 @@ public class MuteService : INService
StartUn_Timer(guild.Id, user.Id, after, TimerType.Ban); // start the timer
}
public async Task TimedRole(IGuildUser user, TimeSpan after, string reason, IRole role)
public async Task TimedRole(
IGuildUser user,
TimeSpan after,
string reason,
IRole role)
{
await user.AddRoleAsync(role);
await using (var uow = _db.GetDbContext())
@@ -352,81 +378,78 @@ public class MuteService : INService
var config = uow.GuildConfigsForId(user.GuildId, set => set.Include(x => x.UnroleTimer));
config.UnroleTimer.Add(new()
{
UserId = user.Id,
UnbanAt = DateTime.UtcNow + after,
RoleId = role.Id
UserId = user.Id, UnbanAt = DateTime.UtcNow + after, RoleId = role.Id
}); // add teh unmute timer to the database
uow.SaveChanges();
}
StartUn_Timer(user.GuildId, user.Id, after, TimerType.AddRole, role.Id); // start the timer
}
public enum TimerType { Mute, Ban, AddRole }
public void StartUn_Timer(ulong guildId, ulong userId, TimeSpan after, TimerType type, ulong? roleId = null)
public void StartUn_Timer(
ulong guildId,
ulong userId,
TimeSpan after,
TimerType type,
ulong? roleId = null)
{
//load the unmute timers for this guild
var userUnTimers = Un_Timers.GetOrAdd(guildId, new ConcurrentDictionary<(ulong, TimerType), Timer>());
//unmute timer to be added
var toAdd = new Timer(async _ =>
{
if (type == TimerType.Ban)
{
try
{
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId); // load the guild
if (guild != null)
if (type == TimerType.Ban)
try
{
await guild.RemoveBanAsync(userId);
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId); // load the guild
if (guild != null) await guild.RemoveBanAsync(userId);
}
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't unban user {0} in guild {1}", userId, guildId);
}
}
else if (type == TimerType.AddRole)
{
try
{
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId);
var user = guild?.GetUser(userId);
var role = guild.GetRole(roleId.Value);
if (guild != null && user != null && user.Roles.Contains(role))
catch (Exception ex)
{
await user.RemoveRoleAsync(role);
Log.Warning(ex, "Couldn't unban user {0} in guild {1}", userId, guildId);
}
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't remove role from user {0} in guild {1}", userId, guildId);
}
}
else
{
try
{
// unmute the user, this will also remove the timer from the db
await UnmuteUser(guildId, userId, _client.CurrentUser, reason: "Timed mute expired");
}
catch (Exception ex)
{
RemoveTimerFromDb(guildId, userId, type); // if unmute errored, just remove unmute from db
Log.Warning(ex, "Couldn't unmute user {0} in guild {1}", userId, guildId);
}
}
}, null, after, Timeout.InfiniteTimeSpan);
else if (type == TimerType.AddRole)
try
{
RemoveTimerFromDb(guildId, userId, type);
StopTimer(guildId, userId, type);
var guild = _client.GetGuild(guildId);
var user = guild?.GetUser(userId);
var role = guild.GetRole(roleId.Value);
if (guild != null && user != null && user.Roles.Contains(role))
await user.RemoveRoleAsync(role);
}
catch (Exception ex)
{
Log.Warning(ex, "Couldn't remove role from user {0} in guild {1}", userId, guildId);
}
else
try
{
// unmute the user, this will also remove the timer from the db
await UnmuteUser(guildId, userId, _client.CurrentUser, reason: "Timed mute expired");
}
catch (Exception ex)
{
RemoveTimerFromDb(guildId, userId, type); // if unmute errored, just remove unmute from db
Log.Warning(ex, "Couldn't unmute user {0} in guild {1}", userId, guildId);
}
},
null,
after,
Timeout.InfiniteTimeSpan);
//add it, or stop the old one and add this one
userUnTimers.AddOrUpdate((userId, type), key => toAdd, (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return toAdd;
});
userUnTimers.AddOrUpdate((userId, type),
key => toAdd,
(key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return toAdd;
});
}
public void StopTimer(ulong guildId, ulong userId, TimerType type)
@@ -434,10 +457,7 @@ public class MuteService : INService
if (!Un_Timers.TryGetValue(guildId, out var userTimer))
return;
if (userTimer.TryRemove((userId, type), out var removed))
{
removed.Change(Timeout.Infinite, Timeout.Infinite);
}
if (userTimer.TryRemove((userId, type), out var removed)) removed.Change(Timeout.Infinite, Timeout.Infinite);
}
private void RemoveTimerFromDb(ulong guildId, ulong userId, TimerType type)
@@ -454,10 +474,8 @@ public class MuteService : INService
var config = uow.GuildConfigsForId(guildId, set => set.Include(x => x.UnbanTimer));
toDelete = config.UnbanTimer.FirstOrDefault(x => x.UserId == userId);
}
if (toDelete != null)
{
uow.Remove(toDelete);
}
if (toDelete != null) uow.Remove(toDelete);
uow.SaveChanges();
}
}
}

View File

@@ -1,6 +1,6 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -13,13 +13,13 @@ public sealed class PlayingRotateService : INService
private readonly DbService _db;
private readonly Bot _bot;
private class TimerState
{
public int Index { get; set; }
}
public PlayingRotateService(DiscordSocketClient client, DbService db, Bot bot,
BotConfigService bss, IEnumerable<IPlaceholderProvider> phProviders, SelfService selfService)
public PlayingRotateService(
DiscordSocketClient client,
DbService db,
Bot bot,
BotConfigService bss,
IEnumerable<IPlaceholderProvider> phProviders,
SelfService selfService)
{
_db = db;
_bot = bot;
@@ -28,10 +28,7 @@ public sealed class PlayingRotateService : INService
if (client.ShardId == 0)
{
_rep = new ReplacementBuilder()
.WithClient(client)
.WithProviders(phProviders)
.Build();
_rep = new ReplacementBuilder().WithClient(client).WithProviders(phProviders).Build();
_t = new(RotatingStatuses, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
@@ -41,17 +38,14 @@ public sealed class PlayingRotateService : INService
{
try
{
var state = (TimerState) objState;
var state = (TimerState)objState;
if (!_bss.Data.RotateStatuses) return;
IReadOnlyList<RotatingPlayingStatus> rotatingStatuses;
await using (var uow = _db.GetDbContext())
{
rotatingStatuses = uow.RotatingStatus
.AsNoTracking()
.OrderBy(x => x.Id)
.ToList();
rotatingStatuses = uow.RotatingStatus.AsNoTracking().OrderBy(x => x.Id).ToList();
}
if (rotatingStatuses.Count == 0)
@@ -76,11 +70,7 @@ public sealed class PlayingRotateService : INService
throw new ArgumentOutOfRangeException(nameof(index));
await using var uow = _db.GetDbContext();
var toRemove = await uow.RotatingStatus
.AsQueryable()
.AsNoTracking()
.Skip(index)
.FirstOrDefaultAsync();
var toRemove = await uow.RotatingStatus.AsQueryable().AsNoTracking().Skip(index).FirstOrDefaultAsync();
if (toRemove is null)
return null;
@@ -93,7 +83,7 @@ public sealed class PlayingRotateService : INService
public async Task AddPlaying(ActivityType t, string status)
{
await using var uow = _db.GetDbContext();
var toAdd = new RotatingPlayingStatus {Status = status, Type = t};
var toAdd = new RotatingPlayingStatus { Status = status, Type = t };
uow.Add(toAdd);
await uow.SaveChangesAsync();
}
@@ -110,4 +100,9 @@ public sealed class PlayingRotateService : INService
using var uow = _db.GetDbContext();
return uow.RotatingStatus.AsNoTracking().ToList();
}
}
private class TimerState
{
public int Index { get; set; }
}
}

View File

@@ -1,38 +1,40 @@
#nullable disable
using System.Threading.Channels;
using NadekoBot.Modules.Administration.Common;
using NadekoBot.Services.Database.Models;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Modules.Administration.Common;
using NadekoBot.Services.Database.Models;
using System.Threading.Channels;
namespace NadekoBot.Modules.Administration.Services;
public class ProtectionService : INService
{
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
{
return Task.CompletedTask;
};
private readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds = new();
private readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds = new();
private readonly ConcurrentDictionary<ulong, AntiAltStats> _antiAltGuilds = new();
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered
= delegate { return Task.CompletedTask; };
private readonly DiscordSocketClient _client;
private readonly MuteService _mute;
private readonly DbService _db;
private readonly UserPunishService _punishService;
private readonly Channel<PunishQueueItem> PunishUserQueue =
System.Threading.Channels.Channel.CreateUnbounded<PunishQueueItem>(new()
{
SingleReader = true,
SingleWriter = false
});
public ProtectionService(DiscordSocketClient client, Bot bot,
MuteService mute, DbService db, UserPunishService punishService)
{
private readonly Channel<PunishQueueItem> PunishUserQueue =
Channel.CreateUnbounded<PunishQueueItem>(new() { SingleReader = true, SingleWriter = false });
public ProtectionService(
DiscordSocketClient client,
Bot bot,
MuteService mute,
DbService db,
UserPunishService punishService)
{
_client = client;
_mute = mute;
_db = db;
@@ -42,18 +44,15 @@ public class ProtectionService : INService
using (var uow = db.GetDbContext())
{
var configs = uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels)
.Include(x => x.AntiAltSetting)
.Where(x => ids.Contains(x.GuildId))
.ToList();
.AsQueryable()
.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels)
.Include(x => x.AntiAltSetting)
.Where(x => ids.Contains(x.GuildId))
.ToList();
foreach (var gc in configs)
{
Initialize(gc);
}
foreach (var gc in configs) Initialize(gc);
}
_client.MessageReceived += HandleAntiSpam;
@@ -61,7 +60,7 @@ public class ProtectionService : INService
bot.JoinedGuild += _bot_JoinedGuild;
_client.LeftGuild += _client_LeftGuild;
_ = Task.Run(RunQueue);
}
@@ -75,8 +74,13 @@ public class ProtectionService : INService
var gu = item.User;
try
{
await _punishService.ApplyPunishment(gu.Guild, gu, _client.CurrentUser,
item.Action, muteTime, item.RoleId, $"{item.Type} Protection");
await _punishService.ApplyPunishment(gu.Guild,
gu,
_client.CurrentUser,
item.Action,
muteTime,
item.RoleId,
$"{item.Type} Protection");
}
catch (Exception ex)
{
@@ -104,11 +108,10 @@ public class ProtectionService : INService
{
using var uow = _db.GetDbContext();
var gcWithData = uow.GuildConfigsForId(gc.GuildId,
set => set
.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiAltSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels));
set => set.Include(x => x.AntiRaidSetting)
.Include(x => x.AntiAltSetting)
.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels));
Initialize(gcWithData);
return Task.CompletedTask;
@@ -121,7 +124,7 @@ public class ProtectionService : INService
if (raid != null)
{
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
var raidStats = new AntiRaidStats { AntiRaidSettings = raid };
_antiRaidGuilds[gc.GuildId] = raidStats;
}
@@ -137,41 +140,38 @@ public class ProtectionService : INService
{
if (user.IsBot)
return Task.CompletedTask;
_antiRaidGuilds.TryGetValue(user.Guild.Id, out var maybeStats);
_antiAltGuilds.TryGetValue(user.Guild.Id, out var maybeAlts);
if (maybeStats is null && maybeAlts is null)
return Task.CompletedTask;
_ = Task.Run(async () =>
{
if (maybeAlts is { } alts)
{
if (user.CreatedAt != default)
{
var diff = DateTime.UtcNow - user.CreatedAt.UtcDateTime;
if (diff < alts.MinAge)
{
alts.Increment();
await PunishUsers(
alts.Action,
await PunishUsers(alts.Action,
ProtectionType.Alting,
alts.ActionDurationMinutes,
alts.ActionDurationMinutes,
alts.RoleId,
user);
return;
}
}
}
try
{
if (maybeStats is not { } stats || !stats.RaidUsers.Add(user))
return;
++stats.UsersCount;
if (stats.UsersCount >= stats.AntiRaidSettings.UserThreshold)
@@ -180,14 +180,13 @@ public class ProtectionService : INService
stats.RaidUsers.Clear();
var settings = stats.AntiRaidSettings;
await PunishUsers(settings.Action, ProtectionType.Raiding,
settings.PunishDuration, null, users);
await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users);
}
await Task.Delay(1000 * stats.AntiRaidSettings.Seconds);
stats.RaidUsers.TryRemove(user);
--stats.UsersCount;
}
catch
{
@@ -208,29 +207,29 @@ public class ProtectionService : INService
{
try
{
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings) ||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new()
{
ChannelId = channel.Id
}))
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings)
|| spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new() { ChannelId = channel.Id }))
return;
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, id => new(msg),
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id,
id => new(msg),
(id, old) =>
{
old.ApplyNextMessage(msg); return old;
old.ApplyNextMessage(msg);
return old;
});
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
{
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
{
stats.Dispose();
var settings = spamSettings.AntiSpamSettings;
await PunishUsers(settings.Action, ProtectionType.Spamming, settings.MuteTime,
settings.RoleId, (IGuildUser)msg.Author);
await PunishUsers(settings.Action,
ProtectionType.Spamming,
settings.MuteTime,
settings.RoleId,
(IGuildUser)msg.Author);
}
}
}
catch
{
@@ -240,18 +239,20 @@ public class ProtectionService : INService
return Task.CompletedTask;
}
private async Task PunishUsers(PunishmentAction action, ProtectionType pt, int muteTime, ulong? roleId,
private async Task PunishUsers(
PunishmentAction action,
ProtectionType pt,
int muteTime,
ulong? roleId,
params IGuildUser[] gus)
{
Log.Information(
"[{PunishType}] - Punishing [{Count}] users with [{PunishAction}] in {GuildName} guild",
Log.Information("[{PunishType}] - Punishing [{Count}] users with [{PunishAction}] in {GuildName} guild",
pt,
gus.Length,
action,
gus[0].Guild.Name);
foreach (var gu in gus)
{
await PunishUserQueue.Writer.WriteAsync(new()
{
Action = action,
@@ -260,24 +261,27 @@ public class ProtectionService : INService
MuteTime = muteTime,
RoleId = roleId
});
}
_ = OnAntiProtectionTriggered(action, pt, gus);
}
public async Task<AntiRaidStats> StartAntiRaidAsync(ulong guildId, int userThreshold, int seconds,
PunishmentAction action, int minutesDuration)
public async Task<AntiRaidStats> StartAntiRaidAsync(
ulong guildId,
int userThreshold,
int seconds,
PunishmentAction action,
int minutesDuration)
{
var g = _client.GetGuild(guildId);
await _mute.GetMuteRole(g);
if (action == PunishmentAction.AddRole)
return null;
if (!IsDurationAllowed(action))
minutesDuration = 0;
var stats = new AntiRaidStats()
var stats = new AntiRaidStats
{
AntiRaidSettings = new()
{
@@ -310,6 +314,7 @@ public class ProtectionService : INService
uow.SaveChanges();
return true;
}
return false;
}
@@ -317,24 +322,26 @@ public class ProtectionService : INService
{
if (_antiSpamGuilds.TryRemove(guildId, out var removed))
{
foreach (var (_, val) in removed.UserStats)
{
val.Dispose();
}
foreach (var (_, val) in removed.UserStats) val.Dispose();
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiSpamSetting)
.ThenInclude(x => x.IgnoredChannels));
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
gc.AntiSpamSetting = null;
uow.SaveChanges();
return true;
}
return false;
}
public async Task<AntiSpamStats> StartAntiSpamAsync(ulong guildId, int messageCount, PunishmentAction action,
int punishDurationMinutes, ulong? roleId)
public async Task<AntiSpamStats> StartAntiSpamAsync(
ulong guildId,
int messageCount,
PunishmentAction action,
int punishDurationMinutes,
ulong? roleId)
{
var g = _client.GetGuild(guildId);
await _mute.GetMuteRole(g);
@@ -349,15 +356,17 @@ public class ProtectionService : INService
Action = action,
MessageThreshold = messageCount,
MuteTime = punishDurationMinutes,
RoleId = roleId,
RoleId = roleId
}
};
stats = _antiSpamGuilds.AddOrUpdate(guildId, stats, (key, old) =>
{
stats.AntiSpamSettings.IgnoredChannels = old.AntiSpamSettings.IgnoredChannels;
return stats;
});
stats = _antiSpamGuilds.AddOrUpdate(guildId,
stats,
(key, old) =>
{
stats.AntiSpamSettings.IgnoredChannels = old.AntiSpamSettings.IgnoredChannels;
return stats;
});
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiSpamSetting));
@@ -373,24 +382,20 @@ public class ProtectionService : INService
{
gc.AntiSpamSetting = stats.AntiSpamSettings;
}
await uow.SaveChangesAsync();
return stats;
}
public async Task<bool?> AntiSpamIgnoreAsync(ulong guildId, ulong channelId)
{
var obj = new AntiSpamIgnore()
{
ChannelId = channelId
};
var obj = new AntiSpamIgnore { ChannelId = channelId };
bool added;
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
var spam = gc.AntiSpamSetting;
if (spam is null)
{
return null;
}
if (spam is null) return null;
if (spam.IgnoredChannels.Add(obj)) // if adding to db is successful
{
@@ -403,9 +408,7 @@ public class ProtectionService : INService
var toRemove = spam.IgnoredChannels.First(x => x.ChannelId == channelId);
uow.Set<AntiSpamIgnore>().Remove(toRemove); // remove from db
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
{
temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache
}
added = false;
}
@@ -437,8 +440,12 @@ public class ProtectionService : INService
}
}
public async Task StartAntiAltAsync(ulong guildId, int minAgeMinutes, PunishmentAction action,
int actionDurationMinutes = 0, ulong? roleId = null)
public async Task StartAntiAltAsync(
ulong guildId,
int minAgeMinutes,
PunishmentAction action,
int actionDurationMinutes = 0,
ulong? roleId = null)
{
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set.Include(x => x.AntiAltSetting));
@@ -447,7 +454,7 @@ public class ProtectionService : INService
Action = action,
ActionDurationMinutes = actionDurationMinutes,
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
RoleId = roleId,
RoleId = roleId
};
await uow.SaveChangesAsync();
@@ -465,4 +472,4 @@ public class ProtectionService : INService
await uow.SaveChangesAsync();
return true;
}
}
}

View File

@@ -9,12 +9,12 @@ public class PruneService : INService
private readonly ILogCommandService _logService;
public PruneService(ILogCommandService logService)
=> this._logService = logService;
=> _logService = logService;
public async Task PruneWhere(ITextChannel channel, int amount, Func<IMessage, bool> predicate)
{
ArgumentNullException.ThrowIfNull(channel, nameof(channel));
if (amount <= 0)
throw new ArgumentOutOfRangeException(nameof(amount));
@@ -46,17 +46,16 @@ public class PruneService : INService
await Task.WhenAll(Task.Delay(1000), channel.DeleteMessagesAsync(bulkDeletable));
foreach (var group in singleDeletable.Chunk(5))
await Task.WhenAll(
Task.Delay(1000),
group.Select(x => x.DeleteAsync())
.WhenAll()
);
await Task.WhenAll(Task.Delay(1000), group.Select(x => x.DeleteAsync()).WhenAll());
//this isn't good, because this still work as if i want to remove only specific user's messages from the last
//100 messages, Maybe this needs to be reduced by msgs.Length instead of 100
amount -= 50;
if(amount > 0)
msgs = (await channel.GetMessagesAsync(lastMessage, Direction.Before, 50).FlattenAsync()).Where(predicate).Take(amount).ToArray();
if (amount > 0)
msgs = (await channel.GetMessagesAsync(lastMessage, Direction.Before, 50).FlattenAsync())
.Where(predicate)
.Take(amount)
.ToArray();
}
}
catch
@@ -68,4 +67,4 @@ public class PruneService : INService
_pruningGuilds.TryRemove(channel.GuildId);
}
}
}
}

View File

@@ -1,10 +1,10 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.Collections;
using NadekoBot.Services.Database.Models;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.Collections;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
@@ -15,36 +15,34 @@ public class RoleCommandsService : INService
private readonly ConcurrentDictionary<ulong, IndexedCollection<ReactionRoleMessage>> _models;
/// <summary>
/// Contains the (Message ID, User ID) of reaction roles that are currently being processed.
/// Contains the (Message ID, User ID) of reaction roles that are currently being processed.
/// </summary>
private readonly ConcurrentHashSet<(ulong, ulong)> _reacting = new();
public RoleCommandsService(DiscordSocketClient client, DbService db,
Bot bot)
public RoleCommandsService(DiscordSocketClient client, DbService db, Bot bot)
{
_db = db;
_client = client;
#if !GLOBAL_NADEKO
_models = bot.AllGuildConfigs.ToDictionary(x => x.GuildId,
x => x.ReactionRoleMessages)
.ToConcurrent();
_models = bot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.ReactionRoleMessages).ToConcurrent();
_client.ReactionAdded += _client_ReactionAdded;
_client.ReactionRemoved += _client_ReactionRemoved;
#endif
}
private Task _client_ReactionAdded(Cacheable<IUserMessage, ulong> msg,
private Task _client_ReactionAdded(
Cacheable<IUserMessage, ulong> msg,
Cacheable<IMessageChannel, ulong> chan,
SocketReaction reaction)
{
_ = Task.Run(async () =>
{
if (!reaction.User.IsSpecified ||
reaction.User.Value.IsBot ||
reaction.User.Value is not SocketGuildUser gusr ||
chan.Value is not SocketGuildChannel gch ||
!_models.TryGetValue(gch.Guild.Id, out var confs))
if (!reaction.User.IsSpecified
|| reaction.User.Value.IsBot
|| reaction.User.Value is not SocketGuildUser gusr
|| chan.Value is not SocketGuildChannel gch
|| !_models.TryGetValue(gch.Guild.Id, out var confs))
return;
var conf = confs.FirstOrDefault(x => x.MessageId == msg.Id);
@@ -53,7 +51,8 @@ public class RoleCommandsService : INService
return;
// compare emote names for backwards compatibility :facepalm:
var reactionRole = conf.ReactionRoles.FirstOrDefault(x => x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
var reactionRole = conf.ReactionRoles.FirstOrDefault(x
=> x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
if (reactionRole != null)
{
@@ -69,7 +68,12 @@ public class RoleCommandsService : INService
try
{
var removeExclusiveTask = RemoveExclusiveReactionRoleAsync(msg, gusr, reaction, conf, reactionRole, CancellationToken.None);
var removeExclusiveTask = RemoveExclusiveReactionRoleAsync(msg,
gusr,
reaction,
conf,
reactionRole,
CancellationToken.None);
var addRoleTask = AddReactionRoleAsync(gusr, reactionRole);
await Task.WhenAll(removeExclusiveTask, addRoleTask);
@@ -83,11 +87,9 @@ public class RoleCommandsService : INService
else
{
var dl = await msg.GetOrDownloadAsync();
await dl.RemoveReactionAsync(reaction.Emote, dl.Author,
new()
{
RetryMode = RetryMode.RetryRatelimit | RetryMode.Retry502
});
await dl.RemoveReactionAsync(reaction.Emote,
dl.Author,
new() { RetryMode = RetryMode.RetryRatelimit | RetryMode.Retry502 });
Log.Warning("User {0} is adding unrelated reactions to the reaction roles message.", dl.Author);
}
});
@@ -95,7 +97,8 @@ public class RoleCommandsService : INService
return Task.CompletedTask;
}
private Task _client_ReactionRemoved(Cacheable<IUserMessage, ulong> msg,
private Task _client_ReactionRemoved(
Cacheable<IUserMessage, ulong> msg,
Cacheable<IMessageChannel, ulong> chan,
SocketReaction reaction)
{
@@ -103,9 +106,9 @@ public class RoleCommandsService : INService
{
try
{
if (!reaction.User.IsSpecified ||
reaction.User.Value.IsBot ||
reaction.User.Value is not SocketGuildUser gusr)
if (!reaction.User.IsSpecified
|| reaction.User.Value.IsBot
|| reaction.User.Value is not SocketGuildUser gusr)
return;
if (chan.Value is not SocketGuildChannel gch)
@@ -119,7 +122,8 @@ public class RoleCommandsService : INService
if (conf is null)
return;
var reactionRole = conf.ReactionRoles.FirstOrDefault(x => x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
var reactionRole = conf.ReactionRoles.FirstOrDefault(x
=> x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
if (reactionRole != null)
{
@@ -143,20 +147,17 @@ public class RoleCommandsService : INService
using var uow = _db.GetDbContext();
var table = uow.GetTable<ReactionRoleMessage>();
table.Delete(x => x.MessageId == rrm.MessageId);
var gc = uow.GuildConfigsForId(id, set => set
.Include(x => x.ReactionRoleMessages)
.ThenInclude(x => x.ReactionRoles));
var gc = uow.GuildConfigsForId(id,
set => set.Include(x => x.ReactionRoleMessages).ThenInclude(x => x.ReactionRoles));
if (gc.ReactionRoleMessages.Count >= 10)
return false;
gc.ReactionRoleMessages.Add(rrm);
uow.SaveChanges();
_models.AddOrUpdate(id,
gc.ReactionRoleMessages,
delegate { return gc.ReactionRoleMessages; });
_models.AddOrUpdate(id, gc.ReactionRoleMessages, delegate { return gc.ReactionRoleMessages; });
return true;
}
@@ -164,19 +165,15 @@ public class RoleCommandsService : INService
{
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(id,
set => set.Include(x => x.ReactionRoleMessages)
.ThenInclude(x => x.ReactionRoles));
uow.Set<ReactionRole>()
.RemoveRange(gc.ReactionRoleMessages[index].ReactionRoles);
set => set.Include(x => x.ReactionRoleMessages).ThenInclude(x => x.ReactionRoles));
uow.Set<ReactionRole>().RemoveRange(gc.ReactionRoleMessages[index].ReactionRoles);
gc.ReactionRoleMessages.RemoveAt(index);
_models.AddOrUpdate(id,
gc.ReactionRoleMessages,
delegate { return gc.ReactionRoleMessages; });
_models.AddOrUpdate(id, gc.ReactionRoleMessages, delegate { return gc.ReactionRoleMessages; });
uow.SaveChanges();
}
/// <summary>
/// Adds a reaction role to the specified user.
/// Adds a reaction role to the specified user.
/// </summary>
/// <param name="user">A Discord guild user.</param>
/// <param name="dbRero">The database settings of this reaction role.</param>
@@ -184,13 +181,11 @@ public class RoleCommandsService : INService
{
var toAdd = user.Guild.GetRole(dbRero.RoleId);
return toAdd != null && !user.Roles.Contains(toAdd)
? user.AddRoleAsync(toAdd)
: Task.CompletedTask;
return toAdd != null && !user.Roles.Contains(toAdd) ? user.AddRoleAsync(toAdd) : Task.CompletedTask;
}
/// <summary>
/// Removes the exclusive reaction roles and reactions from the specified user.
/// Removes the exclusive reaction roles and reactions from the specified user.
/// </summary>
/// <param name="reactionMessage">The Discord message that contains the reaction roles.</param>
/// <param name="user">A Discord guild user.</param>
@@ -200,14 +195,20 @@ public class RoleCommandsService : INService
/// <param name="cToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Occurs when the operation is cancelled before it began.</exception>
/// <exception cref="TaskCanceledException">Occurs when the operation is cancelled while it's still executing.</exception>
private Task RemoveExclusiveReactionRoleAsync(Cacheable<IUserMessage, ulong> reactionMessage, SocketGuildUser user, SocketReaction reaction, ReactionRoleMessage dbReroMsg, ReactionRole dbRero, CancellationToken cToken = default)
private Task RemoveExclusiveReactionRoleAsync(
Cacheable<IUserMessage, ulong> reactionMessage,
SocketGuildUser user,
SocketReaction reaction,
ReactionRoleMessage dbReroMsg,
ReactionRole dbRero,
CancellationToken cToken = default)
{
cToken.ThrowIfCancellationRequested();
var roleIds = dbReroMsg.ReactionRoles.Select(x => x.RoleId)
.Where(x => x != dbRero.RoleId)
.Select(x => user.Guild.GetRole(x))
.Where(x => x != null);
.Where(x => x != dbRero.RoleId)
.Select(x => user.Guild.GetRole(x))
.Where(x => x != null);
var removeReactionsTask = RemoveOldReactionsAsync(reactionMessage, user, reaction, cToken);
@@ -217,7 +218,7 @@ public class RoleCommandsService : INService
}
/// <summary>
/// Removes old reactions from an exclusive reaction role.
/// Removes old reactions from an exclusive reaction role.
/// </summary>
/// <param name="reactionMessage">The Discord message that contains the reaction roles.</param>
/// <param name="user">A Discord guild user.</param>
@@ -225,7 +226,11 @@ public class RoleCommandsService : INService
/// <param name="cToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Occurs when the operation is cancelled before it began.</exception>
/// <exception cref="TaskCanceledException">Occurs when the operation is cancelled while it's still executing.</exception>
private async Task RemoveOldReactionsAsync(Cacheable<IUserMessage, ulong> reactionMessage, SocketGuildUser user, SocketReaction reaction, CancellationToken cToken = default)
private async Task RemoveOldReactionsAsync(
Cacheable<IUserMessage, ulong> reactionMessage,
SocketGuildUser user,
SocketReaction reaction,
CancellationToken cToken = default)
{
cToken.ThrowIfCancellationRequested();
@@ -236,8 +241,10 @@ public class RoleCommandsService : INService
{
if (r.Key.Name == reaction.Emote.Name)
continue;
try { await dl.RemoveReactionAsync(r.Key, user); } catch { }
try { await dl.RemoveReactionAsync(r.Key, user); }
catch { }
await Task.Delay(100, cToken);
}
}
}
}

View File

@@ -1,32 +1,32 @@
#nullable disable
using NadekoBot.Services.Database.Models;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Modules.Xp;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
public class SelfAssignedRolesService : INService
{
private readonly DbService _db;
public enum RemoveResult
{
Removed, // successfully removed
Err_Not_Assignable, // not assignable (error)
Err_Not_Have, // you don't have a role you want to remove (error)
Err_Not_Perms, // bot doesn't have perms (error)
}
public enum AssignResult
{
Assigned, // successfully removed
Err_Not_Assignable, // not assignable (error)
Err_Already_Have, // you already have that role (error)
Err_Not_Perms, // bot doesn't have perms (error)
Err_Lvl_Req, // you are not required level (error)
Err_Lvl_Req // you are not required level (error)
}
public enum RemoveResult
{
Removed, // successfully removed
Err_Not_Assignable, // not assignable (error)
Err_Not_Have, // you don't have a role you want to remove (error)
Err_Not_Perms // bot doesn't have perms (error)
}
private readonly DbService _db;
public SelfAssignedRolesService(DbService db)
=> _db = db;
@@ -34,17 +34,9 @@ public class SelfAssignedRolesService : INService
{
using var uow = _db.GetDbContext();
var roles = uow.SelfAssignableRoles.GetFromGuild(guildId);
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
{
return false;
}
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id)) return false;
uow.SelfAssignableRoles.Add(new()
{
Group = @group,
RoleId = role.Id,
GuildId = role.Guild.Id
});
uow.SelfAssignableRoles.Add(new() { Group = group, RoleId = role.Id, GuildId = role.Guild.Id });
uow.SaveChanges();
return true;
}
@@ -72,31 +64,20 @@ public class SelfAssignedRolesService : INService
var theRoleYouWant = roles.FirstOrDefault(r => r.RoleId == role.Id);
if (theRoleYouWant is null)
{
return (AssignResult.Err_Not_Assignable, autoDelete, null);
}
else if (theRoleYouWant.LevelRequirement > userLevelData.Level)
{
if (theRoleYouWant.LevelRequirement > userLevelData.Level)
return (AssignResult.Err_Lvl_Req, autoDelete, theRoleYouWant.LevelRequirement);
}
else if (guildUser.RoleIds.Contains(role.Id))
{
return (AssignResult.Err_Already_Have, autoDelete, null);
}
if (guildUser.RoleIds.Contains(role.Id)) return (AssignResult.Err_Already_Have, autoDelete, null);
var roleIds = roles
.Where(x => x.Group == theRoleYouWant.Group)
.Select(x => x.RoleId).ToArray();
var roleIds = roles.Where(x => x.Group == theRoleYouWant.Group).Select(x => x.RoleId).ToArray();
if (exclusive)
{
var sameRoles = guildUser.RoleIds
.Where(r => roleIds.Contains(r));
var sameRoles = guildUser.RoleIds.Where(r => roleIds.Contains(r));
foreach (var roleId in sameRoles)
{
var sameRole = guildUser.Guild.GetRole(roleId);
if (sameRole != null)
{
try
{
await guildUser.RemoveRoleAsync(sameRole);
@@ -106,9 +87,9 @@ public class SelfAssignedRolesService : INService
{
// ignored
}
}
}
}
try
{
await guildUser.AddRoleAsync(role);
@@ -126,7 +107,7 @@ public class SelfAssignedRolesService : INService
var set = false;
await using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, y => y.Include(x => x.SelfAssignableRoleGroupNames));
var toUpdate = gc.SelfAssignableRoleGroupNames.FirstOrDefault(x => x.Number == @group);
var toUpdate = gc.SelfAssignableRoleGroupNames.FirstOrDefault(x => x.Number == group);
if (string.IsNullOrWhiteSpace(name))
{
@@ -135,11 +116,7 @@ public class SelfAssignedRolesService : INService
}
else if (toUpdate is null)
{
gc.SelfAssignableRoleGroupNames.Add(new()
{
Name = name,
Number = @group,
});
gc.SelfAssignableRoleGroupNames.Add(new() { Name = name, Number = group });
set = true;
}
else
@@ -158,13 +135,8 @@ public class SelfAssignedRolesService : INService
var (autoDelete, _, roles) = GetAdAndRoles(guildUser.Guild.Id);
if (roles.FirstOrDefault(r => r.RoleId == role.Id) is null)
{
return (RemoveResult.Err_Not_Assignable, autoDelete);
}
if (!guildUser.RoleIds.Contains(role.Id))
{
return (RemoveResult.Err_Not_Have, autoDelete);
}
if (!guildUser.RoleIds.Contains(role.Id)) return (RemoveResult.Err_Not_Have, autoDelete);
try
{
await guildUser.RemoveRoleAsync(role);
@@ -226,7 +198,8 @@ public class SelfAssignedRolesService : INService
return areExclusive;
}
public (bool Exclusive, IEnumerable<(SelfAssignedRole Model, IRole Role)> Roles, IDictionary<int, string> GroupNames) GetRoles(IGuild guild)
public (bool Exclusive, IEnumerable<(SelfAssignedRole Model, IRole Role)> Roles, IDictionary<int, string> GroupNames
) GetRoles(IGuild guild)
{
var exclusive = false;
@@ -238,12 +211,11 @@ public class SelfAssignedRolesService : INService
exclusive = gc.ExclusiveSelfAssignedRoles;
groupNames = gc.SelfAssignableRoleGroupNames.ToDictionary(x => x.Number, x => x.Name);
var roleModels = uow.SelfAssignableRoles.GetFromGuild(guild.Id);
roles = roleModels
.Select(x => (Model: x, Role: guild.GetRole(x.RoleId)));
roles = roleModels.Select(x => (Model: x, Role: guild.GetRole(x.RoleId)));
uow.SelfAssignableRoles.RemoveRange(roles.Where(x => x.Role is null).Select(x => x.Model).ToArray());
uow.SaveChanges();
}
return (exclusive, roles.Where(x => x.Role != null), groupNames);
}
}
}

View File

@@ -1,8 +1,8 @@
#nullable disable
using System.Collections.Immutable;
using NadekoBot.Common.ModuleBehaviors;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Collections.Immutable;
namespace NadekoBot.Modules.Administration.Services;
@@ -56,58 +56,49 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
_activitySetKey = new("activity.set");
_imagesReloadKey = new("images.reload");
_guildLeaveKey = new("guild.leave");
HandleStatusChanges();
if (_client.ShardId == 0)
{
_pubSub.Sub(_imagesReloadKey, async _ => await _imgs.Reload());
}
_pubSub.Sub(_guildLeaveKey, async input =>
{
var guildStr = input.ToString().Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(guildStr))
return;
if (_client.ShardId == 0) _pubSub.Sub(_imagesReloadKey, async _ => await _imgs.Reload());
var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr
|| g.Name.Trim().ToUpperInvariant() == guildStr);
if (server is null)
_pubSub.Sub(_guildLeaveKey,
async input =>
{
return;
}
var guildStr = input.ToString().Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(guildStr))
return;
if (server.OwnerId != _client.CurrentUser.Id)
{
await server.LeaveAsync();
Log.Information($"Left server {server.Name} [{server.Id}]");
}
else
{
await server.DeleteAsync();
Log.Information($"Deleted server {server.Name} [{server.Id}]");
}
});
var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr
|| g.Name.Trim().ToUpperInvariant() == guildStr);
if (server is null) return;
if (server.OwnerId != _client.CurrentUser.Id)
{
await server.LeaveAsync();
Log.Information($"Left server {server.Name} [{server.Id}]");
}
else
{
await server.DeleteAsync();
Log.Information($"Deleted server {server.Name} [{server.Id}]");
}
});
}
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
_autoCommands = uow
.AutoCommands
.AsNoTracking()
.Where(x => x.Interval >= 5)
.AsEnumerable()
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key,
y => y.ToDictionary(x => x.Id, TimerFromAutoCommand)
.ToConcurrent())
.ToConcurrent();
_autoCommands = uow.AutoCommands.AsNoTracking()
.Where(x => x.Interval >= 5)
.AsEnumerable()
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key,
y => y.ToDictionary(x => x.Id, TimerFromAutoCommand).ToConcurrent())
.ToConcurrent();
var startupCommands = uow.AutoCommands.AsNoTracking().Where(x => x.Interval == 0);
foreach (var cmd in startupCommands)
{
try
{
await ExecuteCommand(cmd);
@@ -115,19 +106,12 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
catch
{
}
}
if (_client.ShardId == 0)
{
await LoadOwnerChannels();
}
if (_client.ShardId == 0) await LoadOwnerChannels();
}
private Timer TimerFromAutoCommand(AutoCommand x)
=> new(async obj => await ExecuteCommand((AutoCommand) obj),
x,
x.Interval * 1000,
x.Interval * 1000);
=> new(async obj => await ExecuteCommand((AutoCommand)obj), x, x.Interval * 1000, x.Interval * 1000);
private async Task ExecuteCommand(AutoCommand cmd)
{
@@ -136,7 +120,7 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
if (cmd.GuildId is null)
return;
var guildShard = (int) ((cmd.GuildId.Value >> 22) % (ulong) _creds.TotalShards);
var guildShard = (int)((cmd.GuildId.Value >> 22) % (ulong)_creds.TotalShards);
if (guildShard != _client.ShardId)
return;
var prefix = _cmdHandler.GetPrefix(cmd.GuildId);
@@ -162,34 +146,26 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
if (cmd.Interval >= 5)
{
var autos = _autoCommands.GetOrAdd(cmd.GuildId, new ConcurrentDictionary<int, Timer>());
autos.AddOrUpdate(cmd.Id, key => TimerFromAutoCommand(cmd), (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return TimerFromAutoCommand(cmd);
});
autos.AddOrUpdate(cmd.Id,
key => TimerFromAutoCommand(cmd),
(key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return TimerFromAutoCommand(cmd);
});
}
}
public IEnumerable<AutoCommand> GetStartupCommands()
{
using var uow = _db.GetDbContext();
return uow
.AutoCommands
.AsNoTracking()
.Where(x => x.Interval == 0)
.OrderBy(x => x.Id)
.ToList();
return uow.AutoCommands.AsNoTracking().Where(x => x.Interval == 0).OrderBy(x => x.Id).ToList();
}
public IEnumerable<AutoCommand> GetAutoCommands()
{
using var uow = _db.GetDbContext();
return uow
.AutoCommands
.AsNoTracking()
.Where(x => x.Interval >= 5)
.OrderBy(x => x.Id)
.ToList();
return uow.AutoCommands.AsNoTracking().Where(x => x.Interval >= 5).OrderBy(x => x.Id).ToList();
}
private async Task LoadOwnerChannels()
@@ -204,8 +180,8 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
}));
ownerChannels = channels.Where(x => x != null)
.ToDictionary(x => x.Recipient.Id, x => x)
.ToImmutableDictionary();
.ToDictionary(x => x.Recipient.Id, x => x)
.ToImmutableDictionary();
if (!ownerChannels.Any())
Log.Warning(
@@ -214,7 +190,7 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
Log.Information($"Created {ownerChannels.Count} out of {_creds.OwnerIds.Count} owner message channels.");
}
public Task LeaveGuild(string guildStr)
public Task LeaveGuild(string guildStr)
=> _pubSub.Pub(_guildLeaveKey, guildStr);
// forwards dms
@@ -223,25 +199,21 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
var bs = _bss.Data;
if (msg.Channel is IDMChannel && bs.ForwardMessages && ownerChannels.Any())
{
var title = _strings.GetText(strs.dm_from) +
$" [{msg.Author}]({msg.Author.Id})";
var title = _strings.GetText(strs.dm_from) + $" [{msg.Author}]({msg.Author.Id})";
var attachamentsTxt = _strings.GetText(strs.attachments);
var toSend = msg.Content;
if (msg.Attachments.Count > 0)
{
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
}
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n"
+ string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
if (bs.ForwardToAllOwners)
{
var allOwnerChannels = ownerChannels.Values;
foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
{
try
{
await ownerCh.SendConfirmAsync(_eb, title, toSend);
@@ -250,13 +222,11 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
{
Log.Warning("Can't contact owner with id {0}", ownerCh.Recipient.Id);
}
}
}
else
{
var firstOwnerChannel = ownerChannels.Values.First();
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
{
try
{
await firstOwnerChannel.SendConfirmAsync(_eb, title, toSend);
@@ -265,7 +235,6 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
{
// ignored
}
}
}
}
}
@@ -273,11 +242,7 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
public bool RemoveStartupCommand(int index, out AutoCommand cmd)
{
using var uow = _db.GetDbContext();
cmd = uow.AutoCommands
.AsNoTracking()
.Where(x => x.Interval == 0)
.Skip(index)
.FirstOrDefault();
cmd = uow.AutoCommands.AsNoTracking().Where(x => x.Interval == 0).Skip(index).FirstOrDefault();
if (cmd != null)
{
@@ -288,15 +253,11 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
return false;
}
public bool RemoveAutoCommand(int index, out AutoCommand cmd)
{
using var uow = _db.GetDbContext();
cmd = uow.AutoCommands
.AsNoTracking()
.Where(x => x.Interval >= 5)
.Skip(index)
.FirstOrDefault();
cmd = uow.AutoCommands.AsNoTracking().Where(x => x.Interval >= 5).Skip(index).FirstOrDefault();
if (cmd != null)
{
@@ -337,16 +298,13 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
public void ClearStartupCommands()
{
using var uow = _db.GetDbContext();
var toRemove = uow
.AutoCommands
.AsNoTracking()
.Where(x => x.Interval == 0);
var toRemove = uow.AutoCommands.AsNoTracking().Where(x => x.Interval == 0);
uow.AutoCommands.RemoveRange(toRemove);
uow.SaveChanges();
}
public Task ReloadImagesAsync()
public Task ReloadImagesAsync()
=> _pubSub.Pub(_imagesReloadKey, true);
public bool ForwardMessages()
@@ -363,22 +321,23 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
_bss.ModifyConfig(config => { isToAll = config.ForwardToAllOwners = !config.ForwardToAllOwners; });
return isToAll;
}
private void HandleStatusChanges()
=> _pubSub.Sub(_activitySetKey, async data =>
{
try
{
await _client.SetGameAsync(data.Name, data.Link, type: data.Type);
}
catch (Exception ex)
{
Log.Warning(ex, "Error setting activity");
}
});
public Task SetGameAsync(string game, ActivityType type)
=> _pubSub.Pub(_activitySetKey, new() {Name = game, Link = null, Type = type});
private void HandleStatusChanges()
=> _pubSub.Sub(_activitySetKey,
async data =>
{
try
{
await _client.SetGameAsync(data.Name, data.Link, data.Type);
}
catch (Exception ex)
{
Log.Warning(ex, "Error setting activity");
}
});
public Task SetGameAsync(string game, ActivityType type)
=> _pubSub.Pub(_activitySetKey, new() { Name = game, Link = null, Type = type });
public Task SetStreamAsync(string name, string link)
=> _pubSub.Pub(_activitySetKey, new() { Name = name, Link = link, Type = ActivityType.Streaming });
@@ -389,4 +348,4 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
public string Link { get; init; }
public ActivityType Type { get; init; }
}
}
}

View File

@@ -1,9 +1,9 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Services.Database.Models;
using Newtonsoft.Json;
namespace NadekoBot.Modules.Administration.Services;
@@ -16,7 +16,11 @@ public class UserPunishService : INService
private readonly BotConfigService _bcs;
private readonly Timer _warnExpiryTimer;
public UserPunishService(MuteService mute, DbService db, BlacklistService blacklistService, BotConfigService bcs)
public UserPunishService(
MuteService mute,
DbService db,
BlacklistService blacklistService,
BotConfigService bcs)
{
_mute = mute;
_db = db;
@@ -24,16 +28,24 @@ public class UserPunishService : INService
_bcs = bcs;
_warnExpiryTimer = new(async _ =>
{
await CheckAllWarnExpiresAsync();
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromHours(12));
{
await CheckAllWarnExpiresAsync();
},
null,
TimeSpan.FromSeconds(0),
TimeSpan.FromHours(12));
}
public async Task<WarningPunishment> Warn(IGuild guild, ulong userId, IUser mod, int weight, string reason)
public async Task<WarningPunishment> Warn(
IGuild guild,
ulong userId,
IUser mod,
int weight,
string reason)
{
if (weight <= 0)
throw new ArgumentOutOfRangeException(nameof(weight));
var modName = mod.ToString();
if (string.IsNullOrWhiteSpace(reason))
@@ -41,28 +53,25 @@ public class UserPunishService : INService
var guildId = guild.Id;
var warn = new Warning()
var warn = new Warning
{
UserId = userId,
GuildId = guildId,
Forgiven = false,
Reason = reason,
Moderator = modName,
Weight = weight,
Weight = weight
};
var warnings = 1;
List<WarningPunishment> ps;
await using (var uow = _db.GetDbContext())
{
ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments))
.WarnPunishments;
ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
warnings += uow
.Warnings
.ForId(guildId, userId)
.Where(w => !w.Forgiven && w.UserId == userId)
.Sum(x => x.Weight);
warnings += uow.Warnings.ForId(guildId, userId)
.Where(w => !w.Forgiven && w.UserId == userId)
.Sum(x => x.Weight);
uow.Warnings.Add(warn);
@@ -84,13 +93,18 @@ public class UserPunishService : INService
return null;
}
public async Task ApplyPunishment(IGuild guild, IGuildUser user, IUser mod, PunishmentAction p, int minutes,
ulong? roleId, string reason)
public async Task ApplyPunishment(
IGuild guild,
IGuildUser user,
IUser mod,
PunishmentAction p,
int minutes,
ulong? roleId,
string reason)
{
if (!await CheckPermission(guild, p))
return;
switch (p)
{
case PunishmentAction.Mute:
@@ -121,7 +135,7 @@ public class UserPunishService : INService
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason);
break;
case PunishmentAction.Softban:
await guild.AddBanAsync(user, 7, reason: $"Softban | {reason}");
await guild.AddBanAsync(user, 7, $"Softban | {reason}");
try
{
await guild.RemoveBanAsync(user);
@@ -151,22 +165,19 @@ public class UserPunishService : INService
Log.Warning($"Can't find role {roleId.Value} on server {guild.Id} to apply punishment.");
}
break;
default:
break;
}
}
/// <summary>
/// Used to prevent the bot from hitting 403's when it needs to
/// apply punishments with insufficient permissions
/// Used to prevent the bot from hitting 403's when it needs to
/// apply punishments with insufficient permissions
/// </summary>
/// <param name="guild">Guild the punishment is applied in</param>
/// <param name="punish">Punishment to apply</param>
/// <returns>Whether the bot has sufficient permissions</returns>
private async Task<bool> CheckPermission(IGuild guild, PunishmentAction punish)
{
var botUser = await guild.GetCurrentUserAsync();
switch (punish)
{
@@ -194,21 +205,19 @@ public class UserPunishService : INService
public async Task CheckAllWarnExpiresAsync()
{
await using var uow = _db.GetDbContext();
var cleared = await uow.Database.ExecuteSqlRawAsync($@"UPDATE Warnings
var cleared = await uow.Database.ExecuteSqlRawAsync(@"UPDATE Warnings
SET Forgiven = 1,
ForgivenBy = 'Expiry'
WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND WarnExpireAction = 0)
AND Forgiven = 0
AND DateAdded < datetime('now', (SELECT '-' || WarnExpireHours || ' hours' FROM GuildConfigs as gc WHERE gc.GuildId = Warnings.GuildId));");
var deleted = await uow.Database.ExecuteSqlRawAsync($@"DELETE FROM Warnings
var deleted = await uow.Database.ExecuteSqlRawAsync(@"DELETE FROM Warnings
WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND WarnExpireAction = 1)
AND DateAdded < datetime('now', (SELECT '-' || WarnExpireHours || ' hours' FROM GuildConfigs as gc WHERE gc.GuildId = Warnings.GuildId));");
if(cleared > 0 || deleted > 0)
{
if (cleared > 0 || deleted > 0)
Log.Information($"Cleared {cleared} warnings and deleted {deleted} warnings due to expiry.");
}
}
public async Task CheckWarnExpiresAsync(ulong guildId)
@@ -221,20 +230,16 @@ WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND
var hours = $"{-config.WarnExpireHours} hours";
if (config.WarnExpireAction == WarnExpireAction.Clear)
{
await uow.Database.ExecuteSqlInterpolatedAsync($@"UPDATE warnings
SET Forgiven = 1,
ForgivenBy = 'Expiry'
WHERE GuildId={guildId}
AND Forgiven = 0
AND DateAdded < datetime('now', {hours})");
}
else if (config.WarnExpireAction == WarnExpireAction.Delete)
{
await uow.Database.ExecuteSqlInterpolatedAsync($@"DELETE FROM warnings
WHERE GuildId={guildId}
AND DateAdded < datetime('now', {hours})");
}
await uow.SaveChangesAsync();
}
@@ -245,7 +250,7 @@ WHERE GuildId={guildId}
var config = uow.GuildConfigsForId(guildId, set => set);
return Task.FromResult(config.WarnExpireHours / 24);
}
public async Task WarnExpireAsync(ulong guildId, int days, bool delete)
{
await using (var uow = _db.GetDbContext())
@@ -276,23 +281,28 @@ WHERE GuildId={guildId}
return uow.Warnings.ForId(gid, userId);
}
public async Task<bool> WarnClearAsync(ulong guildId, ulong userId, int index, string moderator)
public async Task<bool> WarnClearAsync(
ulong guildId,
ulong userId,
int index,
string moderator)
{
var toReturn = true;
await using var uow = _db.GetDbContext();
if (index == 0)
{
await uow.Warnings.ForgiveAll(guildId, userId, moderator);
}
else
{
toReturn = uow.Warnings.Forgive(guildId, userId, moderator, index - 1);
}
uow.SaveChanges();
return toReturn;
}
public bool WarnPunish(ulong guildId, int number, PunishmentAction punish, StoopidTime time, IRole role = null)
public bool WarnPunish(
ulong guildId,
int number,
PunishmentAction punish,
StoopidTime time,
IRole role = null)
{
// these 3 don't make sense with time
if (punish is PunishmentAction.Softban or PunishmentAction.Kick or PunishmentAction.RemoveRoles && time != null)
@@ -311,7 +321,7 @@ WHERE GuildId={guildId}
Count = number,
Punishment = punish,
Time = (int?)time?.Time.TotalMinutes ?? 0,
RoleId = punish == PunishmentAction.AddRole ? role.Id : default(ulong?),
RoleId = punish == PunishmentAction.AddRole ? role.Id : default(ulong?)
});
uow.SaveChanges();
return true;
@@ -339,42 +349,37 @@ WHERE GuildId={guildId}
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(guildId, gc => gc.Include(x => x.WarnPunishments))
.WarnPunishments
.OrderBy(x => x.Count)
.ToArray();
.WarnPunishments.OrderBy(x => x.Count)
.ToArray();
}
public (IEnumerable<(string Original, ulong? Id, string Reason)> Bans, int Missing) MassKill(SocketGuild guild, string people)
public (IEnumerable<(string Original, ulong? Id, string Reason)> Bans, int Missing) MassKill(
SocketGuild guild,
string people)
{
var gusers = guild.Users;
//get user objects and reasons
var bans = people.Split("\n")
.Select(x =>
{
var split = x.Trim().Split(" ");
.Select(x =>
{
var split = x.Trim().Split(" ");
var reason = string.Join(" ", split.Skip(1));
var reason = string.Join(" ", split.Skip(1));
if (ulong.TryParse(split[0], out var id))
return (Original: split[0], Id: id, Reason: reason);
if (ulong.TryParse(split[0], out var id))
return (Original: split[0], Id: id, Reason: reason);
return (Original: split[0],
Id: gusers
.FirstOrDefault(u => u.ToString().ToLowerInvariant() == x)
?.Id,
Reason: reason);
})
.ToArray();
return (Original: split[0],
gusers.FirstOrDefault(u => u.ToString().ToLowerInvariant() == x)?.Id,
Reason: reason);
})
.ToArray();
//if user is null, means that person couldn't be found
var missing = bans
.Count(x => !x.Id.HasValue);
var missing = bans.Count(x => !x.Id.HasValue);
//get only data for found users
var found = bans
.Where(x => x.Id.HasValue)
.Select(x => x.Id.Value)
.ToList();
var found = bans.Where(x => x.Id.HasValue).Select(x => x.Id.Value).ToList();
_blacklistService.BlacklistUsers(found);
@@ -384,33 +389,25 @@ WHERE GuildId={guildId}
public string GetBanTemplate(ulong guildId)
{
using var uow = _db.GetDbContext();
var template = uow.BanTemplates
.AsQueryable()
.FirstOrDefault(x => x.GuildId == guildId);
var template = uow.BanTemplates.AsQueryable().FirstOrDefault(x => x.GuildId == guildId);
return template?.Text;
}
public void SetBanTemplate(ulong guildId, string text)
{
using var uow = _db.GetDbContext();
var template = uow.BanTemplates
.AsQueryable()
.FirstOrDefault(x => x.GuildId == guildId);
var template = uow.BanTemplates.AsQueryable().FirstOrDefault(x => x.GuildId == guildId);
if (text is null)
{
if (template is null)
return;
uow.Remove(template);
}
else if (template is null)
{
uow.BanTemplates.Add(new()
{
GuildId = guildId,
Text = text,
});
uow.BanTemplates.Add(new() { GuildId = guildId, Text = text });
}
else
{
@@ -420,67 +417,66 @@ WHERE GuildId={guildId}
uow.SaveChanges();
}
public SmartText GetBanUserDmEmbed(ICommandContext context, IGuildUser target, string defaultMessage,
string banReason, TimeSpan? duration)
=> GetBanUserDmEmbed(
(DiscordSocketClient) context.Client,
(SocketGuild) context.Guild,
(IGuildUser) context.User,
public SmartText GetBanUserDmEmbed(
ICommandContext context,
IGuildUser target,
string defaultMessage,
string banReason,
TimeSpan? duration)
=> GetBanUserDmEmbed((DiscordSocketClient)context.Client,
(SocketGuild)context.Guild,
(IGuildUser)context.User,
target,
defaultMessage,
banReason,
duration);
public SmartText GetBanUserDmEmbed(DiscordSocketClient client, SocketGuild guild,
IGuildUser moderator, IGuildUser target, string defaultMessage, string banReason, TimeSpan? duration)
public SmartText GetBanUserDmEmbed(
DiscordSocketClient client,
SocketGuild guild,
IGuildUser moderator,
IGuildUser target,
string defaultMessage,
string banReason,
TimeSpan? duration)
{
var template = GetBanTemplate(guild.Id);
banReason = string.IsNullOrWhiteSpace(banReason)
? "-"
: banReason;
banReason = string.IsNullOrWhiteSpace(banReason) ? "-" : banReason;
var replacer = new ReplacementBuilder()
.WithServer(client, guild)
.WithOverride("%ban.mod%", () => moderator.ToString())
.WithOverride("%ban.mod.fullname%", () => moderator.ToString())
.WithOverride("%ban.mod.name%", () => moderator.Username)
.WithOverride("%ban.mod.discrim%", () => moderator.Discriminator)
.WithOverride("%ban.user%", () => target.ToString())
.WithOverride("%ban.user.fullname%", () => target.ToString())
.WithOverride("%ban.user.name%", () => target.Username)
.WithOverride("%ban.user.discrim%", () => target.Discriminator)
.WithOverride("%reason%", () => banReason)
.WithOverride("%ban.reason%", () => banReason)
.WithOverride("%ban.duration%", () => duration?.ToString(@"d\.hh\:mm")?? "perma")
.Build();
var replacer = new ReplacementBuilder().WithServer(client, guild)
.WithOverride("%ban.mod%", () => moderator.ToString())
.WithOverride("%ban.mod.fullname%", () => moderator.ToString())
.WithOverride("%ban.mod.name%", () => moderator.Username)
.WithOverride("%ban.mod.discrim%", () => moderator.Discriminator)
.WithOverride("%ban.user%", () => target.ToString())
.WithOverride("%ban.user.fullname%", () => target.ToString())
.WithOverride("%ban.user.name%", () => target.Username)
.WithOverride("%ban.user.discrim%", () => target.Discriminator)
.WithOverride("%reason%", () => banReason)
.WithOverride("%ban.reason%", () => banReason)
.WithOverride("%ban.duration%",
() => duration?.ToString(@"d\.hh\:mm") ?? "perma")
.Build();
// if template isn't set, use the old message style
if (string.IsNullOrWhiteSpace(template))
{
template = JsonConvert.SerializeObject(new
{
color = _bcs.Data.Color.Error.PackedValue >> 8,
description = defaultMessage
color = _bcs.Data.Color.Error.PackedValue >> 8, description = defaultMessage
});
}
// if template is set to "-" do not dm the user
else if (template == "-")
{
return default;
}
// if template is an embed, send that embed with replacements
// otherwise, treat template as a regular string with replacements
else if (!SmartText.CreateFrom(template).IsEmbed)
{
template = JsonConvert.SerializeObject(new
{
color = _bcs.Data.Color.Error.PackedValue >> 8,
description = template
color = _bcs.Data.Color.Error.PackedValue >> 8, description = template
});
}
var output = SmartText.CreateFrom(template);
return replacer.Replace(output);
}
}
}

View File

@@ -1,17 +1,16 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database.Models;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services;
public class VcRoleService : INService
{
private readonly DbService _db;
private readonly DiscordSocketClient _client;
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
public ConcurrentDictionary<ulong, ConcurrentQueue<(bool, IGuildUser, IRole)>> ToAssign { get; }
private readonly DbService _db;
private readonly DiscordSocketClient _client;
public VcRoleService(DiscordSocketClient client, Bot bot, DbService db)
{
@@ -26,12 +25,12 @@ public class VcRoleService : INService
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
uow.Set<GuildConfig>()
.AsQueryable()
.Include(x => x.VcRoleInfos)
.Where(x => guildIds.Contains(x.GuildId))
.AsEnumerable()
.Select(InitializeVcRole)
.WhenAll();
.AsQueryable()
.Include(x => x.VcRoleInfos)
.Where(x => guildIds.Contains(x.GuildId))
.AsEnumerable()
.Select(InitializeVcRole)
.WhenAll();
}
Task.Run(async () =>
@@ -39,42 +38,34 @@ public class VcRoleService : INService
while (true)
{
Task Selector(ConcurrentQueue<(bool, IGuildUser, IRole)> queue)
=> Task.Run(async () =>
{
return Task.Run(async () =>
{
while (queue.TryDequeue(out var item))
{
while (queue.TryDequeue(out var item))
var (add, user, role) = item;
try
{
var (add, user, role) = item;
try
if (add)
{
if (add)
{
if (!user.RoleIds.Contains(role.Id))
{
await user.AddRoleAsync(role);
}
}
else
{
if (user.RoleIds.Contains(role.Id))
{
await user.RemoveRoleAsync(role);
}
}
if (!user.RoleIds.Contains(role.Id)) await user.AddRoleAsync(role);
}
catch
else
{
if (user.RoleIds.Contains(role.Id)) await user.RemoveRoleAsync(role);
}
await Task.Delay(250);
}
}
);
catch
{
}
await ToAssign.Values.Select(Selector)
.Append(Task.Delay(1000))
.WhenAll();
await Task.Delay(250);
}
});
}
await ToAssign.Values.Select(Selector).Append(Task.Delay(1000)).WhenAll();
}
});
@@ -88,10 +79,7 @@ public class VcRoleService : INService
// need to load new guildconfig with vc role included
using (var uow = _db.GetDbContext())
{
var configWithVcRole = uow.GuildConfigsForId(
arg.GuildId,
set => set.Include(x => x.VcRoleInfos)
);
var configWithVcRole = uow.GuildConfigsForId(arg.GuildId, set => set.Include(x => x.VcRoleInfos));
var _ = InitializeVcRole(configWithVcRole);
}
@@ -132,8 +120,7 @@ public class VcRoleService : INService
await using var uow = _db.GetDbContext();
Log.Warning("Removing {MissingRoleCount} missing roles from {ServiceName}",
missingRoles.Count,
nameof(VcRoleService)
);
nameof(VcRoleService));
uow.RemoveRange(missingRoles);
await uow.SaveChangesAsync();
}
@@ -150,15 +137,8 @@ public class VcRoleService : INService
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.VcRoleInfos));
var toDelete = conf.VcRoleInfos.FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one
if(toDelete != null)
{
uow.Remove(toDelete);
}
conf.VcRoleInfos.Add(new()
{
VoiceChannelId = vcId,
RoleId = role.Id,
}); // add new one
if (toDelete != null) uow.Remove(toDelete);
conf.VcRoleInfos.Add(new() { VoiceChannelId = vcId, RoleId = role.Id }); // add new one
uow.SaveChanges();
}
@@ -179,8 +159,7 @@ public class VcRoleService : INService
return true;
}
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
SocketVoiceState newState)
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
{
if (usr is not SocketGuildUser gusr)
return Task.CompletedTask;
@@ -200,15 +179,9 @@ public class VcRoleService : INService
{
//remove old
if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out var role))
{
Assign(false, gusr, role);
}
//add new
if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role))
{
Assign(true, gusr, role);
}
if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role)) Assign(true, gusr, role);
}
}
}
@@ -225,4 +198,4 @@ public class VcRoleService : INService
var queue = ToAssign.GetOrAdd(gusr.Guild.Id, new ConcurrentQueue<(bool, IGuildUser, IRole)>());
queue.Enqueue((v, gusr, role));
}
}
}

View File

@@ -8,7 +8,8 @@ public partial class Administration
[Group]
public class TimeZoneCommands : NadekoSubmodule<GuildTimezoneService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Timezones(int page = 1)
{
@@ -17,55 +18,50 @@ public partial class Administration
if (page is < 0 or > 20)
return;
var timezones = TimeZoneInfo.GetSystemTimeZones()
.OrderBy(x => x.BaseUtcOffset)
.ToArray();
var timezones = TimeZoneInfo.GetSystemTimeZones().OrderBy(x => x.BaseUtcOffset).ToArray();
var timezonesPerPage = 20;
var curTime = DateTimeOffset.UtcNow;
var i = 0;
var timezoneStrings = timezones
.Select(x => (x, ++i % 2 == 0))
.Select(data =>
{
var (tzInfo, flip) = data;
var nameStr = $"{tzInfo.Id,-30}";
var offset = curTime.ToOffset(tzInfo.GetUtcOffset(curTime)).ToString("zzz");
if (flip)
{
return $"{offset} {Format.Code(nameStr)}";
}
else
{
return $"{Format.Code(offset)} {nameStr}";
}
});
var timezoneStrings = timezones.Select(x => (x, ++i % 2 == 0))
.Select(data =>
{
var (tzInfo, flip) = data;
var nameStr = $"{tzInfo.Id,-30}";
var offset = curTime.ToOffset(tzInfo.GetUtcOffset(curTime))
.ToString("zzz");
if (flip)
return $"{offset} {Format.Code(nameStr)}";
return $"{Format.Code(offset)} {nameStr}";
});
await ctx.SendPaginatedConfirmAsync(page,
curPage => _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.timezones_available))
.WithDescription(string.Join("\n", timezoneStrings
.Skip(curPage * timezonesPerPage)
.Take(timezonesPerPage))),
timezones.Length, timezonesPerPage);
.WithOkColor()
.WithTitle(GetText(strs.timezones_available))
.WithDescription(string.Join("\n",
timezoneStrings.Skip(curPage * timezonesPerPage).Take(timezonesPerPage))),
timezones.Length,
timezonesPerPage);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task Timezone()
=> await ReplyConfirmLocalizedAsync(strs.timezone_guild(_service.GetTimeZoneOrUtc(ctx.Guild.Id)));
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task Timezone([Leftover] string id)
{
TimeZoneInfo tz;
try { tz = TimeZoneInfo.FindSystemTimeZoneById(id); } catch { tz = null; }
try { tz = TimeZoneInfo.FindSystemTimeZoneById(id); }
catch { tz = null; }
if (tz is null)
@@ -73,9 +69,10 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.timezone_not_found);
return;
}
_service.SetTimeZone(ctx.Guild.Id, tz);
await SendConfirmAsync(tz.ToString());
}
}
}
}

View File

@@ -2,9 +2,9 @@
using CommandLine;
using Humanizer.Localisation;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration;
@@ -13,6 +13,11 @@ public partial class Administration
[Group]
public class UserPunishCommands : NadekoSubmodule<UserPunishService>
{
public enum AddRole
{
AddRole
}
private readonly MuteService _mute;
private readonly BlacklistService _blacklistService;
@@ -24,47 +29,52 @@ public partial class Administration
private async Task<bool> CheckRoleHierarchy(IGuildUser target)
{
var curUser = ((SocketGuild) ctx.Guild).CurrentUser;
var curUser = ((SocketGuild)ctx.Guild).CurrentUser;
var ownerId = ctx.Guild.OwnerId;
var modMaxRole = ((IGuildUser) ctx.User).GetRoles().Max(r => r.Position);
var modMaxRole = ((IGuildUser)ctx.User).GetRoles().Max(r => r.Position);
var targetMaxRole = target.GetRoles().Max(r => r.Position);
var botMaxRole = curUser.GetRoles().Max(r => r.Position);
// bot can't punish a user who is higher in the hierarchy. Discord will return 403
// moderator can be owner, in which case role hierarchy doesn't matter
// otherwise, moderator has to have a higher role
if (botMaxRole <= targetMaxRole || (ctx.User.Id != ownerId && targetMaxRole >= modMaxRole) || target.Id == ownerId)
if (botMaxRole <= targetMaxRole
|| (ctx.User.Id != ownerId && targetMaxRole >= modMaxRole)
|| target.Id == ownerId)
{
await ReplyErrorLocalizedAsync(strs.hierarchy);
return false;
}
return true;
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public Task Warn(IGuildUser user, [Leftover] string reason = null)
=> Warn(1, user, reason);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public async Task Warn(int weight, IGuildUser user, [Leftover] string reason = null)
{
if (weight <= 0)
return;
if (!await CheckRoleHierarchy(user))
return;
var dmFailed = false;
try
{
await user.EmbedAsync(_eb.Create().WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString())
.AddField(GetText(strs.reason), reason ?? "-"));
await user.EmbedAsync(_eb.Create()
.WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString())
.AddField(GetText(strs.reason), reason ?? "-"));
}
catch
{
@@ -79,50 +89,28 @@ public partial class Administration
catch (Exception ex)
{
Log.Warning(ex.Message);
var errorEmbed = _eb.Create()
.WithErrorColor()
.WithDescription(GetText(strs.cant_apply_punishment));
if (dmFailed)
{
errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
var errorEmbed = _eb.Create().WithErrorColor().WithDescription(GetText(strs.cant_apply_punishment));
if (dmFailed) errorEmbed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
await ctx.Channel.EmbedAsync(errorEmbed);
return;
}
var embed = _eb.Create()
.WithOkColor();
var embed = _eb.Create().WithOkColor();
if (punishment is null)
{
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
}
else
{
embed.WithDescription(GetText(strs.user_warned_and_punished(Format.Bold(user.ToString()),
Format.Bold(punishment.Punishment.ToString()))));
}
if (dmFailed)
{
embed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
if (dmFailed) embed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
await ctx.Channel.EmbedAsync(embed);
}
public class WarnExpireOptions : INadekoCommandOptions
{
[Option('d', "delete", Default = false, HelpText = "Delete warnings instead of clearing them.")]
public bool Delete { get; set; } = false;
public void NormalizeOptions()
{
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[NadekoOptions(typeof(WarnExpireOptions))]
@@ -137,7 +125,8 @@ public partial class Administration
await ReplyErrorLocalizedAsync(strs.warns_expire_in(expireDays));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[NadekoOptions(typeof(WarnExpireOptions))]
@@ -152,51 +141,53 @@ public partial class Administration
await ctx.Channel.TriggerTypingAsync();
await _service.WarnExpireAsync(ctx.Guild.Id, days, opts.Delete);
if(days == 0)
if (days == 0)
{
await ReplyConfirmLocalizedAsync(strs.warn_expire_reset);
return;
}
if (opts.Delete)
{
await ReplyConfirmLocalizedAsync(strs.warn_expire_set_delete(Format.Bold(days.ToString())));
}
else
{
await ReplyConfirmLocalizedAsync(strs.warn_expire_set_clear(Format.Bold(days.ToString())));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[Priority(2)]
public Task Warnlog(int page, [Leftover] IGuildUser user = null)
{
user ??= (IGuildUser) ctx.User;
user ??= (IGuildUser)ctx.User;
return Warnlog(page, user.Id);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[Priority(3)]
public Task Warnlog(IGuildUser user = null)
{
user ??= (IGuildUser) ctx.User;
user ??= (IGuildUser)ctx.User;
return ctx.User.Id == user.Id || ((IGuildUser)ctx.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask;
return ctx.User.Id == user.Id || ((IGuildUser)ctx.User).GuildPermissions.BanMembers
? Warnlog(user.Id)
: Task.CompletedTask;
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[Priority(0)]
public Task Warnlog(int page, ulong userId)
=> InternalWarnlog(userId, page - 1);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[Priority(1)]
@@ -207,58 +198,54 @@ public partial class Administration
{
if (inputPage < 0)
return;
var allWarnings = _service.UserWarnings(ctx.Guild.Id, userId);
await ctx.SendPaginatedConfirmAsync(inputPage, page =>
{
var warnings = allWarnings
.Skip(page * 9)
.Take(9)
.ToArray();
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
var embed = _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.warnlog_for(user)));
if (!warnings.Any())
await ctx.SendPaginatedConfirmAsync(inputPage,
page =>
{
embed.WithDescription(GetText(strs.warnings_none));
}
else
{
var descText = GetText(strs.warn_count(
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
Format.Bold(warnings.Sum(x => x.Weight).ToString())));
embed.WithDescription(descText);
var i = page * 9;
foreach (var w in warnings)
var warnings = allWarnings.Skip(page * 9).Take(9).ToArray();
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
var embed = _eb.Create().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
if (!warnings.Any())
{
i++;
var name = GetText(strs.warned_on_by(
w.DateAdded?.ToString("dd.MM.yyy"),
w.DateAdded?.ToString("HH:mm"),
w.Moderator));
if (w.Forgiven)
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
embed.AddField($"#`{i}` " + name,
Format.Code(GetText(strs.warn_weight(w.Weight))) +
'\n' +
w.Reason.TrimTo(1000));
embed.WithDescription(GetText(strs.warnings_none));
}
}
else
{
var descText = GetText(strs.warn_count(
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
Format.Bold(warnings.Sum(x => x.Weight).ToString())));
return embed;
}, allWarnings.Length, 9);
embed.WithDescription(descText);
var i = page * 9;
foreach (var w in warnings)
{
i++;
var name = GetText(strs.warned_on_by(w.DateAdded?.ToString("dd.MM.yyy"),
w.DateAdded?.ToString("HH:mm"),
w.Moderator));
if (w.Forgiven)
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
embed.AddField($"#`{i}` " + name,
Format.Code(GetText(strs.warn_weight(w.Weight))) + '\n' + w.Reason.TrimTo(1000));
}
}
return embed;
},
allWarnings.Length,
9);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public async Task WarnlogAll(int page = 1)
@@ -267,33 +254,40 @@ public partial class Administration
return;
var warnings = _service.WarnlogAll(ctx.Guild.Id);
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var ws = warnings.Skip(curPage * 15)
.Take(15)
.ToArray()
.Select(x =>
{
var all = x.Count();
var forgiven = x.Count(y => y.Forgiven);
var total = all - forgiven;
var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key);
return (usr?.ToString() ?? x.Key.ToString()) + $" | {total} ({all} - {forgiven})";
});
await ctx.SendPaginatedConfirmAsync(page,
curPage =>
{
var ws = warnings.Skip(curPage * 15)
.Take(15)
.ToArray()
.Select(x =>
{
var all = x.Count();
var forgiven = x.Count(y => y.Forgiven);
var total = all - forgiven;
var usr = ((SocketGuild)ctx.Guild).GetUser(x.Key);
return (usr?.ToString() ?? x.Key.ToString())
+ $" | {total} ({all} - {forgiven})";
});
return _eb.Create().WithOkColor()
.WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws));
}, warnings.Length, 15);
return _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws));
},
warnings.Length,
15);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public Task Warnclear(IGuildUser user, int index = 0)
=> Warnclear(user.Id, index);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public async Task Warnclear(ulong userId, int index = 0)
@@ -309,57 +303,48 @@ public partial class Administration
else
{
if (success)
{
await ReplyConfirmLocalizedAsync(strs.warning_cleared(Format.Bold(index.ToString()), userStr));
}
else
{
await ReplyErrorLocalizedAsync(strs.warning_clear_fail);
}
}
}
public enum AddRole
{
AddRole
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[Priority(1)]
public async Task WarnPunish(int number, AddRole _, IRole role, StoopidTime time = null)
public async Task WarnPunish(
int number,
AddRole _,
IRole role,
StoopidTime time = null)
{
var punish = PunishmentAction.AddRole;
if (ctx.Guild.OwnerId != ctx.User.Id &&
role.Position >= ((IGuildUser)ctx.User).GetRoles().Max(x => x.Position))
if (ctx.Guild.OwnerId != ctx.User.Id
&& role.Position >= ((IGuildUser)ctx.User).GetRoles().Max(x => x.Position))
{
await ReplyErrorLocalizedAsync(strs.role_too_high);
return;
}
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
if (!success)
return;
if (time is null)
{
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(
Format.Bold(punish.ToString()),
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(Format.Bold(punish.ToString()),
Format.Bold(number.ToString())));
}
else
{
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(
Format.Bold(punish.ToString()),
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()),
Format.Bold(time.Input)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public async Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
@@ -374,35 +359,27 @@ public partial class Administration
return;
if (time is null)
{
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(
Format.Bold(punish.ToString()),
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(Format.Bold(punish.ToString()),
Format.Bold(number.ToString())));
}
else
{
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(
Format.Bold(punish.ToString()),
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
Format.Bold(number.ToString()),
Format.Bold(time.Input)));
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
public async Task WarnPunish(int number)
{
if (!_service.WarnPunishRemove(ctx.Guild.Id, number))
{
return;
}
if (!_service.WarnPunishRemove(ctx.Guild.Id, number)) return;
await ReplyConfirmLocalizedAsync(strs.warn_punish_rem(
Format.Bold(number.ToString())));
await ReplyConfirmLocalizedAsync(strs.warn_punish_rem(Format.Bold(number.ToString())));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task WarnPunishList()
{
@@ -410,20 +387,16 @@ public partial class Administration
string list;
if (ps.Any())
{
list = string.Join("\n", ps.Select(x => $"{x.Count} -> {x.Punishment} {(x.Punishment == PunishmentAction.AddRole ? $"<@&{x.RoleId}>" : "")} {(x.Time <= 0 ? "" : x.Time.ToString() + "m")} "));
}
list = string.Join("\n",
ps.Select(x
=> $"{x.Count} -> {x.Punishment} {(x.Punishment == PunishmentAction.AddRole ? $"<@&{x.RoleId}>" : "")} {(x.Time <= 0 ? "" : x.Time + "m")} "));
else
{
list = GetText(strs.warnpl_none);
}
await SendConfirmAsync(
GetText(strs.warn_punish_list),
list);
await SendConfirmAsync(GetText(strs.warn_punish_list), list);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -441,42 +414,34 @@ public partial class Administration
var dmFailed = false;
if (guildUser != null)
{
try
{
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
var embed = _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
if (embed is not null)
{
await guildUser.SendAsync(embed);
}
if (embed is not null) await guildUser.SendAsync(embed);
}
catch
{
dmFailed = true;
}
}
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User.ToString() + " | " + msg).TrimTo(512));
var toSend = _eb.Create().WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true)
.AddField(GetText(strs.duration),
time.Time.Humanize(3,
minUnit: TimeUnit.Minute,
culture: Culture),
true);
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User + " | " + msg).TrimTo(512));
var toSend = _eb.Create()
.WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true)
.AddField(GetText(strs.duration),
time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture),
true);
if (dmFailed)
{
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
if (dmFailed) toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
await ctx.Channel.EmbedAsync(toSend);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -486,11 +451,12 @@ public partial class Administration
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
if (user is null)
{
await ctx.Guild.AddBanAsync(userId, 7, (ctx.User.ToString() + " | " + msg).TrimTo(512));
await ctx.Channel.EmbedAsync(_eb.Create().WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField("ID", userId.ToString(), true));
await ctx.Guild.AddBanAsync(userId, 7, (ctx.User + " | " + msg).TrimTo(512));
await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField("ID", userId.ToString(), true));
}
else
{
@@ -498,7 +464,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -514,32 +481,28 @@ public partial class Administration
{
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
var embed = _service.GetBanUserDmEmbed(Context, user, defaultMessage, msg, null);
if (embed is not null)
{
await ctx.User.SendAsync(embed);
}
if (embed is not null) await ctx.User.SendAsync(embed);
}
catch
{
dmFailed = true;
}
await ctx.Guild.AddBanAsync(user, 7, (ctx.User.ToString() + " | " + msg).TrimTo(512));
await ctx.Guild.AddBanAsync(user, 7, (ctx.User + " | " + msg).TrimTo(512));
var toSend = _eb.Create().WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
var toSend = _eb.Create()
.WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
if (dmFailed) toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
if (dmFailed)
{
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
await ctx.Channel.EmbedAsync(toSend);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -557,12 +520,13 @@ public partial class Administration
await SendConfirmAsync(template);
return;
}
_service.SetBanTemplate(ctx.Guild.Id, message);
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -572,30 +536,28 @@ public partial class Administration
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
[Priority(0)]
public Task BanMessageTest([Leftover] string reason = null)
=> InternalBanMessageTest(reason, null);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
[Priority(1)]
public Task BanMessageTest(StoopidTime duration, [Leftover] string reason = null)
=> InternalBanMessageTest(reason, duration.Time);
private async Task InternalBanMessageTest(string reason, TimeSpan? duration)
{
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), reason));
var embed = _service.GetBanUserDmEmbed(Context,
(IGuildUser)ctx.User,
defaultMessage,
reason,
duration);
var embed = _service.GetBanUserDmEmbed(Context, (IGuildUser)ctx.User, defaultMessage, reason, duration);
if (embed is null)
{
@@ -617,7 +579,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -636,7 +599,8 @@ public partial class Administration
await UnbanInternal(bun.User);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -662,14 +626,16 @@ public partial class Administration
await ReplyConfirmLocalizedAsync(strs.unbanned_user(Format.Bold(user.ToString())));
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.KickMembers | GuildPerm.ManageMessages)]
[BotPerm(GuildPerm.BanMembers)]
public Task Softban(IGuildUser user, [Leftover] string msg = null)
=> SoftbanInternal(user, msg);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.KickMembers | GuildPerm.ManageMessages)]
[BotPerm(GuildPerm.BanMembers)]
@@ -681,7 +647,7 @@ public partial class Administration
await SoftbanInternal(user, msg);
}
private async Task SoftbanInternal(IGuildUser user, [Leftover] string msg = null)
{
if (!await CheckRoleHierarchy(user))
@@ -698,24 +664,23 @@ public partial class Administration
dmFailed = true;
}
await ctx.Guild.AddBanAsync(user, 7, ("Softban | " + ctx.User.ToString() + " | " + msg).TrimTo(512));
await ctx.Guild.AddBanAsync(user, 7, ("Softban | " + ctx.User + " | " + msg).TrimTo(512));
try { await ctx.Guild.RemoveBanAsync(user); }
catch { await ctx.Guild.RemoveBanAsync(user); }
var toSend = _eb.Create().WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
if (dmFailed)
{
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
var toSend = _eb.Create()
.WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
if (dmFailed) toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
await ctx.Channel.EmbedAsync(toSend);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.KickMembers)]
[BotPerm(GuildPerm.KickMembers)]
@@ -723,7 +688,8 @@ public partial class Administration
public Task Kick(IGuildUser user, [Leftover] string msg = null)
=> KickInternal(user, msg);
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.KickMembers)]
[BotPerm(GuildPerm.KickMembers)]
@@ -733,7 +699,7 @@ public partial class Administration
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
if (user is null)
return;
await KickInternal(user, msg);
}
@@ -749,26 +715,25 @@ public partial class Administration
await user.SendErrorAsync(_eb, GetText(strs.kickdm(Format.Bold(ctx.Guild.Name), msg)));
}
catch
{
{
dmFailed = true;
}
await user.KickAsync((ctx.User.ToString() + " | " + msg).TrimTo(512));
var toSend = _eb.Create().WithOkColor()
.WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
if (dmFailed)
{
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
}
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
var toSend = _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true)
.AddField("ID", user.Id.ToString(), true);
if (dmFailed) toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
await ctx.Channel.EmbedAsync(toSend);
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -783,11 +748,11 @@ public partial class Administration
await ctx.Channel.TriggerTypingAsync();
foreach (var userStr in userStrings)
{
if (ulong.TryParse(userStr, out var userId))
{
IUser user = await ctx.Guild.GetUserAsync(userId) ??
await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
IUser user = await ctx.Guild.GetUserAsync(userId)
?? await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id,
userId);
if (user is null)
{
@@ -800,14 +765,10 @@ public partial class Administration
missing.Add(userStr);
continue;
}
}
//Hierachy checks only if the user is in the guild
if (user is IGuildUser gu && !await CheckRoleHierarchy(gu))
{
return;
}
if (user is IGuildUser gu && !await CheckRoleHierarchy(gu)) return;
banning.Add(user);
}
@@ -815,41 +776,38 @@ public partial class Administration
{
missing.Add(userStr);
}
}
var missStr = string.Join("\n", missing);
if (string.IsNullOrWhiteSpace(missStr))
missStr = "-";
var toSend = _eb.Create(ctx)
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor();
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor();
var banningMessage = await ctx.Channel.EmbedAsync(toSend);
foreach (var toBan in banning)
{
try
{
await ctx.Guild.AddBanAsync(toBan.Id, 7, $"{ctx.User} | Massban");
}
catch (Exception ex)
{
Log.Warning(ex, "Error banning {User} user in {GuildId} server",
toBan.Id,
ctx.Guild.Id);
Log.Warning(ex, "Error banning {User} user in {GuildId} server", toBan.Id, ctx.Guild.Id);
}
}
await banningMessage.ModifyAsync(x => x.Embed = _eb.Create()
.WithDescription(GetText(strs.mass_ban_completed(banning.Count())))
.AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithOkColor()
.Build());
.WithDescription(
GetText(strs.mass_ban_completed(banning.Count())))
.AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithOkColor()
.Build());
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
@@ -867,26 +825,37 @@ public partial class Administration
//send a message but don't wait for it
var banningMessageTask = ctx.Channel.EmbedAsync(_eb.Create()
.WithDescription(GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr)
.WithPendingColor());
.WithDescription(
GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr)
.WithPendingColor());
//do the banning
await Task.WhenAll(bans
.Where(x => x.Id.HasValue)
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value, 7, x.Reason, new()
{
RetryMode = RetryMode.AlwaysRetry,
})));
await Task.WhenAll(bans.Where(x => x.Id.HasValue)
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value,
7,
x.Reason,
new() { RetryMode = RetryMode.AlwaysRetry })));
//wait for the message and edit it
var banningMessage = await banningMessageTask;
await banningMessage.ModifyAsync(x => x.Embed = _eb.Create()
.WithDescription(GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr)
.WithOkColor()
.Build());
.WithDescription(
GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr)
.WithOkColor()
.Build());
}
public class WarnExpireOptions : INadekoCommandOptions
{
[Option('d', "delete", Default = false, HelpText = "Delete warnings instead of clearing them.")]
public bool Delete { get; set; } = false;
public void NormalizeOptions()
{
}
}
}
}
}

View File

@@ -8,23 +8,21 @@ public partial class Administration
[Group]
public class VcRoleCommands : NadekoSubmodule<VcRoleService>
{
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireContext(ContextType.Guild)]
public async Task VcRoleRm(ulong vcId)
{
if (_service.RemoveVcRole(ctx.Guild.Id, vcId))
{
await ReplyConfirmLocalizedAsync(strs.vcrole_removed(Format.Bold(vcId.ToString())));
}
else
{
await ReplyErrorLocalizedAsync(strs.vcrole_not_found);
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireContext(ContextType.Guild)]
@@ -43,9 +41,7 @@ public partial class Administration
if (role is null)
{
if (_service.RemoveVcRole(ctx.Guild.Id, vc.Id))
{
await ReplyConfirmLocalizedAsync(strs.vcrole_removed(Format.Bold(vc.Name)));
}
}
else
{
@@ -54,7 +50,8 @@ public partial class Administration
}
}
[NadekoCommand, Aliases]
[NadekoCommand]
[Aliases]
[RequireContext(ContextType.Guild)]
public async Task VcRoleList()
{
@@ -63,22 +60,21 @@ public partial class Administration
if (_service.VcRoles.TryGetValue(ctx.Guild.Id, out var roles))
{
if (!roles.Any())
{
text = GetText(strs.no_vcroles);
}
else
{
text = string.Join("\n", roles.Select(x =>
$"{Format.Bold(guild.GetVoiceChannel(x.Key)?.Name ?? x.Key.ToString())} => {x.Value}"));
}
text = string.Join("\n",
roles.Select(x
=> $"{Format.Bold(guild.GetVoiceChannel(x.Key)?.Name ?? x.Key.ToString())} => {x.Value}"));
}
else
{
text = GetText(strs.no_vcroles);
}
await ctx.Channel.EmbedAsync(_eb.Create().WithOkColor()
.WithTitle(GetText(strs.vc_role_list))
.WithDescription(text));
await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.vc_role_list))
.WithDescription(text));
}
}
}
}