Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/3.6.0 #586

Merged
merged 12 commits into from
Oct 5, 2023
68 changes: 40 additions & 28 deletions backend/MASZ/Attributes/RequireAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using MASZ.Enums;
using MASZ.Exceptions;
using MASZ.Models;
Expand All @@ -17,79 +18,90 @@ public RequireAttribute(params RequireCheckEnum[] checks)
_checks = checks;
}

public async Task<PreconditionResult> CheckRequirementsAsync(SocketModal modal, IServiceProvider services)
=> await InternalCheckRequirementsAsync(modal.User, modal.GuildId, null, services);

public override async Task<PreconditionResult> CheckRequirementsAsync(IInteractionContext context, ICommandInfo commandInfo, IServiceProvider services)
=> await InternalCheckRequirementsAsync(context.User, context.Guild.Id, commandInfo, services);

private async Task<PreconditionResult> InternalCheckRequirementsAsync(IUser user, ulong? guildId, ICommandInfo commandInfo, IServiceProvider services)
{
var identity = await services.GetRequiredService<IdentityManager>().GetIdentity(context.User);
var identity = await services.GetRequiredService<IdentityManager>().GetIdentity(user);

foreach (RequireCheckEnum check in _checks)
{
if (check != RequireCheckEnum.SiteAdmin && !guildId.HasValue)
{
throw new BaseAPIException("Only usable in a guild.", APIError.OnlyUsableInAGuild);
}

switch (check)
{
case RequireCheckEnum.SiteAdmin:
if (!identity.IsSiteAdmin())
{
throw new UnauthorizedException("Only site admins allowed.");
}
continue;
case RequireCheckEnum.GuildRegistered:
await RequireRegisteredGuild(services, context);
await RequireRegisteredGuild(services, guildId.Value);
continue;
case RequireCheckEnum.GuildMember:
await RequireDiscordPermission(DiscordPermission.Member, services, context, identity);
await RequireDiscordPermission(DiscordPermission.Member, services, guildId.Value, identity);
continue;
case RequireCheckEnum.GuildModerator:
await RequireDiscordPermission(DiscordPermission.Moderator, services, context, identity);
await RequireDiscordPermission(DiscordPermission.Moderator, services, guildId.Value, identity);
continue;
case RequireCheckEnum.GuildAdmin:
await RequireDiscordPermission(DiscordPermission.Admin, services, context, identity);
await RequireDiscordPermission(DiscordPermission.Admin, services, guildId.Value, identity);
continue;
case RequireCheckEnum.GuildMuteRole:
try
{
GuildConfig guildConfig = await GuildConfigRepository.CreateDefault(services).GetGuildConfig(context.Guild.Id);
GuildConfig guildConfig = await GuildConfigRepository.CreateDefault(services).GetGuildConfig(guildId.Value);
if (guildConfig.MutedRoles.Length == 0)
{
throw new GuildWithoutMutedRoleException(context.Guild.Id);
throw new GuildWithoutMutedRoleException(guildId.Value);
}
}
catch (ResourceNotFoundException)
{
throw new UnregisteredGuildException(context.Guild.Id);
}
continue;
case RequireCheckEnum.SiteAdmin:
if (!identity.IsSiteAdmin())
{
throw new UnauthorizedException("Only site admins allowed.");
throw new UnregisteredGuildException(guildId.Value);
}
continue;
case RequireCheckEnum.GuildStrictModeMute:
await RequireStrictModeAccess(PunishmentType.Mute, services, context, identity);
await RequireStrictModeAccess(PunishmentType.Mute, services, guildId.Value, identity);
continue;
case RequireCheckEnum.GuildStrictModeKick:
await RequireStrictModeAccess(PunishmentType.Kick, services, context, identity);
await RequireStrictModeAccess(PunishmentType.Kick, services, guildId.Value, identity);
continue;
case RequireCheckEnum.GuildStrictModeBan:
await RequireStrictModeAccess(PunishmentType.Ban, services, context, identity);
await RequireStrictModeAccess(PunishmentType.Ban, services, guildId.Value, identity);
continue;
}
}
return PreconditionResult.FromSuccess();
}

private static async Task RequireRegisteredGuild(IServiceProvider services, IInteractionContext context)
private static async Task RequireRegisteredGuild(IServiceProvider services, ulong guildId)
{
try
{
await GuildConfigRepository.CreateDefault(services).GetGuildConfig(context.Guild.Id);
await GuildConfigRepository.CreateDefault(services).GetGuildConfig(guildId);
}
catch (ResourceNotFoundException)
{
throw new UnregisteredGuildException(context.Guild.Id);
throw new UnregisteredGuildException(guildId);
}
catch (NullReferenceException)
{
throw new BaseAPIException("Only usable in a guild.", APIError.OnlyUsableInAGuild);
}
}

private static async Task RequireDiscordPermission(DiscordPermission permission, IServiceProvider services, IInteractionContext context, Identity identity)
private static async Task RequireDiscordPermission(DiscordPermission permission, IServiceProvider services, ulong guildId, Identity identity)
{
await RequireRegisteredGuild(services, context);
await RequireRegisteredGuild(services, guildId);

if (identity.IsSiteAdmin())
{
Expand All @@ -98,28 +110,28 @@ private static async Task RequireDiscordPermission(DiscordPermission permission,
switch (permission)
{
case DiscordPermission.Member:
if (identity.IsOnGuild(context.Guild.Id)) return;
if (identity.IsOnGuild(guildId)) return;
break;
case DiscordPermission.Moderator:
if (await identity.HasModRoleOrHigherOnGuild(context.Guild.Id)) return;
if (await identity.HasModRoleOrHigherOnGuild(guildId)) return;
break;
case DiscordPermission.Admin:
if (await identity.HasAdminRoleOnGuild(context.Guild.Id)) return;
if (await identity.HasAdminRoleOnGuild(guildId)) return;
break;
}
throw new UnauthorizedException("You are not allowed to do that.");
}

private static async Task RequireStrictModeAccess(PunishmentType punishmentType, IServiceProvider services, IInteractionContext context, Identity identity)
private static async Task RequireStrictModeAccess(PunishmentType punishmentType, IServiceProvider services, ulong guildId, Identity identity)
{
await RequireRegisteredGuild(services, context);
await RequireRegisteredGuild(services, guildId);

if (identity.IsSiteAdmin())
{
return;
}

if (await identity.HasPermissionToExecutePunishment(context.Guild.Id, punishmentType))
if (await identity.HasPermissionToExecutePunishment(guildId, punishmentType))
return;

throw new UnauthorizedException("You are not allowed to do that.");
Expand Down
19 changes: 19 additions & 0 deletions backend/MASZ/Commands/BanModal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Discord;
using Discord.Interactions;
using MASZ.Attributes;
using MASZ.Enums;

namespace MASZ.Commands
{

public class BanModal : BaseCommand<BanModal>
{
[Require(RequireCheckEnum.GuildModerator)]
[UserCommand("ban")]
public async Task UserBan([Summary("user", "user to ban")] IUser user)
{
Modal modal = Models.PunishModal.Create(PunishmentType.Ban, $"Ban {user.Username}", user.Id, true);
await Context.Interaction.RespondWithModalAsync(modal);
}
}
}
36 changes: 36 additions & 0 deletions backend/MASZ/Commands/BaseModalInteraction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using MASZ.Enums;
using Discord.Interactions;
using Discord.WebSocket;
using MASZ.Models;
using MASZ.Services;
using Discord;

namespace MASZ.Commands;

public abstract class BaseModalInteraction<T>
{
public Identity CurrentIdentity;
public Translator Translator { get; set; }
public IdentityManager IdentityManager { get; set; }
public InternalConfiguration Config { get; set; }
public IServiceProvider ServiceProvider { get; set; }
private IUser User { get; set; }

public BaseModalInteraction(IServiceProvider serviceProvider, IUser user)
{
ServiceProvider = serviceProvider;
Translator = serviceProvider.GetService<Translator>();
IdentityManager = serviceProvider.GetService<IdentityManager>();
Config = serviceProvider.GetService<InternalConfiguration>();
User = user;
}

public async Task BeforeExecute()
{
CurrentIdentity = await IdentityManager.GetIdentity(User);
}

public abstract RequireCheckEnum[] _checks { get; }
public abstract Task<PreconditionResult> CheckRequirementsAsync(SocketModal modal, IServiceProvider services);
public abstract Task HandleModal(SocketModal modal);
}
19 changes: 19 additions & 0 deletions backend/MASZ/Commands/KickModal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Discord;
using Discord.Interactions;
using MASZ.Attributes;
using MASZ.Enums;

namespace MASZ.Commands
{

public class KickModal : BaseCommand<KickModal>
{
[Require(RequireCheckEnum.GuildModerator)]
[UserCommand("kick")]
public async Task UserKick([Summary("user", "user to kick")] IUser user)
{
Modal modal = Models.PunishModal.Create(PunishmentType.Kick, $"Kick {user.Username}", user.Id, false);
await Context.Interaction.RespondWithModalAsync(modal);
}
}
}
19 changes: 19 additions & 0 deletions backend/MASZ/Commands/MuteModal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Discord;
using Discord.Interactions;
using MASZ.Attributes;
using MASZ.Enums;

namespace MASZ.Commands
{

public class MuteModal : BaseCommand<MuteModal>
{
[Require(RequireCheckEnum.GuildModerator)]
[UserCommand("mute")]
public async Task UserMute([Summary("user", "user to mute")] IUser user)
{
Modal modal = Models.PunishModal.Create(PunishmentType.Mute, $"Mute {user.Username}", user.Id, true);
await Context.Interaction.RespondWithModalAsync(modal);
}
}
}
100 changes: 100 additions & 0 deletions backend/MASZ/Commands/PunishModal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using MASZ.Attributes;
using MASZ.Enums;
using MASZ.Models;
using MASZ.Repositories;
using MASZ.Utils;

namespace MASZ.Commands
{

public class PunishModal : BaseModalInteraction<PunishModal>
{
public PunishModal(IServiceProvider serviceProvider, IUser user) : base(serviceProvider, user)
{
}

public override RequireCheckEnum[] _checks => new RequireCheckEnum[] { RequireCheckEnum.GuildModerator };

public override async Task<PreconditionResult> CheckRequirementsAsync(SocketModal modal, IServiceProvider services)
=> await new RequireAttribute(_checks).CheckRequirementsAsync(modal, services);


public override async Task HandleModal(SocketModal modal)
{
if (!modal.GuildId.HasValue) {
await modal.RespondAsync(Translator.T().CmdViewInvalidGuildId());
return;
}

await modal.DeferAsync(ephemeral: true);

string punishmentTypeRaw = modal.Data.CustomId.Split(":")[1];
PunishmentType punishmentType;
switch (punishmentTypeRaw)
{
case "warn":
punishmentType = PunishmentType.Warn;
break;
case "mute":
punishmentType = PunishmentType.Mute;
break;
case "kick":
punishmentType = PunishmentType.Kick;
break;
case "ban":
punishmentType = PunishmentType.Ban;
break;
default:
punishmentType = PunishmentType.Warn;
break;
}

ModCase modCase = new()
{
Title = modal.Data.Components.First(x => x.CustomId == PunishModalKey.Reason).Value,
GuildId = modal.GuildId.Value,
UserId = ulong.Parse(modal.Data.CustomId.Split(":")[2]),
ModId = CurrentIdentity.GetCurrentUser().Id
};
if (string.IsNullOrEmpty(modal.Data.Components.First(x => x.CustomId == PunishModalKey.Description).Value))
{
modCase.Description = modCase.Title;
}
else
{
modCase.Description = modal.Data.Components.First(x => x.CustomId == PunishModalKey.Description).Value;
}

var rawDuration = modal.Data.Components.FirstOrDefault(x => x.CustomId == PunishModalKey.Duration);
if (rawDuration != null && !string.IsNullOrEmpty(rawDuration.Value))
{
TimeSpan duration = StringTimeSpan.ParseDateRange(rawDuration.Value);
modCase.PunishedUntil = DateTime.UtcNow.Add(duration);
} else {
modCase.PunishedUntil = null;
}

modCase.PunishmentType = punishmentType;
modCase.PunishmentActive = true;
modCase.CreationType = CaseCreationType.ByCommand;

ModCase created = await ModCaseRepository
.CreateDefault(ServiceProvider, CurrentIdentity)
.CreateModCase(
modCase,
true,
modal.Data.Components.First(x => x.CustomId == PunishModalKey.PublicNotification).Value == "true",
modal.Data.Components.First(x => x.CustomId == PunishModalKey.DmNotification).Value == "true"
);

string url = $"{Config.GetBaseUrl()}/guilds/{created.GuildId}/cases/{created.CaseId}";
await modal.ModifyOriginalResponseAsync((MessageProperties msg) =>
{
msg.Content = Translator.T().CmdPunish(created.CaseId, url);
}); ;
}
}
}
4 changes: 2 additions & 2 deletions backend/MASZ/Commands/UnmuteCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public async Task Unmute([Summary("user", "User to unmute")] IUser user)
embed.AddField(Translator.T().CmdUndoResultTitle(), Translator.T().CmdUndoResultWaiting());

var button = new ComponentBuilder()
.WithButton(Translator.T().CmdUndoUnmuteButtonsDelete(), $"unmute-delete:{user.Id}", ButtonStyle.Primary)
.WithButton(Translator.T().CmdUndoUnmuteButtonsDeactivate(), $"unmute-deactivate:{user.Id}", ButtonStyle.Secondary)
.WithButton(Translator.T().CmdUndoUnmuteButtonsDeactivate(), $"unmute-deactivate:{user.Id}", ButtonStyle.Primary)
.WithButton(Translator.T().CmdUndoUnmuteButtonsDelete(), $"unmute-delete:{user.Id}", ButtonStyle.Secondary)
.WithButton(Translator.T().CmdUndoButtonsCancel(), "unmute-cancel", ButtonStyle.Danger);

await Context.Interaction.RespondAsync(embed: embed.Build(), components: button.Build());
Expand Down
19 changes: 19 additions & 0 deletions backend/MASZ/Commands/WarnModal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Discord;
using Discord.Interactions;
using MASZ.Attributes;
using MASZ.Enums;

namespace MASZ.Commands
{

public class WarnModal : BaseCommand<WarnModal>
{
[Require(RequireCheckEnum.GuildModerator)]
[UserCommand("warn")]
public async Task UserWarn([Summary("user", "user to warn")] IUser user)
{
Modal modal = Models.PunishModal.Create(PunishmentType.Warn, $"Warn {user.Username}", user.Id, false);
await Context.Interaction.RespondWithModalAsync(modal);
}
}
}
Loading
Loading