Advertisement
Guest User

Untitled

a guest
Mar 28th, 2017
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.92 KB | None | 0 0
  1. ```const EventEmitter = require('events').EventEmitter;
  2. const mergeDefault = require('../util/MergeDefault');
  3. const Constants = require('../util/Constants');
  4. const RESTManager = require('./rest/RESTManager');
  5. const ClientDataManager = require('./ClientDataManager');
  6. const ClientManager = require('./ClientManager');
  7. const ClientDataResolver = require('./ClientDataResolver');
  8. const ClientVoiceManager = require('./voice/ClientVoiceManager');
  9. const WebSocketManager = require('./websocket/WebSocketManager');
  10. const ActionsManager = require('./actions/ActionsManager');
  11. const Collection = require('../util/Collection');
  12. const Presence = require('../structures/Presence').Presence;
  13. const ShardClientUtil = require('../sharding/ShardClientUtil');
  14.  
  15. /**
  16. * The starting point for making a Discord Bot.
  17. * @extends {EventEmitter}
  18. */
  19. <class>(this is wrong) Client <extends>(this is wrong) EventEmitter {
  20. /**
  21. * @param {ClientOptions} [options] Options for the client
  22. */
  23. constructor(options = {}) {
  24. <super>(this is wrong)();
  25.  
  26. // Obtain shard details from environment
  27. if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
  28. if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
  29.  
  30. /**
  31. * The options the client was instantiated with
  32. * @type {ClientOptions}
  33. */
  34. this.options = mergeDefault(Constants.DefaultOptions, options);
  35. this._validateOptions();
  36.  
  37. /**
  38. * The REST manager of the client
  39. * @type {RESTManager}
  40. * @private
  41. */
  42. this.rest = new RESTManager(this);
  43.  
  44. /**
  45. * The data manager of the Client
  46. * @type {ClientDataManager}
  47. * @private
  48. */
  49. this.dataManager = new ClientDataManager(this);
  50.  
  51. /**
  52. * The manager of the Client
  53. * @type {ClientManager}
  54. * @private
  55. */
  56. this.manager = new ClientManager(this);
  57.  
  58. /**
  59. * The WebSocket Manager of the Client
  60. * @type {WebSocketManager}
  61. * @private
  62. */
  63. this.ws = new WebSocketManager(this);
  64.  
  65. /**
  66. * The Data Resolver of the Client
  67. * @type {ClientDataResolver}
  68. * @private
  69. */
  70. this.resolver = new ClientDataResolver(this);
  71.  
  72. /**
  73. * The Action Manager of the Client
  74. * @type {ActionsManager}
  75. * @private
  76. */
  77. this.actions = new ActionsManager(this);
  78.  
  79. /**
  80. * The Voice Manager of the Client (`null` in browsers)
  81. * @type {?ClientVoiceManager}
  82. * @private
  83. */
  84. this.voice = !this.browser ? new ClientVoiceManager(this) : null;
  85.  
  86. /**
  87. * The shard helpers for the client (only if the process was spawned as a child, such as from a ShardingManager)
  88. * @type {?ShardClientUtil}
  89. */
  90. this.shard = process.send ? ShardClientUtil.singleton(this) : null;
  91.  
  92. /**
  93. * A collection of the Client's stored users
  94. * @type {Collection<string, User>}
  95. */
  96. this.users = new Collection();
  97.  
  98. /**
  99. * A collection of the Client's stored guilds
  100. * @type {Collection<string, Guild>}
  101. */
  102. this.guilds = new Collection();
  103.  
  104. /**
  105. * A collection of the Client's stored channels
  106. * @type {Collection<string, Channel>}
  107. */
  108. this.channels = new Collection();
  109.  
  110. /**
  111. * A collection of presences for friends of the logged in user.
  112. * <warn>This is only filled when using a user account.</warn>
  113. * @type {Collection<string, Presence>}
  114. */
  115. this.presences = new Collection();
  116.  
  117. if (!this.token && 'CLIENT_TOKEN' in process.env) {
  118. /**
  119. * The authorization token for the logged in user/bot.
  120. * @type {?string}
  121. */
  122. this.token = process.env.CLIENT_TOKEN;
  123. } else {
  124. this.token = null;
  125. }
  126.  
  127. /**
  128. * The ClientUser representing the logged in Client
  129. * @type {?ClientUser}
  130. */
  131. this.user = null;
  132.  
  133. /**
  134. * The date at which the Client was regarded as being in the `READY` state.
  135. * @type {?Date}
  136. */
  137. this.readyAt = null;
  138.  
  139. /**
  140. * The previous heartbeat pings of the websocket (most recent first, limited to three elements)
  141. * @type {number[]}
  142. */
  143. this.pings = [];
  144.  
  145. this._pingTimestamp = 0;
  146. this._timeouts = new Set();
  147. this._intervals = new Set();
  148.  
  149. if (this.options.messageSweepInterval > 0) {
  150. this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000);
  151. }
  152. }
  153.  
  154. /**
  155. * The status for the logged in Client.
  156. * @type {?number}
  157. * @readonly
  158. */
  159. get status() {
  160. return this.ws.status;
  161. }
  162.  
  163. /**
  164. * The uptime for the logged in Client.
  165. * @type {?number}
  166. * @readonly
  167. */
  168. get uptime() {
  169. return this.readyAt ? Date.now() - this.readyAt : null;
  170. }
  171.  
  172. /**
  173. * The average heartbeat ping of the websocket
  174. * @type {number}
  175. * @readonly
  176. */
  177. get ping() {
  178. return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
  179. }
  180.  
  181. /**
  182. * Returns a collection, mapping guild ID to voice connections.
  183. * @type {Collection<string, VoiceConnection>}
  184. * @readonly
  185. */
  186. get voiceConnections() {
  187. if (this.browser) return new Collection();
  188. return this.voice.connections;
  189. }
  190.  
  191. /**
  192. * The emojis that the client can use. Mapped by emoji ID.
  193. * @type {Collection<string, Emoji>}
  194. * @readonly
  195. */
  196. get emojis() {
  197. const emojis = new Collection();
  198. for (const guild of this.guilds.values()) {
  199. for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji);
  200. }
  201. return emojis;
  202. }
  203.  
  204. /**
  205. * The timestamp that the client was last ready at
  206. * @type {?number}
  207. * @readonly
  208. */
  209. get readyTimestamp() {
  210. return this.readyAt ? this.readyAt.getTime() : null;
  211. }
  212.  
  213. /**
  214. * Whether the client is in a browser environment
  215. * @type {boolean}
  216. * @readonly
  217. */
  218. get browser() {
  219. return typeof window !== 'undefined';
  220. }
  221.  
  222. /**
  223. * Logs the client in. If successful, resolves with the account's token. <warn>If you're making a bot, it's
  224. * much better to use a bot account rather than a user account.
  225. * Bot accounts have higher rate limits and have access to some features user accounts don't have. User bots
  226. * that are making a lot of API requests can even be banned.</warn>
  227. * @param {string} token The token used for the account.
  228. * @returns {Promise<string>}
  229. * @example
  230. * // log the client in using a token
  231. * const token = 'my token';
  232. * client.login(token);
  233. * @example
  234. * // log the client in using email and password
  235. * const email = 'user@email.com';
  236. * const password = 'supersecret123';
  237. * client.login(email, password);
  238. */
  239. login(token) {
  240. return this.rest.methods.login(token);
  241. }
  242.  
  243. /**
  244. * Destroys the client and logs out.
  245. * @returns {Promise}
  246. */
  247. destroy() {
  248. for (const t of this._timeouts) clearTimeout(t);
  249. for (const i of this._intervals) clearInterval(i);
  250. this._timeouts.clear();
  251. this._intervals.clear();
  252. return this.manager.destroy();
  253. }
  254.  
  255. /**
  256. * This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
  257. * if you wish to force a sync of guild data, you can use this.
  258. * <warn>This is only available when using a user account.</warn>
  259. * @param {Guild[]|Collection<string, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
  260. */
  261. syncGuilds(guilds = this.guilds) {
  262. if (this.user.bot) return;
  263. this.ws.send({
  264. op: 12,
  265. d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
  266. });
  267. }
  268.  
  269. /**
  270. * Caches a user, or obtains it from the cache if it's already cached.
  271. * <warn>This is only available when using a bot account.</warn>
  272. * @param {string} id The ID of the user to obtain
  273. * @returns {Promise<User>}
  274. */
  275. fetchUser(id) {
  276. if (this.users.has(id)) return Promise.resolve(this.users.get(id));
  277. return this.rest.methods.getUser(id);
  278. }
  279.  
  280. /**
  281. * Fetches an invite object from an invite code.
  282. * @param {InviteResolvable} invite An invite code or URL
  283. * @returns {Promise<Invite>}
  284. */
  285. fetchInvite(invite) {
  286. const code = this.resolver.resolveInviteCode(invite);
  287. return this.rest.methods.getInvite(code);
  288. }
  289.  
  290. /**
  291. * Fetch a webhook by ID.
  292. * @param {string} id ID of the webhook
  293. * @param {string} [token] Token for the webhook
  294. * @returns {Promise<Webhook>}
  295. */
  296. fetchWebhook(id, token) {
  297. return this.rest.methods.getWebhook(id, token);
  298. }
  299.  
  300. /**
  301. * Sweeps all channels' messages and removes the ones older than the max message lifetime.
  302. * If the message has been edited, the time of the edit is used rather than the time of the original message.
  303. * @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
  304. * will be removed from the caches. The default is based on the client's `messageCacheLifetime` option.
  305. * @returns {number} Amount of messages that were removed from the caches,
  306. * or -1 if the message cache lifetime is unlimited
  307. */
  308. sweepMessages(lifetime = this.options.messageCacheLifetime) {
  309. if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('The lifetime must be a number.');
  310. if (lifetime <= 0) {
  311. this.emit('debug', 'Didn\'t sweep messages - lifetime is unlimited');
  312. return -1;
  313. }
  314.  
  315. const lifetimeMs = lifetime * 1000;
  316. const now = Date.now();
  317. let channels = 0;
  318. let messages = 0;
  319.  
  320. for (const channel of this.channels.values()) {
  321. if (!channel.messages) continue;
  322. channels++;
  323.  
  324. for (const message of channel.messages.values()) {
  325. if (now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs) {
  326. channel.messages.delete(message.id);
  327. messages++;
  328. }
  329. }
  330. }
  331.  
  332. this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
  333. return messages;
  334. }
  335.  
  336. /**
  337. * Gets the bot's OAuth2 application.
  338. * <warn>This is only available when using a bot account.</warn>
  339. * @returns {Promise<ClientOAuth2Application>}
  340. */
  341. fetchApplication() {
  342. if (!this.user.bot) throw new Error(Constants.Errors.NO_BOT_ACCOUNT);
  343. return this.rest.methods.getMyApplication();
  344. }
  345.  
  346. /**
  347. * Generate an invite link for your bot
  348. * @param {PermissionResolvable[]|number} [permissions] An array of permissions to request
  349. * @returns {Promise<string>} The invite link
  350. * @example
  351. * client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
  352. * .then(link => {
  353. * console.log(`Generated bot invite link: ${link}`);
  354. * });
  355. */
  356. generateInvite(permissions) {
  357. if (permissions) {
  358. if (permissions instanceof Array) permissions = this.resolver.resolvePermissions(permissions);
  359. } else {
  360. permissions = 0;
  361. }
  362. return this.fetchApplication().then(application =>
  363. `https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
  364. );
  365. }
  366.  
  367. /**
  368. * Sets a timeout that will be automatically cancelled if the client is destroyed.
  369. * @param {Function} fn Function to execute
  370. * @param {number} delay Time to wait before executing (in milliseconds)
  371. * @param {...*} args Arguments for the function
  372. * @returns {Timeout}
  373. */
  374. setTimeout(fn, delay, ...args) {
  375. const timeout = setTimeout(() => {
  376. fn();
  377. this._timeouts.delete(timeout);
  378. }, delay, ...args);
  379. this._timeouts.add(timeout);
  380. return timeout;
  381. }
  382.  
  383. /**
  384. * Clears a timeout
  385. * @param {Timeout} timeout Timeout to cancel
  386. */
  387. clearTimeout(timeout) {
  388. clearTimeout(timeout);
  389. this._timeouts.delete(timeout);
  390. }
  391.  
  392. /**
  393. * Sets an interval that will be automatically cancelled if the client is destroyed.
  394. * @param {Function} fn Function to execute
  395. * @param {number} delay Time to wait before executing (in milliseconds)
  396. * @param {...*} args Arguments for the function
  397. * @returns {Timeout}
  398. */
  399. setInterval(fn, delay, ...args) {
  400. const interval = setInterval(fn, delay, ...args);
  401. this._intervals.add(interval);
  402. return interval;
  403. }
  404.  
  405. /**
  406. * Clears an interval
  407. * @param {Timeout} interval Interval to cancel
  408. */
  409. clearInterval(interval) {
  410. clearInterval(interval);
  411. this._intervals.delete(interval);
  412. }
  413.  
  414. _pong(startTime) {
  415. this.pings.unshift(Date.now() - startTime);
  416. if (this.pings.length > 3) this.pings.length = 3;
  417. this.ws.lastHeartbeatAck = true;
  418. }
  419.  
  420. _setPresence(id, presence) {
  421. if (this.presences.get(id)) {
  422. this.presences.get(id).update(presence);
  423. return;
  424. }
  425. this.presences.set(id, new Presence(presence));
  426. }
  427.  
  428. _eval(script) {
  429. return eval(script);
  430. }
  431.  
  432. _validateOptions(options = this.options) {
  433. if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
  434. throw new TypeError('The shardCount option must be a number.');
  435. }
  436. if (typeof options.shardId !== 'number' || isNaN(options.shardId)) {
  437. throw new TypeError('The shardId option must be a number.');
  438. }
  439. if (options.shardCount < 0) throw new RangeError('The shardCount option must be at least 0.');
  440. if (options.shardId < 0) throw new RangeError('The shardId option must be at least 0.');
  441. if (options.shardId !== 0 && options.shardId >= options.shardCount) {
  442. throw new RangeError('The shardId option must be less than shardCount.');
  443. }
  444. if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
  445. throw new TypeError('The messageCacheMaxSize option must be a number.');
  446. }
  447. if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
  448. throw new TypeError('The messageCacheLifetime option must be a number.');
  449. }
  450. if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
  451. throw new TypeError('The messageSweepInterval option must be a number.');
  452. }
  453. if (typeof options.fetchAllMembers !== 'boolean') {
  454. throw new TypeError('The fetchAllMembers option must be a boolean.');
  455. }
  456. if (typeof options.disableEveryone !== 'boolean') {
  457. throw new TypeError('The disableEveryone option must be a boolean.');
  458. }
  459. if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
  460. throw new TypeError('The restWsBridgeTimeout option must be a number.');
  461. }
  462. if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.');
  463. }
  464. }
  465.  
  466. module.exports = Client;
  467.  
  468. /**
  469. * Emitted for general warnings
  470. * @event Client#warn
  471. * @param {string} info The warning
  472. */
  473.  
  474. /**
  475. * Emitted for general debugging information
  476. * @event Client#debug
  477. * @param {string} info The debug information
  478. */```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement