From 60065044590f098e69a80ec6d83ee29fe391bc30 Mon Sep 17 00:00:00 2001 From: Rei Star Date: Thu, 13 Mar 2025 07:04:42 +0400 Subject: [PATCH] feat: native timestamps --- .../src/plugins/ModActions/commands/BanCmd.ts | 422 ++++++++++-------- .../plugins/ModActions/functions/banUserId.ts | 3 +- .../src/plugins/Mutes/commands/MutesCmd.ts | 26 +- .../plugins/Reminders/commands/RemindCmd.ts | 16 +- .../Reminders/commands/RemindersCmd.ts | 56 +-- .../src/plugins/Utility/commands/AboutCmd.ts | 17 +- backend/src/utils.ts | 8 + 7 files changed, 305 insertions(+), 243 deletions(-) diff --git a/backend/src/plugins/ModActions/commands/BanCmd.ts b/backend/src/plugins/ModActions/commands/BanCmd.ts index d3c5c745..3e888f75 100644 --- a/backend/src/plugins/ModActions/commands/BanCmd.ts +++ b/backend/src/plugins/ModActions/commands/BanCmd.ts @@ -2,10 +2,23 @@ import humanizeDuration from "humanize-duration"; import { getMemberLevel } from "knub/helpers"; import { commandTypeHelpers as ct } from "../../../commandTypes.js"; import { CaseTypes } from "../../../data/CaseTypes.js"; -import { clearExpiringTempban, registerExpiringTempban } from "../../../data/loops/expiringTempbansLoop.js"; -import { canActOn, hasPermission, sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils.js"; +import { + clearExpiringTempban, + registerExpiringTempban, +} from "../../../data/loops/expiringTempbansLoop.js"; +import { + canActOn, + hasPermission, + sendErrorMessage, + sendSuccessMessage, +} from "../../../pluginUtils.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 { waitForButtonConfirm } from "../../../utils/waitForInteraction.js"; import { LogsPlugin } from "../../Logs/LogsPlugin.js"; @@ -16,204 +29,247 @@ import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromA import { modActionsCmd } from "../types.js"; const opts = { - mod: ct.member({ option: true }), - notify: ct.string({ option: true }), - "notify-channel": ct.textChannel({ option: true }), - "delete-days": ct.number({ option: true, shortcut: "d" }), + mod: ct.member({ option: true }), + notify: ct.string({ option: true }), + "notify-channel": ct.textChannel({ option: true }), + "delete-days": ct.number({ option: true, shortcut: "d" }), }; export const BanCmd = modActionsCmd({ - trigger: "ban", - permission: "can_ban", - description: "Ban or Tempban the specified member", + trigger: "ban", + permission: "can_ban", + description: "Ban or Tempban the specified member", - signature: [ - { - user: ct.string(), - time: ct.delay(), - reason: ct.string({ required: false, catchAll: true }), + signature: [ + { + user: ct.string(), + time: ct.delay(), + reason: ct.string({ required: false, catchAll: true }), - ...opts, - }, - { - user: ct.string(), - reason: ct.string({ required: false, catchAll: true }), + ...opts, + }, + { + user: ct.string(), + reason: ct.string({ required: false, catchAll: true }), - ...opts, - }, - ], + ...opts, + }, + ], - async run({ pluginData, message: msg, args }) { - const user = await resolveUser(pluginData.client, args.user); - if (!user.id) { - sendErrorMessage(pluginData, msg.channel, `User not found`); - return; - } - const time = args["time"] ? args["time"] : null; + async run({ pluginData, message: msg, args }) { + const user = await resolveUser(pluginData.client, args.user); + if (!user.id) { + sendErrorMessage(pluginData, msg.channel, `User not found`); + return; + } + const time = args["time"] ? args["time"] : null; - const reason = formatReasonWithAttachments(args.reason, [...msg.attachments.values()]); - const memberToBan = await resolveMember(pluginData.client, pluginData.guild, user.id); - // 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; - } + const reason = formatReasonWithAttachments(args.reason, [ + ...msg.attachments.values(), + ]); + const memberToBan = await resolveMember( + pluginData.client, + pluginData.guild, + user.id, + ); + // 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) - const lock = await pluginData.locks.acquire(banLock(user)); - let forceban = false; - const existingTempban = await pluginData.state.tempbans.findExistingTempbanForUserId(user.id); - if (!memberToBan) { - const banned = await isBanned(pluginData, user.id); - if (banned) { - // Abort if trying to ban user indefinitely if they are already banned indefinitely - if (!existingTempban && !time) { - sendErrorMessage(pluginData, msg.channel, `User is already banned indefinitely.`); - return; - } + // acquire a lock because of the needed user-inputs below (if banned/not on server) + const lock = await pluginData.locks.acquire(banLock(user)); + let forceban = false; + const existingTempban = + await pluginData.state.tempbans.findExistingTempbanForUserId(user.id); + if (!memberToBan) { + const banned = await isBanned(pluginData, user.id); + if (banned) { + // Abort if trying to ban user indefinitely if they are already banned indefinitely + if (!existingTempban && !time) { + sendErrorMessage( + pluginData, + msg.channel, + `User is already banned indefinitely.`, + ); + return; + } - // Ask the mod if we should update the existing ban - const reply = await waitForButtonConfirm( - msg.channel, - { content: "Failed to message the user. Log the warning anyway?" }, - { confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id }, - ); - if (!reply) { - sendErrorMessage(pluginData, msg.channel, "User already banned, update cancelled by moderator"); - lock.unlock(); - return; - } else { - // Update or add new tempban / remove old tempban - if (time && time > 0) { - if (existingTempban) { - await pluginData.state.tempbans.updateExpiryTime(user.id, time, mod.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); - } + // Ask the mod if we should update the existing ban + const reply = await waitForButtonConfirm( + msg.channel, + { content: "Failed to message the user. Log the warning anyway?" }, + { confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id }, + ); + if (!reply) { + sendErrorMessage( + pluginData, + msg.channel, + "User already banned, update cancelled by moderator", + ); + lock.unlock(); + return; + } else { + // Update or add new tempban / remove old tempban + if (time && time > 0) { + if (existingTempban) { + await pluginData.state.tempbans.updateExpiryTime( + user.id, + time, + mod.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 - const casesPlugin = pluginData.getPlugin(CasesPlugin); - const createdCase = await casesPlugin.createCase({ - modId: mod.id, - type: CaseTypes.Ban, - userId: user.id, - reason, - noteDetails: [`Ban updated to ${time ? humanizeDuration(time) : "indefinite"}`], - }); - if (time) { - pluginData.getPlugin(LogsPlugin).logMemberTimedBan({ - mod: mod.user, - user, - caseNumber: createdCase.case_number, - reason, - banTime: humanizeDuration(time), - }); - } else { - pluginData.getPlugin(LogsPlugin).logMemberBan({ - mod: mod.user, - user, - caseNumber: createdCase.case_number, - reason, - }); - } + // 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 createdCase = await casesPlugin.createCase({ + modId: mod.id, + type: CaseTypes.Ban, + userId: user.id, + reason, + noteDetails: [ + `Ban updated to ${time ? `expire ${toRelativeNativeTimestamp(time)}` : "indefinite"}`, + ], + }); + if (time) { + pluginData.getPlugin(LogsPlugin).logMemberTimedBan({ + mod: mod.user, + user, + caseNumber: createdCase.case_number, + reason, + banTime: humanizeDuration(time), + }); + } else { + pluginData.getPlugin(LogsPlugin).logMemberBan({ + mod: mod.user, + user, + caseNumber: createdCase.case_number, + reason, + }); + } - sendSuccessMessage( - pluginData, - msg.channel, - `Ban updated to ${time ? "expire in " + humanizeDuration(time) + " from now" : "indefinite"}`, - ); - lock.unlock(); - return; - } - } else { - // Ask the mod if we should upgrade to a forceban as the user is not on the server - const reply = await waitForButtonConfirm( - msg.channel, - { content: "User not on server, forceban instead?" }, - { confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id }, - ); - if (!reply) { - sendErrorMessage(pluginData, msg.channel, "User not on server, ban cancelled by moderator"); - lock.unlock(); - return; - } else { - forceban = true; - } - } - } + sendSuccessMessage( + pluginData, + msg.channel, + `Ban updated to ${time ? `expire ${toRelativeNativeTimestamp(time)}` : "indefinite"}`, + ); + lock.unlock(); + return; + } + } else { + // Ask the mod if we should upgrade to a forceban as the user is not on the server + const reply = await waitForButtonConfirm( + msg.channel, + { content: "User not on server, forceban instead?" }, + { confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id }, + ); + if (!reply) { + sendErrorMessage( + pluginData, + msg.channel, + "User not on server, ban cancelled by moderator", + ); + lock.unlock(); + return; + } else { + forceban = true; + } + } + } - // Make sure we're allowed to ban this member if they are on the server - if (!forceban && !canActOn(pluginData, msg.member, memberToBan!)) { - const ourLevel = getMemberLevel(pluginData, msg.member); - const targetLevel = getMemberLevel(pluginData, memberToBan!); - sendErrorMessage( - pluginData, - msg.channel, - `Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`, - ); - lock.unlock(); - return; - } + // Make sure we're allowed to ban this member if they are on the server + if (!forceban && !canActOn(pluginData, msg.member, memberToBan!)) { + const ourLevel = getMemberLevel(pluginData, msg.member); + const targetLevel = getMemberLevel(pluginData, memberToBan!); + sendErrorMessage( + pluginData, + msg.channel, + `Cannot ban: target permission level is equal or higher to yours, ${targetLevel} >= ${ourLevel}`, + ); + lock.unlock(); + return; + } - let contactMethods; - try { - contactMethods = readContactMethodsFromArgs(args); - } catch (e) { - sendErrorMessage(pluginData, msg.channel, e.message); - lock.unlock(); - return; - } + let contactMethods; + try { + contactMethods = readContactMethodsFromArgs(args); + } catch (e) { + sendErrorMessage(pluginData, msg.channel, e.message); + lock.unlock(); + return; + } - const deleteMessageDays = - args["delete-days"] ?? (await pluginData.config.getForMessage(msg)).ban_delete_message_days; - const banResult = await banUserId( - pluginData, - user.id, - reason, - { - contactMethods, - caseArgs: { - modId: mod.id, - ppId: mod.id !== msg.author.id ? msg.author.id : undefined, - }, - deleteMessageDays, - modId: mod.id, - }, - time, - ); + const deleteMessageDays = + args["delete-days"] ?? + (await pluginData.config.getForMessage(msg)).ban_delete_message_days; + const banResult = await banUserId( + pluginData, + user.id, + reason, + { + contactMethods, + caseArgs: { + modId: mod.id, + ppId: mod.id !== msg.author.id ? msg.author.id : undefined, + }, + deleteMessageDays, + modId: mod.id, + }, + time, + ); - if (banResult.status === "failed") { - sendErrorMessage(pluginData, msg.channel, `Failed to ban member: ${banResult.error}`); - lock.unlock(); - return; - } + if (banResult.status === "failed") { + sendErrorMessage( + pluginData, + msg.channel, + `Failed to ban member: ${banResult.error}`, + ); + lock.unlock(); + return; + } - let forTime = ""; - if (time && time > 0) { - forTime = `for ${humanizeDuration(time)} `; - } + let forTime = ""; + if (time && time > 0) { + forTime = `for ${humanizeDuration(time)} `; + } - // Confirm the action to the moderator - let response = ""; - if (!forceban) { - response = `Banned **${renderUsername(user)}** ${forTime}(Case #${banResult.case.case_number})`; - if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`; - } else { - response = `Member forcebanned ${forTime}(Case #${banResult.case.case_number})`; - } + // Confirm the action to the moderator + let response = ""; + if (!forceban) { + response = `Banned **${renderUsername(user)}** ${forTime}(Case #${banResult.case.case_number})`; + if (banResult.notifyResult.text) + response += ` (${banResult.notifyResult.text})`; + } else { + response = `Member forcebanned ${forTime}(Case #${banResult.case.case_number})`; + } - lock.unlock(); - sendSuccessMessage(pluginData, msg.channel, response); - }, + lock.unlock(); + sendSuccessMessage(pluginData, msg.channel, response); + }, }); diff --git a/backend/src/plugins/ModActions/functions/banUserId.ts b/backend/src/plugins/ModActions/functions/banUserId.ts index 2ac01af3..e7dd12d5 100644 --- a/backend/src/plugins/ModActions/functions/banUserId.ts +++ b/backend/src/plugins/ModActions/functions/banUserId.ts @@ -14,6 +14,7 @@ import { notifyUser, resolveMember, resolveUser, + toRelativeNativeTimestamp, ucfirst, } from "../../../utils.js"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects.js"; @@ -172,7 +173,7 @@ export async function banUserId( user, caseNumber: createdCase.case_number, reason: reason ?? "", - banTime: humanizeDuration(banTime), + banTime: toRelativeNativeTimestamp(banTime), }); } else { pluginData.getPlugin(LogsPlugin).logMemberBan({ diff --git a/backend/src/plugins/Mutes/commands/MutesCmd.ts b/backend/src/plugins/Mutes/commands/MutesCmd.ts index d599edd4..14cd839e 100644 --- a/backend/src/plugins/Mutes/commands/MutesCmd.ts +++ b/backend/src/plugins/Mutes/commands/MutesCmd.ts @@ -10,7 +10,7 @@ import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes.js"; import { humanizeDurationShort } from "../../../humanizeDurationShort.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"; export const MutesCmd = mutesCmd({ @@ -131,15 +131,15 @@ export const MutesCmd = mutesCmd({ if (mute.expires_at) { const timeUntilExpiry = moment.utc().diff(moment.utc(mute.expires_at, DBDateFormat)); - const humanizedTime = humanizeDurationShort(timeUntilExpiry, { largest: 2, round: true }); - line += ` ⏰ Expires in ${humanizedTime}`; + const humanizedTime = toRelativeNativeTimestamp(timeUntilExpiry, 0); + line += ` ⏰ Expires ${humanizedTime}`; } else { line += ` ⏰ Indefinite`; } - const timeFromMute = moment.utc(mute.created_at, DBDateFormat).diff(moment.utc()); - const humanizedTimeFromMute = humanizeDurationShort(timeFromMute, { largest: 2, round: true }); - line += ` 🕒 Muted ${humanizedTimeFromMute} ago`; + const timeFromMute = moment.utc(mute.created_at, DBDateFormat); + const humanizedTimeFromMute = toRelativeNativeTimestamp(timeFromMute); + line += ` 🕒 Muted ${humanizedTimeFromMute}`; if (mute.banned) { line += ` 🔨 Banned`; @@ -207,12 +207,17 @@ export const MutesCmd = mutesCmd({ const row = new ActionRowBuilder().addComponents(buttons); await listMessage.edit({ components: [row] }); - const collector = listMessage.createMessageComponentCollector({ time: stopCollectionDebounce }); + const collector = listMessage.createMessageComponentCollector({ + time: stopCollectionDebounce, + }); collector.on("collect", async (interaction: MessageComponentInteraction) => { if (msg.author.id !== interaction.user.id) { 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 .catch((err) => console.trace(err.message)); } else { @@ -228,7 +233,10 @@ export const MutesCmd = mutesCmd({ stopCollectionFn = async () => { collector.stop(); - await listMessage.edit({ content: listMessage.content, components: [] }); + await listMessage.edit({ + content: listMessage.content, + components: [], + }); }; bumpCollectionTimeout(); } diff --git a/backend/src/plugins/Reminders/commands/RemindCmd.ts b/backend/src/plugins/Reminders/commands/RemindCmd.ts index 136a5c30..523bee44 100644 --- a/backend/src/plugins/Reminders/commands/RemindCmd.ts +++ b/backend/src/plugins/Reminders/commands/RemindCmd.ts @@ -1,9 +1,8 @@ -import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; import { commandTypeHelpers as ct } from "../../../commandTypes.js"; import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop.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 { remindersCmd } from "../types.js"; @@ -61,16 +60,9 @@ export const RemindCmd = remindersCmd({ registerUpcomingReminder(reminder); - const msUntilReminder = reminderTime.diff(now); - const timeUntilReminder = humanizeDuration(msUntilReminder, { largest: 2, round: true }); - const prettyReminderTime = (await timeAndDate.inMemberTz(msg.author.id, reminderTime)).format( - pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime"), - ); + const timeUntilReminder = toRelativeNativeTimestamp(reminderTime, 0); + const prettyReminderTime = toNativeTimestamp(reminderTime); - sendSuccessMessage( - pluginData, - msg.channel, - `I will remind you in **${timeUntilReminder}** at **${prettyReminderTime}**`, - ); + sendSuccessMessage(pluginData, msg.channel, `I will remind you ${timeUntilReminder} at ${prettyReminderTime}`); }, }); diff --git a/backend/src/plugins/Reminders/commands/RemindersCmd.ts b/backend/src/plugins/Reminders/commands/RemindersCmd.ts index 04ef4125..72afd615 100644 --- a/backend/src/plugins/Reminders/commands/RemindersCmd.ts +++ b/backend/src/plugins/Reminders/commands/RemindersCmd.ts @@ -1,37 +1,37 @@ -import humanizeDuration from "humanize-duration"; import moment from "moment-timezone"; import { sendErrorMessage } from "../../../pluginUtils.js"; -import { createChunkedMessage, DBDateFormat, sorter } from "../../../utils.js"; -import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js"; +import { + createChunkedMessage, + sorter, + toNativeTimestamp, + toRelativeNativeTimestamp, +} from "../../../utils.js"; import { remindersCmd } from "../types.js"; export const RemindersCmd = remindersCmd({ - trigger: "reminders", - permission: "can_use", + trigger: "reminders", + permission: "can_use", - async run({ message: msg, pluginData }) { - const reminders = await pluginData.state.reminders.getRemindersByUserId(msg.author.id); - if (reminders.length === 0) { - sendErrorMessage(pluginData, msg.channel, "No reminders"); - return; - } + async run({ message: msg, pluginData }) { + const reminders = await pluginData.state.reminders.getRemindersByUserId( + msg.author.id, + ); + 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")); - 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")); - }, + createChunkedMessage(msg.channel, lines.join("\n")); + }, }); diff --git a/backend/src/plugins/Utility/commands/AboutCmd.ts b/backend/src/plugins/Utility/commands/AboutCmd.ts index 6de19471..41431689 100644 --- a/backend/src/plugins/Utility/commands/AboutCmd.ts +++ b/backend/src/plugins/Utility/commands/AboutCmd.ts @@ -5,7 +5,7 @@ import shuffle from "lodash/shuffle.js"; import moment from "moment-timezone"; import { rootDir } from "../../../paths.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 { utilityCmd } from "../types.js"; @@ -31,23 +31,18 @@ export const AboutCmd = utilityCmd({ let version; if (lastCommit) { - lastUpdate = timeAndDate - .inGuildTz(moment.utc(lastCommit.committer.date, "X")) - .format(pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime")); + lastUpdate = toRelativeNativeTimestamp(moment.utc(lastCommit.committer.data, "X"), 0); version = lastCommit.shortHash; } else { lastUpdate = "?"; version = "?"; } - const lastReload = humanizeDuration(Date.now() - pluginData.state.lastReload, { - largest: 2, - round: true, - }); + const lastReload = toRelativeNativeTimestamp(pluginData.state.lastReload, 0); const basicInfoRows = [ ["Uptime", prettyUptime], - ["Last config reload", `${lastReload} ago`], + ["Last config reload", lastReload], ["Last bot update", lastUpdate], ["Version", version], ["API latency", `${pluginData.client.ws.ping}ms`], @@ -101,7 +96,9 @@ export const AboutCmd = utilityCmd({ // Use the bot avatar as the embed image if (pluginData.client.user!.displayAvatarURL()) { - aboutEmbed.thumbnail = { url: pluginData.client.user!.displayAvatarURL()! }; + aboutEmbed.thumbnail = { + url: pluginData.client.user!.displayAvatarURL()!, + }; } msg.channel.send({ embeds: [aboutEmbed] }); diff --git a/backend/src/utils.ts b/backend/src/utils.ts index 214d3f20..7907300a 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -446,6 +446,14 @@ export function convertMSToDelayString(ms: number): string { return result; } +export function toNativeTimestamp(time, flag = "f") { + return ``; +} + +export function toRelativeNativeTimestamp(ms, offset = Date.now()) { + return ``; +} + export function successMessage(str: string, emoji = "<:zep_check:906897402101891093>") { return emoji ? `${emoji} ${str}` : str; }