mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Restructured folders and project names, ci should be fixed
This commit is contained in:
335
src/NadekoBot/Modules/Administration/Administration.cs
Normal file
335
src/NadekoBot/Modules/Administration/Administration.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration : NadekoModule<AdministrationService>
|
||||
{
|
||||
public enum List
|
||||
{
|
||||
List = 0,
|
||||
Ls = 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageChannel)]
|
||||
[BotPerm(ChannelPerm.ManageChannel)]
|
||||
public async Task Slowmode(StoopidTime time = null)
|
||||
{
|
||||
var seconds = (int?)time?.Time.TotalSeconds ?? 0;
|
||||
if (!(time is null) && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
|
||||
return;
|
||||
|
||||
|
||||
await ((ITextChannel) Context.Channel).ModifyAsync(tcp =>
|
||||
{
|
||||
tcp.SlowModeInterval = seconds;
|
||||
});
|
||||
|
||||
await Context.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(2)]
|
||||
public async Task Delmsgoncmd(List _)
|
||||
{
|
||||
var guild = (SocketGuild) ctx.Guild;
|
||||
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText("server_delmsgoncmd"))
|
||||
.WithDescription(enabled ? "✅" : "❌");
|
||||
|
||||
var str = string.Join("\n", channels
|
||||
.Select(x =>
|
||||
{
|
||||
var ch = guild.GetChannel(x.ChannelId)?.ToString()
|
||||
?? x.ChannelId.ToString();
|
||||
var prefix = x.State ? "✅ " : "❌ ";
|
||||
return prefix + ch;
|
||||
}));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
str = "-";
|
||||
|
||||
embed.AddField(GetText("channel_delmsgoncmd"), str);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum Server
|
||||
{
|
||||
Server
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(1)]
|
||||
public async Task Delmsgoncmd(Server _ = Server.Server)
|
||||
{
|
||||
if (_service.ToggleDeleteMessageOnCommand(ctx.Guild.Id))
|
||||
{
|
||||
_service.DeleteMessagesOnCommand.Add(ctx.Guild.Id);
|
||||
await ReplyConfirmLocalizedAsync("delmsg_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_service.DeleteMessagesOnCommand.TryRemove(ctx.Guild.Id);
|
||||
await ReplyConfirmLocalizedAsync("delmsg_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Channel
|
||||
{
|
||||
Channel,
|
||||
Ch,
|
||||
Chnl,
|
||||
Chan
|
||||
}
|
||||
|
||||
public enum State
|
||||
{
|
||||
Enable,
|
||||
Disable,
|
||||
Inherit
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Delmsgoncmd(Channel _, State s, ITextChannel ch)
|
||||
=> Delmsgoncmd(_, s, ch.Id);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(1)]
|
||||
public async Task Delmsgoncmd(Channel _, State s, ulong? chId = null)
|
||||
{
|
||||
var actualChId = chId ?? ctx.Channel.Id;
|
||||
await _service.SetDelMsgOnCmdState(ctx.Guild.Id, actualChId, s).ConfigureAwait(false);
|
||||
|
||||
if (s == State.Disable)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("delmsg_channel_off").ConfigureAwait(false);
|
||||
}
|
||||
else if (s == State.Enable)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("delmsg_channel_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("delmsg_channel_inherit").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.DeafenMembers)]
|
||||
[BotPerm(GuildPerm.DeafenMembers)]
|
||||
public async Task Deafen(params IGuildUser[] users)
|
||||
{
|
||||
await _service.DeafenUsers(true, users).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("deafen").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.DeafenMembers)]
|
||||
[BotPerm(GuildPerm.DeafenMembers)]
|
||||
public async Task UnDeafen(params IGuildUser[] users)
|
||||
{
|
||||
await _service.DeafenUsers(false, users).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("undeafen").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task DelVoiChanl([Leftover] IVoiceChannel voiceChannel)
|
||||
{
|
||||
await voiceChannel.DeleteAsync().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("delvoich", Format.Bold(voiceChannel.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task CreatVoiChanl([Leftover] string channelName)
|
||||
{
|
||||
var ch = await ctx.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("createvoich", Format.Bold(ch.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task DelTxtChanl([Leftover] ITextChannel toDelete)
|
||||
{
|
||||
await toDelete.DeleteAsync().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("deltextchan", Format.Bold(toDelete.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task CreaTxtChanl([Leftover] string channelName)
|
||||
{
|
||||
var txtCh = await ctx.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("createtextchan", Format.Bold(txtCh.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task SetTopic([Leftover] string topic = null)
|
||||
{
|
||||
var channel = (ITextChannel) ctx.Channel;
|
||||
topic = topic ?? "";
|
||||
await channel.ModifyAsync(c => c.Topic = topic).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("set_topic").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task SetChanlName([Leftover] string name)
|
||||
{
|
||||
var channel = (ITextChannel) ctx.Channel;
|
||||
await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("set_channel_name").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task NsfwToggle()
|
||||
{
|
||||
var channel = (ITextChannel) ctx.Channel;
|
||||
var isEnabled = channel.IsNsfw;
|
||||
|
||||
await channel.ModifyAsync(c => c.IsNsfw = !isEnabled).ConfigureAwait(false);
|
||||
|
||||
if (isEnabled)
|
||||
await ReplyConfirmLocalizedAsync("nsfw_set_false").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("nsfw_set_true").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Edit(ulong messageId, [Leftover] string text)
|
||||
=> Edit((ITextChannel) ctx.Channel, messageId, text);
|
||||
|
||||
[NadekoCommand, Usage, Description, 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);
|
||||
if (!userPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_u").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botPerms.Has(ChannelPermission.ViewChannel))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_i").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.EditMessage(ctx, channel, messageId, text);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
public Task Delete(ulong messageId, StoopidTime time = null)
|
||||
=> Delete((ITextChannel) ctx.Channel, messageId, time);
|
||||
|
||||
[NadekoCommand, Usage, Description, 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,
|
||||
Func<IMessage, Task> func)
|
||||
{
|
||||
var userPerms = ((SocketGuildUser) ctx.User).GetPermissions(channel);
|
||||
var botPerms = ((SocketGuild) ctx.Guild).CurrentUser.GetPermissions(channel);
|
||||
if (!userPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_u").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_i").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var msg = await channel.GetMessageAsync(messageId).ConfigureAwait(false);
|
||||
if (msg == null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("msg_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (time == null)
|
||||
{
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
else if (time.Time <= TimeSpan.FromDays(7))
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(time.Time).ConfigureAwait(false);
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("time_too_long").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class AutoAssignRoleCommands : NadekoSubmodule<AutoAssignRoleService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task AutoAssignRole([Leftover] IRole role)
|
||||
{
|
||||
var guser = (IGuildUser) ctx.User;
|
||||
if (role.Id == ctx.Guild.EveryoneRole.Id)
|
||||
return;
|
||||
|
||||
// the user can't aar the role which is higher or equal to his highest role
|
||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("hierarchy");
|
||||
return;
|
||||
}
|
||||
|
||||
var roles = await _service.ToggleAarAsync(ctx.Guild.Id, role.Id);
|
||||
if (roles.Count == 0)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("aar_disabled");
|
||||
}
|
||||
else if (roles.Contains(role.Id))
|
||||
{
|
||||
await AutoAssignRole();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("aar_role_removed", Format.Bold(role.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task AutoAssignRole()
|
||||
{
|
||||
if (!_service.TryGetRoles(ctx.Guild.Id, out var roles))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("aar_none");
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = roles.Select(rid => ctx.Guild.GetRole(rid)).Where(r => !(r is null))
|
||||
.ToList();
|
||||
|
||||
if (existing.Count != roles.Count)
|
||||
{
|
||||
await _service.SetAarRolesAsync(ctx.Guild.Id, existing.Select(x => x.Id));
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("aar_roles",
|
||||
'\n' + existing.Select(x => Format.Bold(x.ToString()))
|
||||
.JoinWith(",\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using Discord;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Common
|
||||
{
|
||||
public enum ProtectionType
|
||||
{
|
||||
Raiding,
|
||||
Spamming,
|
||||
Alting
|
||||
}
|
||||
|
||||
public class AntiRaidStats
|
||||
{
|
||||
public AntiRaidSetting AntiRaidSettings { get; set; }
|
||||
public int UsersCount { get; set; }
|
||||
public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
|
||||
}
|
||||
|
||||
public class AntiSpamStats
|
||||
{
|
||||
public AntiSpamSetting AntiSpamSettings { get; set; }
|
||||
public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
|
||||
= new ConcurrentDictionary<ulong, UserSpamStats>();
|
||||
}
|
||||
|
||||
public class AntiAltStats
|
||||
{
|
||||
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;
|
||||
|
||||
public AntiAltStats(AntiAltSetting setting)
|
||||
{
|
||||
_setting = setting;
|
||||
}
|
||||
|
||||
public void Increment() => Interlocked.Increment(ref _counter);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using Discord;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Common
|
||||
{
|
||||
public class PunishQueueItem
|
||||
{
|
||||
public PunishmentAction Action { get; set; }
|
||||
public ProtectionType Type { get; set; }
|
||||
public int MuteTime { get; set; }
|
||||
public ulong? RoleId { get; set; }
|
||||
public IGuildUser User { get; set; }
|
||||
}
|
||||
}
|
50
src/NadekoBot/Modules/Administration/Common/UserSpamStats.cs
Normal file
50
src/NadekoBot/Modules/Administration/Common/UserSpamStats.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Discord;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Common
|
||||
{
|
||||
public sealed class UserSpamStats : IDisposable
|
||||
{
|
||||
public int Count => timers.Count;
|
||||
public string LastMessage { get; set; }
|
||||
|
||||
private ConcurrentQueue<Timer> timers { get; }
|
||||
|
||||
public UserSpamStats(IUserMessage msg)
|
||||
{
|
||||
LastMessage = msg.Content.ToUpperInvariant();
|
||||
timers = new ConcurrentQueue<Timer>();
|
||||
|
||||
ApplyNextMessage(msg);
|
||||
}
|
||||
|
||||
private readonly object applyLock = new object();
|
||||
public void ApplyNextMessage(IUserMessage message)
|
||||
{
|
||||
lock (applyLock)
|
||||
{
|
||||
var upperMsg = message.Content.ToUpperInvariant();
|
||||
if (upperMsg != LastMessage || (string.IsNullOrWhiteSpace(upperMsg) && message.Attachments.Any()))
|
||||
{
|
||||
LastMessage = upperMsg;
|
||||
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));
|
||||
timers.Enqueue(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (timers.TryDequeue(out var old))
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
132
src/NadekoBot/Modules/Administration/DangerousCommands.cs
Normal file
132
src/NadekoBot/Modules/Administration/DangerousCommands.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using NadekoBot.Core.Modules.Administration.Services;
|
||||
using System.Linq;
|
||||
|
||||
#if !GLOBAL_NADEKO
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
[OwnerOnly]
|
||||
public class DangerousCommands : NadekoSubmodule<DangerousCommandsService>
|
||||
{
|
||||
|
||||
private async Task InternalExecSql(string sql, params object[] reps)
|
||||
{
|
||||
sql = string.Format(sql, reps);
|
||||
try
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(GetText("sql_confirm_exec"))
|
||||
.WithDescription(Format.Code(sql));
|
||||
|
||||
if (!await PromptUserConfirmAsync(embed).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var res = await _service.ExecuteSql(sql).ConfigureAwait(false);
|
||||
await ctx.Channel.SendConfirmAsync(res.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ctx.Channel.SendErrorAsync(ex.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
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 new EmbedBuilder()
|
||||
.WithErrorColor()
|
||||
.WithFooter(sql)
|
||||
.WithDescription("-");
|
||||
}
|
||||
|
||||
return new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithFooter(sql)
|
||||
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
||||
.WithDescription(string.Join('\n', items.Select(x => string.Join(" ║ ", x))));
|
||||
|
||||
}, result.Results.Count, 20);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task SqlExec([Leftover]string sql) =>
|
||||
InternalExecSql(sql);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeleteWaifus() =>
|
||||
SqlExec(DangerousCommandsService.WaifusDeleteSql);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeleteWaifu(IUser user) =>
|
||||
DeleteWaifu(user.Id);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeleteWaifu(ulong userId) =>
|
||||
InternalExecSql(DangerousCommandsService.WaifuDeleteSql, userId);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeleteCurrency() =>
|
||||
SqlExec(DangerousCommandsService.CurrencyDeleteSql);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeletePlaylists() =>
|
||||
SqlExec(DangerousCommandsService.MusicPlaylistDeleteSql);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task DeleteXp() =>
|
||||
SqlExec(DangerousCommandsService.XpDeleteSql);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task PurgeUser(ulong userId)
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithDescription(GetText("purge_user_confirm", Format.Bold(userId.ToString())));
|
||||
|
||||
if (!await PromptUserConfirmAsync(embed).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.PurgeUserAsync(userId);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task PurgeUser([Leftover]IUser user)
|
||||
=> PurgeUser(user.Id);
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[OwnerOnly]
|
||||
//public Task DeleteUnusedCrnQ() =>
|
||||
// SqlExec(DangerousCommandsService.DeleteUnusedCustomReactionsAndQuotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,87 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Common.TypeReaders;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class DiscordPermOverrideCommands : NadekoSubmodule<DiscordPermOverrideService>
|
||||
{
|
||||
// override stats, it should require that the user has managessages guild permission
|
||||
// .po 'stats' add user guild managemessages
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task DiscordPermOverride(CommandOrCrInfo cmd, params GuildPerm[] perms)
|
||||
{
|
||||
if (perms is null || perms.Length == 0)
|
||||
{
|
||||
await _service.RemoveOverride(ctx.Guild.Id, cmd.Name);
|
||||
await ReplyConfirmLocalizedAsync("perm_override_reset");
|
||||
return;
|
||||
}
|
||||
|
||||
var aggregatePerms = perms.Aggregate((acc, seed) => seed | acc);
|
||||
await _service.AddOverride(Context.Guild.Id, cmd.Name, aggregatePerms);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("perm_override",
|
||||
Format.Bold(aggregatePerms.ToString()),
|
||||
Format.Code(cmd.Name));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task DiscordPermOverrideReset()
|
||||
{
|
||||
var result = await PromptUserConfirmAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText("perm_override_all_confirm")));
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
await _service.ClearAllOverrides(Context.Guild.Id);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("perm_override_all");
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task DiscordPermOverrideList(int page = 1)
|
||||
{
|
||||
if (--page < 0)
|
||||
return;
|
||||
|
||||
var overrides = await _service.GetAllOverrides(Context.Guild.Id);
|
||||
|
||||
await ctx.SendPaginatedConfirmAsync(page, curPage =>
|
||||
{
|
||||
var eb = new EmbedBuilder()
|
||||
.WithTitle(GetText("perm_overrides"))
|
||||
.WithOkColor();
|
||||
|
||||
var thisPageOverrides = overrides
|
||||
.Skip(9 * curPage)
|
||||
.Take(9)
|
||||
.ToList();
|
||||
|
||||
if (thisPageOverrides.Count == 0)
|
||||
eb.WithDescription(GetText("perm_override_page_none"));
|
||||
else
|
||||
eb.WithDescription(string.Join("\n",
|
||||
thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")));
|
||||
|
||||
return eb;
|
||||
}, overrides.Count, 9, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
src/NadekoBot/Modules/Administration/GameChannelCommands.cs
Normal file
41
src/NadekoBot/Modules/Administration/GameChannelCommands.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class GameChannelCommands : NadekoSubmodule<GameVoiceChannelService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.MoveMembers)]
|
||||
public async Task GameVoiceChannel()
|
||||
{
|
||||
var vch = ((IGuildUser)ctx.User).VoiceChannel;
|
||||
|
||||
if (vch == null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("not_in_voice").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var id = _service.ToggleGameVoiceChannel(ctx.Guild.Id, vch.Id);
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("gvc_disabled").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_service.GameVoiceChannels.Add(vch.Id);
|
||||
await ReplyConfirmLocalizedAsync("gvc_enabled", Format.Bold(vch.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
263
src/NadekoBot/Modules/Administration/LocalizationCommands.cs
Normal file
263
src/NadekoBot/Modules/Administration/LocalizationCommands.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
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", "Українська, Україна"}
|
||||
};
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(0)]
|
||||
public async Task LanguageSet()
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("lang_set_show", Format.Bold(_cultureInfo.ToString()),
|
||||
Format.Bold(_cultureInfo.NativeName))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(1)]
|
||||
public async Task LanguageSet(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
Localization.RemoveGuildCulture(ctx.Guild);
|
||||
ci = Localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
Localization.SetGuildCulture(ctx.Guild, ci);
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("lang_set_fail").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task LanguageSetDefault()
|
||||
{
|
||||
var cul = Localization.DefaultCultureInfo;
|
||||
await ReplyConfirmLocalizedAsync("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task LanguageSetDefault(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
Localization.ResetDefaultCulture();
|
||||
ci = Localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
Localization.SetDefaultCulture(ci);
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("lang_set_bot", Format.Bold(ci.ToString()),
|
||||
Format.Bold(ci.NativeName)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("lang_set_fail").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task LanguagesList()
|
||||
{
|
||||
await ctx.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("lang_list"))
|
||||
.WithDescription(string.Join("\n",
|
||||
supportedLocales.Select(x => $"{Format.Code(x.Key),-10} => {x.Value}")))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* list of language codes for reference.
|
||||
* taken from https://github.com/dotnet/coreclr/blob/ee5862c6a257e60e263537d975ab6c513179d47f/src/mscorlib/src/System/Globalization/CultureData.cs#L192
|
||||
{ "029", "en-029" },
|
||||
{ "AE", "ar-AE" },
|
||||
{ "AF", "prs-AF" },
|
||||
{ "AL", "sq-AL" },
|
||||
{ "AM", "hy-AM" },
|
||||
{ "AR", "es-AR" },
|
||||
{ "AT", "de-AT" },
|
||||
{ "AU", "en-AU" },
|
||||
{ "AZ", "az-Cyrl-AZ" },
|
||||
{ "BA", "bs-Latn-BA" },
|
||||
{ "BD", "bn-BD" },
|
||||
{ "BE", "nl-BE" },
|
||||
{ "BG", "bg-BG" },
|
||||
{ "BH", "ar-BH" },
|
||||
{ "BN", "ms-BN" },
|
||||
{ "BO", "es-BO" },
|
||||
{ "BR", "pt-BR" },
|
||||
{ "BY", "be-BY" },
|
||||
{ "BZ", "en-BZ" },
|
||||
{ "CA", "en-CA" },
|
||||
{ "CH", "it-CH" },
|
||||
{ "CL", "es-CL" },
|
||||
{ "CN", "zh-CN" },
|
||||
{ "CO", "es-CO" },
|
||||
{ "CR", "es-CR" },
|
||||
{ "CS", "sr-Cyrl-CS" },
|
||||
{ "CZ", "cs-CZ" },
|
||||
{ "DE", "de-DE" },
|
||||
{ "DK", "da-DK" },
|
||||
{ "DO", "es-DO" },
|
||||
{ "DZ", "ar-DZ" },
|
||||
{ "EC", "es-EC" },
|
||||
{ "EE", "et-EE" },
|
||||
{ "EG", "ar-EG" },
|
||||
{ "ES", "es-ES" },
|
||||
{ "ET", "am-ET" },
|
||||
{ "FI", "fi-FI" },
|
||||
{ "FO", "fo-FO" },
|
||||
{ "FR", "fr-FR" },
|
||||
{ "GB", "en-GB" },
|
||||
{ "GE", "ka-GE" },
|
||||
{ "GL", "kl-GL" },
|
||||
{ "GR", "el-GR" },
|
||||
{ "GT", "es-GT" },
|
||||
{ "HK", "zh-HK" },
|
||||
{ "HN", "es-HN" },
|
||||
{ "HR", "hr-HR" },
|
||||
{ "HU", "hu-HU" },
|
||||
{ "ID", "id-ID" },
|
||||
{ "IE", "en-IE" },
|
||||
{ "IL", "he-IL" },
|
||||
{ "IN", "hi-IN" },
|
||||
{ "IQ", "ar-IQ" },
|
||||
{ "IR", "fa-IR" },
|
||||
{ "IS", "is-IS" },
|
||||
{ "IT", "it-IT" },
|
||||
{ "IV", "" },
|
||||
{ "JM", "en-JM" },
|
||||
{ "JO", "ar-JO" },
|
||||
{ "JP", "ja-JP" },
|
||||
{ "KE", "sw-KE" },
|
||||
{ "KG", "ky-KG" },
|
||||
{ "KH", "km-KH" },
|
||||
{ "KR", "ko-KR" },
|
||||
{ "KW", "ar-KW" },
|
||||
{ "KZ", "kk-KZ" },
|
||||
{ "LA", "lo-LA" },
|
||||
{ "LB", "ar-LB" },
|
||||
{ "LI", "de-LI" },
|
||||
{ "LK", "si-LK" },
|
||||
{ "LT", "lt-LT" },
|
||||
{ "LU", "lb-LU" },
|
||||
{ "LV", "lv-LV" },
|
||||
{ "LY", "ar-LY" },
|
||||
{ "MA", "ar-MA" },
|
||||
{ "MC", "fr-MC" },
|
||||
{ "ME", "sr-Latn-ME" },
|
||||
{ "MK", "mk-MK" },
|
||||
{ "MN", "mn-MN" },
|
||||
{ "MO", "zh-MO" },
|
||||
{ "MT", "mt-MT" },
|
||||
{ "MV", "dv-MV" },
|
||||
{ "MX", "es-MX" },
|
||||
{ "MY", "ms-MY" },
|
||||
{ "NG", "ig-NG" },
|
||||
{ "NI", "es-NI" },
|
||||
{ "NL", "nl-NL" },
|
||||
{ "NO", "nn-NO" },
|
||||
{ "NP", "ne-NP" },
|
||||
{ "NZ", "en-NZ" },
|
||||
{ "OM", "ar-OM" },
|
||||
{ "PA", "es-PA" },
|
||||
{ "PE", "es-PE" },
|
||||
{ "PH", "en-PH" },
|
||||
{ "PK", "ur-PK" },
|
||||
{ "PL", "pl-PL" },
|
||||
{ "PR", "es-PR" },
|
||||
{ "PT", "pt-PT" },
|
||||
{ "PY", "es-PY" },
|
||||
{ "QA", "ar-QA" },
|
||||
{ "RO", "ro-RO" },
|
||||
{ "RS", "sr-Latn-RS" },
|
||||
{ "RU", "ru-RU" },
|
||||
{ "RW", "rw-RW" },
|
||||
{ "SA", "ar-SA" },
|
||||
{ "SE", "sv-SE" },
|
||||
{ "SG", "zh-SG" },
|
||||
{ "SI", "sl-SI" },
|
||||
{ "SK", "sk-SK" },
|
||||
{ "SN", "wo-SN" },
|
||||
{ "SV", "es-SV" },
|
||||
{ "SY", "ar-SY" },
|
||||
{ "TH", "th-TH" },
|
||||
{ "TJ", "tg-Cyrl-TJ" },
|
||||
{ "TM", "tk-TM" },
|
||||
{ "TN", "ar-TN" },
|
||||
{ "TR", "tr-TR" },
|
||||
{ "TT", "en-TT" },
|
||||
{ "TW", "zh-TW" },
|
||||
{ "UA", "uk-UA" },
|
||||
{ "US", "en-US" },
|
||||
{ "UY", "es-UY" },
|
||||
{ "UZ", "uz-Cyrl-UZ" },
|
||||
{ "VE", "es-VE" },
|
||||
{ "VN", "vi-VN" },
|
||||
{ "YE", "ar-YE" },
|
||||
{ "ZA", "af-ZA" },
|
||||
{ "ZW", "en-ZW" }
|
||||
*/
|
134
src/NadekoBot/Modules/Administration/LogCommands.cs
Normal file
134
src/NadekoBot/Modules/Administration/LogCommands.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
#if !GLOBAL_NADEKO
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Common.TypeReaders.Models;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static NadekoBot.Modules.Administration.Services.LogCommandService;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
[NoPublicBot]
|
||||
public class LogCommands : NadekoSubmodule<LogCommandService>
|
||||
{
|
||||
public enum EnableDisable
|
||||
{
|
||||
Enable,
|
||||
Disable
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task LogServer(PermissionAction action)
|
||||
{
|
||||
await _service.LogServer(ctx.Guild.Id, ctx.Channel.Id, action.Value).ConfigureAwait(false);
|
||||
if (action.Value)
|
||||
await ReplyConfirmLocalizedAsync("log_all").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("log_disabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task LogIgnore()
|
||||
{
|
||||
var channel = (ITextChannel)ctx.Channel;
|
||||
|
||||
var removed = _service.LogIgnore(ctx.Guild.Id, ctx.Channel.Id);
|
||||
|
||||
if (!removed)
|
||||
await ReplyConfirmLocalizedAsync("log_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("log_not_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task LogEvents()
|
||||
{
|
||||
_service.GuildLogSettings.TryGetValue(ctx.Guild.Id, out LogSetting l);
|
||||
var str = string.Join("\n", Enum.GetNames(typeof(LogType))
|
||||
.Select(x =>
|
||||
{
|
||||
var val = l == null ? null : GetLogProperty(l, Enum.Parse<LogType>(x));
|
||||
if (val != null)
|
||||
return $"{Format.Bold(x)} <#{val}>";
|
||||
return Format.Bold(x);
|
||||
}));
|
||||
|
||||
await ctx.Channel.SendConfirmAsync(Format.Bold(GetText("log_events")) + "\n" +
|
||||
str)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static ulong? GetLogProperty(LogSetting l, LogType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Other:
|
||||
return l.LogOtherId;
|
||||
case LogType.MessageUpdated:
|
||||
return l.MessageUpdatedId;
|
||||
case LogType.MessageDeleted:
|
||||
return l.MessageDeletedId;
|
||||
case LogType.UserJoined:
|
||||
return l.UserJoinedId;
|
||||
case LogType.UserLeft:
|
||||
return l.UserLeftId;
|
||||
case LogType.UserBanned:
|
||||
return l.UserBannedId;
|
||||
case LogType.UserUnbanned:
|
||||
return l.UserUnbannedId;
|
||||
case LogType.UserUpdated:
|
||||
return l.UserUpdatedId;
|
||||
case LogType.ChannelCreated:
|
||||
return l.ChannelCreatedId;
|
||||
case LogType.ChannelDestroyed:
|
||||
return l.ChannelDestroyedId;
|
||||
case LogType.ChannelUpdated:
|
||||
return l.ChannelUpdatedId;
|
||||
case LogType.UserPresence:
|
||||
return l.LogUserPresenceId;
|
||||
case LogType.VoicePresence:
|
||||
return l.LogVoicePresenceId;
|
||||
case LogType.VoicePresenceTTS:
|
||||
return l.LogVoicePresenceTTSId;
|
||||
case LogType.UserMuted:
|
||||
return l.UserMutedId;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task Log(LogType type)
|
||||
{
|
||||
var val = _service.Log(ctx.Guild.Id, ctx.Channel.Id, type);
|
||||
|
||||
if (val)
|
||||
await ReplyConfirmLocalizedAsync("log", Format.Bold(type.ToString())).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("log_stop", Format.Bold(type.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
91
src/NadekoBot/Modules/Administration/ModuleCommands.cs
Normal file
91
src/NadekoBot/Modules/Administration/ModuleCommands.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
//using Discord.Commands;
|
||||
//using NadekoBot.Common.Attributes;
|
||||
//using NadekoBot.Modules.Administration.Services;
|
||||
//using NadekoBot.Extensions;
|
||||
//using System;
|
||||
//using System.IO;
|
||||
//using System.Reflection;
|
||||
//using System.Text.RegularExpressions;
|
||||
//using System.Threading.Tasks;
|
||||
//using System.Linq;
|
||||
|
||||
//namespace NadekoBot.Modules.Administration
|
||||
//{
|
||||
// public partial class Administration
|
||||
// {
|
||||
// [Group]
|
||||
// public class PackagesCommands : NadekoSubmodule<PackagesService>
|
||||
// {
|
||||
// private readonly NadekoBot _bot;
|
||||
|
||||
// public PackagesCommands(NadekoBot bot)
|
||||
// {
|
||||
// _bot = bot;
|
||||
// }
|
||||
|
||||
// [NadekoCommand, Usage, Description, Aliases]
|
||||
// [RequireContext(ContextType.Guild)]
|
||||
// public async Task PackageList()
|
||||
// {
|
||||
// _service.ReloadAvailablePackages();
|
||||
// await Context.Channel.SendConfirmAsync(
|
||||
// string.Join(
|
||||
// "\n",
|
||||
// _service.Packages
|
||||
// .Select(x => _bot.LoadedPackages.Contains(x)
|
||||
// ? "【✘】" + x
|
||||
// : "【 】" + x)));
|
||||
// }
|
||||
|
||||
// [NadekoCommand, Usage, Description, Aliases]
|
||||
// [RequireContext(ContextType.Guild)]
|
||||
// [OwnerOnly]
|
||||
// public async Task PackageUnload(string name)
|
||||
// {
|
||||
// if (name.Contains(":") || name.Contains(".") || name.Contains("\\") || name.Contains("/") || name.Contains("~"))
|
||||
// return;
|
||||
// name = name.ToTitleCase();
|
||||
// var package = Assembly.LoadFrom(Path.Combine(AppContext.BaseDirectory,
|
||||
// "modules",
|
||||
// $"NadekoBot.Modules.{name}",
|
||||
// $"NadekoBot.Modules.{name}.dll"));
|
||||
|
||||
// await _bot.UnloadPackage(name).ConfigureAwait(false);
|
||||
// await ReplyAsync(":ok:");
|
||||
// }
|
||||
|
||||
// [NadekoCommand, Usage, Description, Aliases]
|
||||
// [RequireContext(ContextType.Guild)]
|
||||
// [OwnerOnly]
|
||||
// public async Task PackageLoad(string name)
|
||||
// {
|
||||
// if (name.Contains(".") || name.Contains("\\") || name.Contains("/") || name.Contains("~"))
|
||||
// return;
|
||||
// name = name.ToTitleCase();
|
||||
|
||||
// if (await _bot.LoadPackage(name))
|
||||
// await ReplyAsync(":ok:");
|
||||
// else
|
||||
// await ReplyAsync(":x:");
|
||||
// }
|
||||
|
||||
// [NadekoCommand, Usage, Description, Aliases]
|
||||
// [RequireContext(ContextType.Guild)]
|
||||
// [OwnerOnly]
|
||||
// public async Task PackageReload(string name)
|
||||
// {
|
||||
// if (name.Contains(".") || name.Contains("\\") || name.Contains("/") || name.Contains("~"))
|
||||
// return;
|
||||
// name = name.ToTitleCase();
|
||||
|
||||
// if (await _bot.UnloadPackage(name))
|
||||
// {
|
||||
// await _bot.LoadPackage(name);
|
||||
// await ReplyAsync(":ok:");
|
||||
// }
|
||||
// else
|
||||
// await ReplyAsync(":x:");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
237
src/NadekoBot/Modules/Administration/MuteCommands.cs
Normal file
237
src/NadekoBot/Modules/Administration/MuteCommands.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class MuteCommands : NadekoSubmodule<MuteService>
|
||||
{
|
||||
private async Task<bool> VerifyMutePermissions(IGuildUser runnerUser, IGuildUser targetUser)
|
||||
{
|
||||
var runnerUserRoles = runnerUser.GetRoles();
|
||||
var targetUserRoles = targetUser.GetRoles();
|
||||
if (runnerUser.Id != ctx.Guild.OwnerId &&
|
||||
runnerUserRoles.Max(x => x.Position) <= targetUserRoles.Max(x => x.Position))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_perms").ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task MuteRole([Leftover] IRole role = null)
|
||||
{
|
||||
if (role is null)
|
||||
{
|
||||
var muteRole = await _service.GetMuteRole(ctx.Guild).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("mute_role", Format.Code(muteRole.Name)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Context.User.Id != Context.Guild.OwnerId &&
|
||||
role.Position >= ((SocketGuildUser) Context.User).Roles.Max(x => x.Position))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_u").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.SetMuteRoleAsync(ctx.Guild.Id, role.Name).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("mute_role_set").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
||||
[Priority(0)]
|
||||
public async Task Mute(IGuildUser target, [Leftover] string reason = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, target))
|
||||
return;
|
||||
|
||||
await _service.MuteUser(target, ctx.User, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_muted", Format.Bold(target.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex.ToString());
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
||||
[Priority(1)]
|
||||
public async Task Mute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_muted_time", Format.Bold(user.ToString()), (int)time.Time.TotalMinutes).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in mute command");
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
||||
public async Task Unmute(IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
await _service.UnmuteUser(user.GuildId, user.Id, ctx.User, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public async Task ChatMute(IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.MuteUser(user, ctx.User, MuteType.Chat, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex.ToString());
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(1)]
|
||||
public async Task ChatMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_chat_mute_time", Format.Bold(user.ToString()), (int)time.Time.TotalMinutes).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex.ToString());
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.MuteMembers)]
|
||||
[Priority(0)]
|
||||
public async Task VoiceMute(IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.MuteUser(user, ctx.User, MuteType.Voice, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.MuteMembers)]
|
||||
[Priority(1)]
|
||||
public async Task VoiceMute(StoopidTime time,IGuildUser user, [Leftover] string reason = "")
|
||||
{
|
||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_voice_mute_time", Format.Bold(user.ToString()), (int)time.Time.TotalMinutes).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
using Discord.Commands;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using Discord;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class PlayingRotateCommands : NadekoSubmodule<PlayingRotateService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task RotatePlaying()
|
||||
{
|
||||
if (_service.ToggleRotatePlaying())
|
||||
await ReplyConfirmLocalizedAsync("ropl_enabled").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("ropl_disabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task AddPlaying(ActivityType t, [Leftover] string status)
|
||||
{
|
||||
await _service.AddPlaying(t, status).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("ropl_added").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ListPlaying()
|
||||
{
|
||||
var statuses = _service.GetRotatingStatuses();
|
||||
|
||||
if (!statuses.Any())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("ropl_not_set").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 1;
|
||||
await ReplyConfirmLocalizedAsync("ropl_list",
|
||||
string.Join("\n\t", statuses.Select(rs => $"`{i++}.` *{rs.Type}* {rs.Status}")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task RemovePlaying(int index)
|
||||
{
|
||||
index -= 1;
|
||||
|
||||
var msg = await _service.RemovePlayingAsync(index).ConfigureAwait(false);
|
||||
|
||||
if (msg == null)
|
||||
return;
|
||||
|
||||
await ReplyConfirmLocalizedAsync("reprm", msg).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
src/NadekoBot/Modules/Administration/PrefixCommands.cs
Normal file
64
src/NadekoBot/Modules/Administration/PrefixCommands.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class PrefixCommands : NadekoSubmodule
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[Priority(1)]
|
||||
public async Task PrefixCommand()
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("prefix_current", Format.Code(CmdHandler.GetPrefix(ctx.Guild))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum Set
|
||||
{
|
||||
Set
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(0)]
|
||||
public Task PrefixCommand(Set _, [Leftover] string prefix)
|
||||
=> PrefixCommand(prefix);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(0)]
|
||||
public async Task PrefixCommand([Leftover]string prefix)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(prefix))
|
||||
return;
|
||||
|
||||
var oldPrefix = base.Prefix;
|
||||
var newPrefix = CmdHandler.SetPrefix(ctx.Guild, prefix);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("prefix_new", Format.Code(oldPrefix), Format.Code(newPrefix)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task DefPrefix([Leftover]string prefix = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(prefix))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("defprefix_current", CmdHandler.GetPrefix()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var oldPrefix = CmdHandler.GetPrefix();
|
||||
var newPrefix = CmdHandler.SetDefaultPrefix(prefix);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("defprefix_new", Format.Code(oldPrefix), Format.Code(newPrefix)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
295
src/NadekoBot/Modules/Administration/ProtectionCommands.cs
Normal file
295
src/NadekoBot/Modules/Administration/ProtectionCommands.cs
Normal file
@@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Common;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class ProtectionCommands : NadekoSubmodule<ProtectionService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task AntiAlt()
|
||||
{
|
||||
if (await _service.TryStopAntiAlt(ctx.Guild.Id))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("prot_disable", "Anti-Alt");
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync("protection_not_running", "Anti-Alt");
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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;
|
||||
|
||||
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
||||
return;
|
||||
|
||||
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, (int?)punishTime?.Time.TotalMinutes ?? 0);
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover]IRole role)
|
||||
{
|
||||
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
||||
|
||||
if (minAgeMinutes < 1)
|
||||
return;
|
||||
|
||||
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, roleId: role.Id);
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public Task AntiRaid()
|
||||
{
|
||||
if (_service.TryStopAntiRaid(ctx.Guild.Id))
|
||||
{
|
||||
return ReplyConfirmLocalizedAsync("prot_disable", "Anti-Raid");
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyErrorLocalizedAsync("protection_not_running", "Anti-Raid");
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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);
|
||||
|
||||
[NadekoCommand, Usage, Description, 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)
|
||||
{
|
||||
if (action == PunishmentAction.AddRole)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("punishment_unsupported", action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userThreshold < 2 || userThreshold > 30)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("raid_cnt", 2, 30).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seconds < 2 || seconds > 300)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("raid_time", 2, 300).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(punishTime is null))
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("prot_cant_use_time");
|
||||
}
|
||||
}
|
||||
|
||||
var time = (int?) punishTime?.Time.TotalMinutes ?? 0;
|
||||
if (time < 0 || time > 60 * 24)
|
||||
return;
|
||||
|
||||
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds,
|
||||
action, time).ConfigureAwait(false);
|
||||
|
||||
if (stats == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Raid"),
|
||||
$"{ctx.User.Mention} {GetAntiRaidString(stats)}")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public Task AntiSpam()
|
||||
{
|
||||
if (_service.TryStopAntiSpam(ctx.Guild.Id))
|
||||
{
|
||||
return ReplyConfirmLocalizedAsync("prot_disable", "Anti-Spam");
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyErrorLocalizedAsync("protection_not_running", "Anti-Spam");
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(0)]
|
||||
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] IRole role)
|
||||
{
|
||||
if (action != PunishmentAction.AddRole)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return InternalAntiSpam(messageCount, action, null, role);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[Priority(1)]
|
||||
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] StoopidTime punishTime)
|
||||
=> InternalAntiSpam(messageCount, action, punishTime, null);
|
||||
|
||||
[NadekoCommand, Usage, Description, 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)
|
||||
{
|
||||
if (messageCount < 2 || messageCount > 10)
|
||||
return;
|
||||
|
||||
if (!(timeData is null))
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("prot_cant_use_time");
|
||||
}
|
||||
}
|
||||
|
||||
var time = (int?) timeData?.Time.TotalMinutes ?? 0;
|
||||
if (time < 0 || time > 60 * 24)
|
||||
return;
|
||||
|
||||
var stats = await _service.StartAntiSpamAsync(ctx.Guild.Id, messageCount, action, time, role?.Id).ConfigureAwait(false);
|
||||
|
||||
await ctx.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Spam"),
|
||||
$"{ctx.User.Mention} {GetAntiSpamString(stats)}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task AntispamIgnore()
|
||||
{
|
||||
var added = await _service.AntiSpamIgnoreAsync(ctx.Guild.Id, ctx.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if(added is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("protection_not_running", "Anti-Spam").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(added.Value ? "spam_ignore" : "spam_not_ignore", "Anti-Spam").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task AntiList()
|
||||
{
|
||||
var (spam, raid, alt) = _service.GetAntiStats(ctx.Guild.Id);
|
||||
|
||||
if (spam is null && raid is null && alt is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("prot_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("prot_active"));
|
||||
|
||||
if (spam != null)
|
||||
embed.AddField(efb => efb.WithName("Anti-Spam")
|
||||
.WithValue(GetAntiSpamString(spam).TrimTo(1024))
|
||||
.WithIsInline(true));
|
||||
|
||||
if (raid != null)
|
||||
embed.AddField(efb => efb.WithName("Anti-Raid")
|
||||
.WithValue(GetAntiRaidString(raid).TrimTo(1024))
|
||||
.WithIsInline(true));
|
||||
|
||||
if (!(alt is null))
|
||||
embed.AddField("Anti-Alt", GetAntiAltString(alt), true);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private string GetAntiAltString(AntiAltStats alt)
|
||||
=> GetText("anti_alt_status",
|
||||
Format.Bold(alt.MinAge.ToString(@"dd\d\ hh\h\ mm\m\ ")),
|
||||
Format.Bold(alt.Action.ToString()),
|
||||
Format.Bold(alt.Counter.ToString()));
|
||||
|
||||
private string GetAntiSpamString(AntiSpamStats stats)
|
||||
{
|
||||
var settings = stats.AntiSpamSettings;
|
||||
var ignoredString = string.Join(", ", settings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ignoredString))
|
||||
ignoredString = "none";
|
||||
|
||||
string add = "";
|
||||
if (settings.MuteTime > 0)
|
||||
{
|
||||
add = $" ({TimeSpan.FromMinutes(settings.MuteTime):hh\\hmm\\m})";
|
||||
}
|
||||
|
||||
return GetText("spam_stats",
|
||||
Format.Bold(settings.MessageThreshold.ToString()),
|
||||
Format.Bold(settings.Action.ToString() + add),
|
||||
ignoredString);
|
||||
}
|
||||
|
||||
private string GetAntiRaidString(AntiRaidStats stats)
|
||||
{
|
||||
var actionString = Format.Bold(stats.AntiRaidSettings.Action.ToString());
|
||||
|
||||
if (stats.AntiRaidSettings.PunishDuration > 0)
|
||||
{
|
||||
actionString += $" **({TimeSpan.FromMinutes(stats.AntiRaidSettings.PunishDuration):hh\\hmm\\m})**";
|
||||
}
|
||||
|
||||
return GetText("raid_stats",
|
||||
Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
|
||||
Format.Bold(stats.AntiRaidSettings.Seconds.ToString()),
|
||||
actionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
src/NadekoBot/Modules/Administration/PruneCommands.cs
Normal file
85
src/NadekoBot/Modules/Administration/PruneCommands.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using ITextChannel = Discord.ITextChannel;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class PruneCommands : NadekoSubmodule<PruneService>
|
||||
{
|
||||
private static readonly TimeSpan twoWeeks = TimeSpan.FromDays(14);
|
||||
|
||||
//delets her own messages, no perm required
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Prune(string parameter = null)
|
||||
{
|
||||
var user = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
|
||||
if (parameter == "-s" || parameter == "--safe")
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, 100, (x) => x.Author.Id == user.Id && !x.IsPinned).ConfigureAwait(false);
|
||||
else
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, 100, (x) => x.Author.Id == user.Id).ConfigureAwait(false);
|
||||
ctx.Message.DeleteAfter(3);
|
||||
}
|
||||
// prune x
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
[Priority(1)]
|
||||
public async Task Prune(int count, string parameter = null)
|
||||
{
|
||||
count++;
|
||||
if (count < 1)
|
||||
return;
|
||||
if (count > 1000)
|
||||
count = 1000;
|
||||
|
||||
if (parameter == "-s" || parameter == "--safe")
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, count, (x) => !x.IsPinned).ConfigureAwait(false);
|
||||
else
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, count, x => true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//prune @user [x]
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Prune(IGuildUser user, int count = 100, string parameter = null)
|
||||
=> Prune(user.Id, count, parameter);
|
||||
|
||||
//prune userid [x]
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public async Task Prune(ulong userId, int count = 100, string parameter = null)
|
||||
{
|
||||
if (userId == ctx.User.Id)
|
||||
count++;
|
||||
|
||||
if (count < 1)
|
||||
return;
|
||||
|
||||
if (count > 1000)
|
||||
count = 1000;
|
||||
|
||||
if (parameter == "-s" || parameter == "--safe")
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, count, m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks && !m.IsPinned).ConfigureAwait(false);
|
||||
else
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel, count, m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < twoWeeks).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
345
src/NadekoBot/Modules/Administration/RoleCommands.cs
Normal file
345
src/NadekoBot/Modules/Administration/RoleCommands.cs
Normal file
@@ -0,0 +1,345 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
public class RoleCommands : NadekoSubmodule<RoleCommandsService>
|
||||
{
|
||||
private IServiceProvider _services;
|
||||
public enum Exclude { Excl }
|
||||
|
||||
public RoleCommands(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task InternalReactionRoles(bool exclusive, params string[] input)
|
||||
{
|
||||
var msgs = await ((SocketTextChannel)ctx.Channel).GetMessagesAsync().FlattenAsync().ConfigureAwait(false);
|
||||
var prev = (IUserMessage)msgs.FirstOrDefault(x => x is IUserMessage && x.Id != ctx.Message.Id);
|
||||
|
||||
if (prev == null)
|
||||
return;
|
||||
|
||||
if (input.Length % 2 != 0)
|
||||
return;
|
||||
|
||||
var grp = 0;
|
||||
var results = input
|
||||
.GroupBy(x => grp++ / 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);
|
||||
|
||||
var all = await Task.WhenAll(results);
|
||||
|
||||
if (!all.Any())
|
||||
return;
|
||||
|
||||
foreach (var x in all)
|
||||
{
|
||||
try
|
||||
{
|
||||
await prev.AddReactionAsync(x.emote, new RequestOptions()
|
||||
{
|
||||
RetryMode = RetryMode.Retry502 | RetryMode.RetryRatelimit
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch (Discord.Net.HttpException ex) when(ex.HttpCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("reaction_cant_access", Format.Code(x.emote.ToString()));
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (_service.Add(ctx.Guild.Id, new ReactionRoleMessage()
|
||||
{
|
||||
Exclusive = exclusive,
|
||||
MessageId = prev.Id,
|
||||
ChannelId = prev.Channel.Id,
|
||||
ReactionRoles = all.Select(x =>
|
||||
{
|
||||
return new ReactionRole()
|
||||
{
|
||||
EmoteName = x.emote.ToString(),
|
||||
RoleId = x.role.Id,
|
||||
};
|
||||
}).ToList(),
|
||||
}))
|
||||
{
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("reaction_roles_full").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[NoPublicBot]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public Task ReactionRoles(params string[] input) =>
|
||||
InternalReactionRoles(false, input);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[NoPublicBot]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(1)]
|
||||
public Task ReactionRoles(Exclude _, params string[] input) =>
|
||||
InternalReactionRoles(true, input);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[NoPublicBot]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task ReactionRolesList()
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor();
|
||||
if (!_service.Get(ctx.Guild.Id, out var rrs) ||
|
||||
!rrs.Any())
|
||||
{
|
||||
embed.WithDescription(GetText("no_reaction_roles"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var g = ((SocketGuild)ctx.Guild);
|
||||
foreach (var rr in rrs)
|
||||
{
|
||||
var ch = g.GetTextChannel(rr.ChannelId);
|
||||
IUserMessage msg = null;
|
||||
if (!(ch is null))
|
||||
{
|
||||
msg = await ch.GetMessageAsync(rr.MessageId).ConfigureAwait(false) as IUserMessage;
|
||||
}
|
||||
var content = msg?.Content.TrimTo(30) ?? "DELETED!";
|
||||
embed.AddField($"**{rr.Index + 1}.** {(ch?.Name ?? "DELETED!")}",
|
||||
GetText("reaction_roles_message", rr.ReactionRoles?.Count ?? 0, content));
|
||||
}
|
||||
}
|
||||
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
index--;
|
||||
var rr = rrs[index];
|
||||
_service.Remove(ctx.Guild.Id, index);
|
||||
await ReplyConfirmLocalizedAsync("reaction_role_removed", index + 1).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task SetRole(IGuildUser targetUser, [Leftover] IRole roleToAdd)
|
||||
{
|
||||
var runnerUser = (IGuildUser)ctx.User;
|
||||
var runnerMaxRolePosition = runnerUser.GetRoles().Max(x => x.Position);
|
||||
if ((ctx.User.Id != ctx.Guild.OwnerId) && runnerMaxRolePosition <= roleToAdd.Position)
|
||||
return;
|
||||
try
|
||||
{
|
||||
await targetUser.AddRoleAsync(roleToAdd).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("setrole", Format.Bold(roleToAdd.Name), Format.Bold(targetUser.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in setrole command");
|
||||
await ReplyErrorLocalizedAsync("setrole_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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)
|
||||
return;
|
||||
try
|
||||
{
|
||||
await targetUser.RemoveRoleAsync(roleToRemove).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("remrole", Format.Bold(roleToRemove.Name), Format.Bold(targetUser.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("remrole_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
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)
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (roleToEdit.Position > (await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("renrole_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("renrole").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("renrole_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task RemoveAllRoles([Leftover] IGuildUser user)
|
||||
{
|
||||
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)))
|
||||
return;
|
||||
try
|
||||
{
|
||||
await user.RemoveRolesAsync(userRoles).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("rar_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task CreateRole([Leftover] string roleName = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roleName))
|
||||
return;
|
||||
|
||||
var r = await ctx.Guild.CreateRoleAsync(roleName, isMentionable: false).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("cr", Format.Bold(r.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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)
|
||||
return;
|
||||
|
||||
await role.DeleteAsync().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("dr", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task RoleHoist(IRole role)
|
||||
{
|
||||
var newHoisted = !role.IsHoisted;
|
||||
await role.ModifyAsync(r => r.Hoist = newHoisted).ConfigureAwait(false);
|
||||
if (newHoisted)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("rolehoist_enabled", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("rolehoist_disabled", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public async Task RoleColor([Leftover] IRole role)
|
||||
{
|
||||
await ctx.Channel.SendConfirmAsync("Role Color", role.Color.RawValue.ToString("x6")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public async Task RoleColor(SixLabors.ImageSharp.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)).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("rc", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("rc_perms").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,272 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Modules.Administration.Services;
|
||||
using NadekoBot.Extensions;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class SelfAssignedRolesCommands : NadekoSubmodule<SelfAssignedRolesService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
public async Task AdSarm()
|
||||
{
|
||||
var newVal = _service.ToggleAdSarm(ctx.Guild.Id);
|
||||
|
||||
if (newVal)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("adsarm_enable", Prefix).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("adsarm_disable", Prefix).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(1)]
|
||||
public Task Asar([Leftover] IRole role) =>
|
||||
Asar(0, role);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public async Task Asar(int group, [Leftover] IRole role)
|
||||
{
|
||||
var guser = (IGuildUser)ctx.User;
|
||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
||||
return;
|
||||
|
||||
var succ = _service.AddNew(ctx.Guild.Id, role, group);
|
||||
|
||||
if (succ)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("role_added", Format.Bold(role.Name), Format.Bold(group.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("role_in_list", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public async Task Sargn(int group, [Leftover] string name = null)
|
||||
{
|
||||
var guser = (IGuildUser)ctx.User;
|
||||
|
||||
var set = await _service.SetNameAsync(ctx.Guild.Id, group, name).ConfigureAwait(false);
|
||||
|
||||
if (set)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("group_name_added", Format.Bold(group.ToString()), Format.Bold(name.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("group_name_removed", Format.Bold(group.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Rsar([Leftover] IRole role)
|
||||
{
|
||||
var guser = (IGuildUser)ctx.User;
|
||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
||||
return;
|
||||
|
||||
bool success = _service.RemoveSar(role.Guild.Id, role.Id);
|
||||
if (!success)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("self_assign_not").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("self_assign_rem", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Lsar(int page = 1)
|
||||
{
|
||||
if (--page < 0)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
var groupNameText = "";
|
||||
if (!groups.TryGetValue(kvp.Key, out var name))
|
||||
{
|
||||
groupNameText = Format.Bold(GetText("self_assign_group", kvp.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
|
||||
}
|
||||
|
||||
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
|
||||
foreach (var (Model, Role) in kvp.AsEnumerable())
|
||||
{
|
||||
if (Role == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
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 new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(Format.Bold(GetText("self_assign_list", roles.Count())))
|
||||
.WithDescription(rolesStr.ToString())
|
||||
.WithFooter(exclusive
|
||||
? GetText("self_assign_are_exclusive")
|
||||
: GetText("self_assign_are_not_exclusive"));
|
||||
}, roles.Count(), 20).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Togglexclsar()
|
||||
{
|
||||
bool areExclusive = _service.ToggleEsar(ctx.Guild.Id);
|
||||
if (areExclusive)
|
||||
await ReplyConfirmLocalizedAsync("self_assign_excl").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("self_assign_no_excl").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
public async Task RoleLevelReq(int level, [Leftover] IRole role)
|
||||
{
|
||||
if (level < 0)
|
||||
return;
|
||||
|
||||
bool succ = _service.SetLevelReq(ctx.Guild.Id, role, level);
|
||||
|
||||
if (!succ)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("self_assign_not").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("self_assign_level_req",
|
||||
Format.Bold(role.Name),
|
||||
Format.Bold(level.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Iam([Leftover] IRole role)
|
||||
{
|
||||
var guildUser = (IGuildUser)ctx.User;
|
||||
|
||||
var (result, autoDelete, extra) = await _service.Assign(guildUser, role).ConfigureAwait(false);
|
||||
|
||||
IUserMessage msg;
|
||||
if (result == SelfAssignedRolesService.AssignResult.Err_Not_Assignable)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_not").ConfigureAwait(false);
|
||||
}
|
||||
else if (result == SelfAssignedRolesService.AssignResult.Err_Lvl_Req)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_not_level", Format.Bold(extra.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else if (result == SelfAssignedRolesService.AssignResult.Err_Already_Have)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_already", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else if (result == SelfAssignedRolesService.AssignResult.Err_Not_Perms)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_perms").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = await ReplyConfirmLocalizedAsync("self_assign_success", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (autoDelete)
|
||||
{
|
||||
msg.DeleteAfter(3);
|
||||
ctx.Message.DeleteAfter(3);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Iamnot([Leftover] IRole role)
|
||||
{
|
||||
var guildUser = (IGuildUser)ctx.User;
|
||||
|
||||
var (result, autoDelete) = await _service.Remove(guildUser, role).ConfigureAwait(false);
|
||||
|
||||
IUserMessage msg;
|
||||
if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Assignable)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_not").ConfigureAwait(false);
|
||||
}
|
||||
else if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Have)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_not_have", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else if (result == SelfAssignedRolesService.RemoveResult.Err_Not_Perms)
|
||||
{
|
||||
msg = await ReplyErrorLocalizedAsync("self_assign_perms").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = await ReplyConfirmLocalizedAsync("self_assign_remove", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (autoDelete)
|
||||
{
|
||||
msg.DeleteAfter(3);
|
||||
ctx.Message.DeleteAfter(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
546
src/NadekoBot/Modules/Administration/SelfCommands.cs
Normal file
546
src/NadekoBot/Modules/Administration/SelfCommands.cs
Normal file
@@ -0,0 +1,546 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Core.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class SelfCommands : NadekoSubmodule<SelfService>
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly NadekoBot _bot;
|
||||
private readonly IBotStrings _strings;
|
||||
|
||||
public SelfCommands(DiscordSocketClient client, NadekoBot bot, IBotStrings strings)
|
||||
{
|
||||
_client = client;
|
||||
_bot = bot;
|
||||
_strings = strings;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandAdd([Leftover] string cmdText)
|
||||
{
|
||||
if (cmdText.StartsWith(Prefix + "die", StringComparison.InvariantCulture))
|
||||
return;
|
||||
|
||||
var guser = (IGuildUser)ctx.User;
|
||||
var cmd = new AutoCommand()
|
||||
{
|
||||
CommandText = cmdText,
|
||||
ChannelId = ctx.Channel.Id,
|
||||
ChannelName = ctx.Channel.Name,
|
||||
GuildId = ctx.Guild?.Id,
|
||||
GuildName = ctx.Guild?.Name,
|
||||
VoiceChannelId = guser.VoiceChannel?.Id,
|
||||
VoiceChannelName = guser.VoiceChannel?.Name,
|
||||
Interval = 0,
|
||||
};
|
||||
_service.AddNewAutoCommand(cmd);
|
||||
|
||||
await ctx.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("scadd"))
|
||||
.AddField(efb => efb.WithName(GetText("server"))
|
||||
.WithValue(cmd.GuildId == null ? $"-" : $"{cmd.GuildName}/{cmd.GuildId}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("channel"))
|
||||
.WithValue($"{cmd.ChannelName}/{cmd.ChannelId}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("command_text"))
|
||||
.WithValue(cmdText).WithIsInline(false))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task AutoCommandAdd(int interval, [Leftover] string cmdText)
|
||||
{
|
||||
if (cmdText.StartsWith(Prefix + "die", StringComparison.InvariantCulture))
|
||||
return;
|
||||
|
||||
if (interval < 5)
|
||||
return;
|
||||
|
||||
var guser = (IGuildUser)ctx.User;
|
||||
var cmd = new AutoCommand()
|
||||
{
|
||||
CommandText = cmdText,
|
||||
ChannelId = ctx.Channel.Id,
|
||||
ChannelName = ctx.Channel.Name,
|
||||
GuildId = ctx.Guild?.Id,
|
||||
GuildName = ctx.Guild?.Name,
|
||||
VoiceChannelId = guser.VoiceChannel?.Id,
|
||||
VoiceChannelName = guser.VoiceChannel?.Name,
|
||||
Interval = interval,
|
||||
};
|
||||
_service.AddNewAutoCommand(cmd);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("autocmd_add", Format.Code(Format.Sanitize(cmdText)), cmd.Interval).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandsList(int page = 1)
|
||||
{
|
||||
if (page-- < 1)
|
||||
return;
|
||||
|
||||
var scmds = _service.GetStartupCommands()
|
||||
.Skip(page * 5)
|
||||
.Take(5)
|
||||
.ToList();
|
||||
|
||||
if (scmds.Count == 0)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("startcmdlist_none").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 0;
|
||||
await ctx.Channel.SendConfirmAsync(
|
||||
text: string.Join("\n", scmds
|
||||
.Select(x => $@"```css
|
||||
#{++i + page * 5}
|
||||
[{GetText("server")}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
|
||||
[{GetText("channel")}]: {x.ChannelName} #{x.ChannelId}
|
||||
[{GetText("command_text")}]: {x.CommandText}```")),
|
||||
title: string.Empty,
|
||||
footer: GetText("page", page + 1))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task AutoCommandsList(int page = 1)
|
||||
{
|
||||
if (page-- < 1)
|
||||
return;
|
||||
|
||||
var scmds = _service.GetAutoCommands()
|
||||
.Skip(page * 5)
|
||||
.Take(5)
|
||||
.ToList();
|
||||
if (!scmds.Any())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("autocmdlist_none").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 0;
|
||||
await ctx.Channel.SendConfirmAsync(
|
||||
text: string.Join("\n", scmds
|
||||
.Select(x => $@"```css
|
||||
#{++i + page * 5}
|
||||
[{GetText("server")}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
|
||||
[{GetText("channel")}]: {x.ChannelName} #{x.ChannelId}
|
||||
{GetIntervalText(x.Interval)}
|
||||
[{GetText("command_text")}]: {x.CommandText}```")),
|
||||
title: string.Empty,
|
||||
footer: GetText("page", page + 1))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetIntervalText(int interval)
|
||||
{
|
||||
return $"[{GetText("interval")}]: {interval}";
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Wait(int miliseconds)
|
||||
{
|
||||
if (miliseconds <= 0)
|
||||
return;
|
||||
ctx.Message.DeleteAfter(0);
|
||||
try
|
||||
{
|
||||
var msg = await ctx.Channel.SendConfirmAsync($"⏲ {miliseconds}ms")
|
||||
.ConfigureAwait(false);
|
||||
msg.DeleteAfter(miliseconds / 1000);
|
||||
}
|
||||
catch { }
|
||||
|
||||
await Task.Delay(miliseconds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task AutoCommandRemove([Leftover] int index)
|
||||
{
|
||||
if (!_service.RemoveAutoCommand(--index, out _))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("acrm_fail").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandRemove([Leftover] int index)
|
||||
{
|
||||
if (!_service.RemoveStartupCommand(--index, out _))
|
||||
await ReplyErrorLocalizedAsync("scrm_fail").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("scrm").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandsClear()
|
||||
{
|
||||
_service.ClearStartupCommands();
|
||||
|
||||
await ReplyConfirmLocalizedAsync("startcmds_cleared").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardMessages()
|
||||
{
|
||||
var enabled = _service.ForwardMessages();
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync("fwdm_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("fwdm_stop").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardToAll()
|
||||
{
|
||||
var enabled = _service.ForwardToAll();
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync("fwall_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("fwall_stop").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task ShardStats(int page = 1)
|
||||
{
|
||||
if (--page < 0)
|
||||
return;
|
||||
|
||||
var statuses = _service.GetAllShardStatuses();
|
||||
|
||||
var status = string.Join(", ", statuses
|
||||
.GroupBy(x => x.ConnectionState)
|
||||
.Select(x => $"{x.Count()} {x.Key}")
|
||||
.ToArray());
|
||||
|
||||
var allShardStrings = statuses
|
||||
.Select(x =>
|
||||
{
|
||||
var timeDiff = DateTime.UtcNow - x.Time;
|
||||
if (timeDiff >= TimeSpan.FromSeconds(30))
|
||||
return $"Shard #{Format.Bold(x.ShardId.ToString())} **UNRESPONSIVE** for {timeDiff.ToString(@"hh\:mm\:ss")}";
|
||||
return GetText("shard_stats_txt", x.ShardId.ToString(),
|
||||
Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.ToString()), timeDiff.ToString(@"hh\:mm\:ss"));
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
await ctx.SendPaginatedConfirmAsync(page, (curPage) =>
|
||||
{
|
||||
|
||||
var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
str = GetText("no_shards_on_page");
|
||||
|
||||
return new EmbedBuilder()
|
||||
.WithAuthor(a => a.WithName(GetText("shard_stats")))
|
||||
.WithTitle(status)
|
||||
.WithOkColor()
|
||||
.WithDescription(str);
|
||||
}, allShardStrings.Length, 25).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task RestartShard(int shardId)
|
||||
{
|
||||
var success = _service.RestartShard(shardId);
|
||||
if (success)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("shard_reconnecting", Format.Bold("#" + shardId)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("no_shard_id").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public Task Leave([Leftover] string guildStr)
|
||||
{
|
||||
return _service.LeaveGuild(guildStr);
|
||||
}
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Die()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("shutting_down").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
_service.Die();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Restart()
|
||||
{
|
||||
bool success = _service.RestartBot();
|
||||
if (!success)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("restart_fail").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try { await ReplyConfirmLocalizedAsync("restarting").ConfigureAwait(false); } catch { }
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task SetName([Leftover] string newName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await _client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
|
||||
}
|
||||
catch (RateLimitedException)
|
||||
{
|
||||
Log.Warning("You've been ratelimited. Wait 2 hours to change your name");
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("bot_name", Format.Bold(newName)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[UserPerm(GuildPerm.ManageNicknames)]
|
||||
[BotPerm(GuildPerm.ChangeNickname)]
|
||||
[Priority(0)]
|
||||
public async Task SetNick([Leftover] string newNick = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(newNick))
|
||||
return;
|
||||
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("bot_nick", Format.Bold(newNick) ?? "-").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[BotPerm(GuildPerm.ManageNicknames)]
|
||||
[UserPerm(GuildPerm.ManageNicknames)]
|
||||
[Priority(1)]
|
||||
public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null)
|
||||
{
|
||||
var sg = (SocketGuild) Context.Guild;
|
||||
if (sg.OwnerId == gu.Id ||
|
||||
gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("insuf_perms_i");
|
||||
return;
|
||||
}
|
||||
|
||||
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("user_nick", Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task SetStatus([Leftover] SettableUserStatus status)
|
||||
{
|
||||
await _client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task SetAvatar([Leftover] string img = null)
|
||||
{
|
||||
var success = await _service.SetAvatar(img);
|
||||
|
||||
if (success)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("set_avatar").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task SetGame(ActivityType type, [Leftover] string game = null)
|
||||
{
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(Context)
|
||||
.Build();
|
||||
|
||||
await _bot.SetGameAsync(game == null ? game : rep.Replace(game), type).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("set_game").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task SetStream(string url, [Leftover] string name = null)
|
||||
{
|
||||
name = name ?? "";
|
||||
|
||||
await _client.SetGameAsync(name, url, ActivityType.Streaming).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("set_stream").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Send(string where, [Leftover] string msg = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
|
||||
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 == null)
|
||||
return;
|
||||
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(Context)
|
||||
.Build();
|
||||
|
||||
if (ids[1].ToUpperInvariant().StartsWith("C:", StringComparison.InvariantCulture))
|
||||
{
|
||||
var cid = ulong.Parse(ids[1].Substring(2));
|
||||
var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid);
|
||||
if (ch == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CREmbed.TryParse(msg, out var crembed))
|
||||
{
|
||||
rep.Replace(crembed);
|
||||
await ch.EmbedAsync(crembed).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await ch.SendMessageAsync(rep.Replace(msg).SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
else if (ids[1].ToUpperInvariant().StartsWith("U:", StringComparison.InvariantCulture))
|
||||
{
|
||||
var uid = ulong.Parse(ids[1].Substring(2));
|
||||
var user = server.Users.FirstOrDefault(u => u.Id == uid);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CREmbed.TryParse(msg, out var crembed))
|
||||
{
|
||||
rep.Replace(crembed);
|
||||
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).EmbedAsync(crembed)
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(rep.Replace(msg).SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("invalid_format").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ImagesReload()
|
||||
{
|
||||
_service.ReloadImages();
|
||||
await ReplyConfirmLocalizedAsync("images_loading", 0).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task StringsReload()
|
||||
{
|
||||
_strings.Reload();
|
||||
await ReplyConfirmLocalizedAsync("bot_strings_reloaded").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
|
||||
{
|
||||
switch (sus)
|
||||
{
|
||||
case SettableUserStatus.Online:
|
||||
return UserStatus.Online;
|
||||
case SettableUserStatus.Invisible:
|
||||
return UserStatus.Invisible;
|
||||
case SettableUserStatus.Idle:
|
||||
return UserStatus.AFK;
|
||||
case SettableUserStatus.Dnd:
|
||||
return UserStatus.DoNotDisturb;
|
||||
}
|
||||
|
||||
return UserStatus.Online;
|
||||
}
|
||||
|
||||
public enum SettableUserStatus
|
||||
{
|
||||
Online,
|
||||
Invisible,
|
||||
Idle,
|
||||
Dnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
218
src/NadekoBot/Modules/Administration/ServerGreetCommands.cs
Normal file
218
src/NadekoBot/Modules/Administration/ServerGreetCommands.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Common.Attributes;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class ServerGreetCommands : NadekoSubmodule<GreetSettingsService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDel(int timer = 30)
|
||||
{
|
||||
if (timer < 0 || timer > 600)
|
||||
return;
|
||||
|
||||
await _service.SetGreetDel(ctx.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await ReplyConfirmLocalizedAsync("greetdel_on", timer).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("greetdel_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task Greet()
|
||||
{
|
||||
var enabled = await _service.SetGreet(ctx.Guild.Id, ctx.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync("greet_on").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("greet_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetMsg()
|
||||
{
|
||||
string greetMsg = _service.GetGreetMsg(ctx.Guild.Id);
|
||||
return ReplyConfirmLocalizedAsync("greetmsg_cur", greetMsg?.SanitizeMentions());
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetMsg([Leftover] string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
await GreetMsg().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = _service.SetGreetMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("greetmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
await ReplyConfirmLocalizedAsync("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDm()
|
||||
{
|
||||
var enabled = await _service.SetGreetDm(ctx.Guild.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync("greetdm_on").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("greetdm_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetDmMsg()
|
||||
{
|
||||
var dmGreetMsg = _service.GetDmGreetMsg(ctx.Guild.Id);
|
||||
return ReplyConfirmLocalizedAsync("greetdmmsg_cur", dmGreetMsg?.SanitizeMentions());
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDmMsg([Leftover] string text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
await GreetDmMsg().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = _service.SetGreetDmMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("greetdmmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
await ReplyConfirmLocalizedAsync("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task Bye()
|
||||
{
|
||||
var enabled = await _service.SetBye(ctx.Guild.Id, ctx.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync("bye_on").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("bye_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task ByeMsg()
|
||||
{
|
||||
var byeMsg = _service.GetByeMessage(ctx.Guild.Id);
|
||||
return ReplyConfirmLocalizedAsync("byemsg_cur", byeMsg?.SanitizeMentions());
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ByeMsg([Leftover] string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
await ByeMsg().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendByeEnabled = _service.SetByeMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("byemsg_new").ConfigureAwait(false);
|
||||
if (!sendByeEnabled)
|
||||
await ReplyConfirmLocalizedAsync("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ByeDel(int timer = 30)
|
||||
{
|
||||
await _service.SetByeDel(ctx.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await ReplyConfirmLocalizedAsync("byedel_on", timer).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("byedel_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task ByeTest([Leftover] IGuildUser user = null)
|
||||
{
|
||||
user = user ?? (IGuildUser) Context.User;
|
||||
|
||||
await _service.ByeTest((ITextChannel)Context.Channel, user);
|
||||
var enabled = _service.GetByeEnabled(Context.Guild.Id);
|
||||
if (!enabled)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task GreetTest([Leftover] IGuildUser user = null)
|
||||
{
|
||||
user = user ?? (IGuildUser) Context.User;
|
||||
|
||||
await _service.GreetTest((ITextChannel)Context.Channel, user);
|
||||
var enabled = _service.GetGreetEnabled(Context.Guild.Id);
|
||||
if (!enabled)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task GreetDmTest([Leftover] IGuildUser user = null)
|
||||
{
|
||||
user = user ?? (IGuildUser) Context.User;
|
||||
|
||||
var channel = await user.GetOrCreateDMChannelAsync();
|
||||
var success = await _service.GreetDmTest(channel, user);
|
||||
if (success)
|
||||
await Context.OkAsync();
|
||||
else
|
||||
await Context.WarningAsync();
|
||||
var enabled = _service.GetGreetDmEnabled(Context.Guild.Id);
|
||||
if (!enabled)
|
||||
await ReplyConfirmLocalizedAsync("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,185 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class AdministrationService : INService
|
||||
{
|
||||
public ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; }
|
||||
public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; }
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly LogCommandService _logService;
|
||||
|
||||
public AdministrationService(NadekoBot bot, CommandHandler cmdHandler, DbService db,
|
||||
LogCommandService logService)
|
||||
{
|
||||
_db = db;
|
||||
_logService = logService;
|
||||
|
||||
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
|
||||
.Where(g => g.DeleteMessageOnCommand)
|
||||
.Select(g => g.GuildId));
|
||||
|
||||
DeleteMessagesOnCommandChannels = new ConcurrentDictionary<ulong, bool>(bot.AllGuildConfigs
|
||||
.SelectMany(x => x.DelMsgOnCmdChannels)
|
||||
.ToDictionary(x => x.ChannelId, x => x.State)
|
||||
.ToConcurrent());
|
||||
|
||||
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
|
||||
}
|
||||
|
||||
public (bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels) GetDelMsgOnCmdData(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigs.ForId(guildId,
|
||||
set => set.Include(x => x.DelMsgOnCmdChannels));
|
||||
|
||||
return (conf.DeleteMessageOnCommand, conf.DelMsgOnCmdChannels);
|
||||
}
|
||||
}
|
||||
|
||||
private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
if (!(msg.Channel is SocketTextChannel channel))
|
||||
return;
|
||||
|
||||
//wat ?!
|
||||
if (DeleteMessagesOnCommandChannels.TryGetValue(channel.Id, out var state))
|
||||
{
|
||||
if (state && cmd.Name != "prune" && cmd.Name != "pick")
|
||||
{
|
||||
_logService.AddDeleteIgnore(msg.Id);
|
||||
try { await msg.DeleteAsync().ConfigureAwait(false); } 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().ConfigureAwait(false); } catch { }
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool ToggleDeleteMessageOnCommand(ulong guildId)
|
||||
{
|
||||
bool enabled;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigs.ForId(guildId,
|
||||
set => set.Include(x => x.DelMsgOnCmdChannels));
|
||||
|
||||
var old = conf.DelMsgOnCmdChannels.FirstOrDefault(x => x.ChannelId == chId);
|
||||
if (newState == Administration.State.Inherit)
|
||||
{
|
||||
if (!(old is null))
|
||||
{
|
||||
conf.DelMsgOnCmdChannels.Remove(old);
|
||||
uow._context.Remove(old);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (old is null)
|
||||
{
|
||||
old = new DelMsgOnCmdChannel { ChannelId = chId };
|
||||
conf.DelMsgOnCmdChannels.Add(old);
|
||||
}
|
||||
|
||||
old.State = newState == Administration.State.Enable;
|
||||
DeleteMessagesOnCommandChannels[chId] = newState == Administration.State.Enable;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (newState == Administration.State.Disable)
|
||||
{
|
||||
}
|
||||
else if (newState == Administration.State.Enable)
|
||||
{
|
||||
DeleteMessagesOnCommandChannels[chId] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteMessagesOnCommandChannels.TryRemove(chId, out var _);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeafenUsers(bool value, params IGuildUser[] users)
|
||||
{
|
||||
if (!users.Any())
|
||||
return;
|
||||
foreach (var u in users)
|
||||
{
|
||||
try
|
||||
{
|
||||
await u.ModifyAsync(usr => usr.Deaf = value).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task EditMessage(ICommandContext context, ITextChannel chanl, ulong messageId, string text)
|
||||
{
|
||||
var msg = await chanl.GetMessageAsync(messageId);
|
||||
|
||||
if (!(msg is IUserMessage umsg) || msg.Author.Id != context.Client.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(context)
|
||||
.Build();
|
||||
|
||||
if (CREmbed.TryParse(text, out var crembed))
|
||||
{
|
||||
rep.Replace(crembed);
|
||||
await umsg.ModifyAsync(x =>
|
||||
{
|
||||
x.Embed = crembed.ToEmbed().Build();
|
||||
x.Content = crembed.PlainText?.SanitizeMentions() ?? "";
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await umsg.ModifyAsync(x =>
|
||||
{
|
||||
x.Content = text.SanitizeMentions();
|
||||
x.Embed = null;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Core.Services;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Channels;
|
||||
using LinqToDB;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public sealed class AutoAssignRoleService : INService
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly DbService _db;
|
||||
|
||||
//guildid/roleid
|
||||
private readonly ConcurrentDictionary<ulong, IReadOnlyList<ulong>> _autoAssignableRoles;
|
||||
|
||||
private Channel<SocketGuildUser> _assignQueue = Channel.CreateBounded<SocketGuildUser>(
|
||||
new BoundedChannelOptions(100)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropOldest,
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
});
|
||||
|
||||
public AutoAssignRoleService(DiscordSocketClient client, NadekoBot bot, DbService db)
|
||||
{
|
||||
_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();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
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 null))
|
||||
.ToList();
|
||||
|
||||
if (roleIds.Any())
|
||||
{
|
||||
await user.AddRolesAsync(roleIds).ConfigureAwait(false);
|
||||
await Task.Delay(250).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning(
|
||||
"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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Log.Warning(ex, "Error in aar. Probably one of the roles doesn't exist");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_client.UserJoined += OnClientOnUserJoined;
|
||||
_client.RoleDeleted += OnClientRoleDeleted;
|
||||
}
|
||||
|
||||
private async Task OnClientRoleDeleted(SocketRole role)
|
||||
{
|
||||
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 _))
|
||||
await _assignQueue.Writer.WriteAsync(user);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ulong>> ToggleAarAsync(ulong guildId, ulong roleId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
var roles = gc.GetAutoAssignableRoles();
|
||||
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)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
||||
await uow._context
|
||||
.GuildConfigs
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.UpdateAsync(_ => new GuildConfig(){ AutoAssignRoleIds = null});
|
||||
|
||||
_autoAssignableRoles.TryRemove(guildId, out _);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task SetAarRolesAsync(ulong guildId, IEnumerable<ulong> newRoles)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
gc.SetAutoAssignableRoles(newRoles);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public bool TryGetRoles(ulong guildId, out IReadOnlyList<ulong> roles)
|
||||
=> _autoAssignableRoles.TryGetValue(guildId, out roles);
|
||||
}
|
||||
|
||||
public static class GuildConfigExtensions
|
||||
{
|
||||
public static List<ulong> GetAutoAssignableRoles(this GuildConfig gc)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(gc.AutoAssignRoleIds))
|
||||
return new List<ulong>();
|
||||
|
||||
return gc.AutoAssignRoleIds.Split(',').Select(ulong.Parse).ToList();
|
||||
}
|
||||
|
||||
public static void SetAutoAssignableRoles(this GuildConfig gc, IEnumerable<ulong> roles)
|
||||
{
|
||||
gc.AutoAssignRoleIds = roles.JoinWith(',');
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services;
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Core.Modules.Administration.Services
|
||||
{
|
||||
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});
|
||||
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 MusicPlaylistDeleteSql = "DELETE FROM MusicPlaylists;";
|
||||
public const string XpDeleteSql = @"DELETE FROM UserXpStats;
|
||||
UPDATE DiscordUser
|
||||
SET ClubId=NULL,
|
||||
IsClubAdmin=0,
|
||||
TotalXp=0;
|
||||
DELETE FROM ClubApplicants;
|
||||
DELETE FROM ClubBans;
|
||||
DELETE FROM Clubs;";
|
||||
// public const string DeleteUnusedCustomReactionsAndQuotes = @"DELETE FROM CustomReactions
|
||||
//WHERE UseCount=0 AND (DateAdded < date('now', '-7 day') OR DateAdded is null);
|
||||
|
||||
//DELETE FROM Quotes
|
||||
//WHERE UseCount=0 AND (DateAdded < date('now', '-7 day') OR DateAdded is null);";
|
||||
|
||||
private readonly DbService _db;
|
||||
|
||||
public DangerousCommandsService(DbService db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteSql(string sql)
|
||||
{
|
||||
int res;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
res = await uow._context.Database.ExecuteSqlRawAsync(sql);
|
||||
}
|
||||
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 List<string>(),
|
||||
Results = new List<string[]>(),
|
||||
};
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conn = uow._context.Database.GetDbConnection();
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.HasRows)
|
||||
{
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
result.ColumnNames.Add(reader.GetName(i));
|
||||
}
|
||||
while (reader.Read())
|
||||
{
|
||||
var obj = new object[reader.FieldCount];
|
||||
reader.GetValues(obj);
|
||||
result.Results.Add(obj.Select(x => x.ToString()).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task PurgeUserAsync(ulong userId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
||||
// get waifu info
|
||||
var wi = await uow._context.Set<WaifuInfo>()
|
||||
.FirstOrDefaultAsyncEF(x => x.Waifu.UserId == userId);
|
||||
|
||||
// if it exists, delete waifu related things
|
||||
if (!(wi is null))
|
||||
{
|
||||
// remove updates which have new or old as this waifu
|
||||
await uow._context
|
||||
.WaifuUpdates
|
||||
.DeleteAsync(wu => wu.New.UserId == userId || wu.Old.UserId == userId);
|
||||
|
||||
// delete all items this waifu owns
|
||||
await uow._context
|
||||
.Set<WaifuItem>()
|
||||
.DeleteAsync(x => x.WaifuInfoId == wi.Id);
|
||||
|
||||
// all waifus this waifu claims are released
|
||||
await uow._context
|
||||
.Set<WaifuInfo>()
|
||||
.AsQueryable()
|
||||
.Where(x => x.Claimer.UserId == userId)
|
||||
.UpdateAsync(x => new WaifuInfo() {ClaimerId = null});
|
||||
|
||||
// all affinities set to this waifu are reset
|
||||
await uow._context
|
||||
.Set<WaifuInfo>()
|
||||
.AsQueryable()
|
||||
.Where(x => x.Affinity.UserId == userId)
|
||||
.UpdateAsync(x => new WaifuInfo() {AffinityId = null});
|
||||
}
|
||||
|
||||
// delete guild xp
|
||||
await uow._context
|
||||
.UserXpStats
|
||||
.DeleteAsync(x => x.UserId == userId);
|
||||
|
||||
// delete currency transactions
|
||||
await uow._context.Set<CurrencyTransaction>()
|
||||
.DeleteAsync(x => x.UserId == userId);
|
||||
|
||||
// delete user, currency, and clubs go away with it
|
||||
await uow._context.DiscordUser
|
||||
.DeleteAsync(u => u.UserId == userId);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class DiscordPermOverrideService : INService, ILateBlocker
|
||||
{
|
||||
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)
|
||||
{
|
||||
_db = db;
|
||||
_services = services;
|
||||
using var uow = _db.GetDbContext();
|
||||
_overrides = uow._context.DiscordPermOverrides
|
||||
.AsNoTracking()
|
||||
.AsEnumerable()
|
||||
.ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o)
|
||||
.ToConcurrent();
|
||||
}
|
||||
|
||||
public bool TryGetOverrides(ulong guildId, string commandName, out GuildPerm? perm)
|
||||
{
|
||||
commandName = commandName.ToLowerInvariant();
|
||||
if (_overrides.TryGetValue((guildId, commandName), out var dpo))
|
||||
{
|
||||
perm = dpo.Perm;
|
||||
return true;
|
||||
}
|
||||
|
||||
perm = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<PreconditionResult> ExecuteOverrides(ICommandContext ctx, CommandInfo command,
|
||||
GuildPerm perms, IServiceProvider services)
|
||||
{
|
||||
var rupa = new RequireUserPermissionAttribute((GuildPermission) perms);
|
||||
return rupa.CheckPermissionsAsync(ctx, command, services);
|
||||
}
|
||||
|
||||
public async Task AddOverride(ulong guildId, string commandName, GuildPerm perm)
|
||||
{
|
||||
commandName = commandName.ToLowerInvariant();
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var over = await uow._context
|
||||
.Set<DiscordPermOverride>()
|
||||
.AsQueryable()
|
||||
.FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command);
|
||||
|
||||
if (over is null)
|
||||
{
|
||||
uow._context.Set<DiscordPermOverride>()
|
||||
.Add(over = new DiscordPermOverride()
|
||||
{
|
||||
Command = commandName,
|
||||
Perm = perm,
|
||||
GuildId = guildId,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
over.Perm = perm;
|
||||
}
|
||||
|
||||
_overrides[(guildId, commandName)] = over;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearAllOverrides(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var overrides = await uow._context
|
||||
.Set<DiscordPermOverride>()
|
||||
.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.ToListAsync();
|
||||
|
||||
uow._context.RemoveRange(overrides);
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
foreach (var over in overrides)
|
||||
{
|
||||
_overrides.TryRemove((guildId, over.Command), out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveOverride(ulong guildId, string commandName)
|
||||
{
|
||||
commandName = commandName.ToLowerInvariant();
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var over = await uow._context
|
||||
.Set<DiscordPermOverride>()
|
||||
.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName);
|
||||
|
||||
if (over is null)
|
||||
return;
|
||||
|
||||
uow._context.Remove(over);
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
_overrides.TryRemove((guildId, commandName), out _);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<List<DiscordPermOverride>> GetAllOverrides(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
return uow._context
|
||||
.Set<DiscordPermOverride>()
|
||||
.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TryBlockLate(DiscordSocketClient client, ICommandContext context, string moduleName,
|
||||
CommandInfo command)
|
||||
{
|
||||
if (TryGetOverrides(context.Guild?.Id ?? 0, command.Name, out var perm) && !(perm is null))
|
||||
{
|
||||
var result = await new RequireUserPermissionAttribute((GuildPermission) perm)
|
||||
.CheckPermissionsAsync(context, command, _services);
|
||||
|
||||
return !result.IsSuccess;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class GameVoiceChannelService : INService
|
||||
{
|
||||
public ConcurrentHashSet<ulong> GameVoiceChannels { get; } = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public GameVoiceChannelService(DiscordSocketClient client, DbService db, NadekoBot bot)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
|
||||
GameVoiceChannels = new ConcurrentHashSet<ulong>(
|
||||
bot.AllGuildConfigs.Where(gc => gc.GameVoiceChannel != null)
|
||||
.Select(gc => gc.GameVoiceChannel.Value));
|
||||
|
||||
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
_client.GuildMemberUpdated += _client_GuildMemberUpdated;
|
||||
}
|
||||
|
||||
private Task _client_GuildMemberUpdated(SocketGuildUser before, SocketGuildUser after)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//if the user is in the voice channel and that voice channel is gvc
|
||||
var vc = after.VoiceChannel;
|
||||
if (vc == null || !GameVoiceChannels.Contains(vc.Id))
|
||||
return;
|
||||
|
||||
//if the activity has changed, and is a playing activity
|
||||
if (before.Activity != after.Activity
|
||||
&& after.Activity != null
|
||||
&& after.Activity.Type == Discord.ActivityType.Playing)
|
||||
{
|
||||
//trigger gvc
|
||||
await TriggerGvc(after, after.Activity.Name);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error running GuildMemberUpdated in gvc");
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public ulong? ToggleGameVoiceChannel(ulong guildId, ulong vchId)
|
||||
{
|
||||
ulong? id;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
|
||||
if (gc.GameVoiceChannel == vchId)
|
||||
{
|
||||
GameVoiceChannels.TryRemove(vchId);
|
||||
id = gc.GameVoiceChannel = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gc.GameVoiceChannel != null)
|
||||
GameVoiceChannels.TryRemove(gc.GameVoiceChannel.Value);
|
||||
GameVoiceChannels.Add(vchId);
|
||||
id = gc.GameVoiceChannel = vchId;
|
||||
}
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private Task Client_UserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, SocketVoiceState newState)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!(usr is SocketGuildUser gUser))
|
||||
return;
|
||||
|
||||
var game = gUser.Activity?.Name;
|
||||
|
||||
if (oldState.VoiceChannel == newState.VoiceChannel ||
|
||||
newState.VoiceChannel == null)
|
||||
return;
|
||||
|
||||
if (!GameVoiceChannels.Contains(newState.VoiceChannel.Id) ||
|
||||
string.IsNullOrWhiteSpace(game))
|
||||
return;
|
||||
|
||||
await TriggerGvc(gUser, game);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error running VoiceStateUpdate in gvc");
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task TriggerGvc(SocketGuildUser gUser, string game)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(game))
|
||||
return;
|
||||
|
||||
game = game.TrimTo(50).ToLowerInvariant();
|
||||
var vch = gUser.Guild.VoiceChannels
|
||||
.FirstOrDefault(x => x.Name.ToLowerInvariant() == game);
|
||||
|
||||
if (vch == null)
|
||||
return;
|
||||
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await gUser.ModifyAsync(gu => gu.Channel = vch).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class GuildTimezoneService : INService
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, GuildTimezoneService> AllServices { get; } = new ConcurrentDictionary<ulong, GuildTimezoneService>();
|
||||
private readonly ConcurrentDictionary<ulong, TimeZoneInfo> _timezones;
|
||||
private readonly DbService _db;
|
||||
|
||||
public GuildTimezoneService(DiscordSocketClient client, NadekoBot bot, DbService db)
|
||||
{
|
||||
_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)
|
||||
AllServices.TryAdd(curUser.Id, this);
|
||||
_db = db;
|
||||
|
||||
bot.JoinedGuild += Bot_JoinedGuild;
|
||||
}
|
||||
|
||||
private Task Bot_JoinedGuild(GuildConfig arg)
|
||||
{
|
||||
var (guildId, tz) = GetTimzezoneTuple(arg);
|
||||
if (tz != null)
|
||||
_timezones.TryAdd(guildId, tz);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static (ulong GuildId, TimeZoneInfo Timezone) GetTimzezoneTuple(GuildConfig x)
|
||||
{
|
||||
TimeZoneInfo tz;
|
||||
try
|
||||
{
|
||||
if (x.TimeZoneId == null)
|
||||
tz = null;
|
||||
else
|
||||
tz = TimeZoneInfo.FindSystemTimeZoneById(x.TimeZoneId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tz = null;
|
||||
}
|
||||
return (x.GuildId, Timezone: tz);
|
||||
}
|
||||
|
||||
public TimeZoneInfo GetTimeZoneOrDefault(ulong guildId)
|
||||
{
|
||||
if (_timezones.TryGetValue(guildId, out var tz))
|
||||
return tz;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetTimeZone(ulong guildId, TimeZoneInfo tz)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
|
||||
gc.TimeZoneId = tz?.Id;
|
||||
uow.SaveChanges();
|
||||
|
||||
if (tz == null)
|
||||
_timezones.TryRemove(guildId, out tz);
|
||||
else
|
||||
_timezones.AddOrUpdate(guildId, tz, (key, old) => tz);
|
||||
}
|
||||
}
|
||||
|
||||
public TimeZoneInfo GetTimeZoneOrUtc(ulong guildId)
|
||||
=> GetTimeZoneOrDefault(guildId) ?? TimeZoneInfo.Utc;
|
||||
}
|
||||
}
|
1279
src/NadekoBot/Modules/Administration/Services/LogCommandService.cs
Normal file
1279
src/NadekoBot/Modules/Administration/Services/LogCommandService.cs
Normal file
File diff suppressed because it is too large
Load Diff
477
src/NadekoBot/Modules/Administration/Services/MuteService.cs
Normal file
477
src/NadekoBot/Modules/Administration/Services/MuteService.cs
Normal file
@@ -0,0 +1,477 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public enum MuteType
|
||||
{
|
||||
Voice,
|
||||
Chat,
|
||||
All
|
||||
}
|
||||
|
||||
public class MuteService : INService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
|
||||
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
|
||||
|
||||
public ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>> Un_Timers { get; }
|
||||
= new ConcurrentDictionary<ulong, ConcurrentDictionary<(ulong, TimerType), Timer>>();
|
||||
|
||||
public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { };
|
||||
public event Action<IGuildUser, IUser, MuteType, string> UserUnmuted = delegate { };
|
||||
|
||||
private static readonly OverwritePermissions denyOverwrite =
|
||||
new OverwritePermissions(addReactions: PermValue.Deny, sendMessages: PermValue.Deny,
|
||||
attachFiles: PermValue.Deny);
|
||||
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly DbService _db;
|
||||
|
||||
public MuteService(DiscordSocketClient client, DbService db)
|
||||
{
|
||||
_client = client;
|
||||
_db = db;
|
||||
|
||||
using (var uow = db.GetDbContext())
|
||||
{
|
||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||
var configs = uow._context.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();
|
||||
|
||||
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs
|
||||
.ToDictionary(
|
||||
k => k.GuildId,
|
||||
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
||||
));
|
||||
|
||||
var max = TimeSpan.FromDays(49);
|
||||
|
||||
foreach (var conf in configs)
|
||||
{
|
||||
foreach (var x in conf.UnmuteTimers)
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unmute = x.UnmuteAt - DateTime.UtcNow;
|
||||
after = unmute > max ? max : unmute;
|
||||
}
|
||||
|
||||
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Mute);
|
||||
}
|
||||
|
||||
foreach (var x in conf.UnbanTimer)
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unban = x.UnbanAt - DateTime.UtcNow;
|
||||
after = unban > max ? max : unban;
|
||||
}
|
||||
|
||||
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.Ban);
|
||||
}
|
||||
|
||||
foreach (var x in conf.UnroleTimer)
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unban = x.UnbanAt - DateTime.UtcNow;
|
||||
after = unban > max ? max : unban;
|
||||
}
|
||||
|
||||
StartUn_Timer(conf.GuildId, x.UserId, after, TimerType.AddRole, x.RoleId);
|
||||
}
|
||||
}
|
||||
|
||||
_client.UserJoined += Client_UserJoined;
|
||||
}
|
||||
|
||||
UserMuted += OnUserMuted;
|
||||
UserUnmuted += OnUserUnmuted;
|
||||
}
|
||||
|
||||
private void OnUserMuted(IGuildUser user, IUser mod, MuteType type, string reason)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
return;
|
||||
|
||||
var _ = Task.Run(() => user.SendMessageAsync(embed: new EmbedBuilder()
|
||||
.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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
return;
|
||||
|
||||
var _ = Task.Run(() => user.SendMessageAsync(embed: new EmbedBuilder()
|
||||
.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)
|
||||
{
|
||||
try
|
||||
{
|
||||
MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted);
|
||||
|
||||
if (muted == null || !muted.Contains(usr.Id))
|
||||
return Task.CompletedTask;
|
||||
var _ = Task.Run(() => MuteUser(usr, _client.CurrentUser, reason: "Sticky mute").ConfigureAwait(false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in MuteService UserJoined event");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SetMuteRoleAsync(ulong guildId, string name)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
config.MuteRoleName = name;
|
||||
GuildMuteRoles.AddOrUpdate(guildId, name, (id, old) => name);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
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).ConfigureAwait(false); } catch { }
|
||||
var muteRole = await GetMuteRole(usr.Guild).ConfigureAwait(false);
|
||||
if (!usr.RoleIds.Contains(muteRole.Id))
|
||||
await usr.AddRoleAsync(muteRole).ConfigureAwait(false);
|
||||
StopTimer(usr.GuildId, usr.Id, TimerType.Mute);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(usr.Guild.Id,
|
||||
set => set.Include(gc => gc.MutedUsers)
|
||||
.Include(gc => gc.UnmuteTimers));
|
||||
config.MutedUsers.Add(new MutedUserId()
|
||||
{
|
||||
UserId = usr.Id
|
||||
});
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted))
|
||||
muted.Add(usr.Id);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
UserMuted(usr, mod, MuteType.All, reason);
|
||||
}
|
||||
else if (type == MuteType.Voice)
|
||||
{
|
||||
try
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
|
||||
UserMuted(usr, mod, MuteType.Voice, reason);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else if (type == MuteType.Chat)
|
||||
{
|
||||
await usr.AddRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserMuted(usr, mod, MuteType.Chat, 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)
|
||||
{
|
||||
StopTimer(guildId, usrId, TimerType.Mute);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(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._context.Remove(toRemove);
|
||||
}
|
||||
if (MutedUsers.TryGetValue(guildId, out ConcurrentHashSet<ulong> muted))
|
||||
muted.TryRemove(usrId);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usrId);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
if (usr != null)
|
||||
{
|
||||
try { await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); } catch { }
|
||||
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false); } catch { /*ignore*/ }
|
||||
UserUnmuted(usr, mod, MuteType.All, reason);
|
||||
}
|
||||
}
|
||||
else if (type == MuteType.Voice)
|
||||
{
|
||||
if (usr == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false);
|
||||
UserUnmuted(usr, mod, MuteType.Voice, reason);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else if (type == MuteType.Chat)
|
||||
{
|
||||
if (usr == null)
|
||||
return;
|
||||
await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserUnmuted(usr, mod, MuteType.Chat, reason);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IRole> GetMuteRole(IGuild guild)
|
||||
{
|
||||
if (guild == null)
|
||||
throw new ArgumentNullException(nameof(guild));
|
||||
|
||||
const string defaultMuteRoleName = "nadeko-mute";
|
||||
|
||||
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
|
||||
|
||||
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
|
||||
if (muteRole == null)
|
||||
{
|
||||
|
||||
//if it doesn't exist, create it
|
||||
try { muteRole = await guild.CreateRoleAsync(muteRoleName, isMentionable: false).ConfigureAwait(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).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var toOverwrite in (await guild.GetTextChannelsAsync().ConfigureAwait(false)))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!toOverwrite.PermissionOverwrites.Any(x => x.TargetId == muteRole.Id
|
||||
&& x.TargetType == PermissionTarget.Role))
|
||||
{
|
||||
await toOverwrite.AddPermissionOverwriteAsync(muteRole, denyOverwrite)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return muteRole;
|
||||
}
|
||||
|
||||
public async Task TimedMute(IGuildUser user, IUser mod, TimeSpan after, MuteType muteType = MuteType.All, string reason = "")
|
||||
{
|
||||
await MuteUser(user, mod, muteType, reason).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(user.GuildId, set => set.Include(x => x.UnmuteTimers));
|
||||
config.UnmuteTimers.Add(new UnmuteTimer()
|
||||
{
|
||||
UserId = user.Id,
|
||||
UnmuteAt = DateTime.UtcNow + after,
|
||||
}); // add teh unmute timer to the database
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
StartUn_Timer(user.GuildId, user.Id, after, TimerType.Mute); // start the timer
|
||||
}
|
||||
|
||||
public async Task TimedBan(IGuild guild, IUser user, TimeSpan after, string reason)
|
||||
{
|
||||
await guild.AddBanAsync(user.Id, 0, reason).ConfigureAwait(false);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guild.Id, set => set.Include(x => x.UnbanTimer));
|
||||
config.UnbanTimer.Add(new UnbanTimer()
|
||||
{
|
||||
UserId = user.Id,
|
||||
UnbanAt = DateTime.UtcNow + after,
|
||||
}); // add teh unmute timer to the database
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
StartUn_Timer(guild.Id, user.Id, after, TimerType.Ban); // start the timer
|
||||
}
|
||||
|
||||
public async Task TimedRole(IGuildUser user, TimeSpan after, string reason, IRole role)
|
||||
{
|
||||
await user.AddRoleAsync(role).ConfigureAwait(false);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(user.GuildId, set => set.Include(x => x.UnroleTimer));
|
||||
config.UnroleTimer.Add(new UnroleTimer()
|
||||
{
|
||||
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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
await guild.RemoveBanAsync(userId).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
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))
|
||||
{
|
||||
await user.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
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").ConfigureAwait(false);
|
||||
}
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
public void StopTimer(ulong guildId, ulong userId, TimerType type)
|
||||
{
|
||||
if (!Un_Timers.TryGetValue(guildId, out ConcurrentDictionary<(ulong, TimerType), Timer> userTimer))
|
||||
return;
|
||||
|
||||
if (userTimer.TryRemove((userId, type), out Timer removed))
|
||||
{
|
||||
removed.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveTimerFromDb(ulong guildId, ulong userId, TimerType type)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
object toDelete;
|
||||
if (type == TimerType.Mute)
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.UnmuteTimers));
|
||||
toDelete = config.UnmuteTimers.FirstOrDefault(x => x.UserId == userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.UnbanTimer));
|
||||
toDelete = config.UnbanTimer.FirstOrDefault(x => x.UserId == userId);
|
||||
}
|
||||
if (toDelete != null)
|
||||
{
|
||||
uow._context.Remove(toDelete);
|
||||
}
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
//using NadekoBot.Core.Services;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.IO;
|
||||
//using System.Linq;
|
||||
//using System.Text.RegularExpressions;
|
||||
|
||||
//namespace NadekoBot.Modules.Administration.Services
|
||||
//{
|
||||
// public class PackagesService : INService
|
||||
// {
|
||||
// public IEnumerable<string> Packages { get; private set; }
|
||||
|
||||
// public PackagesService()
|
||||
// {
|
||||
// ReloadAvailablePackages();
|
||||
// }
|
||||
|
||||
// public void ReloadAvailablePackages()
|
||||
// {
|
||||
// Packages = Directory.GetDirectories(Path.Combine(Appctx.BaseDirectory, "modules\\"), "NadekoBot.Modules.*", SearchOption.AllDirectories)
|
||||
// .SelectMany(x => Directory.GetFiles(x, "NadekoBot.Modules.*.dll"))
|
||||
// .Select(x => Path.GetFileNameWithoutExtension(x))
|
||||
// .Select(x =>
|
||||
// {
|
||||
// var m = Regex.Match(x, @"NadekoBot\.Modules\.(?<name>.*)");
|
||||
// return m.Groups["name"].Value;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//}
|
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using Discord;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Common;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public sealed class PlayingRotateService : INService
|
||||
{
|
||||
private readonly Timer _t;
|
||||
private readonly BotConfigService _bss;
|
||||
private readonly Replacer _rep;
|
||||
private readonly DbService _db;
|
||||
private readonly NadekoBot _bot;
|
||||
|
||||
private class TimerState
|
||||
{
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
public PlayingRotateService(DiscordSocketClient client, DbService db, NadekoBot bot,
|
||||
BotConfigService bss, IEnumerable<IPlaceholderProvider> phProviders)
|
||||
{
|
||||
_db = db;
|
||||
_bot = bot;
|
||||
_bss = bss;
|
||||
|
||||
if (client.ShardId == 0)
|
||||
{
|
||||
_rep = new ReplacementBuilder()
|
||||
.WithClient(client)
|
||||
.WithProviders(phProviders)
|
||||
.Build();
|
||||
|
||||
_t = new Timer(RotatingStatuses, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
}
|
||||
|
||||
private async void RotatingStatuses(object objState)
|
||||
{
|
||||
try
|
||||
{
|
||||
var state = (TimerState) objState;
|
||||
|
||||
if (!_bss.Data.RotateStatuses) return;
|
||||
|
||||
IReadOnlyList<RotatingPlayingStatus> rotatingStatuses;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
rotatingStatuses = uow._context.RotatingStatus
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.Id)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (rotatingStatuses.Count == 0)
|
||||
return;
|
||||
|
||||
var playingStatus = state.Index >= rotatingStatuses.Count
|
||||
? rotatingStatuses[state.Index = 0]
|
||||
: rotatingStatuses[state.Index++];
|
||||
|
||||
var statusText = _rep.Replace(playingStatus.Status);
|
||||
await _bot.SetGameAsync(statusText, playingStatus.Type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Rotating playing status errored: {ErrorMessage}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> RemovePlayingAsync(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var toRemove = await uow._context.RotatingStatus
|
||||
.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Skip(index)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (toRemove is null)
|
||||
return null;
|
||||
|
||||
uow._context.Remove(toRemove);
|
||||
await uow.SaveChangesAsync();
|
||||
return toRemove.Status;
|
||||
}
|
||||
|
||||
public async Task AddPlaying(ActivityType t, string status)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var toAdd = new RotatingPlayingStatus {Status = status, Type = t};
|
||||
uow._context.Add(toAdd);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public bool ToggleRotatePlaying()
|
||||
{
|
||||
var enabled = false;
|
||||
_bss.ModifyConfig(bs => { enabled = bs.RotateStatuses = !bs.RotateStatuses; });
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public IReadOnlyList<RotatingPlayingStatus> GetRotatingStatuses()
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow._context.RotatingStatus.AsNoTracking().ToList();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,487 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Modules.Administration.Common;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class ProtectionService : INService
|
||||
{
|
||||
private readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds
|
||||
= new ConcurrentDictionary<ulong, AntiRaidStats>();
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds
|
||||
= new ConcurrentDictionary<ulong, AntiSpamStats>();
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, AntiAltStats> _antiAltGuilds
|
||||
= new ConcurrentDictionary<ulong, AntiAltStats>();
|
||||
|
||||
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 UnboundedChannelOptions()
|
||||
{
|
||||
SingleReader = true,
|
||||
SingleWriter = false
|
||||
});
|
||||
|
||||
public ProtectionService(DiscordSocketClient client, NadekoBot bot,
|
||||
MuteService mute, DbService db, UserPunishService punishService)
|
||||
{
|
||||
_client = client;
|
||||
_mute = mute;
|
||||
_db = db;
|
||||
_punishService = punishService;
|
||||
|
||||
var ids = client.GetGuildIds();
|
||||
using (var uow = db.GetDbContext())
|
||||
{
|
||||
var configs = uow._context.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();
|
||||
|
||||
foreach (var gc in configs)
|
||||
{
|
||||
Initialize(gc);
|
||||
}
|
||||
}
|
||||
|
||||
_client.MessageReceived += HandleAntiSpam;
|
||||
_client.UserJoined += HandleUserJoined;
|
||||
|
||||
bot.JoinedGuild += _bot_JoinedGuild;
|
||||
_client.LeftGuild += _client_LeftGuild;
|
||||
|
||||
_ = Task.Run(RunQueue);
|
||||
}
|
||||
|
||||
private async Task RunQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var item = await PunishUserQueue.Reader.ReadAsync();
|
||||
|
||||
var muteTime = item.MuteTime;
|
||||
var gu = item.User;
|
||||
try
|
||||
{
|
||||
await _punishService.ApplyPunishment(gu.Guild, gu, _client.CurrentUser,
|
||||
item.Action, muteTime, item.RoleId, $"{item.Type} Protection");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in punish queue: {Message}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task _client_LeftGuild(SocketGuild guild)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
TryStopAntiRaid(guild.Id);
|
||||
TryStopAntiSpam(guild.Id);
|
||||
await TryStopAntiAlt(guild.Id);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task _bot_JoinedGuild(GuildConfig gc)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var gcWithData = uow.GuildConfigs.ForId(gc.GuildId,
|
||||
set => set
|
||||
.Include(x => x.AntiRaidSetting)
|
||||
.Include(x => x.AntiAltSetting)
|
||||
.Include(x => x.AntiSpamSetting)
|
||||
.ThenInclude(x => x.IgnoredChannels));
|
||||
|
||||
Initialize(gcWithData);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void Initialize(GuildConfig gc)
|
||||
{
|
||||
var raid = gc.AntiRaidSetting;
|
||||
var spam = gc.AntiSpamSetting;
|
||||
|
||||
if (raid != null)
|
||||
{
|
||||
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
|
||||
_antiRaidGuilds[gc.GuildId] = raidStats;
|
||||
}
|
||||
|
||||
if (spam != null)
|
||||
_antiSpamGuilds[gc.GuildId] = new AntiSpamStats() { AntiSpamSettings = spam };
|
||||
|
||||
var alt = gc.AntiAltSetting;
|
||||
if (!(alt is null))
|
||||
_antiAltGuilds[gc.GuildId] = new AntiAltStats(alt);
|
||||
}
|
||||
|
||||
private Task HandleUserJoined(SocketGuildUser user)
|
||||
{
|
||||
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 AntiAltStats alts)
|
||||
{
|
||||
if (user.CreatedAt != default)
|
||||
{
|
||||
var diff = DateTime.UtcNow - user.CreatedAt.UtcDateTime;
|
||||
if (diff < alts.MinAge)
|
||||
{
|
||||
alts.Increment();
|
||||
|
||||
await PunishUsers(
|
||||
alts.Action,
|
||||
ProtectionType.Alting,
|
||||
alts.ActionDurationMinutes,
|
||||
alts.RoleId,
|
||||
user);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!(maybeStats is AntiRaidStats stats) || !stats.RaidUsers.Add(user))
|
||||
return;
|
||||
|
||||
++stats.UsersCount;
|
||||
|
||||
if (stats.UsersCount >= stats.AntiRaidSettings.UserThreshold)
|
||||
{
|
||||
var users = stats.RaidUsers.ToArray();
|
||||
stats.RaidUsers.Clear();
|
||||
var settings = stats.AntiRaidSettings;
|
||||
|
||||
await PunishUsers(settings.Action, ProtectionType.Raiding,
|
||||
settings.PunishDuration, null, users).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(1000 * stats.AntiRaidSettings.Seconds).ConfigureAwait(false);
|
||||
|
||||
stats.RaidUsers.TryRemove(user);
|
||||
--stats.UsersCount;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task HandleAntiSpam(SocketMessage arg)
|
||||
{
|
||||
if (!(arg is SocketUserMessage msg) || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!(msg.Channel is ITextChannel channel))
|
||||
return Task.CompletedTask;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings) ||
|
||||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, (id) => new UserSpamStats(msg),
|
||||
(id, 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)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
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",
|
||||
pt,
|
||||
gus.Length,
|
||||
action,
|
||||
gus[0].Guild.Name);
|
||||
|
||||
foreach (var gu in gus)
|
||||
{
|
||||
await PunishUserQueue.Writer.WriteAsync(new PunishQueueItem()
|
||||
{
|
||||
Action = action,
|
||||
Type = pt,
|
||||
User = gu,
|
||||
MuteTime = muteTime,
|
||||
RoleId = roleId
|
||||
});
|
||||
}
|
||||
|
||||
_ = OnAntiProtectionTriggered(action, pt, gus);
|
||||
}
|
||||
|
||||
public async Task<AntiRaidStats> StartAntiRaidAsync(ulong guildId, int userThreshold, int seconds,
|
||||
PunishmentAction action, int minutesDuration)
|
||||
{
|
||||
var g = _client.GetGuild(guildId);
|
||||
await _mute.GetMuteRole(g).ConfigureAwait(false);
|
||||
|
||||
if (action == PunishmentAction.AddRole)
|
||||
return null;
|
||||
|
||||
if (!IsDurationAllowed(action))
|
||||
minutesDuration = 0;
|
||||
|
||||
var stats = new AntiRaidStats()
|
||||
{
|
||||
AntiRaidSettings = new AntiRaidSetting()
|
||||
{
|
||||
Action = action,
|
||||
Seconds = seconds,
|
||||
UserThreshold = userThreshold,
|
||||
PunishDuration = minutesDuration
|
||||
}
|
||||
};
|
||||
|
||||
_antiRaidGuilds.AddOrUpdate(guildId, stats, (key, old) => stats);
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiRaidSetting));
|
||||
|
||||
gc.AntiRaidSetting = stats.AntiRaidSettings;
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public bool TryStopAntiRaid(ulong guildId)
|
||||
{
|
||||
if (_antiRaidGuilds.TryRemove(guildId, out _))
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiRaidSetting));
|
||||
|
||||
gc.AntiRaidSetting = null;
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryStopAntiSpam(ulong guildId)
|
||||
{
|
||||
if (_antiSpamGuilds.TryRemove(guildId, out var removed))
|
||||
{
|
||||
removed.UserStats.ForEach(x => x.Value.Dispose());
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(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)
|
||||
{
|
||||
var g = _client.GetGuild(guildId);
|
||||
await _mute.GetMuteRole(g).ConfigureAwait(false);
|
||||
|
||||
if (!IsDurationAllowed(action))
|
||||
punishDurationMinutes = 0;
|
||||
|
||||
var stats = new AntiSpamStats
|
||||
{
|
||||
AntiSpamSettings = new AntiSpamSetting()
|
||||
{
|
||||
Action = action,
|
||||
MessageThreshold = messageCount,
|
||||
MuteTime = punishDurationMinutes,
|
||||
RoleId = roleId,
|
||||
}
|
||||
};
|
||||
|
||||
stats = _antiSpamGuilds.AddOrUpdate(guildId, stats, (key, old) =>
|
||||
{
|
||||
stats.AntiSpamSettings.IgnoredChannels = old.AntiSpamSettings.IgnoredChannels;
|
||||
return stats;
|
||||
});
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiSpamSetting));
|
||||
|
||||
if (gc.AntiSpamSetting != null)
|
||||
{
|
||||
gc.AntiSpamSetting.Action = stats.AntiSpamSettings.Action;
|
||||
gc.AntiSpamSetting.MessageThreshold = stats.AntiSpamSettings.MessageThreshold;
|
||||
gc.AntiSpamSetting.MuteTime = stats.AntiSpamSettings.MuteTime;
|
||||
gc.AntiSpamSetting.RoleId = stats.AntiSpamSettings.RoleId;
|
||||
}
|
||||
else
|
||||
{
|
||||
gc.AntiSpamSetting = stats.AntiSpamSettings;
|
||||
}
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
public async Task<bool?> AntiSpamIgnoreAsync(ulong guildId, ulong channelId)
|
||||
{
|
||||
var obj = new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channelId
|
||||
};
|
||||
bool added;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels));
|
||||
var spam = gc.AntiSpamSetting;
|
||||
if (spam is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (spam.IgnoredChannels.Add(obj)) // if adding to db is successful
|
||||
{
|
||||
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var toRemove = spam.IgnoredChannels.First(x => x.ChannelId == channelId);
|
||||
uow._context.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;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
public (AntiSpamStats, AntiRaidStats, AntiAltStats) GetAntiStats(ulong guildId)
|
||||
{
|
||||
_antiRaidGuilds.TryGetValue(guildId, out var antiRaidStats);
|
||||
_antiSpamGuilds.TryGetValue(guildId, out var antiSpamStats);
|
||||
_antiAltGuilds.TryGetValue(guildId, out var antiAltStats);
|
||||
|
||||
return (antiSpamStats, antiRaidStats, antiAltStats);
|
||||
}
|
||||
|
||||
public bool IsDurationAllowed(PunishmentAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case PunishmentAction.Ban:
|
||||
case PunishmentAction.Mute:
|
||||
case PunishmentAction.ChatMute:
|
||||
case PunishmentAction.VoiceMute:
|
||||
case PunishmentAction.AddRole:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAntiAltAsync(ulong guildId, int minAgeMinutes, PunishmentAction action,
|
||||
int actionDurationMinutes = 0, ulong? roleId = null)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiAltSetting));
|
||||
gc.AntiAltSetting = new AntiAltSetting()
|
||||
{
|
||||
Action = action,
|
||||
ActionDurationMinutes = actionDurationMinutes,
|
||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||
RoleId = roleId,
|
||||
};
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
_antiAltGuilds[guildId] = new AntiAltStats(gc.AntiAltSetting);
|
||||
}
|
||||
|
||||
public async Task<bool> TryStopAntiAlt(ulong guildId)
|
||||
{
|
||||
if (!_antiAltGuilds.TryRemove(guildId, out _))
|
||||
return false;
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.AntiAltSetting));
|
||||
gc.AntiAltSetting = null;
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class PruneService : INService
|
||||
{
|
||||
//channelids where prunes are currently occuring
|
||||
private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>();
|
||||
private readonly TimeSpan twoWeeks = TimeSpan.FromDays(14);
|
||||
private readonly LogCommandService _logService;
|
||||
|
||||
public PruneService(LogCommandService logService)
|
||||
{
|
||||
this._logService = logService;
|
||||
}
|
||||
|
||||
public async Task PruneWhere(ITextChannel channel, int amount, Func<IMessage, bool> predicate)
|
||||
{
|
||||
channel.ThrowIfNull(nameof(channel));
|
||||
if (amount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (!_pruningGuilds.Add(channel.GuildId))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
IMessage[] msgs;
|
||||
IMessage lastMessage = null;
|
||||
msgs = (await channel.GetMessagesAsync(50).FlattenAsync().ConfigureAwait(false)).Where(predicate).Take(amount).ToArray();
|
||||
while (amount > 0 && msgs.Any())
|
||||
{
|
||||
lastMessage = msgs[msgs.Length - 1];
|
||||
|
||||
var bulkDeletable = new List<IMessage>();
|
||||
var singleDeletable = new List<IMessage>();
|
||||
foreach (var x in msgs)
|
||||
{
|
||||
_logService.AddDeleteIgnore(x.Id);
|
||||
|
||||
if (DateTime.UtcNow - x.CreatedAt < twoWeeks)
|
||||
bulkDeletable.Add(x);
|
||||
else
|
||||
singleDeletable.Add(x);
|
||||
}
|
||||
|
||||
if (bulkDeletable.Count > 0)
|
||||
await Task.WhenAll(Task.Delay(1000), channel.DeleteMessagesAsync(bulkDeletable)).ConfigureAwait(false);
|
||||
|
||||
var i = 0;
|
||||
foreach (var group in singleDeletable.GroupBy(x => ++i / (singleDeletable.Count / 5)))
|
||||
await Task.WhenAll(Task.Delay(1000), Task.WhenAll(group.Select(x => x.DeleteAsync()))).ConfigureAwait(false);
|
||||
|
||||
//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().ConfigureAwait(false)).Where(predicate).Take(amount).ToArray();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//ignore
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pruningGuilds.TryRemove(channel.GuildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,190 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class RoleCommandsService : INService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ConcurrentDictionary<ulong, IndexedCollection<ReactionRoleMessage>> _models;
|
||||
|
||||
public RoleCommandsService(DiscordSocketClient client, DbService db,
|
||||
NadekoBot bot)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
#if !GLOBAL_NADEKO
|
||||
_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, ISocketMessageChannel chan, SocketReaction reaction)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!reaction.User.IsSpecified ||
|
||||
reaction.User.Value.IsBot ||
|
||||
!(reaction.User.Value is SocketGuildUser gusr))
|
||||
return;
|
||||
|
||||
if (!(chan is SocketGuildChannel gch))
|
||||
return;
|
||||
|
||||
if (!_models.TryGetValue(gch.Guild.Id, out var confs))
|
||||
return;
|
||||
|
||||
var conf = confs.FirstOrDefault(x => x.MessageId == msg.Id);
|
||||
|
||||
if (conf == null)
|
||||
return;
|
||||
|
||||
// compare emote names for backwards compatibility :facepalm:
|
||||
var reactionRole = conf.ReactionRoles.FirstOrDefault(x => x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
|
||||
if (reactionRole != null)
|
||||
{
|
||||
if (conf.Exclusive)
|
||||
{
|
||||
var roleIds = conf.ReactionRoles.Select(x => x.RoleId)
|
||||
.Where(x => x != reactionRole.RoleId)
|
||||
.Select(x => gusr.Guild.GetRole(x))
|
||||
.Where(x => x != null);
|
||||
|
||||
var __ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//if the role is exclusive,
|
||||
// remove all other reactions user added to the message
|
||||
var dl = await msg.GetOrDownloadAsync().ConfigureAwait(false);
|
||||
foreach (var r in dl.Reactions)
|
||||
{
|
||||
if (r.Key.Name == reaction.Emote.Name)
|
||||
continue;
|
||||
try { await dl.RemoveReactionAsync(r.Key, gusr).ConfigureAwait(false); } catch { }
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
await gusr.RemoveRolesAsync(roleIds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var toAdd = gusr.Guild.GetRole(reactionRole.RoleId);
|
||||
if (toAdd != null && !gusr.Roles.Contains(toAdd))
|
||||
{
|
||||
await gusr.AddRolesAsync(new[] { toAdd }).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var dl = await msg.GetOrDownloadAsync().ConfigureAwait(false);
|
||||
await dl.RemoveReactionAsync(reaction.Emote, dl.Author,
|
||||
new RequestOptions()
|
||||
{
|
||||
RetryMode = RetryMode.RetryRatelimit | RetryMode.Retry502
|
||||
}).ConfigureAwait(false);
|
||||
Log.Warning("User {0} is adding unrelated reactions to the reaction roles message.", dl.Author);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task _client_ReactionRemoved(Cacheable<IUserMessage, ulong> msg, ISocketMessageChannel chan, SocketReaction reaction)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!reaction.User.IsSpecified ||
|
||||
reaction.User.Value.IsBot ||
|
||||
!(reaction.User.Value is SocketGuildUser gusr))
|
||||
return;
|
||||
|
||||
if (!(chan is SocketGuildChannel gch))
|
||||
return;
|
||||
|
||||
if (!_models.TryGetValue(gch.Guild.Id, out var confs))
|
||||
return;
|
||||
|
||||
var conf = confs.FirstOrDefault(x => x.MessageId == msg.Id);
|
||||
|
||||
if (conf == null)
|
||||
return;
|
||||
|
||||
var reactionRole = conf.ReactionRoles.FirstOrDefault(x => x.EmoteName == reaction.Emote.Name || x.EmoteName == reaction.Emote.ToString());
|
||||
|
||||
if (reactionRole != null)
|
||||
{
|
||||
var role = gusr.Guild.GetRole(reactionRole.RoleId);
|
||||
if (role == null)
|
||||
return;
|
||||
await gusr.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool Get(ulong id, out IndexedCollection<ReactionRoleMessage> rrs)
|
||||
{
|
||||
return _models.TryGetValue(id, out rrs);
|
||||
}
|
||||
|
||||
public bool Add(ulong id, ReactionRoleMessage rrm)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(id, set => set
|
||||
.Include(x => x.ReactionRoleMessages)
|
||||
.ThenInclude(x => x.ReactionRoles));
|
||||
if (gc.ReactionRoleMessages.Count >= 10)
|
||||
return false;
|
||||
gc.ReactionRoleMessages.Add(rrm);
|
||||
_models.AddOrUpdate(id,
|
||||
gc.ReactionRoleMessages,
|
||||
delegate { return gc.ReactionRoleMessages; });
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Remove(ulong id, int index)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(id,
|
||||
set => set.Include(x => x.ReactionRoleMessages)
|
||||
.ThenInclude(x => x.ReactionRoles));
|
||||
uow._context.Set<ReactionRole>()
|
||||
.RemoveRange(gc.ReactionRoleMessages[index].ReactionRoles);
|
||||
gc.ReactionRoleMessages.RemoveAt(index);
|
||||
_models.AddOrUpdate(id,
|
||||
gc.ReactionRoleMessages,
|
||||
delegate { return gc.ReactionRoleMessages; });
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,270 @@
|
||||
using Discord;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Xp;
|
||||
|
||||
namespace NadekoBot.Core.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)
|
||||
}
|
||||
|
||||
public SelfAssignedRolesService(DbService db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public bool AddNew(ulong guildId, IRole role, int group)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var roles = uow.SelfAssignedRoles.GetFromGuild(guildId);
|
||||
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uow.SelfAssignedRoles.Add(new SelfAssignedRole
|
||||
{
|
||||
Group = group,
|
||||
RoleId = role.Id,
|
||||
GuildId = role.Guild.Id
|
||||
});
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ToggleAdSarm(ulong guildId)
|
||||
{
|
||||
bool newval;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return newval;
|
||||
}
|
||||
|
||||
public async Task<(AssignResult Result, bool AutoDelete, object extra)> Assign(IGuildUser guildUser, IRole role)
|
||||
{
|
||||
LevelStats userLevelData;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var stats = uow.Xp.GetOrCreateUser(guildUser.Guild.Id, guildUser.Id);
|
||||
userLevelData = new LevelStats(stats.Xp + stats.AwardedXp);
|
||||
}
|
||||
|
||||
var (autoDelete, exclusive, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
||||
|
||||
var theRoleYouWant = roles.FirstOrDefault(r => r.RoleId == role.Id);
|
||||
if (theRoleYouWant == null)
|
||||
{
|
||||
return (AssignResult.Err_Not_Assignable, autoDelete, null);
|
||||
}
|
||||
else 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);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
foreach (var roleId in sameRoles)
|
||||
{
|
||||
var sameRole = guildUser.Guild.GetRole(roleId);
|
||||
if (sameRole != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await guildUser.RemoveRoleAsync(sameRole).ConfigureAwait(false);
|
||||
await Task.Delay(300).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
await guildUser.AddRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (AssignResult.Err_Not_Perms, autoDelete, ex);
|
||||
}
|
||||
|
||||
return (AssignResult.Assigned, autoDelete, null);
|
||||
}
|
||||
|
||||
public async Task<bool> SetNameAsync(ulong guildId, int group, string name)
|
||||
{
|
||||
bool set = false;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, y => y.Include(x => x.SelfAssignableRoleGroupNames));
|
||||
var toUpdate = gc.SelfAssignableRoleGroupNames.FirstOrDefault(x => x.Number == group);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
if (toUpdate != null)
|
||||
gc.SelfAssignableRoleGroupNames.Remove(toUpdate);
|
||||
}
|
||||
else if (toUpdate == null)
|
||||
{
|
||||
gc.SelfAssignableRoleGroupNames.Add(new GroupName
|
||||
{
|
||||
Name = name,
|
||||
Number = group,
|
||||
});
|
||||
set = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
toUpdate.Name = name;
|
||||
set = true;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
public async Task<(RemoveResult Result, bool AutoDelete)> Remove(IGuildUser guildUser, IRole role)
|
||||
{
|
||||
var (autoDelete, _, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
||||
|
||||
if (roles.FirstOrDefault(r => r.RoleId == role.Id) == null)
|
||||
{
|
||||
return (RemoveResult.Err_Not_Assignable, autoDelete);
|
||||
}
|
||||
if (!guildUser.RoleIds.Contains(role.Id))
|
||||
{
|
||||
return (RemoveResult.Err_Not_Have, autoDelete);
|
||||
}
|
||||
try
|
||||
{
|
||||
await guildUser.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return (RemoveResult.Err_Not_Perms, autoDelete);
|
||||
}
|
||||
|
||||
return (RemoveResult.Removed, autoDelete);
|
||||
}
|
||||
|
||||
public bool RemoveSar(ulong guildId, ulong roleId)
|
||||
{
|
||||
bool success;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
success = uow.SelfAssignedRoles.DeleteByGuildAndRoleId(guildId, roleId);
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public (bool AutoDelete, bool Exclusive, IEnumerable<SelfAssignedRole>) GetAdAndRoles(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
var autoDelete = gc.AutoDeleteSelfAssignedRoleMessages;
|
||||
var exclusive = gc.ExclusiveSelfAssignedRoles;
|
||||
var roles = uow.SelfAssignedRoles.GetFromGuild(guildId);
|
||||
|
||||
return (autoDelete, exclusive, roles);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetLevelReq(ulong guildId, IRole role, int level)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var roles = uow.SelfAssignedRoles.GetFromGuild(guildId);
|
||||
var sar = roles.FirstOrDefault(x => x.RoleId == role.Id);
|
||||
if (sar != null)
|
||||
{
|
||||
sar.LevelRequirement = level;
|
||||
uow.SaveChanges();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ToggleEsar(ulong guildId)
|
||||
{
|
||||
bool areExclusive;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, set => set);
|
||||
|
||||
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return areExclusive;
|
||||
}
|
||||
|
||||
public (bool Exclusive, IEnumerable<(SelfAssignedRole Model, IRole Role)> Roles, IDictionary<int, string> GroupNames) GetRoles(IGuild guild)
|
||||
{
|
||||
var exclusive = false;
|
||||
|
||||
IEnumerable<(SelfAssignedRole Model, IRole Role)> roles;
|
||||
IDictionary<int, string> groupNames;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var gc = uow.GuildConfigs.ForId(guild.Id, set => set.Include(x => x.SelfAssignableRoleGroupNames));
|
||||
exclusive = gc.ExclusiveSelfAssignedRoles;
|
||||
groupNames = gc.SelfAssignableRoleGroupNames.ToDictionary(x => x.Number, x => x.Name);
|
||||
var roleModels = uow.SelfAssignedRoles.GetFromGuild(guild.Id);
|
||||
roles = roleModels
|
||||
.Select(x => (Model: x, Role: guild.GetRole(x.RoleId)));
|
||||
uow.SelfAssignedRoles.RemoveRange(roles.Where(x => x.Role == null).Select(x => x.Model).ToArray());
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
return (exclusive, roles.Where(x => x.Role != null), groupNames);
|
||||
}
|
||||
}
|
||||
}
|
436
src/NadekoBot/Modules/Administration/Services/SelfService.cs
Normal file
436
src/NadekoBot/Modules/Administration/Services/SelfService.cs
Normal file
@@ -0,0 +1,436 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services;
|
||||
using StackExchange.Redis;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
using NadekoBot.Common.ShardCom;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
{
|
||||
private readonly ConnectionMultiplexer _redis;
|
||||
private readonly CommandHandler _cmdHandler;
|
||||
private readonly DbService _db;
|
||||
private readonly IBotStrings _strings;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
private ImmutableDictionary<ulong, IDMChannel> ownerChannels =
|
||||
new Dictionary<ulong, IDMChannel>().ToImmutableDictionary();
|
||||
|
||||
private ConcurrentDictionary<ulong?, ConcurrentDictionary<int, Timer>> _autoCommands =
|
||||
new ConcurrentDictionary<ulong?, ConcurrentDictionary<int, Timer>>();
|
||||
|
||||
private readonly IDataCache _cache;
|
||||
private readonly IImageCache _imgs;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly BotConfigService _bss;
|
||||
|
||||
public SelfService(DiscordSocketClient client, CommandHandler cmdHandler, DbService db,
|
||||
IBotStrings strings, IBotCredentials creds, IDataCache cache, IHttpClientFactory factory,
|
||||
BotConfigService bss)
|
||||
{
|
||||
_redis = cache.Redis;
|
||||
_cmdHandler = cmdHandler;
|
||||
_db = db;
|
||||
_strings = strings;
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_cache = cache;
|
||||
_imgs = cache.LocalImages;
|
||||
_httpFactory = factory;
|
||||
_bss = bss;
|
||||
|
||||
var sub = _redis.GetSubscriber();
|
||||
if (_client.ShardId == 0)
|
||||
{
|
||||
sub.Subscribe(_creds.RedisKey() + "_reload_images",
|
||||
delegate { _imgs.Reload(); }, CommandFlags.FireAndForget);
|
||||
}
|
||||
|
||||
sub.Subscribe(_creds.RedisKey() + "_leave_guild", async (ch, v) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var guildStr = v.ToString()?.Trim().ToUpperInvariant();
|
||||
if (string.IsNullOrWhiteSpace(guildStr))
|
||||
return;
|
||||
var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ??
|
||||
_client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
|
||||
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (server.OwnerId != _client.CurrentUser.Id)
|
||||
{
|
||||
await server.LeaveAsync().ConfigureAwait(false);
|
||||
Log.Information($"Left server {server.Name} [{server.Id}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
await server.DeleteAsync().ConfigureAwait(false);
|
||||
Log.Information($"Deleted server {server.Name} [{server.Id}]");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}, CommandFlags.FireAndForget);
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
||||
_autoCommands = uow._context
|
||||
.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._context.AutoCommands.AsNoTracking().Where(x => x.Interval == 0);
|
||||
foreach (var cmd in startupCommands)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ExecuteCommand(cmd).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (_client.ShardId == 0)
|
||||
{
|
||||
await LoadOwnerChannels().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private Timer TimerFromAutoCommand(AutoCommand x)
|
||||
{
|
||||
return new Timer(async (obj) => await ExecuteCommand((AutoCommand) obj).ConfigureAwait(false),
|
||||
x,
|
||||
x.Interval * 1000,
|
||||
x.Interval * 1000);
|
||||
}
|
||||
|
||||
private async Task ExecuteCommand(AutoCommand cmd)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cmd.GuildId is null)
|
||||
return;
|
||||
|
||||
var guildShard = (int) ((cmd.GuildId.Value >> 22) % (ulong) _creds.TotalShards);
|
||||
if (guildShard != _client.ShardId)
|
||||
return;
|
||||
var prefix = _cmdHandler.GetPrefix(cmd.GuildId);
|
||||
//if someone already has .die as their startup command, ignore it
|
||||
if (cmd.CommandText.StartsWith(prefix + "die", StringComparison.InvariantCulture))
|
||||
return;
|
||||
await _cmdHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in SelfService ExecuteCommand");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNewAutoCommand(AutoCommand cmd)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
uow._context.AutoCommands.Add(cmd);
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<AutoCommand> GetStartupCommands()
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow._context
|
||||
.AutoCommands
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval == 0)
|
||||
.OrderBy(x => x.Id)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<AutoCommand> GetAutoCommands()
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow._context
|
||||
.AutoCommands
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval >= 5)
|
||||
.OrderBy(x => x.Id)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task LoadOwnerChannels()
|
||||
{
|
||||
var channels = await Task.WhenAll(_creds.OwnerIds.Select(id =>
|
||||
{
|
||||
var user = _client.GetUser(id);
|
||||
if (user == null)
|
||||
return Task.FromResult<IDMChannel>(null);
|
||||
|
||||
return user.GetOrCreateDMChannelAsync();
|
||||
})).ConfigureAwait(false);
|
||||
|
||||
ownerChannels = channels.Where(x => x != null)
|
||||
.ToDictionary(x => x.Recipient.Id, x => x)
|
||||
.ToImmutableDictionary();
|
||||
|
||||
if (!ownerChannels.Any())
|
||||
Log.Warning(
|
||||
"No owner channels created! Make sure you've specified the correct OwnerId in the credentials.json file and invited the bot to a Discord server.");
|
||||
else
|
||||
Log.Information($"Created {ownerChannels.Count} out of {_creds.OwnerIds.Length} owner message channels.");
|
||||
}
|
||||
|
||||
public Task LeaveGuild(string guildStr)
|
||||
{
|
||||
var sub = _cache.Redis.GetSubscriber();
|
||||
return sub.PublishAsync(_creds.RedisKey() + "_leave_guild", guildStr);
|
||||
}
|
||||
|
||||
// forwards dms
|
||||
public async Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg)
|
||||
{
|
||||
var bs = _bss.Data;
|
||||
if (msg.Channel is IDMChannel && _bss.Data.ForwardMessages && ownerChannels.Any())
|
||||
{
|
||||
var title = _strings.GetText("dm_from") +
|
||||
$" [{msg.Author}]({msg.Author.Id})";
|
||||
|
||||
var attachamentsTxt = _strings.GetText("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));
|
||||
}
|
||||
|
||||
if (bs.ForwardToAllOwners)
|
||||
{
|
||||
var allOwnerChannels = ownerChannels.Values;
|
||||
|
||||
foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
|
||||
{
|
||||
try
|
||||
{
|
||||
await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
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(title, toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool RestartBot()
|
||||
{
|
||||
var cmd = _creds.RestartCommand;
|
||||
if (string.IsNullOrWhiteSpace(cmd?.Cmd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Restart();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveStartupCommand(int index, out AutoCommand cmd)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
cmd = uow._context.AutoCommands
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval == 0)
|
||||
.Skip(index)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (cmd != null)
|
||||
{
|
||||
uow._context.Remove(cmd);
|
||||
uow.SaveChanges();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool RemoveAutoCommand(int index, out AutoCommand cmd)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
cmd = uow._context.AutoCommands
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval >= 5)
|
||||
.Skip(index)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (cmd != null)
|
||||
{
|
||||
uow._context.Remove(cmd);
|
||||
if (_autoCommands.TryGetValue(cmd.GuildId, out var autos))
|
||||
if (autos.TryRemove(cmd.Id, out var timer))
|
||||
timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
uow.SaveChanges();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> SetAvatar(string img)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(img))
|
||||
return false;
|
||||
|
||||
if (!Uri.IsWellFormedUriString(img, UriKind.Absolute))
|
||||
return false;
|
||||
|
||||
var uri = new Uri(img);
|
||||
|
||||
using (var http = _httpFactory.CreateClient())
|
||||
using (var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
|
||||
{
|
||||
if (!sr.IsImage())
|
||||
return false;
|
||||
|
||||
// i can't just do ReadAsStreamAsync because dicord.net's image poops itself
|
||||
var imgData = await sr.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
|
||||
using (var imgStream = imgData.ToStream())
|
||||
{
|
||||
await _client.CurrentUser.ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClearStartupCommands()
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var toRemove = uow._context
|
||||
.AutoCommands
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval == 0);
|
||||
|
||||
uow._context.AutoCommands.RemoveRange(toRemove);
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadImages()
|
||||
{
|
||||
var sub = _cache.Redis.GetSubscriber();
|
||||
sub.Publish(_creds.RedisKey() + "_reload_images", "");
|
||||
}
|
||||
|
||||
public void Die()
|
||||
{
|
||||
var sub = _cache.Redis.GetSubscriber();
|
||||
sub.Publish(_creds.RedisKey() + "_die", "", CommandFlags.FireAndForget);
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
Process.Start(_creds.RestartCommand.Cmd, _creds.RestartCommand.Args);
|
||||
var sub = _cache.Redis.GetSubscriber();
|
||||
sub.Publish(_creds.RedisKey() + "_die", "", CommandFlags.FireAndForget);
|
||||
}
|
||||
|
||||
public bool RestartShard(int shardId)
|
||||
{
|
||||
if (shardId < 0 || shardId >= _creds.TotalShards)
|
||||
return false;
|
||||
|
||||
var pub = _cache.Redis.GetSubscriber();
|
||||
pub.Publish(_creds.RedisKey() + "_shardcoord_stop",
|
||||
JsonConvert.SerializeObject(shardId),
|
||||
CommandFlags.FireAndForget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ForwardMessages()
|
||||
{
|
||||
var isForwarding = false;
|
||||
_bss.ModifyConfig(config => { isForwarding = config.ForwardMessages = !config.ForwardMessages; });
|
||||
|
||||
return isForwarding;
|
||||
}
|
||||
|
||||
public bool ForwardToAll()
|
||||
{
|
||||
var isToAll = false;
|
||||
_bss.ModifyConfig(config => { isToAll = config.ForwardToAllOwners = !config.ForwardToAllOwners; });
|
||||
return isToAll;
|
||||
}
|
||||
|
||||
public IEnumerable<ShardComMessage> GetAllShardStatuses()
|
||||
{
|
||||
var db = _cache.Redis.GetDatabase();
|
||||
return db.ListRange(_creds.RedisKey() + "_shardstats")
|
||||
.Select(x => JsonConvert.DeserializeObject<ShardComMessage>(x));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,488 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Permissions.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public class UserPunishService : INService
|
||||
{
|
||||
private readonly MuteService _mute;
|
||||
private readonly DbService _db;
|
||||
private readonly BlacklistService _blacklistService;
|
||||
private readonly Timer _warnExpiryTimer;
|
||||
|
||||
public UserPunishService(MuteService mute, DbService db, BlacklistService blacklistService)
|
||||
{
|
||||
_mute = mute;
|
||||
_db = db;
|
||||
_blacklistService = blacklistService;
|
||||
|
||||
_warnExpiryTimer = new Timer(async _ =>
|
||||
{
|
||||
await CheckAllWarnExpiresAsync();
|
||||
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromHours(12));
|
||||
}
|
||||
|
||||
public async Task<WarningPunishment> Warn(IGuild guild, ulong userId, IUser mod, string reason)
|
||||
{
|
||||
var modName = mod.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
reason = "-";
|
||||
|
||||
var guildId = guild.Id;
|
||||
|
||||
var warn = new Warning()
|
||||
{
|
||||
UserId = userId,
|
||||
GuildId = guildId,
|
||||
Forgiven = false,
|
||||
Reason = reason,
|
||||
Moderator = modName,
|
||||
};
|
||||
|
||||
int warnings = 1;
|
||||
List<WarningPunishment> ps;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
ps = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.WarnPunishments))
|
||||
.WarnPunishments;
|
||||
|
||||
warnings += uow.Warnings
|
||||
.ForId(guildId, userId)
|
||||
.Where(w => !w.Forgiven && w.UserId == userId)
|
||||
.Count();
|
||||
|
||||
uow.Warnings.Add(warn);
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
var p = ps.FirstOrDefault(x => x.Count == warnings);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
var user = await guild.GetUserAsync(userId).ConfigureAwait(false);
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
await ApplyPunishment(guild, user, mod, p.Punishment, p.Time, p.RoleId, "Warned too many times.");
|
||||
return p;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task ApplyPunishment(IGuild guild, IGuildUser user, IUser mod, PunishmentAction p, int minutes,
|
||||
ulong? roleId, string reason)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case PunishmentAction.Mute:
|
||||
if (minutes == 0)
|
||||
await _mute.MuteUser(user, mod, reason: reason).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedMute(user, mod, TimeSpan.FromMinutes(minutes), reason: reason)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.VoiceMute:
|
||||
if (minutes == 0)
|
||||
await _mute.MuteUser(user, mod, MuteType.Voice, reason).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedMute(user, mod, TimeSpan.FromMinutes(minutes), MuteType.Voice, reason)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.ChatMute:
|
||||
if (minutes == 0)
|
||||
await _mute.MuteUser(user, mod, MuteType.Chat, reason).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedMute(user, mod, TimeSpan.FromMinutes(minutes), MuteType.Chat, reason)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.Kick:
|
||||
await user.KickAsync(reason).ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.Ban:
|
||||
if (minutes == 0)
|
||||
await guild.AddBanAsync(user, reason: reason).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.Softban:
|
||||
await guild.AddBanAsync(user, 7, reason: $"Softban | {reason}").ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await guild.RemoveBanAsync(user).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await guild.RemoveBanAsync(user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
case PunishmentAction.RemoveRoles:
|
||||
await user.RemoveRolesAsync(user.GetRoles().Where(x => !x.IsManaged && x != x.Guild.EveryoneRole))
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.AddRole:
|
||||
if (roleId is null)
|
||||
return;
|
||||
var role = guild.GetRole(roleId.Value);
|
||||
if (!(role is null))
|
||||
{
|
||||
if (minutes == 0)
|
||||
await user.AddRoleAsync(role).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedRole(user, TimeSpan.FromMinutes(minutes), reason, role)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"Can't find role {roleId.Value} on server {guild.Id} to apply punishment.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckAllWarnExpiresAsync()
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var cleared = await uow._context.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._context.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)
|
||||
{
|
||||
Log.Information($"Cleared {cleared} warnings and deleted {deleted} warnings due to expiry.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckWarnExpiresAsync(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, inc => inc);
|
||||
|
||||
if (config.WarnExpireHours == 0)
|
||||
return;
|
||||
|
||||
var hours = $"{-config.WarnExpireHours} hours";
|
||||
if (config.WarnExpireAction == WarnExpireAction.Clear)
|
||||
{
|
||||
await uow._context.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._context.Database.ExecuteSqlInterpolatedAsync($@"DELETE FROM warnings
|
||||
WHERE GuildId={guildId}
|
||||
AND DateAdded < datetime('now', {hours})");
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<int> GetWarnExpire(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var config = uow.GuildConfigs.ForId(guildId);
|
||||
return Task.FromResult(config.WarnExpireHours / 24);
|
||||
}
|
||||
|
||||
public async Task WarnExpireAsync(ulong guildId, int days, bool delete)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var config = uow.GuildConfigs.ForId(guildId, inc => inc);
|
||||
|
||||
config.WarnExpireHours = days * 24;
|
||||
config.WarnExpireAction = delete ? WarnExpireAction.Delete : WarnExpireAction.Clear;
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
// no need to check for warn expires
|
||||
if (config.WarnExpireHours == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
await CheckWarnExpiresAsync(guildId);
|
||||
}
|
||||
|
||||
public IGrouping<ulong, Warning>[] WarnlogAll(ulong gid)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
return uow.Warnings.GetForGuild(gid).GroupBy(x => x.UserId).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public Warning[] UserWarnings(ulong gid, ulong userId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
return uow.Warnings.ForId(gid, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> WarnClearAsync(ulong guildId, ulong userId, int index, string moderator)
|
||||
{
|
||||
bool toReturn = true;
|
||||
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)
|
||||
{
|
||||
// these 3 don't make sense with time
|
||||
if ((punish == PunishmentAction.Softban || punish == PunishmentAction.Kick || punish == PunishmentAction.RemoveRoles) && time != null)
|
||||
return false;
|
||||
if (number <= 0 || (time != null && time.Time > TimeSpan.FromDays(49)))
|
||||
return false;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var ps = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||
var toDelete = ps.Where(x => x.Count == number);
|
||||
|
||||
uow._context.RemoveRange(toDelete);
|
||||
|
||||
ps.Add(new WarningPunishment()
|
||||
{
|
||||
Count = number,
|
||||
Punishment = punish,
|
||||
Time = (int?)(time?.Time.TotalMinutes) ?? 0,
|
||||
RoleId = punish == PunishmentAction.AddRole ? role.Id : default(ulong?),
|
||||
});
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WarnPunishRemove(ulong guildId, int number)
|
||||
{
|
||||
if (number <= 0)
|
||||
return false;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var ps = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||
var p = ps.FirstOrDefault(x => x.Count == number);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
uow._context.Remove(p);
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public WarningPunishment[] WarnPunishList(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
return uow.GuildConfigs.ForId(guildId, gc => gc.Include(x => x.WarnPunishments))
|
||||
.WarnPunishments
|
||||
.OrderBy(x => x.Count)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
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(" ");
|
||||
|
||||
var reason = string.Join(" ", split.Skip(1));
|
||||
|
||||
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();
|
||||
|
||||
//if user is null, means that person couldn't be found
|
||||
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();
|
||||
|
||||
_blacklistService.BlacklistUsers(found);
|
||||
|
||||
return (bans, missing);
|
||||
}
|
||||
|
||||
public string GetBanTemplate(ulong guildId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var template = uow._context.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._context.BanTemplates
|
||||
.AsQueryable()
|
||||
.FirstOrDefault(x => x.GuildId == guildId);
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
if (template is null)
|
||||
return;
|
||||
|
||||
uow._context.Remove(template);
|
||||
}
|
||||
else if (template == null)
|
||||
{
|
||||
uow._context.BanTemplates.Add(new BanTemplate()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Text = text,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
template.Text = text;
|
||||
}
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public CREmbed GetBanUserDmEmbed(ICommandContext context, IGuildUser target, string defaultMessage,
|
||||
string banReason, TimeSpan? duration)
|
||||
{
|
||||
return GetBanUserDmEmbed(
|
||||
(DiscordSocketClient) context.Client,
|
||||
(SocketGuild) context.Guild,
|
||||
(IGuildUser) context.User,
|
||||
target,
|
||||
defaultMessage,
|
||||
banReason,
|
||||
duration);
|
||||
}
|
||||
|
||||
public CREmbed 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;
|
||||
|
||||
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();
|
||||
|
||||
CREmbed crEmbed = null;
|
||||
// if template isn't set, use the old message style
|
||||
if (string.IsNullOrWhiteSpace(template))
|
||||
{
|
||||
template = JsonConvert.SerializeObject(new
|
||||
{
|
||||
color = NadekoBot.ErrorColor.RawValue,
|
||||
description = defaultMessage
|
||||
});
|
||||
|
||||
CREmbed.TryParse(template, out crEmbed);
|
||||
}
|
||||
// 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
|
||||
else if (CREmbed.TryParse(template, out crEmbed))
|
||||
{
|
||||
replacer.Replace(crEmbed);
|
||||
}
|
||||
// otherwise, treat template as a regular string with replacements
|
||||
else
|
||||
{
|
||||
template = JsonConvert.SerializeObject(new
|
||||
{
|
||||
color = NadekoBot.ErrorColor.RawValue,
|
||||
description = replacer.Replace(template)
|
||||
});
|
||||
|
||||
CREmbed.TryParse(template, out crEmbed);
|
||||
}
|
||||
|
||||
return crEmbed;
|
||||
}
|
||||
}
|
||||
}
|
230
src/NadekoBot/Modules/Administration/Services/VcRoleService.cs
Normal file
230
src/NadekoBot/Modules/Administration/Services/VcRoleService.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using Serilog;
|
||||
|
||||
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; }
|
||||
|
||||
public VcRoleService(DiscordSocketClient client, NadekoBot bot, DbService db)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
|
||||
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
|
||||
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
|
||||
ToAssign = new ConcurrentDictionary<ulong, ConcurrentQueue<(bool, IGuildUser, IRole)>>();
|
||||
var missingRoles = new ConcurrentBag<VcRoleInfo>();
|
||||
|
||||
using (var uow = db.GetDbContext())
|
||||
{
|
||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||
var configs = uow._context.Set<GuildConfig>()
|
||||
.AsQueryable()
|
||||
.Include(x => x.VcRoleInfos)
|
||||
.Where(x => guildIds.Contains(x.GuildId))
|
||||
.ToList();
|
||||
|
||||
Task.WhenAll(configs.Select(InitializeVcRole));
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var tasks = ToAssign.Values.Select(queue => Task.Run(async () =>
|
||||
{
|
||||
while (queue.TryDequeue(out var item))
|
||||
{
|
||||
var (add, user, role) = item;
|
||||
if (add)
|
||||
{
|
||||
if (!user.RoleIds.Contains(role.Id))
|
||||
{
|
||||
try { await user.AddRoleAsync(role).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (user.RoleIds.Contains(role.Id))
|
||||
{
|
||||
try { await user.RemoveRoleAsync(role).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(250).ConfigureAwait(false);
|
||||
}
|
||||
}));
|
||||
|
||||
await Task.WhenAll(tasks.Append(Task.Delay(1000))).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
|
||||
_client.LeftGuild += _client_LeftGuild;
|
||||
bot.JoinedGuild += Bot_JoinedGuild;
|
||||
}
|
||||
|
||||
private Task Bot_JoinedGuild(GuildConfig arg)
|
||||
{
|
||||
// includeall no longer loads vcrole
|
||||
// need to load new guildconfig with vc role included
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var configWithVcRole = uow.GuildConfigs.ForId(
|
||||
arg.GuildId,
|
||||
set => set.Include(x => x.VcRoleInfos)
|
||||
);
|
||||
var _ = InitializeVcRole(configWithVcRole);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task _client_LeftGuild(SocketGuild arg)
|
||||
{
|
||||
VcRoles.TryRemove(arg.Id, out _);
|
||||
ToAssign.TryRemove(arg.Id, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task InitializeVcRole(GuildConfig gconf)
|
||||
{
|
||||
await Task.Yield();
|
||||
var g = _client.GetGuild(gconf.GuildId);
|
||||
if (g == null)
|
||||
return;
|
||||
|
||||
var infos = new ConcurrentDictionary<ulong, IRole>();
|
||||
var missingRoles = new List<VcRoleInfo>();
|
||||
VcRoles.AddOrUpdate(gconf.GuildId, infos, delegate { return infos; });
|
||||
foreach (var ri in gconf.VcRoleInfos)
|
||||
{
|
||||
var role = g.GetRole(ri.RoleId);
|
||||
if (role == null)
|
||||
{
|
||||
missingRoles.Add(ri);
|
||||
continue;
|
||||
}
|
||||
|
||||
infos.TryAdd(ri.VoiceChannelId, role);
|
||||
}
|
||||
|
||||
if (missingRoles.Any())
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
Log.Warning($"Removing {missingRoles.Count} missing roles from {nameof(VcRoleService)}");
|
||||
uow._context.RemoveRange(missingRoles);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVcRole(ulong guildId, IRole role, ulong vcId)
|
||||
{
|
||||
if (role == null)
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
|
||||
var guildVcRoles = VcRoles.GetOrAdd(guildId, new ConcurrentDictionary<ulong, IRole>());
|
||||
|
||||
guildVcRoles.AddOrUpdate(vcId, role, (key, old) => role);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.VcRoleInfos));
|
||||
var toDelete = conf.VcRoleInfos.FirstOrDefault(x => x.VoiceChannelId == vcId); // remove old one
|
||||
if(toDelete != null)
|
||||
{
|
||||
uow._context.Remove(toDelete);
|
||||
}
|
||||
conf.VcRoleInfos.Add(new VcRoleInfo()
|
||||
{
|
||||
VoiceChannelId = vcId,
|
||||
RoleId = role.Id,
|
||||
}); // add new one
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveVcRole(ulong guildId, ulong vcId)
|
||||
{
|
||||
if (!VcRoles.TryGetValue(guildId, out var guildVcRoles))
|
||||
return false;
|
||||
|
||||
if (!guildVcRoles.TryRemove(vcId, out _))
|
||||
return false;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigs.ForId(guildId, set => set.Include(x => x.VcRoleInfos));
|
||||
var toRemove = conf.VcRoleInfos.Where(x => x.VoiceChannelId == vcId).ToList();
|
||||
uow._context.RemoveRange(toRemove);
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
|
||||
SocketVoiceState newState)
|
||||
{
|
||||
|
||||
var gusr = usr as SocketGuildUser;
|
||||
if (gusr == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var oldVc = oldState.VoiceChannel;
|
||||
var newVc = newState.VoiceChannel;
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (oldVc != newVc)
|
||||
{
|
||||
ulong guildId;
|
||||
guildId = newVc?.Guild.Id ?? oldVc.Guild.Id;
|
||||
|
||||
if (VcRoles.TryGetValue(guildId, out ConcurrentDictionary<ulong, IRole> guildVcRoles))
|
||||
{
|
||||
//remove old
|
||||
if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out IRole role))
|
||||
{
|
||||
Assign(false, gusr, role);
|
||||
}
|
||||
//add new
|
||||
if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role))
|
||||
{
|
||||
Assign(true, gusr, role);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in VcRoleService VoiceStateUpdate");
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void Assign(bool v, SocketGuildUser gusr, IRole role)
|
||||
{
|
||||
var queue = ToAssign.GetOrAdd(gusr.Guild.Id, new ConcurrentQueue<(bool, IGuildUser, IRole)>());
|
||||
queue.Enqueue((v, gusr, role));
|
||||
}
|
||||
}
|
||||
}
|
90
src/NadekoBot/Modules/Administration/TimeZoneCommands.cs
Normal file
90
src/NadekoBot/Modules/Administration/TimeZoneCommands.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class TimeZoneCommands : NadekoSubmodule<GuildTimezoneService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Timezones(int page = 1)
|
||||
{
|
||||
page--;
|
||||
|
||||
if (page < 0 || page > 20)
|
||||
return;
|
||||
|
||||
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}";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
await ctx.SendPaginatedConfirmAsync(page,
|
||||
(curPage) => new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText("timezones_available"))
|
||||
.WithDescription(string.Join("\n", timezoneStrings
|
||||
.Skip(curPage * timezonesPerPage)
|
||||
.Take(timezonesPerPage))),
|
||||
timezones.Length, timezonesPerPage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Timezone()
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("timezone_guild", _service.GetTimeZoneOrUtc(ctx.Guild.Id)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task Timezone([Leftover] string id)
|
||||
{
|
||||
TimeZoneInfo tz;
|
||||
try { tz = TimeZoneInfo.FindSystemTimeZoneById(id); } catch { tz = null; }
|
||||
|
||||
|
||||
if (tz == null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("timezone_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
_service.SetTimeZone(ctx.Guild.Id, tz);
|
||||
|
||||
await ctx.Channel.SendConfirmAsync(tz.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
802
src/NadekoBot/Modules/Administration/UserPunishCommands.cs
Normal file
802
src/NadekoBot/Modules/Administration/UserPunishCommands.cs
Normal file
@@ -0,0 +1,802 @@
|
||||
using CommandLine;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Core.Common;
|
||||
using NadekoBot.Core.Common.TypeReaders.Models;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Permissions.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class UserPunishCommands : NadekoSubmodule<UserPunishService>
|
||||
{
|
||||
private readonly MuteService _mute;
|
||||
private readonly BlacklistService _blacklistService;
|
||||
|
||||
public UserPunishCommands(MuteService mute, BlacklistService blacklistService)
|
||||
{
|
||||
_mute = mute;
|
||||
_blacklistService = blacklistService;
|
||||
}
|
||||
|
||||
private async Task<bool> CheckRoleHierarchy(IGuildUser target)
|
||||
{
|
||||
var curUser = ((SocketGuild) ctx.Guild).CurrentUser;
|
||||
var ownerId = Context.Guild.OwnerId;
|
||||
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 || (Context.User.Id != ownerId && targetMaxRole >= modMaxRole)) || target.Id == ownerId)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("hierarchy");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task Warn(IGuildUser user, [Leftover] string reason = null)
|
||||
{
|
||||
if (!await CheckRoleHierarchy(user))
|
||||
return;
|
||||
|
||||
var dmFailed = false;
|
||||
try
|
||||
{
|
||||
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).EmbedAsync(new EmbedBuilder().WithErrorColor()
|
||||
.WithDescription(GetText("warned_on", ctx.Guild.ToString()))
|
||||
.AddField(efb => efb.WithName(GetText("moderator")).WithValue(ctx.User.ToString()))
|
||||
.AddField(efb => efb.WithName(GetText("reason")).WithValue(reason ?? "-")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
|
||||
WarningPunishment punishment;
|
||||
try
|
||||
{
|
||||
punishment = await _service.Warn(ctx.Guild, user.Id, ctx.User, reason).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex.Message);
|
||||
var errorEmbed = new EmbedBuilder()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText("cant_apply_punishment"));
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
errorEmbed.WithFooter("⚠️ " + GetText("unable_to_dm_user"));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(errorEmbed);
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor();
|
||||
if (punishment == null)
|
||||
{
|
||||
embed.WithDescription(GetText("user_warned",
|
||||
Format.Bold(user.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.WithDescription(GetText("user_warned_and_punished", Format.Bold(user.ToString()),
|
||||
Format.Bold(punishment.Punishment.ToString())));
|
||||
}
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
embed.WithFooter("⚠️ " + GetText("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, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[NadekoOptions(typeof(WarnExpireOptions))]
|
||||
[Priority(1)]
|
||||
public async Task WarnExpire()
|
||||
{
|
||||
var expireDays = await _service.GetWarnExpire(ctx.Guild.Id);
|
||||
|
||||
if (expireDays == 0)
|
||||
await ReplyConfirmLocalizedAsync("warns_dont_expire");
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync("warns_expire_in", expireDays);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[NadekoOptions(typeof(WarnExpireOptions))]
|
||||
[Priority(2)]
|
||||
public async Task WarnExpire(int days, params string[] args)
|
||||
{
|
||||
if (days < 0 || days > 366)
|
||||
return;
|
||||
|
||||
var opts = OptionsParser.ParseFrom<WarnExpireOptions>(args);
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
|
||||
await _service.WarnExpireAsync(ctx.Guild.Id, days, opts.Delete).ConfigureAwait(false);
|
||||
if(days == 0)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_expire_reset").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.Delete)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_expire_set_delete", Format.Bold(days.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_expire_set_clear", Format.Bold(days.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[Priority(2)]
|
||||
public Task Warnlog(int page, [Leftover] IGuildUser user = null)
|
||||
{
|
||||
user ??= (IGuildUser) ctx.User;
|
||||
|
||||
return Warnlog(page, user.Id);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(3)]
|
||||
public Task Warnlog(IGuildUser user = null)
|
||||
{
|
||||
user ??= (IGuildUser) ctx.User;
|
||||
|
||||
return ctx.User.Id == user.Id || ((IGuildUser)ctx.User).GuildPermissions.BanMembers ? Warnlog(user.Id) : Task.CompletedTask;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[Priority(0)]
|
||||
public Task Warnlog(int page, ulong userId)
|
||||
=> InternalWarnlog(userId, page - 1);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[Priority(1)]
|
||||
public Task Warnlog(ulong userId)
|
||||
=> InternalWarnlog(userId, 0);
|
||||
|
||||
private async Task InternalWarnlog(ulong userId, int inputPage)
|
||||
{
|
||||
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 = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText("warnlog_for", user));
|
||||
|
||||
if (!warnings.Any())
|
||||
{
|
||||
embed.WithDescription(GetText("warnings_none"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = page * 9;
|
||||
foreach (var w in warnings)
|
||||
{
|
||||
i++;
|
||||
var name = GetText("warned_on_by",
|
||||
w.DateAdded.Value.ToString("dd.MM.yyy"),
|
||||
w.DateAdded.Value.ToString("HH:mm"),
|
||||
w.Moderator);
|
||||
|
||||
if (w.Forgiven)
|
||||
name = $"{Format.Strikethrough(name)} {GetText("warn_cleared_by", w.ForgivenBy)}";
|
||||
|
||||
embed.AddField($"#`{i}` " + name, w.Reason.TrimTo(1020));
|
||||
}
|
||||
}
|
||||
|
||||
return embed;
|
||||
}, allWarnings.Length, 9);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task WarnlogAll(int page = 1)
|
||||
{
|
||||
if (--page < 0)
|
||||
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})";
|
||||
});
|
||||
|
||||
return new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("warnings_list"))
|
||||
.WithDescription(string.Join("\n", ws));
|
||||
}, warnings.Length, 15).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public Task Warnclear(IGuildUser user, int index = 0)
|
||||
=> Warnclear(user.Id, index);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task Warnclear(ulong userId, int index = 0)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
var success = await _service.WarnClearAsync(ctx.Guild.Id, userId, index, ctx.User.ToString());
|
||||
var userStr = Format.Bold((ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString());
|
||||
if (index == 0)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warnings_cleared", userStr).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warning_cleared", Format.Bold(index.ToString()), userStr)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("warning_clear_fail").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AddRole
|
||||
{
|
||||
AddRole
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[Priority(1)]
|
||||
public async Task WarnPunish(int number, AddRole _, IRole role, StoopidTime time = null)
|
||||
{
|
||||
var punish = PunishmentAction.AddRole;
|
||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_punish_set",
|
||||
Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_punish_set_timed",
|
||||
Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
|
||||
{
|
||||
// this should never happen. Addrole has its own method with higher priority
|
||||
if (punish == PunishmentAction.AddRole)
|
||||
return;
|
||||
|
||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_punish_set",
|
||||
Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("warn_punish_set_timed",
|
||||
Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
public async Task WarnPunish(int number)
|
||||
{
|
||||
if (!_service.WarnPunishRemove(ctx.Guild.Id, number))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync("warn_punish_rem",
|
||||
Format.Bold(number.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task WarnPunishList()
|
||||
{
|
||||
var ps = _service.WarnPunishList(ctx.Guild.Id);
|
||||
|
||||
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")} "));
|
||||
}
|
||||
else
|
||||
{
|
||||
list = GetText("warnpl_none");
|
||||
}
|
||||
await ctx.Channel.SendConfirmAsync(
|
||||
GetText("warn_punish_list"),
|
||||
list).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(1)]
|
||||
public async Task Ban(StoopidTime time, IUser user, [Leftover] string msg = null)
|
||||
{
|
||||
if (time.Time > TimeSpan.FromDays(49))
|
||||
return;
|
||||
|
||||
var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(Context.Guild.Id, user.Id);
|
||||
|
||||
if (guildUser != null && !await CheckRoleHierarchy(guildUser))
|
||||
return;
|
||||
|
||||
var dmFailed = false;
|
||||
|
||||
if (guildUser != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaultMessage = GetText("bandm", Format.Bold(ctx.Guild.Name), msg);
|
||||
var embed = _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
|
||||
if (!(embed is null))
|
||||
{
|
||||
var userChannel = await guildUser.GetOrCreateDMChannelAsync();
|
||||
await userChannel.EmbedAsync(embed);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
await _mute.TimedBan(Context.Guild, user, time.Time, ctx.User.ToString() + " | " + msg).ConfigureAwait(false);
|
||||
var toSend = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText("banned_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("duration")).WithValue($"{time.Time.Days}d {time.Time.Hours}h {time.Time.Minutes}m").WithIsInline(true));
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
toSend.WithFooter("⚠️ " + GetText("unable_to_dm_user"));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(toSend)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(0)]
|
||||
public async Task Ban(ulong userId, [Leftover] string msg = null)
|
||||
{
|
||||
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(Context.Guild.Id, userId);
|
||||
if (user is null)
|
||||
{
|
||||
await ctx.Guild.AddBanAsync(userId, 7, ctx.User.ToString() + " | " + msg);
|
||||
|
||||
await ctx.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText("banned_user"))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(userId.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Ban(user, msg);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(2)]
|
||||
public async Task Ban(IGuildUser user, [Leftover] string msg = null)
|
||||
{
|
||||
if (!await CheckRoleHierarchy(user))
|
||||
return;
|
||||
|
||||
var dmFailed = false;
|
||||
|
||||
try
|
||||
{
|
||||
var defaultMessage = GetText("bandm", Format.Bold(ctx.Guild.Name), msg);
|
||||
var embed = _service.GetBanUserDmEmbed(Context, user, defaultMessage, msg, null);
|
||||
if (!(embed is null))
|
||||
{
|
||||
var userChannel = await user.GetOrCreateDMChannelAsync();
|
||||
await userChannel.EmbedAsync(embed);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
|
||||
await ctx.Guild.AddBanAsync(user, 7, ctx.User.ToString() + " | " + msg).ConfigureAwait(false);
|
||||
|
||||
var toSend = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText("banned_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true));
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
toSend.WithFooter("⚠️ " + GetText("unable_to_dm_user"));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(toSend)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
public async Task BanMessage([Leftover] string message = null)
|
||||
{
|
||||
if (message is null)
|
||||
{
|
||||
var template = _service.GetBanTemplate(Context.Guild.Id);
|
||||
if (template is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("banmsg_default");
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync(template);
|
||||
return;
|
||||
}
|
||||
|
||||
_service.SetBanTemplate(Context.Guild.Id, message);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
public async Task BanMsgReset()
|
||||
{
|
||||
_service.SetBanTemplate(Context.Guild.Id, null);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Priority(0)]
|
||||
public Task BanMessageTest([Leftover] string reason = null)
|
||||
=> InternalBanMessageTest(reason, null);
|
||||
|
||||
[NadekoCommand, Usage, Description, 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 dmChannel = await ctx.User.GetOrCreateDMChannelAsync();
|
||||
var defaultMessage = GetText("bandm", Format.Bold(ctx.Guild.Name), reason);
|
||||
var crEmbed = _service.GetBanUserDmEmbed(Context,
|
||||
(IGuildUser)Context.User,
|
||||
defaultMessage,
|
||||
reason,
|
||||
duration);
|
||||
|
||||
if (crEmbed is null)
|
||||
{
|
||||
await ConfirmLocalizedAsync("bandm_disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await dmChannel.EmbedAsync(crEmbed);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("unable_to_dm_user");
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.OkAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
public async Task Unban([Leftover] string user)
|
||||
{
|
||||
var bans = await ctx.Guild.GetBansAsync().ConfigureAwait(false);
|
||||
|
||||
var bun = bans.FirstOrDefault(x => x.User.ToString().ToLowerInvariant() == user.ToLowerInvariant());
|
||||
|
||||
if (bun == null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("user_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await UnbanInternal(bun.User).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
public async Task Unban(ulong userId)
|
||||
{
|
||||
var bans = await ctx.Guild.GetBansAsync().ConfigureAwait(false);
|
||||
|
||||
var bun = bans.FirstOrDefault(x => x.User.Id == userId);
|
||||
|
||||
if (bun == null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("user_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await UnbanInternal(bun.User).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UnbanInternal(IUser user)
|
||||
{
|
||||
await ctx.Guild.RemoveBanAsync(user).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalizedAsync("unbanned_user", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, 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, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.KickMembers | GuildPerm.ManageMessages)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
public async Task Softban(ulong userId, [Leftover] string msg = null)
|
||||
{
|
||||
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(Context.Guild.Id, userId);
|
||||
if (user is null)
|
||||
return;
|
||||
|
||||
await SoftbanInternal(user, msg);
|
||||
}
|
||||
|
||||
private async Task SoftbanInternal(IGuildUser user, [Leftover] string msg = null)
|
||||
{
|
||||
if (!await CheckRoleHierarchy(user))
|
||||
return;
|
||||
|
||||
var dmFailed = false;
|
||||
|
||||
try
|
||||
{
|
||||
await user.SendErrorAsync(GetText("sbdm", Format.Bold(ctx.Guild.Name), msg)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
|
||||
await ctx.Guild.AddBanAsync(user, 7, "Softban | " + ctx.User.ToString() + " | " + msg).ConfigureAwait(false);
|
||||
try { await ctx.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
catch { await ctx.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
|
||||
var toSend = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("☣ " + GetText("sb_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true));
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
toSend.WithFooter("⚠️ " + GetText("unable_to_dm_user"));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(toSend)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.KickMembers)]
|
||||
[BotPerm(GuildPerm.KickMembers)]
|
||||
[Priority(1)]
|
||||
public Task Kick(IGuildUser user, [Leftover] string msg = null)
|
||||
=> KickInternal(user, msg);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.KickMembers)]
|
||||
[BotPerm(GuildPerm.KickMembers)]
|
||||
[Priority(0)]
|
||||
public async Task Kick(ulong userId, [Leftover] string msg = null)
|
||||
{
|
||||
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(Context.Guild.Id, userId);
|
||||
if (user is null)
|
||||
return;
|
||||
|
||||
await KickInternal(user, msg);
|
||||
}
|
||||
|
||||
public async Task KickInternal(IGuildUser user, string msg = null)
|
||||
{
|
||||
if (!await CheckRoleHierarchy(user))
|
||||
return;
|
||||
|
||||
var dmFailed = false;
|
||||
|
||||
try
|
||||
{
|
||||
await user.SendErrorAsync(GetText("kickdm", Format.Bold(ctx.Guild.Name), msg))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
|
||||
await user.KickAsync(ctx.User.ToString() + " | " + msg).ConfigureAwait(false);
|
||||
|
||||
var toSend = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("kicked_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true));
|
||||
|
||||
if (dmFailed)
|
||||
{
|
||||
toSend.WithFooter("⚠️ " + GetText("unable_to_dm_user"));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(toSend)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[OwnerOnly]
|
||||
public async Task MassKill([Leftover] string people)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(people))
|
||||
return;
|
||||
|
||||
var (bans, missing) = _service.MassKill((SocketGuild)ctx.Guild, people);
|
||||
|
||||
var missStr = string.Join("\n", missing);
|
||||
if (string.IsNullOrWhiteSpace(missStr))
|
||||
missStr = "-";
|
||||
|
||||
//send a message but don't wait for it
|
||||
var banningMessageTask = ctx.Channel.EmbedAsync(new EmbedBuilder()
|
||||
.WithDescription(GetText("mass_kill_in_progress", bans.Count()))
|
||||
.AddField(GetText("invalid", missing), missStr)
|
||||
.WithOkColor());
|
||||
|
||||
//do the banning
|
||||
await Task.WhenAll(bans
|
||||
.Where(x => x.Id.HasValue)
|
||||
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value, 7, x.Reason, new RequestOptions()
|
||||
{
|
||||
RetryMode = RetryMode.AlwaysRetry,
|
||||
})))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
//wait for the message and edit it
|
||||
var banningMessage = await banningMessageTask.ConfigureAwait(false);
|
||||
|
||||
await banningMessage.ModifyAsync(x => x.Embed = new EmbedBuilder()
|
||||
.WithDescription(GetText("mass_kill_completed", bans.Count()))
|
||||
.AddField(GetText("invalid", missing), missStr)
|
||||
.WithOkColor()
|
||||
.Build()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
93
src/NadekoBot/Modules/Administration/VcRoleCommands.cs
Normal file
93
src/NadekoBot/Modules/Administration/VcRoleCommands.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class VcRoleCommands : NadekoSubmodule<VcRoleService>
|
||||
{
|
||||
[NadekoCommand, Usage, Description, 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("vcrole_removed", Format.Bold(vcId.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("vcrole_not_found").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task VcRole([Leftover] IRole role = null)
|
||||
{
|
||||
var user = (IGuildUser)ctx.User;
|
||||
|
||||
var vc = user.VoiceChannel;
|
||||
|
||||
if (vc == null || vc.GuildId != user.GuildId)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync("must_be_in_voice").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
if (_service.RemoveVcRole(ctx.Guild.Id, vc.Id))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync("vcrole_removed", Format.Bold(vc.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_service.AddVcRole(ctx.Guild.Id, role, vc.Id);
|
||||
await ReplyConfirmLocalizedAsync("vcrole_added", Format.Bold(vc.Name), Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task VcRoleList()
|
||||
{
|
||||
var guild = (SocketGuild)ctx.Guild;
|
||||
string text;
|
||||
if (_service.VcRoles.TryGetValue(ctx.Guild.Id, out ConcurrentDictionary<ulong, IRole> roles))
|
||||
{
|
||||
if (!roles.Any())
|
||||
{
|
||||
text = GetText("no_vcroles");
|
||||
}
|
||||
else
|
||||
{
|
||||
text = string.Join("\n", roles.Select(x =>
|
||||
$"{Format.Bold(guild.GetVoiceChannel(x.Key)?.Name ?? x.Key.ToString())} => {x.Value}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text = GetText("no_vcroles");
|
||||
}
|
||||
await ctx.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("vc_role_list"))
|
||||
.WithDescription(text))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user