Skip to content

Commit

Permalink
Add !tqsmute and /tqsmute (#189)
Browse files Browse the repository at this point in the history
* Add !tqsmute and /tqsmute

* Remove unnecessary using statement

* Add tqsmute configs to config.json

* Correct tqsmute role ID in config

* Make /tqsmute reason argument optional to match !tqsmute

* Disallow TQS-mutes if the user is already muted

* Resolve review comments

Co-authored-by: Erisa A <erisa@erisa.uk>

* Properly reapply TQS mutes on rejoin

---------

Co-authored-by: Erisa A <erisa@erisa.uk>
  • Loading branch information
FloatingMilkshake and Erisa authored Apr 27, 2024
1 parent 0b3090c commit 6b771dc
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 33 deletions.
3 changes: 3 additions & 0 deletions CommandChecks/HomeServerPerms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum ServerPermLevel
Tier8,
TierS,
TierX,
TechnicalQueriesSlayer,
TrialModerator,
Moderator,
Admin,
Expand All @@ -38,6 +39,8 @@ public static ServerPermLevel GetPermLevel(DiscordMember target)
return ServerPermLevel.Muted;
else if (target.Roles.Contains(target.Guild.GetRole(Program.cfgjson.TrialModRole)))
return ServerPermLevel.TrialModerator;
else if (target.Roles.Contains(target.Guild.GetRole(Program.cfgjson.TqsRoleId)))
return ServerPermLevel.TechnicalQueriesSlayer;
else if (target.Roles.Contains(target.Guild.GetRole(Program.cfgjson.TierRoles[9])))
return ServerPermLevel.TierX;
else if (target.Roles.Contains(target.Guild.GetRole(Program.cfgjson.TierRoles[8])))
Expand Down
55 changes: 55 additions & 0 deletions Commands/InteractionCommands/MuteInteractions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,60 @@ public async Task UnmuteSlashCommand(
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} That user doesn't appear to be muted, *and* an error occurred while attempting to unmute them anyway. Please contact the bot owner, the error has been logged.");
}
}

[SlashCommand("tqsmute", "Temporarily mute a user in tech support channels.")]
[SlashRequireHomeserverPerm(ServerPermLevel.TechnicalQueriesSlayer)]
public async Task TqsMuteSlashCommand(
InteractionContext ctx,
[Option("user", "The user to mute.")] DiscordUser targetUser,
[Option("reason", "The reason for the mute.")] string reason)
{
await ctx.DeferAsync(ephemeral: true);

// Only allow usage in #tech-support, #tech-support-forum, and their threads
if (ctx.Channel.Id != Program.cfgjson.TechSupportChannel &&
ctx.Channel.Id != Program.cfgjson.SupportForumId &&
ctx.Channel.Parent.Id != Program.cfgjson.TechSupportChannel &&
ctx.Channel.Parent.Id != Program.cfgjson.SupportForumId)
{
await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{Program.cfgjson.Emoji.Error} This command can only be used in <#{Program.cfgjson.TechSupportChannel}>, <#{Program.cfgjson.SupportForumId}>, and threads in those channels!"));
return;
}

// Check if the user is already muted; disallow TQS-mute if so

DiscordRole mutedRole = ctx.Guild.GetRole(Program.cfgjson.MutedRole);
DiscordRole tqsMutedRole = ctx.Guild.GetRole(Program.cfgjson.TqsMutedRole);

if (await Program.db.HashExistsAsync("mutes", targetUser.Id) || ctx.Member.Roles.Contains(mutedRole) || ctx.Member.Roles.Contains(tqsMutedRole))
{
await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{Program.cfgjson.Emoji.Error} {ctx.User.Mention}, that user is already muted."));
return;
}

// Get member
DiscordMember targetMember = default;
try
{
targetMember = await ctx.Guild.GetMemberAsync(targetUser.Id);
}
catch (DSharpPlus.Exceptions.NotFoundException)
{
// blah
}

// Check if user to be muted is staff or TQS, and disallow if so
if (targetMember != default && GetPermLevel(ctx.Member) == ServerPermLevel.TechnicalQueriesSlayer && (GetPermLevel(targetMember) >= ServerPermLevel.TechnicalQueriesSlayer || targetMember.IsBot))
{
await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{Program.cfgjson.Emoji.Error} {ctx.User.Mention}, you cannot mute other TQS or staff members."));
return;
}

// mute duration is static for TQS mutes
TimeSpan muteDuration = TimeSpan.FromHours(Program.cfgjson.TqsMuteDurationHours);

await MuteHelpers.MuteUserAsync(targetUser, reason, ctx.User.Id, ctx.Guild, ctx.Channel, muteDuration, true, true);
await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done. Please open a modmail thread for this user if you haven't already!"));
}
}
}
58 changes: 57 additions & 1 deletion Commands/Mutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public async Task UnmuteCmd(CommandContext ctx, [Description("The user you're tr

// todo: store per-guild
DiscordRole mutedRole = ctx.Guild.GetRole(Program.cfgjson.MutedRole);
DiscordRole tqsMutedRole = ctx.Guild.GetRole(Program.cfgjson.TqsMutedRole);

DiscordMember member = default;
try
Expand All @@ -23,7 +24,7 @@ public async Task UnmuteCmd(CommandContext ctx, [Description("The user you're tr
Program.discord.Logger.LogWarning(eventId: Program.CliptokEventID, exception: ex, message: "Failed to unmute {user} in {server} because they weren't in the server.", $"{DiscordHelpers.UniqueUsername(targetUser)}", ctx.Guild.Name);
}

if ((await Program.db.HashExistsAsync("mutes", targetUser.Id)) || (member != default && member.Roles.Contains(mutedRole)))
if ((await Program.db.HashExistsAsync("mutes", targetUser.Id)) || (member != default && (member.Roles.Contains(mutedRole) || member.Roles.Contains(tqsMutedRole))))
{
await MuteHelpers.UnmuteUserAsync(targetUser, reason);
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Information} Successfully unmuted **{DiscordHelpers.UniqueUsername(targetUser)}**.");
Expand Down Expand Up @@ -93,5 +94,60 @@ public async Task MuteCmd(

_ = MuteHelpers.MuteUserAsync(targetUser, reason, ctx.User.Id, ctx.Guild, ctx.Channel, muteDuration, true);
}

[Command("tqsmute")]
[Description(
"Temporarily mutes a user, preventing them from sending messages in #tech-support and related channels until they're unmuted.")]
[HomeServer, RequireHomeserverPerm(ServerPermLevel.TechnicalQueriesSlayer)]
public async Task TqsMuteCmd(
CommandContext ctx, [Description("The user to mute")] DiscordUser targetUser,
[RemainingText, Description("The reason for the mute")] string reason = "No reason specified.")
{
// Only allow usage in #tech-support, #tech-support-forum, and their threads
if (ctx.Channel.Id != Program.cfgjson.TechSupportChannel &&
ctx.Channel.Id != Program.cfgjson.SupportForumId &&
ctx.Channel.Parent.Id != Program.cfgjson.TechSupportChannel &&
ctx.Channel.Parent.Id != Program.cfgjson.SupportForumId)
{
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} This command can only be used in <#{Program.cfgjson.TechSupportChannel}>, <#{Program.cfgjson.SupportForumId}>, and threads in those channels!");
return;
}

// Check if the user is already muted; disallow TQS-mute if so

DiscordRole mutedRole = ctx.Guild.GetRole(Program.cfgjson.MutedRole);
DiscordRole tqsMutedRole = ctx.Guild.GetRole(Program.cfgjson.TqsMutedRole);

if ((await Program.db.HashExistsAsync("mutes", targetUser.Id)) || (ctx.Member != default && (ctx.Member.Roles.Contains(mutedRole) || ctx.Member.Roles.Contains(tqsMutedRole))))
{
await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} {ctx.User.Mention}, that user is already muted.");
return;
}

// Get member
DiscordMember targetMember = default;
try
{
targetMember = await ctx.Guild.GetMemberAsync(targetUser.Id);
}
catch (DSharpPlus.Exceptions.NotFoundException)
{
// blah
}

// Check if user to be muted is staff or TQS, and disallow if so
if (targetMember != default && GetPermLevel(ctx.Member) == ServerPermLevel.TechnicalQueriesSlayer && (GetPermLevel(targetMember) >= ServerPermLevel.TechnicalQueriesSlayer || targetMember.IsBot))
{
await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.Error} {ctx.User.Mention}, you cannot mute other TQS or staff members.");
return;
}

await ctx.Message.DeleteAsync();

// mute duration is static for TQS mutes
TimeSpan muteDuration = TimeSpan.FromHours(Program.cfgjson.TqsMuteDurationHours);

MuteHelpers.MuteUserAsync(targetUser, reason, ctx.User.Id, ctx.Guild, ctx.Channel, muteDuration, true, true);
}
}
}
7 changes: 4 additions & 3 deletions Events/MemberEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,13 @@ public static async Task GuildMemberRemoved(DiscordClient client, GuildMemberRem
return;

var muteRole = e.Guild.GetRole(cfgjson.MutedRole);
var tqsMuteRole = e.Guild.GetRole(cfgjson.TqsMutedRole);
var userMute = await db.HashGetAsync("mutes", e.Member.Id);

if (!userMute.IsNull && !e.Member.Roles.Contains(muteRole))
if (!userMute.IsNull && !e.Member.Roles.Contains(muteRole) & !e.Member.Roles.Contains(tqsMuteRole))
db.HashDeleteAsync("mutes", e.Member.Id);

if (e.Member.Roles.Contains(muteRole) && userMute.IsNull)
if ((e.Member.Roles.Contains(muteRole) || e.Member.Roles.Contains(tqsMuteRole)) && userMute.IsNull)
{
MemberPunishment newMute = new()
{
Expand All @@ -146,7 +147,7 @@ public static async Task GuildMemberRemoved(DiscordClient client, GuildMemberRem
db.HashSetAsync("mutes", e.Member.Id, JsonConvert.SerializeObject(newMute));
}

if (!userMute.IsNull && !e.Member.Roles.Contains(muteRole))
if (!userMute.IsNull && !e.Member.Roles.Contains(muteRole) && !e.Member.Roles.Contains(tqsMuteRole))
db.HashDeleteAsync("mutes", e.Member.Id);

string rolesStr = "None";
Expand Down
Loading

0 comments on commit 6b771dc

Please sign in to comment.