mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-07-07 02:57:20 +00:00
feat: native timestamps
This commit is contained in:
parent
eb5fda8d19
commit
6006504459
7 changed files with 305 additions and 243 deletions
|
@ -2,10 +2,23 @@ import humanizeDuration from "humanize-duration";
|
||||||
import { getMemberLevel } from "knub/helpers";
|
import { getMemberLevel } from "knub/helpers";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
||||||
import { CaseTypes } from "../../../data/CaseTypes.js";
|
import { CaseTypes } from "../../../data/CaseTypes.js";
|
||||||
import { clearExpiringTempban, registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop.js";
|
import {
|
||||||
import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils.js";
|
clearExpiringTempban,
|
||||||
|
registerExpiringTempban,
|
||||||
|
} from "../../../data/loops/expiringTempbansLoop.js";
|
||||||
|
import {
|
||||||
|
canActOn,
|
||||||
|
hasPermission,
|
||||||
|
sendErrorMessage,
|
||||||
|
sendSuccessMessage,
|
||||||
|
} from "../../../pluginUtils.js";
|
||||||
import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin.js";
|
import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin.js";
|
||||||
import { renderUsername, resolveMember, resolveUser } from "../../../utils.js";
|
import {
|
||||||
|
renderUsername,
|
||||||
|
resolveMember,
|
||||||
|
resolveUser,
|
||||||
|
toRelativeNativeTimestamp,
|
||||||
|
} from "../../../utils.js";
|
||||||
import { banLock } from "../../../utils/lockNameHelpers.js";
|
import { banLock } from "../../../utils/lockNameHelpers.js";
|
||||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction.js";
|
import { waitForButtonConfirm } from "../../../utils/waitForInteraction.js";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin.js";
|
import { LogsPlugin } from "../../Logs/LogsPlugin.js";
|
||||||
|
@ -16,204 +29,247 @@ import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromA
|
||||||
import { modActionsCmd } from "../types.js";
|
import { modActionsCmd } from "../types.js";
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
mod: ct.member({ option: true }),
|
mod: ct.member({ option: true }),
|
||||||
notify: ct.string({ option: true }),
|
notify: ct.string({ option: true }),
|
||||||
"notify-channel": ct.textChannel({ option: true }),
|
"notify-channel": ct.textChannel({ option: true }),
|
||||||
"delete-days": ct.number({ option: true, shortcut: "d" }),
|
"delete-days": ct.number({ option: true, shortcut: "d" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BanCmd = modActionsCmd({
|
export const BanCmd = modActionsCmd({
|
||||||
trigger: "ban",
|
trigger: "ban",
|
||||||
permission: "can_ban",
|
permission: "can_ban",
|
||||||
description: "Ban or Tempban the specified member",
|
description: "Ban or Tempban the specified member",
|
||||||
|
|
||||||
signature: [
|
signature: [
|
||||||
{
|
{
|
||||||
user: ct.string(),
|
user: ct.string(),
|
||||||
time: ct.delay(),
|
time: ct.delay(),
|
||||||
reason: ct.string({ required: false, catchAll: true }),
|
reason: ct.string({ required: false, catchAll: true }),
|
||||||
|
|
||||||
...opts,
|
...opts,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user: ct.string(),
|
user: ct.string(),
|
||||||
reason: ct.string({ required: false, catchAll: true }),
|
reason: ct.string({ required: false, catchAll: true }),
|
||||||
|
|
||||||
...opts,
|
...opts,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
async run({ pluginData, message: msg, args }) {
|
async run({ pluginData, message: msg, args }) {
|
||||||
const user = await resolveUser(pluginData.client, args.user);
|
const user = await resolveUser(pluginData.client, args.user);
|
||||||
if (!user.id) {
|
if (!user.id) {
|
||||||
sendErrorMessage(pluginData, msg.channel, `User not found`);
|
sendErrorMessage(pluginData, msg.channel, `User not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const time = args["time"] ? args["time"] : null;
|
const time = args["time"] ? args["time"] : null;
|
||||||
|
|
||||||
const reason = formatReasonWithAttachments(args.reason, [...msg.attachments.values()]);
|
const reason = formatReasonWithAttachments(args.reason, [
|
||||||
const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id);
|
...msg.attachments.values(),
|
||||||
// The moderator who did the action is the message author or, if used, the specified -mod
|
]);
|
||||||
let mod = msg.member;
|
const memberToBan = await resolveMember(
|
||||||
if (args.mod) {
|
pluginData.client,
|
||||||
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg, channelId: msg.channel.id }))) {
|
pluginData.guild,
|
||||||
sendErrorMessage(pluginData, msg.channel, "You don't have permission to use -mod");
|
user.id,
|
||||||
return;
|
);
|
||||||
}
|
// The moderator who did the action is the message author or, if used, the specified -mod
|
||||||
|
let mod = msg.member;
|
||||||
|
if (args.mod) {
|
||||||
|
if (
|
||||||
|
!(await hasPermission(pluginData, "can_act_as_other", {
|
||||||
|
message: msg,
|
||||||
|
channelId: msg.channel.id,
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
sendErrorMessage(
|
||||||
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
"You don't have permission to use -mod",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mod = args.mod;
|
mod = args.mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
// acquire a lock because of the needed user-inputs below (if banned/not on server)
|
// acquire a lock because of the needed user-inputs below (if banned/not on server)
|
||||||
const lock = await pluginData.locks.acquire(banLock(user));
|
const lock = await pluginData.locks.acquire(banLock(user));
|
||||||
let forceban = false;
|
let forceban = false;
|
||||||
const existingTempban = await pluginData.state.tempbans.findExistingTempbanForUserId(user.id);
|
const existingTempban =
|
||||||
if (!memberToBan) {
|
await pluginData.state.tempbans.findExistingTempbanForUserId(user.id);
|
||||||
const banned = await isBanned(pluginData, user.id);
|
if (!memberToBan) {
|
||||||
if (banned) {
|
const banned = await isBanned(pluginData, user.id);
|
||||||
// Abort if trying to ban user indefinitely if they are already banned indefinitely
|
if (banned) {
|
||||||
if (!existingTempban && !time) {
|
// Abort if trying to ban user indefinitely if they are already banned indefinitely
|
||||||
sendErrorMessage(pluginData, msg.channel, `User is already banned indefinitely.`);
|
if (!existingTempban && !time) {
|
||||||
return;
|
sendErrorMessage(
|
||||||
}
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
`User is already banned indefinitely.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Ask the mod if we should update the existing ban
|
// Ask the mod if we should update the existing ban
|
||||||
const reply = await waitForButtonConfirm(
|
const reply = await waitForButtonConfirm(
|
||||||
msg.channel,
|
msg.channel,
|
||||||
{ content: "Failed to message the user. Log the warning anyway?" },
|
{ content: "Failed to message the user. Log the warning anyway?" },
|
||||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||||
);
|
);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
sendErrorMessage(pluginData, msg.channel, "User already banned, update cancelled by moderator");
|
sendErrorMessage(
|
||||||
lock.unlock();
|
pluginData,
|
||||||
return;
|
msg.channel,
|
||||||
} else {
|
"User already banned, update cancelled by moderator",
|
||||||
// Update or add new tempban / remove old tempban
|
);
|
||||||
if (time && time > 0) {
|
lock.unlock();
|
||||||
if (existingTempban) {
|
return;
|
||||||
await pluginData.state.tempbans.updateExpiryTime(user.id, time, mod.id);
|
} else {
|
||||||
} else {
|
// Update or add new tempban / remove old tempban
|
||||||
await pluginData.state.tempbans.addTempban(user.id, time, mod.id);
|
if (time && time > 0) {
|
||||||
}
|
if (existingTempban) {
|
||||||
const tempban = (await pluginData.state.tempbans.findExistingTempbanForUserId(user.id))!;
|
await pluginData.state.tempbans.updateExpiryTime(
|
||||||
registerExpiringTempban(tempban);
|
user.id,
|
||||||
} else if (existingTempban) {
|
time,
|
||||||
clearExpiringTempban(existingTempban);
|
mod.id,
|
||||||
pluginData.state.tempbans.clear(user.id);
|
);
|
||||||
}
|
} else {
|
||||||
|
await pluginData.state.tempbans.addTempban(user.id, time, mod.id);
|
||||||
|
}
|
||||||
|
const tempban =
|
||||||
|
(await pluginData.state.tempbans.findExistingTempbanForUserId(
|
||||||
|
user.id,
|
||||||
|
))!;
|
||||||
|
registerExpiringTempban(tempban);
|
||||||
|
} else if (existingTempban) {
|
||||||
|
clearExpiringTempban(existingTempban);
|
||||||
|
pluginData.state.tempbans.clear(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new case for the updated ban since we never stored the old case id and log the action
|
// Create a new case for the updated ban since we never stored the old case id and log the action
|
||||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||||
const createdCase = await casesPlugin.createCase({
|
const createdCase = await casesPlugin.createCase({
|
||||||
modId: mod.id,
|
modId: mod.id,
|
||||||
type: CaseTypes.Ban,
|
type: CaseTypes.Ban,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
reason,
|
reason,
|
||||||
noteDetails: [`Ban updated to ${time ? humanizeDuration(time) : "indefinite"}`],
|
noteDetails: [
|
||||||
});
|
`Ban updated to ${time ? `expire ${toRelativeNativeTimestamp(time)}` : "indefinite"}`,
|
||||||
if (time) {
|
],
|
||||||
pluginData.getPlugin(LogsPlugin).logMemberTimedBan({
|
});
|
||||||
mod: mod.user,
|
if (time) {
|
||||||
user,
|
pluginData.getPlugin(LogsPlugin).logMemberTimedBan({
|
||||||
caseNumber: createdCase.case_number,
|
mod: mod.user,
|
||||||
reason,
|
user,
|
||||||
banTime: humanizeDuration(time),
|
caseNumber: createdCase.case_number,
|
||||||
});
|
reason,
|
||||||
} else {
|
banTime: humanizeDuration(time),
|
||||||
pluginData.getPlugin(LogsPlugin).logMemberBan({
|
});
|
||||||
mod: mod.user,
|
} else {
|
||||||
user,
|
pluginData.getPlugin(LogsPlugin).logMemberBan({
|
||||||
caseNumber: createdCase.case_number,
|
mod: mod.user,
|
||||||
reason,
|
user,
|
||||||
});
|
caseNumber: createdCase.case_number,
|
||||||
}
|
reason,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sendSuccessMessage(
|
sendSuccessMessage(
|
||||||
pluginData,
|
pluginData,
|
||||||
msg.channel,
|
msg.channel,
|
||||||
`Ban updated to ${time ? "expire in " + humanizeDuration(time) + " from now" : "indefinite"}`,
|
`Ban updated to ${time ? `expire ${toRelativeNativeTimestamp(time)}` : "indefinite"}`,
|
||||||
);
|
);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ask the mod if we should upgrade to a forceban as the user is not on the server
|
// Ask the mod if we should upgrade to a forceban as the user is not on the server
|
||||||
const reply = await waitForButtonConfirm(
|
const reply = await waitForButtonConfirm(
|
||||||
msg.channel,
|
msg.channel,
|
||||||
{ content: "User not on server, forceban instead?" },
|
{ content: "User not on server, forceban instead?" },
|
||||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||||
);
|
);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
sendErrorMessage(pluginData, msg.channel, "User not on server, ban cancelled by moderator");
|
sendErrorMessage(
|
||||||
lock.unlock();
|
pluginData,
|
||||||
return;
|
msg.channel,
|
||||||
} else {
|
"User not on server, ban cancelled by moderator",
|
||||||
forceban = true;
|
);
|
||||||
}
|
lock.unlock();
|
||||||
}
|
return;
|
||||||
}
|
} else {
|
||||||
|
forceban = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we're allowed to ban this member if they are on the server
|
// Make sure we're allowed to ban this member if they are on the server
|
||||||
if (!forceban && !canActOn(pluginData, msg.member, memberToBan!)) {
|
if (!forceban && !canActOn(pluginData, msg.member, memberToBan!)) {
|
||||||
const ourLevel = getMemberLevel(pluginData, msg.member);
|
const ourLevel = getMemberLevel(pluginData, msg.member);
|
||||||
const targetLevel = getMemberLevel(pluginData, memberToBan!);
|
const targetLevel = getMemberLevel(pluginData, memberToBan!);
|
||||||
sendErrorMessage(
|
sendErrorMessage(
|
||||||
pluginData,
|
pluginData,
|
||||||
msg.channel,
|
msg.channel,
|
||||||
`Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`,
|
`Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`,
|
||||||
);
|
);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let contactMethods;
|
let contactMethods;
|
||||||
try {
|
try {
|
||||||
contactMethods = readContactMethodsFromArgs(args);
|
contactMethods = readContactMethodsFromArgs(args);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendErrorMessage(pluginData, msg.channel, e.message);
|
sendErrorMessage(pluginData, msg.channel, e.message);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteMessageDays =
|
const deleteMessageDays =
|
||||||
args["delete-days"] ?? (await pluginData.config.getForMessage(msg)).ban_delete_message_days;
|
args["delete-days"] ??
|
||||||
const banResult = await banUserId(
|
(await pluginData.config.getForMessage(msg)).ban_delete_message_days;
|
||||||
pluginData,
|
const banResult = await banUserId(
|
||||||
user.id,
|
pluginData,
|
||||||
reason,
|
user.id,
|
||||||
{
|
reason,
|
||||||
contactMethods,
|
{
|
||||||
caseArgs: {
|
contactMethods,
|
||||||
modId: mod.id,
|
caseArgs: {
|
||||||
ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
|
modId: mod.id,
|
||||||
},
|
ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
|
||||||
deleteMessageDays,
|
},
|
||||||
modId: mod.id,
|
deleteMessageDays,
|
||||||
},
|
modId: mod.id,
|
||||||
time,
|
},
|
||||||
);
|
time,
|
||||||
|
);
|
||||||
|
|
||||||
if (banResult.status === "failed") {
|
if (banResult.status === "failed") {
|
||||||
sendErrorMessage(pluginData, msg.channel, `Failed to ban member: ${banResult.error}`);
|
sendErrorMessage(
|
||||||
lock.unlock();
|
pluginData,
|
||||||
return;
|
msg.channel,
|
||||||
}
|
`Failed to ban member: ${banResult.error}`,
|
||||||
|
);
|
||||||
|
lock.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let forTime = "";
|
let forTime = "";
|
||||||
if (time && time > 0) {
|
if (time && time > 0) {
|
||||||
forTime = `for ${humanizeDuration(time)} `;
|
forTime = `for ${humanizeDuration(time)} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm the action to the moderator
|
// Confirm the action to the moderator
|
||||||
let response = "";
|
let response = "";
|
||||||
if (!forceban) {
|
if (!forceban) {
|
||||||
response = `Banned **${renderUsername(user)}** ${forTime}(Case #${banResult.case.case_number})`;
|
response = `Banned **${renderUsername(user)}** ${forTime}(Case #${banResult.case.case_number})`;
|
||||||
if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`;
|
if (banResult.notifyResult.text)
|
||||||
} else {
|
response += ` (${banResult.notifyResult.text})`;
|
||||||
response = `Member forcebanned ${forTime}(Case #${banResult.case.case_number})`;
|
} else {
|
||||||
}
|
response = `Member forcebanned ${forTime}(Case #${banResult.case.case_number})`;
|
||||||
|
}
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
sendSuccessMessage(pluginData, msg.channel, response);
|
sendSuccessMessage(pluginData, msg.channel, response);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
notifyUser,
|
notifyUser,
|
||||||
resolveMember,
|
resolveMember,
|
||||||
resolveUser,
|
resolveUser,
|
||||||
|
toRelativeNativeTimestamp,
|
||||||
ucfirst,
|
ucfirst,
|
||||||
} from "../../../utils.js";
|
} from "../../../utils.js";
|
||||||
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects.js";
|
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects.js";
|
||||||
|
@ -172,7 +173,7 @@ export async function banUserId(
|
||||||
user,
|
user,
|
||||||
caseNumber: createdCase.case_number,
|
caseNumber: createdCase.case_number,
|
||||||
reason: reason ?? "",
|
reason: reason ?? "",
|
||||||
banTime: humanizeDuration(banTime),
|
banTime: toRelativeNativeTimestamp(banTime),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pluginData.getPlugin(LogsPlugin).logMemberBan({
|
pluginData.getPlugin(LogsPlugin).logMemberBan({
|
||||||
|
|
|
@ -10,7 +10,7 @@ import moment from "moment-timezone";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
||||||
import { humanizeDurationShort } from "../../../humanizeDurationShort.js";
|
import { humanizeDurationShort } from "../../../humanizeDurationShort.js";
|
||||||
import { getBaseUrl } from "../../../pluginUtils.js";
|
import { getBaseUrl } from "../../../pluginUtils.js";
|
||||||
import { DBDateFormat, MINUTES, renderUsername, resolveMember } from "../../../utils.js";
|
import { DBDateFormat, MINUTES, renderUsername, resolveMember, toRelativeNativeTimestamp } from "../../../utils.js";
|
||||||
import { IMuteWithDetails, mutesCmd } from "../types.js";
|
import { IMuteWithDetails, mutesCmd } from "../types.js";
|
||||||
|
|
||||||
export const MutesCmd = mutesCmd({
|
export const MutesCmd = mutesCmd({
|
||||||
|
@ -131,15 +131,15 @@ export const MutesCmd = mutesCmd({
|
||||||
|
|
||||||
if (mute.expires_at) {
|
if (mute.expires_at) {
|
||||||
const timeUntilExpiry = moment.utc().diff(moment.utc(mute.expires_at, DBDateFormat));
|
const timeUntilExpiry = moment.utc().diff(moment.utc(mute.expires_at, DBDateFormat));
|
||||||
const humanizedTime = humanizeDurationShort(timeUntilExpiry, { largest: 2, round: true });
|
const humanizedTime = toRelativeNativeTimestamp(timeUntilExpiry, 0);
|
||||||
line += ` ⏰ Expires in ${humanizedTime}`;
|
line += ` ⏰ Expires ${humanizedTime}`;
|
||||||
} else {
|
} else {
|
||||||
line += ` ⏰ Indefinite`;
|
line += ` ⏰ Indefinite`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeFromMute = moment.utc(mute.created_at, DBDateFormat).diff(moment.utc());
|
const timeFromMute = moment.utc(mute.created_at, DBDateFormat);
|
||||||
const humanizedTimeFromMute = humanizeDurationShort(timeFromMute, { largest: 2, round: true });
|
const humanizedTimeFromMute = toRelativeNativeTimestamp(timeFromMute);
|
||||||
line += ` 🕒 Muted ${humanizedTimeFromMute} ago`;
|
line += ` 🕒 Muted ${humanizedTimeFromMute}`;
|
||||||
|
|
||||||
if (mute.banned) {
|
if (mute.banned) {
|
||||||
line += ` 🔨 Banned`;
|
line += ` 🔨 Banned`;
|
||||||
|
@ -207,12 +207,17 @@ export const MutesCmd = mutesCmd({
|
||||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(buttons);
|
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(buttons);
|
||||||
await listMessage.edit({ components: [row] });
|
await listMessage.edit({ components: [row] });
|
||||||
|
|
||||||
const collector = listMessage.createMessageComponentCollector({ time: stopCollectionDebounce });
|
const collector = listMessage.createMessageComponentCollector({
|
||||||
|
time: stopCollectionDebounce,
|
||||||
|
});
|
||||||
|
|
||||||
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
||||||
if (msg.author.id !== interaction.user.id) {
|
if (msg.author.id !== interaction.user.id) {
|
||||||
interaction
|
interaction
|
||||||
.reply({ content: `You are not permitted to use these buttons.`, ephemeral: true })
|
.reply({
|
||||||
|
content: `You are not permitted to use these buttons.`,
|
||||||
|
ephemeral: true,
|
||||||
|
})
|
||||||
// tslint:disable-next-line no-console
|
// tslint:disable-next-line no-console
|
||||||
.catch((err) => console.trace(err.message));
|
.catch((err) => console.trace(err.message));
|
||||||
} else {
|
} else {
|
||||||
|
@ -228,7 +233,10 @@ export const MutesCmd = mutesCmd({
|
||||||
|
|
||||||
stopCollectionFn = async () => {
|
stopCollectionFn = async () => {
|
||||||
collector.stop();
|
collector.stop();
|
||||||
await listMessage.edit({ content: listMessage.content, components: [] });
|
await listMessage.edit({
|
||||||
|
content: listMessage.content,
|
||||||
|
components: [],
|
||||||
|
});
|
||||||
};
|
};
|
||||||
bumpCollectionTimeout();
|
bumpCollectionTimeout();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import humanizeDuration from "humanize-duration";
|
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
import { commandTypeHelpers as ct } from "../../../commandTypes.js";
|
||||||
import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop.js";
|
import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop.js";
|
||||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils.js";
|
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils.js";
|
||||||
import { convertDelayStringToMS, messageLink } from "../../../utils.js";
|
import { convertDelayStringToMS, toNativeTimestamp, toRelativeNativeTimestamp, messageLink } from "../../../utils.js";
|
||||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
|
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
|
||||||
import { remindersCmd } from "../types.js";
|
import { remindersCmd } from "../types.js";
|
||||||
|
|
||||||
|
@ -61,16 +60,9 @@ export const RemindCmd = remindersCmd({
|
||||||
|
|
||||||
registerUpcomingReminder(reminder);
|
registerUpcomingReminder(reminder);
|
||||||
|
|
||||||
const msUntilReminder = reminderTime.diff(now);
|
const timeUntilReminder = toRelativeNativeTimestamp(reminderTime, 0);
|
||||||
const timeUntilReminder = humanizeDuration(msUntilReminder, { largest: 2, round: true });
|
const prettyReminderTime = toNativeTimestamp(reminderTime);
|
||||||
const prettyReminderTime = (await timeAndDate.inMemberTz(msg.author.id, reminderTime)).format(
|
|
||||||
pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime"),
|
|
||||||
);
|
|
||||||
|
|
||||||
sendSuccessMessage(
|
sendSuccessMessage(pluginData, msg.channel, `I will remind you ${timeUntilReminder} at ${prettyReminderTime}`);
|
||||||
pluginData,
|
|
||||||
msg.channel,
|
|
||||||
`I will remind you in **${timeUntilReminder}** at **${prettyReminderTime}**`,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
import humanizeDuration from "humanize-duration";
|
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { sendErrorMessage } from "../../../pluginUtils.js";
|
import { sendErrorMessage } from "../../../pluginUtils.js";
|
||||||
import { createChunkedMessage, DBDateFormat, sorter } from "../../../utils.js";
|
import {
|
||||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
|
createChunkedMessage,
|
||||||
|
sorter,
|
||||||
|
toNativeTimestamp,
|
||||||
|
toRelativeNativeTimestamp,
|
||||||
|
} from "../../../utils.js";
|
||||||
import { remindersCmd } from "../types.js";
|
import { remindersCmd } from "../types.js";
|
||||||
|
|
||||||
export const RemindersCmd = remindersCmd({
|
export const RemindersCmd = remindersCmd({
|
||||||
trigger: "reminders",
|
trigger: "reminders",
|
||||||
permission: "can_use",
|
permission: "can_use",
|
||||||
|
|
||||||
async run({ message: msg, pluginData }) {
|
async run({ message: msg, pluginData }) {
|
||||||
const reminders = await pluginData.state.reminders.getRemindersByUserId(msg.author.id);
|
const reminders = await pluginData.state.reminders.getRemindersByUserId(
|
||||||
if (reminders.length === 0) {
|
msg.author.id,
|
||||||
sendErrorMessage(pluginData, msg.channel, "No reminders");
|
);
|
||||||
return;
|
if (reminders.length === 0) {
|
||||||
}
|
sendErrorMessage(pluginData, msg.channel, "No reminders");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
reminders.sort(sorter("remind_at"));
|
||||||
|
const longestNum = (reminders.length + 1).toString().length;
|
||||||
|
const lines = Array.from(reminders.entries()).map(([i, reminder]) => {
|
||||||
|
const num = i + 1;
|
||||||
|
const paddedNum = num.toString().padStart(longestNum, " ");
|
||||||
|
const target = moment.utc(reminder.remind_at);
|
||||||
|
const relative = toRelativeNativeTimestamp(target, 0);
|
||||||
|
const prettyRemindAt = toNativeTimestamp(target);
|
||||||
|
return `\`${paddedNum}.\` ${prettyRemindAt} (${relative}) ${reminder.body}`;
|
||||||
|
});
|
||||||
|
|
||||||
reminders.sort(sorter("remind_at"));
|
createChunkedMessage(msg.channel, lines.join("\n"));
|
||||||
const longestNum = (reminders.length + 1).toString().length;
|
},
|
||||||
const lines = Array.from(reminders.entries()).map(([i, reminder]) => {
|
|
||||||
const num = i + 1;
|
|
||||||
const paddedNum = num.toString().padStart(longestNum, " ");
|
|
||||||
const target = moment.utc(reminder.remind_at, "YYYY-MM-DD HH:mm:ss");
|
|
||||||
const diff = target.diff(moment.utc());
|
|
||||||
const result = humanizeDuration(diff, { largest: 2, round: true });
|
|
||||||
const prettyRemindAt = timeAndDate
|
|
||||||
.inGuildTz(moment.utc(reminder.remind_at, DBDateFormat))
|
|
||||||
.format(timeAndDate.getDateFormat("pretty_datetime"));
|
|
||||||
return `\`${paddedNum}.\` \`${prettyRemindAt} (${result})\` ${reminder.body}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
createChunkedMessage(msg.channel, lines.join("\n"));
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import shuffle from "lodash/shuffle.js";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { rootDir } from "../../../paths.js";
|
import { rootDir } from "../../../paths.js";
|
||||||
import { getCurrentUptime } from "../../../uptime.js";
|
import { getCurrentUptime } from "../../../uptime.js";
|
||||||
import { resolveMember, sorter } from "../../../utils.js";
|
import { resolveMember, sorter, toRelativeNativeTimestamp } from "../../../utils.js";
|
||||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
|
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
|
||||||
import { utilityCmd } from "../types.js";
|
import { utilityCmd } from "../types.js";
|
||||||
|
|
||||||
|
@ -31,23 +31,18 @@ export const AboutCmd = utilityCmd({
|
||||||
let version;
|
let version;
|
||||||
|
|
||||||
if (lastCommit) {
|
if (lastCommit) {
|
||||||
lastUpdate = timeAndDate
|
lastUpdate = toRelativeNativeTimestamp(moment.utc(lastCommit.committer.data, "X"), 0);
|
||||||
.inGuildTz(moment.utc(lastCommit.committer.date, "X"))
|
|
||||||
.format(pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime"));
|
|
||||||
version = lastCommit.shortHash;
|
version = lastCommit.shortHash;
|
||||||
} else {
|
} else {
|
||||||
lastUpdate = "?";
|
lastUpdate = "?";
|
||||||
version = "?";
|
version = "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastReload = humanizeDuration(Date.now() - pluginData.state.lastReload, {
|
const lastReload = toRelativeNativeTimestamp(pluginData.state.lastReload, 0);
|
||||||
largest: 2,
|
|
||||||
round: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const basicInfoRows = [
|
const basicInfoRows = [
|
||||||
["Uptime", prettyUptime],
|
["Uptime", prettyUptime],
|
||||||
["Last config reload", `${lastReload} ago`],
|
["Last config reload", lastReload],
|
||||||
["Last bot update", lastUpdate],
|
["Last bot update", lastUpdate],
|
||||||
["Version", version],
|
["Version", version],
|
||||||
["API latency", `${pluginData.client.ws.ping}ms`],
|
["API latency", `${pluginData.client.ws.ping}ms`],
|
||||||
|
@ -101,7 +96,9 @@ export const AboutCmd = utilityCmd({
|
||||||
|
|
||||||
// Use the bot avatar as the embed image
|
// Use the bot avatar as the embed image
|
||||||
if (pluginData.client.user!.displayAvatarURL()) {
|
if (pluginData.client.user!.displayAvatarURL()) {
|
||||||
aboutEmbed.thumbnail = { url: pluginData.client.user!.displayAvatarURL()! };
|
aboutEmbed.thumbnail = {
|
||||||
|
url: pluginData.client.user!.displayAvatarURL()!,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.channel.send({ embeds: [aboutEmbed] });
|
msg.channel.send({ embeds: [aboutEmbed] });
|
||||||
|
|
|
@ -446,6 +446,14 @@ export function convertMSToDelayString(ms: number): string {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toNativeTimestamp(time, flag = "f") {
|
||||||
|
return `<t:${Math.round(time / 1000)}:${flag}>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toRelativeNativeTimestamp(ms, offset = Date.now()) {
|
||||||
|
return `<t:${Math.round((offset + ms) / 1000)}:R>`;
|
||||||
|
}
|
||||||
|
|
||||||
export function successMessage(str: string, emoji = "<:zep_check:906897402101891093>") {
|
export function successMessage(str: string, emoji = "<:zep_check:906897402101891093>") {
|
||||||
return emoji ? `${emoji} ${str}` : str;
|
return emoji ? `${emoji} ${str}` : str;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue