const msgs = require("../../helper/messages.js");
const guser = require("../../helper/members.js");
const User = require("./user/user.js");
const Reaction = require("./reaction.js");
/**
* Represents a message
*/
class Message {
/**
* Creates a new message object
* @param {Object} messageData The message data
* @param {Object} client The client object
* @returns {Message}
* @example
* const { Message } = require('guilded.js');
* const message = new Message(messageData, client);
*/
constructor(messageData, client) {
/**
* Client object
* @type {Client}
* @readonly
*/
this.client = client;
/**
* The message id
* @type {String}
* @readonly
*/
this.id = messageData.id;
/**
* The content of the message
* @type {String}
* @readonly
*/
this.content = messageData.content ?? "";
/**
* The attachments of the message
* @type {Array}
* @readonly
*/
this.attachments = [];
/**
* The message channel id
* @type {Channel}
* @readonly
*/
this.channel = {
/**
* The channel id
* @type {String}
* @readonly
*/
id: messageData.channelId,
/**
* Send a message to the current channel
* @param {String | Object} content The content of the message, this can be a string or an object
* @param {String} content.content The content of the message
* @param {Boolean} content.isPrivate Whether the message is private or not
* @param {Boolean} content.isSilent Whether the message is silent or not
* @param {Array} content.replyMessageIds The message ids to reply to
* @param {Array} content.embeds The embeds to send
* @param {Array} content.attachments The attachments to send
* @returns {Promise} The message object
* @private
*/
send: (content) => {
switch (typeof content) {
case "string":
return msgs.sendMessage(
{
content,
channelId: messageData.channelId,
isPrivate: false,
isSilent: false,
client: this.client,
}
);
case "object":
return msgs.sendMessage(
{
content: content.content,
channelId: messageData.channelId,
isPrivate: !!content.isPrivate,
isSilent: !!content.isSilent,
replyMessageIds: content.replyMessageIds
? content.replyMessageIds
: null,
embeds: content.embeds ? content.embeds : null,
attachments: content.attachments ? content.attachments : null,
client: this.client,
}
);
}
},
};
/**
* The server Object
* @type {Server | null}
* @readonly
*/
this.serverId = messageData.serverId ?? null;
// Check if message have attachments
if (this.content.includes("![")) {
const attachments = this.content.match(/!\[(.*?)\]\((.*?)\)/g);
if (attachments) {
for (let i = 0; i < attachments.length; i++) {
const attachment = attachments[i];
const attachmentUrl = attachment.match(/\((.*?)\)/)[1];
this.attachments[i] = attachmentUrl;
}
}
}
if (this.attachments && this.attachments.length > 0) {
for (let i = 0; i < this.attachments.length; i++) {
this.attachments[i] = this.attachments[i]
.replace("![](", "")
.slice(0, -1);
}
}
// Convert the message to a better format
// Example of message: "Hello\n" + "\n" + "Goodbye\n" + "![](https://s3-us-west-2.amazonaws.com/www.guilded.gg/ContentMedia/42237368ad7882d89108acccf777c2ac-Full.webp?w=100&h=100)"
// To: "Hello \n \n Goodbye \n ![](https://s3-us-west-2.amazonaws.com/www.guilded.gg/ContentMedia/42237368ad7882d89108acccf777c2ac-Full.webp?w=100&h=100)"
this.content = this.content
.replace(/\n/g, " \n ")
.replace(/\n {2}\n/g, "\n \n");
// Delete all files of the content
// Example of message: "Hello ![](https://s3-us-west-2.amazonaws.com/www.guilded.gg/ContentMedia/42237368ad7882d89108acccf777c2ac-Full.webp?w=100&h=100)"
// To: "Hello"
this.content = this.content.replace(/!\[(.*?)\]\((.*?)\)/g, "");
if (messageData.mentions) {
this.mentions = {};
if (messageData.mentions.users) {
this.mentions.users = new Map();
for (let i = 0; i < messageData.mentions.users.length; i++) {
const mention = messageData.mentions.users[i];
mention.server = {
id: messageData.serverId,
};
const user = new User(mention);
this.mentions.users.set(user.id, user);
}
this.mentions.users.first = () => {
return this.mentions.users.values().next().value;
};
}
if (messageData.mentions.roles) {
this.mentions.roles = [];
for (let i = 0; i < messageData.mentions.roles.length; i++) {
this.mentions.roles.push(messageData.mentions.roles[i].id);
}
}
}
/**
* Check if the message has replies
* @type {Boolean}
* @readonly
*/
this.hasReplies = Boolean(messageData.replyMessageIds);
/**
* The message replies
* @type {Array<String>}
* @readonly
*/
this.replyMessageIds = messageData.replyMessageIds ?? null;
/**
* Check if the message is private
* @type {Boolean}
* @readonly
*/
this.private = messageData.isPrivate ?? false;
/**
* The created date of the message
* @type {Date}
* @readonly
*/
this.createdAt = new Date(messageData.createdAt);
/**
* The author of the message
* @type {User}
* @readonly
*/
this.author = null;
if (global.cache.users.has(messageData.createdBy)) {
// Check properties and update them
const newAuthor = new User(
{
id: messageData.createdBy,
serverId: messageData.serverId,
},
client
);
const oldAuthor = global.cache.users.get(messageData.createdBy);
for (const property in newAuthor) {
if (
newAuthor[property] !== oldAuthor[property] &&
property !== null &&
property !== undefined &&
property !== NaN
) {
oldAuthor[property] = newAuthor[property];
}
}
this.author = oldAuthor;
global.cache.users.set(messageData.createdBy, oldAuthor);
} else {
this.author = new User(
{
id: messageData.createdBy,
serverId: messageData.serverId,
},
client
);
global.cache.users.set(this.author.id, this.author);
}
/**
* The webhook id of the message
* @type {String | null}
* @readonly
*/
this.webhookId = messageData.createdByWebhookId ?? null;
/**
* The updated date (if the message was edited)
* @type {Date | null}
* @readonly
*/
this.editedAt = messageData.updatedAt
? new Date(messageData.updatedAt)
: null;
/**
* All data of the message (for debugging)
* @type {Object}
* @readonly
*/
this.raw = messageData;
/**
* Send a message to the current channel to the user who sent the message
* @param {String | Object} content The content of the message, this can be a string or an object
* @param {String} content.content The content of the message
* @param {Boolean} content.isPrivate Whether the message is private or not
* @param {Boolean} content.isSilent Whether the message is silent or not
* @param {Array} content.replyMessageIds The message ids to reply to
* @param {Array} content.embeds The embeds to send
* @param {Array} content.attachments The attachments to send
* @returns {Message} The message object
* @example
* message.reply("Hello");
*/
this.reply = (content) => {
switch (typeof content) {
case "string":
return msgs.sendMessage(
{
content,
channelId: messageData.channelId,
isPrivate: false,
isSilent: false,
replyMessageIds: [this.id],
client: this.client,
}
);
case "object":
return msgs.sendMessage(
{
content: content.content,
channelId: messageData.channelId,
isPrivate: !!content.isPrivate,
isSilent: !!content.isSilent,
replyMessageIds: [this.id],
embeds: content.embeds ? content.embeds : null,
attachments: content.attachments ? content.attachments : null,
client: this.client,
}
);
}
};
/**
* Edit the current message
* @param {String | Object} content The content of the message, this can be a string or an object
* @param {String} content.content The content of the message
* @param {Boolean} content.isPrivate Whether the message is private or not
* @param {Boolean} content.isSilent Whether the message is silent or not
* @param {Array} content.replyMessageIds The message ids to reply to
* @param {Array} content.embeds The embeds to send
* @param {Array} content.attachments The attachments to send
* @returns {Message} The message object
* @example
* message.edit("Hello world!");
*/
this.edit = (content) => {
switch (typeof content) {
case "string":
return msgs.editMessage(
{
id: this.id,
content,
channelId: messageData.channelId,
isPrivate: false,
isSilent: false,
client: this.client,
}
);
case "object":
return msgs.editMessage(
{
id: this.id,
content: content.content,
channelId: messageData.channelId,
isPrivate: !!content.isPrivate,
isSilent: !!content.isSilent,
replyMessageIds: [this.id],
embeds: content.embeds ? content.embeds : null,
attachments: content.attachments ? content.attachments : null,
client: this.client,
}
);
}
};
/**
* Delete the current message
* @param {Object} options The options for the delete
* @param {Number} options.timeout The timeout for the delete
* @returns {Message} The message object
* @example
* message.delete();
* message.delete({ timeout: 5000 });
*/
this.delete = (object) => {
return msgs.deleteMessage(
{
id: this.id,
channelId: messageData.channelId,
timeout: object.timeout ? object.timeout : 0,
client: this.client,
}
);
};
/**
* Add a reaction to the current message
* @param {Number} emoji The emoji to react with
* @returns {Reaction} The reaction object
* @example
* message.react(90001164);
*/
this.react = (emoji) => {
if (typeof emoji !== "number") emoji = 90001164;
return msgs.reactMessage(
{
id: this.id,
channelId: messageData.channelId,
emojiId: emoji ?? 90001164,
client: this.client,
}
);
};
/**
* Get the user object of the author of the message or the user you want to get
* @param {string} userId optional - The user id of the user you want to get info from
* @returns {User} - The user object
* @example
* message.getUser();
* message.getUser("123456789");
*/
this.getUser = async (userId) => {
// Check if the user id is in cache
if (client.cache.users.get(userId ?? this.author.id)) {
return client.cache.users.get(userId ?? this.author.id);
} else {
// If not, get the user from the api
const newUser = await guser.getUser(
{
id: userId ?? this.author.id,
serverId: messageData.serverId,
client: client,
}
);
// Add the user to the cache
client.cache.users.set(userId, newUser);
return newUser;
}
};
/**
* Get the member object of the author of the message or the member you want to get
* @param {string} userId optional - The user id of the member you want to get info from
* @returns {Member} - The member object
* @example
* message.getMember();
* message.getMember("123456789");
*/
this.getMember = async (userId) => {
const member = await guser.getMember(
{
id: userId ?? this.author.id,
serverId: messageData.serverId,
client: client,
}
);
return member;
};
}
}
module.exports = Message;