Skip to content

Commit 5b1daf0

Browse files
authored
Improve text around kick/ban/timeout reasons, voting resolutions (#241)
1 parent 374f8d0 commit 5b1daf0

File tree

6 files changed

+28
-26
lines changed

6 files changed

+28
-26
lines changed

app/commands/escalate/handlers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export const EscalationHandlers = {
102102
const reportedMember =
103103
await interaction.guild!.members.fetch(reportedUserId);
104104
await Promise.allSettled([
105-
kick(reportedMember),
105+
kick(reportedMember, "single moderator decision"),
106106
interaction.reply(
107107
`<@${reportedUserId}> kicked by ${interaction.user.username}`,
108108
),
@@ -136,7 +136,7 @@ export const EscalationHandlers = {
136136
const reportedMember =
137137
await interaction.guild!.members.fetch(reportedUserId);
138138
await Promise.allSettled([
139-
ban(reportedMember),
139+
ban(reportedMember, "single moderator decision"),
140140
interaction.reply(
141141
`<@${reportedUserId}> banned by ${interaction.user.username}`,
142142
),
@@ -204,7 +204,7 @@ export const EscalationHandlers = {
204204
const reportedMember =
205205
await interaction.guild!.members.fetch(reportedUserId);
206206
await Promise.allSettled([
207-
timeout(reportedMember),
207+
timeout(reportedMember, "single moderator decision"),
208208
interaction.reply(
209209
`<@${reportedUserId}> timed out by ${interaction.user.username}`,
210210
),

app/commands/escalate/strings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function buildVoteMessageContent(
4949
// Majority voting: always wait for timeout, plurality wins
5050
if (tally.totalVotes === 0) {
5151
status = scheduledFor
52-
? `Majority voting. Resolves <t:${scheduledFor}:R> with leading option.`
52+
? `Majority voting. Resolves <t:${scheduledFor}:R> with a simple majority of participants.`
5353
: `Majority voting. Waiting for votes.`;
5454
} else if (tally.isTied) {
5555
status = `Tied between: ${tally.tiedResolutions.map((r) => humanReadableResolutions[r]).join(", ")}. Tiebreak needed before timeout.`;

app/discord/escalationResolver.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,19 @@ export async function executeResolution(
7575
// }
7676

7777
case resolutions.timeout:
78-
await timeout(reportedMember);
78+
await timeout(reportedMember, "voted resolution");
7979
break;
8080

8181
case resolutions.restrict:
8282
await applyRestriction(reportedMember);
8383
break;
8484

8585
case resolutions.kick:
86-
await kick(reportedMember);
86+
await kick(reportedMember, "voted resolution");
8787
break;
8888

8989
case resolutions.ban:
90-
await ban(reportedMember);
90+
await ban(reportedMember, "voted resolution");
9191
break;
9292
}
9393
} catch (error) {
@@ -147,11 +147,13 @@ async function executeScheduledResolution(
147147
const { modLog } = await fetchSettings(escalation.guild_id, [
148148
SETTINGS.modLog,
149149
]);
150-
const [guild, channel, reportedUser] = await Promise.all([
150+
const [guild, channel, reportedUser, votes] = await Promise.all([
151151
client.guilds.fetch(escalation.guild_id),
152152
client.channels.fetch(escalation.thread_id) as Promise<ThreadChannel>,
153153
client.users.fetch(escalation.reported_user_id).catch(() => null),
154+
getVotesForEscalation(escalation.id),
154155
]);
156+
const voters = new Set(votes.map((v) => v.voter_id));
155157
const [reportedMember, vote] = await Promise.all([
156158
guild.members.fetch(escalation.reported_user_id).catch(() => null),
157159
channel.messages.fetch(escalation.vote_message_id),
@@ -162,13 +164,16 @@ async function executeScheduledResolution(
162164
Number(new Date(escalation.created_at)) / 1000,
163165
);
164166
const elapsedHours = Math.floor((now - createdAt) / 60 / 60);
167+
const totalVotes = votes.length;
168+
const totalVoters = voters.size;
169+
170+
const noticeText = `Resolved with ${totalVotes} votes from ${totalVoters} voters: **${humanReadableResolutions[resolution]}** <@${escalation.reported_user_id}> (${reportedUser?.displayName ?? "no user"})`;
171+
const timing = `-# Resolved <t:${now}:s>, ${elapsedHours}hrs after escalation`;
165172

166173
// Handle case where user left the server or deleted their account
167174
if (!reportedMember) {
168175
const userLeft = reportedUser !== null;
169-
const reason = userLeft
170-
? "User left the server"
171-
: "User account no longer exists";
176+
const reason = userLeft ? "left the server" : "account no longer exists";
172177

173178
log("info", "EscalationResolver", "Resolving escalation - user gone", {
174179
...logBag,
@@ -180,10 +185,8 @@ async function executeScheduledResolution(
180185
await resolveEscalation(escalation.id, resolutions.track);
181186
await vote.edit({ components: getDisabledButtons(vote) });
182187
try {
183-
const displayName = reportedUser?.username ?? "Unknown User";
184188
const notice = await vote.reply({
185-
content: `Resolved: **${humanReadableResolutions[resolutions.track]}** <@${escalation.reported_user_id}> (${displayName})
186-
-# ${reason}. Resolved <t:${now}:s>, ${elapsedHours}hrs after escalation`,
189+
content: `${noticeText}\n${timing} (${reason})`,
187190
});
188191
await notice.forward(modLog);
189192
} catch (error) {
@@ -203,8 +206,7 @@ async function executeScheduledResolution(
203206

204207
try {
205208
const notice = await vote.reply({
206-
content: `Resolved: **${humanReadableResolutions[resolution]}** <@${escalation.reported_user_id}> (${reportedMember.displayName})
207-
-# Resolved <t:${now}:s>, ${elapsedHours}hrs after escalation`,
209+
content: `${noticeText}\n${timing}`,
208210
});
209211
await notice.forward(modLog);
210212
} catch (error) {

app/helpers/modLog.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,9 @@ const constructLog = async ({
338338

339339
// Add indicator if this is forwarded content
340340
const forwardNote = isForwardedMessage(message) ? " (forwarded)" : "";
341-
const preface = `${report}${forwardNote} ${constructDiscordLink(message)} by <@${lastReport.message.author.id}> (${
341+
const preface = `${constructDiscordLink(message)} by <@${lastReport.message.author.id}> (${
342342
lastReport.message.author.username
343-
})`;
343+
})${forwardNote}`;
344344
const extra = origExtra ? `${origExtra}\n` : "";
345345

346346
// For forwarded messages, get attachments from the snapshot
@@ -353,7 +353,7 @@ const constructLog = async ({
353353
);
354354
return {
355355
content: truncateMessage(`${preface}
356-
-# ${extra}${formatDistanceToNowStrict(lastReport.message.createdAt)} ago · <t:${Math.floor(lastReport.message.createdTimestamp / 1000)}:R>`).trim(),
356+
-# ${extra}${formatDistanceToNowStrict(lastReport.message.createdAt)} ago · <t:${Math.floor(lastReport.message.createdTimestamp / 1000)}:R> · ${report}`).trim(),
357357
embeds: embeds.length === 0 ? undefined : embeds,
358358
allowedMentions: { roles: [moderator] },
359359
};

app/helpers/modResponse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const resolutions = {
77
ban: "ban",
88
} as const;
99
export const humanReadableResolutions = {
10-
[resolutions.track]: "No action",
10+
[resolutions.track]: "No action (abstain)",
1111
// [resolutions.warning]: "Formal Warning",
1212
[resolutions.timeout]: "Timeout Overnight",
1313
[resolutions.restrict]: "Restrict",

app/models/discord.server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,29 @@ export const applyRestriction = async (member: GuildMember | null) => {
7777
return member.roles.add(restrictedRole);
7878
};
7979

80-
export const kick = async (member: GuildMember | null) => {
80+
export const kick = async (member: GuildMember | null, reason: string) => {
8181
if (!member) {
8282
console.log("Tried to kick a null member");
8383
return;
8484
}
85-
return member.guild.members.kick(member, "Voted resolution");
85+
return member.guild.members.kick(member, reason);
8686
};
8787

88-
export const ban = async (member: GuildMember | null) => {
88+
export const ban = async (member: GuildMember | null, reason: string) => {
8989
if (!member) {
9090
console.log("Tried to ban a null member");
9191
return;
9292
}
93-
return member.guild.bans.create(member, { reason: "Voted resolution" });
93+
return member.guild.bans.create(member, { reason });
9494
};
9595

9696
const OVERNIGHT = 1000 * 60 * 60 * 20;
97-
export const timeout = async (member: GuildMember | null) => {
97+
export const timeout = async (member: GuildMember | null, reason: string) => {
9898
if (!member) {
9999
console.log("Tried to timeout a null member");
100100
return;
101101
}
102-
return member.timeout(OVERNIGHT, "Voted resolution");
102+
return member.timeout(OVERNIGHT, reason);
103103
};
104104

105105
const authzRoles = {

0 commit comments

Comments
 (0)