diff --git a/lib/Client.js b/lib/Client.js index c74b5e209..58b6e636b 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1,10 +1,16 @@ +//discord.js modules "use strict"; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +var Endpoints = require("./Endpoints.js"); +var User = require("./User.js"); + +//node modules var request = require("superagent"); +var WebSocket = require("ws"); var defaultOptions = { cache_tokens: false @@ -28,6 +34,7 @@ var Client = (function () { this.websocket = null; this.events = new Map(); this.user = null; + this.alreadySentData = false; /* State values: 0 - idle @@ -36,20 +43,148 @@ var Client = (function () { 3 - ready 4 - disconnected */ + + this.userCache = new Map(); + this.channelCache = new Map(); + this.serverCache = new Map(); } _createClass(Client, [{ - key: "login", + key: "debug", + + //def debug + value: function debug(message) { + console.log(message); + } + + //def trigger + }, { + key: "trigger", + value: function trigger(event) {} //def login + }, { + key: "login", value: function login() { var email = arguments.length <= 0 || arguments[0] === undefined ? "foo@bar.com" : arguments[0]; var password = arguments.length <= 1 || arguments[1] === undefined ? "pass1234s" : arguments[1]; + var callback = arguments.length <= 2 || arguments[2] === undefined ? function () {} : arguments[2]; + + var self = this; + + this.createws(); if (this.state === 0 || this.state === 4) { - this.state = 1; - request.post(); + this.state = 1; //set the state to logging in + + request.post(Endpoints.LOGIN).send({ + email: email, + password: password + }).end(function (err, res) { + + if (err) { + self.state = 4; //set state to disconnected + self.trigger("disconnected"); + self.websocket.close(); + callback(err); + } else { + self.state = 2; //set state to logged in (not yet ready) + self.token = res.body.token; //set our token + self.trySendConnData(); + callback(null, self.token); + } + }); + } + } + + //def createws + }, { + key: "createws", + value: function createws() { + if (this.websocket) return false; + + var self = this; + + //good to go + this.websocket = new WebSocket(Endpoints.WEBSOCKET_HUB); + + //open + this.websocket.onopen = function () { + self.trySendConnData(); //try connecting + }; + + //close + this.websocket.onclose = function () { + self.trigger("disconnected"); + }; + + //message + this.websocket.onmessage = function (e) { + + var dat = false, + data = false; + + try { + dat = JSON.parse(e.data); + data = dat.d; + } catch (err) { + self.trigger("error", err, e); + return; + } + + //valid message + switch (dat.t) { + + case "READY": + self.debug("received ready packet"); + + self.user = self.addUser(data.user); + + break; + default: + self.debug("received unknown packet"); + self.trigger("unknown", dat); + break; + + } + }; + } + + //def addUser + }, { + key: "addUser", + value: function addUser(data) { + if (!this.userCache.has(data.id)) { + this.userCache.set(data.id, new User(data)); + } + return this.userCache.get(data.id); + } + + //def trySendConnData + }, { + key: "trySendConnData", + value: function trySendConnData() { + + if (this.token && this.websocket.readyState === WebSocket.OPEN && !this.alreadySentData) { + + this.alreadySentData = true; + + var data = { + op: 2, + d: { + token: this.token, + v: 2, + properties: { + "$os": "discord.js", + "$browser": "discord.js", + "$device": "discord.js", + "$referrer": "", + "$referring_domain": "" + } + } + }; + this.websocket.send(JSON.stringify(data)); } } }, { @@ -60,4 +195,6 @@ var Client = (function () { }]); return Client; -})(); \ No newline at end of file +})(); + +module.exports = Client; \ No newline at end of file diff --git a/lib/endpoints.js b/lib/endpoints.js index fe87bf57f..271b465eb 100644 --- a/lib/endpoints.js +++ b/lib/endpoints.js @@ -7,7 +7,7 @@ exports.WEBSOCKET_HUB = "wss://" + exports.BASE_DOMAIN + "/hub"; exports.API = exports.BASE + "/api"; exports.AUTH = exports.API + "/auth"; exports.LOGIN = exports.AUTH + "/login"; -exports.LOGIN = exports.AUTH + "/logout"; +exports.LOGOUT = exports.AUTH + "/logout"; exports.USERS = exports.API + "/users"; exports.SERVERS = exports.API + "/guilds"; exports.CHANNELS = exports.API + "/channels"; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 4c50da911..fa2343ae4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,23 +1,8 @@ "use strict"; var request = require("superagent"); -var Endpoints = require("./lib/endpoints.js"); -var Server = require("./lib/server.js").Server; -var Message = require("./lib/message.js").Message; -var User = require("./lib/user.js").User; -var Channel = require("./lib/channel.js").Channel; -var List = require("./lib/list.js").List; -var Invite = require("./lib/invite.js").Invite; -var PMChannel = require("./lib/PMChannel.js").PMChannel; -var WebSocket = require('ws'); -var Internal = require("./lib/internal.js").Internal; -var TokenManager = require("./lib/TokenManager.js").TokenManager; +var Endpoints = require("./Endpoints.js"); +var Client = require("./Client.js"); exports.Endpoints = Endpoints; -exports.Server = Server; -exports.Message = Message; -exports.User = User; -exports.Channel = Channel; -exports.List = List; -exports.Invite = Invite; -exports.PMChannel = PMChannel; \ No newline at end of file +exports.Client = Client; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 57ca54c21..77adad245 100644 --- a/lib/server.js +++ b/lib/server.js @@ -2,90 +2,28 @@ var User = require("./user.js").User; var List = require("./list.js").List; -/** - * A wrapper for Server information, contains channels and users too. Developers should not instantiate the class, instead they should - * manipulate Server objects given to them. - * @class Server - * @param {String} region The region of the server - */ -exports.Server = function (region, ownerID, name, id, members, icon, afkTimeout, afkChannelId) { - - /** - * The region of the Server - * @type {String} - * @attribute region - */ - this.region = region; - /** - * The ID of the owner of the Server (not a User!) - * @type {String} - * @attribute ownerID - */ - this.ownerID = ownerID; - /** - * The name of the Server - * @type {String} - * @attribute name - */ - this.name = name; - /** - * The ID of the Server - * @type {String} - * @attribute id - */ - this.id = id; - /** - * List containing members of the Server - * @param {List} - * @attribute members - */ - this.members = new List("id"); - /** - * List containing channelss of the Server - * @param {List} - * @attribute channels - */ - this.channels = new List("id"); - /** - * ID of the Icon of the Server - * @param {String} - * @attribute icon - */ - this.icon = icon; - /** - * The amount of seconds that should pass before the user is - * @type {Number} - * @attribute afkTimeout - */ +exports.Server = function (data) { + this.region = data.region; + this.ownerID = data.owner_id; + this.name = data.name; + this.id = data.id; + this.members = new Map(); + this.channels = new Map(); + this.icon = data.icon; this.afkTimeout = afkTimeout; - /** - * The ID of the AFK Channel, evaluates to false if doesn't exist. - * @type {String} - * @attribute afkChannelId - */ this.afkChannelId = afkChannelId; - for (x in members) { + for (var x in members) { var member = members[x].user; this.members.add(new User(member)); } }; -/** - * Returns a valid URL pointing towards the server's icon if it has one. - * @method getIconURL - * @return {String/Boolean} If there is an icon, a URL is returned. If not, false is returned. - */ exports.Server.prototype.getIconURL = function () { if (!this.icon) return false; return "https://discordapp.com/api/guilds/" + this.id + "/icons/" + this.icon + ".jpg"; }; -/** - * Returns the AFK Channel if a server has one - * @method getAFKChannel - * @return {Channel/Boolean} If there is an AFK Channel, a Channel is returned. If not, false is returned. - */ exports.Server.prototype.getAFKChannel = function () { if (!this.afkChannelId) return false; @@ -93,11 +31,6 @@ exports.Server.prototype.getAFKChannel = function () { return this.channels.filter("id", this.afkChannelId, true); }; -/** - * Returns the #general channel of the server. - * @method getDefaultChannel - * @return {Channel} Returns the #general channel of the Server. - */ exports.Server.prototype.getDefaultChannel = function () { return this.channels.filter("name", "general", true); diff --git a/lib/user.js b/lib/user.js index 44bb6ce7d..e4b6e682c 100644 --- a/lib/user.js +++ b/lib/user.js @@ -1,37 +1,51 @@ "use strict"; -exports.User = function (username, id, discriminator, avatar) { +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - if (!id) { - //there's no second argument - var user = username; - username = user.username; - id = user.id; - discriminator = user.discriminator; - avatar = user.avatar; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var User = (function () { + function User(data) { + _classCallCheck(this, User); + + this.username = data.username; + this.discriminator = data.discriminator; + this.id = data.id; + this.avatar = data.avatar; } - this.username = username; - this.discriminator = discriminator; - this.id = id; - this.avatar = avatar; -}; + // access using user.avatarURL; -exports.User.prototype.getAvatarURL = function () { - if (!this.avatar) return false; - return "https://discordapp.com/api/users/" + this.id + "/avatars/" + this.avatar + ".jpg"; -}; + _createClass(User, [{ + key: "mention", + value: function mention() { + return "<@" + this.id + ">"; + } + }, { + key: "toString", + value: function toString() { + /* + if we embed a user in a String - like so: + "Yo " + user + " what's up?" + It would generate something along the lines of: + "Yo @hydrabolt what's up?" + */ + return this.mention(); + } + }, { + key: "equals", + value: function equals(object) { + return object.id === this.id; + } + }, { + key: "avatarURL", + get: function get() { + if (!this.avatar) return null; + return "https://discordapp.com/api/users/" + this.id + "/avatars/" + this.avatar + ".jpg"; + } + }]); -exports.User.prototype.mention = function () { - return "<@" + this.id + ">"; -}; + return User; +})(); -exports.User.prototype.equals = function (otherUser) { - - return otherUser.id === this.id; -}; - -exports.User.prototype.equalsStrict = function (otherUser) { - - return this.username === otherUser.username && this.discriminator === otherUser.discriminator && this.id === otherUser.id && this.avatar === otherUser.avatar; -}; \ No newline at end of file +module.exports = User; \ No newline at end of file diff --git a/src/Client.js b/src/Client.js index bc783c45b..9b2355969 100644 --- a/src/Client.js +++ b/src/Client.js @@ -1,5 +1,6 @@ //discord.js modules var Endpoints = require("./Endpoints.js"); +var User = require("./User.js"); //node modules var request = require("superagent"); @@ -32,13 +33,24 @@ class Client { 3 - ready 4 - disconnected */ + + this.userCache = new Map(); + this.channelCache = new Map(); + this.serverCache = new Map(); } get ready() { return this.state === 3; } + + //def debug debug(message) { + console.log(message); + } + + //def trigger + trigger(event) { } @@ -47,6 +59,8 @@ class Client { var self = this; + this.createws(); + if (this.state === 0 || this.state === 4) { this.state = 1; //set the state to logging in @@ -60,6 +74,8 @@ class Client { if (err) { self.state = 4; //set state to disconnected + self.trigger("disconnected"); + self.websocket.close(); callback(err); } else { self.state = 2; //set state to logged in (not yet ready) @@ -83,18 +99,66 @@ class Client { //good to go this.websocket = new WebSocket(Endpoints.WEBSOCKET_HUB); + + //open this.websocket.onopen = function () { self.trySendConnData(); //try connecting }; + + //close + this.websocket.onclose = function () { + self.trigger("disconnected"); + } + + //message + this.websocket.onmessage = function (e) { + + var dat = false, data = false; + + try { + dat = JSON.parse(e.data); + data = dat.d; + } catch (err) { + self.trigger("error", err, e); + return; + } + + //valid message + switch (dat.t) { + + case "READY": + self.debug("received ready packet"); + + self.user = self.addUser( data.user ); + + + break; + default: + self.debug("received unknown packet"); + self.trigger("unknown", dat); + break; + + } + + } + + } + + //def addUser + addUser(data) { + if (!this.userCache.has(data.id)){ + this.userCache.set(data.id, new User(data)); + } + return this.userCache.get(data.id); } //def trySendConnData trySendConnData() { if (this.token && this.websocket.readyState === WebSocket.OPEN && !this.alreadySentData) { - + this.alreadySentData = true; - + var data = { op: 2, d: { @@ -113,4 +177,6 @@ class Client { } } -} \ No newline at end of file +} + +module.exports = Client; \ No newline at end of file diff --git a/src/Endpoints.js b/src/Endpoints.js index 00d8667ae..450bf0426 100644 --- a/src/Endpoints.js +++ b/src/Endpoints.js @@ -5,7 +5,7 @@ exports.WEBSOCKET_HUB = `wss://${exports.BASE_DOMAIN}/hub`; exports.API = `${exports.BASE}/api`; exports.AUTH = `${exports.API}/auth`; exports.LOGIN = `${exports.AUTH}/login`; -exports.LOGIN = `${exports.AUTH}/logout`; +exports.LOGOUT = `${exports.AUTH}/logout`; exports.USERS = `${exports.API}/users`; exports.SERVERS = `${exports.API}/guilds`; exports.CHANNELS = `${exports.API}/channels`; \ No newline at end of file diff --git a/src/index.js b/src/index.js index ba14989f3..f64a62a7f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,21 +1,6 @@ var request = require("superagent"); -var Endpoints = require("./lib/endpoints.js"); -var Server = require("./lib/server.js").Server; -var Message = require("./lib/message.js").Message; -var User = require("./lib/user.js").User; -var Channel = require("./lib/channel.js").Channel; -var List = require("./lib/list.js").List; -var Invite = require("./lib/invite.js").Invite; -var PMChannel = require("./lib/PMChannel.js").PMChannel; -var WebSocket = require('ws'); -var Internal = require("./lib/internal.js").Internal; -var TokenManager = require("./lib/TokenManager.js").TokenManager; +var Endpoints = require("./Endpoints.js"); +var Client = require("./Client.js"); exports.Endpoints = Endpoints; -exports.Server = Server; -exports.Message = Message; -exports.User = User; -exports.Channel = Channel; -exports.List = List; -exports.Invite = Invite; -exports.PMChannel = PMChannel; \ No newline at end of file +exports.Client = Client; \ No newline at end of file diff --git a/src/server.js b/src/server.js index a5cfaab6a..a64a7114d 100644 --- a/src/server.js +++ b/src/server.js @@ -1,90 +1,28 @@ var User = require( "./user.js" ).User; var List = require( "./list.js" ).List; -/** - * A wrapper for Server information, contains channels and users too. Developers should not instantiate the class, instead they should - * manipulate Server objects given to them. - * @class Server - * @param {String} region The region of the server - */ -exports.Server = function( region, ownerID, name, id, members, icon, afkTimeout, afkChannelId ) { +exports.Server = function( data ) { + this.region = data.region; + this.ownerID = data.owner_id; + this.name = data.name; + this.id = data.id; + this.members = new Map(); + this.channels = new Map(); + this.icon = data.icon; + this.afkTimeout = data.afk_timeout; + this.afkChannelId = data.afk_channel_id; - /** - * The region of the Server - * @type {String} - * @attribute region - */ - this.region = region; - /** - * The ID of the owner of the Server (not a User!) - * @type {String} - * @attribute ownerID - */ - this.ownerID = ownerID; - /** - * The name of the Server - * @type {String} - * @attribute name - */ - this.name = name; - /** - * The ID of the Server - * @type {String} - * @attribute id - */ - this.id = id; - /** - * List containing members of the Server - * @param {List} - * @attribute members - */ - this.members = new List( "id" ); - /** - * List containing channelss of the Server - * @param {List} - * @attribute channels - */ - this.channels = new List( "id" ); - /** - * ID of the Icon of the Server - * @param {String} - * @attribute icon - */ - this.icon = icon; - /** - * The amount of seconds that should pass before the user is - * @type {Number} - * @attribute afkTimeout - */ - this.afkTimeout = afkTimeout; - /** - * The ID of the AFK Channel, evaluates to false if doesn't exist. - * @type {String} - * @attribute afkChannelId - */ - this.afkChannelId = afkChannelId; - - for ( x in members ) { + for ( var x in members ) { var member = members[ x ].user; this.members.add( new User( member ) ); } } -/** - * Returns a valid URL pointing towards the server's icon if it has one. - * @method getIconURL - * @return {String/Boolean} If there is an icon, a URL is returned. If not, false is returned. - */ exports.Server.prototype.getIconURL = function(){ if(!this.icon) return false; return "https://discordapp.com/api/guilds/"+this.id+"/icons/"+this.icon+".jpg"; } -/** - * Returns the AFK Channel if a server has one - * @method getAFKChannel - * @return {Channel/Boolean} If there is an AFK Channel, a Channel is returned. If not, false is returned. - */ exports.Server.prototype.getAFKChannel = function(){ if(!this.afkChannelId) @@ -94,11 +32,6 @@ exports.Server.prototype.getAFKChannel = function(){ } -/** - * Returns the #general channel of the server. - * @method getDefaultChannel - * @return {Channel} Returns the #general channel of the Server. - */ exports.Server.prototype.getDefaultChannel = function() { return this.channels.filter( "name", "general", true ); diff --git a/src/user.js b/src/user.js index 2bf3cc51b..b69896534 100644 --- a/src/user.js +++ b/src/user.js @@ -1,37 +1,35 @@ -exports.User = function( username, id, discriminator, avatar ) { - - if ( !id ) { //there's no second argument - var user = username; - username = user.username; - id = user.id; - discriminator = user.discriminator; - avatar = user.avatar; +class User{ + constructor( data ){ + this.username = data.username; + this.discriminator = data.discriminator; + this.id = data.id; + this.avatar = data.avatar; + } + + // access using user.avatarURL; + get avatarURL(){ + if( !this.avatar ) + return null; + return `https://discordapp.com/api/users/${this.id}/avatars/${this.avatar}.jpg`; + } + + mention(){ + return `<@${this.id}>`; + } + + toString(){ + /* + if we embed a user in a String - like so: + "Yo " + user + " what's up?" + It would generate something along the lines of: + "Yo @hydrabolt what's up?" + */ + return this.mention(); + } + + equals(object){ + return object.id === this.id; } - - this.username = username; - this.discriminator = discriminator; - this.id = id; - this.avatar = avatar; } -exports.User.prototype.getAvatarURL = function() { - if ( !this.avatar ) - return false; - return "https://discordapp.com/api/users/" + this.id + "/avatars/" + this.avatar + ".jpg"; -} - -exports.User.prototype.mention = function() { - return "<@" + this.id + ">"; -} - -exports.User.prototype.equals = function( otherUser ) { - - return otherUser.id === this.id; - -} - -exports.User.prototype.equalsStrict = function( otherUser ) { - - return ( this.username === otherUser.username && this.discriminator === otherUser.discriminator && this.id === otherUser.id && this.avatar === otherUser.avatar ); - -} +module.exports = User; \ No newline at end of file diff --git a/test/bot.js b/test/bot.js new file mode 100644 index 000000000..e392c7ba2 --- /dev/null +++ b/test/bot.js @@ -0,0 +1,7 @@ +var Discord = require("../lib/index.js"); + +var mybot = new Discord.Client(); + +mybot.login("riftes@outlook.com", "hydrabotsecure", function(err, res){ + console.log(res); +}); \ No newline at end of file