Skip to content

Extending the Client with Uptime

Tracking Ready Time (or Uptime) in Your Seyfert Bot

Adding a readyAt property to your bot’s client is a simple way to track when your bot became ready.

Adding readyAt to the Client

To begin, extend the UsingClient interface by adding the readyAt property. This ensures that the property is type-checked across your project.

Step 1: Extend the Interface

Update your Declare Module file with the following code:

src/events/botReady.ts
declare module "seyfert" {
interface
interface UsingClient
UsingClient
extends
type ParseClient<T extends BaseClient> = T
ParseClient
<
class Client<Ready extends boolean = boolean>
Client
<true>> {
// Other custom properties...
UsingClient.readyAt?: Date
readyAt
?:
interface Date

Enables basic storage and retrieval of dates and times.

Date
; // Tracks when the bot became ready (uptime)
}
// Other declarations...
}

Initializing readyAt

The readyAt property should be set when the bot is ready. This can be done in the ready event handler.

Step 2: Set readyAt in the Ready Event

Add the following code to your botReady event:

src/events/botReady.ts
import {
function createEvent<E extends ClientNameEvents | CustomEventsKeys>(data: {
data: {
name: E;
once?: boolean;
};
run: (...args: ResolveEventParams<E>) => any;
}): {
data: {
name: E;
once?: boolean;
};
run: (...args: ResolveEventParams<E>) => any;
}

Creates an event with the specified data and run function.

@paramdata - The event data.

@returnsThe created event.

@example const myEvent = createEvent({ data: { name: 'ready', once: true }, run: (user, client, shard) => { client.logger.info(Start ${user.username} on shard #${shard}); } });

createEvent
} from "seyfert";
export default
createEvent<"botReady">(data: {
data: {
name: "botReady";
once?: boolean;
};
run: (args_0: ClientUser, args_1: UsingClient, args_2: number) => any;
}): {
data: {
name: "botReady";
once?: boolean;
};
run: (args_0: ClientUser, args_1: UsingClient, args_2: number) => any;
}

Creates an event with the specified data and run function.

@paramdata - The event data.

@returnsThe created event.

@example const myEvent = createEvent({ data: { name: 'ready', once: true }, run: (user, client, shard) => { client.logger.info(Start ${user.username} on shard #${shard}); } });

createEvent
({
data: {
name: "botReady";
once?: boolean;
}
data
: {
once?: boolean
once
: true,
name: "botReady"
name
: "botReady" },
run: (args_0: ClientUser, args_1: UsingClient, args_2: number) => any
run
(
user: ClientUser
user
,
client: UsingClient
client
) {
// Set the readyAt to the current date and time
client: UsingClient
client
.
UsingClient.readyAt?: Date
readyAt
= new
var Date: DateConstructor
new () => Date (+3 overloads)
Date
();
// Other startup logic...
},
});

This ensures that client.readyAt is initialized to the exact moment the bot becomes ready.

For more information on how to declare events, visit Listening to Events.

Using the readyAt Property

You can display the readyAt time using a timestamp formatter. This is especially useful for commands or logs.

Step 3: Format readyAt as a Timestamp

To format and display the readyAt value, use the Formatter.timestamp method:

Formatter.timestamp(client.readyAt);

This will return a formatted timestamp that you can include in your messages or logs.

Example Command

Here’s an example command to display the bot’s ready time:

src/commands/uptime.ts
import {
function Declare(declare: CommandDeclareOptions): <T extends {
new (...args: any[]): object;
}>(target: T) => {
new (...args: any[]): {
name: string;
nsfw: boolean | undefined;
props: ExtraProps | undefined;
contexts: InteractionContextType[];
integrationTypes: ApplicationIntegrationType[];
defaultMemberPermissions: bigint | undefined;
botPermissions: bigint | undefined;
description: string;
type: ApplicationCommandType;
guildId?: string[];
ignore?: IgnoreCommand;
aliases?: string[];
handler?: EntryPointCommandHandlerType;
};
} & T
Declare
, type
class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext
,
class Command
Command
,
const Formatter: {
codeBlock(content: string, language?: string): string;
inlineCode(content: string): `\`${string}\``;
bold(content: string): `**${string}**`;
italic(content: string): `*${string}*`;
... 15 more ...;
generateOAuth2URL(applicationId: string, { scopes, permissions, disableGuildSelect }: OAuth2URLOptions): string;
}

Represents a formatter utility for formatting content.

Formatter
} from "seyfert";
@
function Declare(declare: CommandDeclareOptions): <T extends {
new (...args: any[]): object;
}>(target: T) => {
new (...args: any[]): {
name: string;
nsfw: boolean | undefined;
props: ExtraProps | undefined;
contexts: InteractionContextType[];
integrationTypes: ApplicationIntegrationType[];
defaultMemberPermissions: bigint | undefined;
botPermissions: bigint | undefined;
description: string;
type: ApplicationCommandType;
guildId?: string[];
ignore?: IgnoreCommand;
aliases?: string[];
handler?: EntryPointCommandHandlerType;
};
} & T
Declare
({
name: string
name
: 'uptime',
description: string
description
: 'Show the bot’s ready time',
})
export default class
class ReadyAtCommand
ReadyAtCommand
extends
class Command
Command
{
async
ReadyAtCommand.run(ctx: CommandContext): Promise<void>
run
(
ctx: CommandContext<{}, never>
ctx
:
class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext
) {
const
const readyAtFormatted: `<t:${number}:t>` | `<t:${number}:T>` | `<t:${number}:d>` | `<t:${number}:D>` | `<t:${number}:f>` | `<t:${number}:F>` | `<t:${number}:R>`
readyAtFormatted
=
const Formatter: {
codeBlock(content: string, language?: string): string;
inlineCode(content: string): `\`${string}\``;
bold(content: string): `**${string}**`;
italic(content: string): `*${string}*`;
... 15 more ...;
generateOAuth2URL(applicationId: string, { scopes, permissions, disableGuildSelect }: OAuth2URLOptions): string;
}

Represents a formatter utility for formatting content.

Formatter
.
function timestamp(timestamp: Date, style?: TimestampStyle): `<t:${number}:t>` | `<t:${number}:T>` | `<t:${number}:d>` | `<t:${number}:D>` | `<t:${number}:f>` | `<t:${number}:F>` | `<t:${number}:R>`

Formats the given timestamp into discord unix timestamp format.

@paramtimestamp The timestamp to format.

@paramstyle The style of the timestamp. Defaults to 't'.

@returnsThe formatted timestamp.

timestamp
(
ctx: CommandContext<{}, never>
ctx
.
CommandContext<{}, never>.client: UsingClient
client
.
UsingClient.readyAt: Date
readyAt
);
await
ctx: CommandContext<{}, never>
ctx
.
CommandContext<{}, never>.editOrReply<false>(body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest, withResponse?: false | undefined): Promise<...>
editOrReply
({
content: string
content
: `The bot became ready on ${
const readyAtFormatted: `<t:${number}:t>` | `<t:${number}:T>` | `<t:${number}:d>` | `<t:${number}:D>` | `<t:${number}:f>` | `<t:${number}:F>` | `<t:${number}:R>`
readyAtFormatted
}.` });
}
}

Conclusion

By extending your client with a readyAt property, you gain the ability to track and display your bot’s ready time (or “uptime”) in a user-friendly way. This feature not only improves monitoring but also enhances the overall user experience.