From b7ff327a7c4a02db8e317bdff2f6e0178e6ed33c Mon Sep 17 00:00:00 2001 From: Yuzu Date: Sun, 3 Jul 2022 15:17:17 -0500 Subject: [PATCH 1/2] feat: Guild.create Guild.delete Guild.edit --- structures/channels/GuildChannel.ts | 14 +- structures/guilds/Guild.ts | 204 +++++++++++++++++++++++++++- util/Routes.ts | 4 + 3 files changed, 207 insertions(+), 15 deletions(-) diff --git a/structures/channels/GuildChannel.ts b/structures/channels/GuildChannel.ts index b07ae95..0f595d7 100644 --- a/structures/channels/GuildChannel.ts +++ b/structures/channels/GuildChannel.ts @@ -80,8 +80,6 @@ export class GuildChannel extends BaseChannel implements Model { }; } - /* - * TODO: should be in TextChannel async createThread(options: ThreadCreateOptions): Promise { const thread = await this.session.rest.runMethod( this.session.rest, @@ -89,18 +87,8 @@ export class GuildChannel extends BaseChannel implements Model { Routes.CHANNEL_CREATE_THREAD(this.id), options, ); - return new ThreadChannel(this.session, thread, this.guildId); - }*/ - async delete(reason?: string) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.CHANNEL(this.id), - { - reason, - }, - ); + return new ThreadChannel(this.session, thread, this.guildId); } } diff --git a/structures/guilds/Guild.ts b/structures/guilds/Guild.ts index 727ee39..f0440a6 100644 --- a/structures/guilds/Guild.ts +++ b/structures/guilds/Guild.ts @@ -2,13 +2,18 @@ import type { Model } from "../Base.ts"; import type { Snowflake } from "../../util/Snowflake.ts"; import type { Session } from "../../session/Session.ts"; import type { + ChannelTypes, DiscordEmoji, DiscordGuild, DiscordInviteMetadata, DiscordMemberWithUser, DiscordRole, DiscordListActiveThreads, - DiscordListArchivedThreads, + GuildFeatures, + SystemChannelFlags, + MakeRequired, + VideoQualityModes, + DiscordOverwrite, } from "../../vendor/external.ts"; import type { GetInvite } from "../../util/Routes.ts"; import { @@ -17,7 +22,7 @@ import { VerificationLevels, } from "../../vendor/external.ts"; import { iconBigintToHash, iconHashToBigInt } from "../../util/hash.ts"; -import { urlToBase64 } from "../../util/urlToBase64.ts"; +import { encode as _encode, urlToBase64 } from "../../util/urlToBase64.ts"; import Member from "../Member.ts"; import BaseGuild from "./BaseGuild.ts"; import Role from "../Role.ts"; @@ -90,6 +95,103 @@ export interface ModifyRolePositions { position?: number | null; } +export interface GuildCreateOptionsRole { + id: Snowflake; + name?: string; + color?: number; + hoist?: boolean; + position?: number; + permissions?: bigint; + mentionable?: boolean; + iconURL?: string; + unicodeEmoji?: string | null; +} + +export interface GuildCreateOptionsRole { + id: Snowflake; + name?: string; + color?: number; + hoist?: boolean; + position?: number; + permissions?: bigint; + mentionable?: boolean; + iconHash?: bigint; + unicodeEmoji?: string | null; +} + +export interface GuildCreateOptionsChannel { + id?: Snowflake; + parentId?: Snowflake; + type?: ChannelTypes.GuildText | ChannelTypes.GuildVoice | ChannelTypes.GuildCategory; + name: string; + topic?: string | null; + nsfw?: boolean; + bitrate?: number; + userLimit?: number; + region?: string | null; + videoQualityMode?: VideoQualityModes; + permissionOverwrites?: MakeRequired, "id">[]; + rateLimitPerUser?: number; +} + +/** + * @link https://discord.com/developers/docs/resources/guild#create-guild + * */ +export interface GuildCreateOptions { + name: string; + afkChannelId?: Snowflake; + afkTimeout?: number; + channels?: GuildCreateOptionsChannel[]; + defaultMessageNotifications?: DefaultMessageNotificationLevels; + explicitContentFilter?: ExplicitContentFilterLevels; + iconURL?: string; + roles?: GuildCreateOptionsRole[]; + systemChannelFlags?: SystemChannelFlags; + systemChannelId?: Snowflake; + verificationLevel?: VerificationLevels; +} + +export interface GuildCreateOptions { + name: string; + afkChannelId?: Snowflake; + afkTimeout?: number; + channels?: GuildCreateOptionsChannel[]; + defaultMessageNotifications?: DefaultMessageNotificationLevels; + explicitContentFilter?: ExplicitContentFilterLevels; + iconHash?: bigint; + roles?: GuildCreateOptionsRole[]; + systemChannelFlags?: SystemChannelFlags; + systemChannelId?: Snowflake; + verificationLevel?: VerificationLevels; +} + +/** + * @link https://discord.com/developers/docs/resources/guild#modify-guild-json-params + * */ +export interface GuildEditOptions extends Omit { + ownerId?: Snowflake; + splashURL?: string; + bannerURL?: string; + discoverySplashURL?: string; + features?: GuildFeatures[]; + rulesChannelId?: Snowflake; + description?: string; + premiumProgressBarEnabled?: boolean; +} + +export interface GuildEditOptions extends Omit { + ownerId?: Snowflake; + splashHash?: bigint; + bannerHash?: bigint; + discoverySplashHash?: bigint; + features?: GuildFeatures[]; + rulesChannelId?: Snowflake; + publicUpdatesChannelId?: Snowflake; + preferredLocale?: string | null; + description?: string; + premiumProgressBarEnabled?: boolean; +} + /** * Represents a guild * @link https://discord.com/developers/docs/resources/guild#guild-object @@ -383,6 +485,104 @@ export class Guild extends BaseGuild implements Model { ) as Record, }; } + + /*** + * Makes the bot leave the guild + * */ + async leave() { + } + + /*** + * Deletes a guild + * */ + async delete() { + await this.session.rest.runMethod( + this.session.rest, + "DELETE", + Routes.GUILDS(), + ); + } + + /** + * Creates a guild and returns its data, the bot joins the guild + * This was modified from discord.js to make it compatible + * precondition: Bot should be in less than 10 servers + * */ + static async create(session: Session, options: GuildCreateOptions) { + const guild = await session.rest.runMethod(session.rest, "POST", Routes.GUILDS(), { + name: options.name, + afk_channel_id: options.afkChannelId, + afk_timeout: options.afkTimeout, + default_message_notifications: options.defaultMessageNotifications, + explicit_content_filter: options.explicitContentFilter, + system_channel_flags: options.systemChannelFlags, + verification_level: options.verificationLevel, + icon: "iconURL" in options + ? options.iconURL || urlToBase64(options.iconURL!) + : options.iconHash || iconBigintToHash(options.iconHash!), + channels: options.channels?.map((channel) => ({ + name: channel.name, + nsfw: channel.nsfw, + id: channel.id, + bitrate: channel.bitrate, + parent_id: channel.parentId, + permission_overwrites: channel.permissionOverwrites, + region: channel.region, + user_limit: channel.userLimit, + video_quality_mode: channel.videoQualityMode, + rate_limit_per_user: channel.rateLimitPerUser, + })), + roles: options.roles?.map((role) => ({ + name: role.name, + id: role.id, + color: role.color, + mentionable: role.mentionable, + hoist: role.hoist, + position: role.position, + unicode_emoji: role.unicodeEmoji, + icon: options.iconURL || urlToBase64(options.iconURL!), + })), + }); + + return new Guild(session, guild); + } + + /** + * Edits a guild and returns its data + * */ + async edit(session: Session, options: GuildEditOptions) { + const guild = await session.rest.runMethod(session.rest, "PATCH", Routes.GUILDS(), { + name: options.name, + afk_channel_id: options.afkChannelId, + afk_timeout: options.afkTimeout, + default_message_notifications: options.defaultMessageNotifications, + explicit_content_filter: options.explicitContentFilter, + system_channel_flags: options.systemChannelFlags, + verification_level: options.verificationLevel, + icon: "iconURL" in options + ? options.iconURL || urlToBase64(options.iconURL!) + : options.iconHash || iconBigintToHash(options.iconHash!), + // extra props + splash: "splashURL" in options + ? options.splashURL || urlToBase64(options.splashURL!) + : options.splashHash || iconBigintToHash(options.iconHash!), + banner: "bannerURL" in options + ? options.bannerURL || urlToBase64(options.bannerURL!) + : options.bannerHash || iconBigintToHash(options.bannerHash!), + discovery_splash: "discoverySplashURL" in options + ? options.discoverySplashURL || urlToBase64(options.discoverySplashURL!) + : options.discoverySplashHash || iconBigintToHash(options.discoverySplashHash!), + owner_id: options.ownerId, + rules_channel_id: options.rulesChannelId, + public_updates_channel_id: options.publicUpdatesChannelId, + preferred_locale: options.preferredLocale, + features: options.features, + description: options.description, + premiumProgressBarEnabled: options.premiumProgressBarEnabled, + }); + + return new Guild(session, guild); + } } export default Guild; diff --git a/util/Routes.ts b/util/Routes.ts index 916e319..650b5a2 100644 --- a/util/Routes.ts +++ b/util/Routes.ts @@ -120,6 +120,10 @@ export interface GetInvite { scheduledEventId?: Snowflake; } +export function GUILDS() { + return `/guilds`; +} + export function INVITE(inviteCode: string, options?: GetInvite) { let url = `/invites/${inviteCode}?`; From 202c02017144c41efe85efde64884877ae672f4e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sun, 3 Jul 2022 17:27:56 -0300 Subject: [PATCH 2/2] Resolving merge conflict (#8) stuff --- structures/Message.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/structures/Message.ts b/structures/Message.ts index c1287bd..e265be7 100644 --- a/structures/Message.ts +++ b/structures/Message.ts @@ -112,7 +112,18 @@ export class Message implements Model { }; } + // webhook handling + if (data.author && data.author.discriminator === "0000") { + this.webhook = { + id: data.author.id, + username: data.author.username, + discriminator: data.author.discriminator, + avatar: data.author.avatar ? iconHashToBigInt(data.author.avatar) : undefined, + }; + } + // user is always null on MessageCreate and its replaced with author + if (data.guild_id && data.member && !this.isWebhookMessage()) { this.member = new Member(session, { ...data.member, user: data.author }, data.guild_id); } @@ -178,6 +189,7 @@ export class Message implements Model { return this.editedTimestamp; } + get url() { return `https://discord.com/channels/${this.guildId ?? "@me"}/${this.channelId}/${this.id}`; }