1 |
/** |
2 |
* This file is part of SudoBot. |
3 |
* |
4 |
* Copyright (C) 2021-2023 OSN Developers. |
5 |
* |
6 |
* SudoBot is free software; you can redistribute it and/or modify it |
7 |
* under the terms of the GNU Affero General Public License as published by |
8 |
* the Free Software Foundation, either version 3 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* SudoBot is distributed in the hope that it will be useful, but |
12 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU Affero General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU Affero General Public License |
17 |
* along with SudoBot. If not, see <https://www.gnu.org/licenses/>. |
18 |
*/ |
19 |
|
20 |
import { formatDistanceToNowStrict } from "date-fns"; |
21 |
import { ChatInputCommandInteraction, EmbedBuilder, PermissionsBitField } from "discord.js"; |
22 |
import Command, { CommandReturn, ValidationRule } from "../../core/Command"; |
23 |
import { ChatInputCommandContext } from "../../services/CommandManager"; |
24 |
import { stringToTimeInterval } from "../../utils/datetime"; |
25 |
|
26 |
export default class InfractionEditCommand extends Command { |
27 |
public readonly name = "infraction__edit"; |
28 |
public readonly validationRules: ValidationRule[] = []; |
29 |
public readonly permissions = [PermissionsBitField.Flags.ModerateMembers, PermissionsBitField.Flags.ViewAuditLog]; |
30 |
public readonly permissionMode = "or"; |
31 |
public readonly supportsLegacy: boolean = false; |
32 |
|
33 |
public readonly description = "Edit infractions."; |
34 |
public readonly detailedDescription = "Update an infraction with a new reason or duration or both."; |
35 |
public readonly argumentSyntaxes = ["<infraction_id> [new_reason] [new_duration]"]; |
36 |
|
37 |
async execute(interaction: ChatInputCommandInteraction, context: ChatInputCommandContext): Promise<CommandReturn> { |
38 |
const id = interaction.options.getInteger("id", true); |
39 |
const newReason = interaction.options.getString("new_reason"); |
40 |
const newDuration = interaction.options.getString("new_duration"); |
41 |
|
42 |
if (!newReason && !newDuration) { |
43 |
await interaction.editReply( |
44 |
`${this.emoji("error")} Either provide a reason or duration or both to update this infraction, if it exists!` |
45 |
); |
46 |
return; |
47 |
} |
48 |
|
49 |
const newDurationSeconds = newDuration ? stringToTimeInterval(newDuration) : undefined; |
50 |
const silent = interaction.options.getBoolean("silent") ?? true; |
51 |
|
52 |
if (newDurationSeconds && newDurationSeconds.error) { |
53 |
await interaction.editReply( |
54 |
`${this.emoji("error")} ${newDurationSeconds.error} provided in the \`new_duration\` field` |
55 |
); |
56 |
return; |
57 |
} |
58 |
|
59 |
const infraction = await this.client.prisma.infraction.findFirst({ |
60 |
where: { id, guildId: interaction.guildId! } |
61 |
}); |
62 |
|
63 |
if (!infraction) { |
64 |
await interaction.editReply(`${this.emoji("error")} Could not find an infraction with that ID!`); |
65 |
return; |
66 |
} |
67 |
|
68 |
if (newDurationSeconds?.result && infraction.expiresAt && infraction.expiresAt.getTime() <= Date.now()) { |
69 |
await interaction.editReply(`${this.emoji("error")} That infraction is expired, so you can't change it's duration!`); |
70 |
return; |
71 |
} |
72 |
|
73 |
if (newDurationSeconds?.result && infraction.createdAt.getTime() + newDurationSeconds?.result * 1000 <= Date.now()) { |
74 |
await interaction.editReply( |
75 |
`${this.emoji( |
76 |
"error" |
77 |
)} That duration makes the infraction expire in the past, which is not possible! Please make sure the time of infraction creation plus the new duration is greater than the current time!` |
78 |
); |
79 |
return; |
80 |
} |
81 |
|
82 |
if (newDurationSeconds?.result && infraction.expiresAt === null) { |
83 |
await interaction.editReply( |
84 |
`${this.emoji("error")} This infraction did not have a duration in the first place, so you can't set one now.` |
85 |
); |
86 |
|
87 |
return; |
88 |
} |
89 |
|
90 |
const user = await this.client.fetchUserSafe(infraction.userId); |
91 |
|
92 |
if (newDurationSeconds && infraction.queueId) { |
93 |
const queue = this.client.queueManager.queues.get(`${infraction.queueId}`); |
94 |
|
95 |
if (queue) { |
96 |
await queue.updateTime(new Date(infraction.createdAt.getTime() + newDurationSeconds.result * 1000)); |
97 |
} |
98 |
} |
99 |
|
100 |
if (!silent) { |
101 |
await user?.send({ |
102 |
embeds: [ |
103 |
new EmbedBuilder({ |
104 |
author: { |
105 |
icon_url: interaction.guild?.iconURL() ?? undefined, |
106 |
name: "Your infraction has been updated in " + interaction.guild!.name |
107 |
}, |
108 |
color: 0xf14a60, |
109 |
fields: [ |
110 |
...(newReason |
111 |
? [ |
112 |
{ |
113 |
name: "Reason", |
114 |
value: newReason |
115 |
} |
116 |
] |
117 |
: []), |
118 |
...(newDurationSeconds?.result |
119 |
? [ |
120 |
{ |
121 |
name: "Duration", |
122 |
value: formatDistanceToNowStrict( |
123 |
new Date(infraction.createdAt.getTime() + newDurationSeconds.result * 1000) |
124 |
) |
125 |
} |
126 |
] |
127 |
: []), |
128 |
{ |
129 |
name: "Infraction ID", |
130 |
value: id.toString() |
131 |
} |
132 |
] |
133 |
}).setTimestamp() |
134 |
] |
135 |
}); |
136 |
} |
137 |
|
138 |
await this.client.prisma.infraction.update({ |
139 |
data: { |
140 |
reason: newReason ?? undefined, |
141 |
expiresAt: newDurationSeconds |
142 |
? new Date(infraction.createdAt.getTime() + newDurationSeconds.result * 1000) |
143 |
: undefined |
144 |
}, |
145 |
where: { id } |
146 |
}); |
147 |
|
148 |
await interaction.editReply({ |
149 |
embeds: [ |
150 |
new EmbedBuilder({ |
151 |
title: "Infraction update", |
152 |
description: "Updated successfully!", |
153 |
color: 0x007bff, |
154 |
fields: [ |
155 |
...(newReason |
156 |
? [ |
157 |
{ |
158 |
name: "New Reason", |
159 |
value: newReason |
160 |
} |
161 |
] |
162 |
: []), |
163 |
...(newDurationSeconds?.result |
164 |
? [ |
165 |
{ |
166 |
name: "New Duration", |
167 |
value: formatDistanceToNowStrict( |
168 |
new Date(infraction.createdAt.getTime() + newDurationSeconds.result * 1000) |
169 |
) |
170 |
} |
171 |
] |
172 |
: []), |
173 |
{ |
174 |
name: "ID", |
175 |
value: id.toString() |
176 |
}, |
177 |
{ |
178 |
name: "Notifying User?", |
179 |
value: silent ? "No" : "Yes", |
180 |
inline: true |
181 |
} |
182 |
], |
183 |
footer: { |
184 |
text: "Updated" |
185 |
} |
186 |
}).setTimestamp() |
187 |
] |
188 |
}); |
189 |
} |
190 |
} |