From 3451367591bacf77fb6243659ce3bbd461f35d70 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Wed, 28 Dec 2016 13:04:54 -0600 Subject: [PATCH 01/16] fix the everyone role mentioning (#1032) --- src/structures/Role.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/structures/Role.js b/src/structures/Role.js index 5936da0fc..c15ff4be0 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -321,6 +321,7 @@ class Role { * @returns {string} */ toString() { + if (this.id === this.guild.id) return '@everyone'; return `<@&${this.id}>`; } From 289447e4c91249370674d752779fcf910dab382f Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 16:28:36 -0500 Subject: [PATCH 02/16] Update Client.js --- src/client/Client.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/client/Client.js b/src/client/Client.js index 4fdac08db..11cd5f403 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -345,19 +345,17 @@ class Client extends EventEmitter { /** * Generate an invite link for your bot - * @param {Array|number} [permissions] An array of permissions to request + * @param {PermissionResolvable[]|number} [permissions] An array of permissions to request * @returns {Promise} The invite link * @example * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE']) * .then(link => { - * console.log(link); + * console.log(`Generated bot invite link: ${link}`); * }); */ generateInvite(permissions) { if (permissions) { - if (permissions instanceof Array) { - permissions = this.resolver.resolvePermissions(permissions); - } + if (permissions instanceof Array) permissions = this.resolver.resolvePermissions(permissions); } else { permissions = 0; } From 8e47058286889c38f86a22a7a0e6c680d7f21713 Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 16:30:15 -0500 Subject: [PATCH 03/16] Update ClientDataResolver.js --- src/client/ClientDataResolver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/ClientDataResolver.js b/src/client/ClientDataResolver.js index 9f550cc25..211ee7ede 100644 --- a/src/client/ClientDataResolver.js +++ b/src/client/ClientDataResolver.js @@ -192,7 +192,7 @@ class ClientDataResolver { /** * Turn an array of permissions into a valid discord permission bitfield - * @param {Array} permissions An array of permissions as strings or permissions numbers (see resolvePermission) + * @param {PermissionResolvable[]} permissions Permissions to resolve together * @returns {number} */ resolvePermissions(permissions) { From bbeef44e667e547cb225cca55d0c918f41fb8be9 Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 16:32:14 -0500 Subject: [PATCH 04/16] Update ClientDataResolver.js --- src/client/ClientDataResolver.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/client/ClientDataResolver.js b/src/client/ClientDataResolver.js index 211ee7ede..6e07ea3a6 100644 --- a/src/client/ClientDataResolver.js +++ b/src/client/ClientDataResolver.js @@ -191,15 +191,13 @@ class ClientDataResolver { } /** - * Turn an array of permissions into a valid discord permission bitfield + * Turn an array of permissions into a valid Discord permission bitfield * @param {PermissionResolvable[]} permissions Permissions to resolve together * @returns {number} */ resolvePermissions(permissions) { let bitfield = 0; - for (const permission of permissions) { - bitfield |= this.resolvePermission(permission); - } + for (const permission of permissions) bitfield |= this.resolvePermission(permission); return bitfield; } From e4bae997470087014af9fdb3ebc7cd644a4e2f15 Mon Sep 17 00:00:00 2001 From: Zack Campbell Date: Wed, 28 Dec 2016 15:50:32 -0600 Subject: [PATCH 05/16] Update GuildResolvable typedef (#1034) To reflect the resolver supporting Guild ID strings --- src/client/ClientDataResolver.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/ClientDataResolver.js b/src/client/ClientDataResolver.js index 6e07ea3a6..4c4596775 100644 --- a/src/client/ClientDataResolver.js +++ b/src/client/ClientDataResolver.js @@ -65,7 +65,8 @@ class ClientDataResolver { /** * Data that resolves to give a Guild object. This can be: * * A Guild object - * @typedef {Guild} GuildResolvable + * * A Guild ID + * @typedef {Guild|string} GuildResolvable */ /** From c1b9437f0d4b3f73d699a6a809b23a7ce9992048 Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 18:20:38 -0500 Subject: [PATCH 06/16] Set up typings submodule --- .gitmodules | 3 + typings | 1 + typings/index.d.ts | 786 ------------------------------------------ typings/tsconfig.json | 13 - 4 files changed, 4 insertions(+), 799 deletions(-) create mode 100644 .gitmodules create mode 160000 typings delete mode 100644 typings/index.d.ts delete mode 100644 typings/tsconfig.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..7a99b61b2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "typings"] + path = typings + url = https://github.com/acdenisSK/discord.js-typings diff --git a/typings b/typings new file mode 160000 index 000000000..7523be032 --- /dev/null +++ b/typings @@ -0,0 +1 @@ +Subproject commit 7523be0329197a1d754c5237243d6adadc9e1c52 diff --git a/typings/index.d.ts b/typings/index.d.ts deleted file mode 100644 index 3bfc1151c..000000000 --- a/typings/index.d.ts +++ /dev/null @@ -1,786 +0,0 @@ -// Type definitions for discord.js 10.0.1 -// Project: https://github.com/hydrabolt/discord.js -// Definitions by: acdenisSK (https://github.com/acdenisSK) -// License: MIT - -declare module "discord.js" { - import { EventEmitter } from "events"; - import { Readable as ReadableStream } from "stream"; - import { ChildProcess } from "child_process"; - - export const version: string; - export class Client extends EventEmitter { - constructor(options?: ClientOptions); - email: string; - emojis: Collection; - guilds: Collection; - channels: Collection; - options: ClientOptions; - password: string; - readyAt: Date; - readyTimestamp: number; - status: number; - token: string; - uptime: number; - user: ClientUser; - users: Collection; - voiceConnections: Collection; - clearInterval(timeout: NodeJS.Timer): void; - clearTimeout(timeout: NodeJS.Timer): void; - destroy(): Promise; - fetchInvite(code: string): Promise; - fetchUser(id: string): Promise; - login(tokenOrEmail: string, password?: string): Promise; - setInterval(fn: Function, delay: number, ...args: any[]): NodeJS.Timer; - setTimeout(fn: Function, delay: number, ...args: any[]): NodeJS.Timer; - sweepMessages(lifetime?: number): number; - syncGuilds(guilds?: Guild[]): void; - on(event: string, listener: Function): this; - on(event: "debug", listener: (the: string) => void): this; - on(event: "disconnect", listener: () => void): this; - on(event: "error", listener: (error: Error) => void): this; - on(event: "guildBanAdd", listener: (user: User) => void): this; - on(event: "guildBanRemove", listener: (user: User) => void): this; - on(event: "guildCreate", listener: (guild: Guild) => void): this; - on(event: "guildDelete", listener: (guild: Guild) => void): this; - on(event: "guildEmojiCreate", listener: (emoji: Emoji) => void): this; - on(event: "guildEmojiDelete", listener: (emoji: Emoji) => void): this; - on(event: "guildEmojiUpdate", listener: (oldEmoji: Emoji, newEmoji: Emoji) => void): this; - on(event: "guildMemberAdd", listener: (member: GuildMember) => void): this; - on(event: "guildMemberAvailable", listener: (member: GuildMember) => void): this; - on(event: "guildMemberRemove", listener: (member: GuildMember) => void): this; - on(event: "guildMembersChunk", listener: (members: GuildMember[]) => void): this; - on(event: "guildMemberSpeaking", listener: (member: GuildMember, speaking: boolean) => void): this; - on(event: "guildMemberUpdate", listener: (oldMember: GuildMember, newMember: GuildMember) => void): this; - on(event: "roleCreate", listener: (role: Role) => void): this; - on(event: "roleDelete", listener: (role: Role) => void): this; - on(event: "roleUpdate", listener: (oldRole: Role, newRole: Role) => void): this; - on(event: "guildUnavailable", listener: (guild: Guild) => void): this; - on(event: "guildUpdate", listener: (oldGuild: Guild, newGuild: Guild) => void): this; - on(event: "channelCreate", listener: (channel: Channel) => void): this; - on(event: "channelDelete", listener: (channel: Channel) => void): this; - on(event: "channelPinsUpdate", listener: (channel: Channel, time: Date) => void): this; - on(event: "channelUpdate", listener: (oldChannel: Channel, newChannel: Channel) => void): this; - on(event: "message", listener: (message: Message) => void): this; - on(event: "messageDelete", listener: (message: Message) => void): this; - on(event: "messageDeleteBulk", listener: (messages: Collection) => void): this; - on(event: "messageUpdate", listener: (oldMessage: Message, newMessage: Message) => void): this; - on(event: "presenceUpdate", listener: (oldUser: User, newUser: User) => void): this; - on(event: "ready", listener: () => void): this; - on(event: "reconnecting", listener: () => void): this; - on(event: "typingStart", listener: (channel: Channel, user: User) => void): this; - on(event: "typingStop", listener: (channel: Channel, user: User) => void): this; - on(event: "userUpdate", listener: (oldClientUser: ClientUser, newClientUser: ClientUser) => void): this; - on(event: "voiceStateUpdate", listener: (oldMember: GuildMember, newMember: GuildMember) => void): this; - on(event: "warn", listener: (the: string) => void): this; - } - export class Webhook { - avatar: string; - client: Client; - guildID: string; - channelID: string; - id: string; - name: string; - token: string; - delete(): Promise; - edit(name: string, avatar: FileResolvable): Promise; - sendCode(lang: string, content: StringResolvable, options?: WebhookMessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: WebhookMessageOptions): Promise; - sendMessage(content: StringResolvable, options?: WebhookMessageOptions): Promise; - sendSlackMessage(body: Object): Promise; - sendTTSMessage(content: StringResolvable, options?: WebhookMessageOptions): Promise; - } - class SecretKey { - key: Uint8Array; - } - class RequestHandler { // docs going nowhere again, yay - constructor(restManager: {}); - globalLimit: boolean; - queue: {}[]; - restManager: {}; - handle(); - push(request: {}); - } - export class WebhookClient extends Webhook { - constructor(id: string, token: string, options?: ClientOptions); - options: ClientOptions; - } - export class Emoji { - client: Client; - createdAt: Date; - createdTimestamp: number; - guild: Guild; - id: string; - managed: boolean; - name: string; - requiresColons: boolean; - roles: Collection; - url: string; - toString(): string; - } - export class ReactionEmoji { - id: string; - identifier: string; - name: string; - reaction: MessageReaction; - toString(): string; - } - export class ClientUser extends User { - email: string; - verified: boolean; - blocked: Collection; - friends: Collection; - addFriend(user: UserResolvable): Promise; - removeFriend(user: UserResolvable): Promise; - setAvatar(avatar: Base64Resolvable): Promise; - setEmail(email: string): Promise; - setPassword(password: string): Promise; - setStatus(status?: string): Promise; - setGame(game: string, streamingURL?: string): Promise; - setPresence(data: Object): Promise; - setUsername(username: string): Promise; - createGuild(name: string, region: string, icon?: FileResolvable): Promise; - } - export class Presence { - game: Game; - status: string; - equals(other: Presence): boolean; - } - export class Channel { - client: Client; - createdAt: Date; - createdTimestamp: number; - id: string; - type: string; - delete(): Promise; - } - export class DMChannel extends Channel { - lastMessageID: string; - messages: Collection; - recipient: User; - typing: boolean; - typingCount: number; - awaitMessages(filter: CollectorFilterFunction, options?: AwaitMessagesOptions): Promise>; - bulkDelete(messages: Collection | Message[] | number): Collection; - createCollector(filter: CollectorFilterFunction, options?: CollectorOptions): MessageCollector; - fetchMessage(messageID: string): Promise; - fetchMessages(options?: ChannelLogsQueryOptions): Promise>; - fetchPinnedMessages(): Promise>; - sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: MessageOptions): Promise; - sendMessage(content: string, options?: MessageOptions): Promise; - sendTTSMessage(content: string, options?: MessageOptions): Promise; - startTyping(count?: number): void; - stopTyping(force?: boolean): void; - toString(): string; - } - export class GroupDMChannel extends Channel { - lastMessageID: string; - messages: Collection; - recipients: Collection; - owner: User; - typing: boolean; - typingCount: number; - awaitMessages(filter: CollectorFilterFunction, options?: AwaitMessagesOptions): Promise>; - bulkDelete(messages: Collection | Message[] | number): Collection; - createCollector(filter: CollectorFilterFunction, options?: CollectorOptions): MessageCollector; - fetchMessage(messageID: string): Promise; - fetchMessages(options?: ChannelLogsQueryOptions): Promise>; - fetchPinnedMessages(): Promise>; - sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: MessageOptions): Promise; - sendMessage(content: string, options?: MessageOptions): Promise; - sendTTSMessage(content: string, options?: MessageOptions): Promise; - startTyping(count?: number): void; - stopTyping(force?: boolean): void; - toString(): string; - } - export class GuildChannel extends Channel { - guild: Guild; - name: string; - permissionOverwrites: Collection; - position: number; - createInvite(options?: InviteOptions): Promise; - equals(channel: GuildChannel): boolean; - overwritePermissions(userOrRole: Role | User, options: PermissionOverwriteOptions): Promise; - permissionsFor(member: GuildMemberResolvable): EvaluatedPermissions; - setName(name: string): Promise; - setPosition(position: number): Promise; - setTopic(topic: string): Promise; - toString(): string; - } - export class TextChannel extends GuildChannel { - lastMessageID: string; - members: Collection; - messages: Collection; - topic: string; - typing: boolean; - typingCount: number; - awaitMessages(filter: CollectorFilterFunction, options?: AwaitMessagesOptions): Promise>; - bulkDelete(messages: Collection | Message[] | number): Collection; - createCollector(filter: CollectorFilterFunction, options?: CollectorOptions): MessageCollector; - fetchMessage(messageID: string): Promise; - fetchMessages(options?: ChannelLogsQueryOptions): Promise>; - fetchPinnedMessages(): Promise>; - sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: MessageOptions): Promise; - sendMessage(content: string, options?: MessageOptions): Promise; - sendTTSMessage(content: string, options?: MessageOptions): Promise; - startTyping(count?: number): void; - stopTyping(force?: boolean): void; - } - export class MessageCollector extends EventEmitter { - constructor(channel: Channel, filter: CollectorFilterFunction, options?: CollectorOptions); - collected: Collection; - filter: CollectorFilterFunction; - channel: Channel; - options: CollectorOptions; - stop(reason?: string): void; - on(event: "end", listener: (collection: Collection, reason: string) => void): this; - on(event: "message", listener: (message: Message, collector: MessageCollector) => void): this; - } - export class Game { - name: string; - streaming: boolean; - url: string; - type: number; - equals(other: Game): boolean; - } - export class PermissionOverwrites { - channel: GuildChannel; - id: string; - type: string; - delete(): Promise; - } - export class Guild { - afkChannelID: string; - afkTimeout: number; - available: boolean; - client: Client; - createdAt: Date; - createdTimestamp: number; - defaultChannel: GuildChannel; - embedEnabled: boolean; - emojis: Collection; - features: Object[]; - channels: Collection; - icon: string; - iconURL: string; - id: string; - joinDate: Date; - large: boolean; - memberCount: number; - members: Collection; - name: string; - owner: GuildMember; - ownerID: string; - region: string; - roles: Collection; - splash: string; - verificationLevel: number; - voiceConnection: VoiceConnection; - ban(user: GuildMember, deleteDays?: number): Promise; - createChannel(name: string, type: "text" | "voice"): Promise; - createRole(data?: RoleData): Promise; - delete(): Promise; - edit(data: {}): Promise; - equals(guild: Guild): boolean; - fetchBans(): Promise>; - fetchInvites(): Promise>; - fetchMember(user: UserResolvable): Promise; - fetchMembers(query?: string): Promise; - leave(): Promise; - member(user: UserResolvable): GuildMember; - pruneMembers(days: number, dry?: boolean): Promise; - setAFKChannel(afkChannel: ChannelResovalble): Promise; - setAFKTimeout(afkTimeout: number): Promise; - setIcon(icon: Base64Resolvable): Promise; - setName(name: string): Promise; - setOwner(owner: GuildMemberResolvable): Promise; - setRegion(region: string): Promise; - setSplash(splash: Base64Resolvable): Promise; - setVerificationLevel(level: number): Promise; - sync(): void; - toString(): string; - unban(user: UserResolvable): Promise; - } - export class GuildMember { - bannable: boolean; - client: Client; - deaf: boolean; - guild: Guild; - highestRole: Role; - id: string; - joinDate: Date; - kickable: boolean; - mute: boolean; - nickname: string; - permissions: EvaluatedPermissions; - roles: Collection; - selfDeaf: boolean; - selfMute: boolean; - serverDeaf: boolean; - serverMute: boolean; - speaking: boolean; - user: User; - voiceChannel: VoiceChannel; - voiceChannelID: string; - voiceSessionID: string; - addRole(role: Role | string): Promise; - addRoles(roles: Collection | Role[] | string[]): Promise; - ban(deleteDays?: number): Promise; - deleteDM(): Promise; - edit(data: {}): Promise; - hasPermission(permission: PermissionResolvable, explicit?: boolean): boolean; - hasPermissions(permission: Permissions[], explicit?: boolean): boolean; - kick(): Promise; - permissionsIn(channel: ChannelResovalble): EvaluatedPermissions; - removeRole(role: Role | string): Promise; - removeRoles(roles: Collection | Role[] | string[]): Promise; - sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: MessageOptions): Promise; - sendMessage(content: string, options?: MessageOptions): Promise; - sendTTSMessage(content: string, options?: MessageOptions): Promise; - setDeaf(deaf: boolean): Promise; - setMute(mute: boolean): Promise; - setNickname(nickname: string): Promise; - setRoles(roles: Collection | Role[] | string[]): Promise; - setVoiceChannel(voiceChannel: ChannelResovalble): Promise; - toString(): string; - } - export class User { - avatar: string; - avatarURL: string; - bot: boolean; - client: Client; - createdAt: Date; - createdTimestamp: number; - discriminator: string; - presence: Presence; - id: string; - status: string; - username: string; - block(): Promise; - unblock(): Promise; - fetchProfile(): Promise; - deleteDM(): Promise; - equals(user: User): boolean; - sendCode(lang: string, content: StringResolvable, options?: MessageOptions): Promise; - sendFile(attachment: FileResolvable, fileName?: string, content?: StringResolvable, options?: MessageOptions): Promise; - sendMessage(content: string, options?: MessageOptions): Promise; - sendTTSMessage(content: string, options?: MessageOptions): Promise; - toString(): string; - } - export class PartialGuildChannel { - client: Client; - id: string; - name: string; - type: string; - } - export class PartialGuild { - client: Client; - icon: string; - id: string; - name: string; - splash: string; - } - class PendingVoiceConnection { - data: Object; - deathTimer: NodeJS.Timer; - channel: VoiceChannel; - voiceManager: ClientVoiceManager; - setSessionID(sessionID: string); - setTokenAndEndpoint(token: string, endpoint: string); - upgrade(): VoiceConnection; - } - export class OAuth2Application { - client: Client; - createdAt: Date; - createdTimestamp: number; - description: string; - icon: string; - iconURL: string; - id: string; - name: string; - rpcOrigins: string[]; - toString(): string; - } - export class ClientOAuth2Application extends OAuth2Application { - flags: number; - owner: User; - } - export class Message { - attachments: Collection; - author: User; - channel: TextChannel | DMChannel | GroupDMChannel; - cleanContent: string; - client: Client; - content: string; - createdAt: Date; - createdTimestamp: number; - deletable: boolean; - editable: boolean; - editedAt: Date; - editedTimestamp: number; - edits: Message[]; - embeds: MessageEmbed[]; - guild: Guild; - id: string; - member: GuildMember; - mentions: { - users: Collection; - roles: Collection; - channels: Collection; - everyone: boolean; - }; - nonce: string; - pinnable: boolean; - pinned: boolean; - reactions: Collection; - system: boolean; - tts: boolean; - type: string; - addReaction(emoji: string): MessageReaction; // Not really documented but still worth using/making typings for it. - delete(timeout?: number): Promise; - edit(content: StringResolvable): Promise; - editCode(lang: string, content: StringResolvable): Promise; - equals(message: Message, rawData: Object): boolean; - isMentioned(data: GuildChannel | User | Role | string): boolean; - pin(): Promise; - reply(content: StringResolvable, options?: MessageOptions): Promise; - toString(): string; - unpin(): Promise; - } - export class MessageEmbed { - author: MessageEmbedAuthor; - client: Client; - description: string; - message: Message; - provider: MessageEmbedProvider; - thumbnail: MessageEmbedThumbnail; - title: string; - type: string; - url: string; - } - export class MessageEmbedThumbnail { - embed: MessageEmbed; - height: number; - proxyURL: string; - url: string; - width: number; - } - export class MessageEmbedProvider { - embed: MessageEmbed; - name: string; - url: string; - } - export class MessageEmbedAuthor { - embed: MessageEmbed; - name: string; - url: string; - } - export class RichEmbed { - title?: string; - description?: string; - url?: string; - timestamp?: Date; - color?: number | string; - fields?: { name: string; value: string; inline?: boolean; }[]; - author?: { name: string; url?: string; icon_url?: string; }; - thumbnail?: { url: string; height?: number; width?: number; }; - image?: { url: string; proxy_url?: string; height?: number; width?: number; }; - video?: { url: string; height: number; width: number; }; - footer?: { text?: string; icon_url?: string; }; - - addField(name: string, value: StringResolvable, inline?: boolean): this; - setAuthor(name: string, icon?: string, url?: string): this; - setColor(color: string | number | number[]): this; - setDescription(description: string): this; - setFooter(text: string, icon: string): this; - setImage(url: string): this; - setThumbnail(url: string): this; - setTimestamp(timestamp?: Date): this; - setTitle(title: string): this; - setURL(url: string): this; - } - export class MessageAttachment { - client: Client; - filename: string; - filesize: number; - height: number; - id: string; - message: Message; - proxyURL: string; - url: string; - width: number; - } - export class MessageReaction { - count: number; - emoji: Emoji | ReactionEmoji; - me: boolean; - message: Message; - users: Collection; - fetchUsers(limit?: number): Promise>; - remove(user?: UserResolvable): Promise; - } - export class Invite { - client: Client; - code: string; - createdAt: Date; - createdTimestamp: number; - guild: Guild | PartialGuild; - channel: GuildChannel | PartialGuildChannel; - inviter: User; - maxUses: number; - temporary: boolean; - url: string; - uses: number; - delete(): Promise; - toString(): string; - } - export class VoiceChannel extends GuildChannel { - bitrate: number; - connection: VoiceConnection; - members: Collection; - userLimit: number; - join(): Promise; - leave(): null; - setBitrate(bitrate: number): Promise; - } - export class Shard { - id: string; - manager: ShardingManager; - process: ChildProcess; - eval(script: string): Promise; - fetchClientValue(prop: string): Promise; - send(message: any): Promise; - } - export class ShardingManager extends EventEmitter { - constructor(file: string, options?: { - totalShards?: number; - respawn?: boolean; - shardArgs?: string[]; - token?: string; - }); - file: string; - respawn: boolean; - shardArgs: string[]; - shards: Collection; - totalShards: number; - broadcast(message: any): Promise; - broadcastEval(script: string): Promise; - createShard(id: number): Promise; - fetchClientValues(prop: string): Promise; - spawn(amount?: number, delay?: number): Promise>; - on(event: "launch", listener: (shard: Shard) => void): this; - } - export class ShardClientUtil { - constructor(client: Client); - id: number; - count: number; - broadcastEval(script: string): Promise; - fetchClientValues(prop: string): Promise; - send(message: any): Promise; - singleton(client: Client): ShardClientUtil; - } - export class UserConnection { - id: string; - integrations: Object[]; - name: string; - revoked: boolean; - type: string; - user: User; - } - export class UserProfile { - client: Client; - connections: Collection; - mutualGuilds: Collection; - user: User; - } - export class StreamDispatcher extends EventEmitter { - passes: number; - time: number; - totalStreamTime: number; - volume: number; - end(): void; - pause(): void; - resume(): void; - setVolume(volume: number): void; - setVolumeDecibels(db: number): void; - setVolumeLogarithmic(value: number): void; - on(event: "debug", listener: (information: string) => void): this; - on(event: "end", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "speaking", listener: (value: boolean) => void): this; - on(event: "start", listener: () => void): this; - } - interface Permissions { - CREATE_INSTANT_INVITE: boolean; - KICK_MEMBERS: boolean; - BAN_MEMBERS: boolean; - ADMINISTRATOR: boolean; - MANAGE_CHANNELS: boolean; - MANAGE_GUILD: boolean; - READ_MESSAGES: boolean; - SEND_MESSAGES: boolean; - SEND_TTS_MESSAGES: boolean; - MANAGE_MESSAGES: boolean; - EMBED_LINKS: boolean; - ATTACH_FILES: boolean; - READ_MESSAGE_HISTORY: boolean; - MENTION_EVERYONE: boolean; - USE_EXTERNAL_EMOJIS: boolean; - CONNECT: boolean; - SPEAK: boolean; - MUTE_MEMBERS: boolean; - DEAFEN_MEMBERS: boolean; - MOVE_MEMBERS: boolean; - USE_VAD: boolean; - CHANGE_NICKNAME: boolean; - MANAGE_NICKNAMES: boolean; - MANAGE_ROLES: boolean; - MANAGE_WEBHOOKS: boolean; - } - export class EvaluatedPermissions { - member: GuildMember; - raw: number; - hasPermission(permission: PermissionResolvable, explicit?: boolean): boolean; - hasPermissions(permission: PermissionResolvable[], explicit?: boolean): boolean; - serialize(): Permissions; - } - export class Role { - client: Client; - color: number; - createdAt: Date; createdTimestamp: number; - guild: Guild; - hexColor: string; - hoist: boolean; - id: string; - managed: boolean; - members: Collection; - mentionable: boolean; - name: string; - permissions: number; - position: number; - delete(): Promise; - edit(data: RoleData): Promise; - equals(role: Role): boolean; - hasPermission(permission: PermissionResolvable, explicit?: boolean): boolean; - hasPermissions(permissions: PermissionResolvable[], explicit?: boolean): boolean; - serialize(): Permissions; - setColor(color: string | number): Promise; - setHoist(hoist: boolean): Promise; - setName(name: string): Promise; - setPermissions(permissions: string[]): Promise; - setPosition(position: number): Promise; - toString(): string; - } - export class ClientVoiceManager { - client: Client; - connections: Collection; - pending: Collection; - joinChannel(channel: VoiceChannel): Promise; - sendVoiceStateUpdate(channel: VoiceChannel, options?: Object); - } - class AudioPlayer extends EventEmitter { - dispatcher: StreamDispatcher; - voiceConnection: VoiceConnection; - } - export class VoiceConnection extends EventEmitter { - authentication: Object; - channel: VoiceChannel; - player: AudioPlayer; - receivers: VoiceReceiver[]; - sockets: Object; - ssrcMap: Map; - voiceManager: ClientVoiceManager; - createReceiver(): VoiceReceiver; - disconnect(); - playConvertedStream(stream: ReadableStream, options?: StreamOptions): StreamDispatcher; - playFile(file: string, options?: StreamOptions): StreamDispatcher; - playStream(stream: ReadableStream, options?: StreamOptions): StreamDispatcher; - on(event: "disconnect", listener: (error: Error) => void): this; - on(event: "error", listener: (error: Error) => void): this; - on(event: "ready", listener: () => void): this; - on(event: "speaking", listener: (user: User, speaking: boolean) => void): this; - } - export class VoiceReceiver extends EventEmitter { - connection: VoiceConnection; - destroyed: boolean; - createOpusStream(user: User): ReadableStream; - createPCMStream(user: User): ReadableStream; - destroy(): void; - recreate(): void; - on(event: "opus", listener: (user: User, buffer: Buffer) => void): this; - on(event: "pcm", listener: (user: User, buffer: Buffer) => void): this; - on(event: "warn", listener: (message: string) => void): this; - } - export class Collection extends Map { - array(): value[]; - concat(...collections: Collection[]): Collection; - deleteAll(): Promise; - every(fn: Function, thisArg?: Object): boolean; - exists(prop: string, value: any): boolean; - filter(fn: Function, thisArg?: Object): Collection; - filterArray(fn: Function, thisArg?: Object): value[]; - find(propOrFn: string | Function, value?: any): value; - findAll(prop: string, value: any): value[]; - findKey(propOrFn: string | Function, value?: any): key; - first(): value; - firstKey(): key; - keyArray(): key[]; - last(): value; - lastKey(): key; - map(fn: Function, thisArg?: Object): any[]; - random(): value; - randomKey(): key; - reduce(fn: Function, startVal?: any): any; - some(fn: Function, thisArg?: Object): boolean; - } - type CollectorOptions = { time?: number; max?: number }; - type AwaitMessagesOptions = { time?: number; max?: number; errors?: string[]; }; - type Base64Resolvable = Buffer | string; - type CollectorFilterFunction = (message: Message, collector: MessageCollector) => boolean; - type FileResolvable = Buffer | string; - type GuildMemberResolvable = GuildMember | User; - type GuildResolvable = Guild; - type ChannelLogsQueryOptions = { limit?: number; before?: string; after?: string; around?: string }; - type ChannelResovalble = Channel | Guild | Message | string; - type InviteOptions = { temporary?: boolean; maxAge?: number; maxUses?: number; }; - type MessageOptions = { tts?: boolean; nonce?: string; disableEveryone?: boolean; split?: boolean | SplitOptions; }; - type PermissionOverwriteOptions = Permissions; - type PermissionResolvable = string | string[] | number[]; - type SplitOptions = { maxLength?: number; char?: string; prepend?: string; append?: string; }; - type StreamOptions = { seek?: number; volume?: number; passes?: number; }; - type StringResolvable = any[] | string | any; - type UserResolvable = User | string | Message | Guild | GuildMember; - type WebSocketOptions = { large_threshold?: number; compress?: boolean; }; - type ClientOptions = { - apiRequestMethod?: string; - shardId?: number; - shardCount?: number; - maxMessageCache?: number; - messageCacheLifetime?: number; - messageSweepInterval?: number; - fetchAllMembers?: boolean; - disableEveryone?: boolean; - restWsBridgeTimeout?: number; - ws?: WebSocketOptions; - }; - type WebhookMessageOptions = { - tts?: boolean; - disableEveryone?: boolean; - }; - type WebhookOptions = { - large_threshold?: number; - compress?: boolean; - }; - type RoleData = { - name?: string; - color?: number | string; - hoist?: boolean; - position?: number; - permissions?: string[]; - mentionable?: boolean; - }; -} diff --git a/typings/tsconfig.json b/typings/tsconfig.json deleted file mode 100644 index 8c9dfd770..000000000 --- a/typings/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "noImplicitAny": false, - "strictNullChecks": true, - "noEmit": true, - "forceConsistentCasingInFileNames": true - }, - "files": [ - "index.d.ts" - ] -} \ No newline at end of file From ea798eaaf3242728f5029befe3a276ca4be6da91 Mon Sep 17 00:00:00 2001 From: bdistin Date: Wed, 28 Dec 2016 17:27:02 -0600 Subject: [PATCH 07/16] Update Missing Permission Resolvables (#1035) --- src/client/ClientDataResolver.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/ClientDataResolver.js b/src/client/ClientDataResolver.js index 4c4596775..d38fb7c95 100644 --- a/src/client/ClientDataResolver.js +++ b/src/client/ClientDataResolver.js @@ -174,7 +174,9 @@ class ClientDataResolver { * "USE_VAD", // use voice activity detection * "CHANGE_NICKNAME", * "MANAGE_NICKNAMES", // change nicknames of others - * "MANAGE_ROLES_OR_PERMISSIONS" + * "MANAGE_ROLES_OR_PERMISSIONS", + * "MANAGE_WEBHOOKS", + * "MANAGE_EMOJIS" * ] * ``` * @typedef {string|number} PermissionResolvable From 7ede44bc00aeb449649160de21d4f991245cf070 Mon Sep 17 00:00:00 2001 From: Amish Shah Date: Thu, 29 Dec 2016 00:27:40 +0000 Subject: [PATCH 08/16] Add CloseEvent external --- src/client/websocket/WebSocketManager.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index 985105713..b67af5993 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -229,6 +229,11 @@ class WebSocketManager extends EventEmitter { this.sequence = -1; } + /** + * @external CloseEvent + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent} + */ + /** * Run whenever the connection to the gateway is closed, it will try to reconnect the client. * @param {CloseEvent} event The WebSocket close event From dfa6740b89f9aff753df4bf90142df7439f2368e Mon Sep 17 00:00:00 2001 From: Zack Campbell Date: Wed, 28 Dec 2016 18:52:15 -0600 Subject: [PATCH 09/16] Update gitmodules (#1036) To reflect transfer of ownership of the typings repo, in case the source forwarding does not continue indefinitely going forward --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 7a99b61b2..d5aa0ecce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "typings"] path = typings - url = https://github.com/acdenisSK/discord.js-typings + url = https://github.com/zajrik/discord.js-typings From 17a61731ef5e4fc8d0cccc0896cc87473dd1f929 Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 20:40:07 -0500 Subject: [PATCH 10/16] Update typings (test) --- typings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typings b/typings index 7523be032..9b503a119 160000 --- a/typings +++ b/typings @@ -1 +1 @@ -Subproject commit 7523be0329197a1d754c5237243d6adadc9e1c52 +Subproject commit 9b503a119c10c6873c8fd8cc65576f0992da5967 From a014b59790d34be54c2e6311a22c9913e220b178 Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Wed, 28 Dec 2016 21:35:19 -0500 Subject: [PATCH 11/16] Update webpack --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 780c79f25..f9a466282 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "eslint": "^3.12.0", "parallel-webpack": "^1.6.0", "uglify-js": "mishoo/UglifyJS2#harmony", - "webpack": "2.2.0-rc.2" + "webpack": "2.2.0-rc.3" }, "engines": { "node": ">=6.0.0" From ed8fcf651a2e69715a0e827489acb03e2e5d24c7 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Wed, 28 Dec 2016 22:58:30 -0600 Subject: [PATCH 12/16] Centralise message sending logic in one method, remove sendTTSMessage, add client shortcut in RESTMethods (#1031) * start wip rewrite of sending/editing messages * pass the build, modify the edit method to fit the new system * simplify the applyToClass method * change handling of file options * add back message splitting * i couldn't help myself * add some smart message options * clean up, add sexy options * fix indentation * fix up splitting * add \b * add back old methods for hydar happiness * clean up more * move code handling to the rest method * clean up this.rest.client * Update TextBasedChannel.js * add docs back for the bad methods * fix reply in group dms * srsly gawdl3y * make code better * fix changes for gawdl3y * fix checking * remove getter * make code handling more robust * k * fix up sendEmbed docs * stupid * fix up more docs because aaaaa * no more pls --- src/client/rest/RESTMethods.js | 218 +++++++++---------- src/structures/GuildMember.js | 2 +- src/structures/Message.js | 25 +-- src/structures/TextChannel.js | 2 +- src/structures/User.js | 2 +- src/structures/interface/TextBasedChannel.js | 150 +++++++------ 6 files changed, 201 insertions(+), 198 deletions(-) diff --git a/src/client/rest/RESTMethods.js b/src/client/rest/RESTMethods.js index d895922a7..02effb83a 100644 --- a/src/client/rest/RESTMethods.js +++ b/src/client/rest/RESTMethods.js @@ -2,6 +2,7 @@ const Constants = require('../../util/Constants'); const Collection = require('../../util/Collection'); const splitMessage = require('../../util/SplitMessage'); const parseEmoji = require('../../util/ParseEmoji'); +const escapeMarkdown = require('../../util/EscapeMarkdown'); const User = require('../../structures/User'); const GuildMember = require('../../structures/GuildMember'); @@ -15,13 +16,14 @@ const ClientOAuth2Application = require('../../structures/ClientOAuth2Applicatio class RESTMethods { constructor(restManager) { this.rest = restManager; + this.client = restManager.client; } - login(token = this.rest.client.token) { + login(token = this.client.token) { return new Promise((resolve, reject) => { if (typeof token !== 'string') throw new Error(Constants.Errors.INVALID_TOKEN); token = token.replace(/^Bot\s*/i, ''); - this.rest.client.manager.connectToWebSocket(token, resolve, reject); + this.client.manager.connectToWebSocket(token, resolve, reject); }); } @@ -31,8 +33,8 @@ class RESTMethods { getGateway() { return this.rest.makeRequest('get', Constants.Endpoints.gateway, true).then(res => { - this.rest.client.ws.gateway = `${res.url}/?v=${Constants.PROTOCOL_VERSION}`; - return this.rest.client.ws.gateway; + this.client.ws.gateway = `${res.url}/?v=${Constants.PROTOCOL_VERSION}`; + return this.client.ws.gateway; }); } @@ -40,63 +42,64 @@ class RESTMethods { return this.rest.makeRequest('get', Constants.Endpoints.botGateway, true); } - sendMessage(channel, content, { tts, nonce, embed, disableEveryone, split } = {}, file = null) { + sendMessage(channel, content, { tts, nonce, embed, disableEveryone, split, code } = {}, file = null) { return new Promise((resolve, reject) => { - if (typeof content !== 'undefined') content = this.rest.client.resolver.resolveString(content); + if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content); if (content) { - if (disableEveryone || (typeof disableEveryone === 'undefined' && this.rest.client.options.disableEveryone)) { + if (code) { + content = escapeMarkdown(this.client.resolver.resolveString(content), true); + content = `\`\`\`${typeof code !== 'undefined' && code !== null ? code : ''}\n${content}\n\`\`\``; + } + + if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) { content = content.replace(/@(everyone|here)/g, '@\u200b$1'); } if (split) content = splitMessage(content, typeof split === 'object' ? split : {}); } + const send = (chan) => { + if (content instanceof Array) { + const messages = []; + (function sendChunk(list, index) { + const options = index === list.length ? { tts, embed } : { tts }; + chan.send(list[index], options, index === list.length ? file : null).then((message) => { + messages.push(message); + if (index >= list.length) return resolve(messages); + return sendChunk(list, ++index); + }); + }(content, 0)); + } else { + this.rest.makeRequest('post', Constants.Endpoints.channelMessages(chan.id), true, { + content, tts, nonce, embed, + }, file).then(data => resolve(this.client.actions.MessageCreate.handle(data).message), reject); + } + }; + if (channel instanceof User || channel instanceof GuildMember) { - this.createDM(channel).then(chan => { - this._sendMessageRequest(chan, content, file, tts, nonce, embed, resolve, reject); - }, reject); + this.createDM(channel).then(send, reject); } else { - this._sendMessageRequest(channel, content, file, tts, nonce, embed, resolve, reject); + send(channel); } }); } - _sendMessageRequest(channel, content, file, tts, nonce, embed, resolve, reject) { - if (content instanceof Array) { - const datas = []; - let promise = this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, { - content: content[0], tts, nonce, - }, file).catch(reject); - - for (let i = 1; i <= content.length; i++) { - if (i < content.length) { - const i2 = i; - promise = promise.then(data => { - datas.push(data); - return this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, { - content: content[i2], tts, nonce, embed, - }, file); - }, reject); - } else { - promise.then(data => { - datas.push(data); - resolve(this.rest.client.actions.MessageCreate.handle(datas).messages); - }, reject); - } - } - } else { - this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, { - content, tts, nonce, embed, - }, file) - .then(data => resolve(this.rest.client.actions.MessageCreate.handle(data).message), reject); + updateMessage(message, content, { embed, code } = {}) { + content = this.client.resolver.resolveString(content); + if (code) { + content = escapeMarkdown(this.client.resolver.resolveString(content), true); + content = `\`\`\`${typeof code !== 'undefined' && code !== null ? code : ''}\n${content}\n\`\`\``; } + return this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, { + content, embed, + }).then(data => this.client.actions.MessageUpdate.handle(data).updated); } deleteMessage(message) { return this.rest.makeRequest('del', Constants.Endpoints.channelMessage(message.channel.id, message.id), true) .then(() => - this.rest.client.actions.MessageDelete.handle({ + this.client.actions.MessageDelete.handle({ id: message.id, channel_id: message.channel.id, }).message @@ -107,39 +110,32 @@ class RESTMethods { return this.rest.makeRequest('post', `${Constants.Endpoints.channelMessages(channel.id)}/bulk_delete`, true, { messages, }).then(() => - this.rest.client.actions.MessageDeleteBulk.handle({ + this.client.actions.MessageDeleteBulk.handle({ channel_id: channel.id, ids: messages, }).messages ); } - updateMessage(message, content, { embed } = {}) { - content = this.rest.client.resolver.resolveString(content); - return this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, { - content, embed, - }).then(data => this.rest.client.actions.MessageUpdate.handle(data).updated); - } - createChannel(guild, channelName, channelType, overwrites) { if (overwrites instanceof Collection) overwrites = overwrites.array(); return this.rest.makeRequest('post', Constants.Endpoints.guildChannels(guild.id), true, { name: channelName, type: channelType, permission_overwrites: overwrites, - }).then(data => this.rest.client.actions.ChannelCreate.handle(data).channel); + }).then(data => this.client.actions.ChannelCreate.handle(data).channel); } createDM(recipient) { const dmChannel = this.getExistingDM(recipient); if (dmChannel) return Promise.resolve(dmChannel); - return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.rest.client.user.id), true, { + return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.client.user.id), true, { recipient_id: recipient.id, - }).then(data => this.rest.client.actions.ChannelCreate.handle(data).channel); + }).then(data => this.client.actions.ChannelCreate.handle(data).channel); } getExistingDM(recipient) { - return this.rest.client.channels.find(channel => + return this.client.channels.find(channel => channel.recipient && channel.recipient.id === recipient.id ); } @@ -149,7 +145,7 @@ class RESTMethods { if (!channel) return Promise.reject(new Error('No channel to delete.')); return this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true).then(data => { data.id = channel.id; - return this.rest.client.actions.ChannelDelete.handle(data).channel; + return this.client.actions.ChannelDelete.handle(data).channel; }); } @@ -161,38 +157,38 @@ class RESTMethods { data.bitrate = _data.bitrate || channel.bitrate; data.user_limit = _data.userLimit || channel.userLimit; return this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data).then(newData => - this.rest.client.actions.ChannelUpdate.handle(newData).updated + this.client.actions.ChannelUpdate.handle(newData).updated ); } leaveGuild(guild) { - if (guild.ownerID === this.rest.client.user.id) return Promise.reject(new Error('Guild is owned by the client.')); + if (guild.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.')); return this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true).then(() => - this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild + this.client.actions.GuildDelete.handle({ id: guild.id }).guild ); } createGuild(options) { - options.icon = this.rest.client.resolver.resolveBase64(options.icon) || null; + options.icon = this.client.resolver.resolveBase64(options.icon) || null; options.region = options.region || 'us-central'; return new Promise((resolve, reject) => { this.rest.makeRequest('post', Constants.Endpoints.guilds, true, options).then(data => { - if (this.rest.client.guilds.has(data.id)) { - resolve(this.rest.client.guilds.get(data.id)); + if (this.client.guilds.has(data.id)) { + resolve(this.client.guilds.get(data.id)); return; } const handleGuild = guild => { if (guild.id === data.id) { - this.rest.client.removeListener('guildCreate', handleGuild); - this.rest.client.clearTimeout(timeout); + this.client.removeListener('guildCreate', handleGuild); + this.client.clearTimeout(timeout); resolve(guild); } }; - this.rest.client.on('guildCreate', handleGuild); + this.client.on('guildCreate', handleGuild); - const timeout = this.rest.client.setTimeout(() => { - this.rest.client.removeListener('guildCreate', handleGuild); + const timeout = this.client.setTimeout(() => { + this.client.removeListener('guildCreate', handleGuild); reject(new Error('Took too long to receive guild data.')); }, 10000); }, reject); @@ -202,28 +198,28 @@ class RESTMethods { // untested but probably will work deleteGuild(guild) { return this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true).then(() => - this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild + this.client.actions.GuildDelete.handle({ id: guild.id }).guild ); } getUser(userID) { return this.rest.makeRequest('get', Constants.Endpoints.user(userID), true).then(data => - this.rest.client.actions.UserGet.handle(data).user + this.client.actions.UserGet.handle(data).user ); } updateCurrentUser(_data, password) { - const user = this.rest.client.user; + const user = this.client.user; const data = {}; data.username = _data.username || user.username; - data.avatar = this.rest.client.resolver.resolveBase64(_data.avatar) || user.avatar; + data.avatar = this.client.resolver.resolveBase64(_data.avatar) || user.avatar; if (!user.bot) { data.email = _data.email || user.email; data.password = password; if (_data.new_password) data.new_password = _data.newPassword; } return this.rest.makeRequest('patch', Constants.Endpoints.me, true, data).then(newData => - this.rest.client.actions.UserUpdate.handle(newData).updated + this.client.actions.UserUpdate.handle(newData).updated ); } @@ -232,19 +228,19 @@ class RESTMethods { if (_data.name) data.name = _data.name; if (_data.region) data.region = _data.region; if (_data.verificationLevel) data.verification_level = Number(_data.verificationLevel); - if (_data.afkChannel) data.afk_channel_id = this.rest.client.resolver.resolveChannel(_data.afkChannel).id; + if (_data.afkChannel) data.afk_channel_id = this.client.resolver.resolveChannel(_data.afkChannel).id; if (_data.afkTimeout) data.afk_timeout = Number(_data.afkTimeout); - if (_data.icon) data.icon = this.rest.client.resolver.resolveBase64(_data.icon); - if (_data.owner) data.owner_id = this.rest.client.resolver.resolveUser(_data.owner).id; - if (_data.splash) data.splash = this.rest.client.resolver.resolveBase64(_data.splash); + if (_data.icon) data.icon = this.client.resolver.resolveBase64(_data.icon); + if (_data.owner) data.owner_id = this.client.resolver.resolveUser(_data.owner).id; + if (_data.splash) data.splash = this.client.resolver.resolveBase64(_data.splash); return this.rest.makeRequest('patch', Constants.Endpoints.guild(guild.id), true, data).then(newData => - this.rest.client.actions.GuildUpdate.handle(newData).updated + this.client.actions.GuildUpdate.handle(newData).updated ); } kickGuildMember(guild, member) { return this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true).then(() => - this.rest.client.actions.GuildMemberRemove.handle({ + this.client.actions.GuildMemberRemove.handle({ guild_id: guild.id, user: member.user, }).member @@ -253,7 +249,7 @@ class RESTMethods { createGuildRole(guild) { return this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true).then(role => - this.rest.client.actions.GuildRoleCreate.handle({ + this.client.actions.GuildRoleCreate.handle({ guild_id: guild.id, role, }).role @@ -262,7 +258,7 @@ class RESTMethods { deleteGuildRole(role) { return this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true).then(() => - this.rest.client.actions.GuildRoleDelete.handle({ + this.client.actions.GuildRoleDelete.handle({ guild_id: role.guild.id, role_id: role.id, }).role @@ -301,17 +297,17 @@ class RESTMethods { getGuildMember(guild, user) { return this.rest.makeRequest('get', Constants.Endpoints.guildMember(guild.id, user.id), true).then(data => - this.rest.client.actions.GuildMemberGet.handle(guild, data).member + this.client.actions.GuildMemberGet.handle(guild, data).member ); } updateGuildMember(member, data) { - if (data.channel) data.channel_id = this.rest.client.resolver.resolveChannel(data.channel).id; + if (data.channel) data.channel_id = this.client.resolver.resolveChannel(data.channel).id; if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role); let endpoint = Constants.Endpoints.guildMember(member.guild.id, member.id); // fix your endpoints, discord ;-; - if (member.id === this.rest.client.user.id) { + if (member.id === this.client.user.id) { const keys = Object.keys(data); if (keys.length === 1 && keys[0] === 'nick') { endpoint = Constants.Endpoints.guildMemberNickname(member.guild.id); @@ -348,7 +344,7 @@ class RESTMethods { } banGuildMember(guild, member, deleteDays = 0) { - const id = this.rest.client.resolver.resolveUserID(member); + const id = this.client.resolver.resolveUserID(member); if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.')); return this.rest.makeRequest( 'put', `${Constants.Endpoints.guildBans(guild.id)}/${id}?delete-message-days=${deleteDays}`, true, { @@ -356,9 +352,9 @@ class RESTMethods { } ).then(() => { if (member instanceof GuildMember) return member; - const user = this.rest.client.resolver.resolveUser(id); + const user = this.client.resolver.resolveUser(id); if (user) { - member = this.rest.client.resolver.resolveGuildMember(guild, user); + member = this.client.resolver.resolveGuildMember(guild, user); return member || user; } return id; @@ -367,26 +363,26 @@ class RESTMethods { unbanGuildMember(guild, member) { return new Promise((resolve, reject) => { - const id = this.rest.client.resolver.resolveUserID(member); + const id = this.client.resolver.resolveUserID(member); if (!id) throw new Error('Couldn\'t resolve the user ID to unban.'); const listener = (eGuild, eUser) => { if (eGuild.id === guild.id && eUser.id === id) { - this.rest.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); - this.rest.client.clearTimeout(timeout); + this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); + this.client.clearTimeout(timeout); resolve(eUser); } }; - this.rest.client.on(Constants.Events.GUILD_BAN_REMOVE, listener); + this.client.on(Constants.Events.GUILD_BAN_REMOVE, listener); - const timeout = this.rest.client.setTimeout(() => { - this.rest.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); + const timeout = this.client.setTimeout(() => { + this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); reject(new Error('Took too long to receive the ban remove event.')); }, 10000); this.rest.makeRequest('del', `${Constants.Endpoints.guildBans(guild.id)}/${id}`, true).catch(err => { - this.rest.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); - this.rest.client.clearTimeout(timeout); + this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); + this.client.clearTimeout(timeout); reject(err); }); }); @@ -396,7 +392,7 @@ class RESTMethods { return this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true).then(banItems => { const bannedUsers = new Collection(); for (const banItem of banItems) { - const user = this.rest.client.dataManager.newUser(banItem.user); + const user = this.client.dataManager.newUser(banItem.user); bannedUsers.set(user.id, user); } return bannedUsers; @@ -428,7 +424,7 @@ class RESTMethods { return this.rest.makeRequest( 'patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data ).then(_role => - this.rest.client.actions.GuildRoleUpdate.handle({ + this.client.actions.GuildRoleUpdate.handle({ role: _role, guild_id: role.guild.id, }).updated @@ -455,7 +451,7 @@ class RESTMethods { payload.max_age = options.maxAge; payload.max_uses = options.maxUses; return this.rest.makeRequest('post', `${Constants.Endpoints.channelInvites(channel.id)}`, true, payload) - .then(invite => new Invite(this.rest.client, invite)); + .then(invite => new Invite(this.client, invite)); } deleteInvite(invite) { @@ -464,7 +460,7 @@ class RESTMethods { getInvite(code) { return this.rest.makeRequest('get', Constants.Endpoints.invite(code), true).then(invite => - new Invite(this.rest.client, invite) + new Invite(this.client, invite) ); } @@ -472,7 +468,7 @@ class RESTMethods { return this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true).then(inviteItems => { const invites = new Collection(); for (const inviteItem of inviteItems) { - const invite = new Invite(this.rest.client, inviteItem); + const invite = new Invite(this.client, inviteItem); invites.set(invite.code, invite); } return invites; @@ -486,24 +482,24 @@ class RESTMethods { createEmoji(guild, image, name) { return this.rest.makeRequest('post', `${Constants.Endpoints.guildEmojis(guild.id)}`, true, { name, image }) - .then(data => this.rest.client.actions.EmojiCreate.handle(data, guild).emoji); + .then(data => this.client.actions.EmojiCreate.handle(data, guild).emoji); } deleteEmoji(emoji) { return this.rest.makeRequest('delete', `${Constants.Endpoints.guildEmojis(emoji.guild.id)}/${emoji.id}`, true) - .then(() => this.rest.client.actions.EmojiDelete.handle(emoji).data); + .then(() => this.client.actions.EmojiDelete.handle(emoji).data); } getWebhook(id, token) { return this.rest.makeRequest('get', Constants.Endpoints.webhook(id, token), !token).then(data => - new Webhook(this.rest.client, data) + new Webhook(this.client, data) ); } getGuildWebhooks(guild) { return this.rest.makeRequest('get', Constants.Endpoints.guildWebhooks(guild.id), true).then(data => { const hooks = new Collection(); - for (const hook of data) hooks.set(hook.id, new Webhook(this.rest.client, hook)); + for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); return hooks; }); } @@ -511,14 +507,14 @@ class RESTMethods { getChannelWebhooks(channel) { return this.rest.makeRequest('get', Constants.Endpoints.channelWebhooks(channel.id), true).then(data => { const hooks = new Collection(); - for (const hook of data) hooks.set(hook.id, new Webhook(this.rest.client, hook)); + for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); return hooks; }); } createWebhook(channel, name, avatar) { return this.rest.makeRequest('post', Constants.Endpoints.channelWebhooks(channel.id), true, { name, avatar }) - .then(data => new Webhook(this.rest.client, data)); + .then(data => new Webhook(this.client, data)); } editWebhook(webhook, name, avatar) { @@ -537,9 +533,9 @@ class RESTMethods { } sendWebhookMessage(webhook, content, { avatarURL, tts, disableEveryone, embeds } = {}, file = null) { - if (typeof content !== 'undefined') content = this.rest.client.resolver.resolveString(content); + if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content); if (content) { - if (disableEveryone || (typeof disableEveryone === 'undefined' && this.rest.client.options.disableEveryone)) { + if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) { content = content.replace(/@(everyone|here)/g, '@\u200b$1'); } } @@ -570,7 +566,7 @@ class RESTMethods { return this.rest.makeRequest( 'get', Constants.Endpoints.meMentions(options.limit, options.roles, options.everyone, options.guild) - ).then(res => res.body.map(m => new Message(this.rest.client.channels.get(m.channel_id), m, this.rest.client))); + ).then(res => res.body.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); } addFriend(user) { @@ -597,7 +593,7 @@ class RESTMethods { setRolePositions(guildID, roles) { return this.rest.makeRequest('patch', Constants.Endpoints.guildRoles(guildID), true, roles).then(() => - this.rest.client.actions.GuildRolesPositionUpdate.handle({ + this.client.actions.GuildRolesPositionUpdate.handle({ guild_id: guildID, roles, }).guild @@ -608,8 +604,8 @@ class RESTMethods { return this.rest.makeRequest( 'put', Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji), true ).then(() => - this.rest.client.actions.MessageReactionAdd.handle({ - user_id: this.rest.client.user.id, + this.client.actions.MessageReactionAdd.handle({ + user_id: this.client.user.id, message_id: message.id, emoji: parseEmoji(emoji), channel_id: message.channel.id, @@ -619,11 +615,11 @@ class RESTMethods { removeMessageReaction(message, emoji, user) { let endpoint = Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji); - if (user.id !== this.rest.client.user.id) { + if (user.id !== this.client.user.id) { endpoint = Constants.Endpoints.userMessageReaction(message.channel.id, message.id, emoji, null, user.id); } return this.rest.makeRequest('delete', endpoint, true).then(() => - this.rest.client.actions.MessageReactionRemove.handle({ + this.client.actions.MessageReactionRemove.handle({ user_id: user.id, message_id: message.id, emoji: parseEmoji(emoji), @@ -645,7 +641,7 @@ class RESTMethods { getMyApplication() { return this.rest.makeRequest('get', Constants.Endpoints.myApplication, true).then(app => - new ClientOAuth2Application(this.rest.client, app) + new ClientOAuth2Application(this.client, app) ); } diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 87c78b68f..60a498a3e 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -430,8 +430,8 @@ class GuildMember { } // These are here only for documentation purposes - they are implemented by TextBasedChannel + send() { return; } sendMessage() { return; } - sendTTSMessage() { return; } sendEmbed() { return; } sendFile() { return; } sendCode() { return; } diff --git a/src/structures/Message.js b/src/structures/Message.js index dbd758543..ab6a82899 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -369,12 +369,13 @@ class Message { * Options that can be passed into editMessage * @typedef {Object} MessageEditOptions * @property {Object} [embed] An embed to be added/edited + * @property {string} [code] Language for optional codeblock formatting to apply */ /** * Edit the content of the message - * @param {StringResolvable} content The new content for the message - * @param {MessageEditOptions} [options={}] The options to provide + * @param {StringResolvable} [content] The new content for the message + * @param {MessageEditOptions} [options] The options to provide * @returns {Promise} * @example * // update the content of a message @@ -382,7 +383,13 @@ class Message { * .then(msg => console.log(`Updated the content of a message from ${msg.author}`)) * .catch(console.error); */ - edit(content, options = {}) { + edit(content, options) { + if (!options && typeof content === 'object') { + options = content; + content = ''; + } else if (!options) { + options = {}; + } return this.client.rest.methods.updateMessage(this, content, options); } @@ -467,16 +474,8 @@ class Message { * .catch(console.error); */ reply(content, options = {}) { - content = this.client.resolver.resolveString(content); - const prepend = this.guild ? `${this.author}, ` : ''; - content = `${prepend}${content}`; - - if (options.split) { - if (typeof options.split !== 'object') options.split = {}; - if (!options.split.prepend) options.split.prepend = prepend; - } - - return this.client.rest.methods.sendMessage(this.channel, content, options); + content = `${this.guild || this.channel.type === 'group' ? `${this.author}, ` : ''}${content}`; + return this.channel.send(content, options); } /** diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 449da6759..9697abd82 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -73,8 +73,8 @@ class TextChannel extends GuildChannel { } // These are here only for documentation purposes - they are implemented by TextBasedChannel + send() { return; } sendMessage() { return; } - sendTTSMessage() { return; } sendEmbed() { return; } sendFile() { return; } sendCode() { return; } diff --git a/src/structures/User.js b/src/structures/User.js index 9b3155ebd..993c8e192 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -265,8 +265,8 @@ class User { } // These are here only for documentation purposes - they are implemented by TextBasedChannel + send() { return; } sendMessage() { return; } - sendTTSMessage() { return; } sendEmbed() { return; } sendFile() { return; } sendCode() { return; } diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index 308545f05..61be68d0c 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -2,8 +2,7 @@ const path = require('path'); const Message = require('../Message'); const MessageCollector = require('../MessageCollector'); const Collection = require('../../util/Collection'); -const RichEmbed = require('../RichEmbed'); -const escapeMarkdown = require('../../util/EscapeMarkdown'); + /** * Interface for classes that have text-channel-like features @@ -25,7 +24,7 @@ class TextBasedChannel { } /** - * Options that can be passed into sendMessage, sendTTSMessage, sendFile, sendCode, or Message.reply + * Options that can be passed into send, sendMessage, sendFile, sendEmbed, sendCode, and Message#reply * @typedef {Object} MessageOptions * @property {boolean} [tts=false] Whether or not the message should be spoken aloud * @property {string} [nonce=''] The nonce for the message @@ -33,10 +32,18 @@ class TextBasedChannel { * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details) * @property {boolean} [disableEveryone=this.client.options.disableEveryone] Whether or not @everyone and @here * should be replaced with plain-text + * @property {FileOptions|string} [file] A file to send with the message + * @property {string} [code] Language for optional codeblock formatting to apply * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if * it exceeds the character limit. If an object is provided, these are the options for splitting the message. */ + /** + * @typedef {Object} FileOptions + * @property {BufferResolvable} attachment + * @property {string} [name='file.jpg'] + */ + /** * Options for splitting a message * @typedef {Object} SplitOptions @@ -46,6 +53,45 @@ class TextBasedChannel { * @property {string} [append=''] Text to append to every piece except the last */ + /** + * Send a message to this channel + * @param {StringResolvable} [content] The content to send + * @param {MessageOptions} [options={}] The options to provide + * @returns {Promise} + * @example + * // send a message + * channel.send('hello!') + * .then(message => console.log(`Sent message: ${message.content}`)) + * .catch(console.error); + */ + send(content, options) { + if (!options && typeof content === 'object') { + options = content; + content = ''; + } else if (!options) { + options = {}; + } + if (options.file) { + if (typeof options.file === 'string') options.file = { attachment: options.file }; + if (!options.file.name) { + if (typeof options.file.attachment === 'string') { + options.file.name = path.basename(options.file.attachment); + } else if (options.file.attachment && options.file.attachment.path) { + options.file.name = path.basename(options.file.attachment.path); + } else { + options.file.name = 'file.jpg'; + } + } + return this.client.resolver.resolveBuffer(options.file.attachment).then(file => + this.client.rest.methods.sendMessage(this, content, options, { + file, + name: options.file.name, + }) + ); + } + return this.client.rest.methods.sendMessage(this, content, options); + } + /** * Send a message to this channel * @param {StringResolvable} content The content to send @@ -57,88 +103,48 @@ class TextBasedChannel { * .then(message => console.log(`Sent message: ${message.content}`)) * .catch(console.error); */ - sendMessage(content, options = {}) { - return this.client.rest.methods.sendMessage(this, content, options); - } - - /** - * Send a text-to-speech message to this channel - * @param {StringResolvable} content The content to send - * @param {MessageOptions} [options={}] The options to provide - * @returns {Promise} - * @example - * // send a TTS message - * channel.sendTTSMessage('hello!') - * .then(message => console.log(`Sent tts message: ${message.content}`)) - * .catch(console.error); - */ - sendTTSMessage(content, options = {}) { - Object.assign(options, { tts: true }); - return this.client.rest.methods.sendMessage(this, content, options); + sendMessage(content, options) { + return this.send(content, options); } /** * Send an embed to this channel * @param {RichEmbed|Object} embed The embed to send - * @param {string|MessageOptions} contentOrOptions Content to send or message options - * @param {MessageOptions} options If contentOrOptions is content, this will be options + * @param {string} [content] Content to send + * @param {MessageOptions} [options] The options to provide * @returns {Promise} */ - sendEmbed(embed, contentOrOptions, options = {}) { - if (!(embed instanceof RichEmbed)) embed = new RichEmbed(embed); - let content; - if (contentOrOptions) { - if (typeof contentOrOptions === 'string') { - content = contentOrOptions; - } else { - options = contentOrOptions; - } + sendEmbed(embed, content, options) { + if (!options && typeof content === 'object') { + options = content; + content = ''; + } else if (!options) { + options = {}; } - options.embed = embed; - return this.sendMessage(content, options); + return this.send(content, Object.assign(options, { embed })); } /** * Send a file to this channel * @param {BufferResolvable} attachment The file to send - * @param {string} [fileName="file.jpg"] The name and extension of the file + * @param {string} [name='file.jpg'] The name and extension of the file * @param {StringResolvable} [content] Text message to send with the attachment * @param {MessageOptions} [options] The options to provide * @returns {Promise} */ - sendFile(attachment, fileName, content, options = {}) { - if (!fileName) { - if (typeof attachment === 'string') { - fileName = path.basename(attachment); - } else if (attachment && attachment.path) { - fileName = path.basename(attachment.path); - } else { - fileName = 'file.jpg'; - } - } - return this.client.resolver.resolveBuffer(attachment).then(file => - this.client.rest.methods.sendMessage(this, content, options, { - file, - name: fileName, - }) - ); + sendFile(attachment, name, content, options = {}) { + return this.send(content, Object.assign(options, { file: { attachment, name } })); } /** * Send a code block to this channel * @param {string} lang Language for the code block * @param {StringResolvable} content Content of the code block - * @param {MessageOptions} options The options to provide + * @param {MessageOptions} [options] The options to provide * @returns {Promise} */ sendCode(lang, content, options = {}) { - if (options.split) { - if (typeof options.split !== 'object') options.split = {}; - if (!options.split.prepend) options.split.prepend = `\`\`\`${lang || ''}\n`; - if (!options.split.append) options.split.append = '\n```'; - } - content = escapeMarkdown(this.client.resolver.resolveString(content), true); - return this.sendMessage(`\`\`\`${lang || ''}\n${content}\n\`\`\``, options); + return this.send(content, Object.assign(options, { code: lang })); } /** @@ -349,19 +355,21 @@ class TextBasedChannel { } exports.applyToClass = (structure, full = false) => { - const props = ['sendMessage', 'sendTTSMessage', 'sendEmbed', 'sendFile', 'sendCode']; + const props = ['send', 'sendMessage', 'sendEmbed', 'sendFile', 'sendCode']; if (full) { - props.push('_cacheMessage'); - props.push('fetchMessages'); - props.push('fetchMessage'); - props.push('bulkDelete'); - props.push('startTyping'); - props.push('stopTyping'); - props.push('typing'); - props.push('typingCount'); - props.push('fetchPinnedMessages'); - props.push('createCollector'); - props.push('awaitMessages'); + props.push( + '_cacheMessage', + 'fetchMessages', + 'fetchMessage', + 'bulkDelete', + 'startTyping', + 'stopTyping', + 'typing', + 'typingCount', + 'fetchPinnedMessages', + 'createCollector', + 'awaitMessages' + ); } for (const prop of props) { Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop)); From fe7ed523b3ae8f285249acf93830698188922adc Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Thu, 29 Dec 2016 03:01:28 -0500 Subject: [PATCH 13/16] Improve ClientUser presence method docs --- src/structures/ClientUser.js | 128 ++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 66b9504e9..4db1eeb1e 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -119,8 +119,74 @@ class ClientUser extends User { } /** - * Set the status of the logged in user. - * @param {string} status can be `online`, `idle`, `invisible` or `dnd` (do not disturb) + * Data resembling a raw Discord presence + * @property {PresenceStatus} [status] Status of the user + * @property {boolean} [afk] Whether the user is AFK + * @property {Object} [game] Game the user is playing + * @property {string} [game.name] Name of the game + * @property {string} [game.url] Twitch stream URL + */ + + /** + * Sets the full presence of the client user. + * @param {PresenceData} data Data for the presence + * @returns {Promise} + */ + setPresence(data) { + // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}} + return new Promise(resolve => { + let status = this.localPresence.status || this.presence.status; + let game = this.localPresence.game; + let afk = this.localPresence.afk || this.presence.afk; + + if (!game && this.presence.game) { + game = { + name: this.presence.game.name, + type: this.presence.game.type, + url: this.presence.game.url, + }; + } + + if (data.status) { + if (typeof data.status !== 'string') throw new TypeError('Status must be a string'); + status = data.status; + } + + if (data.game) { + game = data.game; + if (game.url) game.type = 1; + } + + if (typeof data.afk !== 'undefined') afk = data.afk; + afk = Boolean(afk); + + this.localPresence = { status, game, afk }; + this.localPresence.since = 0; + this.localPresence.game = this.localPresence.game || null; + + this.client.ws.send({ + op: 3, + d: this.localPresence, + }); + + this.client._setPresence(this.id, this.localPresence); + + resolve(this); + }); + } + + /** + * A user's status. Must be one of: + * - `online` + * - `idle` + * - `invisible` + * - `dnd` (do not disturb) + * @typedef {string} PresenceStatus + */ + + /** + * Sets the status of the client user. + * @param {PresenceStatus} status Status to change to * @returns {Promise} */ setStatus(status) { @@ -128,9 +194,9 @@ class ClientUser extends User { } /** - * Set the current game of the logged in user. - * @param {string} game the game being played - * @param {string} [streamingURL] an optional URL to a twitch stream, if one is available. + * Sets the game the client user is playing. + * @param {string} game Game being played + * @param {string} [streamingURL] Twitch stream URL * @returns {Promise} */ setGame(game, streamingURL) { @@ -141,8 +207,8 @@ class ClientUser extends User { } /** - * Set/remove the AFK flag for the current user. - * @param {boolean} afk whether or not the user is AFK. + * Sets/removes the AFK flag for the client user. + * @param {boolean} afk Whether or not the user is AFK * @returns {Promise} */ setAFK(afk) { @@ -202,54 +268,6 @@ class ClientUser extends User { ); } } - - /** - * Set the full presence of the current user. - * @param {Object} data the data to provide - * @returns {Promise} - */ - setPresence(data) { - // {"op":3,"d":{"status":"dnd","since":0,"game":null,"afk":false}} - return new Promise(resolve => { - let status = this.localPresence.status || this.presence.status; - let game = this.localPresence.game; - let afk = this.localPresence.afk || this.presence.afk; - - if (!game && this.presence.game) { - game = { - name: this.presence.game.name, - type: this.presence.game.type, - url: this.presence.game.url, - }; - } - - if (data.status) { - if (typeof data.status !== 'string') throw new TypeError('Status must be a string'); - status = data.status; - } - - if (data.game) { - game = data.game; - if (game.url) game.type = 1; - } - - if (typeof data.afk !== 'undefined') afk = data.afk; - afk = Boolean(afk); - - this.localPresence = { status, game, afk }; - this.localPresence.since = 0; - this.localPresence.game = this.localPresence.game || null; - - this.client.ws.send({ - op: 3, - d: this.localPresence, - }); - - this.client._setPresence(this.id, this.localPresence); - - resolve(this); - }); - } } module.exports = ClientUser; From 1b76333b8bba488104fdc74d95b6c624f3c5d34a Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Thu, 29 Dec 2016 03:04:11 -0500 Subject: [PATCH 14/16] Add missing @typedef line --- src/structures/ClientUser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 4db1eeb1e..d526af6a7 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -120,6 +120,7 @@ class ClientUser extends User { /** * Data resembling a raw Discord presence + * @typedef {Object} PresenceData * @property {PresenceStatus} [status] Status of the user * @property {boolean} [afk] Whether the user is AFK * @property {Object} [game] Game the user is playing From 95790dcf08a97175ed88a6736e19a96a94f4a4ea Mon Sep 17 00:00:00 2001 From: Schuyler Cebulskie Date: Thu, 29 Dec 2016 03:21:22 -0500 Subject: [PATCH 15/16] Update User#equals doc --- src/structures/User.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/User.js b/src/structures/User.js index 993c8e192..f7148289a 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -237,7 +237,7 @@ class User { } /** - * Checks if the user is equal to another. It compares username, ID, discriminator, status and the game being played. + * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags. * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. * @param {User} user User to compare with * @returns {boolean} From 6d7293e3c5c53c279758d66c31e20cc1272ab79f Mon Sep 17 00:00:00 2001 From: Amish Shah Date: Thu, 29 Dec 2016 14:00:54 +0000 Subject: [PATCH 16/16] Fix sending an array of messages --- src/structures/interface/TextBasedChannel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index 61be68d0c..536131332 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -65,7 +65,7 @@ class TextBasedChannel { * .catch(console.error); */ send(content, options) { - if (!options && typeof content === 'object') { + if (!options && typeof content === 'object' && !(content instanceof Array)) { options = content; content = ''; } else if (!options) {