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

Revert to simpler duration formats and fix broken delete time frames #927

Merged
merged 2 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions src/discord/commands/guild/d.moderate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { stripIndents } from 'common-tags';
import { user_action_type, user_actions, users } from '@prisma/client';
import moment from 'moment';
import { SlashCommand } from '../../@types/commandDef';
import { parseDuration, validateDurationInput } from '../../../global/utils/parseDuration';
import { makeValid, parseDuration, validateDurationInput } from '../../../global/utils/parseDuration';
import commandContext from '../../utils/context'; // eslint-disable-line
import { getDiscordMember } from '../../utils/guildMemberLookup';
import { embedTemplate } from '../../utils/embedTemplate';
Expand Down Expand Up @@ -1393,11 +1393,16 @@ export async function moderate(
if (isTimeout(command)) {
// log.debug(F, 'Parsing timeout duration');
let durationVal = modalInt.fields.getTextInputValue('duration');
if (durationVal === '') durationVal = '7d';

if (durationVal === '') {
durationVal = '7d';
} else {
durationVal = await makeValid(durationVal);
}

if (!validateDurationInput(durationVal)) {
return {
content: 'Timeout duration must include at least one of seconds, minutes, hours, days, or a week. For example: 5d 5h 5m 5s, 1w or 5d.',
content: 'Timeout duration must include at least one of the following units: seconds, minutes, hours, days, or weeks. Examples of valid formats include: 1 days 23 hours 59 minutes 59 seconds, or just 1 day, etc.',
};
}

Expand All @@ -1413,9 +1418,10 @@ export async function moderate(
// log.debug(F, `duration: ${duration}`);
}
if (isFullBan(command)) {
const durationVal = modalInt.fields.getTextInputValue('ban_duration');
let durationVal = modalInt.fields.getTextInputValue('ban_duration');

if (durationVal !== '') {
durationVal = await makeValid(durationVal);
let tempBanDuration = parseInt(durationVal, 10);

if (Number.isNaN(tempBanDuration)) {
Expand All @@ -1424,7 +1430,7 @@ export async function moderate(

if (!validateDurationInput(durationVal)) {
return {
content: 'Ban duration must include at least one of seconds, minutes, hours, days, weeks, months, or years. For example: 1yr 1M 1w 1d 1h 1m 1s',
content: 'Ban duration must include at least one of the following units: seconds, minutes, hours, days, weeks, months, or years. Examples of valid formats include: 1 year 1 month 1 week 1 day 23 hours 59 minutes 59 seconds, or just 1 year, 1 month, etc.',
};
}

Expand Down Expand Up @@ -1561,9 +1567,31 @@ export async function moderate(
if (isBan(command)) {
if (isFullBan(command) || isUnderban(command) || isBanEvasion(command)) {
targetData.removed_at = new Date();
const deleteMessageValue = actionDuration ?? 0;

let deleteMessageValue = modalInt.fields.getTextInputValue('days');
let deleteDuration = 0;

if (deleteMessageValue !== '') {
deleteMessageValue = await makeValid(deleteMessageValue);
deleteDuration = parseInt(deleteMessageValue, 10);

if (Number.isNaN(deleteDuration)) {
return { content: 'Delete duration must be a number!' };
}

if (!validateDurationInput(deleteMessageValue)) {
return {
content: 'Delete duration must include at least one of the following units: seconds, minutes, hours, days, or weeks, with a maximum duration of 7 days. Examples of valid formats include: 1 day 23 hours 59 minutes 59 seconds, or just 1 day, etc.',
};
}

deleteDuration = await parseDuration(deleteMessageValue);
if (deleteDuration && deleteDuration < 0) {
return { content: 'Delete duration must be at least 1 second!' };
}
}
try {
if (deleteMessageValue > 0 && targetMember) {
if (deleteDuration > 0 && targetMember) {
// log.debug(F, `I am deleting ${deleteMessageValue} days of messages!`);
const response = await last(targetMember.user, buttonInt.guild);
extraMessage = `${targetName}'s last ${response.messageCount} (out of ${response.totalMessages}) messages before being banned :\n${response.messageList}`;
Expand All @@ -1575,7 +1603,8 @@ export async function moderate(
log.info(F, `target: ${targetId} | deleteMessageValue: ${deleteMessageValue} | internalNote: ${internalNote ?? noReason}`);

try {
targetObj = await buttonInt.guild.bans.create(targetId, { deleteMessageSeconds: deleteMessageValue / 1000, reason: internalNote ?? noReason });
log.info(F, `Message delete duration in milliseconds: ${deleteDuration}`);
targetObj = await buttonInt.guild.bans.create(targetId, { deleteMessageSeconds: deleteDuration / 1000, reason: internalNote ?? noReason });
// Set ban duration if present
if (banEndTime !== null) {
actionData.expires_at = banEndTime;
Expand Down Expand Up @@ -1944,7 +1973,7 @@ export async function modModal(
.addComponents(new TextInputBuilder()
.setLabel('Timeout for how long?')
.setStyle(TextInputStyle.Short)
.setPlaceholder('4 days 3hrs 2 mins 30 seconds (Max 7 days, Default 7 days)')
.setPlaceholder('7 days or 1 week, etc. (Max 7 days, Default 7 days)')
.setRequired(false)
.setCustomId('duration')));
}
Expand All @@ -1953,14 +1982,14 @@ export async function modModal(
.addComponents(new TextInputBuilder()
.setLabel('How many days of msg to remove?')
.setStyle(TextInputStyle.Short)
.setPlaceholder('4 days 3hrs 2 mins 30 seconds (Max 7 days, Default 0 days)')
.setPlaceholder('7 days or 1 week, etc. (Max 7 days, Default 0 days)')
.setRequired(false)
.setCustomId('days')));
modal.addComponents(new ActionRowBuilder<TextInputBuilder>()
.addComponents(new TextInputBuilder()
.setLabel('How long should they be banned for?')
.setStyle(TextInputStyle.Short)
.setPlaceholder('365 days (Empty = Permanent)')
.setPlaceholder('1 year or 365 days, etc. (Empty = Permanent)')
.setRequired(false)
.setCustomId('ban_duration')));
}
Expand Down
44 changes: 43 additions & 1 deletion src/global/utils/parseDuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,48 @@ export async function parseDuration(duration:string):Promise<number> {
}

export const validateDurationInput = (input: string): boolean => {
const regex = /^(\d+(yr|M|w|d|h|m|s)\s?)+$/;
const regex = /^(\d+(y|M|w|d|h|m|s)\s?)+$/;
return regex.test(input.trim());
};

export async function makeValid(duration: string): Promise<string> {
// Define a map for the units and their short forms
const unitMap: Record<string, string> = {
years: 'y',
year: 'y',
months: 'M',
month: 'M',
weeks: 'w',
week: 'w',
days: 'd',
day: 'd',
hours: 'h',
hour: 'h',
minutes: 'm',
minute: 'm',
seconds: 's',
second: 's',
};

// Regular expression to match the input format
const regex = /\b(\d+)\s*(years?|months?|weeks?|days?|hours?|minutes?|seconds?)\b/gi;

// Array to store the parsed results
const parts: string[] = [];

// Replace matched parts with their formatted versions
let match;
// eslint-disable-next-line no-cond-assign
while ((match = regex.exec(duration)) !== null) {
const value = match[1]; // The number (e.g., "1")
const unit = match[2].toLowerCase(); // The unit (e.g., "year" or "years")

// Map the unit to its short form and combine with the value
if (unit in unitMap) {
parts.push(`${value}${unitMap[unit]}`);
}
}

// Join the parts with a space to form the final result
return parts.join(' ');
}
Loading