diff --git a/src/index.js b/src/index.js index b3a92f60caba..916056fe0b52 100644 --- a/src/index.js +++ b/src/index.js @@ -85,6 +85,8 @@ module.exports = { ReactionEmoji: require('./structures/ReactionEmoji'), RichPresenceAssets: require('./structures/Presence').RichPresenceAssets, Role: require('./structures/Role'), + Team: require('./structures/Team'), + TeamMember: require('./structures/TeamMember'), TextChannel: require('./structures/TextChannel'), User: require('./structures/User'), VoiceChannel: require('./structures/VoiceChannel'), diff --git a/src/structures/ClientApplication.js b/src/structures/ClientApplication.js index d2bcd17a70cb..ac712bb6a53a 100644 --- a/src/structures/ClientApplication.js +++ b/src/structures/ClientApplication.js @@ -3,6 +3,7 @@ const Snowflake = require('../util/Snowflake'); const { ClientApplicationAssetTypes, Endpoints } = require('../util/Constants'); const Base = require('./Base'); +const Team = require('./Team'); const AssetTypes = Object.keys(ClientApplicationAssetTypes); @@ -67,9 +68,9 @@ class ClientApplication extends Base { /** * The owner of this OAuth application - * @type {?User} + * @type {User|Team} */ - this.owner = data.owner ? this.client.users.add(data.owner) : null; + this.owner = data.team ? new Team(this.client, data.team) : this.client.users.add(data.owner); } /** diff --git a/src/structures/Team.js b/src/structures/Team.js new file mode 100644 index 000000000000..0832a31f3817 --- /dev/null +++ b/src/structures/Team.js @@ -0,0 +1,109 @@ +'use strict'; + +const Snowflake = require('../util/Snowflake'); +const Collection = require('../util/Collection'); +const Base = require('./Base'); +const TeamMember = require('./TeamMember'); + +/** + * Represents a Client OAuth2 Application Team. + * @extends {Base} + */ +class Team extends Base { + constructor(client, data) { + super(client); + this._patch(data); + } + + _patch(data) { + /** + * The ID of the Team + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The name of the Team + * @type {string} + */ + this.name = data.name; + + /** + * The Team's icon hash + * @type {?string} + */ + this.icon = data.icon || null; + + /** + * The Team's owner id + * @type {?string} + */ + this.ownerID = data.owner_user_id || null; + + /** + * The Team's members + * @type {Collection} + */ + this.members = new Collection(); + + for (const memberData of data.members) { + const member = new TeamMember(this.client, this, memberData); + this.members.set(member.id, member); + } + } + + /** + * The owner of this team + * @type {?TeamMember} + * @readonly + */ + get owner() { + return this.members.get(this.ownerID) || null; + } + + /** + * The timestamp the app was created at + * @type {number} + * @readonly + */ + get createdTimestamp() { + return Snowflake.deconstruct(this.id).timestamp; + } + + /** + * The time the app was created at + * @type {Date} + * @readonly + */ + get createdAt() { + return new Date(this.createdTimestamp); + } + + /** + * A link to the application's icon. + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} URL to the icon + */ + iconURL({ format, size } = {}) { + if (!this.icon) return null; + return this.client.rest.cdn.TeamIcon(this.id, this.icon, { format, size }); + } + + /** + * When concatenated with a string, this automatically returns the Team's name instead of the + * Team object. + * @returns {string} + * @example + * // Logs: Team name: My Team + * console.log(`Team name: ${team}`); + */ + toString() { + return this.name; + } + + toJSON() { + return super.toJSON({ createdTimestamp: true }); + } +} + +module.exports = Team; diff --git a/src/structures/TeamMember.js b/src/structures/TeamMember.js new file mode 100644 index 000000000000..f37fcbe04c7e --- /dev/null +++ b/src/structures/TeamMember.js @@ -0,0 +1,62 @@ +'use strict'; + +const Base = require('./Base'); +const { MembershipStates } = require('../util/Constants'); + +/** + * Represents a Client OAuth2 Application Team Member. + * @extends {Base} + */ +class TeamMember extends Base { + constructor(client, team, data) { + super(client); + + /** + * The Team this member is part of + * @type {Team} + */ + this.team = team; + + this._patch(data); + } + + _patch(data) { + /** + * The permissions this Team Member has with reguard to the team + * @type {string[]} + */ + this.permissions = data.permissions; + + /** + * The permissions this Team Member has with reguard to the team + * @type {MembershipStates} + */ + this.membershipState = MembershipStates[data.membership_state]; + + /** + * The user for this Team Member + * @type {User} + */ + this.user = this.client.users.add(data.user); + + /** + * The ID of the Team Member + * @type {Snowflake} + */ + this.id = this.user.id; + } + + /** + * When concatenated with a string, this automatically returns the team members's tag instead of the + * TeamMember object. + * @returns {string} + * @example + * // Logs: Team Member's tag: @Hydrabolt + * console.log(`Team Member's tag: ${teamMember}`); + */ + toString() { + return this.user.toString(); + } +} + +module.exports = TeamMember; diff --git a/src/util/Constants.js b/src/util/Constants.js index 993563c073f6..0587163cdbc5 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -145,6 +145,8 @@ exports.Endpoints = { makeImageUrl(`${root}/channel-icons/${channelID}/${hash}`, { size, format }), Splash: (guildID, hash, format = 'webp', size) => makeImageUrl(`${root}/splashes/${guildID}/${hash}`, { size, format }), + TeamIcon: (teamID, hash, { format = 'webp', size } = {}) => + makeImageUrl(`${root}/team-icons/${teamID}/${hash}`, { size, format }), }; }, invite: (root, code) => `${root}/${code}`, @@ -569,6 +571,19 @@ exports.DefaultMessageNotifications = [ 'MENTIONS', ]; +/** + * The value set for a team members's membership state: + * * INVITED + * * ACCEPTED + * @typedef {string} MembershipStates + */ +exports.MembershipStates = [ + // They start at 1 + null, + 'INVITED', + 'ACCEPTED', +]; + function keyMirror(arr) { let tmp = Object.create(null); for (const value of arr) tmp[value] = value; diff --git a/typings/index.d.ts b/typings/index.d.ts index 7dfe5eed3331..558e12eb9c8a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -255,7 +255,7 @@ declare module 'discord.js' { public icon: string; public id: Snowflake; public name: string; - public owner: User | null; + public owner: User | Team; public rpcOrigins: string[]; public coverImage(options?: AvatarOptions): string; public fetchAssets(): Promise; @@ -264,6 +264,34 @@ declare module 'discord.js' { public toString(): string; } + export class Team extends Base { + constructor(client: Client, data: object); + public id: Snowflake; + public name: string; + public icon: string | null; + public ownerID: Snowflake | null; + public members: Collection; + + public readonly owner: TeamMember; + public readonly createdAt: Date; + public readonly createdTimestamp: number; + + public iconURL(options?: AvatarOptions): string; + public toJSON(): object; + public toString(): string; + } + + export class TeamMember extends Base { + constructor(client: Client, team: Team, data: object); + public team: Team; + public id: Snowflake; + public permissions: string[]; + public membershipState: MembershipStates; + public user: User; + + public toString(): string; + } + export interface ActivityOptions { name?: string; url?: string; @@ -1992,6 +2020,9 @@ declare module 'discord.js' { type InviteResolvable = string; + type MembershipStates = 'INVITED' + | 'ACCEPTED'; + interface MessageCollectorOptions extends CollectorOptions { max?: number; maxProcessed?: number;