From da32c2ec3df709032b83b37e413f4a6a8e7cb0e6 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Fri, 30 Dec 2016 11:14:31 -0600 Subject: [PATCH] add more search stuff (#1046) * add more search stuff * clean up the options * fix link hostname * use some resolvers * fix type --- src/client/rest/RESTMethods.js | 89 +++++++- src/structures/Guild.js | 25 +-- src/structures/MessageSearch.js | 215 ------------------- src/structures/interface/TextBasedChannel.js | 26 +-- 4 files changed, 113 insertions(+), 242 deletions(-) delete mode 100644 src/structures/MessageSearch.js diff --git a/src/client/rest/RESTMethods.js b/src/client/rest/RESTMethods.js index 0484aec1d433..ff94719c5222 100644 --- a/src/client/rest/RESTMethods.js +++ b/src/client/rest/RESTMethods.js @@ -1,3 +1,4 @@ +const long = require('long'); const Constants = require('../../util/Constants'); const Collection = require('../../util/Collection'); const splitMessage = require('../../util/SplitMessage'); @@ -12,6 +13,35 @@ const Invite = require('../../structures/Invite'); const Webhook = require('../../structures/Webhook'); const UserProfile = require('../../structures/UserProfile'); const ClientOAuth2Application = require('../../structures/ClientOAuth2Application'); +const Channel = require('../../structures/Channel'); +const Guild = require('../../structures/Guild'); + +/** + * @typedef {Object} MessageSearchOptions + * @property {string} [content] Message content + * @property {string} [maxID] Maximum ID for the filter + * @property {string} [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='recent'] `recent` or `relevant` + * @property {string} [sortOrder='desc'] `asc` or `desc` + * @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` + * @property {string} [attachmentFilename] The name of an attachment + * @property {string} [attachmentExtention] 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) + */ class RESTMethods { constructor(restManager) { @@ -117,13 +147,68 @@ class RESTMethods { ); } - search(type, id, options) { + search(target, options) { + if (options.before) { + if (!(options.before instanceof Date)) options.before = new Date(options.before); + options.maxID = long.fromNumber(options.before.getTime() - 14200704e5).shiftLeft(22).toString(); + } + + if (options.after) { + if (!(options.after instanceof Date)) options.after = new Date(options.after); + options.minID = long.fromNumber(options.after.getTime() - 14200704e5).shiftLeft(22).toString(); + } + + if (options.during) { + if (!(options.during instanceof Date)) options.during = new Date(options.during); + const t = options.during.getTime() - 14200704e5; + options.minID = long.fromNumber(t).shiftLeft(22).toString(); + options.maxID = long.fromNumber(t + 86400000).shift(222).toString(); + } + + if (options.channel) options.channel = this.client.resolver.resolveChannelID(options.channel); + + if (options.author) options.author = this.client.resolver.resolveUserID(options.author); + + if (options.mentions) options.mentions = this.client.resolver.resolveUserID(options.options.mentions); + + 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, + }; + const queryString = Object.keys(options) .filter(k => options[k]) .map(k => [k, options[k]]) .map(x => x.join('=')) .join('&'); - const url = `${Constants.Endpoints[`${type}Search`](id)}?${queryString}`; + + let type; + if (target instanceof Channel) { + type = 'channel'; + } else if (target instanceof Guild) { + type = 'guild'; + } else { + throw new TypeError('Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.'); + } + + const url = `${Constants.Endpoints[`${type}Search`](target.id)}?${queryString}`; return this.rest.makeRequest('get', url, true).then(body => body.messages.map(x => x.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))) ); diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 8a45507eb55c..472166fbc5cf 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -3,7 +3,6 @@ const Role = require('./Role'); const Emoji = require('./Emoji'); const Presence = require('./Presence').Presence; const GuildMember = require('./GuildMember'); -const MessageSearch = require('./MessageSearch'); const Constants = require('../util/Constants'); const Collection = require('../util/Collection'); const cloneObject = require('../util/CloneObject'); @@ -707,20 +706,22 @@ class Guild { /** * Performs a search * @param {MessageSearchOptions} [options={}] Options to pass to the search - * @returns {MessageSearch} + * @returns {Promise>} + * An array containing arrays of messages. Each inner array is a search context cluster. + * The message which has triggered the result will have the `hit` property set to `true`. * @example - * guild.search() - * .content('discord.js') - * .before('2016-11-17') - * .execute() - * .then(res => { - * const hit = res[0].find(m => m.hit).content; - * console.log(`I found: **${hit}**`); - * }) - * .catch(console.error); + * guild.search({ + * content: 'discord.js', + * before: '2016-11-17' + * }) + * .then(res => { + * const hit = res[0].find(m => m.hit).content; + * console.log(`I found: **${hit}**`); + * }) + * .catch(console.error); */ search(options) { - return new MessageSearch(this, options); + return this.client.rest.methods.search(this, options); } /** diff --git a/src/structures/MessageSearch.js b/src/structures/MessageSearch.js deleted file mode 100644 index 50135f7b43e4..000000000000 --- a/src/structures/MessageSearch.js +++ /dev/null @@ -1,215 +0,0 @@ -const long = require('long'); -let TextChannel, DMChannel, GroupDMChannel, Guild; - -/** - * @typedef {Object} MessageSearchOptions - * @property {string} [content] Message content - * @property {string} [maxID] Maximum ID for the filter - * @property {string} [minID] Minimum ID for the filter - * @property {string} [has] One of `link`, `embed`, `file`, `video`, `image`, or `sound` - * @property {string} [channelID] Channel ID to limit search to (only for guild search endpoint) - * @property {string} [authorID] Author ID to limit search - * @property {string} [sortBy='recent'] `recent` or `relevant` - * @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) - */ - -/** - * Fluent interface for running a search against a guild or channel - */ -class MessageSearch { - /** - * @param {TextChannel|DMChannel|GroupDMChannel|Guild} target Target of the search - * @param {MessageSearchOptions} [options] Options for the search - */ - constructor(target, options = {}) { - if (!TextChannel) { - TextChannel = require('./TextChannel'); - DMChannel = require('./DMChannel'); - GroupDMChannel = require('./GroupDMChannel'); - Guild = require('./Guild'); - } - - if (target instanceof TextChannel || target instanceof DMChannel || target instanceof GroupDMChannel) { - /** - * The type of search, either `channel` or `guild` - * @type {string} - */ - this.type = 'channel'; - } else if (target instanceof Guild) { - this.type = 'guild'; - } else { - throw new TypeError('Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.'); - } - - /** - * Client to use - * @type {Client} - */ - this.client = target.client; - - /** - * ID of the search target - * @type {string} - */ - this.id = target.id; - - /** - * Options for the search - * @type {MessageSearchOptions} - */ - this.options = options; - } - - /** - * Sets the content for the search - * @param {string} content Content to search for - * @returns {MessageSearch} - */ - content(content) { - this.options.content = content; - return this; - } - - /** - * Sets the minimum ID for the search - * @param {string} id Snowflake minimum ID - * @returns {MessageSearch} - */ - minID(id) { - this.options.minID = id; - return this; - } - - /** - * Sets the maximum ID for the search - * @param {string} id Snowflake maximum ID - * @returns {MessageSearch} - */ - maxID(id) { - this.options.maxID = id; - return this; - } - - /** - * Sets the before date for the search - * @param {Date} date Date to find messages before - * @returns {MessageSearch} - */ - before(date) { - if (typeof date !== Date) date = new Date(date); - return this.maxID(long.fromNumber(date.getTime() - 14200704e5).shiftLeft(22).toString()); - } - - /** - * Sets the after date for the search - * @param {Date} date Date to find messages after - * @returns {MessageSearch} - */ - after(date) { - if (typeof date !== Date) date = new Date(date); - return this.minID(long.fromNumber(date.getTime() - 14200704e5).shiftLeft(22).toString()); - } - - /** - * Sets the during date for the search - * @param {Date} date Date to find messages during (range of date to date + 24 hours) - * @returns {MessageSearch} - */ - during(date) { - if (typeof date !== Date) date = new Date(date); - const t = date.getTime() - 14200704e5; - this.minID(long.fromNumber(t).shiftLeft(22).toString()); - this.maxID(long.fromNumber(t + 86400000).shift(222).toString()); - return this; - } - - /** - * Sets the filter for the search - * @param {string} type Filter for some type of embed or attachment that can be in the message - * must be one of ['link', 'embed', 'file', 'video', 'image', 'sound'] - * @returns {MessageSearch} - */ - has(type) { - const allowed = ['link', 'embed', 'file', 'video', 'image', 'sound']; - if (!allowed.includes(type)) throw new Error(`Type must be one of [${allowed.join(', ')}]`); - this.options.has = type; - return this; - } - - /** - * Sets the author for the search - * @param {UserResolvable} user User to only find messages from - * @returns {MessageSearch} - */ - from(user) { - this.options.authorID = this.client.resolver.resolverUserID(user); - return this; - } - - /** - * Sets the channel for the search - * @param {ChannelResolvable} channel Channel to only find messages from - * This is only for use with a guild search - * @returns {MessageSearch} - */ - in(channel) { - this.options.channelID = this.client.resolver.resolveChannelID(channel); - return this; - } - - /** - * Sets the maximum results for the search - * @param {number} limit Maximum number of results (1 to 25) - * @returns {MessageSearch} - */ - limit(limit) { - if (limit < 1 || limit > 25) throw new RangeError('Limit must be within 1 to 25.'); - this.options.limit = limit; - return this; - } - - /** - * Sets the context size for the search - * @param {number} size Number of messages to get around the matched message (0 to 2) - * @returns {MessageSearch} - */ - contextSize(size) { - if (size < 0 || size > 2) throw new RangeError('Context size must be within 0 to 2'); - this.options.contextSize = size; - return this; - } - - /** - * Sets the sorting order for the search - * @param {string} [type='recent'] Sorting type (`recent` or `relevant`) - * @returns {MessageSearch} - */ - sort(type) { - if (type !== 'recent' || type !== 'relevant') throw new Error('Sort type must be `recent` or `relevant`.'); - this.options.sortBy = type; - return this; - } - - /** - * Executes the search - * @returns {Promise>} - * An array containing arrays of messages. Each inner array is a search context cluster. - * The message which has triggered the result will have the `hit` property set to `true`. - */ - execute() { - return this.client.rest.methods.search(this.type, this.id, { - content: this.options.content, - max_id: this.options.maxID, - min_id: this.options.minID, - has: this.options.has, - channel_id: this.options.channelID, - author_id: this.options.authorID, - context_size: this.options.contextSize, - sort_by: this.options.sortBy, - limit: this.options.limit, - }); - } -} - -module.exports = MessageSearch; diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index 08ba4f388513..1249b57dc64f 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -1,10 +1,8 @@ const path = require('path'); const Message = require('../Message'); const MessageCollector = require('../MessageCollector'); -const MessageSearch = require('../MessageSearch'); const Collection = require('../../util/Collection'); - /** * Interface for classes that have text-channel-like features * @interface @@ -218,20 +216,22 @@ class TextBasedChannel { /** * Performs a search * @param {MessageSearchOptions} [options={}] Options to pass to the search - * @returns {MessageSearch} + * @returns {Promise>} + * An array containing arrays of messages. Each inner array is a search context cluster. + * The message which has triggered the result will have the `hit` property set to `true`. * @example - * channel.search() - * .content('discord.js') - * .before('2016-11-17') - * .execute() - * .then(res => { - * const hit = res[0].find(m => m.hit).content; - * console.log(`I found: **${hit}**`); - * }) - * .catch(console.error); + * channel.search({ + * content: 'discord.js', + * before: '2016-11-17' + * }) + * .then(res => { + * const hit = res[0].find(m => m.hit).content; + * console.log(`I found: **${hit}**`); + * }) + * .catch(console.error); */ search(options) { - return new MessageSearch(this, options); + return this.client.rest.methods.search(this, options); } /**