Guest User

Untitled

a guest
May 6th, 2018
347
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 98.73 KB | None | 0 0
  1. "use strict";
  2. (function discordio(Discord){
  3. var isNode = typeof(window) === "undefined" && typeof(navigator) === "undefined";
  4. var CURRENT_VERSION = "2.x.x",
  5. GATEWAY_VERSION = 5,
  6. LARGE_THRESHOLD = 250,
  7. CONNECT_WHEN = null,
  8. Endpoints, Payloads;
  9.  
  10. if (isNode) {
  11. var Util = require('util'),
  12. FS = require('fs'),
  13. UDP = require('dgram'),
  14. Zlib = require('zlib'),
  15. DNS = require('dns'),
  16. Stream = require('stream'),
  17. BN = require('path').basename,
  18. EE = require('events').EventEmitter,
  19. requesters = {
  20. http: require('http'),
  21. https: require('https')
  22. },
  23. ChildProc = require('child_process'),
  24. URL = require('url'),
  25. //NPM Modules
  26. NACL = require('tweetnacl'),
  27. Opus = null;
  28. }
  29.  
  30. /* --- Version Check --- */
  31. try {
  32. CURRENT_VERSION = require('../package.json').version;
  33. } catch(e) {}
  34. if (!isNode) CURRENT_VERSION = CURRENT_VERSION + "-browser";
  35.  
  36. /**
  37. * Discord Client constructor
  38. * @class
  39. * @arg {Object} options
  40. * @arg {String} options.token - The token of the account you wish to log in with.
  41. * @arg {Boolean} [options.autorun] - If true, the client runs when constructed without calling `.connect()`.
  42. * @arg {Number} [options.messageCacheLimit] - The amount of messages to cache in memory, per channel. Used for information on deleted/updated messages. The default is 50.
  43. * @arg {Array<Number>} [options.shard] - The shard array. The first index is the current shard ID, the second is the amount of shards that should be running.
  44. */
  45. Discord.Client = function DiscordClient(options) {
  46. if (!isNode) Emitter.call(this);
  47. if (!options || options.constructor.name !== 'Object') return console.error("An Object is required to create the discord.io client.");
  48.  
  49. applyProperties(this, [
  50. ["_ws", null],
  51. ["_uIDToDM", {}],
  52. ["_ready", false],
  53. ["_vChannels", {}],
  54. ["_messageCache", {}],
  55. ["_connecting", false],
  56. ["_mainKeepAlive", null],
  57. ["_req", APIRequest.bind(this)],
  58. ["_shard", validateShard(options.shard)],
  59. ["_messageCacheLimit", typeof(options.messageCacheLimit) === 'number' ? options.messageCacheLimit : 50],
  60. ]);
  61.  
  62. this.presenceStatus = "offline";
  63. this.connected = false;
  64. this.inviteURL = null;
  65. this.connect = this.connect.bind(this, options);
  66.  
  67. if (options.autorun === true) this.connect();
  68. };
  69. if (isNode) Emitter.call(Discord.Client);
  70.  
  71. /* - DiscordClient - Methods - */
  72. var DCP = Discord.Client.prototype;
  73. /**
  74. * Manually initiate the WebSocket connection to Discord.
  75. */
  76. DCP.connect = function () {
  77. var opts = arguments[0];
  78. if (!this.connected && !this._connecting) return setTimeout(function() {
  79. init(this, opts);
  80. CONNECT_WHEN = Math.max(CONNECT_WHEN, Date.now()) + 6000;
  81. }.bind(this), Math.max( 0, CONNECT_WHEN - Date.now() ));
  82. };
  83.  
  84. /**
  85. * Disconnect the WebSocket connection to Discord.
  86. */
  87. DCP.disconnect = function () {
  88. if (this._ws) return this._ws.close(), log(this, "Manual disconnect called, websocket closed");
  89. return log(this, Discord.LogLevels.Warn, "Manual disconnect called with no WebSocket active, ignored");
  90. };
  91.  
  92. /**
  93. * Retrieve a user object from Discord, Bot only endpoint. You don't have to share a server with this user.
  94. * @arg {Object} input
  95. * @arg {Snowflake} input.userID
  96. */
  97. DCP.getUser = function(input, callback) {
  98. if (!this.bot) return handleErrCB("[getUser] This account is a 'user' type account, and cannot use 'getUser'. Only bots can use this endpoint.", callback);
  99. this._req('get', Endpoints.USER(input.userID), function(err, res) {
  100. handleResCB("Could not get user", err, res, callback);
  101. });
  102. };
  103.  
  104. /**
  105. * Edit the client's user information.
  106. * @arg {Object} input
  107. * @arg {String<Base64>} input.avatar - The last part of a Base64 Data URI. `fs.readFileSync('image.jpg', 'base64')` is enough.
  108. * @arg {String} input.username - A username.
  109. * @arg {String} input.email - [User only] An email.
  110. * @arg {String} input.password - [User only] Your current password.
  111. * @arg {String} input.new_password - [User only] A new password.
  112. */
  113. DCP.editUserInfo = function(input, callback) {
  114. var payload = {
  115. avatar: this.avatar,
  116. email: this.email,
  117. new_password: null,
  118. password: null,
  119. username: this.username
  120. },
  121. plArr = Object.keys(payload);
  122.  
  123. for (var key in input) {
  124. if (plArr.indexOf(key) < 0) return handleErrCB(("[editUserInfo] '" + key + "' is not a valid key. Valid keys are: " + plArr.join(", ")), callback);
  125. payload[key] = input[key];
  126. }
  127. if (input.avatar) payload.avatar = "data:image/jpg;base64," + input.avatar;
  128.  
  129. this._req('patch', Endpoints.ME, payload, function(err, res) {
  130. handleResCB("Unable to edit user information", err, res, callback);
  131. });
  132. };
  133.  
  134. /**
  135. * Change the client's presence.
  136. * @arg {Object} input
  137. * @arg {String|null} input.status - Used to set the status. online, idle, dnd, invisible, and offline are the possible states.
  138. * @arg {Number|null} input.idle_since - Optional, use a Number before the current point in time.
  139. * @arg {Boolean|null} input.afk - Optional, changes how Discord handles push notifications.
  140. * @arg {Object|null} input.game - Used to set game information.
  141. * @arg {String|null} input.game.name - The name of the game.
  142. * @arg {Number|null} input.game.type - Activity type, 0 for game, 1 for Twitch.
  143. * @arg {String|null} input.game.url - A URL matching the streaming service you've selected.
  144. */
  145. DCP.setPresence = function(input) {
  146. var payload = Payloads.STATUS(input);
  147. send(this._ws, payload);
  148.  
  149. if (payload.d.idle_since === null) return void(this.presenceStatus = payload.d.status);
  150. this.presenceStatus = payload.d.status;
  151. };
  152.  
  153. /**
  154. * Receive OAuth information for the current client.
  155. */
  156. DCP.getOauthInfo = function(callback) {
  157. this._req('get', Endpoints.OAUTH, function(err, res) {
  158. handleResCB("Error GETing OAuth information", err, res, callback);
  159. });
  160. };
  161.  
  162. /**
  163. * Receive account settings information for the current client.
  164. */
  165. DCP.getAccountSettings = function(callback) {
  166. this._req('get', Endpoints.SETTINGS, function(err, res) {
  167. handleResCB("Error GETing client settings", err, res, callback);
  168. });
  169. };
  170.  
  171. /* - DiscordClient - Methods - Content - */
  172.  
  173. /**
  174. * Upload a file to a channel.
  175. * @arg {Object} input
  176. * @arg {Snowflake} input.to - The target Channel or User ID.
  177. * @arg {Buffer|String} input.file - A Buffer containing the file data, or a String that's a path to the file.
  178. * @arg {String|null} input.filename - A filename for the uploaded file, required if you provide a Buffer.
  179. * @arg {String|null} input.message - An optional message to provide.
  180. */
  181. DCP.uploadFile = function(input, callback) {
  182. /* After like 15 minutes of fighting with Request, turns out Discord doesn't allow multiple files in one message...
  183. despite having an attachments array.*/
  184. var file, client, multi, message, isBuffer, isString;
  185.  
  186. client = this;
  187. multi = new Multipart();
  188. message = generateMessage(input.message || "");
  189. isBuffer = (input.file instanceof Buffer);
  190. isString = (type(input.file) === 'string');
  191.  
  192. if (!isBuffer && !isString) return handleErrCB("uploadFile requires a String or Buffer as the 'file' value", callback);
  193. if (isBuffer) {
  194. if (!input.filename) return handleErrCB("uploadFile requires a 'filename' value to be set if using a Buffer", callback);
  195. file = input.file;
  196. }
  197. if (isString) try { file = FS.readFileSync(input.file); } catch(e) { return handleErrCB("File does not exist: " + input.file, callback); }
  198.  
  199. [
  200. ["content", message.content],
  201. ["mentions", ""],
  202. ["tts", false],
  203. ["nonce", message.nonce],
  204. ["file", file, input.filename || BN(input.file)]
  205. ].forEach(multi.append, multi);
  206. multi.finalize();
  207.  
  208. resolveID(client, input.to, function(channelID) {
  209. client._req('post', Endpoints.MESSAGES(channelID), multi, function(err, res) {
  210. handleResCB("Unable to upload file", err, res, callback);
  211. });
  212. });
  213. };
  214.  
  215. /**
  216. * Send a message to a channel.
  217. * @arg {Object} input
  218. * @arg {Snowflake} input.to - The target Channel or User ID.
  219. * @arg {String} input.message - The message content.
  220. * @arg {Object} [input.embed] - An embed object to include
  221. * @arg {Boolean} [input.tts] - Enable Text-to-Speech for this message.
  222. * @arg {Number} [input.nonce] - Number-used-only-ONCE. The Discord client uses this to change the message color from grey to white.
  223. * @arg {Boolean} [input.typing] - Indicates whether the message should be sent with simulated typing. Based on message length.
  224. */
  225. DCP.sendMessage = function(input, callback) {
  226. var message = generateMessage(input.message || '', input.embed);
  227. message.tts = (input.tts === true);
  228. message.nonce = input.nonce || message.nonce;
  229.  
  230. if (input.typing === true) {
  231. return simulateTyping(
  232. this,
  233. input.to,
  234. message,
  235. ( (message.content.length * 0.12) * 1000 ),
  236. callback
  237. );
  238. }
  239.  
  240. sendMessage(this, input.to, message, callback);
  241. };
  242.  
  243. /**
  244. * Pull a message object from Discord.
  245. * @arg {Object} input
  246. * @arg {Snowflake} input.channelID - The channel ID that the message is from.
  247. * @arg {Snowflake} input.messageID - The ID of the message.
  248. */
  249. DCP.getMessage = function(input, callback) {
  250. this._req('get', Endpoints.MESSAGES(input.channelID, input.messageID), function(err, res) {
  251. handleResCB("Unable to get message", err, res, callback);
  252. });
  253. };
  254.  
  255. /**
  256. * Pull an array of message objects from Discord.
  257. * @arg {Object} input
  258. * @arg {Snowflake} input.channelID - The channel ID to pull the messages from.
  259. * @arg {Number} [input.limit] - How many messages to pull, defaults to 50.
  260. * @arg {Snowflake} [input.before] - Pull messages before this message ID.
  261. * @arg {Snowflake} [input.after] - Pull messages after this message ID.
  262. */
  263. DCP.getMessages = function(input, callback) {
  264. var client = this, qs = {}, messages = [], lastMessageID = "";
  265. var total = typeof(input.limit) !== 'number' ? 50 : input.limit;
  266.  
  267. if (input.before) qs.before = input.before;
  268. if (input.after) qs.after = input.after;
  269.  
  270. (function getMessages() {
  271. if (total > 100) {
  272. qs.limit = 100;
  273. total = total - 100;
  274. } else {
  275. qs.limit = total;
  276. }
  277.  
  278. if (messages.length >= input.limit) return call(callback, [null, messages]);
  279.  
  280. client._req('get', Endpoints.MESSAGES(input.channelID) + qstringify(qs), function(err, res) {
  281. if (err) return handleErrCB("Unable to get messages", callback);
  282. messages = messages.concat(res.body);
  283. lastMessageID = messages[messages.length - 1] && messages[messages.length - 1].id;
  284. if (lastMessageID) qs.before = lastMessageID;
  285. if (!res.body.length < qs.limit) return call(callback, [null, messages]);
  286. return setTimeout(getMessages, 1000);
  287. });
  288. })();
  289. };
  290.  
  291. /**
  292. * Edit a previously sent message.
  293. * @arg {Object} input
  294. * @arg {Snowflake} input.channelID
  295. * @arg {Snowflake} input.messageID
  296. * @arg {String} input.message - The new message content
  297. * @arg {Object} [input.embed] - The new Discord Embed object
  298. */
  299. DCP.editMessage = function(input, callback) {
  300. this._req('patch', Endpoints.MESSAGES(input.channelID, input.messageID), generateMessage(input.message || '', input.embed), function(err, res) {
  301. handleResCB("Unable to edit message", err, res, callback);
  302. });
  303. };
  304.  
  305. /**
  306. * Delete a posted message.
  307. * @arg {Object} input
  308. * @arg {Snowflake} input.channelID
  309. * @arg {Snowflake} input.messageID
  310. */
  311. DCP.deleteMessage = function(input, callback) {
  312. this._req('delete', Endpoints.MESSAGES(input.channelID, input.messageID), function(err, res) {
  313. handleResCB("Unable to delete message", err, res, callback);
  314. });
  315. };
  316.  
  317. /**
  318. * Delete a batch of messages.
  319. * @arg {Object} input
  320. * @arg {Snowflake} input.channelID
  321. * @arg {Array<Snowflake>} input.messageIDs - An Array of message IDs, with a maximum of 100 indexes.
  322. */
  323. DCP.deleteMessages = function(input, callback) {
  324. this._req('post', Endpoints.BULK_DELETE(input.channelID), {messages: input.messageIDs.slice(0, 100)}, function(err, res) {
  325. handleResCB("Unable to delete messages", err, res, callback);
  326. });
  327. };
  328.  
  329. /**
  330. * Pin a message to the channel.
  331. * @arg {Object} input
  332. * @arg {Snowflake} input.channelID
  333. * @arg {Snowflake} input.messageID
  334. */
  335. DCP.pinMessage = function(input, callback) {
  336. this._req('put', Endpoints.PINNED_MESSAGES(input.channelID, input.messageID), function(err, res) {
  337. handleResCB("Unable to pin message", err, res, callback);
  338. });
  339. };
  340.  
  341. /**
  342. * Get an array of pinned messages from a channel.
  343. * @arg {Object} input
  344. * @arg {Snowflake} input.channelID
  345. */
  346. DCP.getPinnedMessages = function(input, callback) {
  347. this._req('get', Endpoints.PINNED_MESSAGES(input.channelID), function(err, res) {
  348. handleResCB("Unable to get pinned messages", err, res, callback);
  349. });
  350. };
  351.  
  352. /**
  353. * Delete a pinned message from a channel.
  354. * @arg {Object} input
  355. * @arg {Snowflake} input.channelID
  356. * @arg {Snowflake} input.messageID
  357. */
  358. DCP.deletePinnedMessage = function(input, callback) {
  359. this._req('delete', Endpoints.PINNED_MESSAGES(input.channelID, input.messageID), function(err, res) {
  360. handleResCB("Unable to delete pinned message", err, res, callback);
  361. });
  362. };
  363.  
  364. /**
  365. * Send 'typing...' status to a channel
  366. * @arg {Snowflake} channelID
  367. */
  368. DCP.simulateTyping = function(channelID, callback) {
  369. this._req('post', Endpoints.TYPING(channelID), function(err, res) {
  370. handleResCB("Unable to simulate typing", err, res, callback);
  371. });
  372. };
  373.  
  374. /**
  375. * Replace Snowflakes with the names if applicable.
  376. * @arg {String} message - The message to fix.
  377. */
  378. DCP.fixMessage = function(message) {
  379. var client = this;
  380. return message.replace(/<@&(\d*)>|<@!(\d*)>|<@(\d*)>|<#(\d*)>/g, function(match, RID, NID, UID, CID) {
  381. var k, i;
  382. if (UID || CID) {
  383. if (client.users[UID]) return "@" + client.users[UID].username;
  384. if (client.channels[CID]) return "#" + client.channels[CID].name;
  385. }
  386. if (RID || NID) {
  387. k = Object.keys(client.servers);
  388. for (i=0; i<k.length; i++) {
  389. if (client.servers[k[i]].roles[RID]) return "@" + client.servers[k[i]].roles[RID].name;
  390. if (client.servers[k[i]].members[NID]) return "@" + client.servers[k[i]].members[NID].nick;
  391. }
  392. }
  393. });
  394. };
  395.  
  396. /**
  397. * Add an emoji reaction to a message.
  398. * @arg {Object} input
  399. * @arg {Snowflake} input.channelID
  400. * @arg {Snowflake} input.messageID
  401. * @arg {String} input.reaction - Either the emoji unicode or the emoji name:id/object.
  402. */
  403. DCP.addReaction = function(input, callback) {
  404. this._req('put', Endpoints.USER_REACTIONS(input.channelID, input.messageID, stringifyEmoji(input.reaction)), function(err, res) {
  405. handleResCB("Unable to add reaction", err, res, callback);
  406. });
  407. };
  408.  
  409. /**
  410. * Get an emoji reaction of a message.
  411. * @arg {Object} input
  412. * @arg {Snowflake} input.channelID
  413. * @arg {Snowflake} input.messageID
  414. * @arg {String} input.reaction - Either the emoji unicode or the emoji name:id/object.
  415. * @arg {String} [input.limit]
  416. */
  417. DCP.getReaction = function(input, callback) {
  418. var qs = { limit: (typeof(input.limit) !== 'number' ? 100 : input.limit) };
  419. this._req('get', Endpoints.MESSAGE_REACTIONS(input.channelID, input.messageID, stringifyEmoji(input.reaction)) + qstringify(qs), function(err, res) {
  420. handleResCB("Unable to get reaction", err, res, callback);
  421. });
  422. };
  423.  
  424. /**
  425. * Remove an emoji reaction from a message.
  426. * @arg {Object} input
  427. * @arg {Snowflake} input.channelID
  428. * @arg {Snowflake} input.messageID
  429. * @arg {Snowflake} [input.userID]
  430. * @arg {String} input.reaction - Either the emoji unicode or the emoji name:id/object.
  431. */
  432. DCP.removeReaction = function(input, callback) {
  433. this._req('delete', Endpoints.USER_REACTIONS(input.channelID, input.messageID, stringifyEmoji(input.reaction), input.userID), function(err, res) {
  434. handleResCB("Unable to remove reaction", err, res, callback);
  435. });
  436. };
  437.  
  438. /**
  439. * Remove all emoji reactions from a message.
  440. * @arg {Object} input
  441. * @arg {Snowflake} input.channelID
  442. * @arg {Snowflake} input.messageID
  443. */
  444. DCP.removeAllReactions = function(input, callback) {
  445. this._req('delete', Endpoints.MESSAGE_REACTIONS(input.channelID, input.messageID), function(err, res) {
  446. handleResCB("Unable to remove reactions", err, res, callback);
  447. });
  448. };
  449.  
  450. /* - DiscordClient - Methods - Server Management - */
  451.  
  452. /**
  453. * Remove a user from a server.
  454. * @arg {Object} input
  455. * @arg {Snowflake} input.serverID
  456. * @arg {Snowflake} input.userID
  457. */
  458. DCP.kick = function(input, callback) {
  459. this._req('delete', Endpoints.MEMBERS(input.serverID, input.userID), function(err, res) {
  460. handleResCB("Could not kick user", err, res, callback);
  461. });
  462. };
  463.  
  464. /**
  465. * Remove and ban a user from a server.
  466. * @arg {Object} input
  467. * @arg {Snowflake} input.serverID
  468. * @arg {Snowflake} input.userID
  469. * @arg {String} input.reason
  470. * @arg {Number} [input.lastDays] - Removes their messages up until this point, either 1 or 7 days.
  471. */
  472. DCP.ban = function(input, callback) {
  473. var url = Endpoints.BANS(input.serverID, input.userID);
  474. var opts = {};
  475.  
  476. if (input.lastDays) {
  477. opts.lastDays = Number(input.lastDays);
  478. opts.lastDays = Math.min(opts.lastDays, 7);
  479. opts.lastDays = Math.max(opts.lastDays, 1);
  480. }
  481.  
  482. if (input.reason) opts.reason = input.reason;
  483.  
  484. url += qstringify(opts);
  485.  
  486. this._req('put', url, function(err, res) {
  487. handleResCB("Could not ban user", err, res, callback);
  488. });
  489. }
  490.  
  491. /**
  492. * Unban a user from a server.
  493. * @arg {Object} input
  494. * @arg {Snowflake} input.serverID
  495. * @arg {Snowflake} input.userID
  496. */
  497. DCP.unban = function(input, callback) {
  498. this._req('delete', Endpoints.BANS(input.serverID, input.userID), function(err, res) {
  499. handleResCB("Could not unban user", err, res, callback);
  500. });
  501. };
  502.  
  503. /**
  504. * Move a user between voice channels.
  505. * @arg {Object} input
  506. * @arg {Snowflake} input.serverID
  507. * @arg {Snowflake} input.userID
  508. * @arg {Snowflake} input.channelID
  509. */
  510. DCP.moveUserTo = function(input, callback) {
  511. this._req('patch', Endpoints.MEMBERS(input.serverID, input.userID), {channel_id: input.channelID}, function(err, res) {
  512. handleResCB("Could not move the user", err, res, callback);
  513. });
  514. };
  515.  
  516. /**
  517. * Server-mute the user from speaking in all voice channels.
  518. * @arg {Object} input
  519. * @arg {Snowflake} input.serverID
  520. * @arg {Snowflake} input.userID
  521. */
  522. DCP.mute = function(input, callback) {
  523. this._req('patch', Endpoints.MEMBERS(input.serverID, input.userID), {mute: true}, function(err, res) {
  524. handleResCB("Could not mute user", err, res, callback);
  525. });
  526. };
  527.  
  528. /**
  529. * Remove the server-mute from a user.
  530. * @arg {Object} input
  531. * @arg {Snowflake} input.serverID
  532. * @arg {Snowflake} input.userID
  533. */
  534. DCP.unmute = function(input, callback) {
  535. this._req('patch', Endpoints.MEMBERS(input.serverID, input.userID), {mute: false}, function(err, res) {
  536. handleResCB("Could not unmute user", err, res, callback);
  537. });
  538. };
  539.  
  540. /**
  541. * Server-deafen a user.
  542. * @arg {Object} input
  543. * @arg {Snowflake} input.serverID
  544. * @arg {Snowflake} input.userID
  545. */
  546. DCP.deafen = function(input, callback) {
  547. this._req('patch', Endpoints.MEMBERS(input.serverID, input.userID), {deaf: true}, function(err, res) {
  548. handleResCB("Could not deafen user", err, res, callback);
  549. });
  550. };
  551.  
  552. /**
  553. * Remove the server-deafen from a user.
  554. * @arg {Object} input
  555. * @arg {Snowflake} input.serverID
  556. * @arg {Snowflake} input.userID
  557. */
  558. DCP.undeafen = function(input, callback) {
  559. this._req('patch', Endpoints.MEMBERS(input.serverID, input.userID), {deaf: false}, function(err, res) {
  560. handleResCB("Could not undeafen user", err, res, callback);
  561. });
  562. };
  563.  
  564. /**
  565. * Self-mute the client from speaking in all voice channels.
  566. * @arg {Snowflake} serverID
  567. */
  568. DCP.muteSelf = function(serverID, callback) {
  569. var server = this.servers[serverID], channelID, voiceSession;
  570. if (!server) return handleErrCB(("Cannot find the server provided: " + serverID), callback);
  571.  
  572. server.self_mute = true;
  573.  
  574. if (!server.voiceSession) return call(callback, [null]);
  575.  
  576. voiceSession = server.voiceSession;
  577. voiceSession.self_mute = true;
  578. channelID = voiceSession.channelID;
  579. if (!channelID) return call(callback, [null]);
  580. return call(callback, [send(this._ws, Payloads.UPDATE_VOICE(serverID, channelID, true, server.self_deaf))]);
  581. };
  582.  
  583. /**
  584. * Remove the self-mute from the client.
  585. * @arg {Snowflake} serverID
  586. */
  587. DCP.unmuteSelf = function(serverID, callback) {
  588. var server = this.servers[serverID], channelID, voiceSession;
  589. if (!server) return handleErrCB(("Cannot find the server provided: " + serverID), callback);
  590.  
  591. server.self_mute = false;
  592.  
  593. if (!server.voiceSession) return call(callback, [null]);
  594.  
  595. voiceSession = server.voiceSession;
  596. voiceSession.self_mute = false;
  597. channelID = voiceSession.channelID;
  598. if (!channelID) return call(callback, [null]);
  599. return call(callback, [send(this._ws, Payloads.UPDATE_VOICE(serverID, channelID, false, server.self_deaf))]);
  600. };
  601.  
  602. /**
  603. * Self-deafen the client.
  604. * @arg {Snowflake} serverID
  605. */
  606. DCP.deafenSelf = function(serverID, callback) {
  607. var server = this.servers[serverID], channelID, voiceSession;
  608. if (!server) return handleErrCB(("Cannot find the server provided: " + serverID), callback);
  609.  
  610. server.self_deaf = true;
  611.  
  612. if (!server.voiceSession) return call(callback, [null]);
  613.  
  614. voiceSession = server.voiceSession;
  615. voiceSession.self_deaf = true;
  616. channelID = voiceSession.channelID;
  617. if (!channelID) return call(callback, [null]);
  618. return call(callback, [send(this._ws, Payloads.UPDATE_VOICE(serverID, channelID, server.self_mute, true))]);
  619. };
  620.  
  621. /**
  622. * Remove the self-deafen from the client.
  623. * @arg {Snowflake} serverID
  624. */
  625. DCP.undeafenSelf = function(serverID, callback) {
  626. var server = this.servers[serverID], channelID, voiceSession;
  627. if (!server) return handleErrCB(("Cannot find the server provided: " + serverID), callback);
  628.  
  629. server.self_deaf = false;
  630.  
  631. if (!server.voiceSession) return call(callback, [null]);
  632.  
  633. voiceSession = server.voiceSession;
  634. voiceSession.self_deaf = false;
  635. channelID = voiceSession.channelID;
  636. if (!channelID) return call(callback, [null]);
  637. return call(callback, [send(this._ws, Payloads.UPDATE_VOICE(serverID, channelID, server.self_mute, false))]);
  638. };
  639.  
  640. /*Bot server management actions*/
  641.  
  642. /**
  643. * Create a server [User only].
  644. * @arg {Object} input
  645. * @arg {String} input.name - The server's name
  646. * @arg {String} [input.region] - The server's region code, check the Gitbook documentation for all of them.
  647. * @arg {String<Base64>} [input.icon] - The last part of a Base64 Data URI. `fs.readFileSync('image.jpg', 'base64')` is enough.
  648. */
  649. DCP.createServer = function(input, callback) {
  650. var payload, client = this;
  651. payload = {icon: null, name: null, region: null};
  652. for (var key in input) {
  653. if (Object.keys(payload).indexOf(key) < 0) continue;
  654. payload[key] = input[key];
  655. }
  656. if (input.icon) payload.icon = "data:image/jpg;base64," + input.icon;
  657.  
  658. client._req('post', Endpoints.SERVERS(), payload, function(err, res) {
  659. try {
  660. client.servers[res.body.id] = {};
  661. copyKeys(res.body, client.servers[res.body.id]);
  662. } catch(e) {}
  663. handleResCB("Could not create server", err, res, callback);
  664. });
  665. };
  666.  
  667. /**
  668. * Edit server information.
  669. * @arg {Object} input
  670. * @arg {Snowflake} input.serverID
  671. * @arg {String} [input.name]
  672. * @arg {String} [input.icon]
  673. * @arg {String} [input.region]
  674. * @arg {Snowflake} [input.afk_channel_id] - The ID of the voice channel to move a user to after the afk period.
  675. * @arg {Number} [input.afk_timeout] - Time in seconds until a user is moved to the afk channel. 60, 300, 900, 1800, or 3600.
  676. */
  677. DCP.editServer = function(input, callback) {
  678. var payload, serverID = input.serverID, server, client = this;
  679. if (!client.servers[serverID]) return handleErrCB(("[editServer] Server " + serverID + " not found."), callback);
  680.  
  681. server = client.servers[serverID];
  682. payload = {
  683. name: server.name,
  684. icon: server.icon,
  685. region: server.region,
  686. afk_channel_id: server.afk_channel_id,
  687. afk_timeout: server.afk_timeout
  688. };
  689.  
  690. for (var key in input) {
  691. if (Object.keys(payload).indexOf(key) < 0) continue;
  692. if (key === 'afk_channel_id') {
  693. if (server.channels[input[key]] && server.channels[input[key]].type === 'voice') payload[key] = input[key];
  694. continue;
  695. }
  696. if (key === 'afk_timeout') {
  697. if ([60, 300, 900, 1800, 3600].indexOf(Number(input[key])) > -1) payload[key] = input[key];
  698. continue;
  699. }
  700. payload[key] = input[key];
  701. }
  702. if (input.icon) payload.icon = "data:image/jpg;base64," + input.icon;
  703.  
  704. client._req('patch', Endpoints.SERVERS(input.serverID), payload, function(err, res) {
  705. handleResCB("Unable to edit server", err, res, callback);
  706. });
  707. };
  708.  
  709. /**
  710. * Edit the widget information for a server.
  711. * @arg {Object} input
  712. * @arg {Snowflake} input.serverID - The ID of the server whose widget you want to edit.
  713. * @arg {Boolean} [input.enabled] - Whether or not you want the widget to be enabled.
  714. * @arg {Snowflake} [input.channelID] - [Important] The ID of the channel you want the instant invite to point to.
  715. */
  716. DCP.editServerWidget = function(input, callback) {
  717. var client = this, payload, url = Endpoints.SERVERS(input.serverID) + "/embed";
  718.  
  719. client._req('get', url, function(err, res) {
  720. if (err) return handleResCB("Unable to GET server widget settings. Can not edit without retrieving first.", err, res, callback);
  721. payload = {
  722. enabled: ('enabled' in input ? input.enabled : res.body.enabled),
  723. channel_id: ('channelID' in input ? input.channelID : res.body.channel_id)
  724. };
  725. client._req('patch', url, payload, function(err, res) {
  726. handleResCB("Unable to edit server widget", err, res, callback);
  727. });
  728. });
  729. };
  730.  
  731. /**
  732. * [User Account] Add an emoji to a server
  733. * @arg {Object} input
  734. * @arg {Snowflake} input.serverID
  735. * @arg {String} input.name - The emoji's name
  736. * @arg {String<Base64>} input.image - The emoji's image data in Base64
  737. */
  738. DCP.addServerEmoji = function(input, callback) {
  739. var payload = {
  740. name: input.name,
  741. image: "data:image/png;base64," + input.image
  742. };
  743. this._req('post', Endpoints.SERVER_EMOJIS(input.serverID), payload, function(err, res) {
  744. handleResCB("Unable to add emoji to the server", err, res, callback);
  745. });
  746. }
  747.  
  748. /**
  749. * [User Account] Edit a server emoji data (name only, currently)
  750. * @arg {Object} input
  751. * @arg {Snowflake} input.serverID
  752. * @arg {Snowflake} input.emojiID - The emoji's ID
  753. * @arg {String} [input.name]
  754. * @arg {Array<Snowflake>} [input.roles] - An array of role IDs you want to limit the emoji's usage to
  755. */
  756. DCP.editServerEmoji = function(input, callback) {
  757. var emoji, payload = {};
  758. if ( !this.servers[input.serverID] ) return handleErrCB(("[editServerEmoji] Server not available: " + input.serverID), callback);
  759. if ( !this.servers[input.serverID].emojis[input.emojiID]) return handleErrCB(("[editServerEmoji] Emoji not available: " + input.emojiID), callback);
  760.  
  761. emoji = this.servers[input.serverID].emojis[input.emojiID];
  762. payload.name = input.name || emoji.name;
  763. payload.roles = input.roles || emoji.roles;
  764.  
  765. this._req('patch', Endpoints.SERVER_EMOJIS(input.serverID, input.emojiID), payload, function(err, res) {
  766. handleResCB("[editServerEmoji] Could not edit server emoji", err, res, callback);
  767. });
  768. };
  769.  
  770. /**
  771. * [User Account] Remove an emoji from a server
  772. * @arg {Object} input
  773. * @arg {Snowflake} input.serverID
  774. * @arg {Snowflake} input.emojiID
  775. */
  776. DCP.deleteServerEmoji = function(input, callback) {
  777. this._req('delete', Endpoints.SERVER_EMOJIS(input.serverID, input.emojiID), function(err, res) {
  778. handleResCB("[deleteServerEmoji] Could not delete server emoji", err, res, callback);
  779. });
  780. };
  781.  
  782. /**
  783. * Leave a server.
  784. * @arg {Snowflake} serverID
  785. */
  786. DCP.leaveServer = function(serverID, callback) {
  787. this._req('delete', Endpoints.SERVERS_PERSONAL(serverID), function(err, res) {
  788. handleResCB("Could not leave server", err, res, callback);
  789. });
  790. };
  791.  
  792. /**
  793. * Delete a server owned by the client.
  794. * @arg {Snowflake} serverID
  795. */
  796. DCP.deleteServer = function(serverID, callback) {
  797. this._req('delete', Endpoints.SERVERS(serverID), function(err, res) {
  798. handleResCB("Could not delete server", err, res, callback);
  799. });
  800. };
  801.  
  802. /**
  803. * Transfer ownership of a server to another user.
  804. * @arg {Object} input
  805. * @arg {Snowflake} input.serverID
  806. * @arg {Snowflake} input.userID
  807. */
  808. DCP.transferOwnership = function(input, callback) {
  809. this._req('patch', Endpoints.SERVERS(input.serverID), {owner_id: input.userID}, function(err, res) {
  810. handleResCB("Could not transfer server ownership", err, res, callback);
  811. });
  812. };
  813.  
  814. /**
  815. * (Used to) Accept an invite to a server [User Only]. Can no longer be used.
  816. * @deprecated
  817. */
  818. DCP.acceptInvite = function(NUL, callback) {
  819. return handleErrCB("acceptInvite can no longer be used", callback);
  820. };
  821.  
  822. /**
  823. * (Used to) Generate an invite URL for a channel.
  824. * @deprecated
  825. */
  826. DCP.createInvite = function(input, callback) {
  827. var payload, client = this;
  828.  
  829. payload = {
  830. max_age: 0,
  831. max_users: 0,
  832. temporary: false
  833. };
  834.  
  835. if ( Object.keys(input).length === 1 && input.channelID ) {
  836. payload = {
  837. validate: client.internals.lastInviteCode || null
  838. };
  839. }
  840.  
  841. for (var key in input) {
  842. if (Object.keys(payload).indexOf(key) < 0) continue;
  843. payload[key] = input[key];
  844. }
  845.  
  846. this._req('post', Endpoints.CHANNEL(input.channelID) + "/invites", payload, function(err, res) {
  847. try {client.internals.lastInviteCode = res.body.code;} catch(e) {}
  848. handleResCB('Unable to create invite', err, res, callback);
  849. });
  850. };
  851.  
  852. /**
  853. * Delete an invite code.
  854. * @arg {String} inviteCode
  855. */
  856. DCP.deleteInvite = function(inviteCode, callback) {
  857. this._req('delete', Endpoints.INVITES(inviteCode), function(err, res) {
  858. handleResCB('Unable to delete invite', err, res, callback);
  859. });
  860. };
  861.  
  862. /**
  863. * Get information on an invite.
  864. * @arg {String} inviteCode
  865. */
  866. DCP.queryInvite = function(inviteCode, callback) {
  867. this._req('get', Endpoints.INVITES(inviteCode), function(err, res) {
  868. handleResCB('Unable to get information about invite', err, res, callback);
  869. });
  870. };
  871.  
  872. /**
  873. * Get all invites for a server.
  874. * @arg {Snowflake} serverID
  875. */
  876. DCP.getServerInvites = function(serverID, callback) {
  877. this._req('get', Endpoints.SERVERS(serverID) + "/invites", function(err, res) {
  878. handleResCB('Unable to get invite list for server' + serverID, err, res, callback);
  879. });
  880. };
  881.  
  882. /**
  883. * Get all invites for a channel.
  884. * @arg {Snowflake} channelID
  885. */
  886. DCP.getChannelInvites = function(channelID, callback) {
  887. this._req('get', Endpoints.CHANNEL(channelID) + "/invites", function(err, res) {
  888. handleResCB('Unable to get invite list for channel' + channelID, err, res, callback);
  889. });
  890. };
  891.  
  892. /**
  893. * Create a channel.
  894. * @arg {Object} input
  895. * @arg {Snowflake} input.serverID
  896. * @arg {String} input.name
  897. * @arg {String} [input.type] - 'text' or 'voice', defaults to 'text.
  898. */
  899. DCP.createChannel = function(input, callback) {
  900. var client = this, payload = {
  901. name: input.name,
  902. type: (['text', 'voice'].indexOf(input.type) < 0) ? 'text' : input.type
  903. };
  904.  
  905. this._req('post', Endpoints.SERVERS(input.serverID) + "/channels", payload, function(err, res) {
  906. try {
  907. var serverID = res.body.guild_id;
  908. var channelID = res.body.id;
  909.  
  910. client.channels[channelID] = new Channel( client, client.servers[serverID], res.body );
  911. } catch(e) {}
  912. handleResCB('Unable to create channel', err, res, callback);
  913. });
  914. };
  915.  
  916. /**
  917. * Create a Direct Message channel.
  918. * @arg {Snowflake} userID
  919. */
  920. DCP.createDMChannel = function(userID, callback) {
  921. var client = this;
  922. this._req('post', Endpoints.USER(client.id) + "/channels", {recipient_id: userID}, function(err, res) {
  923. if (!err && goodResponse(res)) client._uIDToDM[res.body.recipient.id] = res.body.id;
  924. handleResCB("Unable to create DM Channel", err, res, callback);
  925. });
  926. };
  927.  
  928. /**
  929. * Delete a channel.
  930. * @arg {Snowflake} channelID
  931. */
  932. DCP.deleteChannel = function(channelID, callback) {
  933. this._req('delete', Endpoints.CHANNEL(channelID), function(err, res) {
  934. handleResCB("Unable to delete channel", err, res, callback);
  935. });
  936. };
  937.  
  938. /**
  939. * Edit a channel's information.
  940. * @arg {Object} input
  941. * @arg {Snowflake} input.channelID
  942. * @arg {String} [input.name]
  943. * @arg {String} [input.topic] - The topic of the channel.
  944. * @arg {Number} [input.bitrate] - [Voice Only] The bitrate for the channel.
  945. * @arg {Number} [input.position] - The channel's position on the list.
  946. * @arg {Number} [input.user_limit] - [Voice Only] Imposes a user limit on a voice channel.
  947. */
  948. DCP.editChannelInfo = function(input, callback) {
  949. var channel, payload;
  950.  
  951. try {
  952. channel = this.channels[input.channelID];
  953. payload = {
  954. name: channel.name,
  955. topic: channel.topic,
  956. bitrate: channel.bitrate,
  957. position: channel.position,
  958. user_limit: channel.user_limit
  959. };
  960.  
  961. for (var key in input) {
  962. if (Object.keys(payload).indexOf(key) < 0) continue;
  963. if (+input[key]) {
  964. if (key === 'bitrate') {
  965. payload.bitrate = Math.min( Math.max( input.bitrate, 8000), 96000);
  966. continue;
  967. }
  968. if (key === 'user_limit') {
  969. payload.user_limit = Math.min( Math.max( input.user_limit, 0), 99);
  970. continue;
  971. }
  972. }
  973. payload[key] = input[key];
  974. }
  975.  
  976. this._req('patch', Endpoints.CHANNEL(input.channelID), payload, function(err, res) {
  977. handleResCB("Unable to edit channel", err, res, callback);
  978. });
  979. } catch(e) {return handleErrCB(e, callback);}
  980. };
  981.  
  982. /**
  983. * Edit (or creates) a permission override for a channel.
  984. * @arg {Object} input
  985. * @arg {Snowflake} input.channelID
  986. * @arg {Snowflake} [input.userID]
  987. * @arg {Snowflake} [input.roleID]
  988. * @arg {Array<Number>} input.allow - An array of permissions to allow. Discord.Permissions.XXXXXX.
  989. * @arg {Array<Number>} input.deny - An array of permissions to deny, same as above.
  990. * @arg {Array<Number>} input.default - An array of permissions that cancels out allowed and denied permissions.
  991. */
  992. DCP.editChannelPermissions = function(input, callback) { //Will shrink this up later
  993. var payload, pType, ID, channel, permissions, allowed_values;
  994. if (!input.userID && !input.roleID) return handleErrCB("[editChannelPermissions] No userID or roleID provided", callback);
  995. if (!this.channels[input.channelID]) return handleErrCB(("[editChannelPermissions] No channel found for ID: " + input.channelID), callback);
  996. if (!input.allow && !input.deny && !input.default) return handleErrCB("[editChannelPermissions] No allow, deny or default array provided.", callback);
  997.  
  998. pType = input.userID ? 'user' : 'role';
  999. ID = input[pType + "ID"];
  1000. channel = this.channels[ input.channelID ];
  1001. permissions = channel.permissions[pType][ID] || { allow: 0, deny: 0 };
  1002. allowed_values = [0, 4, 28].concat((channel.type === 'text' ?
  1003. [10, 11, 12, 13, 14, 15, 16, 17, 18] :
  1004. [20, 21, 22, 23, 24, 25] ));
  1005.  
  1006. //Take care of allow first
  1007. if (type(input.allow) === 'array') {
  1008. input.allow.forEach(function(perm) {
  1009. if (allowed_values.indexOf(perm) < 0) return;
  1010. if (hasPermission(perm, permissions.deny)) {
  1011. permissions.deny = removePermission(perm, permissions.deny);
  1012. }
  1013. permissions.allow = givePermission(perm, permissions.allow);
  1014. });
  1015. }
  1016. //Take care of deny second
  1017. if (type(input.deny) === 'array') {
  1018. input.deny.forEach(function(perm) {
  1019. if (allowed_values.indexOf(perm) < 0) return;
  1020. if (hasPermission(perm, permissions.allow)) {
  1021. permissions.allow = removePermission(perm, permissions.allow);
  1022. }
  1023. permissions.deny = givePermission(perm, permissions.deny);
  1024. });
  1025. }
  1026. //Take care of defaulting last
  1027. if (type(input.default) === 'array') {
  1028. input.default.forEach(function(perm) {
  1029. if (allowed_values.indexOf(perm) < 0) return;
  1030. permissions.allow = removePermission(perm, permissions.allow);
  1031. permissions.deny = removePermission(perm, permissions.deny);
  1032. });
  1033. }
  1034.  
  1035. payload = {
  1036. type: (pType === 'user' ? 'member' : 'role'),
  1037. id: ID,
  1038. deny: permissions.deny,
  1039. allow: permissions.allow
  1040. };
  1041.  
  1042. this._req('put', Endpoints.CHANNEL(input.channelID) + "/permissions/" + ID, payload, function(err, res) {
  1043. handleResCB('Unable to edit permission', err, res, callback);
  1044. });
  1045. };
  1046.  
  1047. /**
  1048. * Delete a permission override for a channel.
  1049. * @arg {Object} input
  1050. * @arg {Snowflake} input.channelID
  1051. * @arg {Snowflake} [input.userID]
  1052. * @arg {Snowflake} [input.roleID]
  1053. */
  1054. DCP.deleteChannelPermission = function(input, callback) {
  1055. var payload, pType, ID;
  1056. if (!input.userID && !input.roleID) return handleErrCB("[deleteChannelPermission] No userID or roleID provided", callback);
  1057. if (!this.channels[input.channelID]) return handleErrCB(("[deleteChannelPermission] No channel found for ID: " + input.channelID), callback);
  1058.  
  1059. pType = input.userID ? 'user' : 'role';
  1060. ID = input[pType + "ID"];
  1061.  
  1062. payload = {
  1063. type: (pType === 'user' ? 'member' : 'role'),
  1064. id: ID
  1065. };
  1066.  
  1067. this._req('delete', Endpoints.CHANNEL(input.channelID) + "/permissions/" + ID, payload, function(err, res) {
  1068. handleResCB('Unable to delete permission', err, res, callback);
  1069. });
  1070. };
  1071.  
  1072. /**
  1073. * Create a role for a server.
  1074. * @arg {Snowflake} serverID
  1075. */
  1076. DCP.createRole = function(serverID, callback) {
  1077. var client = this;
  1078. this._req('post', Endpoints.ROLES(serverID), function(err, res) {
  1079. try {
  1080. client.servers[serverID].roles[res.body.id] = new Role(res.body);
  1081. } catch(e) {}
  1082. handleResCB("Unable to create role", err, res, callback);
  1083. });
  1084. };
  1085.  
  1086. /**
  1087. * Edit a role.
  1088. * @arg {Object} input
  1089. * @arg {Snowflake} input.serverID
  1090. * @arg {Snowflake} input.roleID - The ID of the role.
  1091. * @arg {String} [input.name]
  1092. * @arg {String} [input.color] - A color value as a number. Recommend using Hex numbers, as they can map to HTML colors (0xF35353 === #F35353).
  1093. * @arg {Boolean} [input.hoist] - Separates the users in this role from the normal online users.
  1094. * @arg {Object} [input.permissions] - An Object containing the permission as a key, and `true` or `false` as its value. Read the Permissions doc.
  1095. * @arg {Boolean} [input.mentionable] - Toggles if users can @Mention this role.
  1096. */
  1097. DCP.editRole = function(input, callback) {
  1098. var role, payload;
  1099. try {
  1100. role = new Role(this.servers[input.serverID].roles[input.roleID]);
  1101. payload = {
  1102. name: role.name,
  1103. color: role.color,
  1104. hoist: role.hoist,
  1105. permissions: role._permissions,
  1106. mentionable: role.mentionable,
  1107. position: role.position
  1108. };
  1109.  
  1110. for (var key in input) {
  1111. if (Object.keys(payload).indexOf(key) < 0) continue;
  1112. if (key === 'permissions') {
  1113. for (var perm in input[key]) {
  1114. role[perm] = input[key][perm];
  1115. payload.permissions = role._permissions;
  1116. }
  1117. continue;
  1118. }
  1119. if (key === 'color') {
  1120. if (String(input[key])[0] === '#') payload.color = parseInt(String(input[key]).replace('#', '0x'), 16);
  1121. if (Discord.Colors[input[key]]) payload.color = Discord.Colors[input[key]];
  1122. if (type(input[key]) === 'number') payload.color = input[key];
  1123. continue;
  1124. }
  1125. payload[key] = input[key];
  1126. }
  1127. this._req('patch', Endpoints.ROLES(input.serverID, input.roleID), payload, function(err, res) {
  1128. handleResCB("Unable to edit role", err, res, callback);
  1129. });
  1130. } catch(e) {return handleErrCB(('[editRole] ' + e), callback);}
  1131. };
  1132.  
  1133. /**
  1134. * Delete a role.
  1135. * @arg {Object} input
  1136. * @arg {Snowflake} input.serverID
  1137. * @arg {Snowflake} input.roleID
  1138. */
  1139. DCP.deleteRole = function(input, callback) {
  1140. this._req('delete', Endpoints.ROLES(input.serverID, input.roleID), function(err, res) {
  1141. handleResCB("Could not remove role", err, res, callback);
  1142. });
  1143. };
  1144.  
  1145. /**
  1146. * Add a user to a role.
  1147. * @arg {Object} input
  1148. * @arg {Snowflake} input.serverID
  1149. * @arg {Snowflake} input.roleID
  1150. * @arg {Snowflake} input.userID
  1151. */
  1152. DCP.addToRole = function(input, callback) {
  1153. this._req('put', Endpoints.MEMBER_ROLES(input.serverID, input.userID, input.roleID), function(err, res) {
  1154. handleResCB("Could not add role", err, res, callback);
  1155. });
  1156. };
  1157.  
  1158. /**
  1159. * Remove a user from a role.
  1160. * @arg {Object} input
  1161. * @arg {Snowflake} input.serverID
  1162. * @arg {Snowflake} input.roleID
  1163. * @arg {Snowflake} input.userID
  1164. */
  1165. DCP.removeFromRole = function(input, callback) {
  1166. this._req('delete', Endpoints.MEMBER_ROLES(input.serverID, input.userID, input.roleID), function(err, res) {
  1167. handleResCB("Could not remove role", err, res, callback);
  1168. });
  1169. };
  1170.  
  1171. /**
  1172. * Edit a user's nickname.
  1173. * @arg {Object} input
  1174. * @arg {Snowflake} input.serverID
  1175. * @arg {Snowflake} input.userID
  1176. * @arg {String} input.nick - The nickname you'd like displayed.
  1177. */
  1178. DCP.editNickname = function(input, callback) {
  1179. var payload = {nick: String( input.nick ? input.nick : "" )};
  1180. var url = input.userID === this.id ?
  1181. Endpoints.MEMBERS(input.serverID) + "/@me/nick" :
  1182. Endpoints.MEMBERS(input.serverID, input.userID);
  1183.  
  1184. this._req('patch', url, payload, function(err, res) {
  1185. handleResCB("Could not change nickname", err, res, callback);
  1186. });
  1187. };
  1188.  
  1189. /**
  1190. * Edit a user's note.
  1191. * @arg {Object} input
  1192. * @arg {Snowflake} input.userID
  1193. * @arg {String} input.note - The note content that you want to use.
  1194. */
  1195. DCP.editNote = function(input, callback) {
  1196. this._req('put', Endpoints.NOTE(input.userID), {note: input.note}, function(err, res) {
  1197. handleResCB("Could not edit note", err, res, callback);
  1198. });
  1199. };
  1200.  
  1201. /**
  1202. * Retrieve a user object from Discord, the library already caches users, however.
  1203. * @arg {Object} input
  1204. * @arg {Snowflake} input.serverID
  1205. * @arg {Snowflake} input.userID
  1206. */
  1207. DCP.getMember = function(input, callback) {
  1208. this._req('get', Endpoints.MEMBERS(input.serverID, input.userID), function(err, res) {
  1209. handleResCB("Could not get member", err, res, callback);
  1210. });
  1211. };
  1212.  
  1213. /**
  1214. * Retrieve a group of user objects from Discord.
  1215. * @arg {Object} input
  1216. * @arg {Number} [input.limit] - The amount of users to pull, defaults to 50.
  1217. * @arg {Snowflake} [input.after] - The offset using a user ID.
  1218. */
  1219. DCP.getMembers = function(input, callback) {
  1220. var qs = {};
  1221. qs.limit = (typeof(input.limit) !== 'number' ? 50 : input.limit);
  1222. if (input.after) qs.after = input.after;
  1223.  
  1224. this._req('get', Endpoints.MEMBERS(input.serverID) + qstringify(qs), function(err, res) {
  1225. handleResCB("Could not get members", err, res, callback);
  1226. });
  1227. };
  1228.  
  1229. /**
  1230. * Get the ban list from a server
  1231. * @arg {Snowflake} serverID
  1232. */
  1233. DCP.getBans = function(serverID, callback) {
  1234. this._req('get', Endpoints.BANS(serverID), function(err, res) {
  1235. handleResCB("Could not get ban list", err, res, callback);
  1236. });
  1237. };
  1238.  
  1239. /**
  1240. * Get all webhooks for a server
  1241. * @arg {Snowflake} serverID
  1242. */
  1243. DCP.getServerWebhooks = function(serverID, callback) {
  1244. this._req('get', Endpoints.SERVER_WEBHOOKS(serverID), function(err, res) {
  1245. handleResCB("Could not get server Webhooks", err, res, callback);
  1246. });
  1247. };
  1248.  
  1249. /**
  1250. * Get webhooks from a channel
  1251. * @arg {Snowflake} channelID
  1252. */
  1253. DCP.getChannelWebhooks = function(channelID, callback) {
  1254. this._req('get', Endpoints.CHANNEL_WEBHOOKS(channelID), function(err, res) {
  1255. handleResCB("Could not get channel Webhooks", err, res, callback);
  1256. });
  1257. };
  1258.  
  1259. /**
  1260. * Create a webhook for a server
  1261. * @arg {Snowflake} serverID
  1262. */
  1263. DCP.createWebhook = function(serverID, callback) {
  1264. this._req('post', Endpoints.SERVER_WEBHOOKS(serverID), function(err, res) {
  1265. handleResCB("Could not create a Webhook", err, res, callback);
  1266. });
  1267. };
  1268.  
  1269. /**
  1270. * Edit a webhook
  1271. * @arg {Object} input
  1272. * @arg {Snowflake} input.webhookID - The Webhook's ID
  1273. * @arg {String} [input.name]
  1274. * @arg {String<Base64>} [input.avatar]
  1275. * @arg {String} [input.channelID]
  1276. */
  1277. DCP.editWebhook = function(input, callback) {
  1278. var client = this, payload = {}, allowed = ['avatar', 'name'];
  1279. this._req('get', Endpoints.WEBHOOKS(input.webhookID), function(err, res) {
  1280. if (err || !goodResponse(res)) return handleResCB("Couldn't get webhook, do you have permissions to access it?", err, res, callback);
  1281. allowed.forEach(function(key) {
  1282. payload[key] = (key in input ? input[key] : res.body[key]);
  1283. });
  1284. payload.channel_id = input.channelID || res.body.channel_id;
  1285.  
  1286. client._req('patch', Endpoints.WEBHOOKS(input.webhookID), payload, function(err, res) {
  1287. return handleResCB("Couldn't update webhook", err, res, callback);
  1288. });
  1289. });
  1290. };
  1291.  
  1292. /* --- Voice --- */
  1293.  
  1294. /**
  1295. * Join a voice channel.
  1296. * @arg {Snowflake} channelID
  1297. */
  1298. DCP.joinVoiceChannel = function(channelID, callback) {
  1299. var serverID, server, channel, voiceSession;
  1300. try {
  1301. serverID = this.channels[channelID].guild_id;
  1302. server = this.servers[serverID];
  1303. channel = server.channels[channelID];
  1304. } catch(e) {}
  1305. if (!serverID) return handleErrCB(("Cannot find the server related to the channel provided: " + channelID), callback);
  1306. if (channel.type !== 'voice') return handleErrCB(("Selected channel is not a voice channel: " + channelID), callback);
  1307. if (this._vChannels[channelID]) return handleErrCB(("Voice channel already active: " + channelID), callback);
  1308.  
  1309. voiceSession = getVoiceSession(this, channelID, server);
  1310. voiceSession.self_mute = server.self_mute;
  1311. voiceSession.self_deaf = server.self_deaf;
  1312. checkVoiceReady(voiceSession, callback);
  1313. return send(this._ws, Payloads.UPDATE_VOICE(serverID, channelID, server.self_mute, server.self_deaf));
  1314. };
  1315.  
  1316. /**
  1317. * Leave a voice channel.
  1318. * @arg {Snowflake} channelID
  1319. */
  1320. DCP.leaveVoiceChannel = function(channelID, callback) {
  1321. if (!this._vChannels[channelID]) return handleErrCB(("Not in the voice channel: " + channelID), callback);
  1322. return leaveVoiceChannel(this, channelID, callback);
  1323. };
  1324.  
  1325. /**
  1326. * Prepare the client for sending/receiving audio.
  1327. * @arg {Snowflake|Object} channelObj - Either the channel ID, or an Object with `channelID` as a key and the ID as the value.
  1328. * @arg {Number} [channelObj.maxStreamSize] - The size in KB that you wish to receive before pushing out earlier data. Required if you want to store or receive incoming audio.
  1329. * @arg {Boolean} [channelObj.stereo] - Sets the audio to be either stereo or mono. Defaults to true.
  1330. */
  1331. DCP.getAudioContext = function(channelObj, callback) {
  1332. // #q/qeled gave a proper timing solution. Credit where it's due.
  1333. if (!isNode) return handleErrCB("Using audio in the browser is currently not supported.", callback);
  1334. var channelID = channelObj.channelID || channelObj, voiceSession = this._vChannels[channelID], encoder = chooseAudioEncoder(['ffmpeg', 'avconv']);
  1335.  
  1336. if (!voiceSession) return handleErrCB(("You have not joined the voice channel: " + channelID), callback);
  1337. if (voiceSession.ready !== true) return handleErrCB(("The connection to the voice channel " + channelID + " has not been initialized yet."), callback);
  1338. if (!encoder) return handleErrCB("You need either 'ffmpeg' or 'avconv' and they need to be added to PATH", callback);
  1339.  
  1340. voiceSession.audio = voiceSession.audio || new AudioCB(
  1341. voiceSession,
  1342. channelObj.stereo === false ? 1 : 2,
  1343. encoder,
  1344. Math.abs(Number(channelObj.maxStreamSize)));
  1345.  
  1346. return call(callback, [null, voiceSession.audio]);
  1347. };
  1348.  
  1349. /* --- Misc --- */
  1350.  
  1351. /**
  1352. * Retrieves all offline (and online, if using a user account) users, fires the `allUsers` event when done.
  1353. */
  1354. DCP.getAllUsers = function(callback) {
  1355. var servers = Object.keys(this.servers).filter(function(s) {
  1356. s = this.servers[s];
  1357. if (s.members) return s.member_count !== Object.keys(s.members).length && (this.bot ? s.large : true);
  1358. }, this);
  1359.  
  1360. if (!servers[0]) {
  1361. this.emit('allUsers');
  1362. return handleErrCB("There are no users to be collected", callback);
  1363. }
  1364. if (!this.bot) send(this._ws, Payloads.ALL_USERS(this));
  1365.  
  1366. return getOfflineUsers(this, servers, callback);
  1367. };
  1368.  
  1369. /* --- Functions --- */
  1370. function handleErrCB(err, callback) {
  1371. if (!err) return false;
  1372. return call(callback, [new Error(err)]);
  1373. }
  1374. function handleResCB(errMessage, err, res, callback) {
  1375. if (typeof(callback) !== 'function') return;
  1376. res = res || {};
  1377. if (!err && goodResponse(res)) return (callback(null, res.body), true);
  1378.  
  1379. var e = new Error( err || errMessage );
  1380. e.name = "ResponseError";
  1381. e.statusCode = res.statusCode;
  1382. e.statusMessage = res.statusMessage;
  1383. e.response = res.body;
  1384. return (callback(e), false);
  1385. }
  1386. function goodResponse(response) {
  1387. return (response.statusCode / 100 | 0) === 2;
  1388. }
  1389. function stringifyError(response) {
  1390. if (!response) return null;
  1391. return response.statusCode + " " + response.statusMessage + "\n" + JSON.stringify(response.body);
  1392. }
  1393.  
  1394. /* - Functions - Messages - */
  1395. function sendMessage(client, to, message, callback) {
  1396. resolveID(client, to, function(channelID) {
  1397. client._req('post', Endpoints.MESSAGES(channelID), message, function(err, res) {
  1398. handleResCB("Unable to send messages", err, res, callback);
  1399. });
  1400. });
  1401. }
  1402. function cacheMessage(cache, limit, channelID, message) {
  1403. if (!cache[channelID]) cache[channelID] = {};
  1404. if (limit === -1) return void(cache[channelID][message.id] = message);
  1405. var k = Object.keys(cache[channelID]);
  1406. if (k.length > limit) delete(cache[channelID][k[0]]);
  1407. cache[channelID][message.id] = message;
  1408. }
  1409. function generateMessage(message, embed) {
  1410. return {
  1411. content: String(message),
  1412. nonce: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER),
  1413. embed: embed || {}
  1414. };
  1415. }
  1416. function messageHeaders(client) {
  1417. var r = {
  1418. "accept": "*/*",
  1419. "accept-language": "en-US;q=0.8",
  1420. };
  1421. if (isNode) {
  1422. r["accept-encoding"] = "gzip, deflate";
  1423. r.user_agent = "DiscordBot (https://github.com/izy521/discord.io, " + CURRENT_VERSION + ")";
  1424. r.dnt = 1;
  1425. }
  1426. try {
  1427. r.authorization = (client.bot ? "Bot " : "") + client.internals.token;
  1428. } catch(e) {}
  1429. return r;
  1430. }
  1431. function simulateTyping(client, to, message, time, callback) {
  1432. if (time <= 0) return sendMessage(client, to, message, callback);
  1433.  
  1434. client.simulateTyping(to, function() {
  1435. setTimeout(simulateTyping, Math.min(time, 5000), client, to, message, time - 5000, callback);
  1436. });
  1437. }
  1438. function stringifyEmoji(emoji) {
  1439. if (typeof emoji === 'object') // if (emoji.name && emoji.id)
  1440. return emoji.name + ':' + emoji.id;
  1441. if (emoji.indexOf(':') > -1)
  1442. return emoji;
  1443. return encodeURIComponent(decodeURIComponent(emoji));
  1444. }
  1445.  
  1446. /* - Functions - Utils */
  1447. function APIRequest(method, url) {
  1448. var data, callback, opts, req, headers = messageHeaders(this);
  1449. callback = ( typeof(arguments[2]) === 'function' ? arguments[2] : (data = arguments[2], arguments[3]) );
  1450.  
  1451. if (isNode) {
  1452. opts = URL.parse(url);
  1453. opts.method = method;
  1454. opts.headers = headers;
  1455.  
  1456. req = requesters[opts.protocol.slice(0, -1)].request(opts, function(res) {
  1457. var chunks = [];
  1458. res.on('data', function(c) { chunks[chunks.length] = c; });
  1459. res.once('end', function() {
  1460. chunks = Buffer.concat(chunks);
  1461. Zlib.gunzip(chunks, function(err, uc) {
  1462. if (!err) uc = uc.toString();
  1463. try { res.body = JSON.parse(uc || chunks); } catch(e) {}
  1464. return callback(null, res);
  1465. });
  1466. });
  1467. });
  1468. if (type(data) === 'object' || method.toLowerCase() === 'get') req.setHeader("Content-Type", "application/json; charset=utf-8");
  1469. if (data instanceof Multipart) req.setHeader("Content-Type", "multipart/form-data; boundary=" + data.boundary);
  1470. if (data) req.write( data.result || JSON.stringify(data), data.result ? 'binary' : 'utf-8' );
  1471. req.end();
  1472.  
  1473. return req.once('error', function(e) { return callback(e.message); });
  1474. }
  1475.  
  1476. req = new XMLHttpRequest();
  1477. req.open(method.toUpperCase(), url, true);
  1478. for (var key in headers) {
  1479. req.setRequestHeader(key, headers[key]);
  1480. }
  1481. req.onreadystatechange = function() {
  1482. if (req.readyState == 4) {
  1483. req.statusCode = req.status;
  1484. req.statusMessage = req.statusText;
  1485. try {req.body = JSON.parse(req.responseText);} catch (e) { return handleErrCB(e, callback); }
  1486. callback(null, req);
  1487. }
  1488. };
  1489. if (type(data) === 'object' || method.toLowerCase() === 'get') req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  1490. if (data instanceof Multipart) req.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + data.boundary);
  1491. if (data) return req.send( data.result ? data.result : JSON.stringify(data) );
  1492. req.send(null);
  1493. }
  1494. function send(ws, data) {
  1495. if (ws && ws.readyState == 1) ws.send(JSON.stringify(data));
  1496. }
  1497. function copy(obj) {
  1498. try {
  1499. return JSON.parse( JSON.stringify( obj ) );
  1500. } catch(e) {}
  1501. }
  1502. function copyKeys(from, to, omit) {
  1503. if (!omit) omit = [];
  1504. for (var key in from) {
  1505. if (omit.indexOf(key) > -1) continue;
  1506. to[key] = from[key];
  1507. }
  1508. }
  1509. function applyProperties(object, properties) {
  1510. properties.forEach(function(t) {
  1511. Object.defineProperty(object, t[0], {
  1512. configurable: true,
  1513. writable: true,
  1514. value: t[1]
  1515. });
  1516. }, object);
  1517. }
  1518. function type(v) {
  1519. return Object.prototype.toString.call(v).match(/ (.*)]/)[1].toLowerCase();
  1520. }
  1521. function call(f, a) {
  1522. if (typeof(f) != 'function') return;
  1523. return f.apply(null, a);
  1524. }
  1525. function qstringify(obj) {
  1526. //.map + .join is 7x slower!
  1527. var i=0, s = "", k = Object.keys(obj);
  1528. for (i;i<k.length;i++) {
  1529. s += k[i] + "=" + obj[k[i]] + "&";
  1530. }
  1531. return "?" + s.slice(0, -1);
  1532. }
  1533. function emit(client, message) {
  1534. if (!message.t) return;
  1535. var t = message.t.split("_"), i = 1, args = [t[0].toLowerCase()];
  1536.  
  1537. for (i; i<t.length; i++) {
  1538. args[0] += t[i][0] + t[i].slice(1).toLowerCase();
  1539. }
  1540. for (i=2; i<arguments.length; i++) {
  1541. args.push(arguments[i]);
  1542. }
  1543. args.push(message);
  1544. client.emit.apply(client, args);
  1545. }
  1546. function decompressWSMessage(m, f) {
  1547. f = f || {};
  1548. return f.binary ? JSON.parse(Zlib.inflateSync(m).toString()) : JSON.parse(m);
  1549. }
  1550. function removeAllListeners(emitter, type) {
  1551. if (!emitter) return;
  1552. var e = emitter._evts, i, k, o, s, z;
  1553. if (isNode) return type ? emitter.removeAllListeners(type) : emitter.removeAllListeners();
  1554.  
  1555. if (type && e[type]) {
  1556. for (i=0; i<e[type].length; i++) {
  1557. emitter.removeListener(type, e[type][i]);
  1558. }
  1559. }
  1560.  
  1561. if (!type) {
  1562. k = Object.keys(e);
  1563. for (o=0; o<k.length; o++) {
  1564. s = e[ k[o] ];
  1565. for (z=0; z<s.length; z++) {
  1566. emitter.removeListener(k[o], s[z]);
  1567. }
  1568. }
  1569. }
  1570. }
  1571. function colorFromRole(server, member) {
  1572. return member.roles.reduce(function(array, ID) {
  1573. var role = server.roles[ID];
  1574. if (!role) return array;
  1575. return role.position > array[0] && role.color ? [role.position, role.color] : array;
  1576. }, [-1, null])[1];
  1577. }
  1578. function log(client, level_message) {
  1579. var level, message;
  1580. if (arguments.length === 2) {
  1581. level = Discord.LogLevels.Info;
  1582. message = level_message;
  1583. } else {
  1584. level = level_message;
  1585. message = arguments[2];
  1586. }
  1587. return client.emit('log', level, message);
  1588. }
  1589.  
  1590. function givePermission(bit, permissions) {
  1591. return permissions | (1 << bit);
  1592. }
  1593. function removePermission(bit, permissions) {
  1594. return permissions & ~(1 << bit);
  1595. }
  1596. function hasPermission(bit, permissions) {
  1597. return ((permissions >> bit) & 1) == 1;
  1598. }
  1599. //For the Getters and Setters
  1600. function getPerm(bit) {
  1601. return function() {
  1602. return ((this._permissions >> bit) & 1) == 1;
  1603. };
  1604. }
  1605. function setPerm(bit) {
  1606. return function(v) {
  1607. if (v === true) return this._permissions |= (1 << (bit));
  1608. if (v === false) return this._permissions &= ~(1 << bit);
  1609. };
  1610. }
  1611.  
  1612. function getServerInfo(client, servArr) {
  1613. for (var server=0; server<servArr.length; server++) {
  1614. client.servers[servArr[server].id] = new Server(client, servArr[server]);
  1615. }
  1616. }
  1617. function getDirectMessages(client, DMArray) {
  1618. for (var DM=0; DM<DMArray.length; DM++) {
  1619. client.directMessages[DMArray[DM].id] = new DMChannel(client._uIDToDM, DMArray[DM]);
  1620. }
  1621. }
  1622. function resolveID(client, ID, callback) {
  1623. /*Get channel from ServerID, ChannelID or UserID.
  1624. Only really used for sendMessage and uploadFile.*/
  1625. //Callback used instead of return because requesting seems necessary.
  1626.  
  1627. if (client._uIDToDM[ID]) return callback(client._uIDToDM[ID]);
  1628. //If it's a UserID, and it's in the UserID : ChannelID cache, use the found ChannelID
  1629.  
  1630. //If the ID isn't in the UserID : ChannelID cache, let's try seeing if it belongs to a user.
  1631. if (client.users[ID]) return client.createDMChannel(ID, function(err, res) {
  1632. if (err) return console.log("Internal ID resolver error: " + JSON.stringify(err));
  1633. callback(res.id);
  1634. });
  1635.  
  1636. return callback(ID); //Finally, the ID must not belong to a User, so send the message directly to it, as it must be a Channel's.
  1637. }
  1638. function resolveEvent(e) {
  1639. return e.detail || ([e.data][0] ? [e.data] : [e.code]);
  1640. }
  1641.  
  1642. /* --- Initializing --- */
  1643. function init(client, opts) {
  1644. client.servers = {};
  1645. client.channels = {};
  1646. client.users = {};
  1647. client.directMessages = {};
  1648. client.internals = {
  1649. oauth: {},
  1650. version: CURRENT_VERSION,
  1651. settings: {}
  1652. };
  1653. client._connecting = true;
  1654.  
  1655. setupPing(client.internals);
  1656. return getToken(client, opts);
  1657. }
  1658. function getToken(client, opts) {
  1659. if (opts.token) return getGateway(client, opts.token);
  1660. if (!isNode) {
  1661. //Read from localStorage? Sounds like a bad idea, but I'll leave this here.
  1662. }
  1663. }
  1664. function getGateway(client, token) {
  1665. client.internals.token = token;
  1666.  
  1667. return APIRequest('get', Endpoints.GATEWAY, function (err, res) {
  1668. if (err || !goodResponse(res)) {
  1669. client._connecting = false;
  1670. return client.emit("disconnect", "Error GETing gateway:\n" + stringifyError(res), 0);
  1671. }
  1672. return startConnection(client, (res.body.url + "/?encoding=json&v=" + GATEWAY_VERSION));
  1673. });
  1674. }
  1675. function startConnection(client, gateway) {
  1676. client._ws = new Websocket(gateway);
  1677. client.internals.gatewayUrl = gateway;
  1678.  
  1679. client._ws.once('close', handleWSClose.bind(client));
  1680. client._ws.once('error', handleWSClose.bind(client));
  1681. client._ws.on('message', handleWSMessage.bind(client));
  1682. }
  1683. function getOfflineUsers(client, servArr, callback) {
  1684. if (!servArr[0]) return call(callback);
  1685.  
  1686. send(client._ws, Payloads.OFFLINE_USERS(servArr));
  1687. return setTimeout( getOfflineUsers, 0, client, servArr, callback );
  1688. }
  1689. function checkForAllServers(client, ready, message) {
  1690. var all = Object.keys(client.servers).every(function(s) {
  1691. return !client.servers[s].unavailable;
  1692. });
  1693. if (all || ready[0]) return void(client._ready = true && client.emit('ready', message));
  1694. return setTimeout(checkForAllServers, 0, client, ready, message);
  1695. }
  1696. function setupPing(obj) {
  1697. applyProperties(obj, [
  1698. ["_pings", []],
  1699. ["_lastHB", 0]
  1700. ]);
  1701. Object.defineProperty(obj, 'ping', {
  1702. get: function() {
  1703. return ((obj._pings.reduce(function(p, c) { return p + c; }, 0) / obj._pings.length) || 0) | 0;
  1704. },
  1705. set: function() {}
  1706. });
  1707. }
  1708. function validateShard(shard) {
  1709. return (
  1710. type(shard) === 'array' &&
  1711. shard.length === 2 &&
  1712. shard[0] <= shard[1] &&
  1713. shard[1] > 1
  1714. ) ? shard : null;
  1715. }
  1716. function identifyOrResume(client) {
  1717. var payload, internals = client.internals;
  1718.  
  1719. if (internals.sequence && internals.token && internals.sessionID) {
  1720. payload = Payloads.RESUME(client);
  1721. } else {
  1722. payload = Payloads.IDENTIFY(client);
  1723. if (client._shard) payload.d.shard = client._shard;
  1724. }
  1725. client._connecting = false;
  1726.  
  1727. return send(client._ws, payload);
  1728. }
  1729.  
  1730. /* - Functions - Websocket Handling - */
  1731. function handleWSMessage(data, flags) {
  1732. var message = decompressWSMessage(data, flags);
  1733. var _data = message.d;
  1734. var client = this, user, server, member, old,
  1735. userID, serverID, channelID, currentVCID,
  1736. mute, deaf, self_mute, self_deaf;
  1737.  
  1738. switch (message.op) {
  1739. case 0:
  1740. //Event dispatched - update our sequence number
  1741. client.internals.sequence = message.s;
  1742. break;
  1743. case 9:
  1744. //Disconnect after a Invalid Session
  1745. //Disconnect the client if the client has received an invalid session id
  1746. delete client.internals.sequence;
  1747. delete client.internals.sessionID;
  1748. client._ws && client._ws.close(1e3, 'Received an invalid session id');
  1749. break;
  1750. case 10:
  1751. //Start keep-alive interval
  1752. //Disconnect the client if no ping has been received
  1753. //in 15 seconds (I think that's a decent duration)
  1754. //Since v3 you the IDENTIFY/RESUME payload here.
  1755. identifyOrResume(client);
  1756. client.presenceStatus = 'online';
  1757. client.connected = true;
  1758.  
  1759. client._mainKeepAlive = setInterval(function() {
  1760. client.internals.heartbeat = setTimeout(function() {
  1761. client._ws && client._ws.close(1e3, 'No heartbeat received');
  1762. }, 15e3);
  1763. client.internals._lastHB = Date.now();
  1764. send(client._ws, Payloads.HEARTBEAT(client));
  1765. }, _data.heartbeat_interval);
  1766. break;
  1767. case 11:
  1768. clearTimeout(client.internals.heartbeat);
  1769. client.internals._pings.unshift(Date.now() - client.internals._lastHB);
  1770. client.internals._pings = client.internals._pings.slice(0, 10);
  1771. break;
  1772. }
  1773.  
  1774. //Events
  1775. client.emit('any', message);
  1776. //TODO: Remove in v3
  1777. client.emit('debug', message);
  1778. switch (message.t) {
  1779. case "READY":
  1780. copyKeys(_data.user, client);
  1781. client.internals.sessionID = _data.session_id;
  1782.  
  1783. getServerInfo(client, _data.guilds);
  1784. getDirectMessages(client, _data.private_channels);
  1785.  
  1786. if (client.bot) client.getOauthInfo(function(err, res) {
  1787. if (err) return console.log(err);
  1788. client.internals.oauth = res;
  1789. client.inviteURL = "https://discordapp.com/oauth2/authorize?client_id=" + res.id + "&scope=bot";
  1790. });
  1791. if (!client.bot) client.getAccountSettings(function(err, res) {
  1792. if (err) return console.log(err);
  1793. client.internals.settings = res;
  1794. });
  1795.  
  1796. return (function() {
  1797. if (client._ready) return;
  1798. var ready = [false];
  1799. setTimeout(function() { ready[0] = true; }, 3500);
  1800. checkForAllServers(client, ready, message);
  1801. })();
  1802. case "MESSAGE_CREATE":
  1803. client.emit('message', _data.author.username, _data.author.id, _data.channel_id, _data.content, message);
  1804. emit(client, message, _data.author.username, _data.author.id, _data.channel_id, _data.content);
  1805. return cacheMessage(client._messageCache, client._messageCacheLimit, _data.channel_id, _data);
  1806. case "MESSAGE_UPDATE":
  1807. try {
  1808. emit(client, message, client._messageCache[_data.channel_id][_data.id], _data);
  1809. } catch (e) { emit(client, message, undefined, _data); }
  1810. return cacheMessage(client._messageCache, client._messageCacheLimit, _data.channel_id, _data);
  1811. case "PRESENCE_UPDATE":
  1812. if (!_data.guild_id) break;
  1813.  
  1814. serverID = _data.guild_id;
  1815. userID = _data.user.id;
  1816.  
  1817. if (!client.users[userID]) client.users[userID] = new User(_data.user);
  1818.  
  1819. user = client.users[userID];
  1820. member = client.servers[serverID].members[userID] || {};
  1821.  
  1822. copyKeys(_data.user, user);
  1823. user.game = _data.game;
  1824.  
  1825. copyKeys(_data, member, ['user', 'guild_id', 'game']);
  1826. client.emit('presence', user.username, user.id, member.status, user.game, message);
  1827. break;
  1828. case "USER_UPDATE":
  1829. copyKeys(_data, client);
  1830. break;
  1831. case "USER_SETTINGS_UPDATE":
  1832. copyKeys(_data, client.internals);
  1833. break;
  1834. case "GUILD_CREATE":
  1835. /*The lib will attempt to create the server using the response from the
  1836. REST API, if the user using the lib creates the server. There are missing keys, however.
  1837. So we still need this GUILD_CREATE event to fill in the blanks.
  1838. If It's not our created server, then there will be no server with that ID in the cache,
  1839. So go ahead and create one.*/
  1840. client.servers[_data.id] = new Server(client, _data);
  1841. return emit(client, message, client.servers[_data.id]);
  1842. case "GUILD_UPDATE":
  1843. old = copy(client.servers[_data.id]);
  1844. Server.update(client, _data);
  1845. return emit(client, message, old, client.servers[_data.id]);
  1846. case "GUILD_DELETE":
  1847. emit(client, message, client.servers[_data.id]);
  1848. return delete(client.servers[_data.id]);
  1849. case "GUILD_MEMBER_ADD":
  1850. client.users[_data.user.id] = new User(_data.user);
  1851. client.servers[_data.guild_id].members[_data.user.id] = new Member(client, client.servers[_data.guild_id], _data);
  1852. client.servers[_data.guild_id].member_count += 1;
  1853. return emit(client, message, client.servers[_data.guild_id].members[_data.user.id]);
  1854. case "GUILD_MEMBER_UPDATE":
  1855. old = copy(client.servers[_data.guild_id].members[_data.user.id]);
  1856. Member.update(client, client.servers[_data.guild_id], _data);
  1857. return emit(client, message, old, client.servers[_data.guild_id].members[_data.user.id]);
  1858. case "GUILD_MEMBER_REMOVE":
  1859. if (_data.user && _data.user.id === client.id) return;
  1860. client.servers[_data.guild_id].member_count -= 1;
  1861. emit(client, message, client.servers[_data.guild_id].members[_data.user.id]);
  1862. return delete(client.servers[_data.guild_id].members[_data.user.id]);
  1863. case "GUILD_ROLE_CREATE":
  1864. client.servers[_data.guild_id].roles[_data.role.id] = new Role(_data.role);
  1865. return emit(client, message, client.servers[_data.guild_id].roles[_data.role.id]);
  1866. case "GUILD_ROLE_UPDATE":
  1867. server = client.servers[_data.guild_id];
  1868. old = copy(server.roles[_data.role.id]);
  1869. Role.update(server, _data);
  1870. Object.keys(server.members).forEach(function(memberID) {
  1871. var member = server.members[memberID];
  1872. if ( member.roles.indexOf(_data.role.id) < 0 ) return;
  1873. member.color = colorFromRole(server, member);
  1874. });
  1875. return emit(client, message, old, server.roles[_data.role.id]);
  1876. case "GUILD_ROLE_DELETE":
  1877. emit(client, message, client.servers[_data.guild_id].roles[_data.role_id]);
  1878. return delete(client.servers[_data.guild_id].roles[_data.role_id]);
  1879. case "CHANNEL_CREATE":
  1880. channelID = _data.id;
  1881.  
  1882. if (_data.is_private) {
  1883. if (client.directMessages[channelID]) return;
  1884. client.directMessages[channelID] = new DMChannel(client._uIDToDM, _data);
  1885. return emit(client, message, client.directMessages[channelID]);
  1886. }
  1887.  
  1888. if (client.channels[channelID]) return;
  1889. client.channels[channelID] = new Channel(client, client.servers[_data.guild_id], _data);
  1890. return emit(client, message, client.channels[channelID]);
  1891. case "CHANNEL_UPDATE":
  1892. old = copy(client.channels[_data.id]);
  1893. Channel.update(client, _data);
  1894. return emit(client, message, old, client.channels[_data.id]);
  1895. case "CHANNEL_DELETE":
  1896. if (_data.is_private === true) {
  1897. emit(client, message, client.directMessages[_data.id]);
  1898. delete(client.directMessages[_data.id]);
  1899. return delete(client._uIDToDM[_data.recipient.id]);
  1900. }
  1901. emit(client, message, client.servers[_data.guild_id].channels[_data.id]);
  1902. delete(client.servers[_data.guild_id].channels[_data.id]);
  1903. return delete(client.channels[_data.id]);
  1904. case "GUILD_EMOJIS_UPDATE":
  1905. old = copy(client.servers[_data.guild_id].emojis);
  1906. Emoji.update(client.servers[_data.guild_id], _data.emojis);
  1907. return emit(client, message, old, client.servers[_data.guild_id].emojis);
  1908. case "VOICE_STATE_UPDATE":
  1909. serverID = _data.guild_id;
  1910. channelID = _data.channel_id;
  1911. userID = _data.user_id;
  1912. server = client.servers[serverID];
  1913. mute = !!_data.mute;
  1914. self_mute = !!_data.self_mute;
  1915. deaf = !!_data.deaf;
  1916. self_deaf = !!_data.self_deaf;
  1917.  
  1918. try {
  1919. currentVCID = server.members[userID].voice_channel_id;
  1920. if (currentVCID) delete( server.channels[currentVCID].members[userID] );
  1921. if (channelID) server.channels[channelID].members[userID] = _data;
  1922. server.members[userID].mute = (mute || self_mute);
  1923. server.members[userID].deaf = (deaf || self_deaf);
  1924. server.members[userID].voice_channel_id = channelID;
  1925. } catch(e) {}
  1926.  
  1927. if (userID === client.id) {
  1928. server.self_mute = self_mute;
  1929. server.self_deaf = self_deaf;
  1930. if (channelID === null) {
  1931. if (server.voiceSession) leaveVoiceChannel(client, server.voiceSession.channelID);
  1932. server.voiceSession = null;
  1933. } else {
  1934. if (!server.voiceSession) {
  1935. server.voiceSession = getVoiceSession(client, channelID, server);
  1936. }
  1937. if (channelID !== server.voiceSession.channelID) {
  1938. delete( client._vChannels[server.voiceSession.channelID] );
  1939. getVoiceSession(client, channelID, server).channelID = channelID;
  1940. }
  1941.  
  1942. server.voiceSession.session = _data.session_id;
  1943. server.voiceSession.self_mute = self_mute;
  1944. server.voiceSession.self_deaf = self_deaf;
  1945. }
  1946. }
  1947. break;
  1948. case "VOICE_SERVER_UPDATE":
  1949. serverID = _data.guild_id;
  1950. server = client.servers[serverID];
  1951. server.voiceSession.token = _data.token;
  1952. server.voiceSession.severID = serverID;
  1953. server.voiceSession.endpoint = _data.endpoint;
  1954. joinVoiceChannel(client, server.voiceSession);
  1955. break;
  1956. case "GUILD_MEMBERS_CHUNK":
  1957. serverID = _data.guild_id;
  1958. if (!client.servers[serverID].members) client.servers[serverID].members = {};
  1959.  
  1960. _data.members.forEach(function(member) {
  1961. var uID = member.user.id;
  1962. var members = client.servers[serverID].members;
  1963. if (members[uID]) return;
  1964. if (!client.users[uID]) client.users[uID] = new User(member.user);
  1965. members[uID] = new Member(client, client.servers[serverID], member);
  1966. });
  1967. var all = Object.keys(client.servers).every(function(server) {
  1968. server = client.servers[server];
  1969. return server.member_count === Object.keys(server.members).length;
  1970. });
  1971.  
  1972. if (all) return client.emit("allUsers");
  1973. break;
  1974. case "GUILD_SYNC":
  1975. _data.members.forEach(function(member) {
  1976. var uID = member.user.id;
  1977. if (!client.users[uID]) client.users[uID] = new User(member.user);
  1978. client.servers[_data.id].members[uID] = new Member(client, client.servers[_data.id], member);
  1979. });
  1980.  
  1981. _data.presences.forEach(function(presence) {
  1982. var uID = presence.user.id;
  1983. var members = client.servers[_data.id].members;
  1984. if (!members[uID]) return void(new User(presence.user));
  1985. delete(presence.user);
  1986. copyKeys(presence, members[uID]);
  1987. });
  1988. client.servers[_data.id].large = _data.large;
  1989. break;
  1990. }
  1991. return emit(client, message);
  1992. }
  1993. function handleWSClose(code, data) {
  1994. var client = this;
  1995. var eMsg = Discord.Codes.WebSocket[code];
  1996.  
  1997. clearInterval(client._mainKeepAlive);
  1998. client.connected = false;
  1999. client.presenceStatus = "offline";
  2000. removeAllListeners(client._ws, 'message');
  2001.  
  2002. if ([1001, 1006].indexOf(code) > -1) return getGateway(client, client.internals.token);
  2003.  
  2004. client._ready = false;
  2005. client._ws = null;
  2006.  
  2007. client.emit("disconnect", eMsg, code);
  2008. }
  2009.  
  2010. /* - Functions - Voice - */
  2011. function joinVoiceChannel(client, voiceSession) {
  2012. var vWS, vUDP, endpoint = voiceSession.endpoint.split(":")[0];
  2013. //handleVoiceChannelChange(client, voiceSession);
  2014.  
  2015. voiceSession.ws = {};
  2016. voiceSession.udp = {};
  2017. voiceSession.members = {};
  2018. voiceSession.error = null;
  2019. voiceSession.ready = false;
  2020. voiceSession.joined = false;
  2021. voiceSession.translator = {};
  2022. voiceSession.wsKeepAlive = null;
  2023. voiceSession.udpKeepAlive = null;
  2024. voiceSession.keepAlivePackets = 0;
  2025. voiceSession.emitter = new Emitter();
  2026. if (isNode) voiceSession.keepAliveBuffer = new Buffer(8).fill(0);
  2027. vWS = voiceSession.ws.connection = new Websocket("wss://" + endpoint);
  2028.  
  2029. if (isNode) return DNS.lookup(endpoint, function(err, address) {
  2030. if (err) return void(voiceSession.error = err);
  2031.  
  2032. voiceSession.address = address;
  2033. vUDP = voiceSession.udp.connection = UDP.createSocket("udp4");
  2034.  
  2035. vUDP.bind({exclusive: true});
  2036. vUDP.once('message', handleUDPMessage.bind(client, voiceSession));
  2037.  
  2038. vWS.once('open', handlevWSOpen.bind(client, voiceSession));
  2039. vWS.on('message', handlevWSMessage.bind(client, voiceSession));
  2040. vWS.once('close', handlevWSClose.bind(client, voiceSession));
  2041. });
  2042.  
  2043. vWS.once('open', handlevWSOpen.bind(client, voiceSession));
  2044. vWS.on('message', handlevWSMessage.bind(client, voiceSession));
  2045. vWS.once('close', handlevWSClose.bind(client, voiceSession));
  2046. return void(voiceSession.joined = true);
  2047. }
  2048.  
  2049. function leaveVoiceChannel(client, channelID, callback) {
  2050. if (!client._vChannels[channelID]) return;
  2051.  
  2052. try {
  2053. client._vChannels[channelID].ws.connection.close();
  2054. client._vChannels[channelID].udp.connection.close();
  2055. } catch(e) {}
  2056.  
  2057. send(client._ws, Payloads.UPDATE_VOICE(client.channels[channelID].guild_id, null, false, false));
  2058.  
  2059. return call(callback, [null]);
  2060. }
  2061.  
  2062. function keepUDPAlive(VS) {
  2063. if (!VS.keepAliveBuffer) return;
  2064.  
  2065. if (VS.keepAlivePackets > 4294967294) {
  2066. VS.keepAlivePackets = 0;
  2067. VS.keepAliveBuffer.fill(0);
  2068. }
  2069. VS.keepAliveBuffer.writeUIntLE(++VS.keepAlivePackets, 0, 6);
  2070. try {
  2071. return VS.udp.connection.send(VS.keepAliveBuffer, 0, VS.keepAliveBuffer.length, VS.ws.port, VS.address);
  2072. } catch(e) {}
  2073. }
  2074.  
  2075. function getVoiceSession(client, channelID, server) {
  2076. if (!channelID) return null;
  2077. return client._vChannels[channelID] ?
  2078. client._vChannels[channelID] :
  2079. client._vChannels[channelID] = (server && server.voiceSession) || {
  2080. serverID: (server && server.id) || null,
  2081. channelID: channelID,
  2082. token: null,
  2083. session: null,
  2084. endpoint: null,
  2085. self_mute: false,
  2086. self_deaf: false
  2087. };
  2088. }
  2089.  
  2090. function checkVoiceReady(voiceSession, callback) {
  2091. return setTimeout(function() {
  2092. if (voiceSession.error) return call(callback, [voiceSession.error]);
  2093. if (voiceSession.joined) return call(callback, [null, voiceSession.emitter]);
  2094. return checkVoiceReady(voiceSession, callback);
  2095. }, 1);
  2096. }
  2097.  
  2098. /* - Functions - Voice - Handling - */
  2099.  
  2100. function handlevWSOpen(voiceSession) {
  2101. return send(voiceSession.ws.connection, Payloads.VOICE_IDENTIFY(this.id, voiceSession));
  2102. }
  2103. function handlevWSMessage(voiceSession, vMessage, vFlags) {
  2104. var client = this, vData = decompressWSMessage(vMessage, vFlags);
  2105. switch (vData.op) {
  2106. case 2: //Ready (Actually means you're READY to initiate the UDP connection)
  2107. copyKeys(vData.d, voiceSession.ws);
  2108. voiceSession.wsKeepAlive = setInterval(send, vData.d.heartbeat_interval, voiceSession.ws.connection, { "op": 3, "d": null });
  2109.  
  2110. if (!isNode) return;
  2111.  
  2112. var udpDiscPacket = new Buffer(70);
  2113. udpDiscPacket.writeUIntBE(vData.d.ssrc, 0, 4);
  2114. voiceSession.udp.connection.send(
  2115. udpDiscPacket, 0, udpDiscPacket.length, vData.d.port, voiceSession.address,
  2116. function(err) { if (err) {leaveVoiceChannel(client, voiceSession.channelID); handleErrCB("UDP discovery error", callback); } }
  2117. );
  2118.  
  2119. voiceSession.udpKeepAlive = setInterval(keepUDPAlive, 5000, voiceSession);
  2120. break;
  2121. case 4: //Session Discription (Actually means you're ready to send audio... stupid Discord Devs :I)
  2122. voiceSession.selectedMode = vData.d.mode;
  2123. voiceSession.secretKey = vData.d.secret_key;
  2124. voiceSession.joined = true;
  2125. voiceSession.ready = true;
  2126. break;
  2127. case 5: //Speaking (At least this isn't confusing!)
  2128. voiceSession.emitter.emit('speaking', vData.d.user_id, vData.d.ssrc, vData.d.speaking);
  2129. break;
  2130. }
  2131. }
  2132. function handlevWSClose(voiceSession) {
  2133. //Emit the disconnect event first
  2134. voiceSession.emitter.emit("disconnect", voiceSession.channelID);
  2135. voiceSession.emitter = null
  2136. //Kill encoder and decoders
  2137. var audio = voiceSession.audio, members = voiceSession.members;
  2138. if (audio) {
  2139. if (audio._systemEncoder) audio._systemEncoder.kill();
  2140. if (audio._mixedDecoder) audio._mixedDecoder.destroy();
  2141. }
  2142.  
  2143. Object.keys(members).forEach(function(ID) {
  2144. var member = members[ID];
  2145. if (member.decoder) member.decoder.destroy();
  2146. });
  2147.  
  2148. //Clear intervals and remove listeners
  2149. clearInterval(voiceSession.wsKeepAlive);
  2150. clearInterval(voiceSession.udpKeepAlive);
  2151. removeAllListeners(voiceSession.emitter);
  2152. removeAllListeners(voiceSession.udp.connection, 'message');
  2153. removeAllListeners(voiceSession.ws.connection, 'message');
  2154.  
  2155. return delete(this._vChannels[voiceSession.channelID]);
  2156. }
  2157.  
  2158. function handleUDPMessage(voiceSession, msg, rinfo) {
  2159. var buffArr = JSON.parse(JSON.stringify(msg)).data, client = this, vDiscIP = "", vDiscPort;
  2160. for (var i=4; i<buffArr.indexOf(0, i); i++) {
  2161. vDiscIP += String.fromCharCode(buffArr[i]);
  2162. }
  2163. vDiscPort = msg.readUIntLE(msg.length - 2, 2).toString(10);
  2164. return send(voiceSession.ws.connection, Payloads.VOICE_DISCOVERY(vDiscIP, vDiscPort, 'xsalsa20_poly1305'));
  2165. }
  2166.  
  2167. /* - Functions - Voice - AudioCallback - */
  2168. function AudioCB(voiceSession, audioChannels, encoder, maxStreamSize) {
  2169. //With the addition of the new Stream API, `playAudioFile`, `stopAudioFile` and `send`
  2170. //will be removed. However they're deprecated for now, hence the code repetition.
  2171. if (maxStreamSize && !Opus) Opus = require('cjopus');
  2172. Stream.Duplex.call(this);
  2173. var ACBI = this;
  2174.  
  2175. this.audioChannels = audioChannels;
  2176. this.members = voiceSession.members;
  2177.  
  2178. applyProperties(this, [
  2179. ["_sequence", 0],
  2180. ["_timestamp", 0],
  2181. ["_readable", false],
  2182. ["_streamRef", null],
  2183. ["_startTime", null],
  2184. ["_systemEncoder", null],
  2185. ["_playingAudioFile", false],
  2186. ["_voiceSession", voiceSession],
  2187. ["_port", voiceSession.ws.port],
  2188. ["_address", voiceSession.address],
  2189. ["_decodeNonce", new Uint8Array(24)],
  2190. ["_vUDP", voiceSession.udp.connection],
  2191. ["_secretKey", new Uint8Array(voiceSession.secretKey)],
  2192. ["_mixedDecoder", Opus && new Opus.OpusEncoder( 48000, audioChannels ) || null]
  2193. ]);
  2194.  
  2195. createAudioEncoder(this, encoder);
  2196.  
  2197. this._write = function _write(chunk, encoding, callback) {
  2198. ACBI._systemEncoder.stdin.write(chunk);
  2199. return callback();
  2200. };
  2201. this._read = function _read() {};
  2202. this.stop = function stop() {
  2203. return this._systemEncoder.stdout.read = function() { return null };
  2204. };
  2205.  
  2206. if (maxStreamSize) {
  2207. voiceSession.ws.connection.on('message', function(data, flags) {
  2208. data = decompressWSMessage(data, flags);
  2209.  
  2210. if (data.op !== 5) return;
  2211. if (!voiceSession.members[data.d.user_id]) {
  2212. voiceSession.members[data.d.user_id] = new Stream.Readable({
  2213. highWaterMark: maxStreamSize,
  2214. read: function(s) {}
  2215. });
  2216. voiceSession.members[data.d.user_id].decoder = new Opus.OpusEncoder( 48000, ACBI.audioChannels );
  2217. ACBI.emit('newMemberStream', data.d.user_id, voiceSession.members[data.d.user_id]);
  2218. }
  2219.  
  2220. voiceSession.members[data.d.user_id].ssrc = data.d.ssrc;
  2221. voiceSession.translator[data.d.ssrc] = voiceSession.members[data.d.user_id];
  2222. });
  2223. this._vUDP.on('message', handleIncomingAudio.bind(this));
  2224. }
  2225. }
  2226. if (isNode) Util.inherits(AudioCB, Stream.Duplex);
  2227. AudioCB.VoicePacket = (function() {
  2228. if (!isNode) return;
  2229. var header = new Buffer(12), nonce = new Uint8Array(24), output = new Buffer(2048);
  2230.  
  2231. header[0] = 0x80;
  2232. header[1] = 0x78;
  2233.  
  2234. return function(packet, ssrc, sequence, timestamp, key) {
  2235. header.writeUIntBE(sequence, 2, 2);
  2236. header.writeUIntBE(timestamp, 4, 4);
  2237. header.writeUIntBE(ssrc, 8, 4);
  2238. //<Buffer 80 78 00 01 00 00 03 c0 00 00 00 01>
  2239. nonce.set(header);
  2240. //<Buffer 80 78 00 01 00 00 03 c0 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00>
  2241.  
  2242. var encrypted = new Buffer(
  2243. NACL.secretbox(
  2244. new Uint8Array(packet),
  2245. nonce,
  2246. key
  2247. )
  2248. );
  2249.  
  2250. header.copy(output);
  2251. encrypted.copy(output, 12);
  2252.  
  2253. return output.slice(0, header.length + encrypted.length);
  2254. };
  2255. })();
  2256. var ACBP = AudioCB.prototype;
  2257. //TODO: Remove in v3
  2258. ACBP.playAudioFile = function(location, callback) {
  2259. if (!this._mixedDecoder) {
  2260. if (!Opus) Opus = require('cjopus');
  2261. this._mixedDecoder = new Opus.OpusEncoder( 48000, this.audioChannels );
  2262. }
  2263.  
  2264. if (this._playingAudioFile) return handleErrCB("There is already a file being played.", callback);
  2265. var encs = ['ffmpeg', 'avconv'], selection, enc, ACBI = this;
  2266.  
  2267. this._playingAudioFile = true;
  2268. selection = chooseAudioEncoder(encs);
  2269.  
  2270. if (!selection) return console.log("You need either 'ffmpeg' or 'avconv' and they need to be added to PATH");
  2271.  
  2272. enc = ChildProc.spawn(selection , [
  2273. '-i', location,
  2274. '-f', 's16le',
  2275. '-ar', '48000',
  2276. '-ac', ACBI.audioChannels,
  2277. 'pipe:1'
  2278. ], {stdio: ['pipe', 'pipe', 'ignore']});
  2279. enc.stdout.once('end', function() {
  2280. enc.kill();
  2281. send(ACBI._voiceSession.ws.connection, Payloads.VOICE_SPEAK(0));
  2282. ACBI._playingAudioFile = false;
  2283. ACBI.emit('fileEnd');
  2284. });
  2285. enc.stdout.once('error', function(e) {
  2286. enc.stdout.emit('end');
  2287. });
  2288. enc.stdout.once('readable', function() {
  2289. send(ACBI._voiceSession.ws.connection, Payloads.VOICE_SPEAK(1));
  2290. ACBI._startTime = new Date().getTime();
  2291. prepareAudioOld(ACBI, enc.stdout, 1);
  2292. });
  2293. this._streamRef = enc;
  2294. };
  2295. //TODO: Remove in v3
  2296. ACBP.stopAudioFile = function(callback) {
  2297. if (!this._playingAudioFile) return handleErrCB("There is no file being played", callback);
  2298.  
  2299. this._streamRef.stdout.end();
  2300. this._streamRef.kill();
  2301. this._playingAudioFile = false;
  2302.  
  2303. call(callback);
  2304. };
  2305. //TODO: Remove in v3
  2306. ACBP.send = function(stream) {
  2307. if (!this._mixedDecoder) {
  2308. if (!Opus) Opus = require('cjopus');
  2309. this._mixedDecoder = new Opus.OpusEncoder( 48000, this.audioChannels );
  2310. }
  2311. send(this._voiceSession.ws.connection, Payloads.VOICE_SPEAK(1));
  2312. this._startTime = new Date().getTime();
  2313. prepareAudioOld(this, stream, 1);
  2314. };
  2315.  
  2316. function prepareAudio(ACBI, readableStream, cnt) {
  2317. var data = readableStream.read( 320 ) || readableStream.read(); //(128 [kb] * 20 [frame_size]) / 8 == 320
  2318.  
  2319. if (!data) {
  2320. send(ACBI._voiceSession.ws.connection, Payloads.VOICE_SPEAK(0));
  2321. ACBI._readable = false;
  2322. return readableStream.emit('end');
  2323. }
  2324.  
  2325. return setTimeout(function() {
  2326. sendAudio(ACBI, data);
  2327. prepareAudio(ACBI, readableStream, cnt + 1);
  2328. }, 20 + ( (ACBI._startTime + cnt * 20) - Date.now() ));
  2329. }
  2330.  
  2331. //TODO: Remove in v3
  2332. function prepareAudioOld(ACBI, readableStream) {
  2333. var done = false;
  2334.  
  2335. readableStream.on('end', function() {
  2336. done = true;
  2337. send(ACBI._voiceSession.ws.connection, Payloads.VOICE_SPEAK(0));
  2338. });
  2339.  
  2340. _prepareAudio(ACBI, readableStream, 1);
  2341.  
  2342. function _prepareAudio(ACBI, readableStream, cnt) {
  2343. if (done) return;
  2344. var buffer, encoded;
  2345.  
  2346. buffer = readableStream.read( 1920 * ACBI.audioChannels );
  2347. encoded = [0xF8, 0xFF, 0xFE];
  2348.  
  2349. if ((buffer && buffer.length === 1920 * ACBI.audioChannels) && !ACBI._mixedDecoder.destroyed) {
  2350. encoded = ACBI._mixedDecoder.encode(buffer);
  2351. }
  2352.  
  2353. return setTimeout(function() {
  2354. sendAudio(ACBI, encoded);
  2355. _prepareAudio(ACBI, readableStream, cnt + 1);
  2356. }, 20 + ( (ACBI._startTime + cnt * 20) - Date.now() ));
  2357. }
  2358. }
  2359.  
  2360. function sendAudio(ACBI, buffer) {
  2361. ACBI._sequence = (ACBI._sequence + 1 ) < 0xFFFF ? ACBI._sequence + 1 : 0;
  2362. ACBI._timestamp = (ACBI._timestamp + 960) < 0xFFFFFFFF ? ACBI._timestamp + 960 : 0;
  2363. if (ACBI._voiceSession.self_mute) return;
  2364. var audioPacket = AudioCB.VoicePacket(buffer, ACBI._voiceSession.ws.ssrc, ACBI._sequence, ACBI._timestamp, ACBI._secretKey);
  2365.  
  2366. try {
  2367. //It throws a synchronous error if it fails (someone leaves the audio channel while playing audio)
  2368. ACBI._vUDP.send(audioPacket, 0, audioPacket.length, ACBI._port, ACBI._address);
  2369. } catch(e) { return; }
  2370. }
  2371.  
  2372. function handleIncomingAudio(msg) {
  2373. //The response from the UDP keep alive ping
  2374. if (msg.length === 8 || this._voiceSession.self_deaf) return;
  2375.  
  2376. var header = msg.slice(0, 12),
  2377. audio = msg.slice(12),
  2378. ssrc = header.readUIntBE(8, 4),
  2379. member = this._voiceSession.translator[ssrc],
  2380. decrypted, decoded;
  2381.  
  2382. this._decodeNonce.set(header);
  2383.  
  2384. try {
  2385. decrypted = new Buffer(
  2386. NACL.secretbox.open(
  2387. new Uint8Array(audio),
  2388. this._decodeNonce,
  2389. this._secretKey
  2390. )
  2391. );
  2392.  
  2393. if (member) {
  2394. decoded = member.decoder.decode(decrypted);
  2395. addToStreamBuffer(member, decoded);
  2396. } else {
  2397. decoded = this._mixedDecoder.decode(decrypted);
  2398. }
  2399.  
  2400. addToStreamBuffer(this, decoded);
  2401. this.emit('incoming', ssrc, decoded );
  2402. } catch(e) {}
  2403. }
  2404. function addToStreamBuffer(RStream, data) {
  2405. return RStream.push(new Buffer(data)) || !!RStream.read(data.length);
  2406. }
  2407.  
  2408. function chooseAudioEncoder(players) {
  2409. if (!players[0]) return null;
  2410. var s = ChildProc.spawnSync(players.shift());
  2411. return s.error ? chooseAudioEncoder(players) : s.file;
  2412. }
  2413. function createAudioEncoder(ACBI, encoder) {
  2414. var enc = ACBI._systemEncoder;
  2415. if (enc) {
  2416. enc.stdout.emit('end');
  2417. enc.kill();
  2418. ACBI._systemEncoder = null;
  2419. }
  2420.  
  2421. enc = ACBI._systemEncoder = ChildProc.spawn(encoder, [
  2422. '-hide_banner',
  2423. '-loglevel', 'error',
  2424. '-i', 'pipe:0',
  2425. '-map', '0:a',
  2426. '-acodec', 'libopus',
  2427. '-f', 'data',
  2428. '-sample_fmt', 's16',
  2429. '-vbr', 'off',
  2430. '-compression_level', '10',
  2431. '-ar', '48000',
  2432. '-ac', ACBI.audioChannels,
  2433. '-b:a', '128000',
  2434. 'pipe:1'
  2435. ], {stdio: ['pipe', 'pipe', 'pipe']});
  2436.  
  2437. enc.stderr.once('data', function(d) {
  2438. if (ACBI.listeners('error').length > 0) ACBI.emit('error', d.toString());
  2439. });
  2440.  
  2441. enc.stdin.once('error', function(e) {
  2442. enc.stdout.emit('end');
  2443. enc.kill();
  2444. });
  2445.  
  2446. enc.stdout.once('error', function(e) {
  2447. enc.stdout.emit('end');
  2448. enc.kill();
  2449. });
  2450. enc.stdout.once('end', function() {
  2451. createAudioEncoder(ACBI, encoder);
  2452. ACBI.emit('done');
  2453. });
  2454. enc.stdout.on('readable', function() {
  2455. if (ACBI._readable) return;
  2456.  
  2457. ACBI._readable = true;
  2458. send(ACBI._voiceSession.ws.connection, Payloads.VOICE_SPEAK(1));
  2459. ACBI._startTime = new Date().getTime();
  2460. prepareAudio(ACBI, enc.stdout, 1);
  2461. });
  2462. }
  2463.  
  2464. /* - DiscordClient - Classes - */
  2465. function Resource() {}
  2466. Object.defineProperty(Resource.prototype, "creationTime", {
  2467. get: function() { return (+this.id / 4194304) + 1420070400000; },
  2468. set: function() {}
  2469. });
  2470. [Server, Channel, DMChannel, User, Member, Role].forEach(function(p) {
  2471. p.prototype = Object.create(Resource.prototype);
  2472. Object.defineProperty(p.prototype, 'constructor', {value: p, enumerable: false});
  2473. });
  2474.  
  2475. function Server(client, data) {
  2476. var server = this;
  2477. //Accept everything now and trim what we don't need, manually. Any data left in is fine, any data left out could lead to a broken lib.
  2478. copyKeys(data, this);
  2479. this.large = this.large || this.member_count > LARGE_THRESHOLD;
  2480. this.voiceSession = null;
  2481. this.self_mute = !!this.self_mute;
  2482. this.self_deaf = !!this.self_deaf;
  2483. if (data.unavailable) return;
  2484.  
  2485. //Objects so we can use direct property accessing without for loops
  2486. this.channels = {};
  2487. this.members = {};
  2488. this.roles = {};
  2489. this.emojis = {};
  2490.  
  2491. //Copy the data into the objects using IDs as keys
  2492. data.channels.forEach(function(channel) {
  2493. client.channels[channel.id] = new Channel(client, server, channel);
  2494. });
  2495. data.roles.forEach(function(role) {
  2496. server.roles[role.id] = new Role(role);
  2497. });
  2498. data.members.forEach(function(member) {
  2499. client.users[member.user.id] = new User(member.user);
  2500. server.members[member.user.id] = new Member(client, server, member);
  2501. });
  2502. data.presences.forEach(function(presence) {
  2503. var id = presence.user.id;
  2504. if (!client.users[id] || !server.members[id]) return;
  2505. delete(presence.user);
  2506.  
  2507. client.users[id].game = presence.game;
  2508. server.members[id].status = presence.status;
  2509. });
  2510. data.emojis.forEach(function(emoji) {
  2511. server.emojis[emoji.id] = new Emoji(emoji);
  2512. });
  2513. data.voice_states.forEach(function(vs) {
  2514. var cID = vs.channel_id;
  2515. var uID = vs.user_id;
  2516. if (!server.channels[cID] || !server.members[uID]) return;
  2517. server.channels[cID].members[uID] = vs;
  2518. server.members[uID].voice_channel_id = cID;
  2519. });
  2520.  
  2521. //Now we can get rid of any of the things we don't need anymore
  2522. delete(this.voice_states);
  2523. delete(this.presences);
  2524. }
  2525. function Channel(client, server, data) {
  2526. var channel = this;
  2527. this.members = {};
  2528. this.permissions = { user: {}, role: {} };
  2529. this.guild_id = server.id;
  2530. copyKeys(data, this, ['permission_overwrites', 'emojis']);
  2531. Object.defineProperty(server.channels, channel.id, {
  2532. get: function() { return client.channels[channel.id]; },
  2533. set: function(v) { client.channels[channel.id] = v; },
  2534. enumerable: true,
  2535. configurable: true
  2536. });
  2537. data.permission_overwrites.forEach(function(p) {
  2538. var type = (p.type === 'member' ? 'user' : 'role');
  2539. this.permissions[type][p.id] = {allow: p.allow, deny: p.deny};
  2540. }, this);
  2541.  
  2542.  
  2543. delete(this.is_private);
  2544. }
  2545. function DMChannel(translator, data) {
  2546. copyKeys(data, this);
  2547. translator[data.recipient.id] = data.id;
  2548. delete(this.is_private);
  2549. }
  2550. function User(data) {
  2551. copyKeys(data, this);
  2552. this.bot = this.bot || false;
  2553. }
  2554. function Member(client, server, data) {
  2555. copyKeys(data, this, ['user', 'joined_at',]);
  2556. this.id = data.user.id;
  2557. this.joined_at = Date.parse(data.joined_at);
  2558. this.color = colorFromRole(server, this);
  2559. ['username', 'discriminator', 'bot', 'avatar', 'game'].forEach(function(k) {
  2560. if (k in Member.prototype) return;
  2561.  
  2562. Object.defineProperty(Member.prototype, k, {
  2563. get: function() { return client.users[this.id][k]; },
  2564. set: function(v) { client.users[this.id][k] = v; },
  2565. enumerable: true,
  2566. });
  2567. });
  2568. }
  2569. function Role(data) {
  2570. copyKeys(data, this, ['permissions']);
  2571. //Use `permissions` from Discord, or `_permissions` if we're making it out of a cache.
  2572. this._permissions = data._permissions || data.permissions;
  2573. }
  2574. function Emoji(data) {
  2575. copyKeys(data, this);
  2576. }
  2577.  
  2578. function Multipart() {
  2579. this.boundary =
  2580. "NodeDiscordIO" + "-" + CURRENT_VERSION;
  2581. this.result = "";
  2582. }
  2583. Multipart.prototype.append = function(data) {
  2584. /* Header */
  2585. var str = "\r\n--";
  2586. str += this.boundary + "\r\n";
  2587. str += 'Content-Disposition: form-data; name="' + data[0] + '"';
  2588. if (data[2]) {
  2589. str += '; filename="' + data[2] + '"\r\n';
  2590. str += 'Content-Type: application/octet-stream';
  2591. }
  2592. /* Body */
  2593. str += "\r\n\r\n" + ( data[1] instanceof Buffer ? data[1] : new Buffer(String(data[1]), 'utf-8') ).toString('binary');
  2594. this.result += str;
  2595. };
  2596. Multipart.prototype.finalize = function() {
  2597. this.result += "\r\n--" + this.boundary + "--";
  2598. };
  2599.  
  2600. Server.update = function(client, data) {
  2601. if (!client.servers[data.id]) client.servers[data.id] = {}; // new Server(client, data)?
  2602. for (var key in data) {
  2603. if (key === 'roles') {
  2604. data[key].forEach(function(r) {
  2605. client.servers[data.id].roles[r.id] = new Role(r);
  2606. });
  2607. continue;
  2608. }
  2609. if (key === 'emojis') continue;
  2610. client.servers[data.id][key] = data[key];
  2611. }
  2612. };
  2613. Channel.update = function(client, data) {
  2614. if (!client.channels[data.id]) client.channels[data.id] = {}; // new Channel(client, data)?
  2615. for (var key in data) {
  2616. if (key === 'permission_overwrites') {
  2617. data[key].forEach(function(p) {
  2618. var type = (p.type === 'member' ? 'user' : 'role');
  2619. client.channels[data.id].permissions[type][p.id] = {
  2620. allow: p.allow,
  2621. deny: p.deny
  2622. };
  2623. });
  2624. continue;
  2625. }
  2626. client.channels[data.id][key] = data[key];
  2627. }
  2628. delete(client.channels[data.id].is_private);
  2629. };
  2630. Member.update = function(client, server, data) {
  2631. if (!server.members[data.user.id]) return server.members[data.user.id] = new Member(client, server, data);
  2632. var member = server.members[data.user.id];
  2633. copyKeys(data, member, ['user']);
  2634. member.color = colorFromRole(server, member);
  2635. };
  2636. Role.update = function(server, data) {
  2637. if (!server.roles[data.role.id]) server.roles[data.role.id] = {}; // new Role(data)?
  2638. server.roles[data.role.id]._permissions = data.role.permissions;
  2639. copyKeys(data.role, server.roles[data.role.id], ['permissions']);
  2640. };
  2641. Emoji.update = function(server, data) {
  2642. server.emojis = {};
  2643. data.forEach(function(emoji) {
  2644. server.emojis[emoji.id] = new Emoji(emoji);
  2645. });
  2646. }
  2647.  
  2648. Object.defineProperty(Role.prototype, "permission_values", {
  2649. get: function() { return this; },
  2650. set: function() {},
  2651. enumerable: true
  2652. });
  2653. Object.defineProperty(User.prototype, 'avatarURL', {
  2654. get: function() {
  2655. if (!this.avatar) return null;
  2656. var animated = this.avatar.indexOf("a_") > -1;
  2657. return Discord.Endpoints.CDN + "/avatars/" + this.id + "/" + this.avatar + (animated ? ".gif" : ".webp");
  2658. },
  2659. set: function() {}
  2660. });
  2661.  
  2662. //Discord.OAuth;
  2663. Discord.version = CURRENT_VERSION;
  2664. Discord.Emitter = Emitter;
  2665. Discord.Codes = {};
  2666. Discord.Codes.WebSocket = {
  2667. "0" : "Gateway Error",
  2668. "4000": "Unknown Error",
  2669. "4001": "Unknown Opcode",
  2670. "4002": "Decode Error",
  2671. "4003": "Not Authenticated",
  2672. "4004": "Authentication Failed",
  2673. "4005": "Already Authenticated",
  2674. "4006": "Session Not Valid",
  2675. "4007": "Invalid Sequence Number",
  2676. "4008": "Rate Limited",
  2677. "4009": "Session Timeout",
  2678. "4010": "Invalid Shard"
  2679. };
  2680. Discord.Colors = {
  2681. DEFAULT: 0,
  2682. AQUA: 1752220,
  2683. GREEN: 3066993,
  2684. BLUE: 3447003,
  2685. PURPLE: 10181046,
  2686. GOLD: 15844367,
  2687. ORANGE: 15105570,
  2688. RED: 15158332,
  2689. GREY: 9807270,
  2690. DARKER_GREY: 8359053,
  2691. NAVY: 3426654,
  2692. DARK_AQUA: 1146986,
  2693. DARK_GREEN: 2067276,
  2694. DARK_BLUE: 2123412,
  2695. DARK_PURPLE: 7419530,
  2696. DARK_GOLD: 12745742,
  2697. DARK_ORANGE: 11027200,
  2698. DARK_RED: 10038562,
  2699. DARK_GREY: 9936031,
  2700. LIGHT_GREY: 12370112,
  2701. DARK_NAVY: 2899536
  2702. };
  2703. Discord.Permissions = {
  2704. GENERAL_CREATE_INSTANT_INVITE: 0,
  2705. GENERAL_KICK_MEMBERS: 1,
  2706. GENERAL_BAN_MEMBERS: 2,
  2707. GENERAL_ADMINISTRATOR: 3,
  2708. GENERAL_MANAGE_CHANNELS: 4,
  2709. GENERAL_MANAGE_GUILD: 5,
  2710. GENERAL_MANAGE_ROLES: 28,
  2711. GENERAL_MANAGE_NICKNAMES: 27,
  2712. GENERAL_CHANGE_NICKNAME: 26,
  2713. GENERAL_MANAGE_WEBHOOKS: 29,
  2714. GENERAL_MANAGE_EMOJIS: 30,
  2715.  
  2716. TEXT_ADD_REACTIONS: 6,
  2717. TEXT_READ_MESSAGES: 10,
  2718. TEXT_SEND_MESSAGES: 11,
  2719. TEXT_SEND_TTS_MESSAGE: 12,
  2720. TEXT_MANAGE_MESSAGES: 13,
  2721. TEXT_EMBED_LINKS: 14,
  2722. TEXT_ATTACH_FILES: 15,
  2723. TEXT_READ_MESSAGE_HISTORY: 16,
  2724. TEXT_MENTION_EVERYONE: 17,
  2725. TEXT_EXTERNAL_EMOJIS: 18,
  2726.  
  2727. VOICE_CONNECT: 20,
  2728. VOICE_SPEAK: 21,
  2729. VOICE_MUTE_MEMBERS: 22,
  2730. VOICE_DEAFEN_MEMBERS: 23,
  2731. VOICE_MOVE_MEMBERS: 24,
  2732. VOICE_USE_VAD: 25,
  2733. };
  2734. Discord.LogLevels = {
  2735. Verbose: 0,
  2736. Info: 1,
  2737. Warn: 2,
  2738. Error: 3
  2739. };
  2740.  
  2741. Object.keys(Discord.Permissions).forEach(function(pn) {
  2742. Object.defineProperty(Role.prototype, pn, {
  2743. get: getPerm( Discord.Permissions[pn] ),
  2744. set: setPerm( Discord.Permissions[pn] ),
  2745. enumerable: true
  2746. });
  2747. });
  2748.  
  2749. /* Wrappers */
  2750. function Emitter() {
  2751. var emt = this;
  2752. if (isNode) {
  2753. EE.call(this);
  2754. if (this.prototype) return Util.inherits(this, EE);
  2755. return new EE();
  2756. }
  2757. //Thank you, http://stackoverflow.com/a/24216547
  2758. function _Emitter() {
  2759. var eventTarget = document.createDocumentFragment();
  2760. ["addEventListener", "dispatchEvent", "removeEventListener"].forEach(function(method) {
  2761. if (!this[method]) this[method] = eventTarget[method].bind(eventTarget);
  2762. }, this);
  2763. }
  2764. //But I did the rest myself! D:<
  2765. _Emitter.call(this);
  2766. this._evts = {};
  2767. this.on = function(eName, eFunc) {
  2768. if (!emt._evts[eName]) emt._evts[eName] = [];
  2769. emt._evts[eName].push(eOn);
  2770.  
  2771. return this.addEventListener(eName, eOn);
  2772.  
  2773. function eOn(e) {
  2774. return eFunc.apply(null, resolveEvent(e));
  2775. }
  2776. };
  2777. this.once = function(eName, eFunc) {
  2778. if (!emt._evts[eName]) emt._evts[eName] = [];
  2779. emt._evts[eName].push(eOnce);
  2780.  
  2781. return this.addEventListener(eName, eOnce);
  2782.  
  2783. function eOnce(e) {
  2784. eFunc.apply(null, resolveEvent(e));
  2785. return emt.removeListener(eName, eOnce);
  2786. }
  2787. };
  2788. this.removeListener = function(eName, eFunc) {
  2789. if (emt._evts[eName]) emt._evts[eName].splice(emt._evts[eName].lastIndexOf(eFunc), 1);
  2790. return this.removeEventListener(eName, eFunc);
  2791. };
  2792. this.emit = function(eName) {
  2793. return this.dispatchEvent( new CustomEvent(eName, {'detail': Array.prototype.slice.call(arguments, 1) }) );
  2794. };
  2795. return this;
  2796. }
  2797.  
  2798. function Websocket(url, opts) {
  2799. if (isNode) return new (require('ws'))(url, opts);
  2800. return Emitter.call(new WebSocket(url));
  2801. }
  2802.  
  2803. /* Endpoints */
  2804. (function () {
  2805. var API = "https://discordapp.com/api";
  2806. var CDN = "http://cdn.discordapp.com";
  2807. var ME = API + "/users/@me";
  2808. Endpoints = Discord.Endpoints = {
  2809. API: API,
  2810. CDN: CDN,
  2811.  
  2812. ME: ME,
  2813. NOTE: function(userID) {
  2814. return ME + "/notes/" + userID;
  2815. },
  2816. LOGIN: API + "/auth/login",
  2817. OAUTH: API + "/oauth2/applications/@me",
  2818. GATEWAY: API + "/gateway",
  2819. SETTINGS: ME + "/settings",
  2820.  
  2821. SERVERS: function(serverID) {
  2822. return API + "/guilds" + (serverID ? "/" + serverID : "");
  2823. },
  2824. SERVERS_PERSONAL: function(serverID) {
  2825. return this.ME + "/guilds" + (serverID ? "/" + serverID : ""); //Method to list personal servers?
  2826. },
  2827. SERVER_EMOJIS: function(serverID, emojiID) {
  2828. return this.SERVERS(serverID) + "/emojis" + (emojiID ? "/" + emojiID : "");
  2829. },
  2830.  
  2831. CHANNEL: function(channelID) {
  2832. return API + "/channels/" + channelID;
  2833. },
  2834.  
  2835. MEMBERS: function(serverID, userID) {
  2836. return this.SERVERS(serverID) + "/members" + (userID ? "/" + userID : "");
  2837. },
  2838. MEMBER_ROLES: function(serverID, userID, roleID) {
  2839. return this.MEMBERS(serverID, userID) + "/roles" + (roleID ? "/" + roleID : "");
  2840. },
  2841.  
  2842. USER: function(userID) {
  2843. return API + "/users/" + userID;
  2844. },
  2845.  
  2846. ROLES: function(serverID, roleID) {
  2847. return this.SERVERS(serverID) + "/roles" + (roleID ? "/" + roleID : "");
  2848. },
  2849.  
  2850. BANS: function(serverID, userID) {
  2851. return this.SERVERS(serverID) + "/bans" + (userID ? "/" + userID : "");
  2852. },
  2853.  
  2854. MESSAGES: function(channelID, messageID) {
  2855. return this.CHANNEL(channelID) + "/messages" + (messageID ? "/" + messageID : "");
  2856. },
  2857. PINNED_MESSAGES: function(channelID, messageID) {
  2858. return this.CHANNEL(channelID) + "/pins" + (messageID ? "/" + messageID : "");
  2859. },
  2860.  
  2861. MESSAGE_REACTIONS: function(channelID, messageID, reaction) {
  2862. return this.MESSAGES(channelID, messageID) + "/reactions" + ( reaction ? ("/" + reaction) : "" );
  2863. },
  2864. USER_REACTIONS: function(channelID, messageID, reaction, userID) {
  2865. return this.MESSAGE_REACTIONS(channelID, messageID, reaction) + '/' + ( (!userID || userID === this.id) ? '@me' : userID );
  2866. },
  2867.  
  2868. INVITES: function(inviteCode) {
  2869. return API + "/invite/" + inviteCode;
  2870. },
  2871.  
  2872. SERVER_WEBHOOKS: function(serverID) {
  2873. return this.SERVERS(serverID) + "/webhooks";
  2874. },
  2875. CHANNEL_WEBHOOKS: function(channelID) {
  2876. return this.CHANNEL(channelID) +"/webhooks";
  2877. },
  2878.  
  2879. WEBHOOKS: function(webhookID) {
  2880. return API + "/webhooks/" + webhookID;
  2881. },
  2882.  
  2883. BULK_DELETE: function(channelID) {
  2884. return this.CHANNEL(channelID) + "/messages/bulk-delete";
  2885. },
  2886.  
  2887. TYPING: function(channelID) {
  2888. return this.CHANNEL(channelID) + "/typing";
  2889. }
  2890.  
  2891. };
  2892. })();
  2893.  
  2894. /* Payloads */
  2895. (function() {
  2896. Payloads = {
  2897. IDENTIFY: function(client) {
  2898. return {
  2899. op: 2,
  2900. d: {
  2901. token: client.internals.token,
  2902. v: GATEWAY_VERSION,
  2903. compress: isNode && !!Zlib.inflateSync,
  2904. large_threshold: LARGE_THRESHOLD,
  2905. properties: {
  2906. $os: isNode ? require('os').platform() : navigator.platform,
  2907. $browser:"discord.io",
  2908. $device:"discord.io",
  2909. $referrer:"",
  2910. $referring_domain:""
  2911. }
  2912. }
  2913. };
  2914. },
  2915. RESUME: function(client) {
  2916. return {
  2917. op: 6,
  2918. d: {
  2919. seq: client.internals.s,
  2920. token: client.internals.token,
  2921. session_id: client.internals.sessionID
  2922. }
  2923. };
  2924. },
  2925. HEARTBEAT: function(client) {
  2926. return {op: 1, d: client.internals.sequence};
  2927. },
  2928. ALL_USERS: function(client) {
  2929. return {op: 12, d: Object.keys(client.servers)};
  2930. },
  2931. STATUS: function(input) {
  2932. return {
  2933. op: 3,
  2934. d: {
  2935. status: type(input.idle_since) === 'number' ? 'idle' : input.status !== undefined ? input.status : null,
  2936. afk: !!input.afk,
  2937. since: type(input.idle_since) === 'number' || input.status === 'idle' ? Date.now() : null,
  2938. game: type(input.game) === 'object' ?
  2939. {
  2940. name: input.game.name ? String(input.game.name) : null,
  2941. type: input.game.type ? Number(input.game.type) : 0,
  2942. url: input.game.url ? String(input.game.url) : null
  2943. } :
  2944. null
  2945. }
  2946. };
  2947. },
  2948. UPDATE_VOICE: function(serverID, channelID, self_mute, self_deaf) {
  2949. return {
  2950. op: 4,
  2951. d: {
  2952. guild_id: serverID,
  2953. channel_id: channelID,
  2954. self_mute: self_mute,
  2955. self_deaf: self_deaf
  2956. }
  2957. };
  2958. },
  2959. OFFLINE_USERS: function(array) {
  2960. return {
  2961. op: 8,
  2962. d: {
  2963. guild_id: array.splice(0, 50),
  2964. query: "",
  2965. limit: 0
  2966. }
  2967. };
  2968. },
  2969. VOICE_SPEAK: function(v) {
  2970. return {op:5, d:{ speaking: !!v, delay: 0 }};
  2971. },
  2972. VOICE_IDENTIFY: function(clientID, voiceSession) {
  2973. return {
  2974. op: 0,
  2975. d: {
  2976. server_id: voiceSession.serverID,
  2977. user_id: clientID,
  2978. session_id: voiceSession.session,
  2979. token: voiceSession.token
  2980. }
  2981. };
  2982. },
  2983. VOICE_DISCOVERY: function(ip, port, mode) {
  2984. return {
  2985. op:1,
  2986. d:{
  2987. protocol:"udp",
  2988. data:{
  2989. address: ip,
  2990. port: Number(port),
  2991. mode: mode
  2992. }
  2993. }
  2994. };
  2995. }
  2996. }
  2997. })();
  2998.  
  2999. })(typeof exports === 'undefined'? this.Discord = {} : exports);
Add Comment
Please, Sign In to add comment