refactor: remove user bot methods (#2559)

* [WIP] Remove user bots

* more backend userbot removal

* Add mfaEnabled back

* revert client presences store removal

* partially revert getAuth changes

* remove more no longer used children of ClientUserGuildSettings

* fix a bug with this pr and TextBasedChannel.applyToClass

* remove a syncGuilds reference

* more user bot data handling

* various guildSync cleanup

* bots can't call logout

Had the user/bot portions of the code mixed up. Though, does this need to be a promise anymore?

* make ClientManager#destroy() sync

It nolonger needs to be a promise, and nothing depended on it being a promise that I can tell.

* requested change

* Fix massive error

* no longer used as it's userbot only
This commit is contained in:
bdistin
2018-07-25 21:14:23 -05:00
committed by Isabella
parent f963621ef1
commit 5afd77ab73
34 changed files with 15 additions and 1103 deletions

View File

@@ -170,26 +170,6 @@ class ClientApplication extends Base {
} });
}
/**
* Resets the app's secret.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<ClientApplication>}
*/
resetSecret() {
return this.client.api.oauth2.applications[this.id].reset.post()
.then(app => new ClientApplication(this.client, app));
}
/**
* Resets the app's bot token.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<ClientApplication>}
*/
resetToken() {
return this.client.api.oauth2.applications[this.id].bot.reset.post()
.then(app => new ClientApplication(this.client, Object.assign({}, this, { bot: app })));
}
/**
* When concatenated with a string, this automatically returns the application's name instead of the
* ClientApplication object.

View File

@@ -1,10 +1,5 @@
const Structures = require('../util/Structures');
const Collection = require('../util/Collection');
const ClientUserSettings = require('./ClientUserSettings');
const ClientUserGuildSettings = require('./ClientUserGuildSettings');
const Util = require('../util/Util');
const DataResolver = require('../util/DataResolver');
const Guild = require('./Guild');
/**
* Represents the logged in client's Discord user.
@@ -20,75 +15,14 @@ class ClientUser extends Structures.get('User') {
*/
this.verified = data.verified;
/**
* The email of this account
* <warn>This is only filled when using a user account.</warn>
* @type {?string}
*/
this.email = data.email;
this._typing = new Map();
/**
* A Collection of friends for the logged in user
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, User>}
*/
this.friends = new Collection();
/**
* A Collection of blocked users for the logged in user
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, User>}
*/
this.blocked = new Collection();
/**
* A Collection of notes for the logged in user
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, string>}
*/
this.notes = new Collection();
/**
* If the user has Discord premium (nitro)
* <warn>This is only filled when using a user account.</warn>
* @type {?boolean}
*/
this.premium = typeof data.premium === 'boolean' ? data.premium : null;
/**
* If the user has MFA enabled on their account
* <warn>This is only filled when using a user account.</warn>
* If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account
* @type {?boolean}
*/
this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null;
/**
* If the user has ever used a mobile device on Discord
* <warn>This is only filled when using a user account.</warn>
* @type {?boolean}
*/
this.mobile = typeof data.mobile === 'boolean' ? data.mobile : null;
/**
* Various settings for this user
* <warn>This is only filled when using a user account.</warn>
* @type {?ClientUserSettings}
*/
this.settings = data.user_settings ? new ClientUserSettings(this, data.user_settings) : null;
/**
* All of the user's guild settings
* <warn>This is only filled when using a user account.</warn>
* @type {Collection<Snowflake, ClientUserGuildSettings>}
*/
this.guildSettings = new Collection();
if (data.user_guild_settings) {
for (const settings of data.user_guild_settings) {
this.guildSettings.set(settings.guild_id, new ClientUserGuildSettings(this.client, settings));
}
}
if (data.token) this.client.token = data.token;
}
@@ -101,15 +35,7 @@ class ClientUser extends Structures.get('User') {
return this.client.presences.clientPresence;
}
edit(data, passcode) {
if (!this.bot) {
if (typeof passcode !== 'object') {
data.password = passcode;
} else {
data.code = passcode.mfaCode;
data.password = passcode.password;
}
}
edit(data) {
return this.client.api.users('@me').patch({ data })
.then(newData => {
this.client.token = newData.token;
@@ -122,7 +48,6 @@ class ClientUser extends Structures.get('User') {
* <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
* every hour. Use this sparingly!</info>
* @param {string} username The new username
* @param {string} [password] Current password (only for user accounts)
* @returns {Promise<ClientUser>}
* @example
* // Set username
@@ -130,43 +55,8 @@ class ClientUser extends Structures.get('User') {
* .then(user => console.log(`My new username is ${user.username}`))
* .catch(console.error);
*/
setUsername(username, password) {
return this.edit({ username }, password);
}
/**
* Changes the email for the client user's account.
* <warn>This is only available when using a user account.</warn>
* @param {string} email New email to change to
* @param {string} password Current password
* @returns {Promise<ClientUser>}
* @example
* // Set email
* client.user.setEmail('bob@gmail.com', 'some amazing password 123')
* .then(user => console.log(`My new email is ${user.email}`))
* .catch(console.error);
*/
setEmail(email, password) {
return this.edit({ email }, password);
}
/**
* Changes the password for the client user's account.
* <warn>This is only available when using a user account.</warn>
* @param {string} newPassword New password to change to
* @param {Object|string} options Object containing an MFA code, password or both.
* Can be just a string for the password.
* @param {string} [options.oldPassword] Current password
* @param {string} [options.mfaCode] Timed MFA Code
* @returns {Promise<ClientUser>}
* @example
* // Set password
* client.user.setPassword('some new amazing password 456', 'some amazing password 123')
* .then(user => console.log('New password set!'))
* .catch(console.error);
*/
setPassword(newPassword, options) {
return this.edit({ new_password: newPassword }, { password: options.oldPassword, mfaCode: options.mfaCode });
setUsername(username) {
return this.edit({ username });
}
/**
@@ -262,36 +152,6 @@ class ClientUser extends Structures.get('User') {
return this.setPresence({ afk });
}
/**
* Fetches messages that mentioned the client's user.
* <warn>This is only available when using a user account.</warn>
* @param {Object} [options={}] Options for the fetch
* @param {number} [options.limit=25] Maximum number of mentions to retrieve
* @param {boolean} [options.roles=true] Whether to include role mentions
* @param {boolean} [options.everyone=true] Whether to include everyone/here mentions
* @param {GuildResolvable} [options.guild] Limit the search to a specific guild
* @returns {Promise<Message[]>}
* @example
* // Fetch mentions
* client.user.fetchMentions()
* .then(console.log)
* .catch(console.error);
* @example
* // Fetch mentions from a guild
* client.user.fetchMentions({
* guild: '222078108977594368'
* })
* .then(console.log)
* .catch(console.error);
*/
fetchMentions(options = {}) {
if (options.guild instanceof Guild) options.guild = options.guild.id;
Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options);
return this.client.api.users('@me').mentions.get({ query: options })
.then(data => data.map(m => this.client.channels.get(m.channel_id).messages.add(m, false)));
}
/**
* An object containing either a user or access token, and an optional nickname.
* @typedef {Object} GroupDMRecipientOptions
@@ -327,16 +187,6 @@ class ClientUser extends Structures.get('User') {
return this.client.api.users('@me').channels.post({ data })
.then(res => this.client.channels.add(res));
}
toJSON() {
return super.toJSON({
friends: false,
blocked: false,
notes: false,
settings: false,
guildSettings: false,
});
}
}
module.exports = ClientUser;

View File

@@ -1,28 +0,0 @@
const { UserChannelOverrideMap } = require('../util/Constants');
/**
* A wrapper around the ClientUser's channel overrides.
*/
class ClientUserChannelOverride {
constructor(data) {
this.patch(data);
}
/**
* Patch the data contained in this class with new partial data.
* @param {Object} data Data to patch this with
* @private
*/
patch(data) {
for (const [key, value] of Object.entries(UserChannelOverrideMap)) {
if (!data.hasOwnProperty(key)) continue;
if (typeof value === 'function') {
this[value.name] = value(data[key]);
} else {
this[value] = data[key];
}
}
}
}
module.exports = ClientUserChannelOverride;

View File

@@ -1,60 +0,0 @@
const { UserGuildSettingsMap } = require('../util/Constants');
const Collection = require('../util/Collection');
const ClientUserChannelOverride = require('./ClientUserChannelOverride');
/**
* A wrapper around the ClientUser's guild settings.
*/
class ClientUserGuildSettings {
constructor(client, data) {
/**
* The client that created the instance of the ClientUserGuildSettings
* @name ClientUserGuildSettings#client
* @type {Client}
* @readonly
*/
Object.defineProperty(this, 'client', { value: client });
/**
* The ID of the guild these settings are for
* @type {Snowflake}
*/
this.guildID = data.guild_id;
this.channelOverrides = new Collection();
this.patch(data);
}
/**
* Patch the data contained in this class with new partial data.
* @param {Object} data Data to patch this with
* @private
*/
patch(data) {
for (const [key, value] of Object.entries(UserGuildSettingsMap)) {
if (!data.hasOwnProperty(key)) continue;
if (key === 'channel_overrides') {
for (const channel of data[key]) {
const override = this.channelOverrides.get(channel.channel_id);
if (override) override.patch(channel);
else this.channelOverrides.set(channel.channel_id, new ClientUserChannelOverride(channel));
}
} else if (typeof value === 'function') {
this[value.name] = value(data[key]);
} else {
this[value] = data[key];
}
}
}
/**
* Update a specific property of the guild settings.
* @param {string} name Name of property
* @param {*} value Value to patch
* @returns {Promise<Object>}
* @private
*/
update(name, value) {
return this.client.api.users('@me').guilds(this.guildID).settings.patch({ data: { [name]: value } });
}
}
module.exports = ClientUserGuildSettings;

View File

@@ -1,80 +0,0 @@
const { UserSettingsMap } = require('../util/Constants');
const Util = require('../util/Util');
const { Error } = require('../errors');
/**
* A wrapper around the ClientUser's settings.
*/
class ClientUserSettings {
constructor(user, data) {
this.user = user;
this.patch(data);
}
/**
* Patch the data contained in this class with new partial data.
* @param {Object} data Data to patch this with
* @private
*/
patch(data) {
for (const [key, value] of Object.entries(UserSettingsMap)) {
if (!data.hasOwnProperty(key)) continue;
if (typeof value === 'function') {
this[value.name] = value(data[key]);
} else {
this[value] = data[key];
}
}
}
/**
* Update a specific property of of user settings.
* @param {string} name Name of property
* @param {*} value Value to patch
* @returns {Promise<Object>}
* @private
*/
update(name, value) {
return this.user.client.api.users['@me'].settings.patch({ data: { [name]: value } });
}
/**
* Sets the position of the guild in the guild listing.
* @param {Guild} guild The guild to move
* @param {number} position Absolute or relative position
* @param {boolean} [relative=false] Whether to position relatively or absolutely
* @returns {Promise<Guild>}
*/
setGuildPosition(guild, position, relative) {
const temp = Object.assign([], this.guildPositions);
Util.moveElementInArray(temp, guild.id, position, relative);
return this.update('guild_positions', temp).then(() => guild);
}
/**
* Adds a guild to the list of restricted guilds.
* @param {Guild} guild The guild to add
* @returns {Promise<Guild>}
*/
addRestrictedGuild(guild) {
const temp = Object.assign([], this.restrictedGuilds);
if (temp.includes(guild.id)) return Promise.reject(new Error('GUILD_RESTRICTED', true));
temp.push(guild.id);
return this.update('restricted_guilds', temp).then(() => guild);
}
/**
* Removes a guild from the list of restricted guilds.
* @param {Guild} guild The guild to remove
* @returns {Promise<Guild>}
*/
removeRestrictedGuild(guild) {
const temp = Object.assign([], this.restrictedGuilds);
const index = temp.indexOf(guild.id);
if (index < 0) return Promise.reject(new Error('GUILD_RESTRICTED'));
temp.splice(index, 1);
return this.update('restricted_guilds', temp).then(() => guild);
}
}
module.exports = ClientUserSettings;

View File

@@ -184,16 +184,12 @@ class GroupDMChannel extends Channel {
* @param {Object} options Options for this method
* @param {UserResolvable} options.user User to add to this Group DM
* @param {string} [options.accessToken] Access token to use to add the user to this Group DM
* (only available under a bot account)
* @param {string} [options.nick] Permanent nickname to give the user (only available under a bot account)
* @param {string} [options.nick] Permanent nickname to give the user
* @returns {Promise<GroupDMChannel>}
*/
addUser({ user, accessToken, nick }) {
const id = this.client.users.resolveID(user);
const data = this.client.user.bot ?
{ nick, access_token: accessToken } :
{ recipient: id };
return this.client.api.channels[this.id].recipients[id].put({ data })
return this.client.api.channels[this.id].recipients[id].put({ nick, access_token: accessToken })
.then(() => this);
}

View File

@@ -7,7 +7,6 @@ const Collection = require('../util/Collection');
const Util = require('../util/Util');
const DataResolver = require('../util/DataResolver');
const Snowflake = require('../util/Snowflake');
const Shared = require('./shared');
const GuildMemberStore = require('../stores/GuildMemberStore');
const RoleStore = require('../stores/RoleStore');
const GuildEmojiStore = require('../stores/GuildEmojiStore');
@@ -352,79 +351,6 @@ class Guild extends Base {
return this.client.voice.connections.get(this.id) || null;
}
/**
* The position of this guild
* <warn>This is only available when using a user account.</warn>
* @type {?number}
* @readonly
*/
get position() {
if (this.client.user.bot) return null;
if (!this.client.user.settings.guildPositions) return null;
return this.client.user.settings.guildPositions.indexOf(this.id);
}
/**
* Whether the guild is muted
* <warn>This is only available when using a user account.</warn>
* @type {?boolean}
* @readonly
*/
get muted() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.id).muted;
} catch (err) {
return false;
}
}
/**
* The type of message that should notify you
* one of `EVERYTHING`, `MENTIONS`, `NOTHING`
* <warn>This is only available when using a user account.</warn>
* @type {?string}
* @readonly
*/
get messageNotifications() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.id).messageNotifications;
} catch (err) {
return null;
}
}
/**
* Whether to receive mobile push notifications
* <warn>This is only available when using a user account.</warn>
* @type {?boolean}
* @readonly
*/
get mobilePush() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.id).mobilePush;
} catch (err) {
return false;
}
}
/**
* Whether to suppress everyone messages
* <warn>This is only available when using a user account.</warn>
* @type {?boolean}
* @readonly
*/
get suppressEveryone() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.id).suppressEveryone;
} catch (err) {
return null;
}
}
/**
* The `@everyone` role of the guild
* @type {?Role}
@@ -597,26 +523,6 @@ class Guild extends Base {
.then(data => this.members.add(data));
}
/**
* Performs a search within the entire guild.
* <warn>This is only available when using a user account.</warn>
* @param {MessageSearchOptions} [options={}] Options to pass to the search
* @returns {Promise<MessageSearchResult>}
* @example
* guild.search({
* content: 'discord.js',
* before: '2016-11-17'
* })
* .then(res => {
* const hit = res.results[0].find(m => m.hit).content;
* console.log(`I found: **${hit}**, total results: ${res.total}`);
* })
* .catch(console.error);
*/
search(options = {}) {
return Shared.search(this, options);
}
/**
* The data for editing a guild.
* @typedef {Object} GuildEditData
@@ -831,55 +737,6 @@ class Guild extends Base {
return this.edit({ splash: await DataResolver.resolveImage(splash), reason });
}
/**
* Sets the position of the guild in the guild listing.
* <warn>This is only available when using a user account.</warn>
* @param {number} position Absolute or relative position
* @param {boolean} [relative=false] Whether to position relatively or absolutely
* @returns {Promise<Guild>}
*/
setPosition(position, relative) {
if (this.client.user.bot) {
return Promise.reject(new Error('FEATURE_USER_ONLY'));
}
return this.client.user.settings.setGuildPosition(this, position, relative);
}
/**
* Marks all messages in this guild as read.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<Guild>}
*/
acknowledge() {
return this.client.api.guilds(this.id).ack
.post({ data: { token: this.client.rest._ackToken } })
.then(res => {
if (res.token) this.client.rest._ackToken = res.token;
return this;
});
}
/**
* Whether to allow direct messages from guild members.
* <warn>This is only available when using a user account.</warn>
* @param {boolean} allow Whether to allow direct messages
* @returns {Promise<Guild>}
*/
allowDMs(allow) {
if (this.client.user.bot) return Promise.reject(new Error('FEATURE_USER_ONLY'));
const settings = this.client.user.settings;
if (allow) return settings.removeRestrictedGuild(this);
else return settings.addRestrictedGuild(this);
}
/**
* Syncs this guild (already done automatically every 30 seconds).
* <warn>This is only available when using a user account.</warn>
*/
sync() {
if (!this.client.user.bot) this.client.syncGuilds([this]);
}
/**
* The data needed for updating a channel's position.
* @typedef {Object} ChannelPosition

View File

@@ -6,7 +6,6 @@ const PermissionOverwrites = require('./PermissionOverwrites');
const Util = require('../util/Util');
const Permissions = require('../util/Permissions');
const Collection = require('../util/Collection');
const { MessageNotificationTypes } = require('../util/Constants');
const { Error, TypeError } = require('../errors');
/**
@@ -564,37 +563,6 @@ class GuildChannel extends Channel {
delete(reason) {
return this.client.api.channels(this.id).delete({ reason }).then(() => this);
}
/**
* Whether the channel is muted
* <warn>This is only available when using a user account.</warn>
* @type {?boolean}
* @readonly
*/
get muted() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).muted;
} catch (err) {
return false;
}
}
/**
* The type of message that should notify you
* one of `EVERYTHING`, `MENTIONS`, `NOTHING`, `INHERIT`
* <warn>This is only available when using a user account.</warn>
* @type {?string}
* @readonly
*/
get messageNotifications() {
if (this.client.user.bot) return null;
try {
return this.client.user.guildSettings.get(this.guild.id).channelOverrides.get(this.id).messageNotifications;
} catch (err) {
return MessageNotificationTypes[3];
}
}
}
module.exports = GuildChannel;

View File

@@ -499,20 +499,6 @@ class Message extends Base {
return this.channel.send(content, Object.assign(options, { reply: this.member || this.author }));
}
/**
* Marks the message as read.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<Message>}
*/
acknowledge() {
return this.client.api.channels(this.channel.id).messages(this.id).ack
.post({ data: { token: this.client.rest._ackToken } })
.then(res => {
if (res.token) this.client.rest._ackToken = res.token;
return this;
});
}
/**
* Fetches the webhook used to create this message.
* @returns {Promise<?Webhook>}

View File

@@ -1,6 +1,5 @@
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { Presence } = require('./Presence');
const UserProfile = require('./UserProfile');
const Snowflake = require('../util/Snowflake');
const Base = require('./Base');
const { Error } = require('../errors');
@@ -151,16 +150,6 @@ class User extends Base {
return `${this.username}#${this.discriminator}`;
}
/**
* The note that is set for the user
* <warn>This is only available when using a user account.</warn>
* @type {?string}
* @readonly
*/
get note() {
return this.client.user.notes.get(this.id) || null;
}
/**
* Checks whether the user is typing in a channel.
* @param {ChannelResolvable} channel The channel to check in
@@ -222,26 +211,6 @@ class User extends Base {
.then(data => this.client.actions.ChannelDelete.handle(data).channel);
}
/**
* Gets the profile of the user.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<UserProfile>}
*/
fetchProfile() {
return this.client.api.users(this.id).profile.get().then(data => new UserProfile(this, data));
}
/**
* Sets a note for the user.
* <warn>This is only available when using a user account.</warn>
* @param {string} note The note to set for the user
* @returns {Promise<User>}
*/
setNote(note) {
return this.client.api.users('@me').notes(this.id).put({ data: { note } })
.then(() => this);
}
/**
* 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.

View File

@@ -1,83 +0,0 @@
const Collection = require('../util/Collection');
const { UserFlags } = require('../util/Constants');
const UserConnection = require('./UserConnection');
const Base = require('./Base');
/**
* Represents a user's profile on Discord.
* @extends {Base}
*/
class UserProfile extends Base {
constructor(user, data) {
super(user.client);
/**
* The owner of the profile
* @type {User}
*/
this.user = user;
/**
* The guilds that the client user and the user share
* @type {Collection<Snowflake, Guild>}
*/
this.mutualGuilds = new Collection();
/**
* The user's connections
* @type {Collection<Snowflake, UserConnection>}
*/
this.connections = new Collection();
this._patch(data);
}
_patch(data) {
/**
* If the user has Discord Premium
* @type {boolean}
*/
this.premium = Boolean(data.premium_since);
/**
* The Bitfield of the users' flags
* @type {number}
* @private
*/
this._flags = data.user.flags;
/**
* The date since which the user has had Discord Premium
* @type {?Date}
*/
this.premiumSince = data.premium_since ? new Date(data.premium_since) : null;
for (const guild of data.mutual_guilds) {
if (this.client.guilds.has(guild.id)) {
this.mutualGuilds.set(guild.id, this.client.guilds.get(guild.id));
}
}
for (const connection of data.connected_accounts) {
this.connections.set(connection.id, new UserConnection(this.user, connection));
}
}
/**
* The flags the user has
* @type {UserFlags[]}
* @readonly
*/
get flags() {
const flags = [];
for (const [name, flag] of Object.entries(UserFlags)) {
if ((this._flags & flag) === flag) flags.push(name);
}
return flags;
}
toJSON() {
return super.toJSON({ flags: true });
}
}
module.exports = UserProfile;

View File

@@ -119,23 +119,6 @@ class TextBasedChannel {
return Shared.sendMessage(this, options);
}
/**
* Performs a search within the channel.
* <warn>This is only available when using a user account.</warn>
* @param {MessageSearchOptions} [options={}] Options to pass to the search
* @returns {Promise<MessageSearchResult>}
* @example
* channel.search({ content: 'discord.js', before: '2016-11-17' })
* .then(res => {
* const hit = res.results[0].find(m => m.hit).content;
* console.log(`I found: **${hit}**, total results: ${res.total}`);
* })
* .catch(console.error);
*/
search(options = {}) {
return Shared.search(this, options);
}
/**
* Starts a typing indicator in the channel.
* @param {number} [count=1] The number of times startTyping should be considered to have been called
@@ -273,7 +256,6 @@ class TextBasedChannel {
/**
* Bulk deletes given messages that are newer than two weeks.
* <warn>This is only available when using a bot account.</warn>
* @param {Collection<Snowflake, Message>|Message[]|Snowflake[]|number} messages
* Messages or number of messages to delete
* @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically
@@ -316,28 +298,11 @@ class TextBasedChannel {
throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
}
/**
* Marks all messages in this channel as read.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<TextChannel|GroupDMChannel|DMChannel>}
*/
acknowledge() {
if (!this.lastMessageID) return Promise.resolve(this);
return this.client.api.channels[this.id].messages[this.lastMessageID].ack
.post({ data: { token: this.client.rest._ackToken } })
.then(res => {
if (res.token) this.client.rest._ackToken = res.token;
return this;
});
}
static applyToClass(structure, full = false, ignore = []) {
const props = ['send'];
if (full) {
props.push(
'acknowledge',
'lastMessage',
'search',
'bulkDelete',
'startTyping',
'stopTyping',

View File

@@ -1,100 +0,0 @@
const Util = require('../../util/Util');
const { TypeError } = require('../../errors');
/**
* @typedef {Object} MessageSearchOptions
* @property {string} [content] Message content
* @property {Snowflake} [maxID] Maximum ID for the filter
* @property {Snowflake} [minID] Minimum ID for the filter
* @property {string} [has] One of `link`, `embed`, `file`, `video`, `image`, or `sound`,
* or add `-` to negate (e.g. `-file`)
* @property {ChannelResolvable} [channel] Channel to limit search to (only for guild search endpoint)
* @property {UserResolvable} [author] Author to limit search
* @property {string} [authorType] One of `user`, `bot`, `webhook`, or add `-` to negate (e.g. `-webhook`)
* @property {string} [sortBy='timestamp'] `timestamp` or `relevant`
* @property {string} [sortOrder='descending'] `ascending` or `descending`
* @property {number} [contextSize=2] How many messages to get around the matched message (0 to 2)
* @property {number} [limit=25] Maximum number of results to get (1 to 25)
* @property {number} [offset=0] Offset the "pages" of results (since you can only see 25 at a time)
* @property {UserResolvable} [mentions] Mentioned user filter
* @property {boolean} [mentionsEveryone] If everyone is mentioned
* @property {string} [linkHostname] Filter links by hostname
* @property {string} [embedProvider] The name of an embed provider
* @property {string} [embedType] one of `image`, `video`, `url`, `rich`, or add `-` to negate (e.g. `-image`)
* @property {string} [attachmentFilename] The name of an attachment
* @property {string} [attachmentExtension] The extension of an attachment
* @property {Date} [before] Date to find messages before
* @property {Date} [after] Date to find messages before
* @property {Date} [during] Date to find messages during (range of date to date + 24 hours)
* @property {boolean} [nsfw=false] Include results from NSFW channels
*/
/**
* @typedef {Object} MessageSearchResult
* @property {number} total Total result count
* @property {Array<Message[]>} results Array of message results
* The message which has triggered the result will have the `hit` property set to `true`
*/
module.exports = function search(target, options) {
if (typeof options === 'string') options = { content: options };
if (options.before) {
if (!(options.before instanceof Date)) options.before = new Date(options.before);
options.maxID = Util.binaryToID((options.before.getTime() - 14200704e5).toString(2) + '0'.repeat(22));
}
if (options.after) {
if (!(options.after instanceof Date)) options.after = new Date(options.after);
options.minID = Util.binaryToID((options.after.getTime() - 14200704e5).toString(2) + '0'.repeat(22));
}
if (options.during) {
if (!(options.during instanceof Date)) options.during = new Date(options.during);
const t = options.during.getTime() - 14200704e5;
options.minID = Util.binaryToID(t.toString(2) + '0'.repeat(22));
options.maxID = Util.binaryToID((t + 864e5).toString(2) + '0'.repeat(22));
}
if (options.channel) options.channel = target.client.channels.resolveID(options.channel);
if (options.author) options.author = target.client.users.resolveID(options.author);
if (options.mentions) options.mentions = target.client.users.resolveID(options.options.mentions);
if (options.sortOrder) {
options.sortOrder = { ascending: 'asc', descending: 'desc' }[options.sortOrder] || options.sortOrder;
}
options = {
content: options.content,
max_id: options.maxID,
min_id: options.minID,
has: options.has,
channel_id: options.channel,
author_id: options.author,
author_type: options.authorType,
context_size: options.contextSize,
sort_by: options.sortBy,
sort_order: options.sortOrder,
limit: options.limit,
offset: options.offset,
mentions: options.mentions,
mentions_everyone: options.mentionsEveryone,
link_hostname: options.linkHostname,
embed_provider: options.embedProvider,
embed_type: options.embedType,
attachment_filename: options.attachmentFilename,
attachment_extension: options.attachmentExtension,
include_nsfw: options.nsfw,
};
// Lazy load these because some of them use util
const Channel = require('../Channel');
const Guild = require('../Guild');
if (!(target instanceof Channel || target instanceof Guild)) throw new TypeError('SEARCH_CHANNEL_TYPE');
let endpoint = target.client.api[target instanceof Channel ? 'channels' : 'guilds'](target.id).messages().search;
return endpoint.get({ query: options }).then(body => {
const results = body.messages.map(x =>
x.map(m => target.client.channels.get(m.channel_id).messages.add(m, false))
);
return {
total: body.total_results,
results,
};
});
};

View File

@@ -1,5 +1,4 @@
module.exports = {
search: require('./Search'),
sendMessage: require('./SendMessage'),
createMessage: require('./CreateMessage'),
};