feat: Guild Integrations (#2760)

* feat: Guild Integrations

* feat: Guild#createIntegration

* feat: Add ws event GUILD_INTEGRATIONS_UPDATE

* docs: Add `GUILD_INTEGRATIONS_UPDATE` to WSEventType

* misc: Fixed requested change

* docs: Updated typings

* typings: Sort members by name
This commit is contained in:
Kyra
2018-08-24 16:03:29 +02:00
committed by Crawl
parent 13f46b924b
commit c4df02782e
7 changed files with 253 additions and 0 deletions

View File

@@ -31,6 +31,7 @@ class WebSocketPacketManager {
this.register(WSEvents.GUILD_ROLE_UPDATE, require('./handlers/GuildRoleUpdate'));
this.register(WSEvents.GUILD_EMOJIS_UPDATE, require('./handlers/GuildEmojisUpdate'));
this.register(WSEvents.GUILD_MEMBERS_CHUNK, require('./handlers/GuildMembersChunk'));
this.register(WSEvents.GUILD_INTEGRATIONS_UPDATE, require('./handlers/GuildIntegrationsUpdate'));
this.register(WSEvents.CHANNEL_CREATE, require('./handlers/ChannelCreate'));
this.register(WSEvents.CHANNEL_DELETE, require('./handlers/ChannelDelete'));
this.register(WSEvents.CHANNEL_UPDATE, require('./handlers/ChannelUpdate'));

View File

@@ -0,0 +1,19 @@
const AbstractHandler = require('./AbstractHandler');
const { Events } = require('../../../../util/Constants');
class GuildIntegrationsHandler extends AbstractHandler {
handle(packet) {
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
if (guild) client.emit(Events.GUILD_INTEGRATIONS_UPDATE, guild);
}
}
module.exports = GuildIntegrationsHandler;
/**
* Emitted whenever a guild integration is updated
* @event Client#guildIntegrationsUpdate
* @param {Guild} guild The guild whose integrations were updated
*/

View File

@@ -68,6 +68,7 @@ module.exports = {
GuildChannel: require('./structures/GuildChannel'),
GuildEmoji: require('./structures/GuildEmoji'),
GuildMember: require('./structures/GuildMember'),
Integration: require('./structures/Integration'),
Invite: require('./structures/Invite'),
Message: require('./structures/Message'),
MessageAttachment: require('./structures/MessageAttachment'),

View File

@@ -1,4 +1,5 @@
const Invite = require('./Invite');
const Integration = require('./Integration');
const GuildAuditLogs = require('./GuildAuditLogs');
const Webhook = require('./Webhook');
const VoiceRegion = require('./VoiceRegion');
@@ -407,6 +408,42 @@ class Guild extends Base {
);
}
/**
* Fetches a collection of integrations to this guild.
* Resolves with a collection mapping integrations by their ids.
* @returns {Promise<Collection<string, Integration>>}
* @example
* // Fetch integrations
* guild.fetchIntegrations()
* .then(integrations => console.log(`Fetched ${integrations.size} integrations`))
* .catch(console.error);
*/
fetchIntegrations() {
return this.client.api.guilds(this.id).integrations.get().then(data =>
data.reduce((collection, integration) =>
collection.set(integration.id, new Integration(this.client, integration, this)),
new Collection())
);
}
/**
* The data for creating an integration.
* @typedef {Object} IntegrationData
* @property {string} id The integration id
* @property {string} type The integration type
*/
/**
* Creates an integration by attaching an integration object
* @param {IntegrationData} data The data for thes integration
* @param {string} reason Reason for creating the integration
* @returns {Promise<Guild>}
*/
createIntegration(data, reason) {
return this.client.api.guilds(this.id).integrations.post({ data, reason })
.then(() => this);
}
/**
* Fetches a collection of invites to this guild.
* Resolves with a collection mapping invites by their codes.

View File

@@ -0,0 +1,153 @@
const Base = require('./Base');
/**
* The information account for an integration
* @typedef {Object} IntegrationAccount
* @property {string} id The id of the account
* @property {string} name The name of the account
*/
class Integration extends Base {
constructor(client, data, guild) {
super(client);
/**
* The guild this integration belongs to
* @type {Guild}
*/
this.guild = guild;
/**
* The integration id
* @type {Snowflake}
*/
this.id = data.id;
/**
* The integration name
* @type {string}
*/
this.name = data.name;
/**
* The integration type (twitch, youtube, etc)
* @type {string}
*/
this.type = data.type;
/**
* Whether this integration is enabled
* @type {boolean}
*/
this.enabled = data.enabled;
/**
* Whether this integration is syncing
* @type {boolean}
*/
this.syncing = data.syncing;
/**
* The role that this integration uses for subscribers
* @type {Role}
*/
this.role = this.guild.roles.get(data.role_id);
/**
* The user for this integration
* @type {User}
*/
this.user = this.client.users.add(data.user);
/**
* The account integration information
* @type {IntegrationAccount}
*/
this.account = data.account;
/**
* The last time this integration was last synced
* @type {number}
*/
this.syncedAt = data.synced_at;
this._patch(data);
}
_patch(data) {
/**
* The behavior of expiring subscribers
* @type {number}
*/
this.expireBehavior = data.expire_behavior;
/**
* The grace period before expiring subscribers
* @type {number}
*/
this.expireGracePeriod = data.expire_grace_period;
}
/**
* Sync this integration
* @returns {Promise<Integration>}
*/
sync() {
this.syncing = true;
return this.client.api.guilds(this.guild.id).integrations(this.id).post()
.then(() => {
this.syncing = false;
this.syncedAt = Date.now();
return this;
});
}
/**
* The data for editing an integration.
* @typedef {Object} IntegrationEditData
* @property {number} [expireBehavior] The new behaviour of expiring subscribers
* @property {number} [expireGracePeriod] The new grace period before expiring subscribers
*/
/**
* Edits this integration.
* @param {IntegrationEditData} data The data to edit this integration with
* @param {string} reason Reason for editing this integration
* @returns {Promise<Integration>}
*/
edit(data, reason) {
if ('expireBehavior' in data) {
data.expire_behavior = data.expireBehavior;
data.expireBehavior = null;
}
if ('expireGracePeriod' in data) {
data.expire_grace_period = data.expireGracePeriod;
data.expireGracePeriod = null;
}
// The option enable_emoticons is only available for Twitch at this moment
return this.client.api.guilds(this.guild.id).integrations(this.id).patch({ data, reason })
.then(() => {
this._patch(data);
return this;
});
}
/**
* Deletes this integration.
* @returns {Promise<Integration>}
* @param {string} [reason] Reason for deleting this integration
*/
delete(reason) {
return this.client.api.guilds(this.guild.id).integrations(this.id).delete({ reason })
.then(() => this);
}
toJSON() {
return super.toJSON({
role: 'roleID',
guild: 'guildID',
user: 'userID',
});
}
}
module.exports = Integration;

View File

@@ -219,6 +219,7 @@ exports.Events = {
GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable',
GUILD_MEMBER_SPEAKING: 'guildMemberSpeaking',
GUILD_MEMBERS_CHUNK: 'guildMembersChunk',
GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate',
GUILD_ROLE_CREATE: 'roleCreate',
GUILD_ROLE_DELETE: 'roleDelete',
GUILD_ROLE_UPDATE: 'roleUpdate',
@@ -266,6 +267,7 @@ exports.Events = {
* * GUILD_MEMBER_REMOVE
* * GUILD_MEMBER_UPDATE
* * GUILD_MEMBERS_CHUNK
* * GUILD_INTEGRATIONS_UPDATE
* * GUILD_ROLE_CREATE
* * GUILD_ROLE_DELETE
* * GUILD_ROLE_UPDATE
@@ -302,6 +304,7 @@ exports.WSEvents = keyMirror([
'GUILD_MEMBER_REMOVE',
'GUILD_MEMBER_UPDATE',
'GUILD_MEMBERS_CHUNK',
'GUILD_INTEGRATIONS_UPDATE',
'GUILD_ROLE_CREATE',
'GUILD_ROLE_DELETE',
'GUILD_ROLE_UPDATE',

39
typings/index.d.ts vendored
View File

@@ -178,6 +178,7 @@ declare module 'discord.js' {
public on(event: 'guildMemberSpeaking', listener: (member: GuildMember, speaking: Readonly<Speaking>) => void): this;
public on(event: 'guildMemberUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
public on(event: 'guildUpdate', listener: (oldGuild: Guild, newGuild: Guild) => void): this;
public on(event: 'guildIntegrationsUpdate', listener: (guild: Guild) => void): this;
public on(event: 'message' | 'messageDelete' | 'messageReactionRemoveAll', listener: (message: Message) => void): this;
public on(event: 'messageDeleteBulk', listener: (messages: Collection<Snowflake, Message>) => void): this;
public on(event: 'messageReactionAdd' | 'messageReactionRemove', listener: (messageReaction: MessageReaction, user: User) => void): this;
@@ -209,6 +210,7 @@ declare module 'discord.js' {
public once(event: 'guildMemberSpeaking', listener: (member: GuildMember, speaking: Readonly<Speaking>) => void): this;
public once(event: 'guildMemberUpdate', listener: (oldMember: GuildMember, newMember: GuildMember) => void): this;
public once(event: 'guildUpdate', listener: (oldGuild: Guild, newGuild: Guild) => void): this;
public once(event: 'guildIntegrationsUpdate', listener: (guild: Guild) => void): this;
public once(event: 'message' | 'messageDelete' | 'messageReactionRemoveAll', listener: (message: Message) => void): this;
public once(event: 'messageDeleteBulk', listener: (messages: Collection<Snowflake, Message>) => void): this;
public once(event: 'messageReactionAdd' | 'messageReactionRemove', listener: (messageReaction: MessageReaction, user: User) => void): this;
@@ -431,11 +433,13 @@ declare module 'discord.js' {
public readonly verified: boolean;
public readonly voiceConnection: VoiceConnection;
public addMember(user: UserResolvable, options: AddGuildMemberOptions): Promise<GuildMember>;
public createIntegration(data: IntegrationData, reason?: string): Promise<Guild>;
public delete(): Promise<Guild>;
public edit(data: GuildEditData, reason?: string): Promise<Guild>;
public equals(guild: Guild): boolean;
public fetchAuditLogs(options?: GuildAuditLogsFetchOptions): Promise<GuildAuditLogs>;
public fetchBans(): Promise<Collection<Snowflake, { user: User, reason: string }>>;
public fetchIntegrations(): Promise<Collection<string, Integration>>;
public fetchInvites(): Promise<Collection<string, Invite>>;
public fetchVanityCode(): Promise<string>;
public fetchVoiceRegions(): Promise<Collection<string, VoiceRegion>>;
@@ -578,6 +582,25 @@ declare module 'discord.js' {
public toString(): string;
}
export class Integration extends Base {
constructor(client: Client, data: object, guild: Guild);
public account: IntegrationAccount;
public enabled: boolean;
public expireBehavior: number;
public expireGracePeriod: number;
public guild: Guild;
public id: Snowflake;
public name: string;
public role: Role;
public syncedAt: number;
public syncing: boolean;
public type: number;
public user: User;
public delete(reason?: string): Promise<Integration>;
public edit(data: IntegrationEditData, reason?: string): Promise<Integration>;
public sync(): Promise<Integration>;
}
export class Invite extends Base {
constructor(client: Client, data: object);
public channel: GuildChannel | GroupDMChannel;
@@ -1789,6 +1812,21 @@ declare module 'discord.js' {
| 1024
| 2048;
type IntegrationData = {
id: string;
type: string;
};
type IntegrationEditData = {
expireBehavior?: number;
expireGracePeriod?: number;
};
type IntegrationAccount = {
id: string;
name: string;
};
type InviteOptions = {
temporary?: boolean;
maxAge?: number;
@@ -2018,6 +2056,7 @@ declare module 'discord.js' {
| 'GUILD_BAN_ADD'
| 'GUILD_BAN_REMOVE'
| 'GUILD_EMOJIS_UPDATE'
| 'GUILD_INTEGRATIONS_UPDATE'
| 'CHANNEL_CREATE'
| 'CHANNEL_DELETE'
| 'CHANNEL_UPDATE'