Files
discord.js/packages/structures/src/messages/Message.ts
Qjuh 7a7fecbe3c fix(structures): correctly check if flags are present (#11404)
* fix(structures): correctly check if flags are present

* chore: isFieldSet type guard

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2026-01-27 16:47:56 +00:00

180 lines
4.6 KiB
TypeScript

import { DiscordSnowflake } from '@sapphire/snowflake';
import type { APIMessage, MessageFlags } from 'discord-api-types/v10';
import { Structure } from '../Structure.js';
import { MessageFlagsBitField } from '../bitfields/MessageFlagsBitField.js';
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
import { kData, kEditedTimestamp } from '../utils/symbols.js';
import { isFieldSet, isIdSet } from '../utils/type-guards.js';
import type { Partialize } from '../utils/types.js';
// TODO: missing substructures: application
/**
* Represents a message on Discord.
*
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
* @remarks has substructures `Message`, `Channel`, `MessageActivity`, `MessageCall`, `MessageReference`, `Attachment`, `Application`, `ChannelMention`, `Reaction`, `Poll`, `ResolvedInteractionData`, `RoleSubscriptionData`, `Sticker`, all the different `Component`s, ... which need to be instantiated and stored by an extending class using it
*/
export class Message<Omitted extends keyof APIMessage | '' = 'edited_timestamp' | 'timestamp'> extends Structure<
APIMessage,
Omitted
> {
/**
* The template used for removing data from the raw data stored for each Message
*/
public static override DataTemplate: Partial<APIMessage> = {
set timestamp(_: string) {},
set edited_timestamp(_: string) {},
};
protected [kEditedTimestamp]: number | null = null;
/**
* @param data - The raw data received from the API for the message
*/
public constructor(data: Partialize<APIMessage, Omitted>) {
super(data);
this.optimizeData(data);
}
/**
* {@inheritDoc Structure.optimizeData}
*
* @internal
*/
protected override optimizeData(data: Partial<APIMessage>) {
if (data.edited_timestamp) {
this[kEditedTimestamp] = Date.parse(data.edited_timestamp);
}
}
/**
* The message's id
*/
public get id() {
return this[kData].id;
}
/**
* The id of the interaction's application, if this message is a reply to an interaction
*/
public get applicationId() {
return this[kData].application_id;
}
/**
* The channel's id this message was sent in
*/
public get channelId() {
return this[kData].channel_id;
}
/**
* The timestamp this message was created at
*/
public get createdTimestamp() {
return isIdSet(this.id) ? DiscordSnowflake.timestampFrom(this.id) : null;
}
/**
* The time the message was created at
*/
public get createdAt() {
const createdTimestamp = this.createdTimestamp;
return createdTimestamp ? new Date(createdTimestamp) : null;
}
/**
* The content of the message
*/
public get content() {
return this[kData].content;
}
/**
* The timestamp this message was last edited at, or `null` if it never was edited
*/
public get editedTimestamp() {
return this[kEditedTimestamp];
}
/**
* The time the message was last edited at, or `null` if it never was edited
*/
public get editedAt() {
const editedTimestamp = this.editedTimestamp;
return editedTimestamp ? new Date(editedTimestamp) : null;
}
/**
* The flags of this message as a bit field
*/
public get flags() {
return isFieldSet(this[kData], 'flags', 'number')
? new MessageFlagsBitField(this[kData].flags as MessageFlags)
: null;
}
/**
* The nonce used when sending this message.
*
* @remarks This is only present in MESSAGE_CREATE event, if a nonce was provided when sending
*/
public get nonce() {
return this[kData].nonce;
}
/**
* Whether this message is pinned in its channel
*/
public get pinned() {
return this[kData].pinned;
}
/**
* A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread
* It can be used to estimate the relative position of the message in a thread in company with `totalMessageSent` on parent thread
*/
public get position() {
return this[kData].position;
}
/**
* Whether this message was a TTS message
*/
public get tts() {
return this[kData].tts;
}
/**
* The type of message
*/
public get type() {
return this[kData].type;
}
/**
* If the message is generated by a webhook, this is the webhook's id
*/
public get webhookId() {
return this[kData].webhook_id;
}
/**
* {@inheritDoc Structure.toJSON}
*/
public override toJSON() {
const clone = super.toJSON();
if (this[kEditedTimestamp]) {
clone.edited_timestamp = dateToDiscordISOTimestamp(new Date(this[kEditedTimestamp]));
}
const createdAt = this.createdAt;
if (createdAt) {
clone.timestamp = dateToDiscordISOTimestamp(createdAt);
}
return clone;
}
}