aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar MarkKoz <[email protected]>2021-08-05 16:27:13 -0700
committerGravatar MarkKoz <[email protected]>2021-08-05 16:27:13 -0700
commit469cd57693925e78bef6a1163b620a39da208670 (patch)
tree22619d5a2e4c3787360e084610cb04e73adf5c9d
parentTime: remove RFC1123 support (diff)
Time: qualify uses of functions with the module name
In cases where many time utility functions were being imported, this makes the imports shorter and cleaner. In other cases, the function names read better when they're qualified with "time"; the extra context it adds is helpful.
-rw-r--r--bot/converters.py4
-rw-r--r--bot/exts/info/information.py10
-rw-r--r--bot/exts/moderation/defcon.py29
-rw-r--r--bot/exts/moderation/infraction/management.py7
-rw-r--r--bot/exts/moderation/infraction/superstarify.py6
-rw-r--r--bot/exts/moderation/modlog.py6
-rw-r--r--bot/exts/moderation/modpings.py7
-rw-r--r--bot/exts/moderation/stream.py7
-rw-r--r--bot/exts/moderation/watchchannels/_watchchannel.py7
-rw-r--r--bot/exts/recruitment/talentpool/_cog.py3
-rw-r--r--bot/exts/recruitment/talentpool/_review.py8
-rw-r--r--bot/exts/utils/reminders.py10
-rw-r--r--bot/exts/utils/utils.py5
13 files changed, 51 insertions, 58 deletions
diff --git a/bot/converters.py b/bot/converters.py
index 559e759e1..b68c4d623 100644
--- a/bot/converters.py
+++ b/bot/converters.py
@@ -20,9 +20,9 @@ from bot.errors import InvalidInfraction
from bot.exts.info.doc import _inventory_parser
from bot.exts.info.tags import TagIdentifier
from bot.log import get_logger
+from bot.utils import time
from bot.utils.extensions import EXTENSIONS, unqualify
from bot.utils.regex import INVITE_RE
-from bot.utils.time import parse_duration_string
if t.TYPE_CHECKING:
from bot.exts.info.source import SourceType
@@ -338,7 +338,7 @@ class DurationDelta(Converter):
The units need to be provided in descending order of magnitude.
"""
- if not (delta := parse_duration_string(duration)):
+ if not (delta := time.parse_duration_string(duration)):
raise BadArgument(f"`{duration}` is not a valid duration string.")
return delta
diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py
index 1f95c460f..a83ce4d53 100644
--- a/bot/exts/info/information.py
+++ b/bot/exts/info/information.py
@@ -18,10 +18,10 @@ from bot.decorators import in_whitelist
from bot.errors import NonExistentRoleError
from bot.log import get_logger
from bot.pagination import LinePaginator
+from bot.utils import time
from bot.utils.channel import is_mod_channel, is_staff_channel
from bot.utils.checks import cooldown_with_role_bypass, has_no_roles_check, in_whitelist_check
from bot.utils.members import get_or_fetch_member
-from bot.utils.time import TimestampFormats, discord_timestamp, humanize_delta
log = get_logger(__name__)
@@ -83,7 +83,7 @@ class Information(Cog):
defcon_info = ""
if cog := self.bot.get_cog("Defcon"):
- threshold = humanize_delta(cog.threshold) if cog.threshold else "-"
+ threshold = time.humanize_delta(cog.threshold) if cog.threshold else "-"
defcon_info = f"Defcon threshold: {threshold}\n"
verification = f"Verification level: {ctx.guild.verification_level.name}\n"
@@ -173,7 +173,7 @@ class Information(Cog):
"""Returns an embed full of server information."""
embed = Embed(colour=Colour.og_blurple(), title="Server Information")
- created = discord_timestamp(ctx.guild.created_at, TimestampFormats.RELATIVE)
+ created = time.discord_timestamp(ctx.guild.created_at, time.TimestampFormats.RELATIVE)
num_roles = len(ctx.guild.roles) - 1 # Exclude @everyone
# Server Features are only useful in certain channels
@@ -249,7 +249,7 @@ class Information(Cog):
"""Creates an embed containing information on the `user`."""
on_server = bool(await get_or_fetch_member(ctx.guild, user.id))
- created = discord_timestamp(user.created_at, TimestampFormats.RELATIVE)
+ created = time.discord_timestamp(user.created_at, time.TimestampFormats.RELATIVE)
name = str(user)
if on_server and user.nick:
@@ -272,7 +272,7 @@ class Information(Cog):
if on_server:
if user.joined_at:
- joined = discord_timestamp(user.joined_at, TimestampFormats.RELATIVE)
+ joined = time.discord_timestamp(user.joined_at, time.TimestampFormats.RELATIVE)
else:
joined = "Unable to get join date"
diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py
index 14db37367..048e0f990 100644
--- a/bot/exts/moderation/defcon.py
+++ b/bot/exts/moderation/defcon.py
@@ -17,12 +17,9 @@ from bot.constants import Channels, Colours, Emojis, Event, Icons, MODERATION_RO
from bot.converters import DurationDelta, Expiry
from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
-from bot.utils import scheduling
+from bot.utils import scheduling, time
from bot.utils.messages import format_user
from bot.utils.scheduling import Scheduler
-from bot.utils.time import (
- TimestampFormats, discord_timestamp, humanize_delta, parse_duration_string, relativedelta_to_timedelta
-)
log = get_logger(__name__)
@@ -88,7 +85,7 @@ class Defcon(Cog):
try:
settings = await self.defcon_settings.to_dict()
- self.threshold = parse_duration_string(settings["threshold"]) if settings.get("threshold") else None
+ self.threshold = time.parse_duration_string(settings["threshold"]) if settings.get("threshold") else None
self.expiry = datetime.fromisoformat(settings["expiry"]) if settings.get("expiry") else None
except RedisError:
log.exception("Unable to get DEFCON settings!")
@@ -102,7 +99,7 @@ class Defcon(Cog):
self.scheduler.schedule_at(self.expiry, 0, self._remove_threshold())
self._update_notifier()
- log.info(f"DEFCON synchronized: {humanize_delta(self.threshold) if self.threshold else '-'}")
+ log.info(f"DEFCON synchronized: {time.humanize_delta(self.threshold) if self.threshold else '-'}")
self._update_channel_topic()
@@ -112,7 +109,7 @@ class Defcon(Cog):
if self.threshold:
now = arrow.utcnow()
- if now - member.created_at < relativedelta_to_timedelta(self.threshold):
+ if now - member.created_at < time.relativedelta_to_timedelta(self.threshold):
log.info(f"Rejecting user {member}: Account is too new")
message_sent = False
@@ -151,11 +148,12 @@ class Defcon(Cog):
@has_any_role(*MODERATION_ROLES)
async def status(self, ctx: Context) -> None:
"""Check the current status of DEFCON mode."""
+ expiry = time.discord_timestamp(self.expiry, time.TimestampFormats.RELATIVE) if self.expiry else "-"
embed = Embed(
colour=Colour.og_blurple(), title="DEFCON Status",
description=f"""
- **Threshold:** {humanize_delta(self.threshold) if self.threshold else "-"}
- **Expires:** {discord_timestamp(self.expiry, TimestampFormats.RELATIVE) if self.expiry else "-"}
+ **Threshold:** {time.humanize_delta(self.threshold) if self.threshold else "-"}
+ **Expires:** {expiry}
**Verification level:** {ctx.guild.verification_level.name}
"""
)
@@ -213,7 +211,8 @@ class Defcon(Cog):
def _update_channel_topic(self) -> None:
"""Update the #defcon channel topic with the current DEFCON status."""
- new_topic = f"{BASE_CHANNEL_TOPIC}\n(Threshold: {humanize_delta(self.threshold) if self.threshold else '-'})"
+ threshold = time.humanize_delta(self.threshold) if self.threshold else '-'
+ new_topic = f"{BASE_CHANNEL_TOPIC}\n(Threshold: {threshold})"
self.mod_log.ignore(Event.guild_channel_update, Channels.defcon)
scheduling.create_task(self.channel.edit(topic=new_topic))
@@ -256,11 +255,11 @@ class Defcon(Cog):
expiry_message = ""
if expiry:
activity_duration = relativedelta(expiry, arrow.utcnow().datetime)
- expiry_message = f" for the next {humanize_delta(activity_duration, max_units=2)}"
+ expiry_message = f" for the next {time.humanize_delta(activity_duration, max_units=2)}"
if self.threshold:
channel_message = (
- f"updated; accounts must be {humanize_delta(self.threshold)} "
+ f"updated; accounts must be {time.humanize_delta(self.threshold)} "
f"old to join the server{expiry_message}"
)
else:
@@ -290,7 +289,7 @@ class Defcon(Cog):
def _log_threshold_stat(self, threshold: relativedelta) -> None:
"""Adds the threshold to the bot stats in days."""
- threshold_days = relativedelta_to_timedelta(threshold).total_seconds() / SECONDS_IN_DAY
+ threshold_days = time.relativedelta_to_timedelta(threshold).total_seconds() / SECONDS_IN_DAY
self.bot.stats.gauge("defcon.threshold", threshold_days)
async def _send_defcon_log(self, action: Action, actor: User) -> None:
@@ -298,7 +297,7 @@ class Defcon(Cog):
info = action.value
log_msg: str = (
f"**Staffer:** {actor.mention} {actor} (`{actor.id}`)\n"
- f"{info.template.format(threshold=(humanize_delta(self.threshold) if self.threshold else '-'))}"
+ f"{info.template.format(threshold=(time.humanize_delta(self.threshold) if self.threshold else '-'))}"
)
status_msg = f"DEFCON {action.name.lower()}"
@@ -317,7 +316,7 @@ class Defcon(Cog):
@tasks.loop(hours=1)
async def defcon_notifier(self) -> None:
"""Routinely notify moderators that DEFCON is active."""
- await self.channel.send(f"Defcon is on and is set to {humanize_delta(self.threshold)}.")
+ await self.channel.send(f"Defcon is on and is set to {time.humanize_delta(self.threshold)}.")
def cog_unload(self) -> None:
"""Cancel the notifer and threshold removal tasks when the cog unloads."""
diff --git a/bot/exts/moderation/infraction/management.py b/bot/exts/moderation/infraction/management.py
index 9649ff852..fb5af9eaa 100644
--- a/bot/exts/moderation/infraction/management.py
+++ b/bot/exts/moderation/infraction/management.py
@@ -20,7 +20,6 @@ from bot.pagination import LinePaginator
from bot.utils import messages, time
from bot.utils.channel import is_mod_channel
from bot.utils.members import get_or_fetch_member
-from bot.utils.time import humanize_delta, until_expiration
log = get_logger(__name__)
@@ -183,8 +182,8 @@ class ModManagement(commands.Cog):
self.infractions_cog.schedule_expiration(new_infraction)
log_text += f"""
- Previous expiry: {until_expiration(infraction['expires_at']) or "Permanent"}
- New expiry: {until_expiration(new_infraction['expires_at']) or "Permanent"}
+ Previous expiry: {time.until_expiration(infraction['expires_at']) or "Permanent"}
+ New expiry: {time.until_expiration(new_infraction['expires_at']) or "Permanent"}
""".rstrip()
changes = ' & '.join(confirm_messages)
@@ -377,7 +376,7 @@ class ModManagement(commands.Cog):
timezone.utc
)
date_to = dateutil.parser.isoparse(expires_at)
- duration = humanize_delta(relativedelta(date_to, date_from))
+ duration = time.humanize_delta(relativedelta(date_to, date_from))
# Format `dm_sent`
if dm_sent is None:
diff --git a/bot/exts/moderation/infraction/superstarify.py b/bot/exts/moderation/infraction/superstarify.py
index 08c92b8f3..2e272dbb0 100644
--- a/bot/exts/moderation/infraction/superstarify.py
+++ b/bot/exts/moderation/infraction/superstarify.py
@@ -14,9 +14,9 @@ from bot.converters import Duration, Expiry
from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.infraction._scheduler import InfractionScheduler
from bot.log import get_logger
+from bot.utils import time
from bot.utils.members import get_or_fetch_member
from bot.utils.messages import format_user
-from bot.utils.time import format_infraction
log = get_logger(__name__)
NICKNAME_POLICY_URL = "https://pythondiscord.com/pages/rules/#nickname-policy"
@@ -73,7 +73,7 @@ class Superstarify(InfractionScheduler, Cog):
notified = await _utils.notify_infraction(
user=after,
infr_type="Superstarify",
- expires_at=format_infraction(infraction["expires_at"]),
+ expires_at=time.format_infraction(infraction["expires_at"]),
reason=(
"You have tried to change your nickname on the **Python Discord** server "
f"from **{before.display_name}** to **{after.display_name}**, but as you "
@@ -150,7 +150,7 @@ class Superstarify(InfractionScheduler, Cog):
id_ = infraction["id"]
forced_nick = self.get_nick(id_, member.id)
- expiry_str = format_infraction(infraction["expires_at"])
+ expiry_str = time.format_infraction(infraction["expires_at"])
# Apply the infraction
async def action() -> None:
diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py
index fc9204998..d5e209d81 100644
--- a/bot/exts/moderation/modlog.py
+++ b/bot/exts/moderation/modlog.py
@@ -16,8 +16,8 @@ from discord.utils import escape_markdown
from bot.bot import Bot
from bot.constants import Categories, Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, Roles, URLs
from bot.log import get_logger
+from bot.utils import time
from bot.utils.messages import format_user
-from bot.utils.time import humanize_delta
log = get_logger(__name__)
@@ -407,7 +407,7 @@ class ModLog(Cog, name="ModLog"):
now = datetime.now(timezone.utc)
difference = abs(relativedelta(now, member.created_at))
- message = format_user(member) + "\n\n**Account age:** " + humanize_delta(difference)
+ message = format_user(member) + "\n\n**Account age:** " + time.humanize_delta(difference)
if difference.days < 1 and difference.months < 1 and difference.years < 1: # New user account!
message = f"{Emojis.new} {message}"
@@ -713,7 +713,7 @@ class ModLog(Cog, name="ModLog"):
# datetime as the baseline and create a human-readable delta between this edit event
# and the last time the message was edited
timestamp = msg_before.edited_at
- delta = humanize_delta(relativedelta(msg_after.edited_at, msg_before.edited_at))
+ delta = time.humanize_delta(relativedelta(msg_after.edited_at, msg_before.edited_at))
footer = f"Last edited {delta} ago"
else:
# Message was not previously edited, use the created_at datetime as the baseline, no
diff --git a/bot/exts/moderation/modpings.py b/bot/exts/moderation/modpings.py
index 20a8c39d7..b5cd29b12 100644
--- a/bot/exts/moderation/modpings.py
+++ b/bot/exts/moderation/modpings.py
@@ -11,9 +11,8 @@ from bot.bot import Bot
from bot.constants import Colours, Emojis, Guild, Icons, MODERATION_ROLES, Roles
from bot.converters import Expiry
from bot.log import get_logger
-from bot.utils import scheduling
+from bot.utils import scheduling, time
from bot.utils.scheduling import Scheduler
-from bot.utils.time import TimestampFormats, discord_timestamp
log = get_logger(__name__)
@@ -233,8 +232,8 @@ class ModPings(Cog):
await ctx.send(
f"{Emojis.ok_hand} {ctx.author.mention} Scheduled mod pings from "
- f"{discord_timestamp(start, TimestampFormats.TIME)} to "
- f"{discord_timestamp(end, TimestampFormats.TIME)}!"
+ f"{time.discord_timestamp(start, time.TimestampFormats.TIME)} to "
+ f"{time.discord_timestamp(end, time.TimestampFormats.TIME)}!"
)
@schedule_modpings.command(name='delete', aliases=('del', 'd'))
diff --git a/bot/exts/moderation/stream.py b/bot/exts/moderation/stream.py
index 99bbd8721..5a7b12295 100644
--- a/bot/exts/moderation/stream.py
+++ b/bot/exts/moderation/stream.py
@@ -14,9 +14,8 @@ from bot.constants import (
from bot.converters import Expiry
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling
+from bot.utils import scheduling, time
from bot.utils.members import get_or_fetch_member
-from bot.utils.time import discord_timestamp, format_infraction_with_duration
log = get_logger(__name__)
@@ -131,10 +130,10 @@ class Stream(commands.Cog):
await member.add_roles(discord.Object(Roles.video), reason="Temporary streaming access granted")
- await ctx.send(f"{Emojis.check_mark} {member.mention} can now stream until {discord_timestamp(duration)}.")
+ await ctx.send(f"{Emojis.check_mark} {member.mention} can now stream until {time.discord_timestamp(duration)}.")
# Convert here for nicer logging
- revoke_time = format_infraction_with_duration(str(duration))
+ revoke_time = time.format_infraction_with_duration(str(duration))
log.debug(f"Successfully gave {member} ({member.id}) permission to stream until {revoke_time}.")
@commands.command(aliases=("pstream",))
diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py
index 34d445912..106483527 100644
--- a/bot/exts/moderation/watchchannels/_watchchannel.py
+++ b/bot/exts/moderation/watchchannels/_watchchannel.py
@@ -18,9 +18,8 @@ from bot.exts.filters.webhook_remover import WEBHOOK_URL_RE
from bot.exts.moderation.modlog import ModLog
from bot.log import CustomLogger, get_logger
from bot.pagination import LinePaginator
-from bot.utils import CogABCMeta, messages, scheduling
+from bot.utils import CogABCMeta, messages, scheduling, time
from bot.utils.members import get_or_fetch_member
-from bot.utils.time import get_time_delta
log = get_logger(__name__)
@@ -286,7 +285,7 @@ class WatchChannel(metaclass=CogABCMeta):
actor = actor.display_name if actor else self.watched_users[user_id]['actor']
inserted_at = self.watched_users[user_id]['inserted_at']
- time_delta = get_time_delta(inserted_at)
+ time_delta = time.get_time_delta(inserted_at)
reason = self.watched_users[user_id]['reason']
@@ -360,7 +359,7 @@ class WatchChannel(metaclass=CogABCMeta):
if member:
line += f" ({member.name}#{member.discriminator})"
inserted_at = user_data['inserted_at']
- line += f", added {get_time_delta(inserted_at)}"
+ line += f", added {time.get_time_delta(inserted_at)}"
if not member: # Cross off users who left the server.
line = f"~~{line}~~"
list_data["info"][user_id] = line
diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py
index 8fa0be5b1..80274eaea 100644
--- a/bot/exts/recruitment/talentpool/_cog.py
+++ b/bot/exts/recruitment/talentpool/_cog.py
@@ -17,7 +17,6 @@ from bot.log import get_logger
from bot.pagination import LinePaginator
from bot.utils import scheduling, time
from bot.utils.members import get_or_fetch_member
-from bot.utils.time import get_time_delta
AUTOREVIEW_ENABLED_KEY = "autoreview_enabled"
REASON_MAX_CHARS = 1000
@@ -181,7 +180,7 @@ class TalentPool(Cog, name="Talentpool"):
if member:
line += f" ({member.name}#{member.discriminator})"
inserted_at = user_data['inserted_at']
- line += f", added {get_time_delta(inserted_at)}"
+ line += f", added {time.get_time_delta(inserted_at)}"
if not member: # Cross off users who left the server.
line = f"~~{line}~~"
if user_data['reviewed']:
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index 0e7194892..bbffbe6e3 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -17,10 +17,10 @@ from bot.api import ResponseCodeError
from bot.bot import Bot
from bot.constants import Channels, Colours, Emojis, Guild, Roles
from bot.log import get_logger
+from bot.utils import time
from bot.utils.members import get_or_fetch_member
from bot.utils.messages import count_unique_users_reaction, pin_no_system_message
from bot.utils.scheduling import Scheduler
-from bot.utils.time import get_time_delta, time_since
if typing.TYPE_CHECKING:
from bot.exts.recruitment.talentpool._cog import TalentPool
@@ -273,7 +273,7 @@ class Reviewer:
last_channel = user_activity["top_channel_activity"][-1]
channels += f", and {last_channel[1]} in {last_channel[0]}"
- joined_at_formatted = time_since(member.joined_at)
+ joined_at_formatted = time.time_since(member.joined_at)
review = (
f"{member.name} joined the server **{joined_at_formatted}**"
f" and has **{messages} messages**{channels}."
@@ -321,7 +321,7 @@ class Reviewer:
infractions += ", with the last infraction issued "
# Infractions were ordered by time since insertion descending.
- infractions += get_time_delta(infraction_list[0]['inserted_at'])
+ infractions += time.get_time_delta(infraction_list[0]['inserted_at'])
return f"They have {infractions}."
@@ -365,7 +365,7 @@ class Reviewer:
nomination_times = f"{num_entries} times" if num_entries > 1 else "once"
rejection_times = f"{len(history)} times" if len(history) > 1 else "once"
- end_time = time_since(isoparse(history[0]['ended_at']))
+ end_time = time.time_since(isoparse(history[0]['ended_at']))
review = (
f"They were nominated **{nomination_times}** before"
diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py
index 90677b2dd..dc7782727 100644
--- a/bot/exts/utils/reminders.py
+++ b/bot/exts/utils/reminders.py
@@ -13,13 +13,12 @@ from bot.constants import Guild, Icons, MODERATION_ROLES, POSITIVE_REPLIES, Role
from bot.converters import Duration, UnambiguousUser
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling
+from bot.utils import scheduling, time
from bot.utils.checks import has_any_role_check, has_no_roles_check
from bot.utils.lock import lock_arg
from bot.utils.members import get_or_fetch_member
from bot.utils.messages import send_denial
from bot.utils.scheduling import Scheduler
-from bot.utils.time import TimestampFormats, discord_timestamp
log = get_logger(__name__)
@@ -310,7 +309,8 @@ class Reminders(Cog):
}
)
- mention_string = f"Your reminder will arrive on {discord_timestamp(expiration, TimestampFormats.DAY_TIME)}"
+ formatted_time = time.discord_timestamp(expiration, time.TimestampFormats.DAY_TIME)
+ mention_string = f"Your reminder will arrive on {formatted_time}"
if mentions:
mention_string += f" and will mention {len(mentions)} other(s)"
@@ -348,7 +348,7 @@ class Reminders(Cog):
for content, remind_at, id_, mentions in reminders:
# Parse and humanize the time, make it pretty :D
remind_datetime = isoparse(remind_at)
- time = discord_timestamp(remind_datetime, TimestampFormats.RELATIVE)
+ expiry = time.discord_timestamp(remind_datetime, time.TimestampFormats.RELATIVE)
mentions = ", ".join([
# Both Role and User objects have the `name` attribute
@@ -357,7 +357,7 @@ class Reminders(Cog):
mention_string = f"\n**Mentions:** {mentions}" if mentions else ""
text = textwrap.dedent(f"""
- **Reminder #{id_}:** *expires {time}* (ID: {id_}){mention_string}
+ **Reminder #{id_}:** *expires {expiry}* (ID: {id_}){mention_string}
{content}
""").strip()
diff --git a/bot/exts/utils/utils.py b/bot/exts/utils/utils.py
index f76eea516..00fa7a388 100644
--- a/bot/exts/utils/utils.py
+++ b/bot/exts/utils/utils.py
@@ -13,8 +13,7 @@ from bot.converters import Snowflake
from bot.decorators import in_whitelist
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import messages
-from bot.utils.time import time_since
+from bot.utils import messages, time
log = get_logger(__name__)
@@ -173,7 +172,7 @@ class Utils(Cog):
lines = []
for snowflake in snowflakes:
created_at = snowflake_time(snowflake)
- lines.append(f"**{snowflake}**\nCreated at {created_at} ({time_since(created_at)}).")
+ lines.append(f"**{snowflake}**\nCreated at {created_at} ({time.time_since(created_at)}).")
await LinePaginator.paginate(
lines,