From 58f1817ed500bebde7815df1d5444656a0c17d3d Mon Sep 17 00:00:00 2001 From: Quentin Nicolini Date: Fri, 8 Oct 2021 23:12:35 +0200 Subject: [PATCH] Message structure -> Message Transformer --- src/transformers/message.ts | 390 +++++------------------------------- src/transformers/role.ts | 13 +- 2 files changed, 54 insertions(+), 349 deletions(-) diff --git a/src/transformers/message.ts b/src/transformers/message.ts index 464a1f038..4a28d93cb 100644 --- a/src/transformers/message.ts +++ b/src/transformers/message.ts @@ -1,296 +1,58 @@ -import { eventHandlers } from "../bot.ts"; -import { cache, cacheHandlers } from "../cache.ts"; -import { sendDirectMessage } from "../helpers/members/send_direct_message.ts"; -import { addReaction } from "../helpers/messages/add_reaction.ts"; -import { addReactions } from "../helpers/messages/add_reactions.ts"; -import { deleteMessage } from "../helpers/messages/delete_message.ts"; -import { editMessage } from "../helpers/messages/edit_message.ts"; -import { pinMessage } from "../helpers/messages/pin_message.ts"; -import { removeAllReactions } from "../helpers/messages/remove_all_reactions.ts"; -import { removeReaction } from "../helpers/messages/remove_reaction.ts"; -import { removeReactionEmoji } from "../helpers/messages/remove_reaction_emoji.ts"; -import { sendMessage } from "../helpers/messages/send_message.ts"; -import type { GuildMember } from "../types/members/guild_member.ts"; -import type { CreateMessage } from "../types/messages/create_message.ts"; -import type { EditMessage } from "../types/messages/edit_message.ts"; -import type { Message } from "../types/messages/message.ts"; -import { bigintToSnowflake, snowflakeToBigint } from "../util/bigint.ts"; +import { Bot } from "../bot.ts"; +import { Message } from "../types/messages/message.ts"; import { CHANNEL_MENTION_REGEX } from "../util/constants.ts"; -import { iconBigintToHash } from "../util/hash.ts"; -import { channelToThread, DiscordenoThread } from "../util/transformers/channel_to_thread.ts"; -import { createNewProp } from "../util/utils.ts"; -import { DiscordenoChannel } from "./channel.ts"; -import { DiscordenoGuild } from "./guild.ts"; -import { DiscordenoMember } from "./member.ts"; -import { DiscordenoRole } from "./role.ts"; -const MESSAGE_SNOWFLAKES = ["id", "channelId", "guildId", "webhookId", "applicationId"]; +export function transformMessage( + bot: Bot, + data: Message + ) { + return { + // UNTRANSFORMED STUFF HERE + content: data.content || "", + isBot: data.author.bot || false, + tag: `${data.author.username}#${data.author.discriminator.toString().padStart(4, "0")}`, + timestamp: Date.parse(data.timestamp), + editedTimestamp: data.editedTimestamp ? Date.parse(data.editedTimestamp) : undefined, + bitfield: + (data.tts ? 1n : 0n) | + (data.mentionEveryone ? 2n : 0n) | + (data.pinned ? 4n : 0n), + attachments: data.attachments, + embeds: data.embeds, + reactions: data.reactions, + type: data.type, + activity: data.activity, + application: data.application, + flags: data.flags, + interaction: data.interaction, + thread: data.thread, + components: data.components, + stickerItems: data.stickerItems, -const messageToggles = { - /** Whether this was a TTS message */ - tts: 1n, - /** Whether this message mentions everyone */ - mentionEveryone: 2n, - /** Whether this message is pinned */ - pinned: 4n, -}; - -const baseMessage: Partial = { - get channel() { - if (this.guildId) return cache.channels.get(this.channelId!); - return cache.channels.get(this.authorId!); - }, - get guild() { - if (!this.guildId) return undefined; - return cache.guilds.get(this.guildId); - }, - get member() { - if (!this.authorId) return undefined; - return cache.members.get(this.authorId); - }, - get guildMember() { - if (!this.guildId) return undefined; - return this.member?.guilds.get(this.guildId); - }, - get link() { - return `https://discord.com/channels/${this.guildId || "@me"}/${this.channelId}/${this.id}`; - }, - get mentionedRoles() { - return this.mentionedRoleIds?.map((id) => this.guild?.roles.get(id)) || []; - }, - get mentionedChannels() { - return this.mentionedChannelIds?.map((id) => cache.channels.get(id)) || []; - }, - get mentionedMembers() { - return this.mentionedUserIds?.map((id) => cache.members.get(id)) || []; - }, - - // METHODS - delete(reason, delayMilliseconds) { - return deleteMessage(this.channelId!, this.id!, reason, delayMilliseconds); - }, - edit(content) { - return editMessage(this.channelId!, this.id!, content); - }, - pin() { - return pinMessage(this.channelId!, this.id!); - }, - addReaction(reaction) { - return addReaction(this.channelId!, this.id!, reaction); - }, - addReactions(reactions, ordered) { - return addReactions(this.channelId!, this.id!, reactions, ordered); - }, - reply(content, mentionUser = true) { - const contentWithMention: CreateMessage = - typeof content === "string" - ? { - content, - allowedMentions: { - repliedUser: mentionUser, - }, - messageReference: { - messageId: bigintToSnowflake(this.id!), - failIfNotExists: false, - }, - } - : { - ...content, - allowedMentions: { - repliedUser: mentionUser, - ...(content.allowedMentions || {}), - }, - messageReference: { - messageId: bigintToSnowflake(this.id!), - failIfNotExists: content.messageReference?.failIfNotExists === true, - }, - }; - - if (this.guildId) return sendMessage(this.channelId!, contentWithMention); - return sendDirectMessage(this.authorId!, contentWithMention); - }, - send(content) { - if (this.guildId) return sendMessage(this.channelId!, content); - return sendDirectMessage(this.authorId!, content); - }, - async alert(content, timeout = 10, reason = "") { - if (this.guildId) { - return await sendMessage(this.channelId!, content).then((response) => { - response?.delete(reason, timeout * 1000).catch(console.error); - }); - } - - return await sendDirectMessage(this.authorId!, content).then((response) => { - response?.delete(reason, timeout * 1000).catch(console.error); - }); - }, - async alertReply(content, timeout = 10, reason = "") { - return await this.reply!(content).then((response) => response?.delete(reason, timeout * 1000).catch(console.error)); - }, - removeAllReactions() { - return removeAllReactions(this.channelId!, this.id!); - }, - removeReactionEmoji(reaction) { - return removeReactionEmoji(this.channelId!, this.id!, reaction); - }, - removeReaction(reaction, userId) { - return removeReaction(this.channelId!, this.id!, reaction, { userId }); - }, - get tts() { - return Boolean(this.bitfield! & messageToggles.tts); - }, - get mentionEveryone() { - return Boolean(this.bitfield! & messageToggles.mentionEveryone); - }, - get pinned() { - return Boolean(this.bitfield! & messageToggles.pinned); - }, - get thread() { - return cache.threads.get(this.id!); - }, - toJSON() { - return { - id: this.id?.toString(), - channelId: this.channelId?.toString(), - guildId: this.guildId?.toString(), - author: { - id: this.authorId?.toString(), - username: this.tag?.substring(0, this.tag.length - 5), - discriminator: this.tag?.substring(this.tag.length - 4), - avatar: this.member?.avatar ? iconBigintToHash(this.member.avatar) : undefined, - bot: this.member?.bot, - system: this.member?.system, - mfaEnabled: this.member?.mfaEnabled, - locale: this.member?.locale, - verified: this.member?.verified, - email: this.member?.email, - flags: this.member?.flags, - premiumType: this.member?.premiumType, - publicFlags: this.member?.publicFlags, - }, - member: this.member, - content: this.content, - timestamp: this.timestamp ? new Date(this.timestamp).toISOString() : undefined, - editedTimestamp: this.editedTimestamp ? new Date(this.editedTimestamp).toISOString() : undefined, - tts: this.tts, - mentionEveryone: this.mentionEveryone, - mentions: this.mentions, - mentionRoles: this.mentionRoles, - mentionChannels: this.mentionChannels, - attachments: this.attachments, - embeds: this.embeds, - reactions: this.reactions, - nonce: this.nonce, - pinned: this.pinned, - webhookId: this.webhookId, - type: this.type, - activity: this.activity, - application: this.application, - applicationId: this.applicationId, - messageReference: this.messageReference, - flags: this.flags, - stickers: this.stickers, - referencedMessage: this.referencedMessage, - interaction: this.interaction, - thread: this.thread, - components: this.components, - } as Message; - }, -}; - -export async function createDiscordenoMessage(data: Message) { - const { - guildId = "", - mentionChannels = [], - mentions = [], - mentionRoles = [], - editedTimestamp, - author, - messageReference, - ...rest - } = data; - - let bitfield = 0n; - - const props: Record> = {}; - - const requiredPropsSize = cache.requiredStructureProperties.messages.size; - - for (const key of Object.keys(rest) as (keyof typeof rest)[]) { - eventHandlers.debug?.("loop", `Running for of loop in createDiscordenoMessage function.`); - - // If empty all are allowed, otherwise check if this prop is allowed - if (requiredPropsSize && !cache.requiredStructureProperties.messages.has(key)) continue; - - const toggleBits = messageToggles[key as keyof typeof messageToggles]; - if (toggleBits) { - bitfield |= rest[key] ? toggleBits : 0n; - continue; - } - - // Don't add member to props since it would overwrite the message.member getter - // thread should not be cached on a message - if (["member", "thread"].includes(key)) continue; - - props[key] = createNewProp( - MESSAGE_SNOWFLAKES.includes(key) ? (rest[key] ? snowflakeToBigint(rest[key] as string) : undefined) : rest[key] - ); - } - - if (rest.thread) await cacheHandlers.set("threads", snowflakeToBigint(data.id), channelToThread(rest.thread)); - - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("authorId")) - props.authorId = createNewProp(snowflakeToBigint(author.id)); - - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("isBot")) - props.isBot = createNewProp(author.bot || false); - - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("tag")) - props.tag = createNewProp(`${author.username}#${author.discriminator.toString().padStart(4, "0")}`); - - // Discord doesnt give guild id for getMessage() so this will fill it in - const guildIdFinal = - snowflakeToBigint(guildId) || - (await cacheHandlers.get("channels", snowflakeToBigint(data.channelId)))?.guildId || - 0n; - - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("content")) - props.content = createNewProp(data.content || ""); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("guildId")) - props.guildId = createNewProp(guildIdFinal); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("mentionedUserIds")) - props.mentionedUserIds = createNewProp(mentions.map((m) => snowflakeToBigint(m.id))); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("mentionedRoleIds")) - props.mentionedRoleIds = createNewProp(mentionRoles.map((id) => snowflakeToBigint(id))); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("mentionedChannelIds")) - props.mentionedChannelIds = createNewProp([ - // Keep any ids that discord sends - ...mentionChannels.map((m) => snowflakeToBigint(m.id)), + // TRANSFORMED STUFF BELOW + id: bot.transformers.snowflake(data.id), + guildId: data.guildId ? bot.transformers.snowflake(data.guildId) : undefined, + channelId: bot.transformers.snowflake(data.channelId), + webhookId: data.webhookId ? bot.transformers.snowflake(data.webhookId) : undefined, + authorId: bot.transformers.snowflake(data.author.id), + applicationId: data.applicationId ? bot.transformers.snowflake(data.applicationId) : undefined, + messageReference: data.messageReference ? { + messageId: data.messageReference.messageId ? bot.transformers.snowflake(data.messageReference.messageId) : undefined, + channelId: data.messageReference.channelId ? bot.transformers.snowflake(data.messageReference.channelId) : undefined, + guildId: data.messageReference.guildId ? bot.transformers.snowflake(data.messageReference.guildId) : undefined, + } : undefined, + mentionedUserIds: data.mentions.map((m) => bot.transformers.snowflake(m.id)), + mentionedRoleIds: data.mentionRoles.map((id) => bot.transformers.snowflake(id)), + mentionedChannelIds: [ + // Keep any ids tht discord sends + ...data.mentionChannels.map((m) => bot.transformers.snowflake(m.id)), // Add any other ids that can be validated in a channel mention format - ...(rest.content?.match(CHANNEL_MENTION_REGEX) || []).map((text) => + ...(data.content?.match(CHANNEL_MENTION_REGEX) || []).map((text) => // converts the <#123> into 123 - snowflakeToBigint(text.substring(2, text.length - 1)) + bot.transformers.snowflake(text.substring(2, text.length - 1)) ), - ]); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("timestamp")) - props.timestamp = createNewProp(Date.parse(data.timestamp)); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("editedTimestamp")) - props.editedTimestamp = createNewProp(editedTimestamp ? Date.parse(editedTimestamp) : undefined); - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("messageReference")) - props.messageReference = createNewProp( - messageReference - ? { - messageId: messageReference.messageId ? snowflakeToBigint(messageReference.messageId) : undefined, - channelId: messageReference.channelId ? snowflakeToBigint(messageReference.channelId) : undefined, - guildId: messageReference.guildId ? snowflakeToBigint(messageReference.guildId) : undefined, - } - : undefined - ); - - if (!requiredPropsSize || cache.requiredStructureProperties.messages.has("bitfield")) - props.bitfield = createNewProp(bitfield); - - return Object.create(baseMessage, props) as DiscordenoMessage; + ] + } } export interface DiscordenoMessage @@ -339,58 +101,4 @@ export interface DiscordenoMessage timestamp: number; /** When this message was edited (or undefined if never) */ editedTimestamp?: number; - - // GETTERS - - /** The channel where this message was sent. Can be undefined if uncached. */ - channel?: DiscordenoChannel; - /** The guild of this message. Can be undefined if not in cache or in DM */ - guild?: DiscordenoGuild; - /** The member for the user who sent the message. Can be undefined if not in cache or in dm. */ - member?: DiscordenoMember; - /** The guild member details for this guild and member. Can be undefined if not in cache or in dm. */ - guildMember?: Omit & { - joinedAt?: number; - premiumSince?: number; - roles: bigint[]; - }; - /** The url link to this message */ - link: string; - /** The role objects for all the roles that were mentioned in this message */ - mentionedRoles: (DiscordenoRole | undefined)[]; - /** The channel objects for all the channels that were mentioned in this message. */ - mentionedChannels: (DiscordenoChannel | undefined)[]; - /** The member objects for all the members that were mentioned in this message. */ - mentionedMembers: (DiscordenoMember | undefined)[]; - /** The thread if this message has a thread. */ - thread?: DiscordenoThread; - - // METHODS - - /** Delete the message */ - delete(reason?: string, delayMilliseconds?: number): ReturnType; - /** Edit the message */ - edit(content: string | EditMessage): ReturnType; - /** Pins the message in the channel */ - pin(): ReturnType; - /** Add a reaction to the message */ - addReaction(reaction: string): ReturnType; - /** Add multiple reactions to the message without or without order. */ - addReactions(reactions: string[], ordered?: boolean): ReturnType; - /** Send a inline reply to this message */ - reply(content: string | CreateMessage, mentionUser?: boolean): ReturnType; - /** Send a message to this channel where this message is */ - send(content: string | CreateMessage): ReturnType; - /** Send a message to this channel and then delete it after a bit. By default it will delete after 10 seconds with no reason provided. */ - alert(content: string | CreateMessage, timeout?: number, reason?: string): Promise; - /** Send a inline reply to this message but then delete it after a bit. By default it will delete after 10 seconds with no reason provided. */ - alertReply(content: string | CreateMessage, timeout?: number, reason?: string): Promise; - /** Removes all reactions for all emojis on this message */ - removeAllReactions(): ReturnType; - /** Removes all reactions for a single emoji on this message */ - removeReactionEmoji(reaction: string): ReturnType; - /** Removes a reaction from the given user on this message, defaults to bot */ - removeReaction(reaction: string, userId?: bigint): ReturnType; - /** Convert to json */ - toJSON(): Message; } diff --git a/src/transformers/role.ts b/src/transformers/role.ts index f0e84ccc9..b2fa82a82 100644 --- a/src/transformers/role.ts +++ b/src/transformers/role.ts @@ -8,14 +8,16 @@ export function transformRole( } ) { return { - // TODO: decide if its better to spread like this or do manually - // ...payload, // UNTRANSFORMED STUFF HERE - // TODO: decide if we should use spread above or do manually name: payload.role.name, guildId: payload.guildId, position: payload.role.position, color: payload.role.color, + bitfield: + (payload.role.hoist ? 1n : 0n) | + (payload.role.managed ? 2n : 0n) | + (payload.role.mentionable ? 4n : 0n) | + (payload.role.tags?.premiumSubscriber ? 8n : 0n), // TRANSFORMED STUFF BELOW id: bot.transformers.snowflake(payload.role.id), @@ -24,11 +26,6 @@ export function transformRole( ? bot.transformers.snowflake(payload.role.tags.integrationId) : undefined, permissions: payload.role.permissions ? bot.transformers.snowflake(payload.role.permissions) : undefined, - bitfield: - (payload.role?.hoist ? 1n : 0n) | - (payload.role?.managed ? 2n : 0n) | - (payload.role?.mentionable ? 4n : 0n) | - (payload.role.tags?.premiumSubscriber ? 8n : 0n), }; }