mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-03 01:10:07 +00:00
Merge pull request #1118 from TriForMine/fp-attempt-9001
Functional Rewrite - Structures -> Transfomers
This commit is contained in:
@@ -1,144 +1,48 @@
|
||||
import { eventHandlers } from "../bot.ts";
|
||||
import { cache } from "../cache.ts";
|
||||
import { channelOverwriteHasPermission } from "../helpers/channels/channel_overwrite_has_permission.ts";
|
||||
import { cloneChannel } from "../helpers/channels/clone_channel.ts";
|
||||
import { deleteChannel } from "../helpers/channels/delete_channel.ts";
|
||||
import { deleteChannelOverwrite } from "../helpers/channels/delete_channel_overwrite.ts";
|
||||
import { editChannel } from "../helpers/channels/edit_channel.ts";
|
||||
import { editChannelOverwrite } from "../helpers/channels/edit_channel_overwrite.ts";
|
||||
import { sendMessage } from "../helpers/messages/send_message.ts";
|
||||
import { disconnectMember } from "../helpers/mod.ts";
|
||||
import type { Channel } from "../types/channels/channel.ts";
|
||||
import { DiscordChannelTypes } from "../types/channels/channel_types.ts";
|
||||
import type { ModifyChannel } from "../types/channels/modify_channel.ts";
|
||||
import type { DiscordOverwrite, Overwrite } from "../types/channels/overwrite.ts";
|
||||
import type { CreateMessage } from "../types/messages/create_message.ts";
|
||||
import type { PermissionStrings } from "../types/permissions/permission_strings.ts";
|
||||
import { snowflakeToBigint } from "../util/bigint.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { createNewProp } from "../util/utils.ts";
|
||||
import { DiscordenoGuild } from "./guild.ts";
|
||||
import { DiscordenoMember } from "./member.ts";
|
||||
import { DiscordenoMessage } from "./message.ts";
|
||||
import { DiscordenoVoiceState } from "./voice_state.ts";
|
||||
import { Channel } from "../types/channels/channel.ts";
|
||||
import { Bot } from "../bot.ts";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
import { DiscordOverwrite } from "../types/channels/overwrite.ts";
|
||||
|
||||
const CHANNEL_SNOWFLAKES = ["id", "guildId", "lastMessageId", "ownerId", "applicationId", "parentId"];
|
||||
export function transformChannel(
|
||||
bot: Bot,
|
||||
payload: { channel: SnakeCasedPropertiesDeep<Channel> } & { guildId?: bigint }
|
||||
) {
|
||||
return {
|
||||
// UNTRANSFORMED STUFF HERE
|
||||
type: payload.channel.type,
|
||||
position: payload.channel.position,
|
||||
name: payload.channel.name,
|
||||
topic: payload.channel.topic,
|
||||
nsfw: payload.channel.nsfw,
|
||||
bitrate: payload.channel.bitrate,
|
||||
userLimit: payload.channel.user_limit,
|
||||
rateLimitPerUser: payload.channel.rate_limit_per_user,
|
||||
recipients: payload.channel.recipients,
|
||||
icon: payload.channel.icon,
|
||||
rtcRegion: payload.channel.rtc_region,
|
||||
videoQualityMode: payload.channel.video_quality_mode,
|
||||
guildId: payload.guildId || (payload.channel.guild_id ? bot.transformers.snowflake(payload.channel.guild_id) : 0n),
|
||||
lastPinTimestamp: payload.channel.last_pin_timestamp,
|
||||
permissionOverwrites: payload.channel.permission_overwrites
|
||||
? payload.channel.permission_overwrites.map((o) => ({
|
||||
type: o.type,
|
||||
id: bot.transformers.snowflake(o.id),
|
||||
allow: bot.transformers.snowflake(o.allow),
|
||||
deny: bot.transformers.snowflake(o.deny),
|
||||
}))
|
||||
: [],
|
||||
|
||||
const baseChannel: Partial<DiscordenoChannel> = {
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id?.toString(),
|
||||
type: this.type,
|
||||
guildId: this.guildId?.toString(),
|
||||
position: this.position,
|
||||
permissionOverwrites: this.permissionOverwrites?.map((o) => ({
|
||||
...o,
|
||||
id: o.id.toString(),
|
||||
allow: o.allow.toString(),
|
||||
deny: o.deny.toString(),
|
||||
})),
|
||||
name: this.name,
|
||||
topic: this.topic,
|
||||
nsfw: this.nsfw,
|
||||
lastMessageId: this.lastMessageId?.toString(),
|
||||
bitrate: this.bitrate,
|
||||
userLimit: this.userLimit,
|
||||
rateLimitPerUser: this.rateLimitPerUser,
|
||||
recipients: this.recipients,
|
||||
icon: this.icon,
|
||||
ownerId: this.ownerId,
|
||||
applicationId: this.applicationId,
|
||||
parentId: this.parentId,
|
||||
lastPinTimestamp: this.lastPinTimestamp ? new Date(this.lastPinTimestamp).toISOString() : undefined,
|
||||
rtcRegion: this.rtcRegion,
|
||||
videoQualityMode: this.videoQualityMode,
|
||||
} as Channel;
|
||||
},
|
||||
get guild() {
|
||||
return cache.guilds.get(this.guildId!);
|
||||
},
|
||||
get messages() {
|
||||
return cache.messages.filter((m) => m.channelId === this.id!);
|
||||
},
|
||||
get mention() {
|
||||
return `<#${this.id!}>`;
|
||||
},
|
||||
get voiceStates() {
|
||||
return this.guild?.voiceStates.filter((voiceState) => voiceState.channelId === this.id!);
|
||||
},
|
||||
get connectedMembers() {
|
||||
const voiceStates = this.voiceStates;
|
||||
if (!voiceStates) return undefined;
|
||||
|
||||
return new Collection(voiceStates.map((vs) => [vs.userId, cache.members.get(vs.userId)]));
|
||||
},
|
||||
get isNewsChannel() {
|
||||
return this.type === DiscordChannelTypes.GuildNews;
|
||||
},
|
||||
get isGuildTextBasedChannel() {
|
||||
return [DiscordChannelTypes.GuildNews, DiscordChannelTypes.GuildText].includes(this.type!);
|
||||
},
|
||||
send(content) {
|
||||
return sendMessage(this.id!, content);
|
||||
},
|
||||
disconnect(memberId) {
|
||||
return disconnectMember(this.guildId!, memberId);
|
||||
},
|
||||
delete(reason) {
|
||||
return deleteChannel(this.id!, reason);
|
||||
},
|
||||
editOverwrite(id, options) {
|
||||
return editChannelOverwrite(this.guildId!, this.id!, id, options);
|
||||
},
|
||||
deleteOverwrite(id) {
|
||||
return deleteChannelOverwrite(this.guildId!, this.id!, id);
|
||||
},
|
||||
hasPermission(overwrites, permissions) {
|
||||
return channelOverwriteHasPermission(this.guildId!, this.id!, overwrites, permissions);
|
||||
},
|
||||
edit(options, reason) {
|
||||
return editChannel(this.id!, options, reason);
|
||||
},
|
||||
clone(reason) {
|
||||
return cloneChannel(this.id!, reason);
|
||||
},
|
||||
};
|
||||
|
||||
/** Create a structure object */
|
||||
// deno-lint-ignore require-await
|
||||
export async function createDiscordenoChannel(data: Channel, guildId?: bigint) {
|
||||
const { lastPinTimestamp, permissionOverwrites = [], ...rest } = data;
|
||||
|
||||
const requiredPropsSize = cache.requiredStructureProperties.channels.size;
|
||||
|
||||
const props: Record<string, PropertyDescriptor> = {};
|
||||
for (const key of Object.keys(rest) as (keyof typeof rest)[]) {
|
||||
eventHandlers.debug?.("loop", `Running forEach loop in createDiscordenoChannel function.`);
|
||||
// If empty then support all, otherwise we only allow the ones user added
|
||||
if (requiredPropsSize && !cache.requiredStructureProperties.channels.has(key)) continue;
|
||||
|
||||
props[key] = createNewProp(
|
||||
CHANNEL_SNOWFLAKES.includes(key) ? (rest[key] ? snowflakeToBigint(rest[key] as string) : undefined) : rest[key]
|
||||
);
|
||||
}
|
||||
|
||||
// Set the guildId seperately because sometimes guildId is not included
|
||||
if (!requiredPropsSize || cache.requiredStructureProperties.channels.has("guildId"))
|
||||
props.guildId = createNewProp(snowflakeToBigint(guildId?.toString() || data.guildId || ""));
|
||||
|
||||
if (!requiredPropsSize || cache.requiredStructureProperties.channels.has("lastPinTimestamp"))
|
||||
props.lastPinTimestamp = createNewProp(lastPinTimestamp ? Date.parse(lastPinTimestamp) : undefined);
|
||||
if (!requiredPropsSize || cache.requiredStructureProperties.channels.has("permissionOverwrites"))
|
||||
props.permissionOverwrites = createNewProp(
|
||||
permissionOverwrites.map((o) => ({
|
||||
...o,
|
||||
id: snowflakeToBigint(o.id),
|
||||
allow: snowflakeToBigint(o.allow),
|
||||
deny: snowflakeToBigint(o.deny),
|
||||
}))
|
||||
);
|
||||
|
||||
return Object.create(baseChannel, props) as DiscordenoChannel;
|
||||
// TRANSFORMED STUFF BELOW
|
||||
id: bot.transformers.snowflake(payload.channel.id),
|
||||
lastMessageId: payload.channel.last_message_id
|
||||
? bot.transformers.snowflake(payload.channel.last_message_id)
|
||||
: undefined,
|
||||
ownerId: payload.channel.owner_id ? bot.transformers.snowflake(payload.channel.owner_id) : undefined,
|
||||
applicationId: payload.channel.application_id
|
||||
? bot.transformers.snowflake(payload.channel.application_id)
|
||||
: undefined,
|
||||
parentId: payload.channel.parent_id ? bot.transformers.snowflake(payload.channel.parent_id) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export interface DiscordenoChannel
|
||||
@@ -173,64 +77,4 @@ export interface DiscordenoChannel
|
||||
applicationId?: bigint;
|
||||
/** Id of the parent category for a channel (each parent category can contain up to 50 channels) */
|
||||
parentId?: bigint;
|
||||
// GETTERS
|
||||
|
||||
/**
|
||||
* Gets the guild object for this channel.
|
||||
*
|
||||
* ⚠️ ADVANCED: If you use the custom cache, these will not work for you. Getters can not be async and custom cache requires async.
|
||||
*/
|
||||
guild?: DiscordenoGuild;
|
||||
/**
|
||||
* Gets the messages from cache that were sent in this channel
|
||||
*
|
||||
* ⚠️ ADVANCED: If you use the custom cache, these will not work for you. Getters can not be async and custom cache requires async.
|
||||
*/
|
||||
messages: Collection<bigint, DiscordenoMessage>;
|
||||
/** The mention of the channel */
|
||||
mention: string;
|
||||
/**
|
||||
* Gets the voice states for this channel
|
||||
*
|
||||
* ⚠️ ADVANCED: If you use the custom cache, these will not work for you. Getters can not be async and custom cache requires async.
|
||||
*/
|
||||
voiceStates?: Collection<bigint, DiscordenoVoiceState>;
|
||||
/**
|
||||
* Gets the connected members for this channel undefined if member is not cached
|
||||
*
|
||||
* ⚠️ ADVANCED: If you use the custom cache, these will not work for you. Getters can not be async and custom cache requires async.
|
||||
*/
|
||||
connectedMembers?: Collection<bigint, DiscordenoMember | undefined>;
|
||||
/** Whether the channel is a news channel. */
|
||||
isNewsChannel: boolean;
|
||||
/** Whether the channel is a news or text channel in a guild. */
|
||||
isGuildTextBasedChannel: boolean;
|
||||
|
||||
// METHODS
|
||||
|
||||
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||
send(content: string | CreateMessage): ReturnType<typeof sendMessage>;
|
||||
/** Disconnect a member from a voice channel. Requires MOVE_MEMBERS permission. */
|
||||
disconnect(memberId: bigint): ReturnType<typeof disconnectMember>;
|
||||
/** Delete the channel */
|
||||
delete(reason?: string): ReturnType<typeof deleteChannel>;
|
||||
/** Edit a channel Overwrite */
|
||||
editOverwrite(overwriteId: bigint, options: Omit<Overwrite, "id">): ReturnType<typeof editChannelOverwrite>;
|
||||
/** Delete a channel Overwrite */
|
||||
deleteOverwrite(overwriteId: bigint): ReturnType<typeof deleteChannelOverwrite>;
|
||||
/** Checks if a channel overwrite for a user id or a role id has permission in this channel */
|
||||
hasPermission(
|
||||
overwrites: (Omit<DiscordOverwrite, "id" | "allow" | "deny"> & {
|
||||
id: bigint;
|
||||
allow: bigint;
|
||||
deny: bigint;
|
||||
})[],
|
||||
permissions: PermissionStrings[]
|
||||
): ReturnType<typeof channelOverwriteHasPermission>;
|
||||
/** Edit the channel */
|
||||
edit(options: ModifyChannel, reason?: string): ReturnType<typeof editChannel>;
|
||||
/** Create a new channel with the same properties */
|
||||
clone(reason?: string): ReturnType<typeof cloneChannel>;
|
||||
/** Returns the Channel object json value */
|
||||
toJSON(): Channel;
|
||||
}
|
||||
|
||||
@@ -1,360 +1,93 @@
|
||||
import { botId, eventHandlers } from "../bot.ts";
|
||||
import { cache, cacheHandlers } from "../cache.ts";
|
||||
import { deleteGuild } from "../helpers/guilds/delete_guild.ts";
|
||||
import { editGuild } from "../helpers/guilds/edit_guild.ts";
|
||||
import { getAuditLogs } from "../helpers/guilds/get_audit_logs.ts";
|
||||
import { getBan } from "../helpers/guilds/get_ban.ts";
|
||||
import { getBans } from "../helpers/guilds/get_bans.ts";
|
||||
import { guildBannerURL } from "../helpers/guilds/guild_banner_url.ts";
|
||||
import { guildIconURL } from "../helpers/guilds/guild_icon_url.ts";
|
||||
import { guildSplashURL } from "../helpers/guilds/guild_splash_url.ts";
|
||||
import { leaveGuild } from "../helpers/guilds/leave_guild.ts";
|
||||
import { getInvites } from "../helpers/invites/get_invites.ts";
|
||||
import { banMember } from "../helpers/members/ban_member.ts";
|
||||
import { unbanMember } from "../helpers/members/unban_member.ts";
|
||||
import { Bot } from "../bot.ts";
|
||||
import type { PresenceUpdate } from "../types/activity/presence_update.ts";
|
||||
import { GetGuildAuditLog } from "../types/audit_log/get_guild_audit_log.ts";
|
||||
import type { Emoji } from "../types/emojis/emoji.ts";
|
||||
import type { CreateGuildBan } from "../types/guilds/create_guild_ban.ts";
|
||||
import type { Guild } from "../types/guilds/guild.ts";
|
||||
import { DiscordGuildFeatures } from "../types/guilds/guild_features.ts";
|
||||
import type { ModifyGuild } from "../types/guilds/modify_guild.ts";
|
||||
import type { GuildMember, GuildMemberWithUser } from "../types/members/guild_member.ts";
|
||||
import type { DiscordImageFormat } from "../types/misc/image_format.ts";
|
||||
import type { DiscordImageSize } from "../types/misc/image_size.ts";
|
||||
import { snowflakeToBigint } from "../util/bigint.ts";
|
||||
import { cacheMembers } from "../util/cache_members.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { iconHashToBigInt } from "../util/hash.ts";
|
||||
import { channelToThread } from "../util/transformers/channel_to_thread.ts";
|
||||
import { createNewProp } from "../util/utils.ts";
|
||||
import { DiscordenoChannel } from "./channel.ts";
|
||||
import { DiscordenoMember } from "./member.ts";
|
||||
import { structures } from "./mod.ts";
|
||||
import { DiscordenoRole } from "./role.ts";
|
||||
import { DiscordenoVoiceState } from "./voice_state.ts";
|
||||
import { transformChannel } from "./channel.ts";
|
||||
import { DiscordenoRole, transformRole } from "./role.ts";
|
||||
import { DiscordenoVoiceState, transformVoiceState } from "./voice_state.ts";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
|
||||
const GUILD_SNOWFLAKES = [
|
||||
"id",
|
||||
"ownerId",
|
||||
"permissions",
|
||||
"afkChannelId",
|
||||
"widgetChannelId",
|
||||
"applicationId",
|
||||
"systemChannelId",
|
||||
"rulesChannelId",
|
||||
"publicUpdatesChannelId",
|
||||
];
|
||||
export function transformGuild(bot: Bot, payload: { guild: SnakeCasedPropertiesDeep<Guild> } & { shardId: number }) {
|
||||
return {
|
||||
afkTimeout: payload.guild.afk_timeout,
|
||||
approximateMemberCount: payload.guild.approximate_member_count,
|
||||
approximatePresenceCount: payload.guild.approximate_presence_count,
|
||||
defaultMessageNotifications: payload.guild.default_message_notifications,
|
||||
description: payload.guild.description,
|
||||
explicitContentFilter: payload.guild.explicit_content_filter,
|
||||
features: payload.guild.features,
|
||||
maxMembers: payload.guild.max_members,
|
||||
maxPresences: payload.guild.max_presences,
|
||||
maxVideoChannelUsers: payload.guild.max_video_channel_users,
|
||||
mfaLevel: payload.guild.mfa_level,
|
||||
name: payload.guild.name,
|
||||
nsfwLevel: payload.guild.nsfw_level,
|
||||
preferredLocale: payload.guild.preferred_locale,
|
||||
premiumSubscriptionCount: payload.guild.premium_subscription_count,
|
||||
premiumTier: payload.guild.premium_tier,
|
||||
stageInstances: payload.guild.stage_instances,
|
||||
systemChannelFlags: payload.guild.system_channel_flags,
|
||||
vanityUrlCode: payload.guild.vanity_url_code,
|
||||
verificationLevel: payload.guild.verification_level,
|
||||
welcomeScreen: payload.guild.welcome_screen,
|
||||
discoverySplash: payload.guild.discovery_splash,
|
||||
|
||||
export const guildToggles = {
|
||||
/** Whether this user is owner of this guild */
|
||||
owner: 1n,
|
||||
/** Whether the guild widget is enabled */
|
||||
widgetEnabled: 2n,
|
||||
/** Whether this is a large guild */
|
||||
large: 4n,
|
||||
/** Whether this guild is unavailable due to an outage */
|
||||
unavailable: 8n,
|
||||
/** Whether this server's icon is animated */
|
||||
animatedIcon: 16n,
|
||||
/** Whether this server's banner is animated. */
|
||||
animatedBanner: 32n,
|
||||
/** Whether this server's splash is animated. */
|
||||
animatedSplash: 64n,
|
||||
};
|
||||
bitfield:
|
||||
(payload.guild.owner ? 1n : 0n) |
|
||||
(payload.guild.widget_enabled ? 2n : 0n) |
|
||||
(payload.guild.large ? 4n : 0n) |
|
||||
(payload.guild.unavailable ? 8n : 0n),
|
||||
|
||||
const baseGuild: Partial<DiscordenoGuild> = {
|
||||
toJSON() {
|
||||
return {
|
||||
shardId: this.shardId!,
|
||||
id: this.id?.toString(),
|
||||
name: this.name,
|
||||
icon: this.icon,
|
||||
iconHash: undefined,
|
||||
splash: this.splash,
|
||||
discoverySplash: this.discoverySplash,
|
||||
owner: this.owner,
|
||||
ownerId: this.ownerId?.toString(),
|
||||
permissions: this.permissions,
|
||||
afkChannelId: this.afkChannelId?.toString(),
|
||||
afkTimeout: this.afkTimeout,
|
||||
widgetEnabled: this.widgetEnabled,
|
||||
widgetChannelId: this.widgetChannelId?.toString(),
|
||||
verificationLevel: this.verificationLevel,
|
||||
defaultMessageNotifications: this.defaultMessageNotifications,
|
||||
explicitContentFilter: this.explicitContentFilter,
|
||||
roles: this.roles?.map((r) => r.toJSON()) || [],
|
||||
emojis: this.emojis?.array() || [],
|
||||
features: this.features,
|
||||
mfaLevel: this.mfaLevel,
|
||||
applicationId: this.applicationId?.toString(),
|
||||
systemChannelId: this.systemChannelId?.toString(),
|
||||
systemChannelFlags: this.systemChannelFlags,
|
||||
rulesChannelId: this.rulesChannelId?.toString(),
|
||||
joinedAt: this.joinedAt ? new Date(this.joinedAt).toISOString() : undefined,
|
||||
large: this.large,
|
||||
unavailable: this.unavailable,
|
||||
memberCount: this.memberCount,
|
||||
voiceStates: this.voiceStates,
|
||||
members: this.members,
|
||||
channels: this.channels,
|
||||
threads: this.threads,
|
||||
presences: this.presences,
|
||||
maxPresences: this.maxPresences,
|
||||
maxMembers: this.maxMembers,
|
||||
vanityUrlCode: this.vanityUrlCode,
|
||||
description: this.description,
|
||||
banner: this.banner,
|
||||
premiumTier: this.premiumTier,
|
||||
premiumSubscriptionCount: this.premiumSubscriptionCount,
|
||||
preferredLocale: this.preferredLocale,
|
||||
publicUpdatesChannelId: this.publicUpdatesChannelId?.toString(),
|
||||
maxVideoChannelUsers: this.maxVideoChannelUsers,
|
||||
approximateMemberCount: this.approximateMemberCount,
|
||||
approximatePresenceCount: this.approximatePresenceCount,
|
||||
welcomeScreen: this.welcomeScreen,
|
||||
nsfwLevel: this.nsfwLevel,
|
||||
stageInstances: this.stageInstances,
|
||||
} as Guild & { shardId: number };
|
||||
},
|
||||
get members() {
|
||||
return cache.members.filter((member) => member.guilds.has(this.id!));
|
||||
},
|
||||
get channels() {
|
||||
return cache.channels.filter((channel) => channel.guildId === this.id);
|
||||
},
|
||||
get afkChannel() {
|
||||
return cache.channels.get(this.afkChannelId!);
|
||||
},
|
||||
get publicUpdatesChannel() {
|
||||
return cache.channels.get(this.publicUpdatesChannelId!);
|
||||
},
|
||||
get rulesChannel() {
|
||||
return cache.channels.get(this.rulesChannelId!);
|
||||
},
|
||||
get systemChannel() {
|
||||
return cache.channels.get(this.systemChannelId!);
|
||||
},
|
||||
get bot() {
|
||||
return cache.members.get(botId);
|
||||
},
|
||||
get botMember() {
|
||||
return this.bot?.guilds.get(this.id!);
|
||||
},
|
||||
get botVoice() {
|
||||
return this.voiceStates?.get(botId);
|
||||
},
|
||||
get owner() {
|
||||
return cache.members.get(this.ownerId!);
|
||||
},
|
||||
get partnered() {
|
||||
return Boolean(this.features?.includes(DiscordGuildFeatures.Partnered));
|
||||
},
|
||||
get verified() {
|
||||
return Boolean(this.features?.includes(DiscordGuildFeatures.Verified));
|
||||
},
|
||||
bannerURL(size, format) {
|
||||
return guildBannerURL(this.id!, {
|
||||
banner: this.banner!,
|
||||
size,
|
||||
format,
|
||||
animated: this.animatedBanner!,
|
||||
});
|
||||
},
|
||||
splashURL(size, format) {
|
||||
return guildSplashURL(this.id!, {
|
||||
splash: this.splash!,
|
||||
size,
|
||||
format,
|
||||
animated: this.animatedSplash,
|
||||
});
|
||||
},
|
||||
delete() {
|
||||
return deleteGuild(this.id!);
|
||||
},
|
||||
edit(options) {
|
||||
return editGuild(this.id!, options);
|
||||
},
|
||||
auditLogs(options) {
|
||||
return getAuditLogs(this.id!, options);
|
||||
},
|
||||
getBan(memberId) {
|
||||
return getBan(this.id!, memberId);
|
||||
},
|
||||
bans() {
|
||||
return getBans(this.id!);
|
||||
},
|
||||
ban(memberId, options) {
|
||||
return banMember(this.id!, memberId, options);
|
||||
},
|
||||
unban(memberId) {
|
||||
return unbanMember(this.id!, memberId);
|
||||
},
|
||||
invites() {
|
||||
return getInvites(this.id!);
|
||||
},
|
||||
iconURL(size, format) {
|
||||
return guildIconURL(this.id!, {
|
||||
icon: this.icon!,
|
||||
size,
|
||||
format,
|
||||
animated: this.animatedIcon!,
|
||||
});
|
||||
},
|
||||
leave() {
|
||||
return leaveGuild(this.id!);
|
||||
},
|
||||
get isOwner() {
|
||||
return Boolean(this.bitfield! & guildToggles.owner);
|
||||
},
|
||||
get widgetEnabled() {
|
||||
return Boolean(this.bitfield! & guildToggles.widgetEnabled);
|
||||
},
|
||||
get large() {
|
||||
return Boolean(this.bitfield! & guildToggles.large);
|
||||
},
|
||||
get unavailable() {
|
||||
return Boolean(this.bitfield! & guildToggles.unavailable);
|
||||
},
|
||||
get animatedIcon() {
|
||||
return Boolean(this.bitfield! & guildToggles.animatedIcon);
|
||||
},
|
||||
get animatedBanner() {
|
||||
return Boolean(this.bitfield! & guildToggles.animatedBanner);
|
||||
},
|
||||
get animatedSplash() {
|
||||
return Boolean(this.bitfield! & guildToggles.animatedSplash);
|
||||
},
|
||||
};
|
||||
joinedAt: payload.guild.joined_at ? Date.parse(payload.guild.joined_at) : undefined,
|
||||
memberCount: payload.guild.member_count ?? 0,
|
||||
shardId: payload.shardId,
|
||||
icon: payload.guild.icon ? iconHashToBigInt(payload.guild.icon) : undefined,
|
||||
banner: payload.guild.banner ? iconHashToBigInt(payload.guild.banner) : undefined,
|
||||
splash: payload.guild.icon ? iconHashToBigInt(payload.guild.splash) : undefined,
|
||||
|
||||
export async function createDiscordenoGuild(data: Guild, shardId: number) {
|
||||
const {
|
||||
memberCount = 0,
|
||||
voiceStates = [],
|
||||
channels = [],
|
||||
threads = [],
|
||||
presences = [],
|
||||
joinedAt = "",
|
||||
emojis = [],
|
||||
members = [],
|
||||
icon,
|
||||
splash,
|
||||
banner,
|
||||
...rest
|
||||
} = data;
|
||||
// TRANSFORMED STUFF BELOW
|
||||
// TODO: Handle channels/threads in a better way?
|
||||
channels: (payload.guild.channels || []).map((channel) =>
|
||||
transformChannel(bot, { channel, guildId: bot.transformers.snowflake(payload.guild.id) })
|
||||
),
|
||||
threads: (payload.guild.threads || []).map((channel) => channelToThread(channel)),
|
||||
|
||||
let bitfield = 0n;
|
||||
const guildId = snowflakeToBigint(rest.id);
|
||||
roles: new Collection(
|
||||
(payload.guild.roles || [])
|
||||
.map((role) => transformRole(bot, { role, guildId: bot.transformers.snowflake(payload.guild.id) }))
|
||||
.map((role) => [role.id, role])
|
||||
),
|
||||
presences: new Collection((payload.guild.presences || []).map((p) => [bot.transformers.snowflake(p.user!.id), p])),
|
||||
emojis: new Collection((payload.guild.emojis || []).map((emoji) => [bot.transformers.snowflake(emoji.id!), emoji])),
|
||||
voiceStates: new Collection(
|
||||
(payload.guild.voice_states || [])
|
||||
.map((vs) =>
|
||||
transformVoiceState(bot, { voiceState: vs, guildId: bot.transformers.snowflake(payload.guild.id) })
|
||||
)
|
||||
.map((vs) => [vs.userId, vs])
|
||||
),
|
||||
|
||||
const promises = [];
|
||||
|
||||
for (const channel of channels) {
|
||||
promises.push(async () => {
|
||||
const discordenoChannel = await structures.createDiscordenoChannel(channel, guildId);
|
||||
|
||||
return cacheHandlers.set("channels", discordenoChannel.id, discordenoChannel);
|
||||
});
|
||||
}
|
||||
|
||||
for (const thread of threads) {
|
||||
promises.push(() => {
|
||||
const discordenoThread = channelToThread(thread);
|
||||
|
||||
return cacheHandlers.set("threads", discordenoThread.id, discordenoThread);
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
promises.map(async (promise) => {
|
||||
return await promise();
|
||||
})
|
||||
);
|
||||
|
||||
const roles = await Promise.all(
|
||||
(data.roles || []).map((role) =>
|
||||
structures.createDiscordenoRole({
|
||||
role,
|
||||
guildId,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const voiceStateStructs = await Promise.all(
|
||||
voiceStates.map((vs) => {
|
||||
if (vs.member?.joinedAt) members.push(vs.member);
|
||||
return structures.createDiscordenoVoiceState(guildId, vs);
|
||||
})
|
||||
);
|
||||
|
||||
const props: Record<string, ReturnType<typeof createNewProp>> = {};
|
||||
for (const key of Object.keys(rest) as (keyof typeof rest)[]) {
|
||||
eventHandlers.debug?.("loop", `Running for of loop in createDiscordenoGuild function.`);
|
||||
|
||||
// If its empty default allows all, otherwise only allow those users required.
|
||||
if (cache.requiredStructureProperties.guilds.size && !cache.requiredStructureProperties.guilds.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const toggleBits = guildToggles[key as keyof typeof guildToggles];
|
||||
if (toggleBits) {
|
||||
bitfield |= rest[key] ? toggleBits : 0n;
|
||||
continue;
|
||||
}
|
||||
|
||||
props[key] = createNewProp(
|
||||
GUILD_SNOWFLAKES.includes(key) ? (rest[key] ? snowflakeToBigint(rest[key] as string) : undefined) : rest[key]
|
||||
);
|
||||
}
|
||||
|
||||
const hashes = [
|
||||
{ name: "icon", toggle: guildToggles.animatedIcon, value: icon },
|
||||
{ name: "banner", toggle: guildToggles.animatedBanner, value: banner },
|
||||
{ name: "splash", toggle: guildToggles.animatedSplash, value: splash },
|
||||
] as const;
|
||||
|
||||
for (const hash of hashes) {
|
||||
// If its empty default allows all, otherwise only allow those users required.
|
||||
if (cache.requiredStructureProperties.guilds.size && !cache.requiredStructureProperties.guilds.has(hash.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const transformed = hash.value ? iconHashToBigInt(hash.value) : undefined;
|
||||
if (transformed) {
|
||||
props[hash.name] = createNewProp(hash.value);
|
||||
if (transformed.animated) bitfield |= hash.toggle;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("roles")) {
|
||||
props.roles = createNewProp(new Collection(roles.map((r: DiscordenoRole) => [r.id, r])));
|
||||
}
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("joinedAt")) {
|
||||
props.joinedAt = createNewProp(Date.parse(joinedAt));
|
||||
}
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("presences")) {
|
||||
props.presences = createNewProp(new Collection(presences.map((p) => [snowflakeToBigint(p.user!.id), p])));
|
||||
}
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("memberCount")) {
|
||||
props.memberCount = createNewProp(memberCount);
|
||||
}
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("emojis")) {
|
||||
props.emojis = createNewProp(new Collection(emojis.map((emoji) => [snowflakeToBigint(emoji.id!), emoji])));
|
||||
}
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("voiceStates")) {
|
||||
props.voiceStates = createNewProp(new Collection(voiceStateStructs.map((vs) => [vs.userId, vs])));
|
||||
}
|
||||
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("shardId")) {
|
||||
props.shardId = createNewProp(shardId);
|
||||
}
|
||||
|
||||
if (!cache.requiredStructureProperties.guilds.size || cache.requiredStructureProperties.guilds.has("bitfield")) {
|
||||
props.bitfield = createNewProp(bitfield);
|
||||
}
|
||||
|
||||
const guild: DiscordenoGuild = Object.create(baseGuild, props);
|
||||
await cacheMembers(guild.id, members as GuildMemberWithUser[]);
|
||||
return guild;
|
||||
id: bot.transformers.snowflake(payload.guild.id),
|
||||
ownerId: bot.transformers.snowflake(payload.guild.owner_id),
|
||||
permissions: payload.guild.permissions ? bot.transformers.snowflake(payload.guild.permissions) : 0n,
|
||||
afkChannelId: payload.guild.afk_channel_id ? bot.transformers.snowflake(payload.guild.afk_channel_id) : undefined,
|
||||
widgetChannelId: payload.guild.widget_channel_id
|
||||
? bot.transformers.snowflake(payload.guild.widget_channel_id)
|
||||
: undefined,
|
||||
applicationId: payload.guild.application_id ? bot.transformers.snowflake(payload.guild.application_id) : undefined,
|
||||
systemChannelId: payload.guild.system_channel_id
|
||||
? bot.transformers.snowflake(payload.guild.system_channel_id)
|
||||
: undefined,
|
||||
rulesChannelId: payload.guild.rules_channel_id
|
||||
? bot.transformers.snowflake(payload.guild.rules_channel_id)
|
||||
: undefined,
|
||||
publicUpdatesChannelId: payload.guild.public_updates_channel_id
|
||||
? bot.transformers.snowflake(payload.guild.public_updates_channel_id)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export interface DiscordenoGuild
|
||||
@@ -418,63 +151,4 @@ export interface DiscordenoGuild
|
||||
isOwner: boolean;
|
||||
/** Holds all the boolean toggles. */
|
||||
bitfield: bigint;
|
||||
|
||||
// GETTERS
|
||||
/** Members in this guild. */
|
||||
members: Collection<bigint, DiscordenoMember>;
|
||||
/** Channels in this guild. */
|
||||
channels: Collection<bigint, DiscordenoChannel>;
|
||||
/** The afk channel if one is set */
|
||||
afkChannel?: DiscordenoChannel;
|
||||
/** The public update channel if one is set */
|
||||
publicUpdatesChannel?: DiscordenoChannel;
|
||||
/** The rules channel in this guild if one is set */
|
||||
rulesChannel?: DiscordenoChannel;
|
||||
/** The system channel in this guild if one is set */
|
||||
systemChannel?: DiscordenoChannel;
|
||||
/** The bot member in this guild if cached */
|
||||
bot?: DiscordenoMember;
|
||||
/** The bot guild member in this guild if cached */
|
||||
botMember?: Omit<GuildMember, "joinedAt" | "premiumSince" | "roles"> & {
|
||||
joinedAt?: number;
|
||||
premiumSince?: number;
|
||||
roles: bigint[];
|
||||
};
|
||||
/** The bots voice state if there is one in this guild */
|
||||
botVoice?: DiscordenoVoiceState;
|
||||
/** The owner member of this guild */
|
||||
owner?: DiscordenoMember;
|
||||
/** Whether or not this guild is partnered */
|
||||
partnered: boolean;
|
||||
/** Whether or not this guild is verified */
|
||||
verified: boolean;
|
||||
|
||||
// METHODS
|
||||
|
||||
/** The banner url for this server */
|
||||
bannerURL(size?: DiscordImageSize, format?: DiscordImageFormat): string | undefined;
|
||||
/** The splash url for this server */
|
||||
splashURL(size?: DiscordImageSize, format?: DiscordImageFormat): string | undefined;
|
||||
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
|
||||
iconURL(size?: DiscordImageSize, format?: DiscordImageFormat): string | undefined;
|
||||
/** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event. */
|
||||
delete(): ReturnType<typeof deleteGuild>;
|
||||
/** Leave a guild */
|
||||
leave(): ReturnType<typeof leaveGuild>;
|
||||
/** Edit the server. Requires the MANAGE_GUILD permission. */
|
||||
edit(options: ModifyGuild): ReturnType<typeof editGuild>;
|
||||
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
|
||||
auditLogs(options?: GetGuildAuditLog): ReturnType<typeof getAuditLogs>;
|
||||
/** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */
|
||||
getBan(memberId: bigint): ReturnType<typeof getBan>;
|
||||
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
|
||||
bans(): ReturnType<typeof getBans>;
|
||||
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */
|
||||
ban(memberId: bigint, options?: CreateGuildBan): ReturnType<typeof banMember>;
|
||||
/** Remove the ban for a user. Requires BAN_MEMBERS permission */
|
||||
unban(memberId: bigint): ReturnType<typeof unbanMember>;
|
||||
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
|
||||
invites(): ReturnType<typeof getInvites>;
|
||||
/** Get the JSON version of the Guild object used to create this. Includes the shardId as well */
|
||||
toJSON(): Guild & { shardId: number };
|
||||
}
|
||||
|
||||
@@ -1,296 +1,61 @@
|
||||
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";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
|
||||
const MESSAGE_SNOWFLAKES = ["id", "channelId", "guildId", "webhookId", "applicationId"];
|
||||
export function transformMessage(bot: Bot, data: SnakeCasedPropertiesDeep<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.edited_timestamp ? Date.parse(data.edited_timestamp) : undefined,
|
||||
bitfield: (data.tts ? 1n : 0n) | (data.mention_everyone ? 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.sticker_items,
|
||||
|
||||
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<DiscordenoMessage> = {
|
||||
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<string, ReturnType<typeof createNewProp>> = {};
|
||||
|
||||
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.guild_id ? bot.transformers.snowflake(data.guild_id) : undefined,
|
||||
channelId: bot.transformers.snowflake(data.channel_id),
|
||||
webhookId: data.webhook_id ? bot.transformers.snowflake(data.webhook_id) : undefined,
|
||||
authorId: bot.transformers.snowflake(data.author.id),
|
||||
applicationId: data.application_id ? bot.transformers.snowflake(data.application_id) : undefined,
|
||||
messageReference: data.message_reference
|
||||
? {
|
||||
messageId: data.message_reference.message_id
|
||||
? bot.transformers.snowflake(data.message_reference.message_id)
|
||||
: undefined,
|
||||
channelId: data.message_reference.channel_id
|
||||
? bot.transformers.snowflake(data.message_reference.channel_id)
|
||||
: undefined,
|
||||
guildId: data.message_reference.guild_id
|
||||
? bot.transformers.snowflake(data.message_reference.guild_id)
|
||||
: undefined,
|
||||
}
|
||||
: undefined,
|
||||
mentionedUserIds: data.mentions ? data.mentions.map((m) => bot.transformers.snowflake(m.id)) : [],
|
||||
mentionedRoleIds: data.mention_roles ? data.mention_roles.map((id) => bot.transformers.snowflake(id)) : [],
|
||||
mentionedChannelIds: [
|
||||
// Keep any ids tht discord sends
|
||||
...(data.mention_channels ?? []).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 +104,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<GuildMember, "joinedAt" | "premiumSince" | "roles"> & {
|
||||
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<typeof deleteMessage>;
|
||||
/** Edit the message */
|
||||
edit(content: string | EditMessage): ReturnType<typeof editMessage>;
|
||||
/** Pins the message in the channel */
|
||||
pin(): ReturnType<typeof pinMessage>;
|
||||
/** Add a reaction to the message */
|
||||
addReaction(reaction: string): ReturnType<typeof addReaction>;
|
||||
/** Add multiple reactions to the message without or without order. */
|
||||
addReactions(reactions: string[], ordered?: boolean): ReturnType<typeof addReactions>;
|
||||
/** Send a inline reply to this message */
|
||||
reply(content: string | CreateMessage, mentionUser?: boolean): ReturnType<typeof sendMessage>;
|
||||
/** Send a message to this channel where this message is */
|
||||
send(content: string | CreateMessage): ReturnType<typeof sendMessage>;
|
||||
/** 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<void>;
|
||||
/** 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<unknown>;
|
||||
/** Removes all reactions for all emojis on this message */
|
||||
removeAllReactions(): ReturnType<typeof removeAllReactions>;
|
||||
/** Removes all reactions for a single emoji on this message */
|
||||
removeReactionEmoji(reaction: string): ReturnType<typeof removeReactionEmoji>;
|
||||
/** Removes a reaction from the given user on this message, defaults to bot */
|
||||
removeReaction(reaction: string, userId?: bigint): ReturnType<typeof removeReaction>;
|
||||
/** Convert to json */
|
||||
toJSON(): Message;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
import { Bot } from "../bot.ts";
|
||||
import { Role } from "../types/mod.ts";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
|
||||
export function transformRole(
|
||||
bot: Bot,
|
||||
payload: { role: Role } & {
|
||||
payload: { role: SnakeCasedPropertiesDeep<Role> } & {
|
||||
guildId: bigint;
|
||||
}
|
||||
) {
|
||||
): DiscordenoRole {
|
||||
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,
|
||||
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?.premium_subscriber ? 8n : 0n),
|
||||
|
||||
// TRANSFORMED STUFF BELOW
|
||||
id: bot.transformers.snowflake(payload.role.id),
|
||||
botId: payload.role.tags?.botId ? bot.transformers.snowflake(payload.role.tags?.botId) : undefined,
|
||||
botId: payload.role.tags?.bot_id ? bot.transformers.snowflake(payload.role.tags.bot_id) : undefined,
|
||||
integrationId: payload.role.tags?.integration_id
|
||||
? bot.transformers.snowflake(payload.role.tags.integration_id)
|
||||
: undefined,
|
||||
permissions: bot.transformers.snowflake(payload.role.permissions),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,7 +38,7 @@ export interface DiscordenoRole extends Omit<Role, "tags" | "id" | "permissions"
|
||||
/** If this role is the nitro boost role. */
|
||||
isNitroBoostRole: boolean;
|
||||
/** The integration id that is associated with this role */
|
||||
integrationId: bigint;
|
||||
integrationId?: bigint;
|
||||
/** The roles guildId */
|
||||
guildId: bigint;
|
||||
/** Permission bit set */
|
||||
|
||||
@@ -1,123 +1,30 @@
|
||||
import { eventHandlers } from "../bot.ts";
|
||||
import { cache } from "../cache.ts";
|
||||
import type { GuildMember } from "../types/members/guild_member.ts";
|
||||
import type { VoiceState } from "../types/voice/voice_state.ts";
|
||||
import { snowflakeToBigint } from "../util/bigint.ts";
|
||||
import { createNewProp } from "../util/utils.ts";
|
||||
import { DiscordenoGuild } from "./guild.ts";
|
||||
import { DiscordenoMember } from "./member.ts";
|
||||
import { Bot } from "../bot.ts";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
|
||||
const VOICE_STATE_SNOWFLAKES = ["userId", "channelId", "guildId"];
|
||||
export function transformVoiceState(
|
||||
bot: Bot,
|
||||
payload: { voiceState: SnakeCasedPropertiesDeep<VoiceState> } & { guildId: bigint }
|
||||
) {
|
||||
return {
|
||||
bitfield:
|
||||
(payload.voiceState.deaf ? 1n : 0n) |
|
||||
(payload.voiceState.mute ? 2n : 0n) |
|
||||
(payload.voiceState.self_deaf ? 4n : 0n) |
|
||||
(payload.voiceState.self_mute ? 8n : 0n) |
|
||||
(payload.voiceState.self_stream ? 16n : 0n) |
|
||||
(payload.voiceState.self_video ? 32n : 0n) |
|
||||
(payload.voiceState.suppress ? 64n : 0n),
|
||||
|
||||
export const voiceStateToggles = {
|
||||
/** Whether this user is deafened by the server */
|
||||
deaf: 1n,
|
||||
/** Whether this user is muted by the server */
|
||||
mute: 2n,
|
||||
/** Whether this user is locally deafened */
|
||||
selfDeaf: 4n,
|
||||
/** Whether this user is locally muted */
|
||||
selfMute: 8n,
|
||||
/** Whether this user is streaming using "Go Live" */
|
||||
selfStream: 16n,
|
||||
/** Whether this user's camera is enabled */
|
||||
selfVideo: 32n,
|
||||
/** Whether this user is muted by the current user */
|
||||
suppress: 64n,
|
||||
};
|
||||
requestToSpeakTimestamp: payload.voiceState.request_to_speak_timestamp,
|
||||
sessionId: payload.voiceState.session_id,
|
||||
|
||||
const baseRole: Partial<DiscordenoVoiceState> = {
|
||||
get member() {
|
||||
return cache.members.get(this.userId!);
|
||||
},
|
||||
get guildMember() {
|
||||
return this.member?.guilds.get(this.guildId!);
|
||||
},
|
||||
get guild() {
|
||||
return cache.guilds.get(this.guildId!);
|
||||
},
|
||||
get deaf() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.deaf);
|
||||
},
|
||||
get mute() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.mute);
|
||||
},
|
||||
get selfDeaf() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.selfDeaf);
|
||||
},
|
||||
get selfMute() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.selfMute);
|
||||
},
|
||||
get selfStream() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.selfStream);
|
||||
},
|
||||
get selfVideo() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.selfVideo);
|
||||
},
|
||||
get suppress() {
|
||||
return Boolean(this.bitfield! & voiceStateToggles.suppress);
|
||||
},
|
||||
toJSON() {
|
||||
return {
|
||||
guildId: this.guildId?.toString(),
|
||||
channelId: this.channelId?.toString(),
|
||||
userId: this.userId?.toString(),
|
||||
member: this.member,
|
||||
sessionId: this.sessionId,
|
||||
deaf: this.deaf,
|
||||
mute: this.mute,
|
||||
selfDeaf: this.selfDeaf,
|
||||
selfMute: this.selfMute,
|
||||
selfStream: this.selfStream,
|
||||
selfVideo: this.selfVideo,
|
||||
suppress: this.suppress,
|
||||
requestToSpeakTimestamp: this.requestToSpeakTimestamp,
|
||||
} as VoiceState;
|
||||
},
|
||||
};
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
export async function createDiscordenoVoiceState(guildId: bigint, data: VoiceState) {
|
||||
let bitfield = 0n;
|
||||
|
||||
const props: Record<string, ReturnType<typeof createNewProp>> = {};
|
||||
for (const key of Object.keys(data) as (keyof typeof data)[]) {
|
||||
eventHandlers.debug?.("loop", `Running for of loop in createDiscordenoVoiceState function.`);
|
||||
|
||||
// if is empty allow all, otherwise check if prop is required
|
||||
if (cache.requiredStructureProperties.voiceStates.size && !cache.requiredStructureProperties.voiceStates.has(key))
|
||||
continue;
|
||||
|
||||
// We don't need to cache member twice. It will be in cache.members
|
||||
if (key === "member") continue;
|
||||
|
||||
const toggleBits = voiceStateToggles[key as keyof typeof voiceStateToggles];
|
||||
if (toggleBits) {
|
||||
bitfield |= data[key] ? toggleBits : 0n;
|
||||
continue;
|
||||
}
|
||||
|
||||
props[key] = createNewProp(
|
||||
VOICE_STATE_SNOWFLAKES.includes(key)
|
||||
? data[key]
|
||||
? snowflakeToBigint(data[key] as string)
|
||||
: undefined
|
||||
: data[key]
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!cache.requiredStructureProperties.voiceStates.size ||
|
||||
cache.requiredStructureProperties.voiceStates.has("guildId")
|
||||
)
|
||||
props.guildId = createNewProp(guildId);
|
||||
if (
|
||||
!cache.requiredStructureProperties.voiceStates.size ||
|
||||
cache.requiredStructureProperties.voiceStates.has("bitfield")
|
||||
)
|
||||
props.bitfield = createNewProp(bitfield);
|
||||
|
||||
return Object.create(baseRole, props) as DiscordenoVoiceState;
|
||||
channelId: payload.voiceState.channel_id ? bot.transformers.snowflake(payload.voiceState.channel_id) : undefined,
|
||||
guildId:
|
||||
payload.guildId || (payload.voiceState.guild_id ? bot.transformers.snowflake(payload.voiceState.guild_id) : 0n),
|
||||
userId:
|
||||
payload.guildId || (payload.voiceState.user_id ? bot.transformers.snowflake(payload.voiceState.user_id) : 0n),
|
||||
};
|
||||
}
|
||||
|
||||
export interface DiscordenoVoiceState extends Omit<VoiceState, "channelId" | "guildId" | "userId" | "member"> {
|
||||
@@ -129,16 +36,4 @@ export interface DiscordenoVoiceState extends Omit<VoiceState, "channelId" | "gu
|
||||
userId: bigint;
|
||||
/** Holds all the boolean toggles. */
|
||||
bitfield: bigint;
|
||||
|
||||
// GETTERS
|
||||
member: DiscordenoMember;
|
||||
guildMember?: Omit<GuildMember, "joinedAt" | "premiumSince" | "roles"> & {
|
||||
joinedAt?: number;
|
||||
premiumSince?: number;
|
||||
roles: bigint[];
|
||||
};
|
||||
/** The guild where this role is. If undefined, the guild is not cached */
|
||||
guild?: DiscordenoGuild;
|
||||
/** Converts to the raw JSON format. */
|
||||
toJSON(): VoiceState;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ export function iconBigintToHash(icon: bigint) {
|
||||
const hash = icon.toString(16);
|
||||
// Hashes starting with a are animated and with b are not so need to handle that
|
||||
return hash.startsWith("a") ? `a_${hash.substring(1)}` : hash.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user